├── app ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── values │ │ │ │ ├── strings.xml │ │ │ │ ├── colors.xml │ │ │ │ └── styles.xml │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ └── ic_launcher_round.xml │ │ │ ├── layout │ │ │ │ └── activity_main.xml │ │ │ ├── drawable-v24 │ │ │ │ └── ic_launcher_foreground.xml │ │ │ └── drawable │ │ │ │ └── ic_launcher_background.xml │ │ ├── java │ │ │ └── tomlezen │ │ │ │ └── androiddebuglib │ │ │ │ ├── room │ │ │ │ ├── RoomTest.kt │ │ │ │ ├── RoomTestDao.kt │ │ │ │ ├── TestRoomDatabase.kt │ │ │ │ └── RoomDBTestHelper.kt │ │ │ │ ├── database │ │ │ │ ├── TestTwoDb.kt │ │ │ │ ├── TestThreeDb.kt │ │ │ │ ├── TestOneDb.kt │ │ │ │ └── CustomDB.kt │ │ │ │ └── MainActivity.kt │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── tomlezen │ │ │ └── androiddebuglib │ │ │ └── ExampleUnitTest.kt │ └── androidTest │ │ └── java │ │ └── tomlezen │ │ └── androiddebuglib │ │ └── ExampleInstrumentedTest.kt ├── proguard-rules.pro └── build.gradle ├── lib ├── .gitignore ├── src │ ├── main │ │ ├── assets │ │ │ └── web │ │ │ │ ├── favicon.ico │ │ │ │ ├── runtime.a66f828dca56eeb90e02.js │ │ │ │ ├── assets │ │ │ │ └── img │ │ │ │ │ ├── logo-full.svg │ │ │ │ │ └── logo.svg │ │ │ │ ├── index.html │ │ │ │ └── 3rdpartylicenses.txt │ │ ├── res │ │ │ ├── values │ │ │ │ └── strings.xml │ │ │ └── xml │ │ │ │ └── file_paths.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── tlz │ │ │ │ └── ada │ │ │ │ ├── exceptions │ │ │ │ └── AdaException.kt │ │ │ │ ├── models │ │ │ │ ├── KeyValue.kt │ │ │ │ ├── TableInfo.kt │ │ │ │ ├── ServiceInfo.kt │ │ │ │ ├── Table.kt │ │ │ │ ├── Db.kt │ │ │ │ ├── ActivityInfo.kt │ │ │ │ ├── ProviderInfo.kt │ │ │ │ ├── Application.kt │ │ │ │ ├── Log.kt │ │ │ │ ├── DataResponse.kt │ │ │ │ ├── AdaResponse.kt │ │ │ │ ├── InitInfo.kt │ │ │ │ ├── FileInfo.kt │ │ │ │ ├── TableFieldInfo.kt │ │ │ │ └── ApplicationInfo.kt │ │ │ │ ├── handlers │ │ │ │ ├── RequestHandler.kt │ │ │ │ ├── InitRequestHandler.kt │ │ │ │ ├── ScreenShotHandler.kt │ │ │ │ ├── DefaultRequestHandler.kt │ │ │ │ ├── LogRequestHandler.kt │ │ │ │ ├── FileRequestHandler.kt │ │ │ │ └── AppRequestHandler.kt │ │ │ │ ├── db │ │ │ │ ├── SQLiteDb.kt │ │ │ │ ├── AdaDataProvider.kt │ │ │ │ ├── InMemorySQLiteDb.kt │ │ │ │ ├── NormalSQLiteDb.kt │ │ │ │ ├── AdaDataProviderImpl.kt │ │ │ │ └── SpDataProviderImpl.kt │ │ │ │ ├── Ada.kt │ │ │ │ ├── AdaInitProvider.kt │ │ │ │ ├── AdaConstUtils.kt │ │ │ │ ├── AdaTempFileManagerFactory.kt │ │ │ │ ├── AdaActivityLifeCycleListener.kt │ │ │ │ ├── socket │ │ │ │ ├── AdaWebSocket.kt │ │ │ │ └── AdaWSD.kt │ │ │ │ ├── AdaProvider.kt │ │ │ │ ├── AdaWebServer.kt │ │ │ │ └── Adas.kt │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── org │ │ │ └── nanohttpd2 │ │ │ ├── protocols │ │ │ ├── http │ │ │ │ ├── response │ │ │ │ │ ├── IStatus.java │ │ │ │ │ ├── ChunkedOutputStream.java │ │ │ │ │ └── Status.java │ │ │ │ ├── threading │ │ │ │ │ ├── IAsyncRunner.java │ │ │ │ │ └── DefaultAsyncRunner.java │ │ │ │ ├── tempfiles │ │ │ │ │ ├── DefaultTempFileManagerFactory.java │ │ │ │ │ ├── ITempFileManager.java │ │ │ │ │ ├── ITempFile.java │ │ │ │ │ ├── DefaultTempFile.java │ │ │ │ │ └── DefaultTempFileManager.java │ │ │ │ ├── sockets │ │ │ │ │ ├── DefaultServerSocketFactory.java │ │ │ │ │ └── SecureServerSocketFactory.java │ │ │ │ ├── request │ │ │ │ │ └── Method.java │ │ │ │ ├── content │ │ │ │ │ ├── Cookie.java │ │ │ │ │ ├── ContentType.java │ │ │ │ │ └── CookieHandler.java │ │ │ │ ├── IHTTPSession.java │ │ │ │ ├── ServerRunnable.java │ │ │ │ └── ClientHandler.java │ │ │ └── websockets │ │ │ │ ├── State.java │ │ │ │ ├── OpCode.java │ │ │ │ ├── CloseCode.java │ │ │ │ ├── WebSocketException.java │ │ │ │ └── CloseFrame.java │ │ │ └── util │ │ │ ├── IFactory.java │ │ │ ├── IHandler.java │ │ │ ├── IFactoryThrowing.java │ │ │ └── ServerRunner.java │ └── test │ │ └── java │ │ └── com │ │ └── tlz │ │ └── ada │ │ └── ExampleUnitTest.java ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── screenshots ├── Log.png ├── File.png ├── Database.png ├── application.png └── SharePrefrences.png ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── .gitignore ├── gradlew.bat ├── README.md └── gradlew /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /lib/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':lib' 2 | -------------------------------------------------------------------------------- /screenshots/Log.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlezen/AndDevelopAssistant/HEAD/screenshots/Log.png -------------------------------------------------------------------------------- /screenshots/File.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlezen/AndDevelopAssistant/HEAD/screenshots/File.png -------------------------------------------------------------------------------- /screenshots/Database.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlezen/AndDevelopAssistant/HEAD/screenshots/Database.png -------------------------------------------------------------------------------- /screenshots/application.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlezen/AndDevelopAssistant/HEAD/screenshots/application.png -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | AndroidDebugLib 3 | 4 | -------------------------------------------------------------------------------- /screenshots/SharePrefrences.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlezen/AndDevelopAssistant/HEAD/screenshots/SharePrefrences.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlezen/AndDevelopAssistant/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /lib/src/main/assets/web/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlezen/AndDevelopAssistant/HEAD/lib/src/main/assets/web/favicon.ico -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlezen/AndDevelopAssistant/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlezen/AndDevelopAssistant/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlezen/AndDevelopAssistant/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlezen/AndDevelopAssistant/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlezen/AndDevelopAssistant/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlezen/AndDevelopAssistant/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlezen/AndDevelopAssistant/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlezen/AndDevelopAssistant/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlezen/AndDevelopAssistant/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomlezen/AndDevelopAssistant/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /lib/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | lib 3 | 4 | 10000 5 | 6 | -------------------------------------------------------------------------------- /lib/src/main/res/xml/file_paths.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/exceptions/AdaException.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada.exceptions 2 | 3 | /** 4 | * 异常. 5 | * Created by Tomlezen. 6 | * Date: 2019-06-13. 7 | * Time: 22:08. 8 | */ 9 | class AdaException(message: String, val code: Int = 0) : Exception(message) -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Jun 06 21:25:02 CST 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip 7 | -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/models/KeyValue.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada.models 2 | 3 | import androidx.annotation.Keep 4 | 5 | /** 6 | * Created by tomlezen. 7 | * Data: 2018/2/5. 8 | * Time: 14:55. 9 | */ 10 | @Keep 11 | data class KeyValue(val key: String, val value: String?, val type: String) -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/models/TableInfo.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada.models 2 | 3 | import androidx.annotation.Keep 4 | 5 | /** 6 | * Created by tomlezen. 7 | * Data: 2018/2/1. 8 | * Time: 11:42. 9 | * @param name 表名. 10 | * @param fieldInfos 表中所有字段信息. 11 | */ 12 | @Keep 13 | class TableInfo(val name: String, val fieldInfos: List) -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/models/ServiceInfo.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada.models 2 | 3 | import androidx.annotation.Keep 4 | 5 | /** 6 | * Created by Tomlezen. 7 | * Data: 2018/9/4. 8 | * Time: 14:14. 9 | */ 10 | @Keep 11 | class ServiceInfo( 12 | val name: String, 13 | val permission: String?, 14 | val flags: Int, 15 | val exported: Boolean 16 | ) -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/models/Table.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada.models 2 | 3 | import androidx.annotation.Keep 4 | 5 | /** 6 | * 数据库表信息. 7 | * Created by tomlezen. 8 | * Data: 2018/1/27. 9 | * Time: 17:00. 10 | * @param version 数据库版本. 11 | * @param tableInfos 表信息. 12 | */ 13 | @Keep 14 | class Table(val version: Int, val tableInfos: List) -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/models/Db.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada.models 2 | 3 | import androidx.annotation.Keep 4 | 5 | /** 6 | * Created by tomlezen. 7 | * Data: 2018/1/30. 8 | * Time: 12:00. 9 | * @param name 数据库名. 10 | * @param version 数据库版本号. 11 | * @param tables 数据库中得所有表. 12 | */ 13 | @Keep 14 | data class Db(val name: String, val version: Int, val tables: List) -------------------------------------------------------------------------------- /app/src/main/java/tomlezen/androiddebuglib/room/RoomTest.kt: -------------------------------------------------------------------------------- 1 | package tomlezen.androiddebuglib.room 2 | 3 | import androidx.room.Entity 4 | import androidx.room.PrimaryKey 5 | 6 | /** 7 | * Author: tomlezen 8 | * Email: tomlezen@protonmail.com 9 | * Date: 2019-10-31 14:13 10 | */ 11 | @Entity 12 | data class RoomTest( 13 | @PrimaryKey 14 | val id: Int, 15 | val test1: String, 16 | val test2: Int 17 | ) -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/handlers/RequestHandler.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada.handlers 2 | 3 | import org.nanohttpd2.protocols.http.IHTTPSession 4 | import org.nanohttpd2.protocols.http.response.Response 5 | 6 | /** 7 | * 网络请求处理器. 8 | * 9 | * Created by Tomlezen. 10 | * Data: 2018/9/5. 11 | * Time: 13:55. 12 | */ 13 | interface RequestHandler { 14 | 15 | fun onRequest(session: IHTTPSession): Response? 16 | 17 | } -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/models/ActivityInfo.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada.models 2 | 3 | import androidx.annotation.Keep 4 | 5 | /** 6 | * Created by Tomlezen. 7 | * Data: 2018/9/4. 8 | * Time: 14:09. 9 | */ 10 | @Keep 11 | class ActivityInfo( 12 | val name: String, 13 | val launchMode: String?, 14 | val flags: Int, 15 | val configChanges: Int, 16 | val softInputMode: Int, 17 | val permission: String?, 18 | val exported: Boolean 19 | ) -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/models/ProviderInfo.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada.models 2 | 3 | import androidx.annotation.Keep 4 | 5 | /** 6 | * Created by Tomlezen. 7 | * Data: 2018/9/4. 8 | * Time: 14:14. 9 | */ 10 | @Keep 11 | class ProviderInfo( 12 | val name: String, 13 | val authority: String?, 14 | val readPermission: String?, 15 | val writePermission: String?, 16 | val grantUriPermissions: Boolean, 17 | val multiprocess: Boolean 18 | ) -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/models/Application.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada.models 2 | 3 | import androidx.annotation.Keep 4 | 5 | /** 6 | * 应用. 7 | * 8 | * Created by Tomlezen. 9 | * Data: 2018/9/4. 10 | * Time: 13:33. 11 | */ 12 | @Keep 13 | open class Application( 14 | val icon: String, 15 | val name: String, 16 | val pkg: String, 17 | val verName: String?, 18 | val verCode: Int?, 19 | val isSystemApp: Boolean, 20 | var size: Long 21 | ) -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/models/Log.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada.models 2 | 3 | import androidx.annotation.Keep 4 | 5 | /** 6 | * Created by Tomlezen. 7 | * Date: 2018/9/8. 8 | * Time: 下午8:25. 9 | * @param type String 日志类型. 10 | * @param level Int 日志级别. 11 | * @param content String 处理后的日志内容. 12 | * @param originalContent String 原始日志内容. 13 | */ 14 | @Keep 15 | data class Log(val type: String, val level: Int, var content: String, var originalContent: String) -------------------------------------------------------------------------------- /app/src/main/java/tomlezen/androiddebuglib/room/RoomTestDao.kt: -------------------------------------------------------------------------------- 1 | package tomlezen.androiddebuglib.room 2 | 3 | import androidx.room.Dao 4 | import androidx.room.Insert 5 | import androidx.room.OnConflictStrategy 6 | 7 | /** 8 | * Author: tomlezen 9 | * Email: tomlezen@protonmail.com 10 | * Date: 2019-10-31 14:15 11 | */ 12 | @Dao 13 | interface RoomTestDao { 14 | 15 | @Insert(onConflict = OnConflictStrategy.REPLACE) 16 | fun insert(data: RoomTest) 17 | 18 | } -------------------------------------------------------------------------------- /app/src/test/java/tomlezen/androiddebuglib/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package tomlezen.androiddebuglib 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/models/DataResponse.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada.models 2 | 3 | import androidx.annotation.Keep 4 | 5 | /** 6 | * Created by tomlezen. 7 | * Data: 2018/2/1. 8 | * Time: 13:20. 9 | * @param recordsTotal 数据库中总得记录条数. 10 | * @param recordsFiltered 过滤后得数据条数. 11 | * @param data 查询到得数据. 12 | * @param error 错误信息. 13 | */ 14 | @Keep 15 | class DataResponse(val recordsTotal: Int, val recordsFiltered: Int, val data: List, val error: String = "") -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/models/AdaResponse.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada.models 2 | 3 | import androidx.annotation.Keep 4 | 5 | /** 6 | * Created by tomlezen. 7 | * Data: 2018/1/27. 8 | * Time: 16:26. 9 | * @param status 状态码(200标识成功). 10 | * @param data 发送的具体内容. 11 | * @param errMsg 错误信息. 12 | * @param total 数据总数. 13 | */ 14 | @Keep 15 | class AdaResponse( 16 | val status: Int = 200, 17 | val data: Any? = null, 18 | val errMsg: String = "", 19 | val total: Int = 0 20 | ) -------------------------------------------------------------------------------- /app/src/main/java/tomlezen/androiddebuglib/room/TestRoomDatabase.kt: -------------------------------------------------------------------------------- 1 | package tomlezen.androiddebuglib.room 2 | 3 | import androidx.room.Database 4 | import androidx.room.RoomDatabase 5 | 6 | /** 7 | * Author: tomlezen 8 | * Email: tomlezen@protonmail.com 9 | * Date: 2019-10-31 14:05 10 | */ 11 | @Database(entities = [RoomTest::class], version = 1, exportSchema = false) 12 | abstract class TestRoomDatabase: RoomDatabase() { 13 | 14 | abstract fun roomTestDao(): RoomTestDao 15 | 16 | } -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/models/InitInfo.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada.models 2 | 3 | import androidx.annotation.Keep 4 | 5 | /** 6 | * @property dbs List 数据库列表. 7 | * @constructor 8 | */ 9 | @Keep 10 | class InitInfo( 11 | icon: String, 12 | name: String, 13 | pkg: String, 14 | verName: String?, 15 | verCode: Int?, 16 | isSystemApp: Boolean, 17 | size: Long, 18 | val logServer: String, 19 | val dbs: List 20 | ) : Application(icon, name, pkg, verName, verCode, isSystemApp, size) -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/models/FileInfo.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada.models 2 | 3 | import androidx.annotation.Keep 4 | 5 | /** 6 | * Created by Tomlezen. 7 | * Date: 2018/9/5. 8 | * Time: 下午9:35. 9 | */ 10 | @Keep 11 | class FileInfo( 12 | val name: String, 13 | val isDir: Boolean = false, 14 | val path: String, 15 | val size: Long, 16 | val isRead: Boolean, 17 | val isWrite: Boolean, 18 | val isHidden: Boolean, 19 | val modifyTime: Long, 20 | val children: List = listOf() 21 | ) -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/models/TableFieldInfo.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada.models 2 | 3 | import androidx.annotation.Keep 4 | 5 | /** 6 | * Created by tomlezen. 7 | * Data: 2018/2/1. 8 | * Time: 10:58. 9 | * @param name 字段名. 10 | * @param type 字段数据类型. 11 | * @param isPrimaryKey 是否是主键. 12 | * @param nullable 是否可空. 13 | * @param defValue 默认值. 14 | */ 15 | @Keep 16 | data class TableFieldInfo(val name: String, val type: String, val isPrimaryKey: Boolean = false, val nullable: Boolean, val defValue: String?) -------------------------------------------------------------------------------- /lib/src/test/java/com/tlz/ada/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.tlz.ada; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | // assertEquals(4, 2 + 2); 16 | String s = "select * from where 1 = 1"; 17 | System.out.println(s.substring(s.indexOf("where") + 5, s.length())); 18 | } 19 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/tomlezen/androiddebuglib/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package tomlezen.androiddebuglib 2 | 3 | import androidx.test.InstrumentationRegistry 4 | import androidx.test.runner.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getTargetContext() 22 | assertEquals("tomlezen.androiddebuglib", appContext.packageName) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /lib/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | android.enableJetifier=true 13 | android.useAndroidX=true 14 | org.gradle.jvmargs=-Xmx1536m 15 | 16 | # When configured, Gradle will run in incubating parallel mode. 17 | # This option should only be used with decoupled projects. More details, visit 18 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 19 | # org.gradle.parallel=true 20 | -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/db/SQLiteDb.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada.db 2 | 3 | import android.content.ContentValues 4 | import android.database.Cursor 5 | import android.database.SQLException 6 | 7 | 8 | /** 9 | * Created by Tomlezen. 10 | * Date: 2019-10-28. 11 | * Time: 21:53. 12 | */ 13 | interface SQLiteDb { 14 | 15 | val isOpen: Boolean 16 | 17 | val version: Int 18 | 19 | fun delete(table: String, whereClause: String?, whereArgs: Array?): Int 20 | 21 | fun close() 22 | 23 | fun rawQuery(sql: String, selectionArgs: Array? = null): Cursor 24 | 25 | @Throws(SQLException::class) 26 | fun execSQL(sql: String) 27 | 28 | fun insert(table: String, nullColumnHack: String?, values: ContentValues): Long 29 | 30 | fun update(table: String, values: ContentValues, whereClause: String, whereArgs: Array?): Int 31 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/models/ApplicationInfo.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada.models 2 | 3 | import android.content.pm.ApplicationInfo 4 | import androidx.annotation.Keep 5 | 6 | /** 7 | * Created by Tomlezen. 8 | * Data: 2018/9/4. 9 | * Time: 13:33. 10 | */ 11 | @Keep 12 | class ApplicationInfo( 13 | icon: String, 14 | name: String, 15 | pkg: String, 16 | isSystemApp: Boolean, 17 | verName: String, 18 | verCode: Int, 19 | var path: String, 20 | size: Long, 21 | val targetSdk: Int, 22 | val firstInstallTime: Long, 23 | val lastUpdateTime: Long, 24 | val permissions: List>, 25 | val activities: List, 26 | val services: List, 27 | val receivers: List, 28 | val providers: List, 29 | val applicationInfo: ApplicationInfo 30 | ) : Application(icon, name, pkg, verName, verCode, isSystemApp, size) -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/Ada.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Context 5 | import com.google.gson.Gson 6 | import com.google.gson.GsonBuilder 7 | import java.util.concurrent.Executors 8 | import java.util.concurrent.Future 9 | 10 | /** 11 | * Created by Tomlezen. 12 | * Date: 2019-06-13. 13 | * Time: 23:04. 14 | */ 15 | object Ada { 16 | /** Gson实例. */ 17 | val adaGson: Gson by lazy { GsonBuilder().create() } 18 | 19 | @SuppressLint("StaticFieldLeak") 20 | lateinit var adaWebServer: AdaWebServer 21 | internal set 22 | 23 | /** 线程池. */ 24 | private val adaExecutorService by lazy { Executors.newCachedThreadPool() } 25 | 26 | /** 27 | * 提交执行任务. 28 | * @param task () -> Unit 29 | * @return Future<*> 30 | */ 31 | fun submitTask(task: () -> Unit): Future<*> = adaExecutorService.submit(task) 32 | 33 | fun init(ctx: Context) { 34 | adaWebServer = AdaWebServer(ctx, ctx.adaServerPort()) 35 | } 36 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # Intellij 36 | *.iml 37 | .idea/ 38 | .idea/workspace.xml 39 | .idea/tasks.xml 40 | .idea/gradle.xml 41 | .idea/dictionaries 42 | .idea/libraries 43 | 44 | # Keystore files 45 | *.jks 46 | 47 | # External native build folder generated in Android Studio 2.2 and later 48 | .externalNativeBuild 49 | 50 | # Google Services (e.g. APIs or Firebase) 51 | google-services.json 52 | 53 | # Freeline 54 | freeline.py 55 | freeline/ 56 | freeline_project_description.json -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/AdaInitProvider.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada 2 | 3 | import android.content.ContentProvider 4 | import android.content.ContentValues 5 | import android.database.Cursor 6 | import android.net.Uri 7 | 8 | /** 9 | * Created by tomlezen. 10 | * Data: 2018/1/27. 11 | * Time: 14:48. 12 | */ 13 | class AdaInitProvider : ContentProvider() { 14 | 15 | override fun insert(uri: Uri?, values: ContentValues?): Uri? = null 16 | 17 | override fun query(uri: Uri?, projection: Array?, selection: String?, selectionArgs: Array?, sortOrder: String?): Cursor? = null 18 | 19 | override fun onCreate(): Boolean { 20 | context?.let { 21 | Ada.init(it) 22 | } 23 | return true 24 | } 25 | 26 | override fun update(uri: Uri?, values: ContentValues?, selection: String?, selectionArgs: Array?): Int = 0 27 | 28 | override fun delete(uri: Uri?, selection: String?, selectionArgs: Array?): Int = 0 29 | 30 | override fun getType(uri: Uri?): String = "" 31 | } -------------------------------------------------------------------------------- /lib/src/main/assets/web/runtime.a66f828dca56eeb90e02.js: -------------------------------------------------------------------------------- 1 | !function(r){function e(e){for(var t,p,c=e[0],a=e[1],f=e[2],l=0,s=[];l>) 16 | 17 | fun setInMemoryRoomDatabases(databases: Map) 18 | 19 | fun getAllDatabase(): List 20 | 21 | fun getDatabaseFile(dName: String): File? 22 | 23 | fun getAllTable(dName: String): Table 24 | 25 | fun getTableInfo(dName: String, tName: String): TableInfo 26 | 27 | fun getTableDataCount(dName: String, tName: String, where: String): Int 28 | 29 | fun query(dName: String, tName: String, sql: String): List 30 | 31 | fun rawQuery(dName: String, sql: String): Any 32 | 33 | fun add(dName: String, tName: String, content: Array): Boolean 34 | 35 | fun delete(dName: String, tName: String, where: String): Boolean 36 | 37 | fun update(dName: String, tName: String, content: Array, where: String): Boolean 38 | } -------------------------------------------------------------------------------- /app/src/main/java/tomlezen/androiddebuglib/database/TestThreeDb.kt: -------------------------------------------------------------------------------- 1 | package tomlezen.androiddebuglib.database 2 | 3 | import android.content.Context 4 | import android.database.sqlite.SQLiteDatabase 5 | import android.database.sqlite.SQLiteOpenHelper 6 | import java.util.* 7 | 8 | /** 9 | * Created by tomlezen. 10 | * Data: 2018/2/24. 11 | * Time: 11:15. 12 | */ 13 | class TestThreeDb(ctx: Context): SQLiteOpenHelper(ctx, "TestThree.db", null, 1) { 14 | 15 | override fun onCreate(db: SQLiteDatabase) { 16 | db.execSQL("create table table1 (id integer primary key, name text, age integer, birthday text, number text, address text, grade text, teacher text)") 17 | } 18 | 19 | override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { 20 | db.execSQL("DROP TABLE IF EXISTS table1") 21 | onCreate(db) 22 | } 23 | 24 | fun init() { 25 | (0..97).forEach { 26 | writableDatabase.execSQL("insert into table1 (name, age, birthday, number, address, grade, teacher) values ('name$it', ${Random().nextInt()}, 'birthday$it', 'number$it', 'address$it', ${(Random().nextFloat() * 100).toInt()}, 'teacher$it');") 27 | } 28 | 29 | close() 30 | } 31 | 32 | companion object { 33 | fun create(ctx: Context){ 34 | TestThreeDb(ctx).init() 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /lib/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 16 | 17 | 22 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/java/tomlezen/androiddebuglib/room/RoomDBTestHelper.kt: -------------------------------------------------------------------------------- 1 | package tomlezen.androiddebuglib.room 2 | 3 | import android.content.Context 4 | import androidx.room.Room 5 | import androidx.sqlite.db.SupportSQLiteDatabase 6 | 7 | 8 | /** 9 | * Author: tomlezen 10 | * Email: tomlezen@protonmail.com 11 | * Date: 2019-10-31 14:17 12 | */ 13 | class RoomDBTestHelper(ctx: Context) { 14 | 15 | private val roomAppDatabase = Room.databaseBuilder(ctx, TestRoomDatabase::class.java, "RoomTest") 16 | .allowMainThreadQueries() 17 | .build() 18 | 19 | private val _inMemoryAppDatabase = Room.inMemoryDatabaseBuilder(ctx, TestRoomDatabase::class.java) 20 | .allowMainThreadQueries() 21 | .build() 22 | 23 | val inMemoryAppDatabase: SupportSQLiteDatabase 24 | get() = _inMemoryAppDatabase.openHelper.writableDatabase 25 | 26 | val name: String 27 | get() = _inMemoryAppDatabase.openHelper.databaseName ?: "MemoryTest.db" 28 | 29 | fun init() { 30 | (0 until 100).forEach { 31 | _inMemoryAppDatabase.roomTestDao().insert(RoomTest(it, "test$it", 1000 + it)) 32 | } 33 | (0 until 100).forEach { 34 | roomAppDatabase.roomTestDao().insert(RoomTest(it, "test$it", 1000 + it)) 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/AdaTempFileManagerFactory.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada 2 | 3 | import android.content.Context 4 | import org.nanohttpd2.protocols.http.tempfiles.* 5 | import java.io.File 6 | 7 | /** 8 | * Created by Tomlezen. 9 | * Data: 2018/9/5. 10 | * Time: 12:51. 11 | */ 12 | class AdaTempFileManagerFactory(private val ctx: Context) : DefaultTempFileManagerFactory() { 13 | 14 | override fun create(): ITempFileManager = 15 | AndTempFileManager(ctx.externalCacheDir?.absolutePath + "/AndDevelopAssistant") 16 | 17 | class AndTempFileManager(tmpdir: String) : DefaultTempFileManager() { 18 | 19 | private val tempDirFile = File(tmpdir) 20 | private val tempFiles = mutableListOf() 21 | 22 | init { 23 | if (!tempDirFile.exists()) { 24 | tempDirFile.mkdirs() 25 | } else { 26 | // 清空之前未删除掉的文件 27 | runCatching { tempDirFile.listFiles().forEach { it.delete() } } 28 | } 29 | } 30 | 31 | override fun clear() { 32 | this.tempFiles.forEach { 33 | runCatching { it.delete() } 34 | } 35 | this.tempFiles.clear() 36 | } 37 | 38 | override fun createTempFile(filename_hint: String?): ITempFile = 39 | DefaultTempFile(tempDirFile).also { 40 | this.tempFiles += it 41 | } 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/db/InMemorySQLiteDb.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada.db 2 | 3 | import android.content.ContentValues 4 | import android.database.Cursor 5 | import androidx.sqlite.db.SupportSQLiteDatabase 6 | 7 | /** 8 | * Author: tomlezen 9 | * Email: tomlezen@protonmail.com 10 | * Date: 2019-10-30 13:31 11 | */ 12 | class InMemorySQLiteDb(private val db: SupportSQLiteDatabase) : SQLiteDb { 13 | 14 | override val isOpen: Boolean 15 | get() = db.isOpen 16 | 17 | override val version: Int 18 | get() = db.version 19 | 20 | override fun delete(table: String, whereClause: String?, whereArgs: Array?): Int = 21 | db.delete(table, whereClause, whereArgs) 22 | 23 | override fun close() { 24 | // db.close() 25 | } 26 | 27 | override fun rawQuery(sql: String, selectionArgs: Array?): Cursor = 28 | db.query(sql, selectionArgs) 29 | 30 | override fun execSQL(sql: String) { 31 | db.execSQL(sql) 32 | } 33 | 34 | override fun insert(table: String, nullColumnHack: String?, values: ContentValues): Long = 35 | db.insert(table, 0, values) 36 | 37 | override fun update(table: String, values: ContentValues, whereClause: String, whereArgs: Array?): Int = 38 | db.update(table, 0, values, whereClause, whereArgs) 39 | } -------------------------------------------------------------------------------- /app/src/main/java/tomlezen/androiddebuglib/database/TestOneDb.kt: -------------------------------------------------------------------------------- 1 | package tomlezen.androiddebuglib.database 2 | 3 | import android.content.Context 4 | import android.database.sqlite.SQLiteDatabase 5 | import android.database.sqlite.SQLiteOpenHelper 6 | import java.util.* 7 | 8 | /** 9 | * Created by tomlezen. 10 | * Data: 2018/2/24. 11 | * Time: 11:15. 12 | */ 13 | class TestOneDb(ctx: Context): SQLiteOpenHelper(ctx, "TestOne.db", null, 1) { 14 | 15 | override fun onCreate(db: SQLiteDatabase) { 16 | db.execSQL("create table table1 (id integer primary key, field1 text, field2 text, field3 real, field4 blob)") 17 | db.execSQL("create table table2 (id integer primary key, field1 text)") 18 | } 19 | 20 | override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { 21 | db.execSQL("DROP TABLE IF EXISTS table1") 22 | db.execSQL("DROP TABLE IF EXISTS table2") 23 | onCreate(db) 24 | } 25 | 26 | fun init() { 27 | (0..55).forEach { 28 | writableDatabase.execSQL("insert into table1 (field1, field2, field3) values ('value$it', 'content$it', ${Random().nextFloat()});") 29 | } 30 | (0..9).forEach { 31 | writableDatabase.execSQL("insert into table2 (field1) values ('hello$it');") 32 | } 33 | 34 | close() 35 | } 36 | 37 | companion object { 38 | fun create(ctx: Context){ 39 | TestOneDb(ctx).init() 40 | } 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/AdaActivityLifeCycleListener.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada 2 | 3 | import android.app.Activity 4 | import android.app.Application 5 | import android.content.Context 6 | import android.os.Bundle 7 | 8 | /** 9 | * Activity生命周期监听. 10 | * Created by tomlezen. 11 | * Data: 2019/3/18. 12 | * Time: 16:36. 13 | */ 14 | class AdaActivityLifeCycleListener(private val ctx: Context) { 15 | 16 | /** 当前Activity实例. */ 17 | var currentActivityInstance: Activity? = null 18 | 19 | /** Activity生命周期回调. */ 20 | private val activityLifecycleCallbacks = object : Application.ActivityLifecycleCallbacks { 21 | override fun onActivityPaused(activity: Activity?) { 22 | 23 | } 24 | 25 | override fun onActivityResumed(activity: Activity?) { 26 | currentActivityInstance = activity 27 | } 28 | 29 | override fun onActivityStarted(activity: Activity?) { 30 | 31 | } 32 | 33 | override fun onActivityDestroyed(activity: Activity?) { 34 | 35 | } 36 | 37 | override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) { 38 | 39 | } 40 | 41 | override fun onActivityStopped(activity: Activity?) { 42 | if (activity == currentActivityInstance) { 43 | currentActivityInstance = null 44 | } 45 | } 46 | 47 | override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) { 48 | 49 | } 50 | 51 | } 52 | 53 | fun install() { 54 | (ctx as Application).unregisterActivityLifecycleCallbacks(activityLifecycleCallbacks) 55 | ctx.registerActivityLifecycleCallbacks(activityLifecycleCallbacks) 56 | } 57 | 58 | } -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | apply plugin: 'kotlin-android' 4 | apply plugin: 'kotlin-kapt' 5 | apply plugin: 'kotlin-android-extensions' 6 | 7 | android { 8 | compileSdkVersion 28 9 | defaultConfig { 10 | applicationId "tomlezen.androiddebuglib" 11 | minSdkVersion 18 12 | targetSdkVersion 28 13 | versionCode 1 14 | versionName "1.0" 15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 16 | } 17 | buildTypes { 18 | debug { 19 | resValue("string", "ADA_DEBUG_PORT", "10901") 20 | } 21 | release { 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 24 | } 25 | } 26 | 27 | packagingOptions { 28 | exclude 'META-INF/*.kotlin_module' 29 | } 30 | } 31 | 32 | dependencies { 33 | implementation fileTree(dir: 'libs', include: ['*.jar']) 34 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 35 | implementation 'androidx.appcompat:appcompat:1.0.0' 36 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 37 | implementation 'com.tlz.andbase:andbase:0.0.1' 38 | implementation 'androidx.room:room-runtime:2.2.1' 39 | kapt 'androidx.room:room-compiler:2.2.1' 40 | // kapt 'android.arch.persistence.room:compiler:1.1.1' 41 | debugImplementation project(':lib') 42 | // debugImplementation 'com.tlz.tools:AndDevelopAssistant:0.1.1-no-x' 43 | 44 | testImplementation 'junit:junit:4.12' 45 | androidTestImplementation 'androidx.test:runner:1.1.0' 46 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0' 47 | } 48 | -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/handlers/InitRequestHandler.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada.handlers 2 | 3 | import android.content.Context 4 | import com.tlz.ada.* 5 | import com.tlz.ada.db.AdaDataProvider 6 | import com.tlz.ada.models.Db 7 | import com.tlz.ada.models.InitInfo 8 | import org.nanohttpd2.protocols.http.IHTTPSession 9 | import org.nanohttpd2.protocols.http.response.Response 10 | 11 | /** 12 | * 初始化请求处理. 13 | * 14 | * Created by Tomlezen. 15 | * Data: 2018/9/5. 16 | * Time: 15:18. 17 | */ 18 | class InitRequestHandler( 19 | private val ctx: Context, 20 | private val dataProvider: AdaDataProvider, 21 | private val appManager: AdaApplicationManager 22 | ) : RequestHandler { 23 | 24 | override fun onRequest(session: IHTTPSession): Response? = 25 | when (session.uri) { 26 | "/api/init" -> handleInitRequest() 27 | else -> null 28 | } 29 | 30 | /** 31 | * 处理初始化请求. 32 | * @return AdaResponse 33 | */ 34 | private fun handleInitRequest(): Response { 35 | val dbs = mutableListOf() 36 | dataProvider.getAllDatabase().forEach { 37 | runCatching { 38 | val tabWrapper = dataProvider.getAllTable(it) 39 | dbs.add(Db(it, tabWrapper.version, tabWrapper.tableInfos)) 40 | }.onFailure { 41 | it.printStackTrace() 42 | } 43 | } 44 | return appManager.getApplicationInfoByPkg(ctx.packageName)?.run { 45 | responseData( 46 | InitInfo( 47 | icon, 48 | name, 49 | pkg, 50 | verName, 51 | verCode, 52 | isSystemApp, 53 | size, 54 | "${Ada.adaWebServer.serverAddress}/api/log", 55 | dbs 56 | ).toResponse()) 57 | } ?: responseError(errorMsg = "未获取到应用信息") 58 | } 59 | 60 | } -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/socket/AdaWebSocket.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada.socket 2 | 3 | import android.util.Log 4 | import com.tlz.ada.Ada 5 | import org.nanohttpd2.protocols.http.IHTTPSession 6 | import org.nanohttpd2.protocols.websockets.CloseCode 7 | import org.nanohttpd2.protocols.websockets.WebSocket 8 | import org.nanohttpd2.protocols.websockets.WebSocketFrame 9 | import java.io.IOException 10 | 11 | /** 12 | * Created by Tomlezen. 13 | * Data: 2018/9/7. 14 | * Time: 11:27. 15 | */ 16 | class AdaWebSocket(handsShakeRequest: IHTTPSession) : WebSocket(handsShakeRequest) { 17 | 18 | /** ping次数. */ 19 | private var pingCount = 0 20 | /** 被ping次数. */ 21 | private var pongCount = 0 22 | 23 | private val connectSuccessLog by lazy { 24 | com.tlz.ada.models.Log( 25 | "I", 26 | Log.INFO, 27 | "----------------------------------连接成功----------------------------------\n", 28 | "----------------------------------连接成功----------------------------------\n" 29 | ) 30 | } 31 | 32 | override fun onOpen() { 33 | send(Ada.adaGson.toJson(connectSuccessLog)) 34 | } 35 | 36 | override fun onClose(code: CloseCode?, reason: String?, initiatedByRemote: Boolean) { 37 | } 38 | 39 | override fun onMessage(message: WebSocketFrame?) { 40 | } 41 | 42 | override fun onPong(pong: WebSocketFrame?) { 43 | pongCount++ 44 | } 45 | 46 | override fun onException(exception: IOException?) { 47 | // Log.e(TAG, "web socket exception", exception) 48 | } 49 | 50 | override fun ping(payload: ByteArray?) { 51 | super.ping(payload) 52 | pingCount++ 53 | if (pingCount - pongCount > 3) { 54 | close(CloseCode.GoingAway, "Missed too many ping requests.", false) 55 | } 56 | } 57 | 58 | companion object { 59 | // private const val TAG = "DebuggerWebSocket" 60 | } 61 | 62 | } -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/AdaProvider.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada 2 | 3 | import android.util.Pair 4 | import androidx.annotation.Keep 5 | import androidx.sqlite.db.SupportSQLiteDatabase 6 | import java.io.File 7 | import java.net.Inet4Address 8 | import java.net.NetworkInterface 9 | 10 | /** 11 | * 提供给外部访问. 12 | * Created by tomlezen. 13 | * Data: 2018/2/24. 14 | * Time: 13:59. 15 | */ 16 | @Keep 17 | object AdaProvider { 18 | 19 | /** 20 | * 设置自定义数据库文件. 21 | * @param files Map> 22 | */ 23 | @Keep 24 | @JvmStatic 25 | fun setCustomDatabaseFiles(files: Map>) { 26 | Ada.adaWebServer.setCustomDatabaseFiles(files) 27 | } 28 | 29 | /** 30 | * 设置内存数据库. 31 | * @param databases Map 32 | */ 33 | @Keep 34 | @JvmStatic 35 | fun setInMemoryRoomDatabases(databases: Map) { 36 | Ada.adaWebServer.setInMemoryRoomDatabases(databases) 37 | } 38 | 39 | /** 40 | * 获取当前手机ip地址. 41 | * @return String 42 | */ 43 | @Keep 44 | @JvmStatic 45 | fun getPhoneIp(): String = 46 | runCatching { 47 | val en = NetworkInterface.getNetworkInterfaces() 48 | while (en.hasMoreElements()) { 49 | val intf = en.nextElement() 50 | val enumIpAddr = intf.inetAddresses 51 | while (enumIpAddr.hasMoreElements()) { 52 | val inetAddress = enumIpAddr.nextElement() 53 | if (!inetAddress.isLoopbackAddress && inetAddress is Inet4Address) { 54 | return@runCatching inetAddress.getHostAddress().toString() 55 | } 56 | } 57 | } 58 | "没有获取到ip地址" 59 | }.getOrNull() ?: "没有获取到ip地址" 60 | 61 | /** 62 | * 获取服务器地址. 63 | * @return String 64 | */ 65 | @Keep 66 | @JvmStatic 67 | fun getAdaServerAddress(): String = Ada.adaWebServer.serverAddress 68 | } -------------------------------------------------------------------------------- /lib/src/main/java/org/nanohttpd2/protocols/http/response/IStatus.java: -------------------------------------------------------------------------------- 1 | package org.nanohttpd2.protocols.http.response; 2 | 3 | /* 4 | * #%L 5 | * NanoHttpd-Core 6 | * %% 7 | * Copyright (C) 2012 - 2016 nanohttpd 8 | * %% 9 | * Redistribution and use in source and binary forms, with or without modification, 10 | * are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, this 13 | * list of conditions and the following disclaimer. 14 | * 15 | * 2. Redistributions in binary form must reproduce the above copyright notice, 16 | * this list of conditions and the following disclaimer in the documentation 17 | * and/or other materials provided with the distribution. 18 | * 19 | * 3. Neither the name of the nanohttpd nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 27 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 31 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | * #L% 34 | */ 35 | 36 | public interface IStatus { 37 | 38 | String getDescription(); 39 | 40 | int getRequestStatus(); 41 | } 42 | -------------------------------------------------------------------------------- /lib/src/main/java/org/nanohttpd2/protocols/websockets/State.java: -------------------------------------------------------------------------------- 1 | package org.nanohttpd2.protocols.websockets; 2 | 3 | /* 4 | * #%L 5 | * NanoHttpd-Websocket 6 | * %% 7 | * Copyright (C) 2012 - 2016 nanohttpd 8 | * %% 9 | * Redistribution and use in source and binary forms, with or without modification, 10 | * are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, this 13 | * list of conditions and the following disclaimer. 14 | * 15 | * 2. Redistributions in binary form must reproduce the above copyright notice, 16 | * this list of conditions and the following disclaimer in the documentation 17 | * and/or other materials provided with the distribution. 18 | * 19 | * 3. Neither the name of the nanohttpd nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 27 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 31 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | * #L% 34 | */ 35 | 36 | public enum State { 37 | UNCONNECTED, 38 | CONNECTING, 39 | OPEN, 40 | CLOSING, 41 | CLOSED 42 | } 43 | -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/handlers/ScreenShotHandler.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada.handlers 2 | 3 | import android.graphics.Bitmap 4 | import com.tlz.ada.AdaActivityLifeCycleListener 5 | import com.tlz.ada.handleRequestSafely 6 | import com.tlz.ada.responseError 7 | import com.tlz.ada.verifyParams 8 | import org.nanohttpd2.protocols.http.IHTTPSession 9 | import org.nanohttpd2.protocols.http.response.Response 10 | import org.nanohttpd2.protocols.http.response.Response.newChunkedResponse 11 | import org.nanohttpd2.protocols.http.response.Status 12 | import java.io.ByteArrayInputStream 13 | import java.io.ByteArrayOutputStream 14 | import java.io.InputStream 15 | 16 | 17 | /** 18 | * Created by tomlezen. 19 | * Data: 2019/3/18. 20 | * Time: 16:26. 21 | */ 22 | class ScreenShotHandler(private val activityLifeCycleHooker: AdaActivityLifeCycleListener) : RequestHandler { 23 | 24 | override fun onRequest(session: IHTTPSession): Response? = 25 | when (session.uri) { 26 | "/api/app/screenshot" -> session.verifyParams(::handleScreenShotRequest) 27 | else -> null 28 | } 29 | 30 | /** 31 | * 处理截图请求. 32 | * @param session IHTTPSession 33 | * @return AdaResponse 34 | */ 35 | private fun handleScreenShotRequest(session: IHTTPSession): Response = 36 | handleRequestSafely { 37 | activityLifeCycleHooker.currentActivityInstance?.window?.decorView?.let { dView -> 38 | dView.isDrawingCacheEnabled = true 39 | dView.buildDrawingCache() 40 | newChunkedResponse(Status.OK, "image/png", Bitmap.createBitmap(dView.drawingCache).toInputStream()) 41 | } ?: responseError(errorMsg = "app未在前台") 42 | } 43 | 44 | /** 45 | * Bitmap转输入流. 46 | * @receiver Bitmap 47 | * @return InputStream 48 | */ 49 | private fun Bitmap.toInputStream(): InputStream = 50 | ByteArrayOutputStream().use { 51 | compress(Bitmap.CompressFormat.JPEG, 100, it) 52 | ByteArrayInputStream(it.toByteArray()) 53 | } 54 | } -------------------------------------------------------------------------------- /lib/src/main/java/org/nanohttpd2/util/IFactory.java: -------------------------------------------------------------------------------- 1 | package org.nanohttpd2.util; 2 | 3 | /* 4 | * #%L 5 | * NanoHttpd-Core 6 | * %% 7 | * Copyright (C) 2012 - 2016 nanohttpd 8 | * %% 9 | * Redistribution and use in source and binary forms, with or without modification, 10 | * are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, this 13 | * list of conditions and the following disclaimer. 14 | * 15 | * 2. Redistributions in binary form must reproduce the above copyright notice, 16 | * this list of conditions and the following disclaimer in the documentation 17 | * and/or other materials provided with the distribution. 18 | * 19 | * 3. Neither the name of the nanohttpd nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 27 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 31 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | * #L% 34 | */ 35 | 36 | /** 37 | * Represents a simple factory 38 | * 39 | * @author LordFokas 40 | * @param 41 | * The Type of object to create 42 | */ 43 | public interface IFactory { 44 | 45 | T create(); 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/handlers/DefaultRequestHandler.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada.handlers 2 | 3 | import android.content.Context 4 | import com.tlz.ada.handleRequestSafely 5 | import com.tlz.ada.readHtml 6 | import com.tlz.ada.response 7 | import com.tlz.ada.responseHtml 8 | import org.nanohttpd2.protocols.http.IHTTPSession 9 | import org.nanohttpd2.protocols.http.response.Response 10 | import org.nanohttpd2.protocols.http.response.Response.newChunkedResponse 11 | import org.nanohttpd2.protocols.http.response.Status 12 | 13 | /** 14 | * 基础请求处理. 15 | * 16 | * Created by Tomlezen. 17 | * Data: 2018/9/5. 18 | * Time: 16:21. 19 | */ 20 | class DefaultRequestHandler(private val ctx: Context) : RequestHandler { 21 | 22 | override fun onRequest(session: IHTTPSession): Response? = 23 | handleRequestSafely { 24 | val uri = session.uri 25 | when { 26 | uri.endsWith(".png") -> 27 | newChunkedResponse(Status.OK, "image/png", ctx.assets.open("web$uri")) 28 | uri.endsWith(".ico") -> 29 | newChunkedResponse(Status.OK, "image/vnd.microsoft.icon", ctx.assets.open("web$uri")) 30 | uri.endsWith(".svg") -> 31 | newChunkedResponse(Status.OK, "image/svg+xml", ctx.assets.open("web$uri")) 32 | else -> { 33 | try { 34 | val file = uri.readHtml(ctx) 35 | when { 36 | uri.contains(".css") -> response("text/css", file, "86400") 37 | uri.contains(".js") -> response("text/javascript", file, "86400") 38 | uri.contains(".eot") -> response("application/vnd.ms-fontobject", file) 39 | // uri.endsWith(".svg") -> response("image/svg+xml", file) 40 | uri.contains(".ttf") -> response("application/x-font-ttf", file) 41 | uri.contains(".woff2") -> response("font/woff2", file) 42 | uri.contains(".woff") -> response("application/font-woff", file) 43 | else -> responseHtml("/index.html".readHtml(ctx)) 44 | } 45 | } catch (e: Exception) { 46 | responseHtml("/index.html".readHtml(ctx)) 47 | // newFixedLengthResponse(AdaStatus.FORBIDDEN,MIME_PLAINTEXT, "") 48 | } 49 | } 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /lib/src/main/java/org/nanohttpd2/util/IHandler.java: -------------------------------------------------------------------------------- 1 | package org.nanohttpd2.util; 2 | 3 | /* 4 | * #%L 5 | * NanoHttpd-Core 6 | * %% 7 | * Copyright (C) 2012 - 2016 nanohttpd 8 | * %% 9 | * Redistribution and use in source and binary forms, with or without modification, 10 | * are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, this 13 | * list of conditions and the following disclaimer. 14 | * 15 | * 2. Redistributions in binary form must reproduce the above copyright notice, 16 | * this list of conditions and the following disclaimer in the documentation 17 | * and/or other materials provided with the distribution. 18 | * 19 | * 3. Neither the name of the nanohttpd nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 27 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 31 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | * #L% 34 | */ 35 | 36 | /** 37 | * Defines a generic handler that returns an object of type O when given an 38 | * object of type I. 39 | * 40 | * @author LordFokas 41 | * @param 42 | * The input type. 43 | * @param 44 | * The output type. 45 | */ 46 | public interface IHandler { 47 | 48 | public O handle(I input); 49 | } 50 | -------------------------------------------------------------------------------- /lib/src/main/java/org/nanohttpd2/protocols/http/threading/IAsyncRunner.java: -------------------------------------------------------------------------------- 1 | package org.nanohttpd2.protocols.http.threading; 2 | 3 | /* 4 | * #%L 5 | * NanoHttpd-Core 6 | * %% 7 | * Copyright (C) 2012 - 2016 nanohttpd 8 | * %% 9 | * Redistribution and use in source and binary forms, with or without modification, 10 | * are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, this 13 | * list of conditions and the following disclaimer. 14 | * 15 | * 2. Redistributions in binary form must reproduce the above copyright notice, 16 | * this list of conditions and the following disclaimer in the documentation 17 | * and/or other materials provided with the distribution. 18 | * 19 | * 3. Neither the name of the nanohttpd nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 27 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 31 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | * #L% 34 | */ 35 | 36 | import org.nanohttpd2.protocols.http.ClientHandler; 37 | 38 | /** 39 | * Pluggable strategy for asynchronously executing requests. 40 | */ 41 | public interface IAsyncRunner { 42 | 43 | void closeAll(); 44 | 45 | void closed(ClientHandler clientHandler); 46 | 47 | void exec(ClientHandler code); 48 | } 49 | -------------------------------------------------------------------------------- /lib/src/main/assets/web/assets/img/logo-full.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | background 5 | 6 | 7 | 8 | Layer 1 9 | 10 | 11 | 12 | Debugger 13 | 14 | -------------------------------------------------------------------------------- /lib/src/main/assets/web/assets/img/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | ]> 6 | 10 | 11 | 12 | 15 | 21 | 25 | 26 | -------------------------------------------------------------------------------- /lib/src/main/java/org/nanohttpd2/protocols/http/tempfiles/DefaultTempFileManagerFactory.java: -------------------------------------------------------------------------------- 1 | package org.nanohttpd2.protocols.http.tempfiles; 2 | 3 | /* 4 | * #%L 5 | * NanoHttpd-Core 6 | * %% 7 | * Copyright (C) 2012 - 2016 nanohttpd 8 | * %% 9 | * Redistribution and use in source and binary forms, with or without modification, 10 | * are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, this 13 | * list of conditions and the following disclaimer. 14 | * 15 | * 2. Redistributions in binary form must reproduce the above copyright notice, 16 | * this list of conditions and the following disclaimer in the documentation 17 | * and/or other materials provided with the distribution. 18 | * 19 | * 3. Neither the name of the nanohttpd nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 27 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 31 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | * #L% 34 | */ 35 | 36 | import org.nanohttpd2.util.IFactory; 37 | 38 | /** 39 | * Default strategy for creating and cleaning up temporary files. 40 | */ 41 | public class DefaultTempFileManagerFactory implements IFactory { 42 | 43 | @Override 44 | public ITempFileManager create() { 45 | return new DefaultTempFileManager(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/src/main/java/org/nanohttpd2/util/IFactoryThrowing.java: -------------------------------------------------------------------------------- 1 | package org.nanohttpd2.util; 2 | 3 | /* 4 | * #%L 5 | * NanoHttpd-Core 6 | * %% 7 | * Copyright (C) 2012 - 2016 nanohttpd 8 | * %% 9 | * Redistribution and use in source and binary forms, with or without modification, 10 | * are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, this 13 | * list of conditions and the following disclaimer. 14 | * 15 | * 2. Redistributions in binary form must reproduce the above copyright notice, 16 | * this list of conditions and the following disclaimer in the documentation 17 | * and/or other materials provided with the distribution. 18 | * 19 | * 3. Neither the name of the nanohttpd nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 27 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 31 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | * #L% 34 | */ 35 | 36 | /** 37 | * Represents a factory that can throw an exception instead of actually creating 38 | * an object 39 | * 40 | * @author LordFokas 41 | * @param 42 | * The Type of object to create 43 | * @param 44 | * The base Type of exceptions that can be thrown 45 | */ 46 | public interface IFactoryThrowing { 47 | 48 | T create() throws E; 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/tomlezen/androiddebuglib/database/CustomDB.kt: -------------------------------------------------------------------------------- 1 | package tomlezen.androiddebuglib.database 2 | 3 | import android.content.ContentValues 4 | import android.content.Context 5 | import android.content.ContextWrapper 6 | import android.database.DatabaseErrorHandler 7 | import android.database.sqlite.SQLiteDatabase 8 | import android.database.sqlite.SQLiteOpenHelper 9 | import java.io.File 10 | 11 | /** 12 | * Created by tomlezen. 13 | * Data: 2018/2/24. 14 | * Time: 11:01. 15 | */ 16 | class CustomDB(ctx: Context): SQLiteOpenHelper(CustomDatabasePathContext(ctx), "Custom.db", null, 10) { 17 | 18 | private val tableName = "test" 19 | 20 | override fun onCreate(db: SQLiteDatabase?) { 21 | db?.execSQL("create table $tableName (id integer primary key, key text, value integer)") 22 | } 23 | 24 | override fun onUpgrade(db: SQLiteDatabase?, p1: Int, p2: Int) { 25 | db?.execSQL("DROP TABLE IF EXISTS $tableName") 26 | onCreate(db) 27 | } 28 | 29 | fun init(){ 30 | (1..50).forEach { 31 | val contentValues = ContentValues() 32 | contentValues.put("key", "key$it") 33 | contentValues.put("value", "$it") 34 | writableDatabase.insert(tableName, null, contentValues) 35 | } 36 | 37 | close() 38 | } 39 | 40 | private class CustomDatabasePathContext(base: Context) : ContextWrapper(base) { 41 | 42 | override fun getDatabasePath(name: String): File { 43 | val databaseDir = File(String.format("%s/%s", filesDir, "custom_dir")) 44 | databaseDir.mkdirs() 45 | return File(String.format("%s/%s/%s", filesDir, "custom_dir", name)) 46 | } 47 | 48 | override fun openOrCreateDatabase(name: String, mode: Int, factory: SQLiteDatabase.CursorFactory?): SQLiteDatabase { 49 | return SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), null) 50 | } 51 | 52 | override fun openOrCreateDatabase(name: String, mode: Int, factory: SQLiteDatabase.CursorFactory?, errorHandler: DatabaseErrorHandler?): SQLiteDatabase { 53 | return SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), null) 54 | } 55 | } 56 | 57 | companion object { 58 | fun create(ctx: Context){ 59 | CustomDB(ctx).init() 60 | } 61 | } 62 | 63 | } -------------------------------------------------------------------------------- /lib/src/main/java/org/nanohttpd2/protocols/http/tempfiles/ITempFileManager.java: -------------------------------------------------------------------------------- 1 | package org.nanohttpd2.protocols.http.tempfiles; 2 | 3 | /* 4 | * #%L 5 | * NanoHttpd-Core 6 | * %% 7 | * Copyright (C) 2012 - 2016 nanohttpd 8 | * %% 9 | * Redistribution and use in source and binary forms, with or without modification, 10 | * are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, this 13 | * list of conditions and the following disclaimer. 14 | * 15 | * 2. Redistributions in binary form must reproduce the above copyright notice, 16 | * this list of conditions and the following disclaimer in the documentation 17 | * and/or other materials provided with the distribution. 18 | * 19 | * 3. Neither the name of the nanohttpd nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 27 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 31 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | * #L% 34 | */ 35 | 36 | /** 37 | * Temp file manager. 38 | *

