├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── dimens.xml
│ │ │ │ ├── colors.xml
│ │ │ │ ├── themes.xml
│ │ │ │ └── strings.xml
│ │ │ ├── values-land
│ │ │ │ └── dimens.xml
│ │ │ ├── values-w1240dp
│ │ │ │ └── dimens.xml
│ │ │ ├── values-w600dp
│ │ │ │ └── dimens.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── xml
│ │ │ │ ├── network_security_config.xml
│ │ │ │ ├── backup_rules.xml
│ │ │ │ └── data_extraction_rules.xml
│ │ │ ├── values-v23
│ │ │ │ └── themes.xml
│ │ │ ├── layout
│ │ │ │ ├── activity_main.xml
│ │ │ │ ├── item_upload.xml
│ │ │ │ ├── multiple_single_files_layout.xml
│ │ │ │ ├── single_file_upload_layout.xml
│ │ │ │ └── upload_multiple_files_simultaneously_layout.xml
│ │ │ ├── values-night
│ │ │ │ └── themes.xml
│ │ │ └── drawable
│ │ │ │ ├── ic_launcher_foreground.xml
│ │ │ │ └── ic_launcher_background.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── dh
│ │ │ │ └── updemo
│ │ │ │ ├── FileItem.kt
│ │ │ │ ├── FilesItem.kt
│ │ │ │ ├── App.kt
│ │ │ │ ├── MainActivity.kt
│ │ │ │ ├── UploadAdapter.kt
│ │ │ │ ├── SingleFileUploadActivity.kt
│ │ │ │ ├── UploadMultipleFilesSimultaneouslyActivity.kt
│ │ │ │ └── MultipleSingleFileUploadsActivity.kt
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── dh
│ │ │ └── updemo
│ │ │ └── ExampleUnitTest.kt
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── dh
│ │ └── updemo
│ │ └── ExampleInstrumentedTest.kt
├── proguard-rules.pro
└── build.gradle
├── quickupload
├── .gitignore
├── consumer-rules.pro
├── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── dh
│ │ │ │ └── quickupload
│ │ │ │ ├── data
│ │ │ │ ├── UploadStatus.kt
│ │ │ │ ├── UploadElapsedTime.kt
│ │ │ │ ├── UploadRate.kt
│ │ │ │ ├── RetryPolicyConfig.kt
│ │ │ │ ├── NameValue.kt
│ │ │ │ ├── UploadFile.kt
│ │ │ │ ├── HttpUploadTaskParameters.kt
│ │ │ │ ├── UploadTaskParameters.kt
│ │ │ │ └── UploadInfo.kt
│ │ │ │ ├── tools
│ │ │ │ ├── datapreservation
│ │ │ │ │ ├── Persistable.kt
│ │ │ │ │ └── PersistableData.kt
│ │ │ │ ├── translationfile
│ │ │ │ │ ├── SchemeHandler.kt
│ │ │ │ │ ├── FileSchemeHandler.kt
│ │ │ │ │ └── ContentResolverSchemeHandler.kt
│ │ │ │ └── logger
│ │ │ │ │ ├── DefaultExt.kt
│ │ │ │ │ └── Logger.kt
│ │ │ │ ├── exceptions
│ │ │ │ └── Exceptions.kt
│ │ │ │ ├── network
│ │ │ │ ├── BaseNetwork.kt
│ │ │ │ ├── okhttp
│ │ │ │ │ ├── OkHttpExtensions.kt
│ │ │ │ │ ├── OkHttpBodyWriter.kt
│ │ │ │ │ ├── OkHttpNetwork.kt
│ │ │ │ │ └── OkHttpNetworkRequest.kt
│ │ │ │ ├── ServerResponse.kt
│ │ │ │ ├── NetworkRequest.kt
│ │ │ │ └── BodyWriter.kt
│ │ │ │ ├── observer
│ │ │ │ ├── task
│ │ │ │ │ ├── UploadTaskObserver.kt
│ │ │ │ │ ├── TaskCompletionNotifier.kt
│ │ │ │ │ └── UploadObserverBase.kt
│ │ │ │ └── network
│ │ │ │ │ └── NetworkMonitor.kt
│ │ │ │ ├── quick
│ │ │ │ ├── UploadFileExtensions.kt
│ │ │ │ ├── QuickUploadRequest.kt
│ │ │ │ └── QuickUploadTask.kt
│ │ │ │ ├── GeneralUploadTask.kt
│ │ │ │ ├── GeneralUploadRequest.kt
│ │ │ │ ├── UploadRequest.kt
│ │ │ │ ├── UploadConfiguration.kt
│ │ │ │ ├── extensions
│ │ │ │ └── Extensions.kt
│ │ │ │ ├── UploadService.kt
│ │ │ │ └── UploadTask.kt
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── dh
│ │ │ └── quickupload
│ │ │ └── ExampleUnitTest.kt
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── dh
│ │ └── quickupload
│ │ └── ExampleInstrumentedTest.kt
├── proguard-rules.pro
└── build.gradle
├── jitpack.yml
├── pictureresources
├── one.gif
├── two.gif
└── three.gif
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── config.gradle
├── settings.gradle
├── .gitignore
├── gradle.properties
├── README.md
├── gradlew.bat
├── gradlew
└── LICENSE
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/quickupload/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/jitpack.yml:
--------------------------------------------------------------------------------
1 | jdk:
2 | - openjdk17
--------------------------------------------------------------------------------
/quickupload/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/pictureresources/one.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XJ-Up/quickupload/HEAD/pictureresources/one.gif
--------------------------------------------------------------------------------
/pictureresources/two.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XJ-Up/quickupload/HEAD/pictureresources/two.gif
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 16dp
3 |
--------------------------------------------------------------------------------
/pictureresources/three.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XJ-Up/quickupload/HEAD/pictureresources/three.gif
--------------------------------------------------------------------------------
/app/src/main/res/values-land/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 48dp
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w1240dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 200dp
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w600dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 48dp
3 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XJ-Up/quickupload/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XJ-Up/quickupload/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XJ-Up/quickupload/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XJ-Up/quickupload/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XJ-Up/quickupload/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XJ-Up/quickupload/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XJ-Up/quickupload/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XJ-Up/quickupload/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XJ-Up/quickupload/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/config.gradle:
--------------------------------------------------------------------------------
1 | ext {
2 | kotlin_version = "1.6.20"
3 | minsdk_version = 21
4 | targetsdk_version = 34
5 | compilesdk_version = 34
6 | }
7 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XJ-Up/quickupload/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XJ-Up/quickupload/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/xml/network_security_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/quickupload/src/main/java/com/dh/quickupload/data/UploadStatus.kt:
--------------------------------------------------------------------------------
1 | package com.dh.quickupload.data
2 |
3 | enum class UploadStatus {
4 | DEFAULT,
5 | Wait,
6 | InProgress,
7 | Success,
8 | Error,
9 | Completed
10 | }
11 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri May 10 15:10:46 CST 2024
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/quickupload/src/main/java/com/dh/quickupload/data/UploadElapsedTime.kt:
--------------------------------------------------------------------------------
1 | package com.dh.quickupload.data
2 |
3 | /**
4 | * 上传时间
5 | */
6 | data class UploadElapsedTime(val minutes: Int, val seconds: Int) {
7 | val totalSeconds: Int
8 | get() = minutes * 60 + seconds
9 | }
10 |
--------------------------------------------------------------------------------
/quickupload/src/main/java/com/dh/quickupload/tools/datapreservation/Persistable.kt:
--------------------------------------------------------------------------------
1 | package com.dh.quickupload.tools.datapreservation
2 |
3 | interface Persistable {
4 | fun toPersistableData(): PersistableData
5 |
6 | interface Creator {
7 | fun createFromPersistableData(data: PersistableData): T
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/quickupload/src/main/java/com/dh/quickupload/data/UploadRate.kt:
--------------------------------------------------------------------------------
1 | package com.dh.quickupload.data
2 |
3 | data class UploadRate(val value: Int = 0, val unit: UploadRateUnit = UploadRateUnit.BitPerSecond) {
4 | enum class UploadRateUnit {
5 | BitPerSecond,
6 | KilobitPerSecond,
7 | MegabitPerSecond
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/quickupload/src/main/java/com/dh/quickupload/exceptions/Exceptions.kt:
--------------------------------------------------------------------------------
1 | package com.dh.quickupload.exceptions
2 |
3 | import com.dh.quickupload.network.ServerResponse
4 |
5 | class UserCancelledUploadException : Throwable("用户已取消上传")
6 | class UploadError(val serverResponse: ServerResponse) : Throwable("上传错误")
7 | class NoNetworkException: Throwable("网络连接断开")
8 |
--------------------------------------------------------------------------------
/app/src/main/java/com/dh/updemo/FileItem.kt:
--------------------------------------------------------------------------------
1 | package com.dh.updemo
2 |
3 | import com.dh.quickupload.observer.task.UploadObserverBase
4 |
5 | /**
6 | * 单个地址文件上传示例
7 | * 使用:
8 | * 继承 UploadObserverBase()
9 | *
10 | */
11 | data class FileItem(
12 | val fileName: String,
13 | val filePath: String,
14 | override val uploadId: String,
15 | ) : UploadObserverBase(uploadId)
--------------------------------------------------------------------------------
/app/src/main/java/com/dh/updemo/FilesItem.kt:
--------------------------------------------------------------------------------
1 | package com.dh.updemo
2 |
3 | import com.dh.quickupload.observer.task.UploadObserverBase
4 |
5 | /**
6 | * 多个地址文件上传示例
7 | * 使用:
8 | * 继承 UploadObserverBase()
9 | *
10 | */
11 | data class FilesItem(
12 | val fileName: String,
13 | val filePath: MutableList,
14 | override val uploadId: String,
15 | ) : UploadObserverBase(uploadId)
16 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/app/src/test/java/com/dh/updemo/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.dh.updemo
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 | }
--------------------------------------------------------------------------------
/quickupload/src/test/java/com/dh/quickupload/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.dh.quickupload
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 | }
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | gradlePluginPortal()
6 |
7 | }
8 | }
9 | dependencyResolutionManagement {
10 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
11 | repositories {
12 | google()
13 | mavenCentral()
14 | maven { url 'https://jitpack.io' }
15 | }
16 | }
17 |
18 | rootProject.name = "UpDemo"
19 | include ':app'
20 | include ':quickupload'
21 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v23/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
--------------------------------------------------------------------------------
/quickupload/src/main/java/com/dh/quickupload/network/BaseNetwork.kt:
--------------------------------------------------------------------------------
1 | package com.dh.quickupload.network
2 |
3 | import java.io.IOException
4 |
5 | interface BaseNetwork {
6 | /**
7 | * 为给定的URL和HTTP方法创建一个新的连接。
8 | * @ param uploadId请求此连接的上载的ID
9 | * @ param方法HTTP方法
10 | * @ param url要连接到的URL
11 | * @ return新连接对象
12 | * @ 如果在创建连接对象时发生错误,则抛出IOException
13 | */
14 | @Throws(IOException::class)
15 | fun createRequest(uploadId: String, method: String, url: String): NetworkRequest
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/backup_rules.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Gradle files
2 | .gradle/
3 | build/
4 |
5 | # Local configuration file (sdk path, etc)
6 | local.properties
7 |
8 | # Log/OS Files
9 | *.log
10 |
11 | # Android Studio generated files and folders
12 | captures/
13 | .externalNativeBuild/
14 | .cxx/
15 | *.apk
16 | output.json
17 |
18 | # IntelliJ
19 | *.iml
20 | .idea/
21 | misc.xml
22 | deploymentTargetDropDown.xml
23 | render.experimental.xml
24 |
25 | # Keystore files
26 | *.jks
27 | *.keystore
28 |
29 | # Google Services (e.g. APIs or Firebase)
30 | google-services.json
31 |
32 | # Android Profiling
33 | *.hprof
34 |
--------------------------------------------------------------------------------
/quickupload/src/main/java/com/dh/quickupload/observer/task/UploadTaskObserver.kt:
--------------------------------------------------------------------------------
1 | package com.dh.quickupload.observer.task
2 |
3 | import com.dh.quickupload.data.UploadInfo
4 | import com.dh.quickupload.network.ServerResponse
5 |
6 | interface UploadTaskObserver {
7 | fun onWait(info: UploadInfo)
8 |
9 | fun onProgress(
10 | info: UploadInfo
11 | )
12 |
13 | fun onSuccess(
14 | info: UploadInfo,
15 | response: ServerResponse
16 | )
17 |
18 | fun onError(
19 | info: UploadInfo,
20 | exception: Throwable
21 | )
22 |
23 | fun onCompleted(
24 | info: UploadInfo
25 | )
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/data_extraction_rules.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
12 |
13 |
19 |
--------------------------------------------------------------------------------
/quickupload/src/main/java/com/dh/quickupload/data/RetryPolicyConfig.kt:
--------------------------------------------------------------------------------
1 | package com.dh.quickupload.data
2 |
3 | data class RetryPolicyConfig(
4 | /**
5 | * 当上传失败时,设置下一次尝试之前等待的时间 (以秒为单位)
6 | */
7 | val initialWaitTimeSeconds: Int,
8 |
9 | /**
10 | * 设置两次上传尝试之间的最长等待时间 (以秒为单位)。
11 | */
12 | val maxWaitTimeSeconds: Int,
13 |
14 | /**
15 | * 设置退避定时器乘数。例如,如果设置为2,则每次上载
16 | */
17 | val multiplier: Int,
18 |
19 | /**
20 | * 设置每个请求的默认重试次数。
21 | */
22 | val defaultMaxRetries: Int
23 | ) {
24 | override fun toString(): String {
25 | return """{"initialWaitTimeSeconds": $initialWaitTimeSeconds, "maxWaitTimeSeconds": $maxWaitTimeSeconds, "multiplier": $multiplier, "defaultMaxRetries": $defaultMaxRetries}"""
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/dh/updemo/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.dh.updemo
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.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.getInstrumentation().targetContext
22 | assertEquals("com.dh.updemo", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/quickupload/src/main/java/com/dh/quickupload/tools/translationfile/SchemeHandler.kt:
--------------------------------------------------------------------------------
1 | package com.dh.quickupload.tools.translationfile
2 |
3 | import android.content.Context
4 | import java.io.InputStream
5 |
6 | interface SchemeHandler {
7 | /**
8 | * 使用文件路径初始化实例。
9 | */
10 | fun init(path: String)
11 |
12 | /**
13 | * 获取文件大小 (以字节为单位)。
14 | */
15 | fun size(context: Context): Long
16 |
17 | /**
18 | *获取文件输入流以读取它
19 | */
20 | fun stream(context: Context): InputStream
21 |
22 | /**
23 | * 获取文件内容类型
24 | */
25 | fun contentType(context: Context): String
26 |
27 | /**
28 | * 获取文件名
29 | */
30 | fun name(context: Context): String
31 |
32 | /**
33 | * 删除文件
34 | */
35 | fun delete(context: Context): Boolean
36 | }
37 |
--------------------------------------------------------------------------------
/quickupload/src/main/java/com/dh/quickupload/tools/logger/DefaultExt.kt:
--------------------------------------------------------------------------------
1 | package com.dh.quickupload.tools.logger
2 |
3 | import android.util.Log
4 |
5 | class DefaultExt : Logger.Ext {
6 |
7 | companion object {
8 | private const val TAG = "QuickUpload"
9 | }
10 |
11 | override fun error(component: String, uploadId: String, message: String, exception: Throwable?) {
12 | Log.e(TAG, "$component - (uploadId: $uploadId) - $message", exception)
13 | }
14 |
15 | override fun debug(component: String, uploadId: String, message: String) {
16 | Log.i(TAG, "$component - (uploadId: $uploadId) - $message")
17 | }
18 |
19 | override fun info(component: String, uploadId: String, message: String) {
20 | Log.i(TAG, "$component - (uploadId: $uploadId) - $message")
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/quickupload/src/androidTest/java/com/dh/quickupload/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.dh.quickupload
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.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.getInstrumentation().targetContext
22 | assertEquals("com.dh.quickupload.test", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------
/quickupload/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
--------------------------------------------------------------------------------
/quickupload/src/main/java/com/dh/quickupload/network/okhttp/OkHttpExtensions.kt:
--------------------------------------------------------------------------------
1 | package com.dh.quickupload.network.okhttp
2 |
3 | import com.dh.quickupload.network.ServerResponse
4 | import okhttp3.Response
5 |
6 | private fun String.requiresRequestBody() =
7 | this == "POST" || this == "PUT" || this == "PATCH" || this == "PROPPATCH" || this == "REPORT"
8 |
9 | private fun String.permitsRequestBody() = !(this == "GET" || this == "HEAD")
10 |
11 | internal fun String.hasBody(): Boolean {
12 | val method = trim().uppercase()
13 | return method.permitsRequestBody() || method.requiresRequestBody()
14 | }
15 |
16 | private fun Response.headersHashMap() = LinkedHashMap(headers.toMap())
17 |
18 | private fun Response.bodyBytes() = body?.bytes() ?: ByteArray(0)
19 |
20 | internal fun Response.asServerResponse() = ServerResponse(code, bodyBytes(), headersHashMap())
21 |
--------------------------------------------------------------------------------
/quickupload/src/main/java/com/dh/quickupload/network/okhttp/OkHttpBodyWriter.kt:
--------------------------------------------------------------------------------
1 | package com.dh.quickupload.network.okhttp
2 |
3 | import com.dh.quickupload.network.BodyWriter
4 | import okio.BufferedSink
5 | import java.io.IOException
6 |
7 | class OkHttpBodyWriter(private val sink: BufferedSink, listener: OnStreamWriteListener) :
8 | BodyWriter(listener) {
9 | @Throws(IOException::class)
10 | override fun internalWrite(bytes: ByteArray) {
11 | sink.write(bytes)
12 | }
13 |
14 | @Throws(IOException::class)
15 | override fun internalWrite(bytes: ByteArray, lengthToWriteFromStart: Int) {
16 | sink.write(bytes, 0, lengthToWriteFromStart)
17 | }
18 |
19 | @Throws(IOException::class)
20 | override fun flush() {
21 | sink.flush()
22 | }
23 |
24 | @Throws(IOException::class)
25 | override fun close() {
26 | sink.close()
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/quickupload/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/quickupload/src/main/java/com/dh/quickupload/observer/task/TaskCompletionNotifier.kt:
--------------------------------------------------------------------------------
1 | package com.dh.quickupload.observer.task
2 |
3 | import com.dh.quickupload.UploadService
4 | import com.dh.quickupload.data.UploadInfo
5 | import com.dh.quickupload.network.ServerResponse
6 |
7 | class TaskCompletionNotifier(private val service: UploadService) : UploadTaskObserver {
8 | override fun onWait(
9 | info: UploadInfo
10 | ) {
11 | }
12 |
13 | override fun onProgress(
14 | info: UploadInfo
15 | ) {
16 | }
17 |
18 | override fun onSuccess(
19 | info: UploadInfo,
20 |
21 | response: ServerResponse
22 | ) {
23 | }
24 |
25 | override fun onError(
26 | info: UploadInfo,
27 | exception: Throwable
28 | ) {
29 | }
30 |
31 | override fun onCompleted(
32 | info: UploadInfo
33 | ) {
34 | service.taskCompleted(info.uploadId)
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/quickupload/src/main/java/com/dh/quickupload/quick/UploadFileExtensions.kt:
--------------------------------------------------------------------------------
1 | package com.dh.quickupload.quick
2 |
3 | import com.dh.quickupload.data.UploadFile
4 | import com.dh.quickupload.extensions.setOrRemove
5 |
6 | //与每个文件关联的属性
7 | private const val PROPERTY_PARAM_NAME = "multipartParamName"
8 | private const val PROPERTY_REMOTE_FILE_NAME = "multipartRemoteFileName"
9 | private const val PROPERTY_CONTENT_TYPE = "multipartContentType"
10 |
11 | internal var UploadFile.parameterName: String?
12 | get() = properties[PROPERTY_PARAM_NAME]
13 | set(value) {
14 | properties.setOrRemove(PROPERTY_PARAM_NAME, value)
15 | }
16 |
17 | internal var UploadFile.remoteFileName: String?
18 | get() = properties[PROPERTY_REMOTE_FILE_NAME]
19 | set(value) {
20 | properties.setOrRemove(PROPERTY_REMOTE_FILE_NAME, value)
21 | }
22 |
23 | internal var UploadFile.contentType: String?
24 | get() = properties[PROPERTY_CONTENT_TYPE]
25 | set(value) {
26 | properties.setOrRemove(PROPERTY_CONTENT_TYPE, value)
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
15 |
21 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
17 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
17 |
21 |
--------------------------------------------------------------------------------
/quickupload/src/main/java/com/dh/quickupload/tools/translationfile/FileSchemeHandler.kt:
--------------------------------------------------------------------------------
1 | package com.dh.quickupload.tools.translationfile
2 |
3 | import android.content.Context
4 | import com.dh.quickupload.extensions.autoDetectMimeType
5 | import com.dh.quickupload.tools.logger.Logger
6 | import com.dh.quickupload.tools.logger.Logger.NA
7 | import java.io.File
8 | import java.io.FileInputStream
9 | import java.io.IOException
10 |
11 | internal class FileSchemeHandler : SchemeHandler {
12 | private lateinit var file: File
13 |
14 | override fun init(path: String) {
15 | file = File(path)
16 | }
17 |
18 | override fun size(context: Context) = file.length()
19 |
20 | override fun stream(context: Context) = FileInputStream(file)
21 |
22 | override fun contentType(context: Context) = file.absolutePath.autoDetectMimeType()
23 |
24 | override fun name(context: Context) = file.name
25 | ?: throw IOException("无法获取 ${file.absolutePath} 的文件名")
26 |
27 | override fun delete(context: Context) = try {
28 | file.delete()
29 | } catch (exc: Throwable) {
30 | Logger.error(javaClass.simpleName, NA, exc) { "文件删除错误" }
31 | false
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/java/com/dh/updemo/App.kt:
--------------------------------------------------------------------------------
1 | package com.dh.updemo
2 |
3 | import android.app.Application
4 | import android.app.NotificationChannel
5 | import android.app.NotificationManager
6 | import android.content.Context
7 | import android.os.Build
8 | import com.dh.quickupload.BuildConfig
9 | import com.dh.quickupload.UploadConfiguration
10 |
11 | class App : Application() {
12 | companion object {
13 | const val notificationChannelID = "TestChannel"
14 | }
15 |
16 | private fun createNotificationChannel() {
17 | if (Build.VERSION.SDK_INT >= 26) {
18 | val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
19 | val channel = NotificationChannel(
20 | notificationChannelID,
21 | "TestApp Channel",
22 | NotificationManager.IMPORTANCE_LOW
23 | )
24 | manager.createNotificationChannel(channel)
25 | }
26 | }
27 | override fun onCreate() {
28 | super.onCreate()
29 | createNotificationChannel()
30 | UploadConfiguration.initialize(
31 | context = this,
32 | defaultNotificationChannel = notificationChannelID,
33 | debug = BuildConfig.DEBUG
34 | )
35 | }
36 | }
--------------------------------------------------------------------------------
/quickupload/src/main/java/com/dh/quickupload/data/NameValue.kt:
--------------------------------------------------------------------------------
1 | package com.dh.quickupload.data
2 |
3 | import android.os.Parcelable
4 | import kotlinx.parcelize.Parcelize
5 | import com.dh.quickupload.extensions.isASCII
6 | import com.dh.quickupload.tools.datapreservation.Persistable
7 | import com.dh.quickupload.tools.datapreservation.PersistableData
8 |
9 | @Parcelize
10 | data class NameValue(val name: String, val value: String) : Parcelable, Persistable {
11 | fun validateAsHeader(): NameValue {
12 | require(name.isASCII() && value.isASCII()) {
13 | "标头 ${name}及其值 ${value}必须仅为ASCII!"
14 | }
15 |
16 | return this
17 | }
18 |
19 | override fun toPersistableData() = PersistableData().apply {
20 | putString(CodingKeys.name, name)
21 | putString(CodingKeys.value, value)
22 | }
23 |
24 | companion object : Persistable.Creator {
25 | private object CodingKeys {
26 | const val name = "name"
27 | const val value = "value"
28 | }
29 |
30 | override fun createFromPersistableData(data: PersistableData) = NameValue(
31 | name = data.getString(CodingKeys.name),
32 | value = data.getString(CodingKeys.value)
33 | )
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/quickupload/src/main/java/com/dh/quickupload/observer/network/NetworkMonitor.kt:
--------------------------------------------------------------------------------
1 | package com.dh.quickupload.observer.network
2 |
3 | import android.content.Context
4 | import android.net.ConnectivityManager
5 | import android.net.Network
6 | import android.net.NetworkCapabilities
7 | import android.net.NetworkRequest
8 | import com.dh.quickupload.UploadService
9 | import java.lang.ref.WeakReference
10 |
11 | class NetworkMonitor(context: Context) {
12 |
13 | private val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
14 | private val networkCallback: ConnectivityManager.NetworkCallback
15 |
16 | init {
17 | networkCallback = object : ConnectivityManager.NetworkCallback() {
18 | override fun onLost(network: Network) {
19 | super.onLost(network)
20 | UploadService.noNetworkStopAllUploads()
21 | }
22 | }
23 | }
24 |
25 | fun register() {
26 | val networkRequest = NetworkRequest.Builder()
27 | .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
28 | .build()
29 | connectivityManager.registerNetworkCallback(networkRequest, networkCallback)
30 | }
31 |
32 | fun unregister() {
33 | connectivityManager.unregisterNetworkCallback(networkCallback)
34 | }
35 | }
--------------------------------------------------------------------------------
/quickupload/src/main/java/com/dh/quickupload/network/ServerResponse.kt:
--------------------------------------------------------------------------------
1 | package com.dh.quickupload.network
2 |
3 | import android.os.Parcelable
4 | import java.io.Serializable
5 | import kotlinx.parcelize.IgnoredOnParcel
6 | import kotlinx.parcelize.Parcelize
7 |
8 | @Parcelize
9 | data class ServerResponse(
10 | /**
11 | * 服务器响应响应代码。如果您正在实现非HTTP
12 | * 协议,将此设置为200以通知任务已完成
13 | * 成功。小于200或大于299表示的整数值
14 | * 来自服务器的错误响应。
15 | */
16 | val code: Int,
17 |
18 | /**
19 | * 服务器响应正文。
20 | * 如果你的服务器响应一个字符串,你可以得到它
21 | * 如果字符串是JSON,则可以使用org.json等库对其进行解析
22 | * 如果你的服务器没有返回任何东西,设置为空数组。
23 | */
24 | val body: ByteArray,
25 | /**
26 | * 服务器响应标头
27 | */
28 | val headers: LinkedHashMap
29 | ) : Parcelable, Serializable {
30 |
31 | /**
32 | * 获取服务器响应正文作为字符串。
33 | * 如果字符串是JSON,则可以使用org.json等库对其进行解析
34 | * @ 返回字符串
35 | */
36 | @IgnoredOnParcel
37 | val bodyString: String
38 | get() = String(body)
39 |
40 | @IgnoredOnParcel
41 | val isSuccessful: Boolean
42 | get() = code in 200..399
43 |
44 | companion object {
45 | fun successfulEmpty(): ServerResponse {
46 | return ServerResponse(
47 | code = 200, body = ByteArray(1), headers = LinkedHashMap()
48 | )
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
20 | # Enables namespacing of each library's R class so that its R class includes only the
21 | # resources declared in the library itself and none from the library's dependencies,
22 | # thereby reducing the size of the R class for that library
23 | android.nonTransitiveRClass=true
--------------------------------------------------------------------------------
/quickupload/src/main/java/com/dh/quickupload/network/okhttp/OkHttpNetwork.kt:
--------------------------------------------------------------------------------
1 | package com.dh.quickupload.network.okhttp
2 |
3 |
4 | import com.dh.quickupload.UploadConfiguration
5 | import com.dh.quickupload.network.NetworkRequest
6 | import com.dh.quickupload.network.BaseNetwork
7 | import okhttp3.Interceptor
8 | import okhttp3.OkHttpClient
9 | import okhttp3.Response
10 | import java.io.IOException
11 | import java.util.concurrent.TimeUnit
12 |
13 | /**
14 | * OkHttp网络的实现.
15 | */
16 | class OkHttpNetwork(
17 | private val client: OkHttpClient =
18 | OkHttpClient.Builder()
19 | .followRedirects(true)
20 | .followSslRedirects(true)
21 | .retryOnConnectionFailure(true)
22 | .connectTimeout(15, TimeUnit.SECONDS)
23 | .writeTimeout(30, TimeUnit.SECONDS)
24 | .readTimeout(30, TimeUnit.SECONDS)
25 | .cache(null)
26 | .addInterceptor(Interceptor { chain ->
27 | val request = chain.request().newBuilder()
28 | .header("User-Agent", UploadConfiguration.defaultUserAgent)
29 | .build()
30 | chain.proceed(request)
31 | })
32 | .build()
33 | ) : BaseNetwork {
34 | @Throws(IOException::class)
35 | override fun createRequest(uploadId: String, method: String, url: String): NetworkRequest {
36 | return OkHttpNetworkRequest(uploadId, client, method, url)
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | id 'org.jetbrains.kotlin.android'
4 | }
5 |
6 | android {
7 | namespace 'com.dh.updemo'
8 | compileSdk compilesdk_version
9 |
10 | defaultConfig {
11 | applicationId "com.dh.updemo"
12 | minSdk minsdk_version
13 | targetSdk targetsdk_version
14 | versionCode 4
15 | versionName "1.0.0"
16 |
17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
18 | }
19 |
20 | buildTypes {
21 | release {
22 | minifyEnabled false
23 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
24 | }
25 | }
26 | compileOptions {
27 | sourceCompatibility JavaVersion.VERSION_1_8
28 | targetCompatibility JavaVersion.VERSION_1_8
29 | }
30 | kotlinOptions {
31 | jvmTarget = '1.8'
32 | }
33 | buildFeatures {
34 | viewBinding true
35 | }
36 | }
37 |
38 | dependencies {
39 | testImplementation 'junit:junit:4.+'
40 | androidTestImplementation 'androidx.test.ext:junit:1.1.3'
41 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
42 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
43 | implementation 'com.google.android.material:material:1.4.0'
44 | implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
45 | implementation project(':quickupload')
46 | // implementation 'com.github.XJ-Up:quickupload:1.2.1'
47 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_upload.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
22 |
23 |
33 |
34 |
35 |
42 |
--------------------------------------------------------------------------------
/quickupload/src/main/java/com/dh/quickupload/network/NetworkRequest.kt:
--------------------------------------------------------------------------------
1 | package com.dh.quickupload.network
2 |
3 | import java.io.Closeable
4 | import java.io.IOException
5 | import com.dh.quickupload.data.NameValue
6 |
7 | interface NetworkRequest : Closeable {
8 |
9 | /**
10 | * 当Body准备好被写时,委托被调用。
11 | */
12 | interface RequestBodyDelegate {
13 |
14 | /**
15 | * 处理请求正文的写入。
16 | * @ param bodyWriter用于在正文上写入的对象
17 | * @ 如果在写入正文时发生错误,则抛出IOException
18 | */
19 | @Throws(IOException::class)
20 | fun onWriteRequestBody(bodyWriter: BodyWriter)
21 | }
22 |
23 | /**
24 | * 设置请求标头。
25 | * @ param requestHeaders要设置的请求标头
26 | * @ 如果在设置请求头时发生错误,则抛出IOException
27 | * @ return实例
28 | */
29 | @Throws(IOException::class)
30 | fun setHeaders(requestHeaders: List): NetworkRequest
31 |
32 | /**
33 | * 设置总body字节数。
34 | * @ param totalBodyBytes总字节数
35 | * @ param isFixedLengthStreamingMode如果必须使用固定长度流模式,则为true。如果
36 | * 这是假的,必须使用chunked流模式。
37 | * @ return实例
38 | */
39 | fun setTotalBodyBytes(totalBodyBytes: Long, isFixedLengthStreamingMode: Boolean): NetworkRequest
40 |
41 | /**
42 | * 获取服务器响应。
43 | * @ return对象,包含服务器响应状态、标头和正文。
44 | * @ param委托处理请求正文的写入
45 | * @ param监听器,它在写入字节时得到通知,并控制是否
46 | * @ 如果在获取服务器响应时发生错误,则抛出IOException
47 | */
48 | @Throws(IOException::class)
49 | fun getResponse(
50 | delegate: RequestBodyDelegate,
51 | listener: BodyWriter.OnStreamWriteListener
52 | ): ServerResponse
53 | }
54 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
18 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
29 |
31 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://imgse.com/i/pk3f4HO)
2 |
3 | ### 一个让开发者快速完成上传功能的框架 支持java、kotlin,使用说明:[QuickUpDoc](https://xj-up.github.io/quickupdoc/)
4 |
5 | # 截图
6 |
7 | | 单文件上传模式 | 单文件上传模式多个文件上传 | 多个文件同时上传模式 |
8 | |----------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------|
9 | |  |  |  |
10 |
11 | # 如何集成
12 |
13 | - 添加仓库
14 |
15 | ```groovy
16 | // build.gradle(Project:)
17 | allprojects {
18 | repositories {
19 | maven { url 'https://jitpack.io' }
20 | }
21 | }
22 | ```
23 |
24 | - 添加依赖
25 |
26 | ```groovy
27 | // build.gradle(Module:)
28 | dependencies {
29 | implementation 'com.github.XJ-Up:quickupload:1.2.1'
30 | }
31 | ```
32 | ## 具体使用可参考[demo](https://github.com/XJ-Up/quickupload/tree/main/app/src/main/java/com/dh/updemo)
33 | #### 如果你没有服务器上传接口,你可以下载服务器[demo](https://github.com/XJ-Up/TestServer)搭建自己的测试服务器,来体验quickupload
34 |
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/java/com/dh/updemo/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.dh.updemo
2 |
3 | import android.Manifest
4 | import android.content.Intent
5 | import android.content.pm.PackageManager
6 | import android.os.Build
7 | import androidx.appcompat.app.AppCompatActivity
8 | import android.os.Bundle
9 | import android.widget.Button
10 | import androidx.activity.result.contract.ActivityResultContracts
11 | import androidx.core.content.ContextCompat
12 |
13 |
14 | class MainActivity : AppCompatActivity() {
15 | private val notificationPermissionRequest =
16 | registerForActivityResult(ActivityResultContracts.RequestPermission()) {
17 | }
18 |
19 | override fun onCreate(savedInstanceState: Bundle?) {
20 | super.onCreate(savedInstanceState)
21 | setContentView(R.layout.activity_main)
22 | checkPostNotificationsPermission()
23 | findViewById