├── .gitignore ├── .idea ├── encodings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── example │ │ └── androidservice │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── androidservice │ │ │ ├── handler │ │ │ ├── MainHandler.kt │ │ │ ├── MediaHandler.kt │ │ │ ├── MyHttpHandler.kt │ │ │ └── UploadHandler.kt │ │ │ ├── ui │ │ │ ├── App.kt │ │ │ ├── FileInfo.kt │ │ │ ├── MFile.kt │ │ │ ├── MainActivity.kt │ │ │ ├── MediaRepository.kt │ │ │ └── Server.kt │ │ │ └── uitls │ │ │ ├── AndroidClassUtils.kt │ │ │ └── Utils.kt │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.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 │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── example │ └── androidservice │ └── ExampleUnitTest.kt ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── httpserver ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── weechan │ │ └── httpserver │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── weechan │ │ │ └── httpserver │ │ │ └── httpserver │ │ │ ├── HttpRequest.kt │ │ │ ├── HttpResponse.kt │ │ │ ├── HttpServer.kt │ │ │ ├── HttpServerBuilder.kt │ │ │ ├── HttpState.kt │ │ │ ├── ResponseBuilder.kt │ │ │ ├── annotaions │ │ │ └── Http.kt │ │ │ ├── interfaces │ │ │ ├── HttpHandler.kt │ │ │ ├── Reslover.kt │ │ │ └── Response.kt │ │ │ ├── reslover │ │ │ ├── HttpMessageReslover.kt │ │ │ ├── URLResolver.kt │ │ │ ├── body │ │ │ │ ├── FormDataPart.kt │ │ │ │ ├── ResponseBodyStreams.kt │ │ │ │ └── ResponseSink.kt │ │ │ └── reslovebean │ │ │ │ ├── RequestBody.kt │ │ │ │ ├── RequestHeaders.kt │ │ │ │ ├── RequestLine.kt │ │ │ │ └── RequestMessage.kt │ │ │ └── uitls │ │ │ ├── AndroidClassUtils.kt │ │ │ └── Utils.kt │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── weechan │ └── httpserver │ └── ExampleUnitTest.java └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 26 | 27 | 28 | 29 | 30 | 31 | 33 | 34 | 35 | 36 | 37 | Android API 21 Platform 38 | 39 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EasyHttpServer 2 | simple service + simple demo 3 | 4 | [![image](https://jitpack.io/v/weeChanc/AndroidService.svg)](https://jitpack.io/#weeChanc/AndroidService) 5 | 6 | 一个利用Kotlin语言编写的简易的Android嵌入式Http服务器,可以处理GET,POST请求,支持form-data,x-www-urlencoded表单解析, 7 | 支持直接访问Android手机所有文件并且支持断点续传 8 | 9 | 加入依赖 10 | Step 1. Add the JitPack repository to your build file 11 | ``` 12 | allprojects { 13 | repositories { 14 | ... 15 | maven { url 'https://jitpack.io' } 16 | } 17 | } 18 | ``` 19 | Step 2. Add the dependency 20 | ``` 21 | dependencies { 22 | compile 'com.github.weeChanc:AndroidService:0.1' 23 | } 24 | ``` 25 | 使用方法 26 | 1.在应用程序根包名下创建一个包叫做handler 27 | 2.在该包下创建Handler 28 | ``` 29 | @Http("/") //指定请求访问的路径,"/"为本IP默认访问的Handler 30 | class MainHandler() : HttpHandler { 31 | 32 | override fun doGet(request: HttpRequest, response: HttpResponse) { 33 | 34 | //设置返回头 35 | response.addHeaders { 36 | "Access-Control-Allow-Origin" - "*" 37 | "Access-Control-Allow-Methods" - "POST,GET" 38 | } 39 | 40 | //设置返回体 41 | response.write{ 42 | "HELLO WORLD".byteInputStream().writeTo(this) 43 | } 44 | 45 | } 46 | 47 | override fun doPost(request: HttpRequest, response: HttpResponse) { 48 | } 49 | } 50 | ``` 51 | 3.在合适位置创建服务器 52 | ``` 53 | val service = HttpServerFactory 54 | .with(this) 55 | .getHttpServer(8080) 56 | service.start() 57 | ``` 58 | 4.打开浏览器访问手机IP(保证在同一局域网内) 59 | 60 | 其他例子可查看代码 61 | 62 | 63 | 64 | # 计划 10 月份 65 | *添加HTTP1.1长连接支持 66 | *添加蓝牙与wifi同传功能 67 | *重构 68 | 69 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | apply plugin: 'kotlin-android' 4 | 5 | apply plugin: 'kotlin-android-extensions' 6 | 7 | android { 8 | compileSdkVersion 27 9 | defaultConfig { 10 | applicationId "com.example.androidservice" 11 | minSdkVersion 21 12 | targetSdkVersion 27 13 | versionCode 1 14 | versionName "1.0" 15 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 16 | } 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | } 24 | 25 | dependencies { 26 | implementation fileTree(dir: 'libs', include: ['*.jar']) 27 | implementation"org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" 28 | implementation 'com.android.support:appcompat-v7:27.1.0' 29 | implementation 'com.android.support.constraint:constraint-layout:1.0.2' 30 | testImplementation 'junit:junit:4.12' 31 | androidTestImplementation 'com.android.support.test:runner:1.0.1' 32 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' 33 | implementation 'com.google.code.gson:gson:2.8.2' 34 | compile project(':httpserver') 35 | } 36 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/example/androidservice/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.androidservice 2 | 3 | import android.support.test.InstrumentationRegistry 4 | import android.support.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("com.example.androidservice", appContext.packageName) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/androidservice/handler/MainHandler.kt: -------------------------------------------------------------------------------- 1 | package com.example.androidservice.handler 2 | 3 | 4 | import com.example.androidservice.uitls.getHostIp 5 | import com.example.androidservice.uitls.writeTo 6 | import com.weechan.httpserver.httpserver.HttpRequest 7 | import com.weechan.httpserver.httpserver.HttpResponse 8 | import com.weechan.httpserver.httpserver.interfaces.HttpHandler 9 | import com.weechan.httpserver.httpserver.annotaions.Http 10 | 11 | /** 12 | * Created by 铖哥 on 2018/3/23. 13 | */ 14 | @Http("/") 15 | class MainHandler() : HttpHandler { 16 | 17 | val ip : String = getHostIp()+":8080" 18 | 19 | val html = """ 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 138 | 文件浏览器 139 | 140 | 141 | 142 |
143 | 144 | 文档头部 145 | 146 |
147 | 148 |
149 | 150 |
151 | 152 | 156 | 157 | 158 | 162 | 163 | 167 | 168 | 172 | 173 | 177 | 178 | 179 |
180 | 181 |
182 | 183 |
184 | 185 | storage/emulated/0/Pictures/friday 186 |
187 | 188 |
189 | 190 |
191 | 192 | 193 | 194 | 195 | 196 | 318 | 319 | 320 | 321 | 322 | """.trimIndent() 323 | 324 | override fun doGet(request: HttpRequest, response: HttpResponse) { 325 | response.addHeaders { 326 | "Access-Control-Allow-Origin"-"*" 327 | "Access-Control-Allow-Methods"-"POST,GET" 328 | } 329 | response.write{ 330 | html.byteInputStream().writeTo(this) 331 | } 332 | 333 | 334 | } 335 | 336 | override fun doPost(request: HttpRequest, response: HttpResponse) { 337 | // Log.e("MainHandler", request.getRequestBody().string) 338 | } 339 | 340 | 341 | 342 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/androidservice/handler/MediaHandler.kt: -------------------------------------------------------------------------------- 1 | package com.example.androidservice.handler 2 | 3 | 4 | import com.example.androidservice.ui.MediaRepository 5 | import com.example.androidservice.uitls.writeTo 6 | import com.google.gson.Gson 7 | import com.weechan.httpserver.httpserver.HttpRequest 8 | import com.weechan.httpserver.httpserver.HttpResponse 9 | import com.weechan.httpserver.httpserver.interfaces.HttpHandler 10 | import com.weechan.httpserver.httpserver.annotaions.Http 11 | import java.io.InputStream 12 | 13 | /** 14 | * Created by weechan on 18-3-24. 15 | */ 16 | 17 | @Http("/getMedia") 18 | class MediaHandler : HttpHandler { 19 | 20 | 21 | override fun doGet(request: HttpRequest, response: HttpResponse) { 22 | val type = request.getRequestArgument("type") 23 | var jsonIn : InputStream? = null 24 | 25 | when (type) { 26 | "music" -> jsonIn = Gson().toJson(MediaRepository.getMusic()).byteInputStream() 27 | "document" -> jsonIn = Gson().toJson(MediaRepository.getDocument()) .byteInputStream() 28 | "video" ->jsonIn = Gson().toJson(MediaRepository.getVideo()).byteInputStream() 29 | "photoDir" -> jsonIn = Gson().toJson(MediaRepository.getPhotosDirectory()).byteInputStream() 30 | "photo" -> jsonIn = Gson().toJson(MediaRepository.getPhotos(request.getRequestArgument("path"))).byteInputStream() 31 | } 32 | 33 | 34 | response.write { 35 | jsonIn?.writeTo(this) 36 | } 37 | 38 | response.addHeaders { 39 | "Access-Control-Allow-Origin"-"*" 40 | "Access-Control-Allow-Methods"-"POST,GET" 41 | } 42 | } 43 | 44 | 45 | override fun doPost(request: HttpRequest, response: HttpResponse) { 46 | 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/androidservice/handler/MyHttpHandler.kt: -------------------------------------------------------------------------------- 1 | package com.example.androidservice.handler 2 | 3 | import com.example.androidservice.ui.MFile 4 | import com.example.androidservice.ui.MFileResponse 5 | import com.google.gson.Gson 6 | import com.weechan.httpserver.httpserver.HttpRequest 7 | import com.weechan.httpserver.httpserver.HttpResponse 8 | import com.weechan.httpserver.httpserver.interfaces.HttpHandler 9 | import com.weechan.httpserver.httpserver.annotaions.Http 10 | import java.io.File 11 | import java.io.OutputStream 12 | 13 | /** 14 | * Created by 铖哥 on 2018/3/20. 15 | */ 16 | 17 | @Http(route = "/find") 18 | class MyHttpHandler : HttpHandler { 19 | 20 | override fun doPost(request: HttpRequest, response: HttpResponse) { 21 | 22 | } 23 | 24 | override fun doGet(request: HttpRequest, response: HttpResponse) { 25 | val file = File(request.getRequestArgument("path")) 26 | if (file.isDirectory) { 27 | response.write { 28 | writeFileMessage(file, this) 29 | } 30 | } 31 | response.addHeaders { 32 | "Access-Control-Allow-Origin"-"*" 33 | "Access-Control-Allow-Methods"-"POST,GET" 34 | } 35 | } 36 | 37 | private fun writeFileMessage(file: File, output: OutputStream) { 38 | val response: MFileResponse 39 | if (!file.exists()) { 40 | response = MFileResponse(-1, null) 41 | } else { 42 | val mFiles = mutableListOf() 43 | file.listFiles().forEach { 44 | mFiles.add(MFile(it.isFile, it.length(), it.path)) 45 | } 46 | response = MFileResponse(1, mFiles) 47 | } 48 | val resp = Gson().toJson(response) 49 | output.write(resp.toByteArray()) 50 | } 51 | 52 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/androidservice/handler/UploadHandler.kt: -------------------------------------------------------------------------------- 1 | package com.example.androidservice.handler 2 | 3 | import android.os.Environment 4 | import com.weechan.httpserver.httpserver.HttpRequest 5 | import com.weechan.httpserver.httpserver.HttpResponse 6 | import com.weechan.httpserver.httpserver.interfaces.HttpHandler 7 | import com.weechan.httpserver.httpserver.annotaions.Http 8 | import java.io.File 9 | 10 | /** 11 | * Created by 铖哥 on 2018/3/25. 12 | */ 13 | @Http("/uplaod") 14 | class UploadHandler : HttpHandler{ 15 | override fun doGet(request: HttpRequest, response: HttpResponse) { 16 | 17 | } 18 | 19 | override fun doPost(request: HttpRequest, response: HttpResponse) { 20 | 21 | val buf = ByteArray(1024) 22 | //根据key获取表单中key对应的部分 23 | val part = request.getRequestBody().getPart("photo") 24 | val input = part?.inputSink 25 | if (input != null){ 26 | var length = input.read(buf) 27 | val out = File(Environment.getExternalStorageDirectory().path+"/${part.fileName}").outputStream().buffered() 28 | while(length != -1){ 29 | out.write(buf,0,length) 30 | length = input.read(buf) 31 | } 32 | out.flush() 33 | input.close() 34 | } 35 | response.write { write("上传成功 ${Environment.getExternalStorageDirectory().path+"/${part?.fileName}"} ".toByteArray()) } 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/androidservice/ui/App.kt: -------------------------------------------------------------------------------- 1 | package com.example.androidservice.ui 2 | 3 | import android.app.Application 4 | import kotlin.properties.Delegates 5 | 6 | /** 7 | * Created by weechan on 18-3-24. 8 | */ 9 | class App : Application() { 10 | 11 | companion object { 12 | var ctx : Application by Delegates.notNull() 13 | } 14 | 15 | 16 | override fun onCreate() { 17 | super.onCreate() 18 | ctx = this 19 | } 20 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/androidservice/ui/FileInfo.kt: -------------------------------------------------------------------------------- 1 | package com.example.androidservice.ui 2 | 3 | import android.graphics.drawable.Drawable 4 | import android.os.Parcel 5 | import android.os.Parcelable 6 | import com.google.gson.annotations.SerializedName 7 | 8 | /** 9 | * Created by weechan on 18-3-24. 10 | */ 11 | 12 | 13 | /** 14 | * Created by steve on 17-11-27. 15 | */ 16 | data class FileInfo(val name: String, 17 | @SerializedName("path")val location: String, 18 | @SerializedName("size")val initSize: Long = 0, 19 | @Transient var id: Long = 0, 20 | @Transient var icon: Drawable? = null) : Parcelable { 21 | 22 | 23 | constructor(parcel: Parcel) : this( 24 | parcel.readString(), 25 | parcel.readString(), 26 | parcel.readLong(), 27 | parcel.readLong()) { 28 | } 29 | 30 | override fun writeToParcel(parcel: Parcel, flags: Int) { 31 | parcel.writeString(name) 32 | parcel.writeString(location) 33 | parcel.writeLong(initSize) 34 | parcel.writeLong(id) 35 | } 36 | 37 | override fun describeContents(): Int { 38 | return 0 39 | } 40 | 41 | companion object CREATOR : Parcelable.Creator { 42 | override fun createFromParcel(parcel: Parcel): FileInfo { 43 | return FileInfo(parcel) 44 | } 45 | 46 | override fun newArray(size: Int): Array { 47 | return arrayOfNulls(size) 48 | } 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/androidservice/ui/MFile.kt: -------------------------------------------------------------------------------- 1 | package com.example.androidservice.ui 2 | 3 | /** 4 | * Created by 铖哥 on 2018/3/17. 5 | */ 6 | 7 | data class MFileResponse(val code : Int, val mFile: List?) 8 | 9 | data class MFile(val isFile: Boolean, 10 | val size: Long, 11 | val name: String) -------------------------------------------------------------------------------- /app/src/main/java/com/example/androidservice/ui/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.androidservice.ui 2 | 3 | import android.support.v7.app.AppCompatActivity 4 | import android.os.Bundle 5 | import android.os.Environment 6 | import android.util.Log 7 | import com.example.androidservice.R 8 | import com.example.androidservice.uitls.getHostIp 9 | import com.weechan.httpserver.httpserver.HttpServerBuilder 10 | import kotlinx.android.synthetic.main.activity_main.* 11 | 12 | 13 | 14 | class MainActivity : AppCompatActivity() { 15 | 16 | override fun onCreate(savedInstanceState: Bundle?) { 17 | super.onCreate(savedInstanceState) 18 | setContentView(R.layout.activity_main) 19 | 20 | MediaRepository.init() 21 | val service = HttpServerBuilder 22 | .with(this) 23 | .port(8080) 24 | .getHttpServer() 25 | 26 | val port = service.start() 27 | textView.text = getHostIp()+":${port} tempFile${Environment.getExternalStorageDirectory().path}" 28 | 29 | 30 | 31 | } 32 | 33 | 34 | 35 | 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/androidservice/ui/MediaRepository.kt: -------------------------------------------------------------------------------- 1 | package com.example.androidservice.ui 2 | 3 | import android.content.ContentResolver 4 | import android.provider.MediaStore 5 | 6 | import java.io.File 7 | import java.util.stream.Stream 8 | import kotlin.concurrent.thread 9 | 10 | /** 11 | * Created by weechan on 18-3-24. 12 | */ 13 | 14 | /** 15 | * Created by steve on 17-11-22. 16 | */ 17 | 18 | class MediaRepository { 19 | 20 | companion object { 21 | 22 | private val mContentResolver: ContentResolver = App.ctx.contentResolver 23 | 24 | var musics: List? = null 25 | var docs: List? = null 26 | var videos: List? = null 27 | var pictures: List? = null 28 | var apps: List? = null 29 | 30 | fun init() { 31 | 32 | thread{ 33 | getMusic() 34 | getDocument() 35 | getApplications() 36 | getVideo() 37 | } 38 | 39 | } 40 | 41 | @Synchronized 42 | fun getMusic(): List { 43 | 44 | 45 | if (musics != null) return musics!! 46 | 47 | val projection = arrayOf(MediaStore.Audio.Media.TITLE, MediaStore.Audio.Media.DATA, MediaStore.Audio.Media.SIZE) 48 | val cursor = mContentResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, 49 | projection, null, null, MediaStore.Audio.Media.DATE_MODIFIED + " desc") 50 | 51 | 52 | val list = mutableListOf() 53 | 54 | if (cursor != null && cursor.moveToFirst() && cursor.count > 0) 55 | do { 56 | var initSize = (cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.SIZE))).toLong() 57 | val name = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.TITLE)) 58 | val location = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA)) 59 | if (initSize == 0L) initSize = File(location).length() 60 | val info = FileInfo(name, location, initSize) 61 | list.add(info) 62 | } while (cursor.moveToNext()) 63 | 64 | cursor.close() 65 | 66 | 67 | 68 | this.musics = list 69 | return list 70 | 71 | } 72 | 73 | @Synchronized 74 | fun getDocument(): List { 75 | 76 | if (docs != null) return docs!! 77 | 78 | val list = mutableListOf() 79 | val projection = arrayOf(MediaStore.Files.FileColumns.DATA, MediaStore.Files.FileColumns.MIME_TYPE, MediaStore.Files.FileColumns.SIZE, MediaStore.Files.FileColumns.TITLE) 80 | 81 | val selection = (MediaStore.Files.FileColumns.MIME_TYPE + "= ? " 82 | + " or " + MediaStore.Files.FileColumns.MIME_TYPE + " = ? " 83 | + " or " + MediaStore.Files.FileColumns.MIME_TYPE + " = ? " 84 | + " or " + MediaStore.Files.FileColumns.MIME_TYPE + " = ? ") 85 | 86 | val selectionArgs = arrayOf("application/msword", "application/pdf", "application/vnd.ms-powerpoint", "application/vnd.ms-excel") 87 | 88 | val cursor = mContentResolver.query(MediaStore.Files.getContentUri("external"), projection, selection, selectionArgs, MediaStore.Files.FileColumns.DATE_MODIFIED + " desc") 89 | 90 | 91 | 92 | if (cursor != null && cursor.moveToFirst() && cursor.count > 0) { 93 | do { 94 | val location = cursor.getString(cursor.getColumnIndex(MediaStore.Files.FileColumns.DATA)) 95 | val initSize = java.lang.Long.parseLong(cursor.getString(cursor.getColumnIndex(MediaStore.Files.FileColumns.SIZE))) 96 | val name = cursor.getString(cursor.getColumnIndex(MediaStore.Files.FileColumns.TITLE)) 97 | val info = FileInfo(name, location, initSize) 98 | list.add(info) 99 | } while (cursor.moveToNext()) 100 | 101 | cursor.close() 102 | 103 | } 104 | 105 | this.docs = list 106 | return list 107 | } 108 | 109 | @Synchronized 110 | fun getVideo(): List { 111 | 112 | if (videos != null) return videos!! 113 | 114 | 115 | val list = mutableListOf() 116 | 117 | val cursor = mContentResolver.query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, 118 | arrayOf(MediaStore.Video.VideoColumns.DATA, MediaStore.Video.VideoColumns.TITLE, 119 | MediaStore.Video.VideoColumns.SIZE, MediaStore.Video.VideoColumns._ID), 120 | null, null, MediaStore.Video.VideoColumns.DATE_MODIFIED + " desc") 121 | 122 | 123 | 124 | if (cursor != null && cursor.moveToFirst() && cursor.count > 0) { 125 | do { 126 | val location = cursor.getString(cursor.getColumnIndex(MediaStore.Video.VideoColumns.DATA)) 127 | val id = java.lang.Long.valueOf(cursor.getString(cursor.getColumnIndex(MediaStore.Video.VideoColumns._ID))) 128 | 129 | val name = cursor.getString(cursor.getColumnIndex(MediaStore.Video.VideoColumns.TITLE)) 130 | val initSize = java.lang.Long.parseLong(cursor.getString(cursor.getColumnIndex(MediaStore.Video.VideoColumns.SIZE))) 131 | val info = FileInfo(name, location, initSize, id) 132 | list.add(info) 133 | } while (cursor.moveToNext()) 134 | } 135 | cursor?.close() 136 | 137 | this.videos = list 138 | return list 139 | } 140 | 141 | @Synchronized 142 | fun getPhotosDirectory(): List { 143 | 144 | if (pictures != null) return pictures!! 145 | 146 | val list = mutableListOf() 147 | val addPath = HashSet() 148 | 149 | val cursor = mContentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 150 | arrayOf(MediaStore.Images.ImageColumns.DATA), null, null, MediaStore.Images.ImageColumns.DATA + " desc") 151 | 152 | 153 | 154 | if (cursor != null && cursor.moveToFirst() && cursor.count > 0) { 155 | do { 156 | 157 | val location = cursor.getString(cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA)) 158 | val parentPath = File(location).parent 159 | addPath.add(parentPath) 160 | } while (cursor.moveToNext()) 161 | } 162 | cursor.close() 163 | 164 | addPath.forEach { 165 | 166 | val name = it.substring(it.lastIndexOf('/') + 1, it.length) 167 | val location = it 168 | val folderInfo = FileInfo(name, location) 169 | list.add(folderInfo) 170 | } 171 | this.pictures = list 172 | return list 173 | 174 | } 175 | 176 | @Synchronized 177 | fun getPhotos(path: String?): List? { 178 | if(path == null) return null 179 | val list = mutableListOf() 180 | list.addAll(File(path).listFiles().filter { it.isFile }.map { FileInfo(it.name, it.path, it.length()) }) 181 | return list 182 | } 183 | 184 | @Synchronized 185 | fun getApplications(): List? { 186 | 187 | if (apps != null) return apps 188 | 189 | val list = mutableListOf() 190 | val pm = App.ctx.packageManager 191 | var sourceDir: String 192 | 193 | val infos = pm.getInstalledApplications(0) 194 | 195 | for (i in infos) { 196 | sourceDir = i.sourceDir 197 | val initSize = File(sourceDir).length() 198 | val location = sourceDir 199 | val name = i.loadLabel(pm).toString() 200 | val app = FileInfo(name, location, initSize) 201 | list.add(app) 202 | } 203 | 204 | apps = list 205 | 206 | return apps 207 | } 208 | } 209 | 210 | 211 | } 212 | 213 | 214 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/androidservice/ui/Server.kt: -------------------------------------------------------------------------------- 1 | //package com.example.androidservice.ui 2 | // 3 | //import android.app.Notification 4 | //import android.app.Service 5 | //import android.content.Intent 6 | //import android.os.Binder 7 | //import android.os.IBinder 8 | //import com.weechan.httpserver.httpserver.HttpServer 9 | // 10 | //import com.weechan.httpserver.httpserver.HttpServerBuilder 11 | // 12 | ///** 13 | // * Created by 铖哥 on 2018/3/22. 14 | // */ 15 | //class Server : Service() { 16 | // override fun onBind(intent: Intent?): IBinder? { 17 | // return MyBinder() 18 | // } 19 | // 20 | // lateinit var service : HttpServer 21 | // 22 | // override fun onCreate() { 23 | // super.onCreate() 24 | // 25 | // service = HttpServerBuilder 26 | // .with(this) 27 | // .port(8080) 28 | // .getHttpServer() 29 | // 30 | // service.start() 31 | // 32 | // startForeground(1, Notification()) 33 | // } 34 | // 35 | // inner class MyBinder : Binder() { 36 | // fun stop(){ 37 | // service.start() 38 | // } 39 | // 40 | // fun start(){ 41 | // service.stop() 42 | // } 43 | // } 44 | // 45 | //} -------------------------------------------------------------------------------- /app/src/main/java/com/example/androidservice/uitls/AndroidClassUtils.kt: -------------------------------------------------------------------------------- 1 | package com.jimij.myapplication 2 | 3 | import android.content.Context 4 | import android.util.Log 5 | import dalvik.system.DexFile 6 | import kotlin.reflect.KClass 7 | 8 | /** 9 | * Created by jimiji on 2018/3/24. 10 | * to use this method,u need to disable instant run 11 | * Settings->Build->Instant Run 12 | * Instant Run will make the apk different from the release one 13 | */ 14 | private fun getClassesNameListInPackage(packageName: String, context: Context): List { 15 | val realPackageName = "${context.packageName}.$packageName" 16 | val df = DexFile(context.packageCodePath) 17 | val enumration = df.entries() 18 | val list = mutableListOf() 19 | while (enumration.hasMoreElements()) { 20 | val className = enumration.nextElement() 21 | if (className.contains(realPackageName) && !className.contains("$")) { 22 | Log.e("AndroidC",className ) 23 | list.add(className) 24 | } 25 | } 26 | return list 27 | } 28 | 29 | fun getClassesInPackage(packageName: String, context: Context) 30 | = getClassesNameListInPackage(packageName, context).mapNotNull { Class.forName(it) } 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/androidservice/uitls/Utils.kt: -------------------------------------------------------------------------------- 1 | package com.example.androidservice.uitls 2 | 3 | import android.text.TextUtils 4 | import android.webkit.MimeTypeMap 5 | import java.io.File 6 | import java.io.IOException 7 | import java.io.InputStream 8 | import java.io.OutputStream 9 | import java.net.* 10 | import java.util.* 11 | import java.util.regex.Pattern 12 | 13 | /** 14 | * Created by 铖哥 on 2018/3/18. 15 | */ 16 | 17 | fun InputStream.writeTo(outputStream: OutputStream, autoClose: Boolean = false, bufferSize: Int = 1024*2) { 18 | val buffer = ByteArray(bufferSize) 19 | val br = this.buffered() 20 | val bw = outputStream.buffered() 21 | var length = 0 22 | 23 | while ({ length = br.read(buffer); length != -1 }.invoke()) { 24 | bw.write(buffer, 0, length) 25 | } 26 | 27 | bw.flush() 28 | 29 | if (autoClose) { 30 | close() 31 | } 32 | } 33 | 34 | fun InputStream.string(): String { 35 | val buffer = ByteArray(1024*2) 36 | val br = this.buffered() 37 | var length = 0 38 | val sb = StringBuffer() 39 | 40 | while ({ length = br.read(buffer); length != -1 }.invoke()) { 41 | sb.append(String(buffer,0,length)) 42 | } 43 | 44 | return sb.toString() 45 | } 46 | 47 | 48 | 49 | fun getHostIp(): String? { 50 | val regexCIp = "^192\\.168\\.(\\d{1}|[1-9]\\d|1\\d{2}|2[0-4]\\d|25\\d)\\.(\\d{1}|[1-9]\\d|1\\d{2}|2[0-4]\\d|25\\d)$" 51 | //匹配A类地址 52 | val regexAIp = "^10\\.(\\d{1}|[1-9]\\d|1\\d{2}|2[0-4]\\d|25\\d)\\.(\\d{1}|[1-9]\\d|1\\d{2}|2[0-4]\\d|25\\d)\\.(\\d{1}|[1-9]\\d|1\\d{2}|2[0-4]\\d|25\\d)$" 53 | //匹配B类地址 54 | val regexBIp = "^172\\.(1[6-9]|2\\d|3[0-1])\\.(\\d{1}|[1-9]\\d|1\\d{2}|2[0-4]\\d|25\\d)\\.(\\d{1}|[1-9]\\d|1\\d{2}|2[0-4]\\d|25\\d)$" 55 | 56 | val hostIp: String 57 | val ip = Pattern.compile("($regexAIp)|($regexBIp)|($regexCIp)") 58 | var networkInterfaces: Enumeration? = null 59 | try { 60 | networkInterfaces = NetworkInterface.getNetworkInterfaces() 61 | } catch (e: SocketException) { 62 | e.printStackTrace() 63 | } 64 | 65 | var address: InetAddress 66 | while (networkInterfaces!!.hasMoreElements()) { 67 | val networkInterface = networkInterfaces!!.nextElement() 68 | val inetAddresses = networkInterface.getInetAddresses() 69 | while (inetAddresses.hasMoreElements()) { 70 | address = inetAddresses.nextElement() 71 | val hostAddress = address.getHostAddress() 72 | val matcher = ip.matcher(hostAddress) 73 | if (matcher.matches()) { 74 | hostIp = hostAddress 75 | return hostIp 76 | } 77 | 78 | } 79 | } 80 | return null 81 | } 82 | 83 | 84 | 85 | fun getMimeType(path: String): String { 86 | var mimeType = "application/octet-stream" 87 | if (!TextUtils.isEmpty(path)) { 88 | val extension = MimeTypeMap.getFileExtensionFromUrl(path) 89 | if (MimeTypeMap.getSingleton().hasExtension(extension)) 90 | mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension) 91 | } 92 | return mimeType 93 | } 94 | 95 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 19 | 20 |