39 | *

40 | * Temp file managers are created 1-to-1 with incoming requests, to create and 41 | * cleanup temporary files created as a result of handling the request. 42 | *

43 | */ 44 | public interface ITempFileManager { 45 | 46 | void clear(); 47 | 48 | public ITempFile createTempFile(String filename_hint) throws Exception; 49 | } 50 | -------------------------------------------------------------------------------- /lib/src/main/java/org/nanohttpd2/protocols/http/tempfiles/ITempFile.java: -------------------------------------------------------------------------------- 1 | package org.nanohttpd2.protocols.http.tempfiles; 2 | 3 | /* 4 | * #%L 5 | * NanoHttpd-Core 6 | * %% 7 | * Copyright (C) 2012 - 2016 nanohttpd 8 | * %% 9 | * Redistribution and use in source and binary forms, with or without modification, 10 | * are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, this 13 | * list of conditions and the following disclaimer. 14 | * 15 | * 2. Redistributions in binary form must reproduce the above copyright notice, 16 | * this list of conditions and the following disclaimer in the documentation 17 | * and/or other materials provided with the distribution. 18 | * 19 | * 3. Neither the name of the nanohttpd nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 27 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 31 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | * #L% 34 | */ 35 | 36 | import java.io.OutputStream; 37 | 38 | /** 39 | * A temp file. 40 | *

41 | *

42 | * Temp files are responsible for managing the actual temporary storage and 43 | * cleaning themselves up when no longer needed. 44 | *

45 | */ 46 | public interface ITempFile { 47 | 48 | public void delete() throws Exception; 49 | 50 | public String getName(); 51 | 52 | public OutputStream open() throws Exception; 53 | } 54 | -------------------------------------------------------------------------------- /lib/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | apply plugin: 'kotlin-android' 4 | 5 | apply plugin: 'com.novoda.bintray-release' 6 | 7 | android { 8 | compileSdkVersion 28 9 | 10 | compileOptions { 11 | kotlinOptions.freeCompilerArgs += ['-module-name', "com.tlz.ada"] 12 | } 13 | 14 | defaultConfig { 15 | minSdkVersion 16 16 | targetSdkVersion 28 17 | versionCode 1 18 | versionName "1.0" 19 | 20 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 21 | 22 | } 23 | 24 | buildTypes { 25 | release { 26 | minifyEnabled false 27 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 28 | } 29 | } 30 | 31 | sourceSets { 32 | main { 33 | java.srcDirs += 'src/main/kotlin' 34 | } 35 | } 36 | } 37 | 38 | //sourceCompatibility = "1.6" 39 | //targetCompatibility = "1.6" 40 | 41 | dependencies { 42 | implementation fileTree(dir: 'libs', include: ['*.jar']) 43 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 44 | implementation 'androidx.appcompat:appcompat:1.0.0' 45 | implementation 'androidx.room:room-runtime:2.2.1' 46 | // implementation 'android.arch.persistence.room:runtime:1.1.1' 47 | api 'com.google.code.gson:gson:2.8.5' 48 | api 'net.zetetic:android-database-sqlcipher:4.2.0@aar' 49 | testImplementation 'junit:junit:4.12' 50 | androidTestImplementation 'androidx.test:runner:1.1.0' 51 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0' 52 | } 53 | 54 | buildscript{ 55 | tasks.withType(Javadoc) { 56 | failOnError false 57 | options{ 58 | encoding "UTF-8" 59 | charSet 'UTF-8' 60 | links "http://docs.oracle.com/javase/7/docs/api" 61 | } 62 | } 63 | } 64 | 65 | publish { 66 | userOrg = parent.ext.userOrg 67 | artifactId = parent.ext.artifactId 68 | autoPublish = parent.ext.autoPublish 69 | desc = parent.ext.desc 70 | groupId = parent.ext.groupId 71 | publishVersion = parent.ext.publishVersion 72 | uploadName = parent.ext.uploadName 73 | website = parent.ext.website 74 | repoName = parent.ext.repoName 75 | } 76 | -------------------------------------------------------------------------------- /lib/src/main/java/org/nanohttpd2/protocols/http/sockets/DefaultServerSocketFactory.java: -------------------------------------------------------------------------------- 1 | package org.nanohttpd2.protocols.http.sockets; 2 | 3 | /* 4 | * #%L 5 | * NanoHttpd-Core 6 | * %% 7 | * Copyright (C) 2012 - 2016 nanohttpd 8 | * %% 9 | * Redistribution and use in source and binary forms, with or without modification, 10 | * are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, this 13 | * list of conditions and the following disclaimer. 14 | * 15 | * 2. Redistributions in binary form must reproduce the above copyright notice, 16 | * this list of conditions and the following disclaimer in the documentation 17 | * and/or other materials provided with the distribution. 18 | * 19 | * 3. Neither the name of the nanohttpd nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 27 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 31 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | * #L% 34 | */ 35 | 36 | import java.io.IOException; 37 | import java.net.ServerSocket; 38 | 39 | import org.nanohttpd2.util.IFactoryThrowing; 40 | 41 | /** 42 | * Creates a normal ServerSocket for TCP connections 43 | */ 44 | public class DefaultServerSocketFactory implements IFactoryThrowing { 45 | 46 | @Override 47 | public ServerSocket create() throws IOException { 48 | return new ServerSocket(); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/db/NormalSQLiteDb.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada.db 2 | 3 | import android.content.ContentValues 4 | import android.content.Context 5 | import android.database.Cursor 6 | import net.sqlcipher.database.SQLiteDatabase 7 | import java.io.File 8 | 9 | /** 10 | * Author: tomlezen 11 | * Email: tomlezen@protonmail.com 12 | * Date: 2019-10-31 13:16 13 | */ 14 | class NormalSQLiteDb(private val ctx: Context, private val dbFile: File, private val dName: String, private val password: String?) : SQLiteDb { 15 | 16 | private var db: SQLiteDatabase? = null 17 | 18 | override val isOpen: Boolean 19 | get() = false 20 | 21 | override val version: Int 22 | get() = open { 23 | version 24 | } 25 | 26 | override fun delete(table: String, whereClause: String?, whereArgs: Array?): Int = 27 | open { 28 | delete(table, whereClause, whereArgs) 29 | } 30 | 31 | override fun close() { 32 | db?.close() 33 | } 34 | 35 | override fun rawQuery(sql: String, selectionArgs: Array?): Cursor = 36 | open { 37 | rawQuery(sql, selectionArgs) 38 | } 39 | 40 | override fun execSQL(sql: String) { 41 | open { 42 | execSQL(sql) 43 | } 44 | } 45 | 46 | override fun insert(table: String, nullColumnHack: String?, values: ContentValues): Long = 47 | open { 48 | insert(table, nullColumnHack, values) 49 | } 50 | 51 | override fun update(table: String, values: ContentValues, whereClause: String, whereArgs: Array?): Int = 52 | open { 53 | update(table, values, whereClause, whereArgs) 54 | } 55 | 56 | /** 57 | * 打开数据库. 58 | * @param block [@kotlin.ExtensionFunctionType] Function1 59 | * @return T 60 | */ 61 | private fun open(block: SQLiteDatabase.() -> T): T { 62 | close() 63 | SQLiteDatabase.loadLibs(ctx) 64 | db = SQLiteDatabase.openOrCreateDatabase(dbFile, if (password.isNullOrEmpty()) null else password, null) 65 | return try { 66 | // db.beginTransaction() 67 | block.invoke(db!!).apply { 68 | // db.setTransactionSuccessful() 69 | } 70 | } finally { 71 | // db.endTransaction() 72 | // db.close() 73 | } 74 | } 75 | 76 | } -------------------------------------------------------------------------------- /lib/src/main/java/org/nanohttpd2/protocols/websockets/OpCode.java: -------------------------------------------------------------------------------- 1 | package org.nanohttpd2.protocols.websockets; 2 | 3 | /* 4 | * #%L 5 | * NanoHttpd-Websocket 6 | * %% 7 | * Copyright (C) 2012 - 2016 nanohttpd 8 | * %% 9 | * Redistribution and use in source and binary forms, with or without modification, 10 | * are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, this 13 | * list of conditions and the following disclaimer. 14 | * 15 | * 2. Redistributions in binary form must reproduce the above copyright notice, 16 | * this list of conditions and the following disclaimer in the documentation 17 | * and/or other materials provided with the distribution. 18 | * 19 | * 3. Neither the name of the nanohttpd nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 27 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 31 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | * #L% 34 | */ 35 | 36 | public enum OpCode { 37 | Continuation(0), 38 | Text(1), 39 | Binary(2), 40 | Close(8), 41 | Ping(9), 42 | Pong(10); 43 | 44 | public static OpCode find(byte value) { 45 | for (OpCode opcode : values()) { 46 | if (opcode.getValue() == value) { 47 | return opcode; 48 | } 49 | } 50 | return null; 51 | } 52 | 53 | private final byte code; 54 | 55 | private OpCode(int code) { 56 | this.code = (byte) code; 57 | } 58 | 59 | public byte getValue() { 60 | return this.code; 61 | } 62 | 63 | public boolean isControlFrame() { 64 | return this == Close || this == Ping || this == Pong; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/db/AdaDataProviderImpl.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada.db 2 | 3 | import android.content.Context 4 | import androidx.sqlite.db.SupportSQLiteDatabase 5 | import com.tlz.ada.models.KeyValue 6 | import com.tlz.ada.models.Table 7 | import com.tlz.ada.models.TableInfo 8 | import java.io.File 9 | 10 | /** 11 | * Created by tomlezen. 12 | * Data: 2018/1/27. 13 | * Time: 16:01. 14 | */ 15 | class AdaDataProviderImpl(ctx: Context) : AdaDataProvider { 16 | 17 | private val dbDataProvider = DbDataProviderImpl(ctx) 18 | private val spDataProvider = SpDataProviderImpl(ctx) 19 | 20 | override fun setCustomDatabaseFiles(files: Map>) { 21 | dbDataProvider.setCustomDatabaseFiles(files) 22 | spDataProvider.setCustomDatabaseFiles(files) 23 | } 24 | 25 | override fun setInMemoryRoomDatabases(databases: Map) { 26 | dbDataProvider.setInMemoryRoomDatabases(databases) 27 | spDataProvider.setInMemoryRoomDatabases(databases) 28 | } 29 | 30 | override fun getAllDatabase(): List = 31 | spDataProvider.getAllDatabase() + dbDataProvider.getAllDatabase() 32 | 33 | override fun getDatabaseFile(dName: String): File? = 34 | dName.toDataProvider().getDatabaseFile(dName) 35 | 36 | override 37 | fun getAllTable(dName: String): Table = 38 | dName.toDataProvider().getAllTable(dName) 39 | 40 | override fun getTableInfo(dName: String, tName: String): TableInfo = 41 | dName.toDataProvider().getTableInfo(dName, tName) 42 | 43 | override fun getTableDataCount(dName: String, tName: String, where: String): Int = 44 | dName.toDataProvider().getTableDataCount(dName, tName, where) 45 | 46 | override fun query(dName: String, tName: String, sql: String): List = 47 | dName.toDataProvider().query(dName, tName, sql) 48 | 49 | override fun rawQuery(dName: String, sql: String): Any = 50 | dName.toDataProvider().rawQuery(dName, sql) 51 | 52 | override fun add(dName: String, tName: String, content: Array): Boolean = 53 | dName.toDataProvider().add(dName, tName, content) 54 | 55 | override fun delete(dName: String, tName: String, where: String): Boolean = 56 | dName.toDataProvider().delete(dName, tName, where) 57 | 58 | override fun update(dName: String, tName: String, content: Array, where: String): Boolean = 59 | dName.toDataProvider().update(dName, tName, content, where) 60 | 61 | private fun String.toDataProvider(): AdaDataProvider = 62 | if (this == "SharePreferences") spDataProvider else dbDataProvider 63 | } -------------------------------------------------------------------------------- /lib/src/main/java/org/nanohttpd2/protocols/http/request/Method.java: -------------------------------------------------------------------------------- 1 | package org.nanohttpd2.protocols.http.request; 2 | 3 | /* 4 | * #%L 5 | * NanoHttpd-Core 6 | * %% 7 | * Copyright (C) 2012 - 2016 nanohttpd 8 | * %% 9 | * Redistribution and use in source and binary forms, with or without modification, 10 | * are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, this 13 | * list of conditions and the following disclaimer. 14 | * 15 | * 2. Redistributions in binary form must reproduce the above copyright notice, 16 | * this list of conditions and the following disclaimer in the documentation 17 | * and/or other materials provided with the distribution. 18 | * 19 | * 3. Neither the name of the nanohttpd nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 27 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 31 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | * #L% 34 | */ 35 | 36 | /** 37 | * HTTP Request methods, with the ability to decode a String back 38 | * to its enum value. 39 | */ 40 | public enum Method { 41 | GET, 42 | PUT, 43 | POST, 44 | DELETE, 45 | HEAD, 46 | OPTIONS, 47 | TRACE, 48 | CONNECT, 49 | PATCH, 50 | PROPFIND, 51 | PROPPATCH, 52 | MKCOL, 53 | MOVE, 54 | COPY, 55 | LOCK, 56 | UNLOCK, 57 | NOTIFY, 58 | SUBSCRIBE; 59 | 60 | public static Method lookup(String method) { 61 | if (method == null) 62 | return null; 63 | 64 | try { 65 | return valueOf(method); 66 | } catch (IllegalArgumentException e) { 67 | // TODO: Log it? 68 | return null; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib/src/main/java/org/nanohttpd2/protocols/websockets/CloseCode.java: -------------------------------------------------------------------------------- 1 | package org.nanohttpd2.protocols.websockets; 2 | 3 | /* 4 | * #%L 5 | * NanoHttpd-Websocket 6 | * %% 7 | * Copyright (C) 2012 - 2016 nanohttpd 8 | * %% 9 | * Redistribution and use in source and binary forms, with or without modification, 10 | * are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, this 13 | * list of conditions and the following disclaimer. 14 | * 15 | * 2. Redistributions in binary form must reproduce the above copyright notice, 16 | * this list of conditions and the following disclaimer in the documentation 17 | * and/or other materials provided with the distribution. 18 | * 19 | * 3. Neither the name of the nanohttpd nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 27 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 31 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | * #L% 34 | */ 35 | 36 | public enum CloseCode { 37 | NormalClosure(1000), 38 | GoingAway(1001), 39 | ProtocolError(1002), 40 | UnsupportedData(1003), 41 | NoStatusRcvd(1005), 42 | AbnormalClosure(1006), 43 | InvalidFramePayloadData(1007), 44 | PolicyViolation(1008), 45 | MessageTooBig(1009), 46 | MandatoryExt(1010), 47 | InternalServerError(1011), 48 | TLSHandshake(1015); 49 | 50 | public static CloseCode find(int value) { 51 | for (CloseCode code : values()) { 52 | if (code.getValue() == value) { 53 | return code; 54 | } 55 | } 56 | return null; 57 | } 58 | 59 | private final int code; 60 | 61 | private CloseCode(int code) { 62 | this.code = code; 63 | } 64 | 65 | public int getValue() { 66 | return this.code; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /lib/src/main/java/org/nanohttpd2/protocols/websockets/WebSocketException.java: -------------------------------------------------------------------------------- 1 | package org.nanohttpd2.protocols.websockets; 2 | 3 | /* 4 | * #%L 5 | * NanoHttpd-Websocket 6 | * %% 7 | * Copyright (C) 2012 - 2016 nanohttpd 8 | * %% 9 | * Redistribution and use in source and binary forms, with or without modification, 10 | * are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, this 13 | * list of conditions and the following disclaimer. 14 | * 15 | * 2. Redistributions in binary form must reproduce the above copyright notice, 16 | * this list of conditions and the following disclaimer in the documentation 17 | * and/or other materials provided with the distribution. 18 | * 19 | * 3. Neither the name of the nanohttpd nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 27 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 31 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | * #L% 34 | */ 35 | 36 | import java.io.IOException; 37 | 38 | public class WebSocketException extends IOException { 39 | 40 | private static final long serialVersionUID = 1L; 41 | 42 | private final CloseCode code; 43 | 44 | private final String reason; 45 | 46 | public WebSocketException(CloseCode code, String reason) { 47 | this(code, reason, null); 48 | } 49 | 50 | public WebSocketException(CloseCode code, String reason, Exception cause) { 51 | super(code + ": " + reason, cause); 52 | this.code = code; 53 | this.reason = reason; 54 | } 55 | 56 | public WebSocketException(Exception cause) { 57 | this(CloseCode.InternalServerError, cause.toString(), cause); 58 | } 59 | 60 | public CloseCode getCode() { 61 | return this.code; 62 | } 63 | 64 | public String getReason() { 65 | return this.reason; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android开发辅助依赖库 2 | - 数据库增删查改,excel表导出,数据库文件导出 3 | - SharePreferences增删查改 4 | - 应用实时日志查看和历史日志查看,也支持历史日志下载与删除 5 | - 手机所有应用的信息查看,如:Activity、Service、权限等信息,也可以直接下载手机上的应用到电脑上 6 | - 通过web直接安装应用到手机上 7 | - 手机文件管理,包括新增文件、文件夹,删除文件,批量上传文件都手机指定文件夹中 8 | - 应用截图 9 | 10 | 数据库相关处理思路参考[Android-Debug-Database](https://github.com/amitshekhariitbhu/Android-Debug-Database) 11 | (已知目前数据访问bug:当app应用的数据库对象不关闭时(Room),工具将查询不到数据) 12 | 13 | web端使用Angular6开发,因为个人设备和能力有限,没有在太多手机上测试,不能保证100%兼容每台手机 14 | 15 | ## 如何使用 16 | 17 | ``` 18 | 只需要在gradle文件中添加一下代码: 19 | Gradle3.0以上: debugImplementation 'com.tlz.tools:AndDevelopAssistant:0.1.2' 20 | Gradle3.0以下: debugCompile 'com.tlz.tools:AndDevelopAssistant:0.1.2' 21 | 非androidx版本: 22 | Gradle3.0以上: debugImplementation 'com.tlz.tools:AndDevelopAssistant:0.1.2-no-x' 23 | Gradle3.0以下: debugCompile 'com.tlz.tools:AndDevelopAssistant:0.1.2-no-x' 24 | ``` 25 | 26 | 启动app,在浏览器中输入手机端ip地址+10000端口号进行访问(10000是默认端口号),如果不知道手机ip地址,可以在logcat窗口中查看名为AdaWebServer的日志,其中会输入完整的访问地址。日志记录功能默认开启 27 | 28 | ## 如何修改web访问端口 29 | 30 | 需要在build.gradle文件下加入以下代码: 31 | 32 | ``` 33 | debug { 34 | resValue("string", "ADA_DEBUG_PORT", "你的端口号") 35 | } 36 | ``` 37 | 38 | 当手机上有多个应用依赖该库时,就需要自定义端口,避免端口冲突 39 | 40 | ## 如何设置数据库密码 41 | 42 | 需要在Manifest中加入meta-data数据: 43 | 44 | ``` 45 | 46 | ``` 47 | 48 | ## 如何设置自定义数据库 49 | 50 | 可以使用以下模板代码: 51 | 52 | ``` 53 | //初始化自定义数据库文件 54 | if (BuildConfig.DEBUG) { 55 | try { 56 | val initializer = Class.forName("com.tlz.ada.AdaProvider") 57 | val method = initializer.getMethod("setCustomDatabaseFiles", Map::class.java) 58 | val customDatabaseFiles = HashMap>() 59 | customDatabaseFiles.put("Custom.db", Pair(File("${filesDir.absolutePath}/custom_dir/Custom.db"), "")) 60 | method.invoke(null, customDatabaseFiles) 61 | // 获取服务器地址 62 | val serverAddressMethod = initializer.getMethod("getAdaServerAddress") 63 | serverAddress = serverAddressMethod.invoke(null) 64 | } catch (e: Exception) { 65 | e.printStackTrace() 66 | } 67 | } 68 | //注: 69 | //HashMap>:第一个String类型是数据库的名字;File是数据据文件,第二个String是数据据密码,特别注意Pair是android.util.Pair类型不是kotlin的Pair类型 70 | ``` 71 | 72 | ## 问题 73 | 1. 如果遇到AndroidManifest的FileProider问题,请在你申明的provider加上`tools:replace="android:authorities"` 74 | 75 | ## 界面截图 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /lib/src/main/java/org/nanohttpd2/protocols/http/response/ChunkedOutputStream.java: -------------------------------------------------------------------------------- 1 | package org.nanohttpd2.protocols.http.response; 2 | 3 | /* 4 | * #%L 5 | * NanoHttpd-Core 6 | * %% 7 | * Copyright (C) 2012 - 2016 nanohttpd 8 | * %% 9 | * Redistribution and use in source and binary forms, with or without modification, 10 | * are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, this 13 | * list of conditions and the following disclaimer. 14 | * 15 | * 2. Redistributions in binary form must reproduce the above copyright notice, 16 | * this list of conditions and the following disclaimer in the documentation 17 | * and/or other materials provided with the distribution. 18 | * 19 | * 3. Neither the name of the nanohttpd nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 27 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 31 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | * #L% 34 | */ 35 | 36 | import java.io.FilterOutputStream; 37 | import java.io.IOException; 38 | import java.io.OutputStream; 39 | 40 | /** 41 | * Output stream that will automatically send every write to the wrapped 42 | * OutputStream according to chunked transfer: 43 | * http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1 44 | */ 45 | public class ChunkedOutputStream extends FilterOutputStream { 46 | 47 | public ChunkedOutputStream(OutputStream out) { 48 | super(out); 49 | } 50 | 51 | @Override 52 | public void write(int b) throws IOException { 53 | byte[] data = { 54 | (byte) b 55 | }; 56 | write(data, 0, 1); 57 | } 58 | 59 | @Override 60 | public void write(byte[] b) throws IOException { 61 | write(b, 0, b.length); 62 | } 63 | 64 | @Override 65 | public void write(byte[] b, int off, int len) throws IOException { 66 | if (len == 0) 67 | return; 68 | out.write(String.format("%x\r\n", len).getBytes()); 69 | out.write(b, off, len); 70 | out.write("\r\n".getBytes()); 71 | } 72 | 73 | public void finish() throws IOException { 74 | out.write("0\r\n\r\n".getBytes()); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /lib/src/main/java/org/nanohttpd2/util/ServerRunner.java: -------------------------------------------------------------------------------- 1 | package org.nanohttpd2.util; 2 | 3 | /* 4 | * #%L 5 | * NanoHttpd-Webserver 6 | * %% 7 | * Copyright (C) 2012 - 2015 nanohttpd 8 | * %% 9 | * Redistribution and use in source and binary forms, with or without modification, 10 | * are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, this 13 | * list of conditions and the following disclaimer. 14 | * 15 | * 2. Redistributions in binary form must reproduce the above copyright notice, 16 | * this list of conditions and the following disclaimer in the documentation 17 | * and/or other materials provided with the distribution. 18 | * 19 | * 3. Neither the name of the nanohttpd nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 27 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 31 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | * #L% 34 | */ 35 | 36 | import java.io.IOException; 37 | import java.util.logging.Level; 38 | import java.util.logging.Logger; 39 | 40 | import org.nanohttpd2.protocols.http.NanoHTTPD; 41 | 42 | public class ServerRunner { 43 | 44 | /** 45 | * logger to log to. 46 | */ 47 | private static final Logger LOG = Logger.getLogger(ServerRunner.class.getName()); 48 | 49 | public static void executeInstance(NanoHTTPD server) { 50 | try { 51 | server.start(NanoHTTPD.SOCKET_READ_TIMEOUT, false); 52 | } catch (IOException ioe) { 53 | System.err.println("Couldn't start server:\n" + ioe); 54 | System.exit(-1); 55 | } 56 | 57 | System.out.println("Server started, Hit Enter to stop.\n"); 58 | 59 | try { 60 | System.in.read(); 61 | } catch (Throwable ignored) { 62 | } 63 | 64 | server.stop(); 65 | System.out.println("Server stopped.\n"); 66 | } 67 | 68 | public static void run(Class serverClass) { 69 | try { 70 | executeInstance(serverClass.newInstance()); 71 | } catch (Exception e) { 72 | ServerRunner.LOG.log(Level.SEVERE, "Could not create server", e); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /lib/src/main/java/org/nanohttpd2/protocols/http/sockets/SecureServerSocketFactory.java: -------------------------------------------------------------------------------- 1 | package org.nanohttpd2.protocols.http.sockets; 2 | 3 | /* 4 | * #%L 5 | * NanoHttpd-Core 6 | * %% 7 | * Copyright (C) 2012 - 2016 nanohttpd 8 | * %% 9 | * Redistribution and use in source and binary forms, with or without modification, 10 | * are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, this 13 | * list of conditions and the following disclaimer. 14 | * 15 | * 2. Redistributions in binary form must reproduce the above copyright notice, 16 | * this list of conditions and the following disclaimer in the documentation 17 | * and/or other materials provided with the distribution. 18 | * 19 | * 3. Neither the name of the nanohttpd nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 27 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 31 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | * #L% 34 | */ 35 | 36 | import java.io.IOException; 37 | import java.net.ServerSocket; 38 | 39 | import javax.net.ssl.SSLServerSocket; 40 | import javax.net.ssl.SSLServerSocketFactory; 41 | 42 | import org.nanohttpd2.util.IFactoryThrowing; 43 | 44 | /** 45 | * Creates a new SSLServerSocket 46 | */ 47 | public class SecureServerSocketFactory implements IFactoryThrowing { 48 | 49 | private SSLServerSocketFactory sslServerSocketFactory; 50 | 51 | private String[] sslProtocols; 52 | 53 | public SecureServerSocketFactory(SSLServerSocketFactory sslServerSocketFactory, String[] sslProtocols) { 54 | this.sslServerSocketFactory = sslServerSocketFactory; 55 | this.sslProtocols = sslProtocols; 56 | } 57 | 58 | @Override 59 | public ServerSocket create() throws IOException { 60 | SSLServerSocket ss = null; 61 | ss = (SSLServerSocket) this.sslServerSocketFactory.createServerSocket(); 62 | if (this.sslProtocols != null) { 63 | ss.setEnabledProtocols(this.sslProtocols); 64 | } else { 65 | ss.setEnabledProtocols(ss.getSupportedProtocols()); 66 | } 67 | ss.setUseClientMode(false); 68 | ss.setWantClientAuth(false); 69 | ss.setNeedClientAuth(false); 70 | return ss; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /lib/src/main/java/org/nanohttpd2/protocols/http/tempfiles/DefaultTempFile.java: -------------------------------------------------------------------------------- 1 | package org.nanohttpd2.protocols.http.tempfiles; 2 | 3 | /* 4 | * #%L 5 | * NanoHttpd-Core 6 | * %% 7 | * Copyright (C) 2012 - 2016 nanohttpd 8 | * %% 9 | * Redistribution and use in source and binary forms, with or without modification, 10 | * are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, this 13 | * list of conditions and the following disclaimer. 14 | * 15 | * 2. Redistributions in binary form must reproduce the above copyright notice, 16 | * this list of conditions and the following disclaimer in the documentation 17 | * and/or other materials provided with the distribution. 18 | * 19 | * 3. Neither the name of the nanohttpd nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 27 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 31 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | * #L% 34 | */ 35 | 36 | import java.io.File; 37 | import java.io.FileOutputStream; 38 | import java.io.IOException; 39 | import java.io.OutputStream; 40 | 41 | import org.nanohttpd2.protocols.http.NanoHTTPD; 42 | 43 | /** 44 | * Default strategy for creating and cleaning up temporary files. 45 | *

