[
  {
    "path": ".gitignore",
    "content": "# Gradle files\n.gradle/\nbuild/\n\n# Local configuration file (sdk path, etc)\nlocal.properties\n\n# Android Studio generated folders\ncaptures/\n.externalNativeBuild\n\n# IntelliJ project files\n*.iml\n.idea/\n\n# Misc\n.DS_Store\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "Change Log\n==========\n\nVersion 1.0.6 *(2019-03-07)*\n----------------------------\n\n* Fix: Fix query error\n* Fix: Fix DebugDb class not found error\n\n\nVersion 1.0.5 *(2019-02-18)*\n----------------------------\n\n* Reduce size by taking out encrypted database library as a separate module\n* New: Add support for database delete\n* Changed compile to implementation\n* Fix: Minor bug fixes\n\n\nVersion 1.0.4 *(2018-06-23)*\n----------------------------\n\n* Fix: Fix issue of Room Database\n\n\nVersion 1.0.3 *(2018-02-12)*\n----------------------------\n\n* New: Add support for debugging inMemory Room Database\n* Add example for Room Database\n\n\nVersion 1.0.2 *(2018-01-08)*\n----------------------------\n\n* New: Add SqlCipher support\n* New: List table name in non case sensitive alphabetical order\n\n\nVersion 1.0.1 *(2017-06-23)*\n----------------------------\n\n* New: Add insert row feature\n* New: Add custom database files support\n* New: Add method for checking isServerRunning\n* New: Add pragma support\n* Fix: Minor bug fixes\n\n\nVersion 1.0.0 *(2017-02-08)*\n----------------------------\n\n* New: Add support for editing database directly\n* New: Delete rows directly\n* New: Delete Shared Pref\n* New: Edit shared preferences directly\n* New: Add standard code for checking databases files\n* New: Complete offline support\n* Refactor library code\n\n\nVersion 0.5.0 *(2017-01-21)*\n----------------------------\n\n* New: Export DB\n* New: Method to get DB version\n* Fix: Fix proguard issue and other minor issues\n\n\nVersion 0.4.0 *(2016-11-29)*\n----------------------------\n\n* Optimizations\n* Fix: Fix few minor bugs\n\n\nVersion 0.3.0 *(2016-11-23)*\n----------------------------\n\n* New: Add support for custom port\n* Fix: Fix few minor bugs\n\n\nVersion 0.2.0 *(2016-11-17)*\n----------------------------\n\n* New: Add method for getting address\n* Fix: Fix few minor bugs\n\n\nVersion 0.1.0 *(2016-11-16)*\n----------------------------\n\nInitial release.\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\n1. Fork it!\n2. Checkout the development branch: `git checkout development`\n3. Create your feature branch: `git checkout -b my-new-feature`\n4. Add your changes to the index: `git add .`\n5. Commit your changes: `git commit -m 'Add some feature'`\n6. Push to the branch: `git push origin my-new-feature`\n7. Submit a pull request against the `development` branch\n"
  },
  {
    "path": "LICENSE",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "<img src=https://raw.githubusercontent.com/amitshekhariitbhu/Android-Debug-Database/master/assets/debug_db_banner.png >\n\n# Android Debug Database\n\n## Android Debug Database is a powerful library for debugging databases and shared preferences in Android applications\n\n### Android Debug Database allows you to view databases and shared preferences directly in your browser in a very simple way\n\n### What can Android Debug Database do?\n\n* See all the databases.\n* See all the data in the shared preferences used in your application.\n* Run any sql query on the given database to update and delete your data.\n* Directly edit the database values.\n* Directly edit the shared preferences.\n* Directly add a row in the database.\n* Directly add a key-value in the shared preferences.\n* Delete database rows and shared preferences.\n* Search in your data.\n* Sort data.\n* Download database.\n* Debug Room inMemory database.\n\n## About me\n\nHi, I am Amit Shekhar, Founder @ [Outcome School](https://outcomeschool.com) • IIT 2010-14 • I have taught and mentored many developers, and their efforts landed them high-paying tech jobs, helped many tech companies in solving their unique problems, and created many open-source libraries being used by top companies. I am passionate about sharing knowledge through open-source, blogs, and videos.\n\n### Follow Amit Shekhar\n\n- [X/Twitter](https://twitter.com/amitiitbhu)\n- [LinkedIn](https://www.linkedin.com/in/amit-shekhar-iitbhu)\n- [GitHub](https://github.com/amitshekhariitbhu)\n\n### Follow Outcome School\n\n- [YouTube](https://youtube.com/@OutcomeSchool)\n- [X/Twitter](https://x.com/outcome_school)\n- [LinkedIn](https://www.linkedin.com/company/outcomeschool)\n- [GitHub](http://github.com/OutcomeSchool)\n\n## I teach at Outcome School\n\n- [AI and Machine Learning](https://outcomeschool.com/program/ai-and-machine-learning)\n- [Android](https://outcomeschool.com/program/android)\n\nJoin Outcome School and get a high-paying tech job: [Outcome School](https://outcomeschool.com)\n\n## [Outcome School Blog](https://outcomeschool.com/blog) - High-quality content to learn Android concepts.\n\n### All these features work without rooting your device -> No need of rooted device\n\n### Using Android Debug Database Library in your application\n\nAdd this in your `settings.gradle`:\n```groovy\nmaven { url 'https://jitpack.io' }\n```\n\nIf you are using `settings.gradle.kts`, add the following:\n```kotlin\nmaven { setUrl(\"https://jitpack.io\") }\n```\n\nAdd this in your `build.gradle`\n```groovy\ndebugImplementation 'com.github.amitshekhariitbhu.Android-Debug-Database:debug-db:1.0.7'\n```\n\nIf you are using `build.gradle.kts`, add the following:\n```kotlin\ndebugImplementation(\"com.github.amitshekhariitbhu.Android-Debug-Database:debug-db:1.0.7\")\n```\n\nUsing the Android Debug Database with encrypted database\n\nAdd this in your `build.gradle`\n```groovy\ndebugImplementation 'com.github.amitshekhariitbhu.Android-Debug-Database:debug-db-encrypt:1.0.7'\n```\n\nIf you are using `build.gradle.kts`, add the following:\n```kotlin\ndebugImplementation(\"com.github.amitshekhariitbhu.Android-Debug-Database:debug-db-encrypt:1.0.7\")\n```\n\nAnd to provide the password for the DB, you should add this in the Gradle:\nDB_PASSWORD_{VARIABLE}, if for example, PERSON is the database name: DB_PASSWORD_PERSON\n```groovy\ndebug {\n    resValue(\"string\", \"DB_PASSWORD_PERSON\", \"password\")\n}\n```\n\nUse `debugImplementation` so that it will only compile in your debug build and not in your release build.\n\nThat’s all, just start the application, you will see in the logcat an entry like follows :\n\n* D/DebugDB: Open http://XXX.XXX.X.XXX:8080 in your browser\n\n* You can also always get the debug address url from your code by calling the method `DebugDB.getAddressLog();`\n\nNow open the provided link in your browser.\n\nImportant:\n\n* Your Android phone and laptop should be connected to the same Network (Wifi or LAN).\n* If you are using it over usb, run `adb forward tcp:8080 tcp:8080`\n\nNote      : If you want use different port other than 8080.\n            In the app build.gradle file under buildTypes do the following change\n\n```groovy\ndebug {\n    resValue(\"string\", \"PORT_NUMBER\", \"8081\")\n}\n```\n\nYou will see something like this :\n\n### Seeing values\n\n<img src=https://raw.githubusercontent.com/amitshekhariitbhu/Android-Debug-Database/master/assets/debugdb.png >\n\n### Editing values\n\n<img src=https://raw.githubusercontent.com/amitshekhariitbhu/Android-Debug-Database/master/assets/debugdb_edit.png >\n\n### Working with emulator\n\n* Android Default Emulator: Run the command in the terminal - `adb forward tcp:8080 tcp:8080` and open http://localhost:8080\n* Genymotion Emulator: Enable bridge from configure virtual device (option available in genymotion)\n\n### Getting address with toast, in case you missed the address log in logcat\n\nAs this library is auto-initialize, if you want to get the address log, add the following method and call (we have to do like this to avoid build error in release build as this library will not be included in the release build) using reflection.\n\n```java\npublic static void showDebugDBAddressLogToast(Context context) {\n    if (BuildConfig.DEBUG) {\n       try {\n            Class<?> debugDB = Class.forName(\"com.amitshekhar.DebugDB\");\n            Method getAddressLog = debugDB.getMethod(\"getAddressLog\");\n            Object value = getAddressLog.invoke(null);\n            Toast.makeText(context, (String) value, Toast.LENGTH_LONG).show();\n       } catch (Exception ignore) {\n\n       }\n    }\n}\n```\n\n### Adding custom database files\n\nAs this library is auto-initialize, if you want to debug custom database files, add the following method and call\n\n```java\npublic static void setCustomDatabaseFiles(Context context) {\n    if (BuildConfig.DEBUG) {\n        try {\n            Class<?> debugDB = Class.forName(\"com.amitshekhar.DebugDB\");\n            Class[] argTypes = new Class[]{HashMap.class};\n            Method setCustomDatabaseFiles = debugDB.getMethod(\"setCustomDatabaseFiles\", argTypes);\n            HashMap<String, Pair<File, String>> customDatabaseFiles = new HashMap<>();\n            // set your custom database files\n            customDatabaseFiles.put(ExtTestDBHelper.DATABASE_NAME,\n                    new Pair<>(new File(context.getFilesDir() + \"/\" + ExtTestDBHelper.DIR_NAME +\n                                                    \"/\" + ExtTestDBHelper.DATABASE_NAME), \"\"));\n            setCustomDatabaseFiles.invoke(null, customDatabaseFiles);\n        } catch (Exception ignore) {\n\n        }\n    }\n}\n```\n\n### Adding InMemory Room databases\n\nAs this library is auto-initialize, if you want to debug inMemory Room databases, add the following method and call\n\n```java\npublic static void setInMemoryRoomDatabases(SupportSQLiteDatabase... database) {\n    if (BuildConfig.DEBUG) {\n        try {\n            Class<?> debugDB = Class.forName(\"com.amitshekhar.DebugDB\");\n            Class[] argTypes = new Class[]{HashMap.class};\n            HashMap<String, SupportSQLiteDatabase> inMemoryDatabases = new HashMap<>();\n            // set your inMemory databases\n            inMemoryDatabases.put(\"InMemoryOne.db\", database[0]);\n            Method setRoomInMemoryDatabase = debugDB.getMethod(\"setInMemoryRoomDatabases\", argTypes);\n            setRoomInMemoryDatabase.invoke(null, inMemoryDatabases);\n        } catch (Exception ignore) {\n\n        }\n    }\n}\n```\n\n### Find this project useful ? :heart:\n\n* Support it by clicking the :star: button on the upper right of this page. :v:\n\n### TODO\n\n* Simplify emulator issue [Issue Link](https://github.com/amitshekhariitbhu/Android-Debug-Database/issues/6)\n* And of course many more features and bug fixes.\n\nYou can connect with me on:\n\n- [Twitter](https://twitter.com/amitiitbhu)\n- [LinkedIn](https://www.linkedin.com/in/amit-shekhar-iitbhu)\n- [GitHub](https://github.com/amitshekhariitbhu)\n- [Facebook](https://www.facebook.com/amit.shekhar.iitbhu)\n\n[**Read all of our blogs here.**](https://outcomeschool.com/blog)\n\n### License\n\n```\n   Copyright (C) 2024 Amit Shekhar\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n```\n\n### Contributing to Android Debug Database\n\nAll pull requests are welcome, make sure to follow the [contribution guidelines](CONTRIBUTING.md)\nwhen you submit pull request.\n"
  },
  {
    "path": "build.gradle",
    "content": "/*\n *\n *  *    Copyright (C) 2019 Amit Shekhar\n *  *    Copyright (C) 2011 Android Open Source Project\n *  *\n *  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n *  *    you may not use this file except in compliance with the License.\n *  *    You may obtain a copy of the License at\n *  *\n *  *        http://www.apache.org/licenses/LICENSE-2.0\n *  *\n *  *    Unless required by applicable law or agreed to in writing, software\n *  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  *    See the License for the specific language governing permissions and\n *  *    limitations under the License.\n *\n */\n\n// Top-level build file where you can add configuration options common to all sub-projects/modules.\nplugins {\n    id 'com.android.application' version '7.3.0' apply false\n    id 'com.android.library' version '7.3.0' apply false\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\next {\n    compileSdk = 33\n    targetSdk = 33\n    minSdk = 14\n}"
  },
  {
    "path": "debug-db/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "debug-db/build.gradle",
    "content": "apply plugin: 'com.android.library'\n\nandroid {\n    compileSdk rootProject.ext.compileSdk\n    defaultConfig {\n        minSdk rootProject.ext.minSdk\n        targetSdk rootProject.ext.targetSdk\n        versionCode 1\n        versionName \"1.0\"\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n    }\n}\n\ndependencies {\n    api project(':debug-db-base')\n    testImplementation 'junit:junit:4.13.2'\n    androidTestImplementation 'androidx.test:runner:1.5.2'\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'\n}"
  },
  {
    "path": "debug-db/gradle.properties",
    "content": "ARTIFACT_ID=debug-db"
  },
  {
    "path": "debug-db/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "debug-db/src/androidTest/java/com/amitshekhar/debug/ExampleInstrumentedTest.java",
    "content": "package com.amitshekhar.debug;\n\nimport android.content.Context;\nimport android.support.test.InstrumentationRegistry;\nimport android.support.test.runner.AndroidJUnit4;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\nimport static org.junit.Assert.*;\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\n@RunWith(AndroidJUnit4.class)\npublic class ExampleInstrumentedTest {\n    @Test\n    public void useAppContext() {\n        // Context of the app under test.\n        Context appContext = InstrumentationRegistry.getTargetContext();\n\n        assertEquals(\"com.amitshekhar.debug.test\", appContext.getPackageName());\n    }\n}\n"
  },
  {
    "path": "debug-db/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.amitshekhar.debug\">\n\n    <application>\n        <provider\n            android:name=\".DebugDBInitProvider\"\n            android:authorities=\"${applicationId}.DebugDBInitProvider\"\n            android:enabled=\"true\"\n            android:exported=\"false\" />\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "debug-db/src/main/java/com/amitshekhar/debug/DebugDBInitProvider.java",
    "content": "/*\n *\n *  *    Copyright (C) 2019 Amit Shekhar\n *  *    Copyright (C) 2011 Android Open Source Project\n *  *\n *  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n *  *    you may not use this file except in compliance with the License.\n *  *    You may obtain a copy of the License at\n *  *\n *  *        http://www.apache.org/licenses/LICENSE-2.0\n *  *\n *  *    Unless required by applicable law or agreed to in writing, software\n *  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  *    See the License for the specific language governing permissions and\n *  *    limitations under the License.\n *\n */\n\npackage com.amitshekhar.debug;\n\nimport android.content.ContentProvider;\nimport android.content.ContentValues;\nimport android.content.Context;\nimport android.content.pm.ProviderInfo;\nimport android.database.Cursor;\nimport android.net.Uri;\n\nimport com.amitshekhar.DebugDB;\nimport com.amitshekhar.debug.sqlite.DebugDBFactory;\n\n/**\n * Created by amitshekhar on 16/11/16.\n */\n\npublic class DebugDBInitProvider extends ContentProvider {\n\n\n    public DebugDBInitProvider() {\n    }\n\n    @Override\n    public boolean onCreate() {\n        DebugDB.initialize(getContext(), new DebugDBFactory());\n        return true;\n    }\n\n    @Override\n    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {\n        return null;\n    }\n\n    @Override\n    public String getType(Uri uri) {\n        return null;\n    }\n\n    @Override\n    public Uri insert(Uri uri, ContentValues values) {\n        return null;\n    }\n\n    @Override\n    public int delete(Uri uri, String selection, String[] selectionArgs) {\n        return 0;\n    }\n\n    @Override\n    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {\n        return 0;\n    }\n\n    @Override\n    public void attachInfo(Context context, ProviderInfo providerInfo) {\n        if (providerInfo == null) {\n            throw new NullPointerException(\"DebugDBInitProvider ProviderInfo cannot be null.\");\n        }\n        // So if the authorities equal the library internal ones, the developer forgot to set his applicationId\n        if (\"com.amitshekhar.debug.DebugDBInitProvider\".equals(providerInfo.authority)) {\n            throw new IllegalStateException(\"Incorrect provider authority in manifest. Most likely due to a \"\n                    + \"missing applicationId variable in application\\'s build.gradle.\");\n        }\n        super.attachInfo(context, providerInfo);\n    }\n\n}\n"
  },
  {
    "path": "debug-db/src/main/java/com/amitshekhar/debug/sqlite/DebugDBFactory.java",
    "content": "package com.amitshekhar.debug.sqlite;\n\nimport android.content.Context;\nimport android.database.sqlite.SQLiteDatabase;\n\nimport com.amitshekhar.sqlite.DBFactory;\nimport com.amitshekhar.sqlite.SQLiteDB;\n\npublic class DebugDBFactory implements DBFactory {\n\n    @Override\n    public SQLiteDB create(Context context, String path, String password) {\n        return new DebugSQLiteDB(SQLiteDatabase.openOrCreateDatabase(path, null));\n    }\n\n}\n"
  },
  {
    "path": "debug-db/src/main/java/com/amitshekhar/debug/sqlite/DebugSQLiteDB.java",
    "content": "package com.amitshekhar.debug.sqlite;\n\nimport android.content.ContentValues;\nimport android.database.Cursor;\nimport android.database.SQLException;\nimport android.database.sqlite.SQLiteDatabase;\n\nimport com.amitshekhar.sqlite.SQLiteDB;\n\npublic class DebugSQLiteDB implements SQLiteDB {\n\n    private final SQLiteDatabase database;\n\n    public DebugSQLiteDB(SQLiteDatabase database) {\n        this.database = database;\n    }\n\n    @Override\n    public int delete(String table, String whereClause, String[] whereArgs) {\n        return database.delete(table, whereClause, whereArgs);\n    }\n\n    @Override\n    public boolean isOpen() {\n        return database.isOpen();\n    }\n\n    @Override\n    public void close() {\n        database.close();\n    }\n\n    @Override\n    public Cursor rawQuery(String sql, String[] selectionArgs) {\n        return database.rawQuery(sql, selectionArgs);\n    }\n\n    @Override\n    public void execSQL(String sql) throws SQLException {\n        database.execSQL(sql);\n    }\n\n    @Override\n    public long insert(String table, String nullColumnHack, ContentValues values) {\n        return database.insert(table, nullColumnHack, values);\n    }\n\n    @Override\n    public int update(String table, ContentValues values, String whereClause, String[] whereArgs) {\n        return database.update(table, values, whereClause, whereArgs);\n    }\n\n    @Override\n    public int getVersion() {\n        return database.getVersion();\n    }\n}\n"
  },
  {
    "path": "debug-db/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">debug-db</string>\n</resources>\n"
  },
  {
    "path": "debug-db/src/test/java/com/amitshekhar/debug/ExampleUnitTest.java",
    "content": "package com.amitshekhar.debug;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\npublic class ExampleUnitTest {\n    @Test\n    public void addition_isCorrect() {\n        assertEquals(4, 2 + 2);\n    }\n}"
  },
  {
    "path": "debug-db-base/build.gradle",
    "content": "/*\n *\n *  *    Copyright (C) 2019 Amit Shekhar\n *  *    Copyright (C) 2011 Android Open Source Project\n *  *\n *  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n *  *    you may not use this file except in compliance with the License.\n *  *    You may obtain a copy of the License at\n *  *\n *  *        http://www.apache.org/licenses/LICENSE-2.0\n *  *\n *  *    Unless required by applicable law or agreed to in writing, software\n *  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  *    See the License for the specific language governing permissions and\n *  *    limitations under the License.\n *\n */\n\napply plugin: 'com.android.library'\n\nandroid {\n    compileSdk rootProject.ext.compileSdk\n    defaultConfig {\n        minSdk rootProject.ext.minSdk\n        targetSdk rootProject.ext.targetSdk\n        versionCode 1\n        versionName \"1.0\"\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n        resValue(\"string\", \"PORT_NUMBER\", \"8080\")\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n}\n\ndependencies {\n    implementation 'com.google.code.gson:gson:2.8.5'\n    implementation \"androidx.room:room-runtime:2.5.0\"\n    testImplementation 'junit:junit:4.13.2'\n    androidTestImplementation 'androidx.test:runner:1.5.2'\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'\n}"
  },
  {
    "path": "debug-db-base/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n-renamesourcefileattribute SourceFile\n\n-keepparameternames\n-keepattributes Exceptions,InnerClasses,Signature,Deprecated,EnclosingMethod\n\n# Preserve all annotations.\n\n-keepattributes *Annotation*\n\n# Preserve all public classes, and their public and protected fields and\n# methods.\n\n-keep public class * {\n    public protected *;\n}\n\n# Preserve all .class method names.\n\n-keepclassmembernames class * {\n    java.lang.Class class$(java.lang.String);\n    java.lang.Class class$(java.lang.String, boolean);\n}\n\n# Preserve all native method names and the names of their classes.\n\n-keepclasseswithmembernames class * {\n    native <methods>;\n}\n\n# Preserve the special static methods that are required in all enumeration\n# classes.\n\n-keepclassmembers class * extends java.lang.Enum {\n    public static **[] values();\n    public static ** valueOf(java.lang.String);\n}\n\n# Explicitly preserve all serialization members. The Serializable interface\n# is only a marker interface, so it wouldn't save them.\n# You can comment this out if your library doesn't use serialization.\n# If your code contains serializable classes that have to be backward\n# compatible, please refer to the manual.\n\n-keepclassmembers class * implements java.io.Serializable {\n    static final long serialVersionUID;\n    static final java.io.ObjectStreamField[] serialPersistentFields;\n    private void writeObject(java.io.ObjectOutputStream);\n    private void readObject(java.io.ObjectInputStream);\n    java.lang.Object writeReplace();\n    java.lang.Object readResolve();\n}\n\n# Your library may contain more items that need to be preserved;\n# typically classes that are dynamically created using Class.forName:\n\n# -keep public class mypackage.MyClass\n# -keep public interface mypackage.MyInterface\n# -keep public class * implements mypackage.MyInterface\n"
  },
  {
    "path": "debug-db-base/src/androidTest/java/com/amitshekhar/ExampleInstrumentedTest.java",
    "content": "/*\n *\n *  *    Copyright (C) 2019 Amit Shekhar\n *  *    Copyright (C) 2011 Android Open Source Project\n *  *\n *  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n *  *    you may not use this file except in compliance with the License.\n *  *    You may obtain a copy of the License at\n *  *\n *  *        http://www.apache.org/licenses/LICENSE-2.0\n *  *\n *  *    Unless required by applicable law or agreed to in writing, software\n *  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  *    See the License for the specific language governing permissions and\n *  *    limitations under the License.\n *\n */\n\npackage com.amitshekhar;\n\nimport android.content.Context;\nimport android.support.test.InstrumentationRegistry;\nimport android.support.test.runner.AndroidJUnit4;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\nimport static org.junit.Assert.*;\n\n/**\n * Instrumentation test, which will execute on an Android device.\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\n@RunWith(AndroidJUnit4.class)\npublic class ExampleInstrumentedTest {\n    @Test\n    public void useAppContext() throws Exception {\n        // Context of the app under test.\n        Context appContext = InstrumentationRegistry.getTargetContext();\n\n        assertEquals(\"com.amitshekhar.test\", appContext.getPackageName());\n    }\n}\n"
  },
  {
    "path": "debug-db-base/src/main/AndroidManifest.xml",
    "content": "<!--\n  ~ /*\n  ~  *    Copyright (C) 2019 Amit Shekhar\n  ~  *    Copyright (C) 2011 Android Open Source Project\n  ~  *\n  ~  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~  *    you may not use this file except in compliance with the License.\n  ~  *    You may obtain a copy of the License at\n  ~  *\n  ~  *        http://www.apache.org/licenses/LICENSE-2.0\n  ~  *\n  ~  *    Unless required by applicable law or agreed to in writing, software\n  ~  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~  *    See the License for the specific language governing permissions and\n  ~  *    limitations under the License.\n  ~  */\n  -->\n\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.amitshekhar\">\n\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n    <uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\" />\n\n</manifest>\n"
  },
  {
    "path": "debug-db-base/src/main/assets/app.js",
    "content": "$( document ).ready(function() {\n    getDBList();\n    $(\"#query\").keypress(function(e){\n        if(e.which == 13) {\n            queryFunction();\n        }\n    });\n    //update currently selected database\n    $( document ).on( \"click\", \"#db-list .list-group-item\", function() {\n        $(\"#db-list .list-group-item\").each(function() {\n            $(this).removeClass('selected');\n        });\n        $(this).addClass('selected');\n    });\n\n    //update currently table database\n    $( document ).on( \"click\", \"#table-list .list-group-item\", function() {\n        $(\"#table-list .list-group-item\").each(function() {\n            $(this).removeClass('selected');\n        });\n        $(this).addClass('selected');\n    });\n\n\n});\n\nvar isDatabaseSelected = true;\n\nfunction getData(tableName) {\n\n   $.ajax({url: \"getAllDataFromTheTable?tableName=\"+tableName, success: function(result){\n\n           result = JSON.parse(result);\n           inflateData(result);\n\n   }});\n\n}\n\nfunction queryFunction() {\n\n   var query = $('#query').val();\n\n   $.ajax({url: \"query?query=\"+escape(query), success: function(result){\n\n           result = JSON.parse(result);\n           inflateData(result);\n\n   }});\n\n}\n\nfunction downloadDb() {\n    if (isDatabaseSelected) {\n        $.ajax({url: \"downloadDb\", success: function(){\n             window.location = 'downloadDb';\n        }});\n    }\n}\n\nfunction deleteDb() {\n    if (isDatabaseSelected) {\n        $.ajax({url: \"deleteDb\", success: function(result){\n            result = JSON.parse(result);\n            if(result.isSuccessful){\n               console.log(\"Database deleted successfully\");\n               showSuccessInfo(\"Database Deleted Successfully\");\n               getDBList();\n            } else {\n               console.log(\"Database delete failed\");\n               showErrorInfo(\"Database Delete Failed\");\n            }\n        }});\n    }\n}\n\nfunction getDBList() {\n\n   $.ajax({url: \"getDbList\", success: function(result){\n\n           result = JSON.parse(result);\n           var dbList = result.rows;\n           $('#db-list').empty();\n           var isSelectionDone = false;\n           for(var count = 0; count < dbList.length; count++){\n             var dbName = dbList[count][0];\n             var isEncrypted = dbList[count][1];\n             var isDownloadable = dbList[count][2];\n             var dbAttribute = isEncrypted == \"true\" ? ' <span class=\"glyphicon glyphicon-lock\" aria-hidden=\"true\" style=\"color:blue\"></span>' : \"\";\n             if(dbName.indexOf(\"journal\") == -1 && dbName.indexOf(\"-wal\") == -1 && dbName.indexOf(\"-shm\") == -1){\n                $(\"#db-list\").append(\"<a href='#' id=\" + dbName + \" class='list-group-item' onClick='openDatabaseAndGetTableList(\\\"\"+ dbName + \"\\\", \\\"\"+ isDownloadable + \"\\\");'>\" + dbName + dbAttribute + \"</a>\");\n                if(!isSelectionDone){\n                    isSelectionDone = true;\n                    $('#db-list').find('a').trigger('click');\n                }\n             }\n           }\n\n   }});\n\n}\n\nvar lastTableName = getHashValue('table');\nfunction openDatabaseAndGetTableList(db, isDownloadable) {\n\n    if(\"APP_SHARED_PREFERENCES\" == db) {\n        $('#run-query').removeClass('active');\n        $('#run-query').addClass('disabled');\n        $('#selected-db-download').removeClass('active');\n        $('#selected-db-delete').removeClass('active');\n        $('#selected-db-download').addClass('disabled');\n        $('#selected-db-delete').addClass('disabled');\n        isDatabaseSelected = false;\n        $(\"#selected-db-info\").text(\"SharedPreferences\");\n    } else {\n        $('#run-query').removeClass('disabled');\n        $('#run-query').addClass('active');\n        $(\"#selected-db-info\").text(\"Selected Database : \"+db);\n        if(\"true\" == isDownloadable) {\n            $('#selected-db-download').addClass('active');\n            $('#selected-db-delete').addClass('active');\n            $('#selected-db-download').removeClass('disabled');\n            $('#selected-db-delete').removeClass('disabled');\n        } else {\n            $('#selected-db-download').removeClass('active');\n            $('#selected-db-delete').removeClass('active');\n            $('#selected-db-download').addClass('disabled');\n            $('#selected-db-delete').addClass('disabled');\n        }\n        isDatabaseSelected = true;\n    }\n\n\n   $.ajax({url: \"getTableList?database=\"+db, success: function(result){\n\n           result = JSON.parse(result);\n           var tableList = result.rows;\n           var dbVersion = result.dbVersion;\n           if(\"APP_SHARED_PREFERENCES\" != db) {\n                $(\"#selected-db-info\").text(\"Selected Database : \"+db +\" Version : \"+dbVersion);\n           }\n           $('#table-list').empty()\n           for(var count = 0; count < tableList.length; count++){\n                var tableName = tableList[count];\n\t\t\t\t$(\"#table-list\").append(\"<a href='#table=\" + tableName + \"' data-db-name='\" + db + \"' data-table-name='\" + tableName\n\t\t\t\t\t+ \"' class='list-group-item' onClick='getData(\\\"\" + tableName + \"\\\");'>\" + tableName + \"</a>\");\n\t\t\t}\n\n\t\t\tif (lastTableName !== null) {\n\t\t\t\t$('a[data-table-name=' + lastTableName + ']').trigger('click');\n\t\t\t}\n   }});\n\n}\n\nfunction inflateData(result){\n\n   if(result.isSuccessful){\n\n      if(!result.isSelectQuery){\n         showSuccessInfo(\"Query Executed Successfully\");\n         return;\n      }\n\n      var columnHeader = result.tableInfos;\n\n      // set function to return cell data for different usages like set, display, filter, search etc..\n      for(var i = 0; i < columnHeader.length; i++) {\n        columnHeader[i]['targets'] = i;\n        columnHeader[i]['data'] = function(row, type, val, meta) {\n            var dataType = row[meta.col].dataType;\n            if (type == \"sort\" && dataType == \"boolean\") {\n                return row[meta.col].value ? 1 : 0;\n            }\n            return row[meta.col].value;\n        }\n      }\n      var columnData = result.rows;\n       var tableId = \"#db-data\";\n        if ($.fn.DataTable.isDataTable(tableId) ) {\n          $(tableId).DataTable().destroy();\n        }\n\n       $(\"#db-data-div\").remove();\n       $(\"#parent-data-div\").append('<div id=\"db-data-div\"><table class=\"display nowrap\" cellpadding=\"0\" border=\"0\" cellspacing=\"0\" width=\"100%\" class=\"table table-striped table-bordered display\" id=\"db-data\"></table></div>');\n\n       var availableButtons;\n       if (result.isEditable) {\n            availableButtons = [\n                {\n                    text : 'Add',\n                    name : 'add' // don not change name\n                },\n                {\n                    extend: 'selected', // Bind to Selected row\n                    text: 'Edit',\n                    name: 'edit'        // do not change name\n                },\n                {\n                    extend: 'selected',\n                    text: 'Delete',\n                    name: 'delete'\n                }\n            ];\n       } else {\n            availableButtons = [];\n       }\n\n       $(tableId).dataTable({\n           \"data\": columnData,\n           \"columnDefs\": columnHeader,\n           'bPaginate': true,\n           'searching': true,\n           'bFilter': true,\n           'bInfo': true,\n           \"bSort\" : true,\n           \"scrollX\": true,\n           \"iDisplayLength\": 10,\n           \"dom\": \"Bfrtip\",\n            select: 'single',\n            altEditor: true,     // Enable altEditor\n            buttons: availableButtons\n       })\n\n       //attach row-updated listener\n       $(tableId).on('update-row.dt', function (e, updatedRowData, callback) {\n            var updatedRowDataArray = JSON.parse(updatedRowData);\n            //add value for each column\n            var data = columnHeader;\n            for(var i = 0; i < data.length; i++) {\n                data[i].value = updatedRowDataArray[i].value;\n                data[i].dataType = updatedRowDataArray[i].dataType;\n            }\n            //send update table data request to server\n            updateTableData(data, callback);\n       });\n\n\n       //attach delete-updated listener\n       $(tableId).on('delete-row.dt', function (e, updatedRowData, callback) {\n            var deleteRowDataArray = JSON.parse(updatedRowData);\n\n            console.log(deleteRowDataArray);\n\n            //add value for each column\n            var data = columnHeader;\n            for(var i = 0; i < data.length; i++) {\n                data[i].value = deleteRowDataArray[i].value;\n                data[i].dataType = deleteRowDataArray[i].dataType;\n\n            }\n\n            //send delete table data request to server\n            deleteTableData(data, callback);\n       });\n\n\n\n       $(tableId).on('add-row.dt', function (e, updatedRowData, callback) {\n                   var deleteRowDataArray = JSON.parse(updatedRowData);\n\n                   console.log(deleteRowDataArray);\n\n                   //add value for each column\n                   var data = columnHeader;\n                   for(var i = 0; i < data.length; i++) {\n                       data[i].value = deleteRowDataArray[i].value;\n                       data[i].dataType = deleteRowDataArray[i].dataType;\n                   }\n\n                   //send delete table data request to server\n                   addTableData(data, callback);\n              });\n\n       // hack to fix alignment issue when scrollX is enabled\n       $(\".dataTables_scrollHeadInner\").css({\"width\":\"100%\"});\n       $(\".table \").css({\"width\":\"100%\"});\n   }else{\n      if(!result.isSelectQuery){\n         showErrorInfo(\"Query Execution Failed\");\n      }else {\n         showErrorInfo(\"Some Error Occurred\");\n      }\n   }\n\n}\n\n//send update database request to server\nfunction updateTableData(updatedData, callback) {\n    //get currently selected element\n    var selectedTableElement = $(\"#table-list .list-group-item.selected\");\n\n    var filteredUpdatedData = updatedData.map(function(columnData){\n        return {\n            title: columnData.title,\n            isPrimary: columnData.isPrimary,\n            value: columnData.value,\n            dataType: columnData.dataType\n        }\n    });\n    //build request parameters\n    var requestParameters = {};\n    requestParameters.dbName = selectedTableElement.attr('data-db-name');\n    requestParameters.tableName = selectedTableElement.attr('data-table-name');;\n    requestParameters.updatedData = encodeURIComponent(JSON.stringify(filteredUpdatedData));\n\n    //execute request\n    $.ajax({\n        url: \"updateTableData\",\n        type: 'GET',\n        data: requestParameters,\n        success: function(response) {\n            response = JSON.parse(response);\n            if(response.isSuccessful){\n               console.log(\"Data updated successfully\");\n               callback(true);\n               showSuccessInfo(\"Data Updated Successfully\");\n            } else {\n               console.log(\"Data updated failed\");\n               callback(false);\n            }\n        }\n    })\n}\n\n\nfunction deleteTableData(deleteData, callback) {\n\n    var selectedTableElement = $(\"#table-list .list-group-item.selected\");\n        var filteredUpdatedData = deleteData.map(function(columnData){\n            return {\n                title: columnData.title,\n                isPrimary: columnData.isPrimary,\n                value: columnData.value,\n                dataType: columnData.dataType\n            }\n        });\n\n        //build request parameters\n        var requestParameters = {};\n        requestParameters.dbName = selectedTableElement.attr('data-db-name');\n        requestParameters.tableName = selectedTableElement.attr('data-table-name');;\n        requestParameters.deleteData = encodeURIComponent(JSON.stringify(filteredUpdatedData));\n\n        //execute request\n        $.ajax({\n            url: \"deleteTableData\",\n            type: 'GET',\n            data: requestParameters,\n            success: function(response) {\n                response = JSON.parse(response);\n                if(response.isSuccessful){\n                   console.log(\"Data deleted successfully\");\n                   callback(true);\n                   showSuccessInfo(\"Data Deleted Successfully\");\n                } else {\n                   console.log(\"Data delete failed\");\n                   callback(false);\n                }\n            }\n    })\n}\n\nfunction addTableData(deleteData, callback) {\n\n    var selectedTableElement = $(\"#table-list .list-group-item.selected\");\n    var filteredUpdatedData = deleteData.map(function(columnData){\n        return {\n            title: columnData.title,\n            isPrimary: columnData.isPrimary,\n            value: columnData.value,\n            dataType: columnData.dataType\n        }\n    });\n\n    console.log(filteredUpdatedData);\n\n    //build request parameters\n    var requestParameters = {};\n    requestParameters.dbName = selectedTableElement.attr('data-db-name');\n    requestParameters.tableName = selectedTableElement.attr('data-table-name');;\n    requestParameters.addData = encodeURIComponent(JSON.stringify(filteredUpdatedData));\n\n    console.log(requestParameters);\n\n    //execute request\n    $.ajax({\n        url: \"addTableData\",\n        type: 'GET',\n        data: requestParameters,\n        success: function(response) {\n            response = JSON.parse(response);\n            if(response.isSuccessful){\n               console.log(\"Data Added successfully\");\n               callback(true);\n               getData(requestParameters.tableName);\n               showSuccessInfo(\"Data Added Successfully\");\n            } else {\n               console.log(\"Data Adding failed\");\n               callback(false);\n            }\n        }\n    });\n}\n\nfunction showSuccessInfo(message){\n    var snackbarId = \"snackbar\";\n    var snackbarElement = $(\"#\"+snackbarId);\n    snackbarElement.addClass(\"show\");\n    snackbarElement.css({\"backgroundColor\": \"#5cb85c\"});\n    snackbarElement.html(message)\n    setTimeout(function(){\n        snackbarElement.removeClass(\"show\");\n    }, 3000);\n}\n\nfunction showErrorInfo(message){\n    var snackbarId = \"snackbar\";\n    var snackbarElement = $(\"#\"+snackbarId);\n    snackbarElement.addClass(\"show\");\n    snackbarElement.css({\"backgroundColor\": \"#d9534f\"});\n    snackbarElement.html(message)\n    setTimeout(function(){\n        snackbarElement.removeClass(\"show\");\n    }, 3000);\n}\n\nfunction getHashValue(key) {\n\tvar matches = location.hash.match(new RegExp(key + '=([^&]*)'));\n\treturn matches ? matches[1] : null;\n}\n"
  },
  {
    "path": "debug-db-base/src/main/assets/custom.css",
    "content": ".padding-fifty {\n   padding-top: 50px;\n}\n\n.padding-twenty {\n    padding-top: 20px;\n}\n\n.display-none {\n    display: none;\n}\n\n.list-group-item {\n    word-break: break-all;\n}\n\n.list-group-item.selected {\n    background: #dff0d8 !important;\n    color: #3c763d !important;\n    font-weight: bold;\n}\n\n#snackbar {\n    visibility: hidden;\n    min-width: 250px;\n    margin-left: -125px;\n    background-color: #5cb85c;\n    color: #fff;\n    text-align: center;\n    border-radius: 2px;\n    padding: 16px;\n    position: fixed;\n    z-index: 1;\n    left: 50%;\n    bottom: 30px;\n    font-size: 17px;\n}\n\n#snackbar.show {\n    visibility: visible;\n    -webkit-animation: fadein 0.5s, fadeout 0.5s 2.5s;\n    animation: fadein 0.5s, fadeout 0.5s 2.5s;\n}\n\n@-webkit-keyframes fadein {\n    from {bottom: 0; opacity: 0;}\n    to {bottom: 30px; opacity: 1;}\n}\n\n@keyframes fadein {\n    from {bottom: 0; opacity: 0;}\n    to {bottom: 30px; opacity: 1;}\n}\n\n@-webkit-keyframes fadeout {\n    from {bottom: 30px; opacity: 1;}\n    to {bottom: 0; opacity: 0;}\n}\n"
  },
  {
    "path": "debug-db-base/src/main/assets/dataTables.altEditor.free.js",
    "content": "/*! Datatables altEditor 1.0\n */\n/**\n * @summary     altEditor\n * @description Lightweight editor for DataTables\n * @version     1.0\n * @file        dataTables.editor.lite.js\n * @author      kingkode (www.kingkode.com)\n * @contact     www.kingkode.com/contact\n * @copyright   Copyright 2016 Kingkode\n *\n * This source file is free software, available under the following license:\n *   MIT license - http://datatables.net/license/mit\n *\n * This source file is distributed in the hope that it will be useful, but\n * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY\n * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.\n *\n * For details please refer to: http://www.kingkode.com\n */\n(function(factory) {\n    if (typeof define === 'function' && define.amd) {\n        // AMD\n        define(['jquery', 'datatables.net'], function($) {\n            return factory($, window, document);\n        });\n    } else if (typeof exports === 'object') {\n        // CommonJS\n        module.exports = function(root, $) {\n            if (!root) {\n                root = window;\n            }\n\n            if (!$ || !$.fn.dataTable) {\n                $ = require('datatables.net')(root, $).$;\n            }\n\n            return factory($, root, root.document);\n        };\n    } else {\n        // Browser\n        factory(jQuery, window, document);\n    }\n}(function($, window, document, undefined) {\n    'use strict';\n    var DataTable = $.fn.dataTable;\n\n\n    var _instance = 0;\n\n    /**\n     * altEditor provides modal editing of records for Datatables\n     *\n     * @class altEditor\n     * @constructor\n     * @param {object} oTD DataTables settings object\n     * @param {object} oConfig Configuration object for altEditor\n     */\n    var altEditor = function(dt, opts) {\n        if (!DataTable.versionCheck || !DataTable.versionCheck('1.10.8')) {\n            throw (\"Warning: altEditor requires DataTables 1.10.8 or greater\");\n        }\n\n        // User and defaults configuration object\n        this.c = $.extend(true, {},\n            DataTable.defaults.altEditor,\n            altEditor.defaults,\n            opts\n        );\n\n        /**\n         * @namespace Settings object which contains customisable information for altEditor instance\n         */\n        this.s = {\n            /** @type {DataTable.Api} DataTables' API instance */\n            dt: new DataTable.Api(dt),\n\n            /** @type {String} Unique namespace for events attached to the document */\n            namespace: '.altEditor' + (_instance++)\n        };\n\n\n        /**\n         * @namespace Common and useful DOM elements for the class instance\n         */\n        this.dom = {\n            /** @type {jQuery} altEditor handle */\n            modal: $('<div class=\"dt-altEditor-handle\"/>'),\n        };\n\n\n        /* Constructor logic */\n        this._constructor();\n    }\n\n\n\n    $.extend(altEditor.prototype, {\n        /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n         * Constructor\n         * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */\n\n        /**\n         * Initialise the RowReorder instance\n         *\n         * @private\n         */\n        _constructor: function() {\n            // console.log('altEditor Enabled')\n            var that = this;\n            var dt = this.s.dt;\n\n            this._setup();\n\n            dt.on('destroy.altEditor', function() {\n                dt.off('.altEditor');\n                $(dt.table().body()).off(that.s.namespace);\n                $(document.body).off(that.s.namespace);\n            });\n        },\n\n        /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n         * Private methods\n         * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */\n\n        /**\n         * Setup dom and bind button actions\n         *\n         * @private\n         */\n        _setup: function() {\n            // console.log('Setup');\n\n            var that = this;\n            var dt = this.s.dt;\n\n            // add modal\n            $('body').append('\\\n            <div class=\"modal fade\" id=\"altEditor-modal\" tabindex=\"-1\" role=\"dialog\">\\\n              <div class=\"modal-dialog\">\\\n                <div class=\"modal-content\">\\\n                  <div class=\"modal-header\">\\\n                    <button type=\"button\" class=\"close\" data-dismiss=\"modal\" aria-label=\"Close\"><span aria-hidden=\"true\">&times;</span></button>\\\n                    <h4 class=\"modal-title\"></h4>\\\n                  </div>\\\n                  <div class=\"modal-body\">\\\n                    <p></p>\\\n                  </div>\\\n                  <div class=\"modal-footer\">\\\n                    <button type=\"button\" class=\"btn btn-default\" data-dismiss=\"modal\">Close</button>\\\n                    <button type=\"button\" class=\"btn btn-primary\">Save changes</button>\\\n                  </div>\\\n                </div>\\\n              </div>\\\n            </div>');\n\n\n            // add Edit Button\n            if (this.s.dt.button('edit:name')) {\n                this.s.dt.button('edit:name').action(function(e, dt, node, config) {\n                    var rows = dt.rows({\n                        selected: true\n                    }).count();\n\n                    that._openEditModal();\n                });\n\n                $(document).on('click', '#editRowBtn', function(e) {\n                    e.preventDefault();\n                    e.stopPropagation();\n                    that._editRowData();\n                });\n\n            }\n\n            // add Delete Button\n            if (this.s.dt.button('delete:name')) {\n                this.s.dt.button('delete:name').action(function(e, dt, node, config) {\n                    var rows = dt.rows({\n                        selected: true\n                    }).count();\n\n                    that._openDeleteModal();\n                });\n\n                $(document).on('click', '#deleteRowBtn', function(e) {\n                    e.preventDefault();\n                    e.stopPropagation();\n                    that._deleteRow();\n                });\n            }\n\n            // add Add Button\n            if (this.s.dt.button('add:name')) {\n                this.s.dt.button('add:name').action(function(e, dt, node, config) {\n                    var rows = dt.rows({\n                        selected: true\n                    }).count();\n\n                    that._openAddModal();\n                });\n\n                $(document).on('click', '#addRowBtn', function(e) {\n                    e.preventDefault();\n                    e.stopPropagation();\n                    that._addRowData();\n                });\n            }\n\n        },\n\n        /**\n         * Emit an event on the DataTable for listeners\n         *\n         * @param  {string} name Event name\n         * @param  {array} args Event arguments\n         * @private\n         */\n        _emitEvent: function(name, args) {\n            this.s.dt.iterator('table', function(ctx, i) {\n                $(ctx.nTable).triggerHandler(name + '.dt', args);\n            });\n        },\n\n        /**\n         * Open Edit Modal for selected row\n         *\n         * @private\n         */\n        _openEditModal: function() {\n            var that = this;\n            var dt = this.s.dt;\n\n            var columnDefs = [];\n\n            for (var i = 0; i < dt.context[0].aoColumns.length; i++) {\n                columnDefs.push({\n                    title: dt.context[0].aoColumns[i].sTitle,\n                    dataType: dt.context[0].aoColumns[i].dataType,\n                    isPrimary: dt.context[0].aoColumns[i].isPrimary,\n                })\n            }\n\n            var adata = dt.rows({\n                selected: true\n            });\n\n\n            var data = \"\";\n\n            data += \"<form class='form-horizontal' name='altEditor-form' role='form'>\";\n\n            for (var j in columnDefs) {\n                var cellData = adata.data()[0][j];\n                var inputSectionHTML = \"<div class='form-group'><label for='__INPUT_NAME__' class='col-sm-4 control-label'>__INPUT_NAME__</label>__INPUT_HTML__</div>\";\n\n                var inputHTML = \"<div class='col-sm-7'><input data-type='__INPUT_DATA_TYPE__' __INPUT_READ_ONLY_ATTRIBUTE__ type='__INPUT_TYPE__'  id='__INPUT_NAME__' name='__INPUT_NAME__' placeholder='__INPUT_NAME__' style='overflow:hidden'  class='form-control' value='__INPUT_VALUE__'></div>\";\n\n                var option1Checked = \"\";\n                var option2Checked = \"checked\";\n                if (cellData.dataType == \"boolean\") {\n                    if(JSON.parse(cellData.value)) {\n                        option1Checked = \"checked\";\n                        option2Checked = \"\";\n                    }\n                    inputHTML = \"<div class='col-sm-7'><div class='checkbox'><label><label class='radio-inline'><input data-type='__INPUT_DATA_TYPE__' __OPTION_1_CHECKED__ type='radio' name='__INPUT_NAME__' id='__INPUT_NAME__' value='1'>true</label><label class='radio-inline'><input data-type='__INPUT_DATA_TYPE__' __OPTION_2_CHECKED__ type='radio' name='__INPUT_NAME__' id='__INPUT_NAME__' value='0'>false</label></div></div>\"\n                }\n                //set input type\n                var inputType = \"text\";\n                var inputReadOnlyAttribute = \"\";\n                switch (cellData.dataType) {\n                    case 'integer':\n                        inputType = \"number\";\n                        break;\n                    case 'real':\n                        inputType = \"number\";\n                        break;\n                    case 'boolean':\n                        inputType = \"checkbox\";\n                        break;\n                    case 'long':\n                        inputType = \"number\";\n                        break;\n                    case 'float':\n                        inputType = \"number\";\n                        break;\n                    case 'text':\n                        inputType = \"text\";\n                        break;\n                    case 'string_set':\n                        inputType = \"text\";\n                        break;\n                }\n                //set input to read-only if it is a primary key\n                if (columnDefs[j].isPrimary) {\n                    inputReadOnlyAttribute = \"readonly\"\n                }\n\n                //append input html\n                inputSectionHTML = inputSectionHTML.replace(/__INPUT_HTML__/g, inputHTML);\n                inputSectionHTML = inputSectionHTML.replace(/__INPUT_READ_ONLY_ATTRIBUTE__/g, inputReadOnlyAttribute);\n                inputSectionHTML = inputSectionHTML.replace(/__INPUT_TYPE__/g, inputType);\n                inputSectionHTML = inputSectionHTML.replace(/__INPUT_DATA_TYPE__/g, cellData.dataType);\n                inputSectionHTML = inputSectionHTML.replace(/__INPUT_VALUE__/g, cellData.value);\n                inputSectionHTML = inputSectionHTML.replace(/__INPUT_NAME__/g, columnDefs[j].title);\n                inputSectionHTML = inputSectionHTML.replace(/__OPTION_1_CHECKED__/g, option1Checked);\n                inputSectionHTML = inputSectionHTML.replace(/__OPTION_2_CHECKED__/g, option2Checked);\n                data += inputSectionHTML;\n            }\n            data += \"</form>\";\n\n\n            $('#altEditor-modal').on('show.bs.modal', function() {\n                $('#altEditor-modal').find('.modal-title').html('Edit Record');\n                $('#altEditor-modal').find('.modal-body').html('<pre>' + data + '</pre>');\n                $('#altEditor-modal').find('.modal-footer').html(\"<button type='button' data-content='remove' class='btn btn-default' data-dismiss='modal'>Close</button>\\\n               <button type='button' data-content='remove' class='btn btn-primary' id='editRowBtn'>Save Changes</button>\");\n            });\n\n            $('#altEditor-modal').modal('show');\n            $('#altEditor-modal input[0]').focus();\n\n        },\n\n        _editRowData: function() {\n            var that = this;\n            var dt = this.s.dt;\n\n            var data = [];\n\n            $('form[name=\"altEditor-form\"] input').each(function(i) {\n                var addToList = true;\n                var value = $(this).val();\n                if($(this).attr('type') == \"radio\" && $(this).prop('checked') == false) {\n                    addToList = false;\n                }\n                value = $(this).attr('type') == \"radio\" ? $(this).val() == \"1\" : value;\n                if (addToList){\n                    data.push({\n                        \"value\": value,\n                        \"dataType\": $(this).attr('data-type')\n                    });\n                }\n            });\n            var editButtonCurrentText = $(\"#editRowBtn\").text();\n            $(\"#editRowBtn\").addClass('disabled');\n            $(\"#editRowBtn\").text(\"Saving..\");\n            that._emitEvent(\"update-row\", [\n                JSON.stringify(data),\n                function(isUpdated) {\n\n\n                    //set error message and other properties based on whether update is successfull or not\n                    var alertAdditionClasses = \"alert-success\";\n                    var alertMessage = \"This record has been updated\";\n                    var alertHeading = \"Success\";\n                    if (!isUpdated) {\n                        alertAdditionClasses = \"alert-danger\";\n                        alertMessage = \"Error occurred while updating this record\";\n                        alertHeading = \"Error\";\n                    }\n\n                    //create alert element html and append it to modal\n                    var messageHTML = '\\\n                        <div class=\"alert __ALERT_ADDITION_CLASSES__\" role=\"alert\">\\\n                            <strong>__ALERT_HEADING__!</strong>\\\n                            __ALERT_MESSAGE__.\\\n                        </div>\\\n                    ';\n                    messageHTML = messageHTML.replace(/__ALERT_ADDITION_CLASSES__/g, alertAdditionClasses);\n                    messageHTML = messageHTML.replace(/__ALERT_HEADING__/g, alertHeading);\n                    messageHTML = messageHTML.replace(/__ALERT_MESSAGE__/g, alertMessage);\n                    $('#altEditor-modal .modal-body').append(messageHTML);\n\n                    //update datatable, if update is successfull\n                    if (isUpdated) {\n                        dt.row({\n                            selected: true\n                        }).data(data);\n                        //remove existing alert elements\n                        $('#altEditor-modal').modal('hide');\n                    }\n                    $(\"#editRowBtn\").removeClass('disabled');\n                    $(\"#editRowBtn\").text(editButtonCurrentText);\n                }\n            ]);\n        },\n\n\n        /**\n         * Open Delete Modal for selected row\n         *\n         * @private\n         */\n        _openDeleteModal: function() {\n            var that = this;\n            var dt = this.s.dt;\n\n            var columnDefs = [];\n\n            for (var i = 0; i < dt.context[0].aoColumns.length; i++) {\n                columnDefs.push({\n                    title: dt.context[0].aoColumns[i].sTitle\n                })\n            }\n\n            var adata = dt.rows({\n                selected: true\n            });\n\n            var data = \"\";\n\n            data += \"<form name='altEditor-form' role='form'>\";\n            for (var i in columnDefs) {\n                var cellData = adata.data()[0][i];\n\n                var inputType = \"text\";\n                switch (cellData.dataType) {\n                    case 'integer':\n                        inputType = \"number\";\n                        break;\n                    case 'real':\n                        inputType = \"number\";\n                        break;\n                    case 'boolean':\n                        inputType = \"checkbox\";\n                        break;\n                    case 'long':\n                        inputType = \"number\";\n                        break;\n                    case 'float':\n                        inputType = \"number\";\n                        break;\n                    case 'text':\n                        inputType = \"text\";\n                        break;\n                    case 'string_set':\n                        inputType = \"text\";\n                        break;\n                }\n\n                data += \"<div class='form-group'><label for='\" + columnDefs[i].title + \"'>\" + columnDefs[i].title + \" : </label><input  type='hidden' data-type='\" + inputType + \"'  id='\" + columnDefs[i].title + \"' name='\" + columnDefs[i].title + \"' placeholder='\" + columnDefs[i].title + \"' style='overflow:hidden'  class='form-control' value='\" + cellData.value + \"' >\" + cellData.value + \"</input></div>\";\n            }\n            data += \"</form>\";\n\n            $('#altEditor-modal').on('show.bs.modal', function() {\n                $('#altEditor-modal').find('.modal-title').html('Delete Record');\n                $('#altEditor-modal').find('.modal-body').html('<pre>' + data + '</pre>');\n                $('#altEditor-modal').find('.modal-footer').html(\"<button type='button' data-content='remove' class='btn btn-default' data-dismiss='modal'>Close</button>\\\n               <button type='button' data-content='remove' class='btn btn-danger' id='deleteRowBtn'>Delete</button>\");\n            });\n\n            $('#altEditor-modal').modal('show');\n            $('#altEditor-modal input[0]').focus();\n\n        },\n\n        _deleteRow: function() {\n            var that = this;\n            var dt = this.s.dt;\n\n            var data = [];\n\n            $('form[name=\"altEditor-form\"] input').each(function(i) {\n                var addToList = true;\n                var value = $(this).val();\n                value = $(this).val();\n\n                console.log(\"Value : \" + value);\n                if (addToList){\n                    data.push({\n                        \"value\": value,\n                        \"dataType\": $(this).attr('data-type')\n                    });\n                }\n            });\n\n            $('#altEditor-modal .modal-body .alert').remove();\n\n            var message = '<div class=\"alert alert-success\" role=\"alert\">\\\n           <strong>Success!</strong> This record has been deleted.\\\n         </div>';\n\n            $('#altEditor-modal .modal-body').append(message);\n\n\n            that._emitEvent(\"delete-row\", [\n                            JSON.stringify(data),\n                            function(isDeleted) {\n                               if (isDeleted) {\n                                    dt.row({\n                                        selected: true\n                                    }).remove();\n\n                                    dt.draw();\n                               }\n\n                                //remove existing alert elements\n                                $('#altEditor-modal').modal('hide');\n                            }\n                        ]);\n\n\n\n        },\n\n\n        /**\n         * Open Add Modal for selected row\n         *\n         * @private\n         */\n        _openAddModal: function() {\n            var that = this;\n            var dt = this.s.dt;\n\n            var columnDefs = [];\n\n            for (var i = 0; i < dt.context[0].aoColumns.length; i++) {\n                columnDefs.push({\n                    title: dt.context[0].aoColumns[i].sTitle,\n                    dataType: dt.context[0].aoColumns[i].sType,\n                    isPrimary: dt.context[0].aoColumns[i].isPrimary,\n                    value : \"\",\n                })\n            }\n\n\n           var data = \"\";\n\n           data += \"<form class='form-horizontal' name='altEditor-form' role='form'>\";\n\n           for (var j in columnDefs) {\n               var inputSectionHTML = \"<div class='form-group'><label for='__INPUT_NAME__' class='col-sm-4 control-label'>__INPUT_NAME__</label>__INPUT_HTML__</div>\";\n\n               var inputHTML = \"<div class='col-sm-7'><input data-type='__INPUT_DATA_TYPE__' __INPUT_READ_ONLY_ATTRIBUTE__ type='__INPUT_TYPE__'  id='__INPUT_NAME__' name='__INPUT_NAME__' placeholder='__INPUT_NAME__' style='overflow:hidden'  class='form-control' value='__INPUT_VALUE__'></div>\";\n\n               var option1Checked = \"\";\n               var option2Checked = \"checked\";\n               if (columnDefs[j].dataType == \"boolean\") {\n                   if(JSON.parse(columnDefs[j].value)) {\n                       option1Checked = \"checked\";\n                       option2Checked = \"\";\n                   }\n                   inputHTML = \"<div class='col-sm-7'><div class='checkbox'><label><label class='radio-inline'><input data-type='__INPUT_DATA_TYPE__' __OPTION_1_CHECKED__ type='radio' name='__INPUT_NAME__' id='__INPUT_NAME__' value='1'>true</label><label class='radio-inline'><input data-type='__INPUT_DATA_TYPE__' __OPTION_2_CHECKED__ type='radio' name='__INPUT_NAME__' id='__INPUT_NAME__' value='0'>false</label></div></div>\"\n               }\n               //set input type\n               var inputType = \"text\";\n               var inputReadOnlyAttribute = \"\";\n               switch (columnDefs[j].dataType) {\n                   case 'num':\n                       inputType = \"number\";\n                       break;\n                   case 'string':\n                       inputType = \"text\";\n                       break;\n               }\n               //set input to read-only if it is a primary key\n//               if (columnDefs[j].isPrimary) {\n//                   inputReadOnlyAttribute = \"readonly\"\n//               }\n\n               //append input html\n               inputSectionHTML = inputSectionHTML.replace(/__INPUT_HTML__/g, inputHTML);\n               inputSectionHTML = inputSectionHTML.replace(/__INPUT_READ_ONLY_ATTRIBUTE__/g, inputReadOnlyAttribute);\n               inputSectionHTML = inputSectionHTML.replace(/__INPUT_TYPE__/g, inputType);\n               inputSectionHTML = inputSectionHTML.replace(/__INPUT_DATA_TYPE__/g, columnDefs[j].dataType);\n               inputSectionHTML = inputSectionHTML.replace(/__INPUT_VALUE__/g, columnDefs[j].value);\n               inputSectionHTML = inputSectionHTML.replace(/__INPUT_NAME__/g, columnDefs[j].title);\n               inputSectionHTML = inputSectionHTML.replace(/__OPTION_1_CHECKED__/g, option1Checked);\n               inputSectionHTML = inputSectionHTML.replace(/__OPTION_2_CHECKED__/g, option2Checked);\n               data += inputSectionHTML;\n           }\n           data += \"</form>\";\n\n\n            $('#altEditor-modal').on('show.bs.modal', function() {\n                $('#altEditor-modal').find('.modal-title').html('Add Record');\n                $('#altEditor-modal').find('.modal-body').html('<pre>' + data + '</pre>');\n                $('#altEditor-modal').find('.modal-footer').html(\"<button type='button' data-content='remove' class='btn btn-default' data-dismiss='modal'>Close</button>\\\n               <button type='button' data-content='remove' class='btn btn-primary' id='addRowBtn'>Add Record</button>\");\n            });\n\n            $('#altEditor-modal').modal('show');\n            $('#altEditor-modal input[0]').focus();\n        },\n\n        _addRowData: function() {\n            console.log('add row')\n            var that = this;\n            var dt = this.s.dt;\n\n            var data = [];\n\n            $('form[name=\"altEditor-form\"] input').each(function(i) {\n                var addToList = true;\n                var value = $(this).val();\n                if($(this).attr('type') == \"radio\" && $(this).prop('checked') == false) {\n                    addToList = false;\n                }\n                value = $(this).attr('type') == \"radio\" ? $(this).val() == \"1\" : value;\n                if (addToList){\n                    data.push({\n                        \"value\": value,\n                        \"dataType\": $(this).attr('data-type')\n                    });\n                }\n            });\n            var editButtonCurrentText = $(\"#editRowBtn\").text();\n            $(\"#addRowBtn\").addClass('disabled');\n            $(\"#addRowBtn\").text(\"Saving..\");\n\n            console.log(JSON.stringify(data));\n\n            that._emitEvent(\"add-row\", [\n                JSON.stringify(data),\n                function(isAdded) {\n\n\n                    //set error message and other properties based on whether update is successfull or not\n                    var alertAdditionClasses = \"alert-success\";\n                    var alertMessage = \"This record has been added\";\n                    var alertHeading = \"Success\";\n                    if (!isAdded) {\n                        alertAdditionClasses = \"alert-danger\";\n                        alertMessage = \"Error occurred while adding this record\";\n                        alertHeading = \"Error\";\n                    }\n\n                    //create alert element html and append it to modal\n                    var messageHTML = '\\\n                        <div class=\"alert __ALERT_ADDITION_CLASSES__\" role=\"alert\">\\\n                            <strong>__ALERT_HEADING__!</strong>\\\n                            __ALERT_MESSAGE__.\\\n                        </div>\\\n                    ';\n                    messageHTML = messageHTML.replace(/__ALERT_ADDITION_CLASSES__/g, alertAdditionClasses);\n                    messageHTML = messageHTML.replace(/__ALERT_HEADING__/g, alertHeading);\n                    messageHTML = messageHTML.replace(/__ALERT_MESSAGE__/g, alertMessage);\n                    $('#altEditor-modal .modal-body').append(messageHTML);\n\n                    //update datatable, if update is successfull\n                    if (isAdded) {\n                        dt.row().data(data);\n                        //remove existing alert elements\n                        $('#altEditor-modal').modal('hide');\n                    }\n                    $(\"#addRowBtn\").removeClass('disabled');\n                    $(\"#addRowBtn\").text(editButtonCurrentText);\n                }\n            ]);\n        },\n\n        _getExecutionLocationFolder: function() {\n            var fileName = \"dataTables.altEditor.js\";\n            var scriptList = $(\"script[src]\");\n            var jsFileObject = $.grep(scriptList, function(el) {\n\n                if (el.src.indexOf(fileName) !== -1) {\n                    return el;\n                }\n            });\n            var jsFilePath = jsFileObject[0].src;\n            var jsFileDirectory = jsFilePath.substring(0, jsFilePath.lastIndexOf(\"/\") + 1);\n            return jsFileDirectory;\n        }\n    });\n\n\n\n    /**\n     * altEditor version\n     *\n     * @static\n     * @type      String\n     */\n    altEditor.version = '1.0';\n\n\n    /**\n     * altEditor defaults\n     *\n     * @namespace\n     */\n    altEditor.defaults = {\n        /** @type {Boolean} Ask user what they want to do, even for a single option */\n        alwaysAsk: false,\n\n        /** @type {string|null} What will trigger a focus */\n        focus: null, // focus, click, hover\n\n        /** @type {column-selector} Columns to provide auto fill for */\n        columns: '', // all\n\n        /** @type {boolean|null} Update the cells after a drag */\n        update: null, // false is editor given, true otherwise\n\n        /** @type {DataTable.Editor} Editor instance for automatic submission */\n        editor: null\n    };\n\n\n    /**\n     * Classes used by altEditor that are configurable\n     *\n     * @namespace\n     */\n    altEditor.classes = {\n        /** @type {String} Class used by the selection button */\n        btn: 'btn'\n    };\n\n\n    // Attach a listener to the document which listens for DataTables initialisation\n    // events so we can automatically initialise\n    $(document).on('preInit.dt.altEditor', function(e, settings, json) {\n        if (e.namespace !== 'dt') {\n            return;\n        }\n\n        var init = settings.oInit.altEditor;\n        var defaults = DataTable.defaults.altEditor;\n\n        if (init || defaults) {\n            var opts = $.extend({}, init, defaults);\n\n            if (init !== false) {\n                new altEditor(settings, opts);\n            }\n        }\n    });\n\n\n    // Alias for access\n    DataTable.altEditor = altEditor;\n\n    return altEditor;\n}));\n"
  },
  {
    "path": "debug-db-base/src/main/assets/index.html",
    "content": "<!DOCTYPE html>\n<!--\n  ~ /*\n  ~  *    Copyright (C) 2019 Amit Shekhar\n  ~  *    Copyright (C) 2011 Android Open Source Project\n  ~  *\n  ~  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~  *    you may not use this file except in compliance with the License.\n  ~  *    You may obtain a copy of the License at\n  ~  *\n  ~  *        http://www.apache.org/licenses/LICENSE-2.0\n  ~  *\n  ~  *    Unless required by applicable law or agreed to in writing, software\n  ~  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~  *    See the License for the specific language governing permissions and\n  ~  *    limitations under the License.\n  ~  */\n  -->\n\n<html lang=\"en\">\n<head>\n    <title>Android Debug Database</title>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n    <link rel='shortcut icon' href='favicon.ico' type='image/x-icon'>\n\n    <script src=\"jquery.min.js\"></script>\n    <script src=\"bootstrap.min.js\"></script>\n    <script src=\"jquery.dataTables.min.js\"></script>\n    <script src=\"dataTables.altEditor.free.js\"></script>\n    <script src=\"dataTables.buttons.min.js\"></script>\n    <script src=\"dataTables.select.min.js\"></script>\n    <script src=\"dataTables.responsive.min.js\"></script>\n\n    <link href=\"bootstrap.min.css\" rel=\"stylesheet\">\n    <link href=\"custom.css\" rel=\"stylesheet\">\n    <link href=\"jquery.dataTables.min.css\" rel=\"stylesheet\">\n    <link href=\"buttons.dataTables.min.css\" rel=\"stylesheet\">\n    <link href=\"select.dataTables.min.css\" rel=\"stylesheet\">\n    <link href=\"responsive.dataTables.min.css\" rel=\"stylesheet\">\n\n</head>\n<body>\n\n<!-- Navigation -->\n<nav class=\"navbar navbar-default navbar-fixed-top\" role=\"navigation\">\n    <div class=\"container\">\n        <!-- Brand and toggle get grouped for better mobile display -->\n        <div class=\"navbar-header\">\n            <button type=\"button\" class=\"navbar-toggle\" data-toggle=\"collapse\"\n                    data-target=\"#bs-example-navbar-collapse-1\">\n                <span class=\"sr-only\">Toggle navigation</span>\n                <span class=\"icon-bar\"></span>\n                <span class=\"icon-bar\"></span>\n                <span class=\"icon-bar\"></span>\n            </button>\n            <a class=\"navbar-brand\" href=\"index.html\">Android Debug Database</a>\n        </div>\n        <!-- Collect the nav links, forms, and other content for toggling -->\n        <div class=\"collapse navbar-collapse\" id=\"bs-example-navbar-collapse-1\">\n            <ul class=\"nav navbar-nav navbar-right\">\n                <li>\n                    <a href=\"https://github.com/amitshekhariitbhu/Android-Debug-Database\"\n                       target=\"_blank\">GitHub</a>\n                </li>\n                <li>\n                    <a href=\"https://twitter.com/amitiitbhu\"\n                       target=\"_blank\">Twitter</a>\n                </li>\n                <li>\n                    <a href=\"https://outcomeschool.com/blog\"\n                       target=\"_blank\">Outcome School Blog</a>\n                </li>\n                <li class=\"dropdown\">\n                    <a href=\"#\" class=\"dropdown-toggle\" data-toggle=\"dropdown\">Other Projects<b\n                            class=\"caret\"></b></a>\n                    <ul class=\"dropdown-menu\">\n                        <li>\n                            <a href=\"https://github.com/amitshekhariitbhu/Fast-Android-Networking\"\n                               target=\"_blank\">Fast Android Networking</a>\n                        </li>\n                        <li>\n                            <a href=\"https://github.com/amitshekhariitbhu/RxJava2-Android-Samples\"\n                               target=\"_blank\">RxJava2 Android Samples</a>\n                        </li>\n                        <li>\n                            <a href=\"https://github.com/amitshekhariitbhu/GlideBitmapPool\"\n                               target=\"_blank\">Glide Bitmap Pool</a>\n                        </li>\n                        <li>\n                            <a href=\"https://github.com/amitshekhariitbhu/awesome-android-complete-reference\"\n                               target=\"_blank\">Awesome Android Complete Reference</a>\n                        </li>\n                    </ul>\n                </li>\n            </ul>\n        </div>\n        <!-- /.navbar-collapse -->\n    </div>\n    <!-- /.container -->\n</nav>\n\n\n<div class=\"container padding-fifty\">\n\n    <div class=\"row padding-twenty\">\n        <div class=\"col-sm-12\">\n            <div class=\"form-group\">\n                <label for=\"query\">Query</label>\n                <input class=\"form-control\" id=\"query\">\n            </div>\n            <button id=\"selected-db-download\" type=\"button\" onclick=\"downloadDb()\"\n                    class=\"btn btn-info\">\n                Download\n            </button>\n            <button id=\"selected-db-delete\" type=\"button\" onclick=\"deleteDb()\" class=\"btn btn-info\">\n                Delete\n            </button>\n            <label id=\"selected-db-info\" class=\"panel-info\">Welcome</label>\n            <button id=\"run-query\" type=\"submit\" onclick=\"queryFunction()\"\n                    class=\"btn btn-primary pull-right disabled\">Run\n                Query\n            </button>\n        </div>\n    </div>\n\n\n    <div class=\"row padding-twenty\">\n\n        <div class=\"col-sm-2\">\n            <div class=\"panel panel-info\">\n                <div class=\"panel-heading\">Databases</div>\n            </div>\n            <div id=\"db-list\" class=\"list-group\">\n            </div>\n        </div>\n\n        <div class=\"col-sm-2\">\n            <div class=\"panel panel-info\">\n                <div class=\"panel-heading\">Tables</div>\n            </div>\n            <div id=\"table-list\" class=\"list-group\">\n            </div>\n        </div>\n\n        <div id=\"parent-data-div\" class=\"col-sm-8\">\n            <div class=\"panel panel-info\">\n                <div class=\"panel-heading\">Data</div>\n            </div>\n        </div>\n    </div>\n\n    <div id=\"snackbar\">Data Updated Successfully</div>\n\n</div>\n\n<script src=\"app.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "debug-db-base/src/main/java/com/amitshekhar/DebugDB.java",
    "content": "/*\n *\n *  *    Copyright (C) 2019 Amit Shekhar\n *  *    Copyright (C) 2011 Android Open Source Project\n *  *\n *  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n *  *    you may not use this file except in compliance with the License.\n *  *    You may obtain a copy of the License at\n *  *\n *  *        http://www.apache.org/licenses/LICENSE-2.0\n *  *\n *  *    Unless required by applicable law or agreed to in writing, software\n *  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  *    See the License for the specific language governing permissions and\n *  *    limitations under the License.\n *\n */\n\npackage com.amitshekhar;\n\nimport android.content.Context;\nimport android.util.Log;\nimport android.util.Pair;\n\nimport androidx.sqlite.db.SupportSQLiteDatabase;\n\nimport com.amitshekhar.server.ClientServer;\nimport com.amitshekhar.sqlite.DBFactory;\nimport com.amitshekhar.utils.NetworkUtils;\n\nimport java.io.File;\nimport java.util.HashMap;\n\n/**\n * Created by amitshekhar on 15/11/16.\n */\n\npublic class DebugDB {\n\n    private static final String TAG = DebugDB.class.getSimpleName();\n    private static final int DEFAULT_PORT = 8080;\n    private static ClientServer clientServer;\n    private static String addressLog = \"not available\";\n\n    private DebugDB() {\n        // This class in not publicly instantiable\n    }\n\n    public static void initialize(Context context, DBFactory dbFactory) {\n        int portNumber;\n\n        try {\n            portNumber = Integer.valueOf(context.getString(R.string.PORT_NUMBER));\n        } catch (NumberFormatException ex) {\n            Log.e(TAG, \"PORT_NUMBER should be integer\", ex);\n            portNumber = DEFAULT_PORT;\n            Log.i(TAG, \"Using Default port : \" + DEFAULT_PORT);\n        }\n\n        clientServer = new ClientServer(context, portNumber, dbFactory);\n        clientServer.start();\n        addressLog = NetworkUtils.getAddressLog(context, portNumber);\n        Log.d(TAG, addressLog);\n    }\n\n    public static String getAddressLog() {\n        Log.d(TAG, addressLog);\n        return addressLog;\n    }\n\n    public static void shutDown() {\n        if (clientServer != null) {\n            clientServer.stop();\n            clientServer = null;\n        }\n    }\n\n    public static void setCustomDatabaseFiles(HashMap<String, Pair<File, String>> customDatabaseFiles) {\n        if (clientServer != null) {\n            clientServer.setCustomDatabaseFiles(customDatabaseFiles);\n        }\n    }\n\n    public static void setInMemoryRoomDatabases(HashMap<String, SupportSQLiteDatabase> databases) {\n        if (clientServer != null) {\n            clientServer.setInMemoryRoomDatabases(databases);\n        }\n    }\n\n    public static boolean isServerRunning() {\n        return clientServer != null && clientServer.isRunning();\n    }\n\n}\n"
  },
  {
    "path": "debug-db-base/src/main/java/com/amitshekhar/model/Response.java",
    "content": "/*\n *\n *  *    Copyright (C) 2019 Amit Shekhar\n *  *    Copyright (C) 2011 Android Open Source Project\n *  *\n *  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n *  *    you may not use this file except in compliance with the License.\n *  *    You may obtain a copy of the License at\n *  *\n *  *        http://www.apache.org/licenses/LICENSE-2.0\n *  *\n *  *    Unless required by applicable law or agreed to in writing, software\n *  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  *    See the License for the specific language governing permissions and\n *  *    limitations under the License.\n *\n */\n\npackage com.amitshekhar.model;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Created by amitshekhar on 15/11/16.\n */\n\npublic class Response {\n\n    public List<Object> rows = new ArrayList<>();\n    public List<String> columns = new ArrayList<>();\n    public boolean isSuccessful;\n    public String error;\n    public int dbVersion;\n\n    public Response() {\n\n    }\n\n}\n"
  },
  {
    "path": "debug-db-base/src/main/java/com/amitshekhar/model/RowDataRequest.java",
    "content": "/*\n *\n *  *    Copyright (C) 2019 Amit Shekhar\n *  *    Copyright (C) 2011 Android Open Source Project\n *  *\n *  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n *  *    you may not use this file except in compliance with the License.\n *  *    You may obtain a copy of the License at\n *  *\n *  *        http://www.apache.org/licenses/LICENSE-2.0\n *  *\n *  *    Unless required by applicable law or agreed to in writing, software\n *  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  *    See the License for the specific language governing permissions and\n *  *    limitations under the License.\n *\n */\n\npackage com.amitshekhar.model;\n\n/**\n * Created by amitshekhar on 04/02/17.\n */\n\npublic class RowDataRequest {\n\n    public String title;\n    public boolean isPrimary;\n    public String dataType;\n    public String value;\n\n}\n"
  },
  {
    "path": "debug-db-base/src/main/java/com/amitshekhar/model/TableDataResponse.java",
    "content": "/*\n *\n *  *    Copyright (C) 2019 Amit Shekhar\n *  *    Copyright (C) 2011 Android Open Source Project\n *  *\n *  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n *  *    you may not use this file except in compliance with the License.\n *  *    You may obtain a copy of the License at\n *  *\n *  *        http://www.apache.org/licenses/LICENSE-2.0\n *  *\n *  *    Unless required by applicable law or agreed to in writing, software\n *  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  *    See the License for the specific language governing permissions and\n *  *    limitations under the License.\n *\n */\n\npackage com.amitshekhar.model;\n\nimport java.util.List;\n\n/**\n * Created by amitshekhar on 04/02/17.\n */\n\npublic class TableDataResponse {\n\n    public List<TableInfo> tableInfos;\n    public boolean isSuccessful;\n    public List<Object> rows;\n    public String errorMessage;\n    public boolean isEditable;\n    public boolean isSelectQuery;\n\n    public static class TableInfo {\n        public String title;\n        public boolean isPrimary;\n    }\n\n    public static class ColumnData {\n        public String dataType;\n        public Object value;\n    }\n\n}\n"
  },
  {
    "path": "debug-db-base/src/main/java/com/amitshekhar/model/UpdateRowResponse.java",
    "content": "/*\n *\n *  *    Copyright (C) 2019 Amit Shekhar\n *  *    Copyright (C) 2011 Android Open Source Project\n *  *\n *  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n *  *    you may not use this file except in compliance with the License.\n *  *    You may obtain a copy of the License at\n *  *\n *  *        http://www.apache.org/licenses/LICENSE-2.0\n *  *\n *  *    Unless required by applicable law or agreed to in writing, software\n *  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  *    See the License for the specific language governing permissions and\n *  *    limitations under the License.\n *\n */\n\npackage com.amitshekhar.model;\n\n/**\n * Created by amitshekhar on 04/02/17.\n */\n\npublic class UpdateRowResponse {\n\n    public boolean isSuccessful;\n\n}\n"
  },
  {
    "path": "debug-db-base/src/main/java/com/amitshekhar/server/ClientServer.java",
    "content": "/*\n *\n *  *    Copyright (C) 2019 Amit Shekhar\n *  *    Copyright (C) 2011 Android Open Source Project\n *  *\n *  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n *  *    you may not use this file except in compliance with the License.\n *  *    You may obtain a copy of the License at\n *  *\n *  *        http://www.apache.org/licenses/LICENSE-2.0\n *  *\n *  *    Unless required by applicable law or agreed to in writing, software\n *  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  *    See the License for the specific language governing permissions and\n *  *    limitations under the License.\n *\n */\n\npackage com.amitshekhar.server;\n\n/**\n * Created by amitshekhar on 15/11/16.\n */\n\nimport android.content.Context;\nimport android.util.Log;\nimport android.util.Pair;\n\nimport androidx.sqlite.db.SupportSQLiteDatabase;\n\nimport com.amitshekhar.sqlite.DBFactory;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.ServerSocket;\nimport java.net.Socket;\nimport java.net.SocketException;\nimport java.util.HashMap;\n\npublic class ClientServer implements Runnable {\n\n    private static final String TAG = \"ClientServer\";\n\n    private final int mPort;\n    private final RequestHandler mRequestHandler;\n    private boolean mIsRunning;\n    private ServerSocket mServerSocket;\n\n    public ClientServer(Context context, int port, DBFactory dbFactory) {\n        mRequestHandler = new RequestHandler(context, dbFactory);\n        mPort = port;\n    }\n\n    public void start() {\n        mIsRunning = true;\n        new Thread(this).start();\n    }\n\n    public void stop() {\n        try {\n            mIsRunning = false;\n            if (null != mServerSocket) {\n                mServerSocket.close();\n                mServerSocket = null;\n            }\n        } catch (Exception e) {\n            Log.e(TAG, \"Error closing the server socket.\", e);\n        }\n    }\n\n    @Override\n    public void run() {\n        try {\n            mServerSocket = new ServerSocket(mPort);\n            while (mIsRunning) {\n                Socket socket = mServerSocket.accept();\n                mRequestHandler.handle(socket);\n                socket.close();\n            }\n        } catch (SocketException e) {\n            // The server was stopped; ignore.\n        } catch (IOException e) {\n            Log.e(TAG, \"Web server error.\", e);\n        } catch (Exception ignore) {\n            Log.e(TAG, \"Exception.\", ignore);\n        }\n    }\n\n    public void setCustomDatabaseFiles(HashMap<String, Pair<File, String>> customDatabaseFiles) {\n        mRequestHandler.setCustomDatabaseFiles(customDatabaseFiles);\n    }\n\n    public void setInMemoryRoomDatabases(HashMap<String, SupportSQLiteDatabase> databases) {\n        mRequestHandler.setInMemoryRoomDatabases(databases);\n    }\n\n    public boolean isRunning() {\n        return mIsRunning;\n    }\n}\n"
  },
  {
    "path": "debug-db-base/src/main/java/com/amitshekhar/server/RequestHandler.java",
    "content": "/*\n *\n *  *    Copyright (C) 2019 Amit Shekhar\n *  *    Copyright (C) 2011 Android Open Source Project\n *  *\n *  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n *  *    you may not use this file except in compliance with the License.\n *  *    You may obtain a copy of the License at\n *  *\n *  *        http://www.apache.org/licenses/LICENSE-2.0\n *  *\n *  *    Unless required by applicable law or agreed to in writing, software\n *  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  *    See the License for the specific language governing permissions and\n *  *    limitations under the License.\n *\n */\n\npackage com.amitshekhar.server;\n\nimport android.content.Context;\nimport android.content.res.AssetManager;\nimport android.net.Uri;\nimport android.text.TextUtils;\nimport android.util.Pair;\n\nimport androidx.sqlite.db.SupportSQLiteDatabase;\n\nimport com.amitshekhar.model.Response;\nimport com.amitshekhar.model.RowDataRequest;\nimport com.amitshekhar.model.TableDataResponse;\nimport com.amitshekhar.model.UpdateRowResponse;\nimport com.amitshekhar.sqlite.DBFactory;\nimport com.amitshekhar.sqlite.InMemoryDebugSQLiteDB;\nimport com.amitshekhar.sqlite.SQLiteDB;\nimport com.amitshekhar.utils.Constants;\nimport com.amitshekhar.utils.DatabaseFileProvider;\nimport com.amitshekhar.utils.DatabaseHelper;\nimport com.amitshekhar.utils.PrefHelper;\nimport com.amitshekhar.utils.Utils;\nimport com.google.gson.Gson;\nimport com.google.gson.GsonBuilder;\nimport com.google.gson.reflect.TypeToken;\n\nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.io.PrintStream;\nimport java.net.Socket;\nimport java.net.URLDecoder;\nimport java.util.HashMap;\nimport java.util.List;\n\n/**\n * Created by amitshekhar on 06/02/17.\n */\n\npublic class RequestHandler {\n\n    private final Context mContext;\n    private final Gson mGson;\n    private final AssetManager mAssets;\n    private final DBFactory mDbFactory;\n    private boolean isDbOpened;\n    private SQLiteDB sqLiteDB;\n    private HashMap<String, Pair<File, String>> mDatabaseFiles;\n    private HashMap<String, Pair<File, String>> mCustomDatabaseFiles;\n    private String mSelectedDatabase = null;\n    private HashMap<String, SupportSQLiteDatabase> mRoomInMemoryDatabases = new HashMap<>();\n\n    public RequestHandler(Context context, DBFactory dbFactory) {\n        mContext = context;\n        mAssets = context.getResources().getAssets();\n        mGson = new GsonBuilder().serializeNulls().create();\n        mDbFactory = dbFactory;\n    }\n\n    public void handle(Socket socket) throws IOException {\n        BufferedReader reader = null;\n        PrintStream output = null;\n        try {\n            String route = null;\n\n            // Read HTTP headers and parse out the route.\n            reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));\n            String line;\n            while (!TextUtils.isEmpty(line = reader.readLine())) {\n                if (line.startsWith(\"GET /\")) {\n                    int start = line.indexOf('/') + 1;\n                    int end = line.indexOf(' ', start);\n                    route = line.substring(start, end);\n                    break;\n                }\n            }\n\n            // Output stream that we send the response to\n            output = new PrintStream(socket.getOutputStream());\n\n            if (route == null || route.isEmpty()) {\n                route = \"index.html\";\n            }\n\n            byte[] bytes;\n\n            if (route.startsWith(\"getDbList\")) {\n                final String response = getDBListResponse();\n                bytes = response.getBytes();\n            } else if (route.startsWith(\"getAllDataFromTheTable\")) {\n                final String response = getAllDataFromTheTableResponse(route);\n                bytes = response.getBytes();\n            } else if (route.startsWith(\"getTableList\")) {\n                final String response = getTableListResponse(route);\n                bytes = response.getBytes();\n            } else if (route.startsWith(\"addTableData\")) {\n                final String response = addTableDataAndGetResponse(route);\n                bytes = response.getBytes();\n            } else if (route.startsWith(\"updateTableData\")) {\n                final String response = updateTableDataAndGetResponse(route);\n                bytes = response.getBytes();\n            } else if (route.startsWith(\"deleteTableData\")) {\n                final String response = deleteTableDataAndGetResponse(route);\n                bytes = response.getBytes();\n            } else if (route.startsWith(\"query\")) {\n                final String response = executeQueryAndGetResponse(route);\n                bytes = response.getBytes();\n            } else if (route.startsWith(\"deleteDb\")) {\n                final String response = deleteSelectedDatabaseAndGetResponse();\n                bytes = response.getBytes();\n            } else if (route.startsWith(\"downloadDb\")) {\n                bytes = Utils.getDatabase(mSelectedDatabase, mDatabaseFiles);\n            } else {\n                bytes = Utils.loadContent(route, mAssets);\n            }\n\n            if (null == bytes) {\n                writeServerError(output);\n                return;\n            }\n\n            // Send out the content.\n            output.println(\"HTTP/1.0 200 OK\");\n            output.println(\"Content-Type: \" + Utils.detectMimeType(route));\n\n            if (route.startsWith(\"downloadDb\")) {\n                output.println(\"Content-Disposition: attachment; filename=\" + mSelectedDatabase);\n            } else {\n                output.println(\"Content-Length: \" + bytes.length);\n            }\n            output.println();\n            output.write(bytes);\n            output.flush();\n        } finally {\n            try {\n                if (null != output) {\n                    output.close();\n                }\n                if (null != reader) {\n                    reader.close();\n                }\n            } catch (Exception e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    public void setCustomDatabaseFiles(HashMap<String, Pair<File, String>> customDatabaseFiles) {\n        mCustomDatabaseFiles = customDatabaseFiles;\n    }\n\n    public void setInMemoryRoomDatabases(HashMap<String, SupportSQLiteDatabase> databases) {\n        mRoomInMemoryDatabases = databases;\n    }\n\n    private void writeServerError(PrintStream output) {\n        output.println(\"HTTP/1.0 500 Internal Server Error\");\n        output.flush();\n    }\n\n    private void openDatabase(String database) {\n        closeDatabase();\n        if (mRoomInMemoryDatabases.containsKey(database)) {\n            sqLiteDB = new InMemoryDebugSQLiteDB(mRoomInMemoryDatabases.get(database));\n        } else {\n            File databaseFile = mDatabaseFiles.get(database).first;\n            String password = mDatabaseFiles.get(database).second;\n            sqLiteDB = mDbFactory.create(mContext, databaseFile.getAbsolutePath(), password);\n        }\n        isDbOpened = true;\n    }\n\n    private void closeDatabase() {\n        if (sqLiteDB != null && sqLiteDB.isOpen()) {\n            sqLiteDB.close();\n        }\n        sqLiteDB = null;\n        isDbOpened = false;\n    }\n\n    private String getDBListResponse() {\n        mDatabaseFiles = DatabaseFileProvider.getDatabaseFiles(mContext);\n        if (mCustomDatabaseFiles != null) {\n            mDatabaseFiles.putAll(mCustomDatabaseFiles);\n        }\n        Response response = new Response();\n        if (mDatabaseFiles != null) {\n            for (HashMap.Entry<String, Pair<File, String>> entry : mDatabaseFiles.entrySet()) {\n                String[] dbEntry = {entry.getKey(), !entry.getValue().second.equals(\"\") ? \"true\" : \"false\", \"true\"};\n                response.rows.add(dbEntry);\n            }\n        }\n        if (mRoomInMemoryDatabases != null) {\n            for (HashMap.Entry<String, SupportSQLiteDatabase> entry : mRoomInMemoryDatabases.entrySet()) {\n                String[] dbEntry = {entry.getKey(), \"false\", \"false\"};\n                response.rows.add(dbEntry);\n            }\n        }\n        response.rows.add(new String[]{Constants.APP_SHARED_PREFERENCES, \"false\", \"false\"});\n        response.isSuccessful = true;\n        return mGson.toJson(response);\n    }\n\n    private String getAllDataFromTheTableResponse(String route) {\n\n        String tableName = null;\n\n        if (route.contains(\"?tableName=\")) {\n            tableName = route.substring(route.indexOf(\"=\") + 1, route.length());\n        }\n\n        TableDataResponse response;\n\n        if (isDbOpened) {\n            String sql = \"SELECT * FROM \" + tableName;\n            response = DatabaseHelper.getTableData(sqLiteDB, sql, tableName);\n        } else {\n            response = PrefHelper.getAllPrefData(mContext, tableName);\n        }\n\n        return mGson.toJson(response);\n\n    }\n\n    private String executeQueryAndGetResponse(String route) {\n        String query = null;\n        String data = null;\n        String first;\n        try {\n            if (route.contains(\"?query=\")) {\n                query = route.substring(route.indexOf(\"=\") + 1, route.length());\n            }\n            try {\n                query = URLDecoder.decode(query, \"UTF-8\");\n            } catch (Exception e) {\n                e.printStackTrace();\n            }\n\n            if (query != null) {\n                String[] statements = query.split(\";\");\n\n                for (int i = 0; i < statements.length; i++) {\n\n                    String aQuery = statements[i].trim();\n                    first = aQuery.split(\" \")[0].toLowerCase();\n                    if (first.equals(\"select\") || first.equals(\"pragma\")) {\n                        TableDataResponse response = DatabaseHelper.getTableData(sqLiteDB, aQuery, null);\n                        data = mGson.toJson(response);\n                        if (!response.isSuccessful) {\n                            break;\n                        }\n                    } else {\n                        TableDataResponse response = DatabaseHelper.exec(sqLiteDB, aQuery);\n                        data = mGson.toJson(response);\n                        if (!response.isSuccessful) {\n                            break;\n                        }\n                    }\n                }\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n\n        if (data == null) {\n            Response response = new Response();\n            response.isSuccessful = false;\n            data = mGson.toJson(response);\n        }\n\n        return data;\n    }\n\n    private String getTableListResponse(String route) {\n        String database = null;\n        if (route.contains(\"?database=\")) {\n            database = route.substring(route.indexOf(\"=\") + 1, route.length());\n        }\n\n        Response response;\n\n        if (Constants.APP_SHARED_PREFERENCES.equals(database)) {\n            response = PrefHelper.getAllPrefTableName(mContext);\n            closeDatabase();\n            mSelectedDatabase = Constants.APP_SHARED_PREFERENCES;\n        } else {\n            openDatabase(database);\n            response = DatabaseHelper.getAllTableName(sqLiteDB);\n            mSelectedDatabase = database;\n        }\n        return mGson.toJson(response);\n    }\n\n\n    private String addTableDataAndGetResponse(String route) {\n        UpdateRowResponse response;\n        try {\n            Uri uri = Uri.parse(URLDecoder.decode(route, \"UTF-8\"));\n            String tableName = uri.getQueryParameter(\"tableName\");\n            String updatedData = uri.getQueryParameter(\"addData\");\n            List<RowDataRequest> rowDataRequests = mGson.fromJson(updatedData, new TypeToken<List<RowDataRequest>>() {\n            }.getType());\n            if (Constants.APP_SHARED_PREFERENCES.equals(mSelectedDatabase)) {\n                response = PrefHelper.addOrUpdateRow(mContext, tableName, rowDataRequests);\n            } else {\n                response = DatabaseHelper.addRow(sqLiteDB, tableName, rowDataRequests);\n            }\n            return mGson.toJson(response);\n        } catch (Exception e) {\n            e.printStackTrace();\n            response = new UpdateRowResponse();\n            response.isSuccessful = false;\n            return mGson.toJson(response);\n        }\n    }\n\n    private String updateTableDataAndGetResponse(String route) {\n        UpdateRowResponse response;\n        try {\n            Uri uri = Uri.parse(URLDecoder.decode(route, \"UTF-8\"));\n            String tableName = uri.getQueryParameter(\"tableName\");\n            String updatedData = uri.getQueryParameter(\"updatedData\");\n            List<RowDataRequest> rowDataRequests = mGson.fromJson(updatedData, new TypeToken<List<RowDataRequest>>() {\n            }.getType());\n            if (Constants.APP_SHARED_PREFERENCES.equals(mSelectedDatabase)) {\n                response = PrefHelper.addOrUpdateRow(mContext, tableName, rowDataRequests);\n            } else {\n                response = DatabaseHelper.updateRow(sqLiteDB, tableName, rowDataRequests);\n            }\n            return mGson.toJson(response);\n        } catch (Exception e) {\n            e.printStackTrace();\n            response = new UpdateRowResponse();\n            response.isSuccessful = false;\n            return mGson.toJson(response);\n        }\n    }\n\n\n    private String deleteTableDataAndGetResponse(String route) {\n        UpdateRowResponse response;\n        try {\n            Uri uri = Uri.parse(URLDecoder.decode(route, \"UTF-8\"));\n            String tableName = uri.getQueryParameter(\"tableName\");\n            String updatedData = uri.getQueryParameter(\"deleteData\");\n            List<RowDataRequest> rowDataRequests = mGson.fromJson(updatedData, new TypeToken<List<RowDataRequest>>() {\n            }.getType());\n            if (Constants.APP_SHARED_PREFERENCES.equals(mSelectedDatabase)) {\n                response = PrefHelper.deleteRow(mContext, tableName, rowDataRequests);\n            } else {\n                response = DatabaseHelper.deleteRow(sqLiteDB, tableName, rowDataRequests);\n            }\n            return mGson.toJson(response);\n        } catch (Exception e) {\n            e.printStackTrace();\n            response = new UpdateRowResponse();\n            response.isSuccessful = false;\n            return mGson.toJson(response);\n        }\n    }\n\n    private String deleteSelectedDatabaseAndGetResponse() {\n        UpdateRowResponse response = new UpdateRowResponse();\n\n        if (mSelectedDatabase == null || !mDatabaseFiles.containsKey(mSelectedDatabase)) {\n            response.isSuccessful = false;\n            return mGson.toJson(response);\n        }\n\n        try {\n            closeDatabase();\n\n            File dbFile = mDatabaseFiles.get(mSelectedDatabase).first;\n            response.isSuccessful = dbFile.delete();\n\n            if (response.isSuccessful) {\n                mDatabaseFiles.remove(mSelectedDatabase);\n                mCustomDatabaseFiles.remove(mSelectedDatabase);\n            }\n\n            return mGson.toJson(response);\n        } catch (Exception e) {\n            e.printStackTrace();\n            response.isSuccessful = false;\n            return mGson.toJson(response);\n        }\n    }\n}\n"
  },
  {
    "path": "debug-db-base/src/main/java/com/amitshekhar/sqlite/DBFactory.java",
    "content": "package com.amitshekhar.sqlite;\n\nimport android.content.Context;\n\npublic interface DBFactory {\n\n    SQLiteDB create(Context context, String path, String password);\n\n}\n"
  },
  {
    "path": "debug-db-base/src/main/java/com/amitshekhar/sqlite/InMemoryDebugSQLiteDB.java",
    "content": "package com.amitshekhar.sqlite;\n\nimport android.content.ContentValues;\nimport android.database.Cursor;\nimport android.database.SQLException;\n\nimport androidx.sqlite.db.SupportSQLiteDatabase;\n\n/**\n * Created by anandgaurav on 12/02/18.\n */\n\npublic class InMemoryDebugSQLiteDB implements SQLiteDB {\n\n    private final SupportSQLiteDatabase database;\n\n    public InMemoryDebugSQLiteDB(SupportSQLiteDatabase database) {\n        this.database = database;\n    }\n\n    @Override\n    public int delete(String table, String whereClause, String[] whereArgs) {\n        return database.delete(table, whereClause, whereArgs);\n    }\n\n    @Override\n    public boolean isOpen() {\n        return database.isOpen();\n    }\n\n    @Override\n    public void close() {\n        // no ops\n    }\n\n    @Override\n    public Cursor rawQuery(String sql, String[] selectionArgs) {\n        return database.query(sql, selectionArgs);\n    }\n\n    @Override\n    public void execSQL(String sql) throws SQLException {\n        database.execSQL(sql);\n    }\n\n    @Override\n    public long insert(String table, String nullColumnHack, ContentValues values) {\n        return database.insert(table, 0, values);\n    }\n\n    @Override\n    public int update(String table, ContentValues values, String whereClause, String[] whereArgs) {\n        return database.update(table, 0, values, whereClause, whereArgs);\n    }\n\n    @Override\n    public int getVersion() {\n        return database.getVersion();\n    }\n}\n"
  },
  {
    "path": "debug-db-base/src/main/java/com/amitshekhar/sqlite/SQLiteDB.java",
    "content": "package com.amitshekhar.sqlite;\n\nimport android.content.ContentValues;\nimport android.database.Cursor;\nimport android.database.SQLException;\n\n\n/**\n * Created by anandgaurav on 12/02/18.\n */\n\npublic interface SQLiteDB {\n\n    int delete(String table, String whereClause, String[] whereArgs);\n\n    boolean isOpen();\n\n    void close();\n\n    Cursor rawQuery(String sql, String[] selectionArgs);\n\n    void execSQL(String sql) throws SQLException;\n\n    long insert(String table, String nullColumnHack, ContentValues values);\n\n    int update(String table, ContentValues values, String whereClause, String[] whereArgs);\n\n    int getVersion();\n\n}\n"
  },
  {
    "path": "debug-db-base/src/main/java/com/amitshekhar/utils/Constants.java",
    "content": "/*\n *\n *  *    Copyright (C) 2019 Amit Shekhar\n *  *    Copyright (C) 2011 Android Open Source Project\n *  *\n *  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n *  *    you may not use this file except in compliance with the License.\n *  *    You may obtain a copy of the License at\n *  *\n *  *        http://www.apache.org/licenses/LICENSE-2.0\n *  *\n *  *    Unless required by applicable law or agreed to in writing, software\n *  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  *    See the License for the specific language governing permissions and\n *  *    limitations under the License.\n *\n */\n\npackage com.amitshekhar.utils;\n\n/**\n * Created by amitshekhar on 16/11/16.\n */\n\npublic final class Constants {\n\n    private Constants() {\n        // This class in not publicly instantiable\n    }\n\n    public static final String APP_SHARED_PREFERENCES = \"APP_SHARED_PREFERENCES\";\n    public static final String PK = \"pk\";\n    public static final String NAME = \"name\";\n    public static final String NULL = \"null\";\n\n}\n"
  },
  {
    "path": "debug-db-base/src/main/java/com/amitshekhar/utils/ConverterUtils.java",
    "content": "/*\n *\n *  *    Copyright (C) 2019 Amit Shekhar\n *  *    Copyright (C) 2011 Android Open Source Project\n *  *\n *  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n *  *    you may not use this file except in compliance with the License.\n *  *    You may obtain a copy of the License at\n *  *\n *  *        http://www.apache.org/licenses/LICENSE-2.0\n *  *\n *  *    Unless required by applicable law or agreed to in writing, software\n *  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  *    See the License for the specific language governing permissions and\n *  *    limitations under the License.\n *\n */\n\npackage com.amitshekhar.utils;\n\nimport java.io.UnsupportedEncodingException;\n\n/**\n * Created by amitshekhar on 06/02/17.\n */\n\npublic class ConverterUtils {\n\n    private static final int MAX_BLOB_LENGTH = 512;\n\n    private static final String UNKNOWN_BLOB_LABEL = \"{blob}\";\n\n    private ConverterUtils() {\n        // This class in not publicly instantiable\n    }\n\n    public static String blobToString(byte[] blob) {\n        if (blob.length <= MAX_BLOB_LENGTH) {\n            if (fastIsAscii(blob)) {\n                try {\n                    return new String(blob, \"US-ASCII\");\n                } catch (UnsupportedEncodingException ignored) {\n\n                }\n            }\n        }\n        return UNKNOWN_BLOB_LABEL;\n    }\n\n    public static boolean fastIsAscii(byte[] blob) {\n        for (byte b : blob) {\n            if ((b & ~0x7f) != 0) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "debug-db-base/src/main/java/com/amitshekhar/utils/DataType.java",
    "content": "/*\n *\n *  *    Copyright (C) 2019 Amit Shekhar\n *  *    Copyright (C) 2011 Android Open Source Project\n *  *\n *  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n *  *    you may not use this file except in compliance with the License.\n *  *    You may obtain a copy of the License at\n *  *\n *  *        http://www.apache.org/licenses/LICENSE-2.0\n *  *\n *  *    Unless required by applicable law or agreed to in writing, software\n *  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  *    See the License for the specific language governing permissions and\n *  *    limitations under the License.\n *\n */\n\npackage com.amitshekhar.utils;\n\n/**\n * Created by amitshekhar on 04/02/17.\n */\n\npublic class DataType {\n\n    private DataType() {\n        // This class in not publicly instantiable\n    }\n\n    public static final String BOOLEAN = \"boolean\";\n    public static final String INTEGER = \"integer\";\n    public static final String REAL = \"real\";\n    public static final String TEXT = \"text\";\n    public static final String LONG = \"long\";\n    public static final String FLOAT = \"float\";\n    public static final String STRING_SET = \"string_set\";\n\n}\n"
  },
  {
    "path": "debug-db-base/src/main/java/com/amitshekhar/utils/DatabaseFileProvider.java",
    "content": "/*\n *\n *  *    Copyright (C) 2019 Amit Shekhar\n *  *    Copyright (C) 2011 Android Open Source Project\n *  *\n *  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n *  *    you may not use this file except in compliance with the License.\n *  *    You may obtain a copy of the License at\n *  *\n *  *        http://www.apache.org/licenses/LICENSE-2.0\n *  *\n *  *    Unless required by applicable law or agreed to in writing, software\n *  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  *    See the License for the specific language governing permissions and\n *  *    limitations under the License.\n *\n */\n\npackage com.amitshekhar.utils;\n\nimport android.content.Context;\nimport android.util.Pair;\n\nimport java.io.File;\nimport java.text.MessageFormat;\nimport java.util.HashMap;\n\n/**\n * Created by amitshekhar on 06/02/17.\n */\n\npublic class DatabaseFileProvider {\n\n    private final static String DB_PASSWORD_RESOURCE = \"DB_PASSWORD_{0}\";\n\n    private DatabaseFileProvider() {\n        // This class in not publicly instantiable\n    }\n\n    public static HashMap<String, Pair<File, String>> getDatabaseFiles(Context context) {\n        HashMap<String, Pair<File, String>> databaseFiles = new HashMap<>();\n        try {\n            for (String databaseName : context.databaseList()) {\n                String password = getDbPasswordFromStringResources(context, databaseName);\n                databaseFiles.put(databaseName, new Pair<>(context.getDatabasePath(databaseName), password));\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return databaseFiles;\n    }\n\n    private static String getDbPasswordFromStringResources(Context context, String name) {\n        String nameWithoutExt = name;\n        if (nameWithoutExt.endsWith(\".db\")) {\n            nameWithoutExt = nameWithoutExt.substring(0, nameWithoutExt.lastIndexOf('.'));\n        }\n        String resourceName = MessageFormat.format(DB_PASSWORD_RESOURCE, nameWithoutExt.toUpperCase());\n        String password = \"\";\n\n        int resourceId = context.getResources().getIdentifier(resourceName, \"string\", context.getPackageName());\n\n        if (resourceId != 0) {\n            password = context.getString(resourceId);\n        }\n\n        return password;\n    }\n}\n"
  },
  {
    "path": "debug-db-base/src/main/java/com/amitshekhar/utils/DatabaseHelper.java",
    "content": "/*\n *\n *  *    Copyright (C) 2019 Amit Shekhar\n *  *    Copyright (C) 2011 Android Open Source Project\n *  *\n *  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n *  *    you may not use this file except in compliance with the License.\n *  *    You may obtain a copy of the License at\n *  *\n *  *        http://www.apache.org/licenses/LICENSE-2.0\n *  *\n *  *    Unless required by applicable law or agreed to in writing, software\n *  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  *    See the License for the specific language governing permissions and\n *  *    limitations under the License.\n *\n */\n\npackage com.amitshekhar.utils;\n\nimport android.content.ContentValues;\nimport android.database.Cursor;\nimport android.text.TextUtils;\n\nimport com.amitshekhar.model.Response;\nimport com.amitshekhar.model.RowDataRequest;\nimport com.amitshekhar.model.TableDataResponse;\nimport com.amitshekhar.model.UpdateRowResponse;\nimport com.amitshekhar.sqlite.SQLiteDB;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\n\n/**\n * Created by amitshekhar on 06/02/17.\n */\n\npublic class DatabaseHelper {\n\n    private DatabaseHelper() {\n        // This class in not publicly instantiable\n    }\n\n    public static Response getAllTableName(SQLiteDB database) {\n        Response response = new Response();\n        Cursor c = database.rawQuery(\"SELECT name FROM sqlite_master WHERE type='table' OR type='view' ORDER BY name COLLATE NOCASE\", null);\n        if (c.moveToFirst()) {\n            while (!c.isAfterLast()) {\n                response.rows.add(c.getString(0));\n                c.moveToNext();\n            }\n        }\n        c.close();\n        response.isSuccessful = true;\n        try {\n            response.dbVersion = database.getVersion();\n        } catch (Exception ignore) {\n\n        }\n        return response;\n    }\n\n    public static TableDataResponse getTableData(SQLiteDB db, String selectQuery, String tableName) {\n\n        TableDataResponse tableData = new TableDataResponse();\n        tableData.isSelectQuery = true;\n        if (tableName == null) {\n            tableName = getTableName(selectQuery);\n        }\n\n        final String quotedTableName = getQuotedTableName(tableName);\n\n        if (tableName != null) {\n            final String pragmaQuery = \"PRAGMA table_info(\" + quotedTableName + \")\";\n            tableData.tableInfos = getTableInfo(db, pragmaQuery);\n        }\n        Cursor cursor = null;\n        boolean isView = false;\n        try {\n            cursor = db.rawQuery(\"SELECT type FROM sqlite_master WHERE name=?\",\n                    new String[]{quotedTableName});\n            if (cursor.moveToFirst()) {\n                isView = \"view\".equalsIgnoreCase(cursor.getString(0));\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        } finally {\n            if (cursor != null) {\n                cursor.close();\n            }\n        }\n        tableData.isEditable = tableName != null && tableData.tableInfos != null && !isView;\n\n\n        if (!TextUtils.isEmpty(tableName)) {\n            selectQuery = selectQuery.replace(tableName, quotedTableName);\n        }\n\n        try {\n            cursor = db.rawQuery(selectQuery, null);\n        } catch (Exception e) {\n            e.printStackTrace();\n            tableData.isSuccessful = false;\n            tableData.errorMessage = e.getMessage();\n            return tableData;\n        }\n\n        if (cursor != null) {\n            cursor.moveToFirst();\n\n            // setting tableInfo when tableName is not known and making\n            // it non-editable also by making isPrimary true for all\n            if (tableData.tableInfos == null) {\n                tableData.tableInfos = new ArrayList<>();\n                for (int i = 0; i < cursor.getColumnCount(); i++) {\n                    TableDataResponse.TableInfo tableInfo = new TableDataResponse.TableInfo();\n                    tableInfo.title = cursor.getColumnName(i);\n                    tableInfo.isPrimary = true;\n                    tableData.tableInfos.add(tableInfo);\n                }\n            }\n\n            tableData.isSuccessful = true;\n            tableData.rows = new ArrayList<>();\n\n            String[] columnNames = cursor.getColumnNames();\n\n            List<TableDataResponse.TableInfo> tableInfoListModified = new ArrayList<>();\n\n            for (String columnName : columnNames) {\n                for (TableDataResponse.TableInfo tableInfo : tableData.tableInfos) {\n                    if (columnName.equals(tableInfo.title)) {\n                        tableInfoListModified.add(tableInfo);\n                        break;\n                    }\n                }\n            }\n\n            if (tableData.tableInfos.size() != tableInfoListModified.size()) {\n                tableData.tableInfos = tableInfoListModified;\n                tableData.isEditable = false;\n            }\n\n            if (cursor.getCount() > 0) {\n\n                do {\n                    List<TableDataResponse.ColumnData> row = new ArrayList<>();\n                    for (int i = 0; i < cursor.getColumnCount(); i++) {\n                        TableDataResponse.ColumnData columnData = new TableDataResponse.ColumnData();\n                        switch (cursor.getType(i)) {\n                            case Cursor.FIELD_TYPE_BLOB:\n                                columnData.dataType = DataType.TEXT;\n                                columnData.value = ConverterUtils.blobToString(cursor.getBlob(i));\n                                break;\n                            case Cursor.FIELD_TYPE_FLOAT:\n                                columnData.dataType = DataType.REAL;\n                                columnData.value = cursor.getDouble(i);\n                                break;\n                            case Cursor.FIELD_TYPE_INTEGER:\n                                columnData.dataType = DataType.INTEGER;\n                                columnData.value = cursor.getLong(i);\n                                break;\n                            case Cursor.FIELD_TYPE_STRING:\n                                columnData.dataType = DataType.TEXT;\n                                columnData.value = cursor.getString(i);\n                                break;\n                            default:\n                                columnData.dataType = DataType.TEXT;\n                                columnData.value = cursor.getString(i);\n                        }\n                        row.add(columnData);\n                    }\n                    tableData.rows.add(row);\n\n                } while (cursor.moveToNext());\n            }\n            cursor.close();\n            return tableData;\n        } else {\n            tableData.isSuccessful = false;\n            tableData.errorMessage = \"Cursor is null\";\n            return tableData;\n        }\n\n    }\n\n\n    private static String getQuotedTableName(String tableName) {\n        return String.format(\"[%s]\", tableName);\n    }\n\n    private static List<TableDataResponse.TableInfo> getTableInfo(SQLiteDB db, String pragmaQuery) {\n\n        Cursor cursor;\n        try {\n            cursor = db.rawQuery(pragmaQuery, null);\n        } catch (Exception e) {\n            e.printStackTrace();\n            return null;\n        }\n\n        if (cursor != null) {\n\n            List<TableDataResponse.TableInfo> tableInfoList = new ArrayList<>();\n\n            cursor.moveToFirst();\n\n            if (cursor.getCount() > 0) {\n                do {\n                    TableDataResponse.TableInfo tableInfo = new TableDataResponse.TableInfo();\n\n                    for (int i = 0; i < cursor.getColumnCount(); i++) {\n\n                        final String columnName = cursor.getColumnName(i);\n\n                        switch (columnName) {\n                            case Constants.PK:\n                                tableInfo.isPrimary = cursor.getInt(i) == 1;\n                                break;\n                            case Constants.NAME:\n                                tableInfo.title = cursor.getString(i);\n                                break;\n                            default:\n                        }\n\n                    }\n                    tableInfoList.add(tableInfo);\n\n                } while (cursor.moveToNext());\n            }\n            cursor.close();\n            return tableInfoList;\n        }\n        return null;\n    }\n\n\n    public static UpdateRowResponse addRow(SQLiteDB db, String tableName,\n                                           List<RowDataRequest> rowDataRequests) {\n        UpdateRowResponse updateRowResponse = new UpdateRowResponse();\n\n        if (rowDataRequests == null || tableName == null) {\n            updateRowResponse.isSuccessful = false;\n            return updateRowResponse;\n        }\n\n        tableName = getQuotedTableName(tableName);\n\n        ContentValues contentValues = new ContentValues();\n\n        for (RowDataRequest rowDataRequest : rowDataRequests) {\n            if (Constants.NULL.equals(rowDataRequest.value)) {\n                rowDataRequest.value = null;\n            }\n\n            switch (rowDataRequest.dataType) {\n                case DataType.INTEGER:\n                    contentValues.put(rowDataRequest.title, Long.valueOf(rowDataRequest.value));\n                    break;\n                case DataType.REAL:\n                    contentValues.put(rowDataRequest.title, Double.valueOf(rowDataRequest.value));\n                    break;\n                case DataType.TEXT:\n                    contentValues.put(rowDataRequest.title, rowDataRequest.value);\n                    break;\n                default:\n                    contentValues.put(rowDataRequest.title, rowDataRequest.value);\n                    break;\n            }\n        }\n\n        long result = db.insert(tableName, null, contentValues);\n        updateRowResponse.isSuccessful = result > 0;\n\n        return updateRowResponse;\n\n    }\n\n\n    public static UpdateRowResponse updateRow(SQLiteDB db, String tableName, List<RowDataRequest> rowDataRequests) {\n\n        UpdateRowResponse updateRowResponse = new UpdateRowResponse();\n\n        if (rowDataRequests == null || tableName == null) {\n            updateRowResponse.isSuccessful = false;\n            return updateRowResponse;\n        }\n\n        tableName = getQuotedTableName(tableName);\n\n        ContentValues contentValues = new ContentValues();\n\n        String whereClause = null;\n        List<String> whereArgsList = new ArrayList<>();\n\n        for (RowDataRequest rowDataRequest : rowDataRequests) {\n            if (Constants.NULL.equals(rowDataRequest.value)) {\n                rowDataRequest.value = null;\n            }\n            if (rowDataRequest.isPrimary) {\n                if (whereClause == null) {\n                    whereClause = rowDataRequest.title + \"=? \";\n                } else {\n                    whereClause = whereClause + \"and \" + rowDataRequest.title + \"=? \";\n                }\n                whereArgsList.add(rowDataRequest.value);\n            } else {\n                switch (rowDataRequest.dataType) {\n                    case DataType.INTEGER:\n                        contentValues.put(rowDataRequest.title, Long.valueOf(rowDataRequest.value));\n                        break;\n                    case DataType.REAL:\n                        contentValues.put(rowDataRequest.title, Double.valueOf(rowDataRequest.value));\n                        break;\n                    case DataType.TEXT:\n                        contentValues.put(rowDataRequest.title, rowDataRequest.value);\n                        break;\n                    default:\n                }\n            }\n        }\n\n        String[] whereArgs = new String[whereArgsList.size()];\n\n        for (int i = 0; i < whereArgsList.size(); i++) {\n            whereArgs[i] = whereArgsList.get(i);\n        }\n\n        db.update(tableName, contentValues, whereClause, whereArgs);\n        updateRowResponse.isSuccessful = true;\n        return updateRowResponse;\n    }\n\n\n    public static UpdateRowResponse deleteRow(SQLiteDB db, String tableName,\n                                              List<RowDataRequest> rowDataRequests) {\n\n        UpdateRowResponse updateRowResponse = new UpdateRowResponse();\n\n        if (rowDataRequests == null || tableName == null) {\n            updateRowResponse.isSuccessful = false;\n            return updateRowResponse;\n        }\n\n        tableName = getQuotedTableName(tableName);\n\n\n        String whereClause = null;\n        List<String> whereArgsList = new ArrayList<>();\n\n        for (RowDataRequest rowDataRequest : rowDataRequests) {\n            if (Constants.NULL.equals(rowDataRequest.value)) {\n                rowDataRequest.value = null;\n            }\n            if (rowDataRequest.isPrimary) {\n                if (whereClause == null) {\n                    whereClause = rowDataRequest.title + \"=? \";\n                } else {\n                    whereClause = whereClause + \"and \" + rowDataRequest.title + \"=? \";\n                }\n                whereArgsList.add(rowDataRequest.value);\n            }\n        }\n\n        if (whereArgsList.size() == 0) {\n            updateRowResponse.isSuccessful = true;\n            return updateRowResponse;\n        }\n\n        String[] whereArgs = new String[whereArgsList.size()];\n\n        for (int i = 0; i < whereArgsList.size(); i++) {\n            whereArgs[i] = whereArgsList.get(i);\n        }\n\n        db.delete(tableName, whereClause, whereArgs);\n        updateRowResponse.isSuccessful = true;\n        return updateRowResponse;\n    }\n\n\n    public static TableDataResponse exec(SQLiteDB database, String sql) {\n        TableDataResponse tableDataResponse = new TableDataResponse();\n        tableDataResponse.isSelectQuery = false;\n        try {\n\n            String tableName = getTableName(sql);\n\n            if (!TextUtils.isEmpty(tableName)) {\n                String quotedTableName = getQuotedTableName(tableName);\n                sql = sql.replace(tableName, quotedTableName);\n            }\n\n            database.execSQL(sql);\n        } catch (Exception e) {\n            e.printStackTrace();\n            tableDataResponse.isSuccessful = false;\n            tableDataResponse.errorMessage = e.getMessage();\n            return tableDataResponse;\n        }\n        tableDataResponse.isSuccessful = true;\n        return tableDataResponse;\n    }\n\n    private static String getTableName(String selectQuery) {\n        // TODO: Handle JOIN Query\n        TableNameParser tableNameParser = new TableNameParser(selectQuery);\n        HashSet<String> tableNames = (HashSet<String>) tableNameParser.tables();\n\n        for (String tableName : tableNames) {\n            if (!TextUtils.isEmpty(tableName)) {\n                return tableName;\n            }\n        }\n\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "debug-db-base/src/main/java/com/amitshekhar/utils/NetworkUtils.java",
    "content": "/*\n *\n *  *    Copyright (C) 2019 Amit Shekhar\n *  *    Copyright (C) 2011 Android Open Source Project\n *  *\n *  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n *  *    you may not use this file except in compliance with the License.\n *  *    You may obtain a copy of the License at\n *  *\n *  *        http://www.apache.org/licenses/LICENSE-2.0\n *  *\n *  *    Unless required by applicable law or agreed to in writing, software\n *  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  *    See the License for the specific language governing permissions and\n *  *    limitations under the License.\n *\n */\n\npackage com.amitshekhar.utils;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.net.wifi.WifiManager;\n\n/**\n * Created by amitshekhar on 15/11/16.\n */\n\npublic final class NetworkUtils {\n\n    private NetworkUtils() {\n        // This class in not publicly instantiable\n    }\n\n    public static String getAddressLog(Context context, int port) {\n        WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);\n        int ipAddress = wifiManager.getConnectionInfo().getIpAddress();\n        @SuppressLint(\"DefaultLocale\")\n        final String formattedIpAddress = String.format(\"%d.%d.%d.%d\",\n                (ipAddress & 0xff),\n                (ipAddress >> 8 & 0xff),\n                (ipAddress >> 16 & 0xff),\n                (ipAddress >> 24 & 0xff));\n        return \"Open http://\" + formattedIpAddress + \":\" + port + \" in your browser\";\n    }\n\n}\n"
  },
  {
    "path": "debug-db-base/src/main/java/com/amitshekhar/utils/PrefHelper.java",
    "content": "/*\n *\n *  *    Copyright (C) 2019 Amit Shekhar\n *  *    Copyright (C) 2011 Android Open Source Project\n *  *\n *  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n *  *    you may not use this file except in compliance with the License.\n *  *    You may obtain a copy of the License at\n *  *\n *  *        http://www.apache.org/licenses/LICENSE-2.0\n *  *\n *  *    Unless required by applicable law or agreed to in writing, software\n *  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  *    See the License for the specific language governing permissions and\n *  *    limitations under the License.\n *\n */\n\npackage com.amitshekhar.utils;\n\nimport android.content.Context;\nimport android.content.SharedPreferences;\n\nimport com.amitshekhar.model.Response;\nimport com.amitshekhar.model.RowDataRequest;\nimport com.amitshekhar.model.TableDataResponse;\nimport com.amitshekhar.model.UpdateRowResponse;\n\nimport org.json.JSONArray;\n\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * Created by amitshekhar on 06/02/17.\n */\n\npublic class PrefHelper {\n\n    private static final String PREFS_SUFFIX = \".xml\";\n\n    private PrefHelper() {\n        // This class in not publicly instantiable\n    }\n\n    public static List<String> getSharedPreferenceTags(Context context) {\n\n        ArrayList<String> tags = new ArrayList<>();\n\n        String rootPath = context.getApplicationInfo().dataDir + \"/shared_prefs\";\n        File root = new File(rootPath);\n        if (root.exists()) {\n            for (File file : root.listFiles()) {\n                String fileName = file.getName();\n                if (fileName.endsWith(PREFS_SUFFIX)) {\n                    tags.add(fileName.substring(0, fileName.length() - PREFS_SUFFIX.length()));\n                }\n            }\n        }\n\n        Collections.sort(tags);\n\n        return tags;\n    }\n\n    public static Response getAllPrefTableName(Context context) {\n\n        Response response = new Response();\n\n        List<String> prefTags = getSharedPreferenceTags(context);\n\n        for (String tag : prefTags) {\n            response.rows.add(tag);\n        }\n\n        response.isSuccessful = true;\n\n        return response;\n    }\n\n    public static TableDataResponse getAllPrefData(Context context, String tag) {\n\n        TableDataResponse response = new TableDataResponse();\n        response.isEditable = true;\n        response.isSuccessful = true;\n        response.isSelectQuery = true;\n\n        TableDataResponse.TableInfo keyInfo = new TableDataResponse.TableInfo();\n        keyInfo.isPrimary = true;\n        keyInfo.title = \"Key\";\n\n        TableDataResponse.TableInfo valueInfo = new TableDataResponse.TableInfo();\n        valueInfo.isPrimary = false;\n        valueInfo.title = \"Value\";\n\n        response.tableInfos = new ArrayList<>();\n        response.tableInfos.add(keyInfo);\n        response.tableInfos.add(valueInfo);\n\n        response.rows = new ArrayList<>();\n\n        SharedPreferences preferences = context.getSharedPreferences(tag, Context.MODE_PRIVATE);\n        Map<String, ?> allEntries = preferences.getAll();\n        for (Map.Entry<String, ?> entry : allEntries.entrySet()) {\n            List<TableDataResponse.ColumnData> row = new ArrayList<>();\n            TableDataResponse.ColumnData keyColumnData = new TableDataResponse.ColumnData();\n            keyColumnData.dataType = DataType.TEXT;\n            keyColumnData.value = entry.getKey();\n\n            row.add(keyColumnData);\n\n            TableDataResponse.ColumnData valueColumnData = new TableDataResponse.ColumnData();\n            valueColumnData.value = entry.getValue().toString();\n            if (entry.getValue() != null) {\n                if (entry.getValue() instanceof String) {\n                    valueColumnData.dataType = DataType.TEXT;\n                } else if (entry.getValue() instanceof Integer) {\n                    valueColumnData.dataType = DataType.INTEGER;\n                } else if (entry.getValue() instanceof Long) {\n                    valueColumnData.dataType = DataType.LONG;\n                } else if (entry.getValue() instanceof Float) {\n                    valueColumnData.dataType = DataType.FLOAT;\n                } else if (entry.getValue() instanceof Boolean) {\n                    valueColumnData.dataType = DataType.BOOLEAN;\n                } else if (entry.getValue() instanceof Set) {\n                    valueColumnData.dataType = DataType.STRING_SET;\n                }\n            } else {\n                valueColumnData.dataType = DataType.TEXT;\n            }\n            row.add(valueColumnData);\n            response.rows.add(row);\n        }\n\n        return response;\n\n    }\n\n    public static UpdateRowResponse addOrUpdateRow(Context context, String tableName,\n                                                   List<RowDataRequest> rowDataRequests) {\n        UpdateRowResponse updateRowResponse = new UpdateRowResponse();\n\n        if (tableName == null) {\n            return updateRowResponse;\n        }\n\n        RowDataRequest rowDataKey = rowDataRequests.get(0);\n        RowDataRequest rowDataValue = rowDataRequests.get(1);\n\n        String key = rowDataKey.value;\n        String value = rowDataValue.value;\n        String dataType = rowDataValue.dataType;\n\n        if (Constants.NULL.equals(value)) {\n            value = null;\n        }\n\n        SharedPreferences preferences = context.getSharedPreferences(tableName, Context.MODE_PRIVATE);\n\n        try {\n            switch (dataType) {\n                case DataType.TEXT:\n                    preferences.edit().putString(key, value).apply();\n                    updateRowResponse.isSuccessful = true;\n                    break;\n                case DataType.INTEGER:\n                    preferences.edit().putInt(key, Integer.valueOf(value)).apply();\n                    updateRowResponse.isSuccessful = true;\n                    break;\n                case DataType.LONG:\n                    preferences.edit().putLong(key, Long.valueOf(value)).apply();\n                    updateRowResponse.isSuccessful = true;\n                    break;\n                case DataType.FLOAT:\n                    preferences.edit().putFloat(key, Float.valueOf(value)).apply();\n                    updateRowResponse.isSuccessful = true;\n                    break;\n                case DataType.BOOLEAN:\n                    preferences.edit().putBoolean(key, Boolean.valueOf(value)).apply();\n                    updateRowResponse.isSuccessful = true;\n                    break;\n                case DataType.STRING_SET:\n                    JSONArray jsonArray = new JSONArray(value);\n                    Set<String> stringSet = new HashSet<>();\n                    for (int i = 0; i < jsonArray.length(); i++) {\n                        stringSet.add(jsonArray.getString(i));\n                    }\n                    preferences.edit().putStringSet(key, stringSet).apply();\n                    updateRowResponse.isSuccessful = true;\n                    break;\n                default:\n                    preferences.edit().putString(key, value).apply();\n                    updateRowResponse.isSuccessful = true;\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n\n        return updateRowResponse;\n    }\n\n\n    public static UpdateRowResponse deleteRow(Context context, String tableName,\n                                              List<RowDataRequest> rowDataRequests) {\n        UpdateRowResponse updateRowResponse = new UpdateRowResponse();\n\n        if (tableName == null) {\n            return updateRowResponse;\n        }\n\n        RowDataRequest rowDataKey = rowDataRequests.get(0);\n\n        String key = rowDataKey.value;\n\n\n        SharedPreferences preferences = context.getSharedPreferences(tableName, Context.MODE_PRIVATE);\n\n        try {\n            preferences.edit()\n                    .remove(key).apply();\n            updateRowResponse.isSuccessful = true;\n        } catch (Exception ex) {\n            updateRowResponse.isSuccessful = false;\n        }\n\n        return updateRowResponse;\n    }\n}\n"
  },
  {
    "path": "debug-db-base/src/main/java/com/amitshekhar/utils/TableNameParser.java",
    "content": "/*\n *\n *  *    Copyright (C) 2019 Amit Shekhar\n *  *    Copyright (C) 2011 Android Open Source Project\n *  *\n *  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n *  *    you may not use this file except in compliance with the License.\n *  *    You may obtain a copy of the License at\n *  *\n *  *        http://www.apache.org/licenses/LICENSE-2.0\n *  *\n *  *    Unless required by applicable law or agreed to in writing, software\n *  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  *    See the License for the specific language governing permissions and\n *  *    limitations under the License.\n *\n */\npackage com.amitshekhar.utils;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * Ultra light, Ultra fast parser to extract table name out SQLs, supports oracle dialect SQLs as well.\n *\n * @author Nadeem Mohammad\n *         <p>\n *         Ref : https://github.com/mnadeem/sql-table-name-parser\n */\npublic final class TableNameParser {\n\n    private static final int NO_INDEX = -1;\n    private static final String SPACE = \" \";\n    private static final String REGEX_SPACE = \"\\\\s+\";\n\n    private static final String TOKEN_ORACLE_HINT_START = \"/*+\";\n    private static final String TOKEN_ORACLE_HINT_END = \"*/\";\n    private static final String TOKEN_SINGLE_LINE_COMMENT = \"--\";\n    private static final String TOKEN_SEMI_COLON = \";\";\n    private static final String TOKEN_PARAN_START = \"(\";\n    private static final String TOKEN_COMMA = \",\";\n    private static final String TOKEN_SET = \"set\";\n    private static final String TOKEN_OF = \"of\";\n    private static final String TOKEN_DUAL = \"dual\";\n    private static final String TOKEN_DELETE = \"delete\";\n    private static final String TOKEN_CREATE = \"create\";\n    private static final String TOKEN_INDEX = \"index\";\n    private static final String TOKEN_ASTERICK = \"*\";\n    private static final String KEYWORD_JOIN = \"join\";\n    private static final String KEYWORD_INTO = \"into\";\n    private static final String KEYWORD_TABLE = \"table\";\n    private static final String KEYWORD_FROM = \"from\";\n    private static final String KEYWORD_USING = \"using\";\n    private static final String KEYWORD_UPDATE = \"update\";\n    private static final List<String> concerned = Arrays.asList(KEYWORD_TABLE, KEYWORD_INTO, KEYWORD_JOIN, KEYWORD_USING, KEYWORD_UPDATE);\n    private static final List<String> ignored = Arrays.asList(TOKEN_PARAN_START, TOKEN_SET, TOKEN_OF, TOKEN_DUAL);\n    private static String TOKEN_NEWLINE = \"\\\\r\\\\n|\\\\r|\\\\n|\\\\n\\\\r\";\n    private Map<String, String> tables = new HashMap<String, String>();\n\n    /**\n     * Extracts table names out of SQL\n     *\n     * @param sql\n     */\n    public TableNameParser(final String sql) {\n        String nocomments = removeComments(sql);\n        String normalized = normalized(nocomments);\n        String cleansed = clean(normalized);\n        String[] tokens = cleansed.split(REGEX_SPACE);\n        int index = 0;\n\n        String firstToken = tokens[index];\n        if (isOracleSpecialDelete(firstToken, tokens, index)) {\n            handleSpecialOracleSpecialDelete(firstToken, tokens, index);\n        } else if (isCreateIndex(firstToken, tokens, index)) {\n            handleCreateIndex(firstToken, tokens, index);\n        } else {\n            while (moreTokens(tokens, index)) {\n                String currentToken = tokens[index++];\n\n                if (isFromToken(currentToken)) {\n                    processFromToken(tokens, index);\n                } else if (shouldProcess(currentToken)) {\n                    String nextToken = tokens[index++];\n                    considerInclusion(nextToken);\n\n                    if (moreTokens(tokens, index)) {\n                        nextToken = tokens[index++];\n                    }\n                }\n            }\n        }\n    }\n\n    private String removeComments(final String sql) {\n        StringBuilder sb = new StringBuilder(sql);\n        int nextCommentPosition = sb.indexOf(TOKEN_SINGLE_LINE_COMMENT);\n        while (nextCommentPosition > -1) {\n            int end = indexOfRegex(TOKEN_NEWLINE, sb.substring(nextCommentPosition));\n            if (end == -1) {\n                return sb.substring(0, nextCommentPosition);\n            } else {\n                sb.replace(nextCommentPosition, end + nextCommentPosition, \"\");\n            }\n            nextCommentPosition = sb.indexOf(TOKEN_SINGLE_LINE_COMMENT);\n        }\n        return sb.toString();\n    }\n\n    private int indexOfRegex(String regex, String string) {\n        Pattern pattern = Pattern.compile(regex);\n        Matcher matcher = pattern.matcher(string);\n        return matcher.find() ? matcher.start() : -1;\n    }\n\n    private String normalized(final String sql) {\n        String normalized = sql.trim().replaceAll(TOKEN_NEWLINE, SPACE).replaceAll(TOKEN_COMMA, \" , \")\n                .replaceAll(\"\\\\(\", \" ( \").replaceAll(\"\\\\)\", \" ) \");\n        if (normalized.endsWith(TOKEN_SEMI_COLON)) {\n            normalized = normalized.substring(0, normalized.length() - 1);\n        }\n        return normalized;\n    }\n\n    private String clean(final String normalized) {\n        int start = normalized.indexOf(TOKEN_ORACLE_HINT_START);\n        int end = NO_INDEX;\n        if (start != NO_INDEX) {\n            end = normalized.indexOf(TOKEN_ORACLE_HINT_END);\n            if (end != NO_INDEX) {\n                String firstHalf = normalized.substring(0, start);\n                String secondHalf = normalized.substring(end + 2, normalized.length());\n                return firstHalf.trim() + SPACE + secondHalf.trim();\n            }\n        }\n        return normalized;\n    }\n\n    private boolean isOracleSpecialDelete(final String currentToken, final String[] tokens, int index) {\n        index++;// Point to next token\n        if (TOKEN_DELETE.equals(currentToken)) {\n            if (moreTokens(tokens, index)) {\n                String nextToken = tokens[index++];\n                if (!KEYWORD_FROM.equals(nextToken) && !TOKEN_ASTERICK.equals(nextToken)) {\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n\n    private void handleSpecialOracleSpecialDelete(final String currentToken, final String[] tokens, int index) {\n        String tableName = tokens[index + 1];\n        considerInclusion(tableName);\n    }\n\n    private boolean isCreateIndex(String currentToken, String[] tokens, int index) {\n        index++; // Point to next token\n        if (TOKEN_CREATE.equals(currentToken.toLowerCase()) && hasIthToken(tokens, index, 3)) {\n            String nextToken = tokens[index++];\n            if (TOKEN_INDEX.equals(nextToken.toLowerCase())) {\n                return true;\n            }\n\n        }\n        return false;\n    }\n\n    private void handleCreateIndex(String currentToken, String[] tokens, int index) {\n        String tableName = tokens[index + 4];\n        considerInclusion(tableName);\n    }\n\n    private boolean hasIthToken(String[] tokens, int currentIndex, int tokenNumber) {\n        if (moreTokens(tokens, currentIndex) && tokens.length > currentIndex + tokenNumber) {\n            return true;\n        }\n        return false;\n    }\n\n    private boolean shouldProcess(final String currentToken) {\n        return concerned.contains(currentToken.toLowerCase());\n    }\n\n    private boolean isFromToken(final String currentToken) {\n        return KEYWORD_FROM.equals(currentToken.toLowerCase());\n    }\n\n    private void processFromToken(final String[] tokens, int index) {\n        String currentToken = tokens[index++];\n        considerInclusion(currentToken);\n\n        String nextToken = null;\n        if (moreTokens(tokens, index)) {\n            nextToken = tokens[index++];\n        }\n\n        if (shouldProcessMultipleTables(nextToken)) {\n            processNonAliasedMultiTables(tokens, index, nextToken);\n        } else {\n            processAliasedMultiTables(tokens, index, currentToken);\n        }\n    }\n\n    private void processNonAliasedMultiTables(final String[] tokens, int index, String nextToken) {\n        while (nextToken.equals(TOKEN_COMMA)) {\n            String currentToken = tokens[index++];\n            considerInclusion(currentToken);\n            if (moreTokens(tokens, index)) {\n                nextToken = tokens[index++];\n            } else {\n                break;\n            }\n        }\n    }\n\n    private void processAliasedMultiTables(final String[] tokens, int index, String currentToken) {\n        String nextNextToken = null;\n        if (moreTokens(tokens, index)) {\n            nextNextToken = tokens[index++];\n        }\n\n        if (shouldProcessMultipleTables(nextNextToken)) {\n            while (moreTokens(tokens, index) && nextNextToken.equals(TOKEN_COMMA)) {\n                if (moreTokens(tokens, index)) {\n                    currentToken = tokens[index++];\n                }\n                if (moreTokens(tokens, index)) {\n                    index++;\n                }\n                if (moreTokens(tokens, index)) {\n                    nextNextToken = tokens[index++];\n                }\n                considerInclusion(currentToken);\n            }\n        }\n    }\n\n    private boolean shouldProcessMultipleTables(final String nextToken) {\n        return nextToken != null && nextToken.equals(TOKEN_COMMA);\n    }\n\n    private boolean moreTokens(final String[] tokens, int index) {\n        return index < tokens.length;\n    }\n\n    private void considerInclusion(final String token) {\n        if (!ignored.contains(token.toLowerCase()) && !this.tables.containsKey(token.toLowerCase())) {\n            this.tables.put(token.toLowerCase(), token);\n        }\n    }\n\n    /**\n     * @return table names extracted out of sql\n     */\n    public Collection<String> tables() {\n        return new HashSet<String>(this.tables.values());\n    }\n}\n"
  },
  {
    "path": "debug-db-base/src/main/java/com/amitshekhar/utils/Utils.java",
    "content": "/*\n *\n *  *    Copyright (C) 2019 Amit Shekhar\n *  *    Copyright (C) 2011 Android Open Source Project\n *  *\n *  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n *  *    you may not use this file except in compliance with the License.\n *  *    You may obtain a copy of the License at\n *  *\n *  *        http://www.apache.org/licenses/LICENSE-2.0\n *  *\n *  *    Unless required by applicable law or agreed to in writing, software\n *  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  *    See the License for the specific language governing permissions and\n *  *    limitations under the License.\n *\n */\n\npackage com.amitshekhar.utils;\n\nimport android.content.res.AssetManager;\nimport android.text.TextUtils;\nimport android.util.Log;\nimport android.util.Pair;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.HashMap;\n\n/**\n * Created by amitshekhar on 06/02/17.\n */\n\npublic class Utils {\n\n    private static final String TAG = \"Utils\";\n\n    private Utils() {\n        // This class in not publicly instantiable\n    }\n\n    public static String detectMimeType(String fileName) {\n        if (TextUtils.isEmpty(fileName)) {\n            return null;\n        } else if (fileName.endsWith(\".html\")) {\n            return \"text/html\";\n        } else if (fileName.endsWith(\".js\")) {\n            return \"application/javascript\";\n        } else if (fileName.endsWith(\".css\")) {\n            return \"text/css\";\n        } else {\n            return \"application/octet-stream\";\n        }\n    }\n\n    public static byte[] loadContent(String fileName, AssetManager assetManager) throws IOException {\n        InputStream input = null;\n        try {\n            ByteArrayOutputStream output = new ByteArrayOutputStream();\n            input = assetManager.open(fileName);\n            byte[] buffer = new byte[1024];\n            int size;\n            while (-1 != (size = input.read(buffer))) {\n                output.write(buffer, 0, size);\n            }\n            output.flush();\n            return output.toByteArray();\n        } catch (FileNotFoundException e) {\n            return null;\n        } finally {\n            try {\n                if (null != input) {\n                    input.close();\n                }\n            } catch (Exception e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    public static byte[] getDatabase(String selectedDatabase, HashMap<String, Pair<File, String>> databaseFiles) {\n        if (TextUtils.isEmpty(selectedDatabase) || !databaseFiles.containsKey(selectedDatabase)) {\n            return null;\n        }\n\n        byte[] byteArray = new byte[0];\n        try {\n            File file = databaseFiles.get(selectedDatabase).first;\n\n            byteArray = null;\n            try {\n                InputStream inputStream = new FileInputStream(file);\n                ByteArrayOutputStream bos = new ByteArrayOutputStream();\n                byte[] b = new byte[(int) file.length()];\n                int bytesRead;\n\n                while ((bytesRead = inputStream.read(b)) != -1) {\n                    bos.write(b, 0, bytesRead);\n                }\n\n                byteArray = bos.toByteArray();\n            } catch (IOException e) {\n                Log.e(TAG, \"getDatabase: \", e);\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n\n        return byteArray;\n    }\n\n}\n"
  },
  {
    "path": "debug-db-base/src/main/res/values/strings.xml",
    "content": "<!--\n  ~ /*\n  ~  *    Copyright (C) 2019 Amit Shekhar\n  ~  *    Copyright (C) 2011 Android Open Source Project\n  ~  *\n  ~  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~  *    you may not use this file except in compliance with the License.\n  ~  *    You may obtain a copy of the License at\n  ~  *\n  ~  *        http://www.apache.org/licenses/LICENSE-2.0\n  ~  *\n  ~  *    Unless required by applicable law or agreed to in writing, software\n  ~  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~  *    See the License for the specific language governing permissions and\n  ~  *    limitations under the License.\n  ~  */\n  -->\n\n<resources>\n    <string name=\"app_name\">Debug-DB</string>\n</resources>\n"
  },
  {
    "path": "debug-db-base/src/test/java/com/amitshekhar/ExampleUnitTest.java",
    "content": "/*\n *\n *  *    Copyright (C) 2019 Amit Shekhar\n *  *    Copyright (C) 2011 Android Open Source Project\n *  *\n *  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n *  *    you may not use this file except in compliance with the License.\n *  *    You may obtain a copy of the License at\n *  *\n *  *        http://www.apache.org/licenses/LICENSE-2.0\n *  *\n *  *    Unless required by applicable law or agreed to in writing, software\n *  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  *    See the License for the specific language governing permissions and\n *  *    limitations under the License.\n *\n */\n\npackage com.amitshekhar;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\npublic class ExampleUnitTest {\n    @Test\n    public void addition_isCorrect() throws Exception {\n        assertEquals(4, 2 + 2);\n    }\n}\n"
  },
  {
    "path": "debug-db-encrypt/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "debug-db-encrypt/build.gradle",
    "content": "apply plugin: 'com.android.library'\n\nandroid {\n    compileSdk rootProject.ext.compileSdk\n    defaultConfig {\n        minSdk rootProject.ext.minSdk\n        targetSdk rootProject.ext.targetSdk\n        versionCode 1\n        versionName \"1.0\"\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n    }\n}\n\ndependencies {\n    api project(':debug-db-base')\n    implementation 'net.zetetic:android-database-sqlcipher:3.5.9'\n    testImplementation 'junit:junit:4.13.2'\n    androidTestImplementation 'androidx.test:runner:1.5.2'\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'\n}"
  },
  {
    "path": "debug-db-encrypt/gradle.properties",
    "content": "ARTIFACT_ID=debug-db-enncrypt"
  },
  {
    "path": "debug-db-encrypt/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "debug-db-encrypt/src/androidTest/java/com/amitshekhar/debug/encrypt/ExampleInstrumentedTest.java",
    "content": "package com.amitshekhar.debug.encrypt;\n\nimport android.content.Context;\nimport android.support.test.InstrumentationRegistry;\nimport android.support.test.runner.AndroidJUnit4;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\nimport static org.junit.Assert.*;\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\n@RunWith(AndroidJUnit4.class)\npublic class ExampleInstrumentedTest {\n    @Test\n    public void useAppContext() {\n        // Context of the app under test.\n        Context appContext = InstrumentationRegistry.getTargetContext();\n\n        assertEquals(\"com.amitshekhar.debug.encrypt.test\", appContext.getPackageName());\n    }\n}\n"
  },
  {
    "path": "debug-db-encrypt/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.amitshekhar.debug.encrypt\">\n\n    <application>\n        <provider\n            android:name=\".DebugDBEncryptInitProvider\"\n            android:authorities=\"${applicationId}.DebugDBEncryptInitProvider\"\n            android:enabled=\"true\"\n            android:exported=\"false\" />\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "debug-db-encrypt/src/main/java/com/amitshekhar/debug/encrypt/DebugDBEncryptInitProvider.java",
    "content": "package com.amitshekhar.debug.encrypt;\n\nimport android.content.ContentProvider;\nimport android.content.ContentValues;\nimport android.content.Context;\nimport android.content.pm.ProviderInfo;\nimport android.database.Cursor;\nimport android.net.Uri;\n\nimport com.amitshekhar.DebugDB;\nimport com.amitshekhar.debug.encrypt.sqlite.DebugDBEncryptFactory;\n\npublic class DebugDBEncryptInitProvider extends ContentProvider {\n\n\n    public DebugDBEncryptInitProvider() {\n    }\n\n    @Override\n    public boolean onCreate() {\n        DebugDB.initialize(getContext(), new DebugDBEncryptFactory());\n        return true;\n    }\n\n    @Override\n    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {\n        return null;\n    }\n\n    @Override\n    public String getType(Uri uri) {\n        return null;\n    }\n\n    @Override\n    public Uri insert(Uri uri, ContentValues values) {\n        return null;\n    }\n\n    @Override\n    public int delete(Uri uri, String selection, String[] selectionArgs) {\n        return 0;\n    }\n\n    @Override\n    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {\n        return 0;\n    }\n\n    @Override\n    public void attachInfo(Context context, ProviderInfo providerInfo) {\n        if (providerInfo == null) {\n            throw new NullPointerException(\"DebugDBEncryptInitProvider ProviderInfo cannot be null.\");\n        }\n        // So if the authorities equal the library internal ones, the developer forgot to set his applicationId\n        if (\"com.amitshekhar.debug.encrypt.DebugDBEncryptInitProvider\".equals(providerInfo.authority)) {\n            throw new IllegalStateException(\"Incorrect provider authority in manifest. Most likely due to a \"\n                    + \"missing applicationId variable in application\\'s build.gradle.\");\n        }\n        super.attachInfo(context, providerInfo);\n    }\n\n}\n"
  },
  {
    "path": "debug-db-encrypt/src/main/java/com/amitshekhar/debug/encrypt/sqlite/DebugDBEncryptFactory.java",
    "content": "package com.amitshekhar.debug.encrypt.sqlite;\n\nimport android.content.Context;\n\nimport com.amitshekhar.sqlite.DBFactory;\nimport com.amitshekhar.sqlite.SQLiteDB;\n\nimport net.sqlcipher.database.SQLiteDatabase;\n\npublic class DebugDBEncryptFactory implements DBFactory {\n\n    @Override\n    public SQLiteDB create(Context context, String path, String password) {\n        SQLiteDatabase.loadLibs(context);\n        return new DebugEncryptSQLiteDB(SQLiteDatabase.openOrCreateDatabase(path, password, null));\n    }\n\n}\n"
  },
  {
    "path": "debug-db-encrypt/src/main/java/com/amitshekhar/debug/encrypt/sqlite/DebugEncryptSQLiteDB.java",
    "content": "package com.amitshekhar.debug.encrypt.sqlite;\n\nimport android.content.ContentValues;\nimport android.database.Cursor;\nimport android.database.SQLException;\n\nimport com.amitshekhar.sqlite.SQLiteDB;\n\nimport net.sqlcipher.database.SQLiteDatabase;\n\n/**\n * Created by anandgaurav on 12/02/18.\n */\n\npublic class DebugEncryptSQLiteDB implements SQLiteDB {\n\n    private final SQLiteDatabase database;\n\n    public DebugEncryptSQLiteDB(SQLiteDatabase database) {\n        this.database = database;\n    }\n\n    @Override\n    public int delete(String table, String whereClause, String[] whereArgs) {\n        return database.delete(table, whereClause, whereArgs);\n    }\n\n    @Override\n    public boolean isOpen() {\n        return database.isOpen();\n    }\n\n    @Override\n    public void close() {\n        database.close();\n    }\n\n    @Override\n    public Cursor rawQuery(String sql, String[] selectionArgs) {\n        return database.rawQuery(sql, selectionArgs);\n    }\n\n    @Override\n    public void execSQL(String sql) throws SQLException {\n        database.execSQL(sql);\n    }\n\n    @Override\n    public long insert(String table, String nullColumnHack, ContentValues values) {\n        return database.insert(table, nullColumnHack, values);\n    }\n\n    @Override\n    public int update(String table, ContentValues values, String whereClause, String[] whereArgs) {\n        return database.update(table, values, whereClause, whereArgs);\n    }\n\n    @Override\n    public int getVersion() {\n        return database.getVersion();\n    }\n}\n"
  },
  {
    "path": "debug-db-encrypt/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">debug-db-encrypt</string>\n</resources>\n"
  },
  {
    "path": "debug-db-encrypt/src/test/java/com/amitshekhar/debug/encrypt/ExampleUnitTest.java",
    "content": "package com.amitshekhar.debug.encrypt;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\npublic class ExampleUnitTest {\n    @Test\n    public void addition_isCorrect() {\n        assertEquals(4, 2 + 2);\n    }\n}"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Thu Apr 07 10:15:47 CST 2022\ndistributionBase=GRADLE_USER_HOME\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-7.4-bin.zip\ndistributionPath=wrapper/dists\nzipStorePath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\n"
  },
  {
    "path": "gradle.properties",
    "content": "#\n# /*\n#  *    Copyright (C) 2019 Amit Shekhar\n#  *    Copyright (C) 2011 Android Open Source Project\n#  *\n#  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n#  *    you may not use this file except in compliance with the License.\n#  *    You may obtain a copy of the License at\n#  *\n#  *        http://www.apache.org/licenses/LICENSE-2.0\n#  *\n#  *    Unless required by applicable law or agreed to in writing, software\n#  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n#  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#  *    See the License for the specific language governing permissions and\n#  *    limitations under the License.\n#  */\n#\n\n# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx1536m\n\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\nandroid.useAndroidX=true\nandroid.enableJetifier=true\nsigning.keyId=xxxxxx\nsigning.password=xxxxxx\nsigning.secretKeyRingFile=xxxxxx.gpg"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env sh\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn () {\n    echo \"$*\"\n}\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave () {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=$(save \"$@\")\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\n# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong\nif [ \"$(uname)\" = \"Darwin\" ] && [ \"$HOME\" = \"$PWD\" ]; then\n  cd \"$(dirname \"$0\")\"\nfi\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:init\r\n@rem Get command-line arguments, handling Windows variants\r\n\r\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\r\n\r\n:win9xME_args\r\n@rem Slurp the command line arguments.\r\nset CMD_LINE_ARGS=\r\nset _SKIP=2\r\n\r\n:win9xME_args_slurp\r\nif \"x%~1\" == \"x\" goto execute\r\n\r\nset CMD_LINE_ARGS=%*\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "sample-app/build.gradle",
    "content": "/*\n *\n *  *    Copyright (C) 2019 Amit Shekhar\n *  *    Copyright (C) 2011 Android Open Source Project\n *  *\n *  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n *  *    you may not use this file except in compliance with the License.\n *  *    You may obtain a copy of the License at\n *  *\n *  *        http://www.apache.org/licenses/LICENSE-2.0\n *  *\n *  *    Unless required by applicable law or agreed to in writing, software\n *  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  *    See the License for the specific language governing permissions and\n *  *    limitations under the License.\n *\n */\n\napply plugin: 'com.android.application'\n\nandroid {\n    compileSdk rootProject.ext.compileSdk\n    defaultConfig {\n        minSdk rootProject.ext.minSdk\n        applicationId \"com.sample\"\n        targetSdk rootProject.ext.targetSdk\n        versionCode 1\n        versionName \"1.0\"\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n    buildTypes {\n        debug {\n            resValue(\"string\", \"PORT_NUMBER\", \"8080\")\n        }\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n}\n\ndependencies {\n    debugImplementation project(':debug-db')\n    implementation 'androidx.appcompat:appcompat:1.4.2'\n    implementation \"androidx.room:room-runtime:2.5.0\"\n    annotationProcessor \"androidx.room:room-compiler:2.5.0\"\n    testImplementation 'junit:junit:4.13.2'\n    androidTestImplementation 'androidx.test:runner:1.5.2'\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'\n}\n"
  },
  {
    "path": "sample-app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "sample-app/src/androidTest/java/com/sample/ExampleInstrumentedTest.java",
    "content": "/*\n *\n *  *    Copyright (C) 2019 Amit Shekhar\n *  *    Copyright (C) 2011 Android Open Source Project\n *  *\n *  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n *  *    you may not use this file except in compliance with the License.\n *  *    You may obtain a copy of the License at\n *  *\n *  *        http://www.apache.org/licenses/LICENSE-2.0\n *  *\n *  *    Unless required by applicable law or agreed to in writing, software\n *  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  *    See the License for the specific language governing permissions and\n *  *    limitations under the License.\n *\n */\n\npackage com.sample;\n\nimport android.content.Context;\nimport android.support.test.InstrumentationRegistry;\nimport android.support.test.runner.AndroidJUnit4;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\nimport static org.junit.Assert.*;\n\n/**\n * Instrumentation test, which will execute on an Android device.\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\n@RunWith(AndroidJUnit4.class)\npublic class ExampleInstrumentedTest {\n    @Test\n    public void useAppContext() throws Exception {\n        // Context of the app under test.\n        Context appContext = InstrumentationRegistry.getTargetContext();\n\n        assertEquals(\"com.sample\", appContext.getPackageName());\n    }\n}\n"
  },
  {
    "path": "sample-app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ /*\n  ~  *    Copyright (C) 2019 Amit Shekhar\n  ~  *    Copyright (C) 2011 Android Open Source Project\n  ~  *\n  ~  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~  *    you may not use this file except in compliance with the License.\n  ~  *    You may obtain a copy of the License at\n  ~  *\n  ~  *        http://www.apache.org/licenses/LICENSE-2.0\n  ~  *\n  ~  *    Unless required by applicable law or agreed to in writing, software\n  ~  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~  *    See the License for the specific language governing permissions and\n  ~  *    limitations under the License.\n  ~  */\n  -->\n\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.sample\">\n\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" />\n    <uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\" />\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/AppTheme\">\n        <activity android:name=\".MainActivity\" android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "sample-app/src/main/java/com/sample/MainActivity.java",
    "content": "/*\n *\n *  *    Copyright (C) 2019 Amit Shekhar\n *  *    Copyright (C) 2011 Android Open Source Project\n *  *\n *  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n *  *    you may not use this file except in compliance with the License.\n *  *    You may obtain a copy of the License at\n *  *\n *  *        http://www.apache.org/licenses/LICENSE-2.0\n *  *\n *  *    Unless required by applicable law or agreed to in writing, software\n *  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  *    See the License for the specific language governing permissions and\n *  *    limitations under the License.\n *\n */\n\npackage com.sample;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.content.SharedPreferences;\nimport android.os.Bundle;\nimport android.preference.PreferenceManager;\nimport android.view.View;\n\nimport androidx.appcompat.app.AppCompatActivity;\n\nimport com.sample.database.CarDBHelper;\nimport com.sample.database.ContactDBHelper;\nimport com.sample.database.ExtTestDBHelper;\nimport com.sample.database.room.User;\nimport com.sample.database.room.UserDBHelper;\nimport com.sample.utils.Utils;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\npublic class MainActivity extends AppCompatActivity {\n\n    @SuppressLint(\"CommitPrefEdits\")\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n\n        super.onCreate(savedInstanceState);\n\n        setContentView(R.layout.activity_main);\n\n        Set<String> stringSet = new HashSet<>();\n        stringSet.add(\"SetOne\");\n        stringSet.add(\"SetTwo\");\n        stringSet.add(\"SetThree\");\n\n        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());\n\n        SharedPreferences prefsOne = getSharedPreferences(\"countPrefOne\", Context.MODE_PRIVATE);\n        SharedPreferences prefsTwo = getSharedPreferences(\"countPrefTwo\", Context.MODE_PRIVATE);\n\n        sharedPreferences.edit().putString(\"testOne\", \"one\").commit();\n        sharedPreferences.edit().putInt(\"testTwo\", 2).commit();\n        sharedPreferences.edit().putLong(\"testThree\", 100000L).commit();\n        sharedPreferences.edit().putFloat(\"testFour\", 3.01F).commit();\n        sharedPreferences.edit().putBoolean(\"testFive\", true).commit();\n        sharedPreferences.edit().putStringSet(\"testSix\", stringSet).commit();\n\n        prefsOne.edit().putString(\"testOneNew\", \"one\").commit();\n\n        prefsTwo.edit().putString(\"testTwoNew\", \"two\").commit();\n\n        ContactDBHelper contactDBHelper = new ContactDBHelper(getApplicationContext());\n        if (contactDBHelper.count() == 0) {\n            for (int i = 0; i < 100; i++) {\n                String name = \"name_\" + i;\n                String phone = \"phone_\" + i;\n                String email = \"email_\" + i;\n                String street = \"street_\" + i;\n                String place = \"place_\" + i;\n                contactDBHelper.insertContact(name, phone, email, street, place);\n            }\n        }\n\n        CarDBHelper carDBHelper = new CarDBHelper(getApplicationContext());\n        if (carDBHelper.count() == 0) {\n            for (int i = 0; i < 50; i++) {\n                String name = \"name_\" + i;\n                String color = \"RED\";\n                float mileage = i + 10.45f;\n                carDBHelper.insertCar(name, color, mileage);\n            }\n        }\n\n        ExtTestDBHelper extTestDBHelper = new ExtTestDBHelper(getApplicationContext());\n        if (extTestDBHelper.count() == 0) {\n            for (int i = 0; i < 20; i++) {\n                String value = \"value_\" + i;\n                extTestDBHelper.insertTest(value);\n            }\n        }\n\n        // Room database\n        UserDBHelper userDBHelper = new UserDBHelper(getApplicationContext());\n        if (userDBHelper.count() == 0) {\n            List<User> userList = new ArrayList<>();\n            for (int i = 0; i < 20; i++) {\n                User user = new User();\n                user.id = (long) (i + 1);\n                user.name = \"user_\" + i;\n                userList.add(user);\n            }\n            userDBHelper.insertUser(userList);\n        }\n\n        // Room inMemory database\n        if (userDBHelper.countInMemory() == 0) {\n            List<User> userList = new ArrayList<>();\n            for (int i = 0; i < 20; i++) {\n                User user = new User();\n                user.id = (long) (i + 1);\n                user.name = \"in_memory_user_\" + i;\n                userList.add(user);\n            }\n            userDBHelper.insertUserInMemory(userList);\n        }\n\n        Utils.setCustomDatabaseFiles(getApplicationContext());\n        Utils.setInMemoryRoomDatabases(userDBHelper.getInMemoryDatabase());\n    }\n\n    public void showDebugDbAddress(View view) {\n        Utils.showDebugDBAddressLogToast(getApplicationContext());\n    }\n}\n"
  },
  {
    "path": "sample-app/src/main/java/com/sample/database/CarDBHelper.java",
    "content": "/*\n *\n *  *    Copyright (C) 2019 Amit Shekhar\n *  *    Copyright (C) 2011 Android Open Source Project\n *  *\n *  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n *  *    you may not use this file except in compliance with the License.\n *  *    You may obtain a copy of the License at\n *  *\n *  *        http://www.apache.org/licenses/LICENSE-2.0\n *  *\n *  *    Unless required by applicable law or agreed to in writing, software\n *  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  *    See the License for the specific language governing permissions and\n *  *    limitations under the License.\n *\n */\n\npackage com.sample.database;\n\nimport android.content.ContentValues;\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.database.DatabaseUtils;\nimport android.database.sqlite.SQLiteDatabase;\nimport android.database.sqlite.SQLiteOpenHelper;\n\nimport java.util.ArrayList;\n\n/**\n * Created by amitshekhar on 06/02/17.\n */\n\npublic class CarDBHelper extends SQLiteOpenHelper {\n\n    public static final String DATABASE_NAME = \"Car.db\";\n    public static final String CARS_TABLE_NAME = \"cars\";\n    public static final String CARS_COLUMN_ID = \"id\";\n    public static final String CARS_COLUMN_NAME = \"name\";\n    public static final String CARS_COLUMN_COLOR = \"color\";\n    public static final String CCARS_COLUMN_MILEAGE = \"mileage\";\n\n    public CarDBHelper(Context context) {\n        super(context, DATABASE_NAME, null, 1);\n    }\n\n    @Override\n    public void onCreate(SQLiteDatabase db) {\n        // TODO Auto-generated method stub\n        db.execSQL(\n                \"create table cars \" +\n                        \"(id integer primary key, name text, color text, mileage real)\"\n        );\n\n        db.execSQL(\"create table [transaction] (id integer primary key, name text)\");\n\n        for (int i = 0; i < 10; i++) {\n            db.execSQL(\"insert into [transaction] (name) values ('hello');\");\n        }\n    }\n\n    @Override\n    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {\n        // TODO Auto-generated method stub\n        db.execSQL(\"DROP TABLE IF EXISTS cars\");\n        onCreate(db);\n    }\n\n    public boolean insertCar(String name, String color, float mileage) {\n        SQLiteDatabase db = this.getWritableDatabase();\n        ContentValues contentValues = new ContentValues();\n        contentValues.put(\"name\", name);\n        contentValues.put(\"color\", color);\n        contentValues.put(\"mileage\", mileage);\n        db.insert(\"cars\", null, contentValues);\n        return true;\n    }\n\n    public Cursor getData(int id) {\n        SQLiteDatabase db = this.getReadableDatabase();\n        Cursor res = db.rawQuery(\"select * from cars where id=\" + id + \"\", null);\n        return res;\n    }\n\n    public int numberOfRows() {\n        SQLiteDatabase db = this.getReadableDatabase();\n        int numRows = (int) DatabaseUtils.queryNumEntries(db, CARS_TABLE_NAME);\n        return numRows;\n    }\n\n    public boolean updateCar(Integer id, String name, String color, float mileage) {\n        SQLiteDatabase db = this.getWritableDatabase();\n        ContentValues contentValues = new ContentValues();\n        contentValues.put(\"name\", name);\n        contentValues.put(\"color\", color);\n        contentValues.put(\"mileage\", mileage);\n        db.update(\"cars\", contentValues, \"id = ? \", new String[]{Integer.toString(id)});\n        return true;\n    }\n\n    public Integer deleteCar(Integer id) {\n        SQLiteDatabase db = this.getWritableDatabase();\n        return db.delete(\"cars\",\n                \"id = ? \",\n                new String[]{Integer.toString(id)});\n    }\n\n    public ArrayList<String> getAllCars() {\n        ArrayList<String> arrayList = new ArrayList<>();\n\n        //hp = new HashMap();\n        SQLiteDatabase db = this.getReadableDatabase();\n        Cursor res = db.rawQuery(\"select * from cars\", null);\n        res.moveToFirst();\n\n        while (!res.isAfterLast()) {\n            arrayList.add(res.getString(res.getColumnIndex(CARS_COLUMN_NAME)));\n            res.moveToNext();\n        }\n        return arrayList;\n    }\n\n    public int count() {\n        SQLiteDatabase db = getReadableDatabase();\n        Cursor cursor = db.rawQuery(\"select * from cars\", null);\n        if (cursor != null && cursor.getCount() > 0) {\n            cursor.moveToFirst();\n            return cursor.getInt(0);\n        } else {\n            return 0;\n        }\n    }\n}\n"
  },
  {
    "path": "sample-app/src/main/java/com/sample/database/ContactDBHelper.java",
    "content": "/*\n *\n *  *    Copyright (C) 2019 Amit Shekhar\n *  *    Copyright (C) 2011 Android Open Source Project\n *  *\n *  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n *  *    you may not use this file except in compliance with the License.\n *  *    You may obtain a copy of the License at\n *  *\n *  *        http://www.apache.org/licenses/LICENSE-2.0\n *  *\n *  *    Unless required by applicable law or agreed to in writing, software\n *  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  *    See the License for the specific language governing permissions and\n *  *    limitations under the License.\n *\n */\n\npackage com.sample.database;\n\nimport android.content.ContentValues;\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.database.DatabaseUtils;\nimport android.database.sqlite.SQLiteDatabase;\nimport android.database.sqlite.SQLiteOpenHelper;\n\nimport java.util.ArrayList;\nimport java.util.Calendar;\n\n/**\n * Created by amitshekhar on 16/11/16.\n */\npublic class ContactDBHelper extends SQLiteOpenHelper {\n\n    public static final String DATABASE_NAME = \"Contact.db\";\n    public static final String CONTACTS_TABLE_NAME = \"contacts\";\n    public static final String CONTACTS_COLUMN_ID = \"id\";\n    public static final String CONTACTS_COLUMN_NAME = \"name\";\n    public static final String CONTACTS_COLUMN_EMAIL = \"email\";\n    public static final String CONTACTS_COLUMN_STREET = \"street\";\n    public static final String CONTACTS_COLUMN_CITY = \"place\";\n    public static final String CONTACTS_COLUMN_PHONE = \"phone\";\n    public static final String CONTACTS_CREATED_AT = \"createdAt\";\n\n    public ContactDBHelper(Context context) {\n        super(context, DATABASE_NAME, null, 1);\n    }\n\n    @Override\n    public void onCreate(SQLiteDatabase db) {\n        // TODO Auto-generated method stub\n        db.execSQL(\n                \"create table contacts \" +\n                        \"(id integer primary key, name text, phone text, email text, street text, place text, createdAt integer)\"\n        );\n    }\n\n    @Override\n    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {\n        // TODO Auto-generated method stub\n        db.execSQL(\"DROP TABLE IF EXISTS contacts\");\n        onCreate(db);\n    }\n\n    public boolean insertContact(String name, String phone, String email, String street, String place) {\n        SQLiteDatabase db = this.getWritableDatabase();\n        ContentValues contentValues = new ContentValues();\n        contentValues.put(\"name\", name);\n        contentValues.put(\"phone\", phone);\n        contentValues.put(\"email\", email);\n        contentValues.put(\"street\", street);\n        contentValues.put(\"place\", place);\n        contentValues.put(CONTACTS_CREATED_AT, Calendar.getInstance().getTimeInMillis());\n        db.insert(\"contacts\", null, contentValues);\n        return true;\n    }\n\n    public Cursor getData(int id) {\n        SQLiteDatabase db = this.getReadableDatabase();\n        Cursor res = db.rawQuery(\"select * from contacts where id=\" + id + \"\", null);\n        return res;\n    }\n\n    public int numberOfRows() {\n        SQLiteDatabase db = this.getReadableDatabase();\n        int numRows = (int) DatabaseUtils.queryNumEntries(db, CONTACTS_TABLE_NAME);\n        return numRows;\n    }\n\n    public boolean updateContact(Integer id, String name, String phone, String email, String street, String place) {\n        SQLiteDatabase db = this.getWritableDatabase();\n        ContentValues contentValues = new ContentValues();\n        contentValues.put(\"name\", name);\n        contentValues.put(\"phone\", phone);\n        contentValues.put(\"email\", email);\n        contentValues.put(\"street\", street);\n        contentValues.put(\"place\", place);\n        db.update(\"contacts\", contentValues, \"id = ? \", new String[]{Integer.toString(id)});\n        return true;\n    }\n\n    public Integer deleteContact(Integer id) {\n        SQLiteDatabase db = this.getWritableDatabase();\n        return db.delete(\"contacts\",\n                \"id = ? \",\n                new String[]{Integer.toString(id)});\n    }\n\n    public ArrayList<String> getAllCotacts() {\n        ArrayList<String> arrayList = new ArrayList<>();\n\n        //hp = new HashMap();\n        SQLiteDatabase db = this.getReadableDatabase();\n        Cursor res = db.rawQuery(\"select * from contacts\", null);\n        res.moveToFirst();\n\n        while (!res.isAfterLast()) {\n            arrayList.add(res.getString(res.getColumnIndex(CONTACTS_COLUMN_NAME)));\n            res.moveToNext();\n        }\n        return arrayList;\n    }\n\n    public int count() {\n        SQLiteDatabase db = getReadableDatabase();\n        Cursor cursor = db.rawQuery(\"select * from contacts\", null);\n        if (cursor != null && cursor.getCount() > 0) {\n            cursor.moveToFirst();\n            return cursor.getInt(0);\n        } else {\n            return 0;\n        }\n    }\n}\n"
  },
  {
    "path": "sample-app/src/main/java/com/sample/database/ExtTestDBHelper.java",
    "content": "package com.sample.database;\n\nimport android.content.ContentValues;\nimport android.content.Context;\nimport android.content.ContextWrapper;\nimport android.database.Cursor;\nimport android.database.DatabaseErrorHandler;\nimport android.database.sqlite.SQLiteDatabase;\nimport android.database.sqlite.SQLiteOpenHelper;\n\nimport java.io.File;\nimport java.util.Calendar;\n\npublic class ExtTestDBHelper extends SQLiteOpenHelper {\n\n    public static final String DIR_NAME = \"custom_dir\";\n    public static final String DATABASE_NAME = \"ExtTest.db\";\n    public static final String TEST_TABLE_NAME = \"test\";\n    public static final String TEST_ID = \"id\";\n    public static final String TEST_COLUMN_VALUE = \"value\";\n    public static final String TEST_CREATED_AT = \"createdAt\";\n\n    public ExtTestDBHelper(Context context) {\n        super(new CustomDatabasePathContext(context), DATABASE_NAME, null, 1);\n    }\n\n    @Override\n    public void onCreate(SQLiteDatabase db) {\n        // TODO Auto-generated method stub\n        db.execSQL(\n                String.format(\n                        \"create table %s (%s integer primary key, %s text, %s integer)\",\n                        TEST_TABLE_NAME,\n                        TEST_ID,\n                        TEST_COLUMN_VALUE,\n                        TEST_CREATED_AT\n                )\n        );\n    }\n\n    @Override\n    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {\n        // TODO Auto-generated method stub\n        db.execSQL(\"DROP TABLE IF EXISTS \" + TEST_TABLE_NAME);\n        onCreate(db);\n    }\n\n    public boolean insertTest(String value) {\n        SQLiteDatabase db = this.getWritableDatabase();\n        ContentValues contentValues = new ContentValues();\n        contentValues.put(\"value\", value);\n        contentValues.put(TEST_CREATED_AT, Calendar.getInstance().getTimeInMillis());\n        db.insert(TEST_TABLE_NAME, null, contentValues);\n        return true;\n    }\n\n    public int count() {\n        SQLiteDatabase db = getReadableDatabase();\n        Cursor cursor = db.rawQuery(\"select COUNT(*) from \" + TEST_TABLE_NAME, null);\n        if (cursor != null && cursor.getCount() > 0) {\n            cursor.moveToFirst();\n            return cursor.getInt(0);\n        } else {\n            return 0;\n        }\n    }\n\n    private static class CustomDatabasePathContext extends ContextWrapper {\n\n        public CustomDatabasePathContext(Context base) {\n            super(base);\n        }\n\n        @Override\n        public File getDatabasePath(String name) {\n            File databaseDir = new File(String.format(\"%s/%s\", getFilesDir(), ExtTestDBHelper.DIR_NAME));\n            databaseDir.mkdirs();\n            File databaseFile = new File(String.format(\"%s/%s/%s\", getFilesDir(), ExtTestDBHelper.DIR_NAME, name));\n            return databaseFile;\n        }\n\n        @Override\n        public SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory) {\n            return SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), null);\n        }\n\n        @Override\n        public SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory, DatabaseErrorHandler errorHandler) {\n            return SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), null);\n        }\n    }\n}\n"
  },
  {
    "path": "sample-app/src/main/java/com/sample/database/room/AppDatabase.java",
    "content": "package com.sample.database.room;\n\n\nimport androidx.room.Database;\nimport androidx.room.RoomDatabase;\n\n/**\n * Created by anandgaurav on 12/02/18.\n */\n@Database(entities = {User.class}, version = 1, exportSchema = false)\npublic abstract class AppDatabase extends RoomDatabase {\n\n    public abstract UserDao userDao();\n\n}\n"
  },
  {
    "path": "sample-app/src/main/java/com/sample/database/room/User.java",
    "content": "package com.sample.database.room;\n\n\nimport androidx.room.Entity;\nimport androidx.room.PrimaryKey;\n\n/**\n * Created by anandgaurav on 12/02/18.\n */\n@Entity(tableName = \"users\")\npublic class User {\n\n    @PrimaryKey\n    public Long id;\n\n    public String name;\n\n}\n"
  },
  {
    "path": "sample-app/src/main/java/com/sample/database/room/UserDBHelper.java",
    "content": "package com.sample.database.room;\n\nimport android.content.Context;\n\nimport androidx.room.Room;\nimport androidx.sqlite.db.SupportSQLiteDatabase;\n\nimport java.util.List;\n\n/**\n * Created by anandgaurav on 12/02/18.\n */\n\npublic class UserDBHelper {\n\n    private final AppDatabase appDatabase;\n    private final AppDatabase inMemoryAppDatabase;\n\n    public UserDBHelper(Context context) {\n        appDatabase = Room.databaseBuilder(context, AppDatabase.class, \"User.db\")\n                .allowMainThreadQueries()\n                .build();\n        inMemoryAppDatabase = Room.inMemoryDatabaseBuilder(context, AppDatabase.class)\n                .allowMainThreadQueries()\n                .build();\n    }\n\n    public void insertUser(List<User> userList) {\n        appDatabase.userDao().insertAll(userList);\n    }\n\n    public void insertUserInMemory(List<User> userList) {\n        inMemoryAppDatabase.userDao().insertAll(userList);\n    }\n\n    public int count() {\n        return appDatabase.userDao().loadAll().size();\n    }\n\n    public int countInMemory() {\n        return inMemoryAppDatabase.userDao().loadAll().size();\n    }\n\n    public SupportSQLiteDatabase getInMemoryDatabase() {\n        return inMemoryAppDatabase.getOpenHelper().getWritableDatabase();\n    }\n}\n"
  },
  {
    "path": "sample-app/src/main/java/com/sample/database/room/UserDao.java",
    "content": "package com.sample.database.room;\n\n\nimport androidx.room.Dao;\nimport androidx.room.Delete;\nimport androidx.room.Insert;\nimport androidx.room.OnConflictStrategy;\nimport androidx.room.Query;\n\nimport java.util.List;\n\n/**\n * Created by anandgaurav on 12/02/18.\n */\n\n@Dao\npublic interface UserDao {\n\n    @Query(\"SELECT * FROM users\")\n    List<User> loadAll();\n\n    @Query(\"SELECT * FROM users WHERE id IN (:userIds)\")\n    List<User> loadAllByIds(List<Integer> userIds);\n\n    @Insert(onConflict = OnConflictStrategy.REPLACE)\n    void insert(User user);\n\n    @Insert(onConflict = OnConflictStrategy.REPLACE)\n    void insertAll(List<User> users);\n\n    @Delete\n    void delete(User user);\n\n}\n"
  },
  {
    "path": "sample-app/src/main/java/com/sample/utils/Utils.java",
    "content": "/*\n *\n *  *    Copyright (C) 2019 Amit Shekhar\n *  *    Copyright (C) 2011 Android Open Source Project\n *  *\n *  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n *  *    you may not use this file except in compliance with the License.\n *  *    You may obtain a copy of the License at\n *  *\n *  *        http://www.apache.org/licenses/LICENSE-2.0\n *  *\n *  *    Unless required by applicable law or agreed to in writing, software\n *  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  *    See the License for the specific language governing permissions and\n *  *    limitations under the License.\n *\n */\n\npackage com.sample.utils;\n\nimport android.content.Context;\nimport android.util.Pair;\nimport android.widget.Toast;\n\nimport androidx.sqlite.db.SupportSQLiteDatabase;\n\nimport com.sample.BuildConfig;\nimport com.sample.database.ExtTestDBHelper;\n\nimport java.io.File;\nimport java.lang.reflect.Method;\nimport java.util.HashMap;\n\n/**\n * Created by amitshekhar on 07/02/17.\n */\n\npublic class Utils {\n\n    private Utils() {\n        // This class is not publicly instantiable\n    }\n\n    public static void showDebugDBAddressLogToast(Context context) {\n        if (BuildConfig.DEBUG) {\n            try {\n                Class<?> debugDB = Class.forName(\"com.amitshekhar.DebugDB\");\n                Method getAddressLog = debugDB.getMethod(\"getAddressLog\");\n                Object value = getAddressLog.invoke(null);\n                Toast.makeText(context, (String) value, Toast.LENGTH_LONG).show();\n            } catch (Exception ignore) {\n\n            }\n        }\n    }\n\n    public static void setCustomDatabaseFiles(Context context) {\n        if (BuildConfig.DEBUG) {\n            try {\n                Class<?> debugDB = Class.forName(\"com.amitshekhar.DebugDB\");\n                Class[] argTypes = new Class[]{HashMap.class};\n                Method setCustomDatabaseFiles = debugDB.getMethod(\"setCustomDatabaseFiles\", argTypes);\n                HashMap<String, Pair<File, String>> customDatabaseFiles = new HashMap<>();\n                // set your custom database files\n                customDatabaseFiles.put(ExtTestDBHelper.DATABASE_NAME,\n                        new Pair<>(new File(context.getFilesDir() + \"/\" + ExtTestDBHelper.DIR_NAME +\n                                \"/\" + ExtTestDBHelper.DATABASE_NAME), \"\"));\n                setCustomDatabaseFiles.invoke(null, customDatabaseFiles);\n            } catch (Exception ignore) {\n\n            }\n        }\n    }\n\n    public static void setInMemoryRoomDatabases(SupportSQLiteDatabase... database) {\n        if (BuildConfig.DEBUG) {\n            try {\n                Class<?> debugDB = Class.forName(\"com.amitshekhar.DebugDB\");\n                Class[] argTypes = new Class[]{HashMap.class};\n                HashMap<String, SupportSQLiteDatabase> inMemoryDatabases = new HashMap<>();\n                // set your inMemory databases\n                inMemoryDatabases.put(\"InMemoryOne.db\", database[0]);\n                Method setRoomInMemoryDatabase = debugDB.getMethod(\"setInMemoryRoomDatabases\", argTypes);\n                setRoomInMemoryDatabase.invoke(null, inMemoryDatabases);\n            } catch (Exception ignore) {\n\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "sample-app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ /*\n  ~  *    Copyright (C) 2019 Amit Shekhar\n  ~  *    Copyright (C) 2011 Android Open Source Project\n  ~  *\n  ~  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~  *    you may not use this file except in compliance with the License.\n  ~  *    You may obtain a copy of the License at\n  ~  *\n  ~  *        http://www.apache.org/licenses/LICENSE-2.0\n  ~  *\n  ~  *    Unless required by applicable law or agreed to in writing, software\n  ~  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~  *    See the License for the specific language governing permissions and\n  ~  *    limitations under the License.\n  ~  */\n  -->\n\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/activity_main\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    tools:context=\"com.sample.MainActivity\">\n\n    <Button\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:textColor=\"@android:color/black\"\n        android:onClick=\"showDebugDbAddress\"\n        android:layout_gravity=\"center\"\n        android:text=\"@string/show_debug_db_address\" />\n\n</FrameLayout>\n"
  },
  {
    "path": "sample-app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ /*\n  ~  *    Copyright (C) 2019 Amit Shekhar\n  ~  *    Copyright (C) 2011 Android Open Source Project\n  ~  *\n  ~  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~  *    you may not use this file except in compliance with the License.\n  ~  *    You may obtain a copy of the License at\n  ~  *\n  ~  *        http://www.apache.org/licenses/LICENSE-2.0\n  ~  *\n  ~  *    Unless required by applicable law or agreed to in writing, software\n  ~  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~  *    See the License for the specific language governing permissions and\n  ~  *    limitations under the License.\n  ~  */\n  -->\n\n<resources>\n    <color name=\"colorPrimary\">#3F51B5</color>\n    <color name=\"colorPrimaryDark\">#303F9F</color>\n    <color name=\"colorAccent\">#FF4081</color>\n</resources>\n"
  },
  {
    "path": "sample-app/src/main/res/values/dimens.xml",
    "content": "<!--\n  ~ /*\n  ~  *    Copyright (C) 2019 Amit Shekhar\n  ~  *    Copyright (C) 2011 Android Open Source Project\n  ~  *\n  ~  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~  *    you may not use this file except in compliance with the License.\n  ~  *    You may obtain a copy of the License at\n  ~  *\n  ~  *        http://www.apache.org/licenses/LICENSE-2.0\n  ~  *\n  ~  *    Unless required by applicable law or agreed to in writing, software\n  ~  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~  *    See the License for the specific language governing permissions and\n  ~  *    limitations under the License.\n  ~  */\n  -->\n\n<resources>\n    <!-- Default screen margins, per the Android Design guidelines. -->\n    <dimen name=\"activity_horizontal_margin\">16dp</dimen>\n    <dimen name=\"activity_vertical_margin\">16dp</dimen>\n</resources>\n"
  },
  {
    "path": "sample-app/src/main/res/values/strings.xml",
    "content": "<!--\n  ~ /*\n  ~  *    Copyright (C) 2019 Amit Shekhar\n  ~  *    Copyright (C) 2011 Android Open Source Project\n  ~  *\n  ~  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~  *    you may not use this file except in compliance with the License.\n  ~  *    You may obtain a copy of the License at\n  ~  *\n  ~  *        http://www.apache.org/licenses/LICENSE-2.0\n  ~  *\n  ~  *    Unless required by applicable law or agreed to in writing, software\n  ~  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~  *    See the License for the specific language governing permissions and\n  ~  *    limitations under the License.\n  ~  */\n  -->\n\n<resources>\n    <string name=\"app_name\">Android-Debug-Database</string>\n    <string name=\"show_debug_db_address\">Show Debug Db Address</string>\n</resources>\n"
  },
  {
    "path": "sample-app/src/main/res/values/styles.xml",
    "content": "<!--\n  ~ /*\n  ~  *    Copyright (C) 2019 Amit Shekhar\n  ~  *    Copyright (C) 2011 Android Open Source Project\n  ~  *\n  ~  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~  *    you may not use this file except in compliance with the License.\n  ~  *    You may obtain a copy of the License at\n  ~  *\n  ~  *        http://www.apache.org/licenses/LICENSE-2.0\n  ~  *\n  ~  *    Unless required by applicable law or agreed to in writing, software\n  ~  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~  *    See the License for the specific language governing permissions and\n  ~  *    limitations under the License.\n  ~  */\n  -->\n\n<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n        <!-- Customize your theme here. -->\n        <item name=\"colorPrimary\">@color/colorPrimary</item>\n        <item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>\n        <item name=\"colorAccent\">@color/colorAccent</item>\n    </style>\n\n</resources>\n"
  },
  {
    "path": "sample-app/src/main/res/values-w820dp/dimens.xml",
    "content": "<!--\n  ~ /*\n  ~  *    Copyright (C) 2019 Amit Shekhar\n  ~  *    Copyright (C) 2011 Android Open Source Project\n  ~  *\n  ~  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~  *    you may not use this file except in compliance with the License.\n  ~  *    You may obtain a copy of the License at\n  ~  *\n  ~  *        http://www.apache.org/licenses/LICENSE-2.0\n  ~  *\n  ~  *    Unless required by applicable law or agreed to in writing, software\n  ~  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~  *    See the License for the specific language governing permissions and\n  ~  *    limitations under the License.\n  ~  */\n  -->\n\n<resources>\n    <!-- Example customization of dimensions originally defined in res/values/dimens.xml\n         (such as screen margins) for screens with more than 820dp of available width. This\n         would include 7\" and 10\" devices in landscape (~960dp and ~1280dp respectively). -->\n    <dimen name=\"activity_horizontal_margin\">64dp</dimen>\n</resources>\n"
  },
  {
    "path": "sample-app/src/test/java/com/sample/ExampleUnitTest.java",
    "content": "/*\n *\n *  *    Copyright (C) 2019 Amit Shekhar\n *  *    Copyright (C) 2011 Android Open Source Project\n *  *\n *  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n *  *    you may not use this file except in compliance with the License.\n *  *    You may obtain a copy of the License at\n *  *\n *  *        http://www.apache.org/licenses/LICENSE-2.0\n *  *\n *  *    Unless required by applicable law or agreed to in writing, software\n *  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  *    See the License for the specific language governing permissions and\n *  *    limitations under the License.\n *\n */\n\npackage com.sample;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\npublic class ExampleUnitTest {\n    @Test\n    public void addition_isCorrect() throws Exception {\n        assertEquals(4, 2 + 2);\n    }\n}\n"
  },
  {
    "path": "sample-app-encrypt/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "sample-app-encrypt/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdk rootProject.ext.compileSdk\n    defaultConfig {\n        minSdk rootProject.ext.minSdk\n        applicationId \"com.sample.encrypt\"\n        targetSdk rootProject.ext.targetSdk\n        versionCode 1\n        versionName \"1.0\"\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n\n    }\n    buildTypes {\n        debug {\n            resValue(\"string\", \"PORT_NUMBER\", \"8080\")\n            resValue(\"string\", \"DB_PASSWORD_PERSON\", \"a_password\")\n        }\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n}\n\ndependencies {\n    debugImplementation project(':debug-db-encrypt')\n    implementation 'androidx.appcompat:appcompat:1.4.2'\n    implementation 'net.zetetic:android-database-sqlcipher:3.5.9'\n    implementation \"androidx.room:room-runtime:2.5.0\"\n    annotationProcessor \"androidx.room:room-compiler:2.5.0\"\n    testImplementation 'junit:junit:4.13.2'\n    androidTestImplementation 'androidx.test:runner:1.5.2'\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'\n}\n"
  },
  {
    "path": "sample-app-encrypt/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "sample-app-encrypt/src/androidTest/java/com/sample/encrypt/ExampleInstrumentedTest.java",
    "content": "package com.sample.encrypt;\n\nimport android.content.Context;\nimport android.support.test.InstrumentationRegistry;\nimport android.support.test.runner.AndroidJUnit4;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\nimport static org.junit.Assert.*;\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\n@RunWith(AndroidJUnit4.class)\npublic class ExampleInstrumentedTest {\n    @Test\n    public void useAppContext() {\n        // Context of the app under test.\n        Context appContext = InstrumentationRegistry.getTargetContext();\n\n        assertEquals(\"com.sample.encrypt\", appContext.getPackageName());\n    }\n}\n"
  },
  {
    "path": "sample-app-encrypt/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.sample.encrypt\">\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/AppTheme\">\n        <activity\n            android:name=\".MainActivity\"\n            android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n    </application>\n\n</manifest>"
  },
  {
    "path": "sample-app-encrypt/src/main/java/com/sample/encrypt/MainActivity.java",
    "content": "/*\n *\n *  *    Copyright (C) 2019 Amit Shekhar\n *  *    Copyright (C) 2011 Android Open Source Project\n *  *\n *  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n *  *    you may not use this file except in compliance with the License.\n *  *    You may obtain a copy of the License at\n *  *\n *  *        http://www.apache.org/licenses/LICENSE-2.0\n *  *\n *  *    Unless required by applicable law or agreed to in writing, software\n *  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  *    See the License for the specific language governing permissions and\n *  *    limitations under the License.\n *\n */\n\npackage com.sample.encrypt;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.content.SharedPreferences;\nimport android.os.Bundle;\nimport android.preference.PreferenceManager;\nimport android.view.View;\n\nimport androidx.appcompat.app.AppCompatActivity;\n\nimport com.sample.encrypt.database.CarDBHelper;\nimport com.sample.encrypt.database.ContactDBHelper;\nimport com.sample.encrypt.database.ExtTestDBHelper;\nimport com.sample.encrypt.database.PersonDBHelper;\nimport com.sample.encrypt.database.room.User;\nimport com.sample.encrypt.database.room.UserDBHelper;\nimport com.sample.encrypt.utils.Utils;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\npublic class MainActivity extends AppCompatActivity {\n\n    @SuppressLint(\"CommitPrefEdits\")\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n\n        super.onCreate(savedInstanceState);\n\n        setContentView(R.layout.activity_main);\n\n        Set<String> stringSet = new HashSet<>();\n        stringSet.add(\"SetOne\");\n        stringSet.add(\"SetTwo\");\n        stringSet.add(\"SetThree\");\n\n        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());\n\n        SharedPreferences prefsOne = getSharedPreferences(\"countPrefOne\", Context.MODE_PRIVATE);\n        SharedPreferences prefsTwo = getSharedPreferences(\"countPrefTwo\", Context.MODE_PRIVATE);\n\n        sharedPreferences.edit().putString(\"testOne\", \"one\").commit();\n        sharedPreferences.edit().putInt(\"testTwo\", 2).commit();\n        sharedPreferences.edit().putLong(\"testThree\", 100000L).commit();\n        sharedPreferences.edit().putFloat(\"testFour\", 3.01F).commit();\n        sharedPreferences.edit().putBoolean(\"testFive\", true).commit();\n        sharedPreferences.edit().putStringSet(\"testSix\", stringSet).commit();\n\n        prefsOne.edit().putString(\"testOneNew\", \"one\").commit();\n\n        prefsTwo.edit().putString(\"testTwoNew\", \"two\").commit();\n\n        ContactDBHelper contactDBHelper = new ContactDBHelper(getApplicationContext());\n        if (contactDBHelper.count() == 0) {\n            for (int i = 0; i < 100; i++) {\n                String name = \"name_\" + i;\n                String phone = \"phone_\" + i;\n                String email = \"email_\" + i;\n                String street = \"street_\" + i;\n                String place = \"place_\" + i;\n                contactDBHelper.insertContact(name, phone, email, street, place);\n            }\n        }\n\n        CarDBHelper carDBHelper = new CarDBHelper(getApplicationContext());\n        if (carDBHelper.count() == 0) {\n            for (int i = 0; i < 50; i++) {\n                String name = \"name_\" + i;\n                String color = \"RED\";\n                float mileage = i + 10.45f;\n                carDBHelper.insertCar(name, color, mileage);\n            }\n        }\n\n        ExtTestDBHelper extTestDBHelper = new ExtTestDBHelper(getApplicationContext());\n        if (extTestDBHelper.count() == 0) {\n            for (int i = 0; i < 20; i++) {\n                String value = \"value_\" + i;\n                extTestDBHelper.insertTest(value);\n            }\n        }\n\n        // Create Person encrypted database\n        PersonDBHelper personDBHelper = new PersonDBHelper(getApplicationContext());\n        if (personDBHelper.count() == 0) {\n            for (int i = 0; i < 100; i++) {\n                String firstName = PersonDBHelper.PERSON_COLUMN_FIRST_NAME + \"_\" + i;\n                String lastName = PersonDBHelper.PERSON_COLUMN_LAST_NAME + \"_\" + i;\n                String address = PersonDBHelper.PERSON_COLUMN_ADDRESS + \"_\" + i;\n                personDBHelper.insertPerson(firstName, lastName, address);\n            }\n        }\n\n        // Room database\n        UserDBHelper userDBHelper = new UserDBHelper(getApplicationContext());\n        if (userDBHelper.count() == 0) {\n            List<User> userList = new ArrayList<>();\n            for (int i = 0; i < 20; i++) {\n                User user = new User();\n                user.id = (long) (i + 1);\n                user.name = \"user_\" + i;\n                userList.add(user);\n            }\n            userDBHelper.insertUser(userList);\n        }\n\n        // Room inMemory database\n        if (userDBHelper.countInMemory() == 0) {\n            List<User> userList = new ArrayList<>();\n            for (int i = 0; i < 20; i++) {\n                User user = new User();\n                user.id = (long) (i + 1);\n                user.name = \"in_memory_user_\" + i;\n                userList.add(user);\n            }\n            userDBHelper.insertUserInMemory(userList);\n        }\n\n        Utils.setCustomDatabaseFiles(getApplicationContext());\n        Utils.setInMemoryRoomDatabases(userDBHelper.getInMemoryDatabase());\n    }\n\n    public void showDebugDbAddress(View view) {\n        Utils.showDebugDBAddressLogToast(getApplicationContext());\n    }\n}\n"
  },
  {
    "path": "sample-app-encrypt/src/main/java/com/sample/encrypt/database/CarDBHelper.java",
    "content": "/*\n *\n *  *    Copyright (C) 2019 Amit Shekhar\n *  *    Copyright (C) 2011 Android Open Source Project\n *  *\n *  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n *  *    you may not use this file except in compliance with the License.\n *  *    You may obtain a copy of the License at\n *  *\n *  *        http://www.apache.org/licenses/LICENSE-2.0\n *  *\n *  *    Unless required by applicable law or agreed to in writing, software\n *  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  *    See the License for the specific language governing permissions and\n *  *    limitations under the License.\n *\n */\n\npackage com.sample.encrypt.database;\n\nimport android.content.ContentValues;\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.database.DatabaseUtils;\nimport android.database.sqlite.SQLiteDatabase;\nimport android.database.sqlite.SQLiteOpenHelper;\n\nimport java.util.ArrayList;\n\n/**\n * Created by amitshekhar on 06/02/17.\n */\n\npublic class CarDBHelper extends SQLiteOpenHelper {\n\n    public static final String DATABASE_NAME = \"Car.db\";\n    public static final String CARS_TABLE_NAME = \"cars\";\n    public static final String CARS_COLUMN_ID = \"id\";\n    public static final String CARS_COLUMN_NAME = \"name\";\n    public static final String CARS_COLUMN_COLOR = \"color\";\n    public static final String CCARS_COLUMN_MILEAGE = \"mileage\";\n\n    public CarDBHelper(Context context) {\n        super(context, DATABASE_NAME, null, 1);\n    }\n\n    @Override\n    public void onCreate(SQLiteDatabase db) {\n        // TODO Auto-generated method stub\n        db.execSQL(\n                \"create table cars \" +\n                        \"(id integer primary key, name text, color text, mileage real)\"\n        );\n\n        db.execSQL(\"create table [transaction] (id integer primary key, name text)\");\n\n        for (int i = 0; i < 10; i++) {\n            db.execSQL(\"insert into [transaction] (name) values ('hello');\");\n        }\n    }\n\n    @Override\n    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {\n        // TODO Auto-generated method stub\n        db.execSQL(\"DROP TABLE IF EXISTS cars\");\n        onCreate(db);\n    }\n\n    public boolean insertCar(String name, String color, float mileage) {\n        SQLiteDatabase db = this.getWritableDatabase();\n        ContentValues contentValues = new ContentValues();\n        contentValues.put(\"name\", name);\n        contentValues.put(\"color\", color);\n        contentValues.put(\"mileage\", mileage);\n        db.insert(\"cars\", null, contentValues);\n        return true;\n    }\n\n    public Cursor getData(int id) {\n        SQLiteDatabase db = this.getReadableDatabase();\n        Cursor res = db.rawQuery(\"select * from cars where id=\" + id + \"\", null);\n        return res;\n    }\n\n    public int numberOfRows() {\n        SQLiteDatabase db = this.getReadableDatabase();\n        int numRows = (int) DatabaseUtils.queryNumEntries(db, CARS_TABLE_NAME);\n        return numRows;\n    }\n\n    public boolean updateCar(Integer id, String name, String color, float mileage) {\n        SQLiteDatabase db = this.getWritableDatabase();\n        ContentValues contentValues = new ContentValues();\n        contentValues.put(\"name\", name);\n        contentValues.put(\"color\", color);\n        contentValues.put(\"mileage\", mileage);\n        db.update(\"cars\", contentValues, \"id = ? \", new String[]{Integer.toString(id)});\n        return true;\n    }\n\n    public Integer deleteCar(Integer id) {\n        SQLiteDatabase db = this.getWritableDatabase();\n        return db.delete(\"cars\",\n                \"id = ? \",\n                new String[]{Integer.toString(id)});\n    }\n\n    public ArrayList<String> getAllCars() {\n        ArrayList<String> arrayList = new ArrayList<>();\n\n        //hp = new HashMap();\n        SQLiteDatabase db = this.getReadableDatabase();\n        Cursor res = db.rawQuery(\"select * from cars\", null);\n        res.moveToFirst();\n\n        while (!res.isAfterLast()) {\n            arrayList.add(res.getString(res.getColumnIndex(CARS_COLUMN_NAME)));\n            res.moveToNext();\n        }\n        return arrayList;\n    }\n\n    public int count() {\n        SQLiteDatabase db = getReadableDatabase();\n        Cursor cursor = db.rawQuery(\"select * from cars\", null);\n        if (cursor != null && cursor.getCount() > 0) {\n            cursor.moveToFirst();\n            return cursor.getInt(0);\n        } else {\n            return 0;\n        }\n    }\n}\n"
  },
  {
    "path": "sample-app-encrypt/src/main/java/com/sample/encrypt/database/ContactDBHelper.java",
    "content": "/*\n *\n *  *    Copyright (C) 2019 Amit Shekhar\n *  *    Copyright (C) 2011 Android Open Source Project\n *  *\n *  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n *  *    you may not use this file except in compliance with the License.\n *  *    You may obtain a copy of the License at\n *  *\n *  *        http://www.apache.org/licenses/LICENSE-2.0\n *  *\n *  *    Unless required by applicable law or agreed to in writing, software\n *  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  *    See the License for the specific language governing permissions and\n *  *    limitations under the License.\n *\n */\n\npackage com.sample.encrypt.database;\n\nimport android.content.ContentValues;\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.database.DatabaseUtils;\nimport android.database.sqlite.SQLiteDatabase;\nimport android.database.sqlite.SQLiteOpenHelper;\n\nimport java.util.ArrayList;\nimport java.util.Calendar;\n\n/**\n * Created by amitshekhar on 16/11/16.\n */\npublic class ContactDBHelper extends SQLiteOpenHelper {\n\n    public static final String DATABASE_NAME = \"Contact.db\";\n    public static final String CONTACTS_TABLE_NAME = \"contacts\";\n    public static final String CONTACTS_COLUMN_ID = \"id\";\n    public static final String CONTACTS_COLUMN_NAME = \"name\";\n    public static final String CONTACTS_COLUMN_EMAIL = \"email\";\n    public static final String CONTACTS_COLUMN_STREET = \"street\";\n    public static final String CONTACTS_COLUMN_CITY = \"place\";\n    public static final String CONTACTS_COLUMN_PHONE = \"phone\";\n    public static final String CONTACTS_CREATED_AT = \"createdAt\";\n\n    public ContactDBHelper(Context context) {\n        super(context, DATABASE_NAME, null, 1);\n    }\n\n    @Override\n    public void onCreate(SQLiteDatabase db) {\n        // TODO Auto-generated method stub\n        db.execSQL(\n                \"create table contacts \" +\n                        \"(id integer primary key, name text, phone text, email text, street text, place text, createdAt integer)\"\n        );\n    }\n\n    @Override\n    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {\n        // TODO Auto-generated method stub\n        db.execSQL(\"DROP TABLE IF EXISTS contacts\");\n        onCreate(db);\n    }\n\n    public boolean insertContact(String name, String phone, String email, String street, String place) {\n        SQLiteDatabase db = this.getWritableDatabase();\n        ContentValues contentValues = new ContentValues();\n        contentValues.put(\"name\", name);\n        contentValues.put(\"phone\", phone);\n        contentValues.put(\"email\", email);\n        contentValues.put(\"street\", street);\n        contentValues.put(\"place\", place);\n        contentValues.put(CONTACTS_CREATED_AT, Calendar.getInstance().getTimeInMillis());\n        db.insert(\"contacts\", null, contentValues);\n        return true;\n    }\n\n    public Cursor getData(int id) {\n        SQLiteDatabase db = this.getReadableDatabase();\n        Cursor res = db.rawQuery(\"select * from contacts where id=\" + id + \"\", null);\n        return res;\n    }\n\n    public int numberOfRows() {\n        SQLiteDatabase db = this.getReadableDatabase();\n        int numRows = (int) DatabaseUtils.queryNumEntries(db, CONTACTS_TABLE_NAME);\n        return numRows;\n    }\n\n    public boolean updateContact(Integer id, String name, String phone, String email, String street, String place) {\n        SQLiteDatabase db = this.getWritableDatabase();\n        ContentValues contentValues = new ContentValues();\n        contentValues.put(\"name\", name);\n        contentValues.put(\"phone\", phone);\n        contentValues.put(\"email\", email);\n        contentValues.put(\"street\", street);\n        contentValues.put(\"place\", place);\n        db.update(\"contacts\", contentValues, \"id = ? \", new String[]{Integer.toString(id)});\n        return true;\n    }\n\n    public Integer deleteContact(Integer id) {\n        SQLiteDatabase db = this.getWritableDatabase();\n        return db.delete(\"contacts\",\n                \"id = ? \",\n                new String[]{Integer.toString(id)});\n    }\n\n    public ArrayList<String> getAllCotacts() {\n        ArrayList<String> arrayList = new ArrayList<>();\n\n        //hp = new HashMap();\n        SQLiteDatabase db = this.getReadableDatabase();\n        Cursor res = db.rawQuery(\"select * from contacts\", null);\n        res.moveToFirst();\n\n        while (!res.isAfterLast()) {\n            arrayList.add(res.getString(res.getColumnIndex(CONTACTS_COLUMN_NAME)));\n            res.moveToNext();\n        }\n        return arrayList;\n    }\n\n    public int count() {\n        SQLiteDatabase db = getReadableDatabase();\n        Cursor cursor = db.rawQuery(\"select * from contacts\", null);\n        if (cursor != null && cursor.getCount() > 0) {\n            cursor.moveToFirst();\n            return cursor.getInt(0);\n        } else {\n            return 0;\n        }\n    }\n}\n"
  },
  {
    "path": "sample-app-encrypt/src/main/java/com/sample/encrypt/database/ExtTestDBHelper.java",
    "content": "package com.sample.encrypt.database;\n\nimport android.content.ContentValues;\nimport android.content.Context;\nimport android.content.ContextWrapper;\nimport android.database.Cursor;\nimport android.database.DatabaseErrorHandler;\nimport android.database.sqlite.SQLiteDatabase;\nimport android.database.sqlite.SQLiteOpenHelper;\n\nimport java.io.File;\nimport java.util.Calendar;\n\npublic class ExtTestDBHelper extends SQLiteOpenHelper {\n\n    public static final String DIR_NAME = \"custom_dir\";\n    public static final String DATABASE_NAME = \"ExtTest.db\";\n    public static final String TEST_TABLE_NAME = \"test\";\n    public static final String TEST_ID = \"id\";\n    public static final String TEST_COLUMN_VALUE = \"value\";\n    public static final String TEST_CREATED_AT = \"createdAt\";\n\n    public ExtTestDBHelper(Context context) {\n        super(new CustomDatabasePathContext(context), DATABASE_NAME, null, 1);\n    }\n\n    @Override\n    public void onCreate(SQLiteDatabase db) {\n        // TODO Auto-generated method stub\n        db.execSQL(\n                String.format(\n                        \"create table %s (%s integer primary key, %s text, %s integer)\",\n                        TEST_TABLE_NAME,\n                        TEST_ID,\n                        TEST_COLUMN_VALUE,\n                        TEST_CREATED_AT\n                )\n        );\n    }\n\n    @Override\n    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {\n        // TODO Auto-generated method stub\n        db.execSQL(\"DROP TABLE IF EXISTS \" + TEST_TABLE_NAME);\n        onCreate(db);\n    }\n\n    public boolean insertTest(String value) {\n        SQLiteDatabase db = this.getWritableDatabase();\n        ContentValues contentValues = new ContentValues();\n        contentValues.put(\"value\", value);\n        contentValues.put(TEST_CREATED_AT, Calendar.getInstance().getTimeInMillis());\n        db.insert(TEST_TABLE_NAME, null, contentValues);\n        return true;\n    }\n\n    public int count() {\n        SQLiteDatabase db = getReadableDatabase();\n        Cursor cursor = db.rawQuery(\"select COUNT(*) from \" + TEST_TABLE_NAME, null);\n        if (cursor != null && cursor.getCount() > 0) {\n            cursor.moveToFirst();\n            return cursor.getInt(0);\n        } else {\n            return 0;\n        }\n    }\n\n    private static class CustomDatabasePathContext extends ContextWrapper {\n\n        public CustomDatabasePathContext(Context base) {\n            super(base);\n        }\n\n        @Override\n        public File getDatabasePath(String name) {\n            File databaseDir = new File(String.format(\"%s/%s\", getFilesDir(), ExtTestDBHelper.DIR_NAME));\n            databaseDir.mkdirs();\n            File databaseFile = new File(String.format(\"%s/%s/%s\", getFilesDir(), ExtTestDBHelper.DIR_NAME, name));\n            return databaseFile;\n        }\n\n        @Override\n        public SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory) {\n            return SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), null);\n        }\n\n        @Override\n        public SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory, DatabaseErrorHandler errorHandler) {\n            return SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), null);\n        }\n    }\n}\n"
  },
  {
    "path": "sample-app-encrypt/src/main/java/com/sample/encrypt/database/PersonDBHelper.java",
    "content": "/*\n *\n *  *    Copyright (C) 2019 Amit Shekhar\n *  *    Copyright (C) 2011 Android Open Source Project\n *  *\n *  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n *  *    you may not use this file except in compliance with the License.\n *  *    You may obtain a copy of the License at\n *  *\n *  *        http://www.apache.org/licenses/LICENSE-2.0\n *  *\n *  *    Unless required by applicable law or agreed to in writing, software\n *  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  *    See the License for the specific language governing permissions and\n *  *    limitations under the License.\n *\n */\n\npackage com.sample.encrypt.database;\n\nimport android.content.ContentValues;\nimport android.content.Context;\nimport android.database.Cursor;\n\nimport net.sqlcipher.DatabaseUtils;\nimport net.sqlcipher.database.SQLiteDatabase;\nimport net.sqlcipher.database.SQLiteOpenHelper;\n\nimport java.util.ArrayList;\n\npublic class PersonDBHelper extends SQLiteOpenHelper {\n\n    public static final String DATABASE_NAME = \"Person.db\";\n    public static final String PERSON_TABLE_NAME = \"person\";\n    public static final String PERSON_COLUMN_ID = \"id\";\n    public static final String PERSON_COLUMN_FIRST_NAME = \"first_name\";\n    public static final String PERSON_COLUMN_LAST_NAME = \"last_name\";\n    public static final String PERSON_COLUMN_ADDRESS = \"address\";\n    private static final String DB_PASSWORD = \"a_password\";\n\n    public PersonDBHelper(Context context) {\n\n        super(context, DATABASE_NAME, null, 1);\n        SQLiteDatabase.loadLibs(context);\n    }\n\n    @Override\n    public void onCreate(SQLiteDatabase db) {\n        db.execSQL(\n                \"create table person \" +\n                        \"(id integer primary key, first_name text, last_name text, address text)\"\n        );\n    }\n\n    @Override\n    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {\n        db.execSQL(\"DROP TABLE IF EXISTS person\");\n        onCreate(db);\n    }\n\n    public boolean insertPerson(String firstName, String lastName, String address) {\n        SQLiteDatabase db = this.getWritableDatabase(DB_PASSWORD);\n        ContentValues contentValues = new ContentValues();\n        contentValues.put(\"first_name\", firstName);\n        contentValues.put(\"last_name\", lastName);\n        contentValues.put(\"address\", address);\n        db.insert(\"person\", null, contentValues);\n        db.close();\n        return true;\n    }\n\n    public Cursor getData(int id) {\n        SQLiteDatabase db = this.getReadableDatabase(DB_PASSWORD);\n        Cursor res = db.rawQuery(\"select * from person where id=\" + id + \"\", null);\n        return res;\n    }\n\n    public int numberOfRows() {\n        SQLiteDatabase db = this.getReadableDatabase(DB_PASSWORD);\n        int numRows = (int) DatabaseUtils.queryNumEntries(db, PERSON_TABLE_NAME);\n        return numRows;\n    }\n\n    public boolean updatePerson(Integer id, String firstName, String lastName, String address, float mileage) {\n        SQLiteDatabase db = this.getWritableDatabase(DB_PASSWORD);\n        ContentValues contentValues = new ContentValues();\n        contentValues.put(\"first_name\", firstName);\n        contentValues.put(\"last_name\", lastName);\n        contentValues.put(\"address\", address);\n        db.update(\"person\", contentValues, \"id = ? \", new String[]{Integer.toString(id)});\n        db.close();\n        return true;\n    }\n\n    public Integer deletePerson(Integer id) {\n        SQLiteDatabase db = this.getWritableDatabase(DB_PASSWORD);\n        return db.delete(\"person\",\n                \"id = ? \",\n                new String[]{Integer.toString(id)});\n    }\n\n    public ArrayList<String> getAllPerson() {\n        ArrayList<String> arrayList = new ArrayList<>();\n\n        SQLiteDatabase db = this.getReadableDatabase(DB_PASSWORD);\n        Cursor res = db.rawQuery(\"select * from person\", null);\n        res.moveToFirst();\n\n        while (!res.isAfterLast()) {\n            arrayList.add(\n                    res.getString(res.getColumnIndex(PERSON_COLUMN_FIRST_NAME)) + \" \" +\n                            res.getString(res.getColumnIndex(PERSON_COLUMN_LAST_NAME)));\n            res.moveToNext();\n        }\n        res.close();\n        db.close();\n        return arrayList;\n    }\n\n    public int count() {\n        SQLiteDatabase db = getReadableDatabase(DB_PASSWORD);\n        Cursor cursor = db.rawQuery(\"select * from person\", null);\n        try {\n            if (cursor != null && cursor.getCount() > 0) {\n                cursor.moveToFirst();\n                return cursor.getInt(0);\n            } else {\n                return 0;\n            }\n        } finally {\n            cursor.close();\n            db.close();\n        }\n    }\n}\n"
  },
  {
    "path": "sample-app-encrypt/src/main/java/com/sample/encrypt/database/room/AppDatabase.java",
    "content": "package com.sample.encrypt.database.room;\n\n\nimport androidx.room.Database;\nimport androidx.room.RoomDatabase;\n\n/**\n * Created by anandgaurav on 12/02/18.\n */\n@Database(entities = {User.class}, version = 1, exportSchema = false)\npublic abstract class AppDatabase extends RoomDatabase {\n\n    public abstract UserDao userDao();\n\n}\n"
  },
  {
    "path": "sample-app-encrypt/src/main/java/com/sample/encrypt/database/room/User.java",
    "content": "package com.sample.encrypt.database.room;\n\nimport androidx.room.Entity;\nimport androidx.room.PrimaryKey;\n\n/**\n * Created by anandgaurav on 12/02/18.\n */\n@Entity(tableName = \"users\")\npublic class User {\n\n    @PrimaryKey\n    public Long id;\n\n    public String name;\n\n}\n"
  },
  {
    "path": "sample-app-encrypt/src/main/java/com/sample/encrypt/database/room/UserDBHelper.java",
    "content": "package com.sample.encrypt.database.room;\n\nimport android.content.Context;\n\nimport androidx.room.Room;\nimport androidx.sqlite.db.SupportSQLiteDatabase;\n\nimport java.util.List;\n\n/**\n * Created by anandgaurav on 12/02/18.\n */\n\npublic class UserDBHelper {\n\n    private final AppDatabase appDatabase;\n    private final AppDatabase inMemoryAppDatabase;\n\n    public UserDBHelper(Context context) {\n        appDatabase = Room.databaseBuilder(context, AppDatabase.class, \"User.db\")\n                .allowMainThreadQueries()\n                .build();\n        inMemoryAppDatabase = Room.inMemoryDatabaseBuilder(context, AppDatabase.class)\n                .allowMainThreadQueries()\n                .build();\n    }\n\n    public void insertUser(List<User> userList) {\n        appDatabase.userDao().insertAll(userList);\n    }\n\n    public void insertUserInMemory(List<User> userList) {\n        inMemoryAppDatabase.userDao().insertAll(userList);\n    }\n\n    public int count() {\n        return appDatabase.userDao().loadAll().size();\n    }\n\n    public int countInMemory() {\n        return inMemoryAppDatabase.userDao().loadAll().size();\n    }\n\n    public SupportSQLiteDatabase getInMemoryDatabase() {\n        return inMemoryAppDatabase.getOpenHelper().getWritableDatabase();\n    }\n}\n"
  },
  {
    "path": "sample-app-encrypt/src/main/java/com/sample/encrypt/database/room/UserDao.java",
    "content": "package com.sample.encrypt.database.room;\n\n\nimport androidx.room.Dao;\nimport androidx.room.Delete;\nimport androidx.room.Insert;\nimport androidx.room.OnConflictStrategy;\nimport androidx.room.Query;\n\nimport java.util.List;\n\n/**\n * Created by anandgaurav on 12/02/18.\n */\n\n@Dao\npublic interface UserDao {\n\n    @Query(\"SELECT * FROM users\")\n    List<User> loadAll();\n\n    @Query(\"SELECT * FROM users WHERE id IN (:userIds)\")\n    List<User> loadAllByIds(List<Integer> userIds);\n\n    @Insert(onConflict = OnConflictStrategy.REPLACE)\n    void insert(User user);\n\n    @Insert(onConflict = OnConflictStrategy.REPLACE)\n    void insertAll(List<User> users);\n\n    @Delete\n    void delete(User user);\n\n}\n"
  },
  {
    "path": "sample-app-encrypt/src/main/java/com/sample/encrypt/utils/Utils.java",
    "content": "/*\n *\n *  *    Copyright (C) 2019 Amit Shekhar\n *  *    Copyright (C) 2011 Android Open Source Project\n *  *\n *  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n *  *    you may not use this file except in compliance with the License.\n *  *    You may obtain a copy of the License at\n *  *\n *  *        http://www.apache.org/licenses/LICENSE-2.0\n *  *\n *  *    Unless required by applicable law or agreed to in writing, software\n *  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  *    See the License for the specific language governing permissions and\n *  *    limitations under the License.\n *\n */\n\npackage com.sample.encrypt.utils;\n\nimport android.content.Context;\nimport android.util.Pair;\nimport android.widget.Toast;\n\nimport androidx.sqlite.db.SupportSQLiteDatabase;\n\nimport com.sample.encrypt.BuildConfig;\nimport com.sample.encrypt.database.ExtTestDBHelper;\n\nimport java.io.File;\nimport java.lang.reflect.Method;\nimport java.util.HashMap;\n\n/**\n * Created by amitshekhar on 07/02/17.\n */\n\npublic class Utils {\n\n    private Utils() {\n        // This class is not publicly instantiable\n    }\n\n    public static void showDebugDBAddressLogToast(Context context) {\n        if (BuildConfig.DEBUG) {\n            try {\n                Class<?> debugDB = Class.forName(\"com.amitshekhar.DebugDB\");\n                Method getAddressLog = debugDB.getMethod(\"getAddressLog\");\n                Object value = getAddressLog.invoke(null);\n                Toast.makeText(context, (String) value, Toast.LENGTH_LONG).show();\n            } catch (Exception ignore) {\n\n            }\n        }\n    }\n\n    public static void setCustomDatabaseFiles(Context context) {\n        if (BuildConfig.DEBUG) {\n            try {\n                Class<?> debugDB = Class.forName(\"com.amitshekhar.DebugDB\");\n                Class[] argTypes = new Class[]{HashMap.class};\n                Method setCustomDatabaseFiles = debugDB.getMethod(\"setCustomDatabaseFiles\", argTypes);\n                HashMap<String, Pair<File, String>> customDatabaseFiles = new HashMap<>();\n                // set your custom database files\n                customDatabaseFiles.put(ExtTestDBHelper.DATABASE_NAME,\n                        new Pair<>(new File(context.getFilesDir() + \"/\" + ExtTestDBHelper.DIR_NAME +\n                                \"/\" + ExtTestDBHelper.DATABASE_NAME), \"\"));\n                setCustomDatabaseFiles.invoke(null, customDatabaseFiles);\n            } catch (Exception ignore) {\n\n            }\n        }\n    }\n\n    public static void setInMemoryRoomDatabases(SupportSQLiteDatabase... database) {\n        if (BuildConfig.DEBUG) {\n            try {\n                Class<?> debugDB = Class.forName(\"com.amitshekhar.DebugDB\");\n                Class[] argTypes = new Class[]{HashMap.class};\n                HashMap<String, SupportSQLiteDatabase> inMemoryDatabases = new HashMap<>();\n                // set your inMemory databases\n                inMemoryDatabases.put(\"InMemoryOne.db\", database[0]);\n                Method setRoomInMemoryDatabase = debugDB.getMethod(\"setInMemoryRoomDatabases\", argTypes);\n                setRoomInMemoryDatabase.invoke(null, inMemoryDatabases);\n            } catch (Exception ignore) {\n\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "sample-app-encrypt/src/main/res/drawable/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n    <path\n        android:fillColor=\"#008577\"\n        android:pathData=\"M0,0h108v108h-108z\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M9,0L9,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,0L19,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,0L29,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,0L39,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,0L49,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,0L59,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,0L69,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,0L79,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M89,0L89,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M99,0L99,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,9L108,9\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,19L108,19\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,29L108,29\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,39L108,39\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,49L108,49\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,59L108,59\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,69L108,69\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,79L108,79\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,89L108,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,99L108,99\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,29L89,29\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,39L89,39\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,49L89,49\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,59L89,59\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,69L89,69\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,79L89,79\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,19L29,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,19L39,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,19L49,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,19L59,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,19L69,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,19L79,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n</vector>\n"
  },
  {
    "path": "sample-app-encrypt/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n    <path\n        android:fillType=\"evenOdd\"\n        android:pathData=\"M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z\"\n        android:strokeWidth=\"1\"\n        android:strokeColor=\"#00000000\">\n        <aapt:attr name=\"android:fillColor\">\n            <gradient\n                android:endX=\"78.5885\"\n                android:endY=\"90.9159\"\n                android:startX=\"48.7653\"\n                android:startY=\"61.0927\"\n                android:type=\"linear\">\n                <item\n                    android:color=\"#44000000\"\n                    android:offset=\"0.0\" />\n                <item\n                    android:color=\"#00000000\"\n                    android:offset=\"1.0\" />\n            </gradient>\n        </aapt:attr>\n    </path>\n    <path\n        android:fillColor=\"#FFFFFF\"\n        android:fillType=\"nonZero\"\n        android:pathData=\"M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z\"\n        android:strokeWidth=\"1\"\n        android:strokeColor=\"#00000000\" />\n</vector>\n"
  },
  {
    "path": "sample-app-encrypt/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ /*\n  ~  *    Copyright (C) 2019 Amit Shekhar\n  ~  *    Copyright (C) 2011 Android Open Source Project\n  ~  *\n  ~  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~  *    you may not use this file except in compliance with the License.\n  ~  *    You may obtain a copy of the License at\n  ~  *\n  ~  *        http://www.apache.org/licenses/LICENSE-2.0\n  ~  *\n  ~  *    Unless required by applicable law or agreed to in writing, software\n  ~  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~  *    See the License for the specific language governing permissions and\n  ~  *    limitations under the License.\n  ~  */\n  -->\n\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/activity_main\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    tools:context=\"com.sample.encrypt.MainActivity\">\n\n    <Button\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:textColor=\"@android:color/black\"\n        android:onClick=\"showDebugDbAddress\"\n        android:layout_gravity=\"center\"\n        android:text=\"@string/show_debug_db_address\" />\n\n</FrameLayout>\n"
  },
  {
    "path": "sample-app-encrypt/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background\" />\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\" />\n</adaptive-icon>"
  },
  {
    "path": "sample-app-encrypt/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background\" />\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\" />\n</adaptive-icon>"
  },
  {
    "path": "sample-app-encrypt/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorPrimary\">#008577</color>\n    <color name=\"colorPrimaryDark\">#00574B</color>\n    <color name=\"colorAccent\">#D81B60</color>\n</resources>\n"
  },
  {
    "path": "sample-app-encrypt/src/main/res/values/dimens.xml",
    "content": "<!--\n  ~ /*\n  ~  *    Copyright (C) 2019 Amit Shekhar\n  ~  *    Copyright (C) 2011 Android Open Source Project\n  ~  *\n  ~  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~  *    you may not use this file except in compliance with the License.\n  ~  *    You may obtain a copy of the License at\n  ~  *\n  ~  *        http://www.apache.org/licenses/LICENSE-2.0\n  ~  *\n  ~  *    Unless required by applicable law or agreed to in writing, software\n  ~  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~  *    See the License for the specific language governing permissions and\n  ~  *    limitations under the License.\n  ~  */\n  -->\n\n<resources>\n    <!-- Default screen margins, per the Android Design guidelines. -->\n    <dimen name=\"activity_horizontal_margin\">16dp</dimen>\n    <dimen name=\"activity_vertical_margin\">16dp</dimen>\n</resources>\n"
  },
  {
    "path": "sample-app-encrypt/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">Sample App Encrypt</string>\n    <string name=\"show_debug_db_address\">Show Debug Db Address</string>\n</resources>\n"
  },
  {
    "path": "sample-app-encrypt/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n        <!-- Customize your theme here. -->\n        <item name=\"colorPrimary\">@color/colorPrimary</item>\n        <item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>\n        <item name=\"colorAccent\">@color/colorAccent</item>\n    </style>\n\n</resources>\n"
  },
  {
    "path": "sample-app-encrypt/src/test/java/com/sample/encrypt/ExampleUnitTest.java",
    "content": "package com.sample.encrypt;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\npublic class ExampleUnitTest {\n    @Test\n    public void addition_isCorrect() {\n        assertEquals(4, 2 + 2);\n    }\n}"
  },
  {
    "path": "settings.gradle",
    "content": "/*\n *\n *  *    Copyright (C) 2019 Amit Shekhar\n *  *    Copyright (C) 2011 Android Open Source Project\n *  *\n *  *    Licensed under the Apache License, Version 2.0 (the \"License\");\n *  *    you may not use this file except in compliance with the License.\n *  *    You may obtain a copy of the License at\n *  *\n *  *        http://www.apache.org/licenses/LICENSE-2.0\n *  *\n *  *    Unless required by applicable law or agreed to in writing, software\n *  *    distributed under the License is distributed on an \"AS IS\" BASIS,\n *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  *    See the License for the specific language governing permissions and\n *  *    limitations under the License.\n *\n */\npluginManagement {\n    repositories {\n        gradlePluginPortal()\n        google()\n        mavenCentral()\n    }\n}\ndependencyResolutionManagement {\n    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n    repositories {\n        google()\n        mavenCentral()\n    }\n}\nrootProject.name = \"Android-Debug-Database\"\ninclude ':sample-app'\ninclude ':debug-db-base'\ninclude ':debug-db'\ninclude ':debug-db-encrypt'\ninclude ':sample-app-encrypt'"
  }
]