46 | *

47 | * By default, files are created by File.createTempFile() in the 48 | * directory specified. 49 | *

50 | */ 51 | public class DefaultTempFile implements ITempFile { 52 | 53 | private final File file; 54 | 55 | private final OutputStream fstream; 56 | 57 | public DefaultTempFile(File tempdir) throws IOException { 58 | this.file = File.createTempFile("NanoHTTPD-", "", tempdir); 59 | this.fstream = new FileOutputStream(this.file); 60 | } 61 | 62 | @Override 63 | public void delete() throws Exception { 64 | NanoHTTPD.safeClose(this.fstream); 65 | if (!this.file.delete()) { 66 | throw new Exception("could not delete temporary file: " + this.file.getAbsolutePath()); 67 | } 68 | } 69 | 70 | @Override 71 | public String getName() { 72 | return this.file.getAbsolutePath(); 73 | } 74 | 75 | @Override 76 | public OutputStream open() throws Exception { 77 | return this.fstream; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /lib/src/main/java/org/nanohttpd2/protocols/http/content/Cookie.java: -------------------------------------------------------------------------------- 1 | package org.nanohttpd2.protocols.http.content; 2 | 3 | /* 4 | * #%L 5 | * NanoHttpd-Core 6 | * %% 7 | * Copyright (C) 2012 - 2016 nanohttpd 8 | * %% 9 | * Redistribution and use in source and binary forms, with or without modification, 10 | * are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, this 13 | * list of conditions and the following disclaimer. 14 | * 15 | * 2. Redistributions in binary form must reproduce the above copyright notice, 16 | * this list of conditions and the following disclaimer in the documentation 17 | * and/or other materials provided with the distribution. 18 | * 19 | * 3. Neither the name of the nanohttpd nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 27 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 31 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | * #L% 34 | */ 35 | 36 | import java.text.SimpleDateFormat; 37 | import java.util.Calendar; 38 | import java.util.Locale; 39 | import java.util.TimeZone; 40 | 41 | /** 42 | * A simple cookie representation. This is old code and is flawed in many ways. 43 | * 44 | * @author LordFokas 45 | */ 46 | public class Cookie { 47 | 48 | public static String getHTTPTime(int days) { 49 | Calendar calendar = Calendar.getInstance(); 50 | SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US); 51 | dateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); 52 | calendar.add(Calendar.DAY_OF_MONTH, days); 53 | return dateFormat.format(calendar.getTime()); 54 | } 55 | 56 | private final String n, v, e; 57 | 58 | public Cookie(String name, String value) { 59 | this(name, value, 30); 60 | } 61 | 62 | public Cookie(String name, String value, int numDays) { 63 | this.n = name; 64 | this.v = value; 65 | this.e = getHTTPTime(numDays); 66 | } 67 | 68 | public Cookie(String name, String value, String expires) { 69 | this.n = name; 70 | this.v = value; 71 | this.e = expires; 72 | } 73 | 74 | public String getHTTPHeader() { 75 | String fmt = "%s=%s; expires=%s"; 76 | return String.format(fmt, this.n, this.v, this.e); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /app/src/main/java/tomlezen/androiddebuglib/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package tomlezen.androiddebuglib 2 | 3 | import android.Manifest 4 | import android.annotation.SuppressLint 5 | import android.content.Context 6 | import android.os.Bundle 7 | import androidx.appcompat.app.AppCompatActivity 8 | import android.util.Pair 9 | import android.widget.TextView 10 | import android.widget.Toast 11 | import androidx.sqlite.db.SupportSQLiteDatabase 12 | import com.tlz.andbase.persmission.RxPermissions 13 | import tomlezen.androiddebuglib.database.CustomDB 14 | import tomlezen.androiddebuglib.database.TestOneDb 15 | import tomlezen.androiddebuglib.database.TestThreeDb 16 | import tomlezen.androiddebuglib.database.TestTwoDb 17 | import tomlezen.androiddebuglib.room.RoomDBTestHelper 18 | import java.io.File 19 | import java.util.* 20 | 21 | class MainActivity : AppCompatActivity() { 22 | 23 | @SuppressLint("SetTextI18n") 24 | override fun onCreate(savedInstanceState: Bundle?) { 25 | super.onCreate(savedInstanceState) 26 | setContentView(R.layout.activity_main) 27 | val roomDbTestHelper = RoomDBTestHelper(this) 28 | Thread{ 29 | val prefsOne = getSharedPreferences("TestOne", Context.MODE_PRIVATE) 30 | val prefsTwo = getSharedPreferences("TestTwo", Context.MODE_PRIVATE) 31 | 32 | prefsOne.edit().apply{ 33 | putString("testString", "string") 34 | putInt("testInt", 1) 35 | putLong("testLong", System.currentTimeMillis()) 36 | putFloat("testFloat", Random().nextFloat()) 37 | putBoolean("testBoolean", false) 38 | putStringSet("testStringSet", setOf("value1", "value2", "value2")) 39 | }.apply() 40 | 41 | prefsTwo.edit().apply{ 42 | putString("test1", "one").commit() 43 | putString("test2", "two").commit() 44 | }.apply() 45 | 46 | CustomDB.create(this) 47 | TestOneDb.create(this) 48 | TestTwoDb.create(this) 49 | TestThreeDb.create(this) 50 | 51 | roomDbTestHelper.init() 52 | }.start() 53 | 54 | if (BuildConfig.DEBUG) { 55 | try { 56 | //初始化自定义数据库文件 57 | val initializer = Class.forName("com.tlz.ada.AdaProvider") 58 | val method = initializer.getMethod("setCustomDatabaseFiles", Map::class.java) 59 | val customDatabaseFiles = HashMap>() 60 | customDatabaseFiles["Custom.db"] = Pair(File("${filesDir.absolutePath}/custom_dir/Custom.db"), "") 61 | method.invoke(null, customDatabaseFiles) 62 | 63 | //初始化内存数据库 64 | val setInMemoryDbMethod = initializer.getMethod("setInMemoryRoomDatabases", Map::class.java) 65 | val inMemoryDbs = HashMap() 66 | inMemoryDbs[roomDbTestHelper.name] = roomDbTestHelper.inMemoryAppDatabase 67 | setInMemoryDbMethod.invoke(null, inMemoryDbs) 68 | 69 | //获取服务端地址 70 | val serverAddressMethod = initializer.getMethod("getAdaServerAddress") 71 | findViewById(R.id.tv_ip).text = "服务器地址:${serverAddressMethod.invoke(null)}" 72 | } catch (e: Exception) { 73 | e.printStackTrace() 74 | } 75 | } 76 | 77 | RxPermissions.with(this) 78 | .request(arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE)) 79 | .filter { !it } 80 | .subscribe{ 81 | Toast.makeText(this, "您拒绝了文件读写权限,会导致文件相关功能不可用", Toast.LENGTH_LONG).show() 82 | } 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /lib/src/main/java/org/nanohttpd2/protocols/http/IHTTPSession.java: -------------------------------------------------------------------------------- 1 | package org.nanohttpd2.protocols.http; 2 | 3 | /* 4 | * #%L 5 | * NanoHttpd-Core 6 | * %% 7 | * Copyright (C) 2012 - 2016 nanohttpd 8 | * %% 9 | * Redistribution and use in source and binary forms, with or without modification, 10 | * are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, this 13 | * list of conditions and the following disclaimer. 14 | * 15 | * 2. Redistributions in binary form must reproduce the above copyright notice, 16 | * this list of conditions and the following disclaimer in the documentation 17 | * and/or other materials provided with the distribution. 18 | * 19 | * 3. Neither the name of the nanohttpd nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 27 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 31 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | * #L% 34 | */ 35 | 36 | import java.io.IOException; 37 | import java.io.InputStream; 38 | import java.util.List; 39 | import java.util.Map; 40 | 41 | import org.nanohttpd2.protocols.http.NanoHTTPD.ResponseException; 42 | import org.nanohttpd2.protocols.http.content.CookieHandler; 43 | import org.nanohttpd2.protocols.http.request.Method; 44 | 45 | /** 46 | * Handles one session, i.e. parses the HTTP request and returns the response. 47 | */ 48 | public interface IHTTPSession { 49 | 50 | void execute() throws IOException; 51 | 52 | CookieHandler getCookies(); 53 | 54 | Map getHeaders(); 55 | 56 | InputStream getInputStream(); 57 | 58 | Method getMethod(); 59 | 60 | /** 61 | * This method will only return the first value for a given parameter. You 62 | * will want to use getParameters if you expect multiple values for a given 63 | * key. 64 | * 65 | * @deprecated use {@link #getParameters()} instead. 66 | */ 67 | @Deprecated 68 | Map getParms(); 69 | 70 | Map> getParameters(); 71 | 72 | String getQueryParameterString(); 73 | 74 | /** 75 | * @return the path part of the URL. 76 | */ 77 | String getUri(); 78 | 79 | /** 80 | * Adds the files in the request body to the files map. 81 | * 82 | * @param files 83 | * map to modify 84 | */ 85 | void parseBody(Map files) throws IOException, ResponseException; 86 | 87 | /** 88 | * Get the remote ip address of the requester. 89 | * 90 | * @return the IP address. 91 | */ 92 | String getRemoteIpAddress(); 93 | } 94 | -------------------------------------------------------------------------------- /lib/src/main/java/org/nanohttpd2/protocols/websockets/CloseFrame.java: -------------------------------------------------------------------------------- 1 | package org.nanohttpd2.protocols.websockets; 2 | 3 | /* 4 | * #%L 5 | * NanoHttpd-Websocket 6 | * %% 7 | * Copyright (C) 2012 - 2016 nanohttpd 8 | * %% 9 | * Redistribution and use in source and binary forms, with or without modification, 10 | * are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, this 13 | * list of conditions and the following disclaimer. 14 | * 15 | * 2. Redistributions in binary form must reproduce the above copyright notice, 16 | * this list of conditions and the following disclaimer in the documentation 17 | * and/or other materials provided with the distribution. 18 | * 19 | * 3. Neither the name of the nanohttpd nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 27 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 31 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | * #L% 34 | */ 35 | 36 | import java.nio.charset.CharacterCodingException; 37 | 38 | public class CloseFrame extends WebSocketFrame { 39 | 40 | private static byte[] generatePayload(CloseCode code, String closeReason) throws CharacterCodingException { 41 | if (code != null) { 42 | byte[] reasonBytes = text2Binary(closeReason); 43 | byte[] payload = new byte[reasonBytes.length + 2]; 44 | payload[0] = (byte) (code.getValue() >> 8 & 0xFF); 45 | payload[1] = (byte) (code.getValue() & 0xFF); 46 | System.arraycopy(reasonBytes, 0, payload, 2, reasonBytes.length); 47 | return payload; 48 | } else { 49 | return new byte[0]; 50 | } 51 | } 52 | 53 | private CloseCode _closeCode; 54 | 55 | private String _closeReason; 56 | 57 | public CloseFrame(CloseCode code, String closeReason) throws CharacterCodingException { 58 | super(OpCode.Close, true, generatePayload(code, closeReason)); 59 | } 60 | 61 | public CloseFrame(WebSocketFrame wrap) throws CharacterCodingException { 62 | super(wrap); 63 | assert wrap.getOpCode() == OpCode.Close; 64 | if (wrap.getBinaryPayload().length >= 2) { 65 | this._closeCode = CloseCode.find((wrap.getBinaryPayload()[0] & 0xFF) << 8 | wrap.getBinaryPayload()[1] & 0xFF); 66 | this._closeReason = binary2Text(getBinaryPayload(), 2, getBinaryPayload().length - 2); 67 | } 68 | } 69 | 70 | public CloseCode getCloseCode() { 71 | return this._closeCode; 72 | } 73 | 74 | public String getCloseReason() { 75 | return this._closeReason; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /lib/src/main/java/org/nanohttpd2/protocols/http/tempfiles/DefaultTempFileManager.java: -------------------------------------------------------------------------------- 1 | package org.nanohttpd2.protocols.http.tempfiles; 2 | 3 | /* 4 | * #%L 5 | * NanoHttpd-Core 6 | * %% 7 | * Copyright (C) 2012 - 2016 nanohttpd 8 | * %% 9 | * Redistribution and use in source and binary forms, with or without modification, 10 | * are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, this 13 | * list of conditions and the following disclaimer. 14 | * 15 | * 2. Redistributions in binary form must reproduce the above copyright notice, 16 | * this list of conditions and the following disclaimer in the documentation 17 | * and/or other materials provided with the distribution. 18 | * 19 | * 3. Neither the name of the nanohttpd nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 27 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 31 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | * #L% 34 | */ 35 | 36 | import java.io.File; 37 | import java.util.ArrayList; 38 | import java.util.List; 39 | import java.util.logging.Level; 40 | 41 | import org.nanohttpd2.protocols.http.NanoHTTPD; 42 | 43 | /** 44 | * Default strategy for creating and cleaning up temporary files. 45 | *

46 | *

47 | * This class stores its files in the standard location (that is, wherever 48 | * java.io.tmpdir points to). Files are added to an internal list, 49 | * and deleted when no longer needed (that is, when clear() is 50 | * invoked at the end of processing a request). 51 | *

52 | */ 53 | public class DefaultTempFileManager implements ITempFileManager { 54 | 55 | private final File tmpdir; 56 | 57 | private final List tempFiles; 58 | 59 | public DefaultTempFileManager() { 60 | this.tmpdir = new File(System.getProperty("java.io.tmpdir")); 61 | if (!tmpdir.exists()) { 62 | tmpdir.mkdirs(); 63 | } 64 | this.tempFiles = new ArrayList(); 65 | } 66 | 67 | @Override 68 | public void clear() { 69 | for (ITempFile file : this.tempFiles) { 70 | try { 71 | file.delete(); 72 | } catch (Exception ignored) { 73 | NanoHTTPD.LOG.log(Level.WARNING, "could not delete file ", ignored); 74 | } 75 | } 76 | this.tempFiles.clear(); 77 | } 78 | 79 | @Override 80 | public ITempFile createTempFile(String filename_hint) throws Exception { 81 | DefaultTempFile tempFile = new DefaultTempFile(this.tmpdir); 82 | this.tempFiles.add(tempFile); 83 | return tempFile; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /lib/src/main/assets/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Android开发助手 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 105 | 106 | 107 | 108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 | 117 | 118 | -------------------------------------------------------------------------------- /lib/src/main/java/org/nanohttpd2/protocols/http/threading/DefaultAsyncRunner.java: -------------------------------------------------------------------------------- 1 | package org.nanohttpd2.protocols.http.threading; 2 | 3 | /* 4 | * #%L 5 | * NanoHttpd-Core 6 | * %% 7 | * Copyright (C) 2012 - 2016 nanohttpd 8 | * %% 9 | * Redistribution and use in source and binary forms, with or without modification, 10 | * are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, this 13 | * list of conditions and the following disclaimer. 14 | * 15 | * 2. Redistributions in binary form must reproduce the above copyright notice, 16 | * this list of conditions and the following disclaimer in the documentation 17 | * and/or other materials provided with the distribution. 18 | * 19 | * 3. Neither the name of the nanohttpd nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 27 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 31 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | * #L% 34 | */ 35 | 36 | import java.util.ArrayList; 37 | import java.util.Collections; 38 | import java.util.List; 39 | 40 | import org.nanohttpd2.protocols.http.ClientHandler; 41 | 42 | /** 43 | * Default threading strategy for NanoHTTPD. 44 | *

45 | *

46 | * By default, the server spawns a new Thread for every incoming request. These 47 | * are set to daemon status, and named according to the request number. 48 | * The name is useful when profiling the application. 49 | *

50 | */ 51 | public class DefaultAsyncRunner implements IAsyncRunner { 52 | 53 | protected long requestCount; 54 | 55 | private final List running = Collections.synchronizedList(new ArrayList()); 56 | 57 | /** 58 | * @return a list with currently running clients. 59 | */ 60 | public List getRunning() { 61 | return running; 62 | } 63 | 64 | @Override 65 | public void closeAll() { 66 | // copy of the list for concurrency 67 | for (ClientHandler clientHandler : new ArrayList(this.running)) { 68 | clientHandler.close(); 69 | } 70 | } 71 | 72 | @Override 73 | public void closed(ClientHandler clientHandler) { 74 | this.running.remove(clientHandler); 75 | } 76 | 77 | @Override 78 | public void exec(ClientHandler clientHandler) { 79 | ++this.requestCount; 80 | this.running.add(clientHandler); 81 | createThread(clientHandler).start(); 82 | } 83 | 84 | protected Thread createThread(ClientHandler clientHandler) { 85 | Thread t = new Thread(clientHandler); 86 | t.setDaemon(true); 87 | t.setName("NanoHttpd Request Processor (#" + this.requestCount + ")"); 88 | return t; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /lib/src/main/java/org/nanohttpd2/protocols/http/ServerRunnable.java: -------------------------------------------------------------------------------- 1 | package org.nanohttpd2.protocols.http; 2 | 3 | /* 4 | * #%L 5 | * NanoHttpd-Core 6 | * %% 7 | * Copyright (C) 2012 - 2016 nanohttpd 8 | * %% 9 | * Redistribution and use in source and binary forms, with or without modification, 10 | * are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, this 13 | * list of conditions and the following disclaimer. 14 | * 15 | * 2. Redistributions in binary form must reproduce the above copyright notice, 16 | * this list of conditions and the following disclaimer in the documentation 17 | * and/or other materials provided with the distribution. 18 | * 19 | * 3. Neither the name of the nanohttpd nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 27 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 31 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | * #L% 34 | */ 35 | 36 | import java.io.IOException; 37 | import java.io.InputStream; 38 | import java.net.InetSocketAddress; 39 | import java.net.Socket; 40 | import java.util.logging.Level; 41 | 42 | /** 43 | * The runnable that will be used for the main listening thread. 44 | */ 45 | public class ServerRunnable implements Runnable { 46 | 47 | private NanoHTTPD httpd; 48 | 49 | private final int timeout; 50 | 51 | private IOException bindException; 52 | 53 | private boolean hasBinded = false; 54 | 55 | public ServerRunnable(NanoHTTPD httpd, int timeout) { 56 | this.httpd = httpd; 57 | this.timeout = timeout; 58 | } 59 | 60 | @Override 61 | public void run() { 62 | try { 63 | httpd.getMyServerSocket().bind(httpd.hostname != null ? new InetSocketAddress(httpd.hostname, httpd.myPort) : new InetSocketAddress(httpd.myPort)); 64 | hasBinded = true; 65 | } catch (IOException e) { 66 | this.bindException = e; 67 | return; 68 | } 69 | do { 70 | try { 71 | final Socket finalAccept = httpd.getMyServerSocket().accept(); 72 | if (this.timeout > 0) { 73 | finalAccept.setSoTimeout(this.timeout); 74 | } 75 | final InputStream inputStream = finalAccept.getInputStream(); 76 | httpd.asyncRunner.exec(httpd.createClientHandler(finalAccept, inputStream)); 77 | } catch (IOException e) { 78 | NanoHTTPD.LOG.log(Level.FINE, "Communication with the client broken", e); 79 | } 80 | } while (!httpd.getMyServerSocket().isClosed()); 81 | } 82 | 83 | public IOException getBindException() { 84 | return bindException; 85 | } 86 | 87 | public boolean hasBinded() { 88 | return hasBinded; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/socket/AdaWSD.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada.socket 2 | 3 | import com.tlz.ada.Ada 4 | import com.tlz.ada.models.Log 5 | import org.nanohttpd2.protocols.http.IHTTPSession 6 | import org.nanohttpd2.protocols.http.NanoHTTPD 7 | import org.nanohttpd2.protocols.http.response.Response 8 | import org.nanohttpd2.protocols.websockets.NanoWSD 9 | import org.nanohttpd2.protocols.websockets.WebSocket 10 | 11 | 12 | /** 13 | * Created by Tomlezen. 14 | * Data: 2018/9/7. 15 | * Time: 11:25. 16 | */ 17 | class AdaWSD(port: Int) : NanoWSD(port) { 18 | 19 | private val active = mutableListOf() 20 | private val toAdd = mutableListOf() 21 | private val toRemove = mutableListOf() 22 | 23 | /** 24 | * 启动. 25 | * @param nanoHTTPD NanoHTTPD 26 | */ 27 | fun start(nanoHTTPD: NanoHTTPD) { 28 | toAdd.clear() 29 | toRemove.clear() 30 | active.clear() 31 | Ada.submitTask { 32 | var nextTime = System.currentTimeMillis() 33 | while (nanoHTTPD.isAlive) { 34 | nextTime += 4000L 35 | while (System.currentTimeMillis() < nextTime) { 36 | try { 37 | Thread.sleep(nextTime - System.currentTimeMillis()) 38 | } catch (ignored: InterruptedException) { 39 | } 40 | } 41 | synchronized(toAdd) { 42 | active.addAll(toAdd) 43 | toAdd.clear() 44 | } 45 | synchronized(toRemove) { 46 | active.removeAll(toRemove) 47 | toRemove.clear() 48 | for (ws in active) { 49 | try { 50 | ws.ping(pingPayload) 51 | } catch (e: Exception) { 52 | toRemove.add(ws) 53 | } 54 | } 55 | } 56 | } 57 | } 58 | } 59 | 60 | /** 61 | * 请求. 62 | * @param session IHTTPSession 63 | * @return AdaResponse? 64 | */ 65 | fun onRequest(session: IHTTPSession): Response? = 66 | if (session.uri == "/api/log") handleWebSocket(session) else null 67 | 68 | override fun openWebSocket(handshake: IHTTPSession): WebSocket { 69 | val socket = AdaWebSocket(handshake) 70 | synchronized(toAdd) { 71 | if (!toAdd.contains(socket)) 72 | toAdd.add(socket) 73 | } 74 | return socket 75 | } 76 | 77 | /** 78 | * 关闭socket. 79 | * @param webSocket DebuggerWebSocket 80 | */ 81 | fun closeSocket(socket: AdaWebSocket) { 82 | synchronized(toRemove) { 83 | if (!toRemove.contains(socket)) 84 | toRemove.add(socket) 85 | } 86 | } 87 | 88 | /** 89 | * 发送消息. 90 | * @param log String 91 | */ 92 | fun send(log: String) { 93 | val logJson by lazy { Ada.adaGson.toJson(wrapLog(log.toLogObj())) } 94 | active.filter { it.isOpen } 95 | .forEach { 96 | runCatching { it.send(logJson) } 97 | } 98 | } 99 | 100 | private fun String.toLogObj() = 101 | when { 102 | contains("V/") -> Log("V", android.util.Log.VERBOSE, this, this) 103 | contains("D/") -> Log("D", android.util.Log.DEBUG, this, this) 104 | contains("I/") -> Log("I", android.util.Log.INFO, this, this) 105 | contains("W/") -> Log("W", android.util.Log.WARN, this, this) 106 | contains("E/") -> Log("E", android.util.Log.ERROR, this, this) 107 | else -> Log("A", android.util.Log.ASSERT, this, this) 108 | } 109 | 110 | /** 111 | * 包装下. 112 | * @return String 113 | */ 114 | private fun wrapLog(log: Log): Log { 115 | when (log.type) { 116 | "E" -> log.content = "

${log.content}

" 117 | "W" -> log.content = "

${log.content}

" 118 | else -> log.content = "

${log.content}

" 119 | } 120 | return log 121 | } 122 | 123 | companion object { 124 | private val pingPayload = "1337DEADBEEFC001".toByteArray() 125 | } 126 | 127 | } -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/AdaWebServer.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada 2 | 3 | import android.Manifest 4 | import android.content.Context 5 | import android.util.Log 6 | import android.util.Pair 7 | import androidx.sqlite.db.SupportSQLiteDatabase 8 | import com.tlz.ada.db.AdaDataProvider 9 | import com.tlz.ada.db.AdaDataProviderImpl 10 | import com.tlz.ada.handlers.* 11 | import com.tlz.ada.socket.AdaWSD 12 | import org.nanohttpd2.protocols.http.IHTTPSession 13 | import org.nanohttpd2.protocols.http.NanoHTTPD 14 | import org.nanohttpd2.protocols.http.response.Response 15 | import org.nanohttpd2.protocols.http.response.Response.newFixedLengthResponse 16 | import org.nanohttpd2.protocols.http.response.Status 17 | import java.io.File 18 | 19 | 20 | /** 21 | * 服务器. 22 | * Created by tomlezen. 23 | * Data: 2018/1/27. 24 | * Time: 15:00. 25 | */ 26 | class AdaWebServer internal constructor(internal val ctx: Context, port: Int) : NanoHTTPD(port) { 27 | 28 | private val dataProvider: AdaDataProvider by lazy { AdaDataProviderImpl(ctx) } 29 | private val appManager by lazy { AdaApplicationManager(ctx) } 30 | private val activityLifeCycleHooker by lazy { AdaActivityLifeCycleListener(ctx) } 31 | 32 | /** 所有请求处理器. */ 33 | private val handlers = mutableListOf() 34 | 35 | /** 服务器地址. */ 36 | var serverAddress: String = "" 37 | private set 38 | 39 | /** 文件读写权限是否通过. */ 40 | var filePermissionGranted = false 41 | get() { 42 | if (!field) { 43 | field = ctx.isPermissionsGranted(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE) 44 | } 45 | return field 46 | } 47 | 48 | init { 49 | serverAddress = "${AdaProvider.getPhoneIp()}:$port" 50 | activityLifeCycleHooker.install() 51 | tempFileManagerFactory = AdaTempFileManagerFactory(ctx) 52 | Ada.submitTask { 53 | runCatching { 54 | // 注册各种处理器 55 | val wsd = AdaWSD(port) 56 | handlers.add(LogRequestHandler(ctx, wsd)) 57 | handlers.add(InitRequestHandler(ctx, dataProvider, appManager)) 58 | handlers.add(DbRequestHandler(dataProvider)) 59 | handlers.add(AppRequestHandler(ctx, appManager)) 60 | handlers.add(FileRequestHandler(this)) 61 | handlers.add(ScreenShotHandler(activityLifeCycleHooker)) 62 | handlers.add(DefaultRequestHandler(ctx)) 63 | 64 | start(10000, false) 65 | wsd.start(this) 66 | Log.e(TAG, "address: $serverAddress") 67 | }.onFailure { 68 | Log.e(TAG, "Android调试辅助初始化失败") 69 | } 70 | } 71 | } 72 | 73 | /** 74 | * 设置自定义数据库. 75 | * @param files Map> 76 | */ 77 | fun setCustomDatabaseFiles(files: Map>) { 78 | if (files.isEmpty()) return 79 | val newFiles = mutableMapOf>() 80 | files.forEach { 81 | newFiles[it.key] = it.value.first to it.value.second 82 | } 83 | dataProvider.setCustomDatabaseFiles(newFiles) 84 | } 85 | 86 | /** 87 | * 设置内存数据库. 88 | * @param databases Map 89 | */ 90 | fun setInMemoryRoomDatabases(databases: Map) { 91 | if (databases.isEmpty()) return 92 | dataProvider.setInMemoryRoomDatabases(databases) 93 | } 94 | 95 | override fun serve(session: IHTTPSession?): Response { 96 | session?.run { 97 | val startTimeMillis = System.currentTimeMillis() 98 | handlers.forEach { 99 | it.onRequest(session)?.let { resp -> 100 | if (BuildConfig.DEBUG) { 101 | Log.i(TAG, "${session.uri} response time ${System.currentTimeMillis() - startTimeMillis}") 102 | } 103 | return resp 104 | } 105 | } 106 | } 107 | return newFixedLengthResponse(Status.FORBIDDEN, MIME_PLAINTEXT, "不支持该请求") 108 | } 109 | 110 | companion object { 111 | const val TAG = "AdaWebServer" 112 | } 113 | 114 | } -------------------------------------------------------------------------------- /lib/src/main/java/org/nanohttpd2/protocols/http/ClientHandler.java: -------------------------------------------------------------------------------- 1 | package org.nanohttpd2.protocols.http; 2 | 3 | /* 4 | * #%L 5 | * NanoHttpd-Core 6 | * %% 7 | * Copyright (C) 2012 - 2016 nanohttpd 8 | * %% 9 | * Redistribution and use in source and binary forms, with or without modification, 10 | * are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, this 13 | * list of conditions and the following disclaimer. 14 | * 15 | * 2. Redistributions in binary form must reproduce the above copyright notice, 16 | * this list of conditions and the following disclaimer in the documentation 17 | * and/or other materials provided with the distribution. 18 | * 19 | * 3. Neither the name of the nanohttpd nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 27 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 31 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | * #L% 34 | */ 35 | 36 | import java.io.InputStream; 37 | import java.io.OutputStream; 38 | import java.net.Socket; 39 | import java.net.SocketException; 40 | import java.net.SocketTimeoutException; 41 | import java.util.logging.Level; 42 | 43 | import org.nanohttpd2.protocols.http.tempfiles.ITempFileManager; 44 | 45 | /** 46 | * The runnable that will be used for every new client connection. 47 | */ 48 | public class ClientHandler implements Runnable { 49 | 50 | private final NanoHTTPD httpd; 51 | 52 | private final InputStream inputStream; 53 | 54 | private final Socket acceptSocket; 55 | 56 | public ClientHandler(NanoHTTPD httpd, InputStream inputStream, Socket acceptSocket) { 57 | this.httpd = httpd; 58 | this.inputStream = inputStream; 59 | this.acceptSocket = acceptSocket; 60 | } 61 | 62 | public void close() { 63 | NanoHTTPD.safeClose(this.inputStream); 64 | NanoHTTPD.safeClose(this.acceptSocket); 65 | } 66 | 67 | @Override 68 | public void run() { 69 | OutputStream outputStream = null; 70 | try { 71 | outputStream = this.acceptSocket.getOutputStream(); 72 | ITempFileManager tempFileManager = httpd.getTempFileManagerFactory().create(); 73 | HTTPSession session = new HTTPSession(httpd, tempFileManager, this.inputStream, outputStream, this.acceptSocket.getInetAddress()); 74 | while (!this.acceptSocket.isClosed()) { 75 | session.execute(); 76 | } 77 | } catch (Exception e) { 78 | // When the socket is closed by the client, 79 | // we throw our own SocketException 80 | // to break the "keep alive" loop above. If 81 | // the exception was anything other 82 | // than the expected SocketException OR a 83 | // SocketTimeoutException, print the 84 | // stacktrace 85 | if (!(e instanceof SocketException && "NanoHttpd Shutdown".equals(e.getMessage())) && !(e instanceof SocketTimeoutException)) { 86 | NanoHTTPD.LOG.log(Level.SEVERE, "Communication with the client broken, or an bug in the handler code", e); 87 | } 88 | } finally { 89 | NanoHTTPD.safeClose(outputStream); 90 | NanoHTTPD.safeClose(this.inputStream); 91 | NanoHTTPD.safeClose(this.acceptSocket); 92 | httpd.asyncRunner.closed(this); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /lib/src/main/java/org/nanohttpd2/protocols/http/response/Status.java: -------------------------------------------------------------------------------- 1 | package org.nanohttpd2.protocols.http.response; 2 | 3 | /* 4 | * #%L 5 | * NanoHttpd-Core 6 | * %% 7 | * Copyright (C) 2012 - 2016 nanohttpd 8 | * %% 9 | * Redistribution and use in source and binary forms, with or without modification, 10 | * are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, this 13 | * list of conditions and the following disclaimer. 14 | * 15 | * 2. Redistributions in binary form must reproduce the above copyright notice, 16 | * this list of conditions and the following disclaimer in the documentation 17 | * and/or other materials provided with the distribution. 18 | * 19 | * 3. Neither the name of the nanohttpd nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 27 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 31 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | * #L% 34 | */ 35 | 36 | /** 37 | * Some HTTP response status codes 38 | */ 39 | public enum Status implements IStatus { 40 | SWITCH_PROTOCOL(101, "Switching Protocols"), 41 | 42 | OK(200, "OK"), 43 | CREATED(201, "Created"), 44 | ACCEPTED(202, "Accepted"), 45 | NO_CONTENT(204, "No Content"), 46 | PARTIAL_CONTENT(206, "Partial Content"), 47 | MULTI_STATUS(207, "Multi-Status"), 48 | 49 | REDIRECT(301, "Moved Permanently"), 50 | /** 51 | * Many user agents mishandle 302 in ways that violate the RFC1945 spec 52 | * (i.e., redirect a POST to a GET). 303 and 307 were added in RFC2616 to 53 | * address this. You should prefer 303 and 307 unless the calling user agent 54 | * does not support 303 and 307 functionality 55 | */ 56 | @Deprecated 57 | FOUND(302, "Found"), 58 | REDIRECT_SEE_OTHER(303, "See Other"), 59 | NOT_MODIFIED(304, "Not Modified"), 60 | TEMPORARY_REDIRECT(307, "Temporary Redirect"), 61 | 62 | BAD_REQUEST(400, "Bad Request"), 63 | UNAUTHORIZED(401, "Unauthorized"), 64 | FORBIDDEN(403, "Forbidden"), 65 | NOT_FOUND(404, "Not Found"), 66 | METHOD_NOT_ALLOWED(405, "Method Not Allowed"), 67 | NOT_ACCEPTABLE(406, "Not Acceptable"), 68 | REQUEST_TIMEOUT(408, "Request Timeout"), 69 | CONFLICT(409, "Conflict"), 70 | GONE(410, "Gone"), 71 | LENGTH_REQUIRED(411, "Length Required"), 72 | PRECONDITION_FAILED(412, "Precondition Failed"), 73 | PAYLOAD_TOO_LARGE(413, "Payload Too Large"), 74 | UNSUPPORTED_MEDIA_TYPE(415, "Unsupported Media Type"), 75 | RANGE_NOT_SATISFIABLE(416, "Requested Range Not Satisfiable"), 76 | EXPECTATION_FAILED(417, "Expectation Failed"), 77 | TOO_MANY_REQUESTS(429, "Too Many Requests"), 78 | 79 | INTERNAL_ERROR(500, "Internal Server Error"), 80 | NOT_IMPLEMENTED(501, "Not Implemented"), 81 | SERVICE_UNAVAILABLE(503, "Service Unavailable"), 82 | UNSUPPORTED_HTTP_VERSION(505, "HTTP Version Not Supported"); 83 | 84 | private final int requestStatus; 85 | 86 | private final String description; 87 | 88 | Status(int requestStatus, String description) { 89 | this.requestStatus = requestStatus; 90 | this.description = description; 91 | } 92 | 93 | public static Status lookup(int requestStatus) { 94 | for (Status status : Status.values()) { 95 | if (status.getRequestStatus() == requestStatus) { 96 | return status; 97 | } 98 | } 99 | return null; 100 | } 101 | 102 | @Override 103 | public String getDescription() { 104 | return "" + this.requestStatus + " " + this.description; 105 | } 106 | 107 | @Override 108 | public int getRequestStatus() { 109 | return this.requestStatus; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/db/SpDataProviderImpl.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada.db 2 | 3 | import android.content.Context 4 | import android.content.SharedPreferences 5 | import androidx.sqlite.db.SupportSQLiteDatabase 6 | import com.tlz.ada.Ada 7 | import com.tlz.ada.AdaConstUtils 8 | import com.tlz.ada.exceptions.AdaException 9 | import com.tlz.ada.models.KeyValue 10 | import com.tlz.ada.models.Table 11 | import com.tlz.ada.models.TableInfo 12 | import java.io.File 13 | 14 | /** 15 | * SharePreferences数据处理. 16 | * Created by Tomlezen. 17 | * Date: 2019-06-09. 18 | * Time: 22:03. 19 | */ 20 | internal class SpDataProviderImpl(private val ctx: Context) : AdaDataProvider { 21 | 22 | private var spTables = mutableListOf() 23 | 24 | override fun setCustomDatabaseFiles(files: Map>) {} 25 | 26 | override fun setInMemoryRoomDatabases(databases: Map) {} 27 | 28 | override fun getAllDatabase(): List = listOf(AdaConstUtils.PREFS) 29 | 30 | override fun getDatabaseFile(dName: String): File? = null 31 | 32 | override fun getAllTable(dName: String): Table { 33 | val tables = mutableListOf() 34 | val spDir = File(ctx.applicationInfo.dataDir + "/shared_prefs") 35 | if (spDir.exists()) { 36 | spDir.listFiles() 37 | .map { it.name } 38 | .filter { it.endsWith(".xml") } 39 | .map { it.substring(0, it.length - 4) } 40 | .mapTo(tables) { TableInfo(it, listOf()) } 41 | } 42 | spTables = tables 43 | return Table(0, tables) 44 | } 45 | 46 | override fun getTableInfo(dName: String, tName: String): TableInfo = 47 | spTables.find { it.name == tName } ?: throw AdaException("不存在该表信息: dName=$dName;tName=$tName") 48 | 49 | override fun getTableDataCount(dName: String, tName: String, where: String): Int = -1 50 | 51 | override fun query(dName: String, tName: String, sql: String): List { 52 | val data = mutableListOf() 53 | val sharePreferences = ctx.getSharedPreferences(tName, Context.MODE_PRIVATE) 54 | for (entry in sharePreferences.all.entries) { 55 | data.add( 56 | KeyValue( 57 | entry.key, 58 | entry.value?.toString() ?: "null", 59 | when { 60 | entry.value is String -> AdaConstUtils.TYPE_TEXT 61 | entry.value is Int -> AdaConstUtils.TYPE_INTEGER 62 | entry.value is Long -> AdaConstUtils.TYPE_LONG 63 | entry.value is Float -> AdaConstUtils.TYPE_FLOAT 64 | entry.value is Boolean -> AdaConstUtils.TYPE_BOOLEAN 65 | entry.value is Set<*> -> AdaConstUtils.TYPE_STRING_SET 66 | else -> AdaConstUtils.TYPE_TEXT 67 | }) 68 | ) 69 | } 70 | if (sql.isNotBlank()) { 71 | if (sql == "asc") { 72 | data.sortWith(Comparator { o1, o2 -> if ((o1 as KeyValue).key > (o2 as KeyValue).key) 1 else -1 }) 73 | } else { 74 | data.sortWith(Comparator { o1, o2 -> if ((o1 as KeyValue).key > (o2 as KeyValue).key) -1 else 1 }) 75 | } 76 | } 77 | return data 78 | } 79 | 80 | override fun rawQuery(dName: String, sql: String): Any = false 81 | 82 | override fun add(dName: String, tName: String, content: Array): Boolean = 83 | update(dName, tName, content, "") 84 | 85 | override fun delete(dName: String, tName: String, where: String): Boolean { 86 | ctx.getSharedPreferences(tName, Context.MODE_PRIVATE).edit().remove(where).apply() 87 | return true 88 | } 89 | 90 | override fun update(dName: String, tName: String, content: Array, where: String): Boolean { 91 | tName.edit { 92 | val keyValue = content.first() 93 | when (keyValue.type) { 94 | AdaConstUtils.TYPE_INTEGER -> putInt(keyValue.key, keyValue.value?.toInt() ?: 0) 95 | AdaConstUtils.TYPE_FLOAT -> putFloat(keyValue.key, keyValue.value?.toFloat() ?: 0f) 96 | AdaConstUtils.TYPE_LONG -> putLong(keyValue.key, keyValue.value?.toLong() ?: 0L) 97 | AdaConstUtils.TYPE_BOOLEAN -> putBoolean(keyValue.key, keyValue.value?.toBoolean() ?: false) 98 | AdaConstUtils.TYPE_STRING_SET -> 99 | putStringSet( 100 | keyValue.key, 101 | Ada.adaGson.fromJson>(keyValue.value, Array::class.java).mapTo(mutableSetOf()) { it } 102 | ) 103 | else -> putString(keyValue.key, keyValue.value) 104 | } 105 | } 106 | return true 107 | } 108 | 109 | fun String.edit(block: SharedPreferences.Editor.() -> Unit) { 110 | ctx.getSharedPreferences(this, Context.MODE_PRIVATE).edit().apply(block).apply() 111 | } 112 | } -------------------------------------------------------------------------------- /lib/src/main/java/org/nanohttpd2/protocols/http/content/ContentType.java: -------------------------------------------------------------------------------- 1 | package org.nanohttpd2.protocols.http.content; 2 | 3 | /* 4 | * #%L 5 | * NanoHttpd-Core 6 | * %% 7 | * Copyright (C) 2012 - 2016 nanohttpd 8 | * %% 9 | * Redistribution and use in source and binary forms, with or without modification, 10 | * are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, this 13 | * list of conditions and the following disclaimer. 14 | * 15 | * 2. Redistributions in binary form must reproduce the above copyright notice, 16 | * this list of conditions and the following disclaimer in the documentation 17 | * and/or other materials provided with the distribution. 18 | * 19 | * 3. Neither the name of the nanohttpd nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 27 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 31 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | * #L% 34 | */ 35 | 36 | import java.util.regex.Matcher; 37 | import java.util.regex.Pattern; 38 | 39 | public class ContentType { 40 | 41 | private static final String ASCII_ENCODING = "US-ASCII"; 42 | 43 | private static final String MULTIPART_FORM_DATA_HEADER = "multipart/form-data"; 44 | 45 | private static final String CONTENT_REGEX = "[ |\t]*([^/^ ^;^,]+/[^ ^;^,]+)"; 46 | 47 | private static final Pattern MIME_PATTERN = Pattern.compile(CONTENT_REGEX, Pattern.CASE_INSENSITIVE); 48 | 49 | private static final String CHARSET_REGEX = "[ |\t]*(charset)[ |\t]*=[ |\t]*['|\"]?([^\"^'^;^,]*)['|\"]?"; 50 | 51 | private static final Pattern CHARSET_PATTERN = Pattern.compile(CHARSET_REGEX, Pattern.CASE_INSENSITIVE); 52 | 53 | private static final String BOUNDARY_REGEX = "[ |\t]*(boundary)[ |\t]*=[ |\t]*['|\"]?([^\"^'^;^,]*)['|\"]?"; 54 | 55 | private static final Pattern BOUNDARY_PATTERN = Pattern.compile(BOUNDARY_REGEX, Pattern.CASE_INSENSITIVE); 56 | 57 | private final String contentTypeHeader; 58 | 59 | private final String contentType; 60 | 61 | private final String encoding; 62 | 63 | private final String boundary; 64 | 65 | public ContentType(String contentTypeHeader) { 66 | this.contentTypeHeader = contentTypeHeader; 67 | if (contentTypeHeader != null) { 68 | contentType = getDetailFromContentHeader(contentTypeHeader, MIME_PATTERN, "", 1); 69 | encoding = getDetailFromContentHeader(contentTypeHeader, CHARSET_PATTERN, null, 2); 70 | } else { 71 | contentType = ""; 72 | encoding = "UTF-8"; 73 | } 74 | if (MULTIPART_FORM_DATA_HEADER.equalsIgnoreCase(contentType)) { 75 | boundary = getDetailFromContentHeader(contentTypeHeader, BOUNDARY_PATTERN, null, 2); 76 | } else { 77 | boundary = null; 78 | } 79 | } 80 | 81 | private String getDetailFromContentHeader(String contentTypeHeader, Pattern pattern, String defaultValue, int group) { 82 | Matcher matcher = pattern.matcher(contentTypeHeader); 83 | return matcher.find() ? matcher.group(group) : defaultValue; 84 | } 85 | 86 | public String getContentTypeHeader() { 87 | return contentTypeHeader; 88 | } 89 | 90 | public String getContentType() { 91 | return contentType; 92 | } 93 | 94 | public String getEncoding() { 95 | return encoding == null ? ASCII_ENCODING : encoding; 96 | } 97 | 98 | public String getBoundary() { 99 | return boundary; 100 | } 101 | 102 | public boolean isMultipart() { 103 | return MULTIPART_FORM_DATA_HEADER.equalsIgnoreCase(contentType); 104 | } 105 | 106 | public ContentType tryUTF8() { 107 | if (encoding == null) { 108 | return new ContentType(this.contentTypeHeader + "; charset=UTF-8"); 109 | } 110 | return this; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /lib/src/main/java/org/nanohttpd2/protocols/http/content/CookieHandler.java: -------------------------------------------------------------------------------- 1 | package org.nanohttpd2.protocols.http.content; 2 | 3 | /* 4 | * #%L 5 | * NanoHttpd-Core 6 | * %% 7 | * Copyright (C) 2012 - 2016 nanohttpd 8 | * %% 9 | * Redistribution and use in source and binary forms, with or without modification, 10 | * are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, this 13 | * list of conditions and the following disclaimer. 14 | * 15 | * 2. Redistributions in binary form must reproduce the above copyright notice, 16 | * this list of conditions and the following disclaimer in the documentation 17 | * and/or other materials provided with the distribution. 18 | * 19 | * 3. Neither the name of the nanohttpd nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 27 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 31 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | * #L% 34 | */ 35 | 36 | import java.util.ArrayList; 37 | import java.util.HashMap; 38 | import java.util.Iterator; 39 | import java.util.Map; 40 | 41 | import org.nanohttpd2.protocols.http.response.Response; 42 | 43 | /** 44 | * Provides rudimentary support for cookies. Doesn't support 'path', 'secure' 45 | * nor 'httpOnly'. Feel free to improve it and/or add unsupported features. This 46 | * is old code and it's flawed in many ways. 47 | * 48 | * @author LordFokas 49 | */ 50 | public class CookieHandler implements Iterable { 51 | 52 | private final HashMap cookies = new HashMap(); 53 | 54 | private final ArrayList queue = new ArrayList(); 55 | 56 | public CookieHandler(Map httpHeaders) { 57 | String raw = httpHeaders.get("cookie"); 58 | if (raw != null) { 59 | String[] tokens = raw.split(";"); 60 | for (String token : tokens) { 61 | String[] data = token.trim().split("="); 62 | if (data.length == 2) { 63 | this.cookies.put(data[0], data[1]); 64 | } 65 | } 66 | } 67 | } 68 | 69 | /** 70 | * Set a cookie with an expiration date from a month ago, effectively 71 | * deleting it on the client side. 72 | * 73 | * @param name 74 | * The cookie name. 75 | */ 76 | public void delete(String name) { 77 | set(name, "-delete-", -30); 78 | } 79 | 80 | @Override 81 | public Iterator iterator() { 82 | return this.cookies.keySet().iterator(); 83 | } 84 | 85 | /** 86 | * Read a cookie from the HTTP Headers. 87 | * 88 | * @param name 89 | * The cookie's name. 90 | * @return The cookie's value if it exists, null otherwise. 91 | */ 92 | public String read(String name) { 93 | return this.cookies.get(name); 94 | } 95 | 96 | public void set(Cookie cookie) { 97 | this.queue.add(cookie); 98 | } 99 | 100 | /** 101 | * Sets a cookie. 102 | * 103 | * @param name 104 | * The cookie's name. 105 | * @param value 106 | * The cookie's value. 107 | * @param expires 108 | * How many days until the cookie expires. 109 | */ 110 | public void set(String name, String value, int expires) { 111 | this.queue.add(new Cookie(name, value, Cookie.getHTTPTime(expires))); 112 | } 113 | 114 | /** 115 | * Internally used by the webserver to add all queued cookies into the 116 | * AdaResponse's HTTP Headers. 117 | * 118 | * @param response 119 | * The AdaResponse object to which headers the queued cookies will 120 | * be added. 121 | */ 122 | public void unloadQueue(Response response) { 123 | for (Cookie cookie : this.queue) { 124 | response.addCookieHeader(cookie.getHTTPHeader()); 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/handlers/LogRequestHandler.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada.handlers 2 | 3 | import android.content.Context 4 | import com.tlz.ada.* 5 | import com.tlz.ada.models.FileInfo 6 | import com.tlz.ada.socket.AdaWSD 7 | import org.nanohttpd2.protocols.http.IHTTPSession 8 | import org.nanohttpd2.protocols.http.response.Response 9 | import org.nanohttpd2.protocols.http.response.Response.newChunkedResponse 10 | import org.nanohttpd2.protocols.http.response.Status 11 | import java.io.File 12 | import java.text.SimpleDateFormat 13 | import java.util.* 14 | 15 | /** 16 | * 日志处理. 17 | * 18 | * Created by Tomlezen. 19 | * Date: 2018/9/6. 20 | * Time: 下午9:47. 21 | */ 22 | class LogRequestHandler( 23 | private val ctx: Context, 24 | private val wsd: AdaWSD 25 | ) : RequestHandler { 26 | 27 | /** 日志文件,根据具体时间来生成. */ 28 | private val logFileName by lazy { 29 | val format = SimpleDateFormat("yyyy-MM-dd hh:mm:ss", Locale.getDefault()) 30 | format.format(Date()) 31 | } 32 | 33 | /** 日志缓存文件夹. */ 34 | private val logCacheFolder by lazy { 35 | ctx.externalCacheDir?.absolutePath + "/log/" 36 | } 37 | 38 | /** 每次初始化都新建一个日志文件. */ 39 | private val logFile by lazy { 40 | File(logCacheFolder, "$logFileName.txt").apply { 41 | if (!exists()) { 42 | parentFile.mkdirs() 43 | createNewFile() 44 | } 45 | } 46 | } 47 | 48 | init { 49 | // 自动开始日志记录功能 50 | Ada.submitTask { 51 | cmd("logcat -v time") { 52 | // 写入到文件中 53 | logFile.appendText("\n" + it) 54 | wsd.send(it) 55 | } 56 | } 57 | } 58 | 59 | override fun onRequest(session: IHTTPSession): Response? = 60 | when (session.uri) { 61 | "/api/log/list" -> handleLogListRequest() 62 | "/api/log/delete" -> session.verifyParams(::handleLogDeleteRequest, AdaConstUtils.FILES) 63 | "/api/log/download" -> session.verifyParams(::handleLogDownloadRequest, AdaConstUtils.FILE_NAME) 64 | else -> wsd.onRequest(session) 65 | } 66 | 67 | /** 68 | * 处理日志列表请求. 69 | * @return AdaResponse 70 | */ 71 | private fun handleLogListRequest(): Response = 72 | handleRequestSafely { 73 | val files = mutableListOf() 74 | val logCache = File(logCacheFolder) 75 | if (logCache.exists()) { 76 | logCache.listFiles() 77 | .sortedByDescending { it.lastModified() } 78 | .filter { !it.isDirectory } 79 | .mapTo(files) { 80 | FileInfo( 81 | it.name, 82 | it.isDirectory, 83 | it.absolutePath, 84 | it.length(), 85 | it.canRead(), 86 | it.canWrite(), 87 | it.isHidden, 88 | it.lastModified() 89 | ) 90 | } 91 | } 92 | responseData(files.toResponse()) 93 | } 94 | 95 | /** 96 | * 处理日志删除请求. 97 | * @param session: IHTTPSession 98 | */ 99 | private fun handleLogDeleteRequest(session: IHTTPSession): Response = 100 | handleRequestSafely { 101 | val files = Ada.adaGson.fromJson>(session.parms[AdaConstUtils.FILES] 102 | ?: "{}", Array::class.java) 103 | File(logCacheFolder).listFiles() 104 | .filter { it.name in files } 105 | .forEach { 106 | it.delete() 107 | } 108 | handleLogListRequest() 109 | } 110 | 111 | /** 112 | * 处理日志文件下载请求. 113 | * @param session IHTTPSession 114 | * @return AdaResponse 115 | */ 116 | private fun handleLogDownloadRequest(session: IHTTPSession): Response = 117 | handleRequestSafely { 118 | val fileName = session.parms[AdaConstUtils.FILE_NAME] ?: "" 119 | // val file = File(logCacheFolder, fileName) 120 | val file = File(fileName.replace("*", "/")) 121 | if (!file.exists()) { 122 | responseError(errorMsg = "文件不存在") 123 | } else if (!file.canRead()) { 124 | responseError(errorMsg = "文件不可读取") 125 | } else { 126 | newChunkedResponse(Status.OK, "*/*", file.inputStream()).apply { 127 | addHeader("Content-Disposition", "attachment; filename=${file.name}") 128 | } 129 | } 130 | } 131 | } -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/handlers/FileRequestHandler.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada.handlers 2 | 3 | import android.os.Environment 4 | import com.tlz.ada.* 5 | import com.tlz.ada.AdaConstUtils.FILE_NAME 6 | import com.tlz.ada.AdaConstUtils.FOLDER_NAME 7 | import com.tlz.ada.AdaConstUtils.PATH 8 | import org.nanohttpd2.protocols.http.IHTTPSession 9 | import org.nanohttpd2.protocols.http.response.Response 10 | import org.nanohttpd2.protocols.http.response.Response.newChunkedResponse 11 | import org.nanohttpd2.protocols.http.response.Status 12 | import java.io.File 13 | 14 | /** 15 | * 文件请求处理. 16 | * 17 | * Created by Tomlezen.≥ 18 | * Data: 2018/9/5. 19 | * Time: 17:54. 20 | */ 21 | class FileRequestHandler(private val webServer: AdaWebServer) : RequestHandler { 22 | 23 | override fun onRequest(session: IHTTPSession): Response? = 24 | when (session.uri) { 25 | "/api/file/list" -> checkPermission { session.verifyParams(::handleFileListRequest, PATH) } 26 | "/api/file/new_file" -> checkPermission { session.verifyParams(::handleNewFileRequest, PATH, FILE_NAME) } 27 | "/api/file/new_folder" -> checkPermission { session.verifyParams(::handleNewFolderRequest, PATH, FOLDER_NAME) } 28 | "/api/file/upload" -> checkPermission { session.verifyParams(::handleUploadRequest, PATH) } 29 | "/api/file/download" -> checkPermission { session.verifyParams(::handleDownloadRequest, PATH) } 30 | "/api/file/delete" -> checkPermission { session.verifyParams(::handleFileDeleteRequest, PATH) } 31 | else -> null 32 | } 33 | 34 | /** 35 | * 检查权限. 36 | * @param doOnPermissionGranted () -> AdaResponse 37 | * @return AdaResponse 38 | */ 39 | private fun checkPermission(doOnPermissionGranted: () -> Response): Response = 40 | if (webServer.filePermissionGranted) { 41 | doOnPermissionGranted.invoke() 42 | } else { 43 | responseError(errorMsg = "没有文件读写权限") 44 | } 45 | 46 | /** 47 | * 处理文件列表请求. 48 | * @param session IHTTPSession 49 | */ 50 | private fun handleFileListRequest(session: IHTTPSession): Response = 51 | handleRequestSafely { 52 | responseData(session.filePath().listFiles().toResponse()) 53 | } 54 | 55 | /** 56 | * 处理新建文件请求. 57 | * @param session IHTTPSession 58 | * @return AdaResponse 59 | */ 60 | private fun handleNewFileRequest(session: IHTTPSession): Response = 61 | handleRequestSafely { 62 | val path = session.parms[PATH] ?: "" 63 | val fileName = session.parms[FILE_NAME] ?: "" 64 | if (fileName.isEmpty()) { 65 | responseError(errorMsg = "非法的文件名") 66 | } else { 67 | try { 68 | val realPath = if (path == "root") Environment.getExternalStorageDirectory().absolutePath else path 69 | val newFile = File(realPath, fileName) 70 | if (newFile.createNewFile()) { 71 | responseData(realPath.listFiles().toResponse()) 72 | } else { 73 | 74 | responseError(errorMsg = "文件创建失败") 75 | } 76 | } catch (e: Exception) { 77 | responseError(errorMsg = "文件创建失败:${e.message}") 78 | } 79 | } 80 | } 81 | 82 | /** 83 | * 处理新建文件夹请求. 84 | * @param session IHTTPSession 85 | * @return AdaResponse 86 | */ 87 | private fun handleNewFolderRequest(session: IHTTPSession): Response = 88 | handleRequestSafely { 89 | val folderName = session.parms[FOLDER_NAME] ?: "" 90 | if (folderName.isEmpty()) { 91 | responseError(errorMsg = "非法的文件夹名") 92 | } else { 93 | try { 94 | val path = session.filePath() 95 | val newFile = File(path, folderName) 96 | if (newFile.mkdir()) { 97 | responseData(path.listFiles().toResponse()) 98 | } else { 99 | responseError(errorMsg = "文件夹创建失败") 100 | } 101 | } catch (e: Exception) { 102 | responseError(errorMsg = "文件夹创建失败:${e.message}") 103 | } 104 | } 105 | } 106 | 107 | /** 108 | * 处理文件上传请求. 109 | * @param session IHTTPSession 110 | * @return AdaResponse 111 | */ 112 | private fun handleUploadRequest(session: IHTTPSession): Response = 113 | handleRequestSafely { 114 | val path = session.filePath() 115 | val parseBody = hashMapOf() 116 | session.parseBody(parseBody) 117 | if (parseBody.isNotEmpty()) { 118 | val filePaths = mutableListOf() 119 | parseBody.forEach { 120 | val cacheFile = File(it.value) 121 | val filePath = "$path/${session.parms[it.key]}" 122 | cacheFile.renameTo(File(filePath)) 123 | filePaths.add(filePath) 124 | } 125 | responseData(path.listFiles().toResponse()) 126 | } else { 127 | responseError(errorMsg = "空文件") 128 | } 129 | } 130 | 131 | /** 132 | * 处理文件下载请求. 133 | * @param session IHTTPSession 134 | * @return AdaResponse 135 | */ 136 | private fun handleDownloadRequest(session: IHTTPSession): Response = 137 | handleRequestSafely { 138 | val path = session.parms[PATH] 139 | val file = File(path) 140 | if (!file.exists()) { 141 | responseError(errorMsg = "文件不存在") 142 | } else if (!file.canRead()) { 143 | responseError(errorMsg = "文件不可读取") 144 | } else { 145 | newChunkedResponse(Status.OK, "*/*", file.inputStream()).apply { 146 | addHeader("Content-Disposition", "attachment; filename=${file.name}") 147 | } 148 | } 149 | } 150 | 151 | /** 152 | * 处理文件删除请求. 153 | * @param session IHTTPSession 154 | * @return AdaResponse 155 | */ 156 | private fun handleFileDeleteRequest(session: IHTTPSession): Response = 157 | handleRequestSafely { 158 | val path = session.filePath() 159 | val file = File(path) 160 | if (file.exists()) { 161 | val parent = file.parentFile.absolutePath 162 | if (file.deleteRecursively()) { 163 | responseData(parent.listFiles().toResponse()) 164 | } else { 165 | responseError(errorMsg = "删除失败") 166 | } 167 | } else { 168 | responseError(errorMsg = "文件不存在") 169 | } 170 | } 171 | 172 | private fun IHTTPSession.filePath() = 173 | with(parms[PATH] ?: "") { 174 | if (this == "root") { 175 | Environment.getExternalStorageDirectory().absolutePath 176 | } else { 177 | this 178 | } 179 | } 180 | } -------------------------------------------------------------------------------- /lib/src/main/assets/web/3rdpartylicenses.txt: -------------------------------------------------------------------------------- 1 | core-js@2.5.7 2 | MIT 3 | Copyright (c) 2014-2018 Denis Pushkarev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | zone.js@0.8.26 24 | MIT 25 | The MIT License 26 | 27 | Copyright (c) 2016-2018 Google, Inc. 28 | 29 | Permission is hereby granted, free of charge, to any person obtaining a copy 30 | of this software and associated documentation files (the "Software"), to deal 31 | in the Software without restriction, including without limitation the rights 32 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 33 | copies of the Software, and to permit persons to whom the Software is 34 | furnished to do so, subject to the following conditions: 35 | 36 | The above copyright notice and this permission notice shall be included in 37 | all copies or substantial portions of the Software. 38 | 39 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 40 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 41 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 42 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 43 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 44 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 45 | THE SOFTWARE. 46 | 47 | date-fns@1.29.0 48 | MIT 49 | # License 50 | 51 | date-fns is licensed under the [MIT license](http://kossnocorp.mit-license.org). 52 | Read more about MIT at [TLDRLegal](https://tldrlegal.com/license/mit-license). 53 | 54 | extend@3.0.2 55 | MIT 56 | The MIT License (MIT) 57 | 58 | Copyright (c) 2014 Stefan Thomas 59 | 60 | Permission is hereby granted, free of charge, to any person obtaining 61 | a copy of this software and associated documentation files (the 62 | "Software"), to deal in the Software without restriction, including 63 | without limitation the rights to use, copy, modify, merge, publish, 64 | distribute, sublicense, and/or sell copies of the Software, and to 65 | permit persons to whom the Software is furnished to do so, subject to 66 | the following conditions: 67 | 68 | The above copyright notice and this permission notice shall be 69 | included in all copies or substantial portions of the Software. 70 | 71 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 72 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 73 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 74 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 75 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 76 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 77 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 78 | 79 | webpack@4.8.3 80 | MIT 81 | Copyright JS Foundation and other contributors 82 | 83 | Permission is hereby granted, free of charge, to any person obtaining 84 | a copy of this software and associated documentation files (the 85 | 'Software'), to deal in the Software without restriction, including 86 | without limitation the rights to use, copy, modify, merge, publish, 87 | distribute, sublicense, and/or sell copies of the Software, and to 88 | permit persons to whom the Software is furnished to do so, subject to 89 | the following conditions: 90 | 91 | The above copyright notice and this permission notice shall be 92 | included in all copies or substantial portions of the Software. 93 | 94 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 95 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 96 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 97 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 98 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 99 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 100 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 101 | 102 | file-saver@1.3.8 103 | MIT 104 | The MIT License 105 | 106 | Copyright © 2016 [Eli Grey][1]. 107 | 108 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 109 | 110 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 111 | 112 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 113 | 114 | [1]: http://eligrey.com 115 | 116 | @angular/common@6.1.7 117 | MIT 118 | MIT -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/Adas.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.content.pm.PackageManager 7 | import android.net.Uri 8 | import android.os.Build 9 | import androidx.core.content.ContextCompat 10 | import androidx.core.content.FileProvider 11 | import com.tlz.ada.models.AdaResponse 12 | import com.tlz.ada.models.FileInfo 13 | import org.nanohttpd2.protocols.http.IHTTPSession 14 | import org.nanohttpd2.protocols.http.NanoHTTPD.MIME_HTML 15 | import org.nanohttpd2.protocols.http.NanoHTTPD.MIME_PLAINTEXT 16 | import org.nanohttpd2.protocols.http.response.Response 17 | import org.nanohttpd2.protocols.http.response.Response.newFixedLengthResponse 18 | import org.nanohttpd2.protocols.http.response.Status 19 | import java.io.BufferedReader 20 | import java.io.File 21 | import java.io.InputStreamReader 22 | 23 | fun rMin(v1: Int, v2: Int): Int { 24 | return if (v2 == -1 || v2 > v1) v1 else v2 25 | } 26 | 27 | 28 | /** 29 | * 获取metadata数据. 30 | * @receiver Context 31 | * @param key String 32 | * @return String 33 | */ 34 | internal fun Context.metaData(key: String): String = 35 | packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA)?.metaData?.getString(key) 36 | ?: "" 37 | 38 | //internal fun Context.metaDataInt(key: String, default: Int = 0): Int = 39 | // packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA)?.metaData?.getInt(key, default) 40 | // ?: default 41 | 42 | /** 43 | * 读取html文件. 44 | * @receiver String 45 | * @param ctx Context 46 | * @return String 47 | */ 48 | internal fun String.readHtml(ctx: Context): String = ctx.assets.open("web$this").bufferedReader().readText() 49 | 50 | /** 51 | * 执行cmd命令. 52 | * @param cmd String 53 | * @return List 54 | */ 55 | internal fun cmd(cmd: String): List { 56 | val p = Runtime.getRuntime().exec(cmd) 57 | p.waitFor() 58 | val read = BufferedReader(InputStreamReader(p.inputStream)) 59 | val lines = mutableListOf() 60 | var line: String? = read.readLine() 61 | while (line != null) { 62 | lines += line 63 | line = read.readLine() 64 | } 65 | read.close() 66 | return lines 67 | } 68 | 69 | /** 70 | * 执行cmd命令. 71 | * @param cmd String 72 | * @param onResult (List) -> Unit 73 | * @return Process 74 | */ 75 | internal fun cmd(cmd: String, onResult: (String) -> Unit): Process { 76 | val p = Runtime.getRuntime().exec(cmd) 77 | val read = BufferedReader(InputStreamReader(p.inputStream)) 78 | var line: String? = read.readLine() 79 | while (line != null) { 80 | onResult.invoke(line) 81 | line = read.readLine() 82 | } 83 | read.close() 84 | return p 85 | } 86 | 87 | /** 88 | * 安装应用. 89 | * @receiver Context 90 | * @param apkFile File 91 | */ 92 | internal fun Context.installApk(apkFile: File) { 93 | val install = Intent(Intent.ACTION_VIEW) 94 | install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 95 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 96 | val apkUri = FileProvider.getUriForFile(this, "$packageName.AndDevelopAssistantFileProvider", apkFile) 97 | install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) 98 | install.setDataAndType(apkUri, "application/vnd.android.package-archive"); 99 | } else { 100 | install.setDataAndType(Uri.parse("file://" + apkFile.absolutePath), "application/vnd.android.package-archive"); 101 | } 102 | startActivity(install) 103 | } 104 | 105 | /** 106 | * 获取目录下的所有文件. 107 | * @receiver String 108 | * @return List 109 | */ 110 | fun String.listFiles(): List { 111 | val dirContent = mutableListOf() 112 | val dirFile = File(this) 113 | if (dirFile.exists() && dirFile.canRead()) { 114 | dirFile.listFiles()?.mapTo(dirContent) { 115 | FileInfo( 116 | it.name, 117 | it.isDirectory, 118 | it.absolutePath, 119 | it.length(), 120 | it.canRead(), 121 | it.canWrite(), 122 | it.isHidden, 123 | it.lastModified() 124 | ) 125 | } 126 | } 127 | return dirContent 128 | } 129 | 130 | fun response(type: String, html: String, cacheTime: String): Response = 131 | newFixedLengthResponse(Status.OK, type, html).apply { 132 | addHeader("Cache-Control", "public") 133 | addHeader("Cache-Control", "max-age=$cacheTime") 134 | } 135 | 136 | fun response(type: String, html: String): Response = 137 | newFixedLengthResponse(Status.OK, type, html) 138 | 139 | fun responseData(data: Any): Response = 140 | newFixedLengthResponse(Status.OK, MIME_PLAINTEXT, Ada.adaGson.toJson(data)) 141 | 142 | fun responseHtml(html: String): Response = 143 | newFixedLengthResponse(Status.OK, MIME_HTML, html) 144 | 145 | fun responseError(status: Int = 401, errorMsg: String): Response = 146 | responseData(com.tlz.ada.models.AdaResponse(status, null, errorMsg)) 147 | 148 | internal fun Any.toResponse(): AdaResponse = AdaResponse(data = this) 149 | 150 | /** 151 | * 安全处理请求. 152 | * @param errorMsg String? 153 | * @param action () -> AdaResponse 154 | * @return AdaResponse 155 | */ 156 | @SuppressLint("LongLogTag") 157 | internal fun handleRequestSafely(errorMsg: String? = null, action: () -> Response): Response = 158 | try { 159 | action.invoke() 160 | } catch (t: Throwable) { 161 | responseError(errorMsg = errorMsg ?: "${t.message}") 162 | } 163 | 164 | /** 165 | * 校验参数. 166 | * @receiver Map 167 | * @param params Array 168 | */ 169 | fun IHTTPSession.verifyParams(doOnPass: (IHTTPSession) -> Response, vararg params: String): Response { 170 | return if (params.any { !this.parms.containsKey(it) }) { 171 | newFixedLengthResponse(Status.BAD_REQUEST, MIME_PLAINTEXT, "请求参数错误") 172 | } else { 173 | doOnPass(this) 174 | } 175 | } 176 | 177 | /** 178 | * 检查权限是否通过. 179 | * @receiver Context 180 | * @param permissions Array 181 | * @return Boolean 182 | */ 183 | fun Context.isPermissionsGranted(vararg permissions: String): Boolean = 184 | permissions.all { ContextCompat.checkSelfPermission(this, it) == PackageManager.PERMISSION_GRANTED } 185 | 186 | /** 187 | * 服务器端口. 188 | * @receiver Context 189 | * @return Int 190 | */ 191 | internal fun Context.adaServerPort() = 192 | runCatching { 193 | getString(R.string.ADA_DEBUG_PORT).toIntOrNull() ?: 10000 194 | }.getOrNull() ?: 10000 -------------------------------------------------------------------------------- /lib/src/main/kotlin/com/tlz/ada/handlers/AppRequestHandler.kt: -------------------------------------------------------------------------------- 1 | package com.tlz.ada.handlers 2 | 3 | import android.content.Context 4 | import android.graphics.Bitmap 5 | import android.graphics.drawable.BitmapDrawable 6 | import com.tlz.ada.* 7 | import com.tlz.ada.AdaConstUtils.FILTER 8 | import com.tlz.ada.AdaConstUtils.PAGE_INDEX 9 | import com.tlz.ada.AdaConstUtils.PAGE_SIZE 10 | import com.tlz.ada.AdaConstUtils.PKG 11 | import com.tlz.ada.AdaConstUtils.SEARCH 12 | import com.tlz.ada.models.AdaResponse 13 | import org.nanohttpd2.protocols.http.IHTTPSession 14 | import org.nanohttpd2.protocols.http.response.Response 15 | import org.nanohttpd2.protocols.http.response.Response.newChunkedResponse 16 | import org.nanohttpd2.protocols.http.response.Status 17 | import java.io.ByteArrayInputStream 18 | import java.io.ByteArrayOutputStream 19 | import java.io.File 20 | import java.util.* 21 | import kotlin.math.min 22 | 23 | /** 24 | * 应用请求处理. 25 | * 26 | * Created by Tomlezen. 27 | * Data: 2018/9/5. 28 | * Time: 16:04. 29 | */ 30 | class AppRequestHandler( 31 | private val ctx: Context, 32 | private val appManager: AdaApplicationManager 33 | ) : RequestHandler { 34 | override fun onRequest(session: IHTTPSession): Response? = 35 | when (session.uri) { 36 | "/api/app/list" -> session.verifyParams(::handleAppListRequest, PAGE_SIZE, PAGE_SIZE, FILTER) 37 | "/api/app/info" -> session.verifyParams(::handleAppInfoRequest, PKG) 38 | "/api/app/install" -> session.verifyParams(::handleAppInstallRequest) 39 | "/api/app/download" -> session.verifyParams(::handleAppDownloadRequest, PKG) 40 | "/api/app/icon" -> session.verifyParams(::handleAppIconRequest, PKG) 41 | else -> null 42 | } 43 | 44 | /** 45 | * 处理应用列表请求. 46 | * @param session IHTTPSession 47 | * @return AdaResponse 48 | */ 49 | private fun handleAppListRequest(session: IHTTPSession): Response = 50 | handleRequestSafely { 51 | val pageIndex = session.parms[PAGE_INDEX]?.toInt() ?: 1 52 | val pageSize = session.parms[PAGE_SIZE]?.toInt() ?: 10 53 | val search = session.parms[SEARCH] ?: "" 54 | val filter = session.parms[FILTER] ?: "all" 55 | var sortDir: String? = null 56 | var sortField: String? = null 57 | session.parms.forEach { 58 | if (it.value == "asc" || it.value == "desc") { 59 | sortField = it.key 60 | sortDir = it.value 61 | return@forEach 62 | } 63 | } 64 | 65 | val filteredData = when (filter) { 66 | "system" -> appManager.applicationList.filter { it.isSystemApp } 67 | "non-system" -> appManager.applicationList.filter { !it.isSystemApp } 68 | else -> appManager.applicationList 69 | }.filter { search.isEmpty() || it.name.toUpperCase(Locale.getDefault()).contains(search.toUpperCase(Locale.getDefault())) } 70 | val data = if (sortDir != null && sortField != null) { 71 | if (sortDir == "asc") { 72 | filteredData.sortedBy { 73 | when (sortField) { 74 | "name" -> it.name 75 | "size" -> it.size.toString() 76 | "pkg" -> it.pkg 77 | else -> 1.toString() 78 | } 79 | } 80 | } else { 81 | filteredData.sortedByDescending { 82 | when (sortField) { 83 | "name" -> it.name 84 | "size" -> it.size.toString() 85 | "pkg" -> it.pkg 86 | else -> 1.toString() 87 | } 88 | } 89 | } 90 | } else filteredData 91 | val resultData = data.subList((pageIndex - 1) * pageSize, min(pageIndex * pageSize, data.size)) 92 | responseData(AdaResponse(data = resultData, total = data.size)) 93 | } 94 | 95 | /** 96 | * 处理应用信息请求. 97 | * @param session IHTTPSession 98 | * @return AdaResponse 99 | */ 100 | private fun handleAppInfoRequest(session: IHTTPSession): Response = 101 | handleRequestSafely { 102 | responseData(AdaResponse(data = appManager.getApplicationInfoByPkg(session.parms[PKG] 103 | ?: ""))) 104 | } 105 | 106 | /** 107 | * 处理应用安装请求. 108 | * @param session IHTTPSession 109 | * @return AdaResponse 110 | */ 111 | private fun handleAppInstallRequest(session: IHTTPSession): Response = 112 | handleRequestSafely { 113 | val apkFilePath = ctx.externalCacheDir?.absolutePath + "/install_apk.apk" 114 | val apkFile = File(apkFilePath) 115 | if (apkFile.exists()) { 116 | apkFile.delete() 117 | } 118 | val parseBody = hashMapOf() 119 | session.parseBody(parseBody) 120 | if (parseBody.keys.contains("file")) { 121 | File(parseBody["file"]).renameTo(apkFile) 122 | } 123 | ctx.installApk(apkFile) 124 | responseData("success".toResponse()) 125 | } 126 | 127 | /** 128 | * 处理应用下载请求. 129 | * @param session IHTTPSession 130 | * @return AdaResponse 131 | */ 132 | private fun handleAppDownloadRequest(session: IHTTPSession): Response = 133 | handleRequestSafely { 134 | val pkg = session.parms["pkg"] ?: "" 135 | val appInfo = appManager.getApplicationInfoByPkg(pkg) 136 | if (appInfo?.path.isNullOrEmpty()) { 137 | responseError(errorMsg = "不存在该应用") 138 | } else { 139 | newChunkedResponse(Status.OK, "application/octet-stream", File(appInfo!!.path).inputStream()).apply { 140 | addHeader("Content-Disposition", "attachment; filename=${appInfo.name}.apk") 141 | } 142 | } 143 | } 144 | 145 | /** 146 | * 处理应用logo请求. 147 | * @param session IHTTPSession 148 | * @return AdaResponse 149 | */ 150 | private fun handleAppIconRequest(session: IHTTPSession): Response = 151 | handleRequestSafely { 152 | val pkg = session.parms["pkg"] ?: "" 153 | val os = ByteArrayOutputStream() 154 | val bitmapDrawable = appManager.getApplicationInfoByPkg(pkg)?.applicationInfo?.loadIcon(ctx.packageManager) as? BitmapDrawable 155 | bitmapDrawable?.bitmap?.compress(Bitmap.CompressFormat.PNG, 100, os) 156 | val ins = ByteArrayInputStream(os.toByteArray()) 157 | newChunkedResponse(Status.OK, "image/png", ins) 158 | } 159 | } --------------------------------------------------------------------------------