├── .github └── workflows │ └── android_ci.yml ├── .gitignore ├── CHANGE.md ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── itxca │ │ └── sample │ │ └── msa │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── itxca │ │ │ └── sample │ │ │ └── msa │ │ │ ├── ActivityResultContractsActivity.kt │ │ │ ├── ContractActivity.kt │ │ │ ├── FragmentActivity.kt │ │ │ ├── FragmentAdapter.kt │ │ │ ├── LogViewModel.kt │ │ │ ├── MainActivity.kt │ │ │ ├── SampleActivity.kt │ │ │ ├── SecondActivity.kt │ │ │ ├── TestFragment.kt │ │ │ ├── Utils.kt │ │ │ └── base │ │ │ ├── BaseActivity.kt │ │ │ └── BaseFragment.kt │ └── res │ │ ├── anim │ │ ├── bottom_in.xml │ │ └── bottom_out.xml │ │ ├── layout │ │ ├── activity_activity_result_contracts.xml │ │ ├── activity_fragment.xml │ │ ├── activity_main.xml │ │ ├── activity_second.xml │ │ └── fragment_test.xml │ │ ├── mipmap-xhdpi │ │ └── logo.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ ├── styles.xml │ │ └── themes.xml │ └── test │ └── java │ └── com │ └── itxca │ └── sample │ └── msa │ └── ExampleUnitTest.kt ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── library ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── itxca │ └── msa │ ├── Extension.kt │ ├── IManageStartActivity.kt │ ├── ManageStartActivity.kt │ └── StartActivityResult.kt ├── screenshots ├── 1.png ├── 2.png ├── 3.png ├── logo.png └── msa.gif └── settings.gradle /.github/workflows/android_ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v3 16 | - name: set up JDK 11 17 | uses: actions/setup-java@v3 18 | with: 19 | java-version: '11' 20 | distribution: 'temurin' 21 | cache: gradle 22 | 23 | - name: Grant execute permission for gradlew 24 | run: chmod +x gradlew 25 | - name: Build with Gradle 26 | run: ./gradlew build 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /.idea 4 | /local.properties 5 | /.idea/caches 6 | /.idea/libraries 7 | /.idea/modules.xml 8 | /.idea/workspace.xml 9 | /.idea/navEditor.xml 10 | /.idea/assetWizardSettings.xml 11 | .DS_Store 12 | /build 13 | /captures 14 | .externalNativeBuild 15 | .cxx 16 | local.properties 17 | gradle.properties -------------------------------------------------------------------------------- /CHANGE.md: -------------------------------------------------------------------------------- 1 | #### 更新历史 2 | 3 | --- 4 | - 1.0.5 5 | 兼容`ActivityResultLauncher.launch(I input, ActivityOptionsCompat options)`, 支持Activity启动时配置`ActivityOptionsCompat` 6 | 7 | - 1.0.4 8 | 修复系统配置变更(如屏幕旋转)导致`Activity重建`后回调被清空的问题 9 | 10 | - 1.0.3 11 | 移除引用`androidx.activity:activity-ktx:1.3.1`后导致需要`minCompileSdk (30)`的问题 12 | 13 | - 1.0.2 14 | `IManageStartActivity`的lifecycleHost从param改为receiver 15 | 16 | - 1.0.1 17 | 修复`META-INF/library_release.kotlin_module`错误 18 | 19 | - 1.0.0 20 | 发布`ManageStartActivity` -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 TxcA 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 all 13 | 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 THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![CI](https://github.com/TxcA/ManageStartActivity/workflows/CI/badge.svg?branch=master&event=push)](https://github.com/TxcA/SpannableX/actions) 2 | [![MavenCentral](https://img.shields.io/maven-central/v/com.itxca.msa/msa)](https://search.maven.org/artifact/com.itxca.msa/msa) 3 | [![API](https://img.shields.io/badge/API-14%2B-blue.svg?style=flat)](https://android-arsenal.com/api?level=14) 4 | [![License](http://img.shields.io/badge/License-MIT-brightgreen.svg?style=flat)](https://opensource.org/licenses/MIT) 5 | 6 | ![logo](screenshots/logo.png) 7 | 8 | ## ManageStartActivity [MSA] 9 | 10 | ActivityResultLauncher辅助类,一行代码解决~~startActivityForResult~~过时问题。 11 | 12 | 基于`registerForActivityResult`实现,满足日常使用需求。 13 | 14 | > 由于使用到了委托,所以只支持Kotlin,Java无法使用(或者说是没有使用的意义,变成了普通的方法调用) 15 | 16 | Android提供的`ActivityResultContracts`默认协定完整sample [ActivityResultContractsActivity](https://github.com/TxcA/ManageStartActivity/blob/master/app/src/main/java/com/itxca/sample/msa/ActivityResultContractsActivity.kt) 17 | 18 | --- 19 | ### 环境依赖 20 | 21 | root gradle: 22 | ```groovy 23 | allprojects { 24 | repositories { 25 | // release依赖仓库(4.1后as默认配置有) 26 | mavenCentral() 27 | } 28 | } 29 | ``` 30 | 31 | project dependencies: 32 | ```groovy 33 | dependencies { 34 | // need androidx appcompat 35 | implementation 'androidx.appcompat:appcompat:1.3.1' 36 | 37 | // Manage start activity 38 | implementation 'com.itxca.msa:msa:1.0.5' 39 | } 40 | ``` 41 | 42 | --- 43 | ### 文档 44 | 45 | - **1.0.4 非兼容升级, 现在必须在`super.onCreate()`后初始化 `initManageStartActivity` - 详情: [#1](https://github.com/TxcA/ManageStartActivity/issues/1)** 46 | 47 | #### 扩展简写说明 48 | 49 | 可以写`IManageStartActivity by ManageStartActivity() `, 也可以使用`IMsa by msa()`。完全一样,只是实现了一个简写。 50 | 51 | ```kotlin 52 | // IMsa = IManageStartActivity 53 | typealias IMsa = IManageStartActivity 54 | 55 | // msa() = ManageStartActivity() 56 | fun msa(): ManageStartActivity = ManageStartActivity() 57 | ``` 58 | 59 | #### 推荐基于Base类,方便统一管理。 60 | 61 | ```kotlin 62 | // 注意实现 IMsa by msa() 委托 63 | abstract class BaseActivity : AppCompatActivity(), IMsa by msa() { 64 | override fun onCreate(savedInstanceState: Bundle?) { 65 | super.onCreate(savedInstanceState) 66 | // 从 1.0.4 开始, 必须在`super.onCreate()`后初始化 `initManageStartActivity` 67 | // 因为需要使用 `SavedStateRegistry` 来保存回调状态 68 | initManageStartActivity() 69 | } 70 | } 71 | ``` 72 | 73 | ```kotlin 74 | // 注意实现 IMsa by msa() 委托 75 | abstract class BaseFragment : Fragment(), IMsa by msa() { 76 | override fun onCreate(savedInstanceState: Bundle?) { 77 | super.onCreate(savedInstanceState) 78 | // 从 1.0.4 开始, 必须在`super.onCreate()`后初始化 `initManageStartActivity` 79 | // 因为需要使用 `SavedStateRegistry` 来保存回调状态 80 | initManageStartActivity() 81 | } 82 | } 83 | ``` 84 | 85 | #### SampleActivity 86 | 87 | 完整使用方法说明 88 | 89 | ```kotlin 90 | // 注意实现 `IMsa by msa()` 委托 91 | class SampleActivity : AppCompatActivity(), IMsa by msa() { 92 | 93 | override fun onCreate(savedInstanceState: Bundle?) { 94 | super.onCreate(savedInstanceState) 95 | // 从 1.0.4 开始, 必须在`super.onCreate()`后初始化 `initManageStartActivity` 96 | // 因为需要使用 `SavedStateRegistry` 来保存回调状态 97 | initManageStartActivity() 98 | } 99 | 100 | /** 101 | * 直接启动Activity 102 | * 3 种方式 103 | */ 104 | fun startActivity() { 105 | // Android习惯模式,传入KClass即可 106 | startActivity(MainActivity::class, { 107 | // 自定义返回 ActivityOptionsCompat, 详细请查看README完整API说明 108 | null 109 | }) { 110 | putExtra("key", "value") 111 | } 112 | 113 | // KClass扩展方法模式 114 | MainActivity::class.start { 115 | putExtra("key", "value") 116 | } 117 | 118 | // Intent扩展方法模式 119 | Intent(this, MainActivity::class.java).apply { 120 | putExtra("key", "value") 121 | }.start() 122 | } 123 | 124 | /** 125 | * 启动Activity并需要回调结果 126 | * 4 种方法 127 | */ 128 | fun startActivityForResult() { 129 | // Android习惯模式,传入KClass即可 130 | startActivityForResult(MainActivity::class, { 131 | putExtra("key", "value") 132 | }) { code: Int, data: Intent? -> 133 | // code = resultCode 134 | } 135 | 136 | // KClass扩展方法模式 137 | MainActivity::class.startForResult({ 138 | putExtra("key", "value") 139 | }) {code: Int, data: Intent? -> 140 | // code = resultCode 141 | } 142 | 143 | // Android习惯模式,传入Intent即可 144 | startActivityForResult(Intent(this, MainActivity::class.java).apply { 145 | putExtra("key", "value") 146 | }){ code: Int, data: Intent? -> 147 | // code = resultCode 148 | } 149 | 150 | // Intent扩展方法模式 151 | Intent(this, MainActivity::class.java).apply { 152 | putExtra("key", "value") 153 | }.startForResult { code: Int, data: Intent? -> 154 | // code = resultCode 155 | } 156 | } 157 | 158 | /** 159 | * Kotlin协程挂起函数 160 | * 4 种方式 161 | */ 162 | fun startActivityForResultCoroutine() { 163 | lifecycleScope.launch { 164 | // Android习惯模式,传入KClass即可 165 | val (code1: Int, data1: Intent?) = startActivityForResultSync(MainActivity::class) { 166 | putExtra("key", "value") 167 | } 168 | 169 | // KClass扩展方法模式 170 | val (code2: Int, data2: Intent?) = MainActivity::class.startForResultSync { 171 | putExtra("key", "value") 172 | } 173 | 174 | // Android习惯模式,传入Intent即可 175 | val (code3: Int, data3: Intent?) = startActivityForResultSync(Intent(this@SampleActivity, MainActivity::class.java).apply { 176 | putExtra("key", "value") 177 | }) 178 | 179 | // Intent扩展方法模式 180 | val (code4: Int, data4: Intent?) = Intent(this@SampleActivity, MainActivity::class.java).apply { 181 | putExtra("key", "value") 182 | }.startForResultSync() 183 | } 184 | } 185 | } 186 | ``` 187 | 188 | ### 完整API 189 | ```kotlin 190 | // startActivityForResult 191 | fun startActivityForResult(target: KClass, block: Intent.() -> Unit = {}, options: () -> ActivityOptionsCompat? = { null }, result: StartActivityResult) 192 | fun > T.startForResult(block: Intent.() -> Unit = {}, options: () -> ActivityOptionsCompat? = { null }, result: StartActivityResult) 193 | suspend fun startActivityForResultSync(target: KClass, options: () -> ActivityOptionsCompat? = { null }, block: Intent.() -> Unit = {}): Result 194 | suspend fun > T.startForResultSync(options: () -> ActivityOptionsCompat? = { null }, block: Intent.() -> Unit = {}): Result 195 | 196 | // intent startActivityForResult 197 | fun startActivityForResult(intent: Intent, options: () -> ActivityOptionsCompat? = { null }, result: StartActivityResult) 198 | fun Intent.startForResult(options: () -> ActivityOptionsCompat? = { null }, result: StartActivityResult) 199 | suspend fun startActivityForResultSync(intent: Intent, options: () -> ActivityOptionsCompat? = { null }): Result 200 | suspend fun Intent.startForResultSync(options: () -> ActivityOptionsCompat? = { null }): Result 201 | 202 | // startActivity 203 | fun startActivity(target: KClass, options: () -> ActivityOptionsCompat? = { null }, block: Intent.() -> Unit = {}) 204 | fun > T.start(options: () -> ActivityOptionsCompat? = { null }, block: Intent.() -> Unit = {}) 205 | fun Intent.start(options: () -> ActivityOptionsCompat? = { null }) 206 | ``` 207 | 208 | --- 209 | ### Change 210 | [更新历史](CHANGE.md) 211 | 212 | --- 213 | ### Screenshots 214 | 215 | ![screenshot1](screenshots/msa.gif) 216 | ![screenshot2](screenshots/1.png) 217 | 218 | ****** 219 | ![screenshot3](screenshots/2.png) 220 | ![screenshot4](screenshots/3.png) 221 | 222 | --- 223 | ### License 224 | 225 | ``` 226 | MIT License 227 | 228 | Copyright (c) 2021 TxcA 229 | 230 | Permission is hereby granted, free of charge, to any person obtaining a copy 231 | of this software and associated documentation files (the "Software"), to deal 232 | in the Software without restriction, including without limitation the rights 233 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 234 | copies of the Software, and to permit persons to whom the Software is 235 | furnished to do so, subject to the following conditions: 236 | 237 | The above copyright notice and this permission notice shall be included in all 238 | copies or substantial portions of the Software. 239 | 240 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 241 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 242 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 243 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 244 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 245 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 246 | SOFTWARE. 247 | ``` -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'kotlin-android' 4 | id 'kotlin-kapt' 5 | } 6 | 7 | android { 8 | compileSdk 30 9 | 10 | defaultConfig { 11 | applicationId "com.itxca.sample.msa" 12 | minSdk 21 13 | targetSdk 30 14 | versionCode 1 15 | versionName "1.0" 16 | 17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 18 | } 19 | 20 | buildFeatures{ 21 | viewBinding true 22 | } 23 | 24 | buildTypes { 25 | release { 26 | minifyEnabled false 27 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 28 | } 29 | } 30 | compileOptions { 31 | sourceCompatibility JavaVersion.VERSION_1_8 32 | targetCompatibility JavaVersion.VERSION_1_8 33 | } 34 | kotlinOptions { 35 | jvmTarget = '1.8' 36 | } 37 | } 38 | 39 | dependencies { 40 | implementation 'androidx.core:core-ktx:1.6.0' 41 | implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' 42 | implementation 'androidx.activity:activity-ktx:1.3.1' 43 | implementation 'androidx.fragment:fragment-ktx:1.3.6' 44 | implementation 'androidx.appcompat:appcompat:1.3.1' 45 | implementation 'com.google.android.material:material:1.4.0' 46 | testImplementation 'junit:junit:4.13.2' 47 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 48 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 49 | 50 | implementation project(':library') 51 | } -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /app/src/androidTest/java/com/itxca/sample/msa/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.itxca.sample.msa 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.itxca.sample.msa", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | 11 | 12 | 13 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/itxca/sample/msa/ActivityResultContractsActivity.kt: -------------------------------------------------------------------------------- 1 | package com.itxca.sample.msa 2 | 3 | import android.Manifest 4 | import android.annotation.SuppressLint 5 | import android.content.Intent 6 | import android.net.Uri 7 | import android.os.Bundle 8 | import android.os.StrictMode 9 | import android.os.StrictMode.VmPolicy 10 | import android.widget.Toast 11 | import androidx.activity.result.contract.ActivityResultContract 12 | import androidx.activity.result.contract.ActivityResultContracts 13 | import androidx.appcompat.app.AppCompatActivity 14 | import androidx.lifecycle.lifecycleScope 15 | import com.itxca.sample.msa.databinding.ActivityActivityResultContractsBinding 16 | import kotlinx.coroutines.delay 17 | import kotlinx.coroutines.launch 18 | 19 | 20 | /** 21 | * 22 | * Project Name : ManageStartActivity 23 | * Package Name : com.itxca.sample.msa 24 | * Create Time : 2021-10-22 23:31 25 | * Create By : @author xIao 26 | * Version : 1.0.0 27 | * 28 | **/ 29 | 30 | class ActivityResultContractsActivity : AppCompatActivity() { 31 | 32 | private val viewBinding by lazy { ActivityActivityResultContractsBinding.inflate(layoutInflater) } 33 | 34 | // 35 | private val videoSaveUri by lazy { Uri.fromFile(externalCacheDir?.resolve("video.mp4")) } 36 | private val imageSaveUri by lazy { Uri.fromFile(externalCacheDir?.resolve("image.png")) } 37 | // 38 | 39 | // 40 | /** 41 | * 自定义Contract 42 | */ 43 | private val launcherContractActivity = 44 | ContractActivity.Contract().let { contract -> 45 | registerForActivityResult(contract) { 46 | contract.updateLog("result: $it") 47 | } 48 | } 49 | 50 | /** 51 | * 可以理解为 [startActivityForResult] 52 | */ 53 | private val launcherStartActivityForResult = 54 | ActivityResultContracts.StartActivityForResult().let { contract -> 55 | registerForActivityResult(contract) { 56 | contract.updateLog(it.toString()) 57 | } 58 | } 59 | 60 | /** 61 | * 通过`MediaStore.ACTION_IMAGE_CAPTURE`拍照并保存 62 | */ 63 | private val launcherTakePicture = 64 | ActivityResultContracts.TakePicture().let { contract -> 65 | registerForActivityResult(contract) { 66 | contract.updateLog("saveSuccess: $it") 67 | } 68 | } 69 | 70 | /** 71 | * 通过`MediaStore.ACTION_IMAGE_CAPTURE`拍照 72 | */ 73 | private val launcherTakePicturePreview = 74 | ActivityResultContracts.TakePicturePreview().let { contract -> 75 | registerForActivityResult(contract) { 76 | contract.updateLog("bitmap : $it") 77 | } 78 | } 79 | 80 | /** 81 | * 通过`MediaStore.ACTION_VIDEO_CAPTURE`拍摄视频并保存 82 | * androidx.activity 1.3.0-alpha08后提供,androidx.appcompat好像还没提供该类 83 | */ 84 | private val launcherCaptureVideo = 85 | ActivityResultContracts.CaptureVideo().let { contract -> 86 | registerForActivityResult(contract) { 87 | contract.updateLog("saveSuccess: $it") 88 | } 89 | } 90 | 91 | /** 92 | * 请求单个权限 93 | */ 94 | private val launcherRequestPermission = 95 | ActivityResultContracts.RequestPermission().let { contract -> 96 | registerForActivityResult(contract) { 97 | contract.updateLog("grant: $it") 98 | } 99 | } 100 | 101 | /** 102 | * 请求多个权限 103 | */ 104 | private val launcherRequestMultiplePermissions = 105 | ActivityResultContracts.RequestMultiplePermissions().let { contract -> 106 | registerForActivityResult(contract) { 107 | contract.updateLog(it.map { result -> "${result.key} - ${result.value}" } 108 | .joinToString()) 109 | } 110 | } 111 | 112 | /** 113 | * 通过`Intent.ACTION_CREATE_DOCUMENT`创建一个文件 114 | */ 115 | private val launcherCreateDocument = 116 | ActivityResultContracts.CreateDocument().let { contract -> 117 | registerForActivityResult(contract) { 118 | contract.updateLog("uri= $it") 119 | } 120 | } 121 | 122 | /** 123 | * 通过`Intent.ACTION_GET_CONTENT`获取一个文件 124 | * 这个方法可以通过`android.content.ContentResolver.openInputStream`获取到文件的原始数据 125 | */ 126 | private val launcherGetContent = 127 | ActivityResultContracts.GetContent().let { contract -> 128 | registerForActivityResult(contract) { 129 | contract.updateLog("uri= $it, size= ${ 130 | contentResolver.openInputStream(it)?.use { input -> 131 | input.readBytes().size 132 | } ?: 0 133 | }") 134 | } 135 | } 136 | 137 | /** 138 | * 通过`Intent.ACTION_GET_CONTENT`及`Intent.EXTRA_ALLOW_MULTIPLE`获取一个或多个文件 139 | * 这个方法可以通过`android.content.ContentResolver.openInputStream`获取到文件的原始数据 140 | */ 141 | private val launcherGetMultipleContents = 142 | ActivityResultContracts.GetMultipleContents().let { contract -> 143 | registerForActivityResult(contract) { 144 | contract.updateLog(it.mapIndexed { index, uri -> 145 | "index= $index, uri: $it, size= ${ 146 | contentResolver.openInputStream(uri)?.use { input -> 147 | input.readBytes().size 148 | } ?: 0 149 | }" 150 | }.joinToString("\n")) 151 | } 152 | } 153 | 154 | /** 155 | * 通过`Intent.ACTION_OPEN_DOCUMENT`选择文件 156 | */ 157 | private val launcherOpenDocument = 158 | ActivityResultContracts.OpenDocument().let { contract -> 159 | registerForActivityResult(contract) { 160 | contract.updateLog("uri= $it") 161 | } 162 | } 163 | 164 | /** 165 | * 通过`Intent.ACTION_OPEN_DOCUMENT_TREE`选择一个目录,返回一个Uri并得到该目录下全部文档的管理权 166 | */ 167 | private val launcherOpenDocumentTree = 168 | ActivityResultContracts.OpenDocumentTree().let { contract -> 169 | registerForActivityResult(contract) { 170 | contract.updateLog("uri= $it") 171 | } 172 | } 173 | 174 | /** 175 | * 通过`Intent.ACTION_OPEN_DOCUMENT`及`Intent.EXTRA_ALLOW_MULTIPLE`获取一个或多个文件 176 | */ 177 | private val launcherOpenMultipleDocuments = 178 | ActivityResultContracts.OpenMultipleDocuments().let { contract -> 179 | registerForActivityResult(contract) { 180 | contract.updateLog(it.mapIndexed { index, uri -> 181 | "index= $index, uri: $it, size= ${ 182 | contentResolver.openInputStream(uri)?.use { input -> 183 | input.readBytes().size 184 | } ?: 0 185 | }" 186 | }.joinToString("\n")) 187 | } 188 | } 189 | 190 | /** 191 | * 通过`Intent.ACTION_PICK`从系统通讯录中获取联系人 192 | */ 193 | private val launcherPickContact = 194 | ActivityResultContracts.PickContact().let { contract -> 195 | registerForActivityResult(contract) { 196 | contract.updateLog("uri= $it") 197 | } 198 | } 199 | 200 | /** 201 | * 通过`MediaStore.ACTION_VIDEO_CAPTURE`拍摄视频并保存 202 | * 弃用了,官方解释是缩略图的Bitmap的返回不稳定,替换方法为[ActivityResultContracts.CaptureVideo] 203 | */ 204 | @Deprecated("已被标记为弃用") 205 | private val launcherTakeVideo = 206 | ActivityResultContracts.TakeVideo().let { contract -> 207 | registerForActivityResult(contract) { 208 | contract.updateLog("thumbnail bitmap= $it") 209 | } 210 | } 211 | // 212 | 213 | override fun onCreate(savedInstanceState: Bundle?) { 214 | super.onCreate(savedInstanceState) 215 | setContentView(viewBinding.root) 216 | VmPolicy.Builder().run { 217 | StrictMode.setVmPolicy(build()) 218 | detectFileUriExposure() 219 | } 220 | title = this::class.java.simpleName 221 | initView() 222 | } 223 | 224 | private fun initView() { 225 | /** 该方法会抛出异常,原因为必须在`STARTED`前调用`registerForActivityResult `*/ 226 | viewBinding.btnStartActivityForResultError.setOnClickListener { 227 | lifecycleScope.launch { 228 | Toast.makeText(this@ActivityResultContractsActivity, "该方法会抛出`IllegalStateException`异常", Toast.LENGTH_LONG) 229 | .show() 230 | delay(500L) 231 | registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { 232 | }.launch(Intent(this@ActivityResultContractsActivity, SecondActivity::class.java)) 233 | } 234 | } 235 | 236 | viewBinding.btnContractActivity.setOnClickListener { 237 | launcherContractActivity.launch("hi, Activity Result API!") 238 | } 239 | 240 | viewBinding.btnStartActivityForResult.setOnClickListener { 241 | launcherStartActivityForResult.launch(Intent(this@ActivityResultContractsActivity, SecondActivity::class.java)) 242 | } 243 | 244 | viewBinding.btnTakePicture.setOnClickListener { 245 | launcherTakePicture.launch(imageSaveUri) 246 | } 247 | 248 | viewBinding.btnTakePicturePreview.setOnClickListener { 249 | launcherTakePicturePreview.launch(null) 250 | } 251 | 252 | viewBinding.btnCaptureVideo.setOnClickListener { 253 | launcherCaptureVideo.launch(videoSaveUri) 254 | } 255 | 256 | viewBinding.btnRequestPermission.setOnClickListener { 257 | launcherRequestPermission.launch(Manifest.permission.READ_PHONE_STATE) 258 | } 259 | 260 | viewBinding.btnRequestMultiplePermissions.setOnClickListener { 261 | launcherRequestMultiplePermissions.launch(arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.READ_EXTERNAL_STORAGE)) 262 | } 263 | 264 | viewBinding.btnCreateDocument.setOnClickListener { 265 | launcherCreateDocument.launch("ManageStartActivity.txt") 266 | } 267 | 268 | viewBinding.btnGetContent.setOnClickListener { 269 | launcherGetContent.launch(MIME_IMAGE) 270 | } 271 | 272 | viewBinding.btnGetMultipleContents.setOnClickListener { 273 | launcherGetMultipleContents.launch(MIME_IMAGE) 274 | } 275 | 276 | viewBinding.btnOpenDocument.setOnClickListener { 277 | launcherOpenDocument.launch(arrayOf(MIME_IMAGE)) 278 | } 279 | 280 | viewBinding.btnOpenDocumentTree.setOnClickListener { 281 | launcherOpenDocumentTree.launch(null) 282 | } 283 | 284 | viewBinding.btnOpenMultipleDocuments.setOnClickListener { 285 | launcherOpenMultipleDocuments.launch(arrayOf(MIME_IMAGE)) 286 | } 287 | 288 | viewBinding.btnPickContact.setOnClickListener { 289 | launcherPickContact.launch(null) 290 | } 291 | 292 | viewBinding.btnTakeVideo.setOnClickListener { 293 | launcherTakeVideo.launch(videoSaveUri) 294 | } 295 | } 296 | 297 | @SuppressLint("SetTextI18n") 298 | private fun > T.updateLog(message: String) { 299 | viewBinding.tvLogcat.text = "[${this::class.java.simpleName}] $message" 300 | } 301 | 302 | companion object{ 303 | private const val MIME_IMAGE = "image/*" 304 | } 305 | } -------------------------------------------------------------------------------- /app/src/main/java/com/itxca/sample/msa/ContractActivity.kt: -------------------------------------------------------------------------------- 1 | package com.itxca.sample.msa 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.os.Bundle 7 | import android.view.View 8 | import androidx.activity.result.contract.ActivityResultContract 9 | import androidx.appcompat.app.AppCompatActivity 10 | import com.itxca.sample.msa.databinding.ActivitySecondBinding 11 | 12 | /** 13 | * 14 | * Project Name : ManageStartActivity 15 | * Package Name : com.itxca.sample.msa 16 | * Create Time : 2021-10-23 3:16 17 | * Create By : @author xIao 18 | * Version : 1.0.0 19 | * 20 | **/ 21 | 22 | class ContractActivity : AppCompatActivity() { 23 | 24 | private val viewBinding by lazy { 25 | ActivitySecondBinding.inflate(layoutInflater) 26 | } 27 | 28 | override fun onCreate(savedInstanceState: Bundle?) { 29 | super.onCreate(savedInstanceState) 30 | setContentView(viewBinding.root) 31 | title = this::class.java.simpleName 32 | 33 | viewBinding.finish.setOnClickListener(::finish) 34 | viewBinding.finishResultOk.setOnClickListener(::finishResultOk) 35 | 36 | loadIntentData() 37 | } 38 | 39 | private fun loadIntentData() { 40 | viewBinding.tvLogcat.text = intent?.getStringExtra(Contract.EXTRA_NAME) ?: "error" 41 | } 42 | 43 | private fun finish(v: View) { 44 | finish() 45 | } 46 | 47 | private fun finishResultOk(v: View) { 48 | setResult(RESULT_OK, Intent().apply { 49 | putExtra(Contract.EXTRA_RESULT, this@ContractActivity.extraMessage()) 50 | }) 51 | finish() 52 | } 53 | 54 | /** 55 | * 继承[ActivityResultContract] 56 | * 57 | * 泛型第一个参数为传入到 [ContractActivity] 的参数 58 | * 第二个参数为 [ContractActivity] 返回给启动Activity的返回值 59 | */ 60 | class Contract : ActivityResultContract() { 61 | /** 62 | * 创建启动Intent 63 | * @param context [Context] 64 | * @param input 当前类的第一个泛型参, 这里自己实现传递过程 65 | */ 66 | override fun createIntent(context: Context, input: String): Intent = 67 | Intent(context, ContractActivity::class.java).apply { 68 | putExtra(EXTRA_NAME, "$input - ${System.currentTimeMillis()}") 69 | } 70 | 71 | /** 72 | * 解析结果 73 | * @param resultCode [Activity.setResult] 的 resultCode 74 | * @param intent [Activity.setResult] 的 intent 75 | * 其实这里类似 [Activity.onActivityResult] 处理过程,只是由于返回是明确的,所以少了requestCode 76 | */ 77 | override fun parseResult(resultCode: Int, intent: Intent?): String { 78 | return if (resultCode == Activity.RESULT_OK && intent != null) { 79 | "Contract - ${intent.getStringExtra(EXTRA_RESULT)}" 80 | } else { 81 | "error" 82 | } 83 | } 84 | 85 | companion object { 86 | /** EXTRA */ 87 | const val EXTRA_NAME = "extra_name" 88 | const val EXTRA_RESULT = "extra_result" 89 | } 90 | } 91 | } -------------------------------------------------------------------------------- /app/src/main/java/com/itxca/sample/msa/FragmentActivity.kt: -------------------------------------------------------------------------------- 1 | package com.itxca.sample.msa 2 | 3 | import android.os.Bundle 4 | import com.google.android.material.tabs.TabLayoutMediator 5 | import com.itxca.sample.msa.base.BaseActivity 6 | import com.itxca.sample.msa.databinding.ActivityFragmentBinding 7 | 8 | /** 9 | * 10 | * Project Name : ManageStartActivity 11 | * Package Name : com.itxca.sample.msa 12 | * Create Time : 2021-09-23 16:06 13 | * Create By : @author xIao 14 | * Version : 1.0.0 15 | * 16 | **/ 17 | 18 | class FragmentActivity : BaseActivity() { 19 | private val viewBinding by lazy { ActivityFragmentBinding.inflate(layoutInflater) } 20 | 21 | private val titles = listOf("1", "2") 22 | 23 | override fun onCreate(savedInstanceState: Bundle?) { 24 | super.onCreate(savedInstanceState) 25 | setContentView(viewBinding.root) 26 | title = this::class.java.simpleName 27 | 28 | viewBinding.viewPager.adapter = FragmentAdapter( 29 | this, listOf( 30 | TestFragment.newInstance(titles[0]), 31 | TestFragment.newInstance(titles[1]) 32 | ) 33 | ) 34 | TabLayoutMediator(viewBinding.tab, viewBinding.viewPager) { tab, position -> 35 | tab.text = titles[position] 36 | }.attach() 37 | } 38 | } -------------------------------------------------------------------------------- /app/src/main/java/com/itxca/sample/msa/FragmentAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.itxca.sample.msa 2 | 3 | import androidx.fragment.app.Fragment 4 | import androidx.fragment.app.FragmentActivity 5 | import androidx.viewpager2.adapter.FragmentStateAdapter 6 | 7 | /** 8 | * 9 | * Project Name : ManageStartActivity 10 | * Package Name : com.itxca.sample.msa 11 | * Create Time : 2021-09-23 16:12 12 | * Create By : @author xIao 13 | * Version : 1.0.0 14 | * 15 | **/ 16 | 17 | class FragmentAdapter( 18 | fragmentActivity: FragmentActivity, 19 | private val fragments: List 20 | ) : FragmentStateAdapter(fragmentActivity) { 21 | override fun getItemCount(): Int = fragments.size 22 | override fun createFragment(position: Int): Fragment = fragments[position] 23 | } -------------------------------------------------------------------------------- /app/src/main/java/com/itxca/sample/msa/LogViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.itxca.sample.msa 2 | 3 | import android.content.Intent 4 | import androidx.activity.result.ActivityResult 5 | import androidx.lifecycle.MutableLiveData 6 | import androidx.lifecycle.ViewModel 7 | 8 | /** 9 | * 10 | * Project Name : ManageStartActivity 11 | * Package Name : com.itxca.sample.msa 12 | * Create Time : 2021-11-02 16:24 13 | * Create By : @author xIao 14 | * Version : 1.0.0 15 | * 16 | **/ 17 | 18 | class LogViewModel : ViewModel() { 19 | 20 | /** 21 | * 通过 ViewModel+ LiveData 兼容 Activity 在屏幕旋转旋转、语言切换等情况下重建恢复 22 | */ 23 | val logString = MutableLiveData() 24 | 25 | fun printLog(code: Int, data: Intent?) { 26 | logString.value = ActivityResult(code, data) 27 | } 28 | } -------------------------------------------------------------------------------- /app/src/main/java/com/itxca/sample/msa/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.itxca.sample.msa 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Intent 5 | import android.graphics.BitmapFactory 6 | import android.os.Bundle 7 | import android.view.View 8 | import android.widget.Toast 9 | import androidx.activity.viewModels 10 | import androidx.core.app.ActivityOptionsCompat 11 | import androidx.lifecycle.lifecycleScope 12 | import com.itxca.sample.msa.base.BaseActivity 13 | import com.itxca.sample.msa.databinding.ActivityMainBinding 14 | import kotlinx.coroutines.launch 15 | 16 | @SuppressLint("SetTextI18n") 17 | class MainActivity : BaseActivity() { 18 | private val viewBinding by lazy { ActivityMainBinding.inflate(layoutInflater) } 19 | private val viewModel: LogViewModel by viewModels() 20 | 21 | override fun onCreate(savedInstanceState: Bundle?) { 22 | super.onCreate(savedInstanceState) 23 | setContentView(viewBinding.root) 24 | 25 | viewBinding.startActivityTarget.setOnClickListener(::startActivityTarget) 26 | viewBinding.startActivityForResultTarget.setOnClickListener(::startActivityForResultTarget) 27 | viewBinding.startActivityForResultIntent.setOnClickListener(::startActivityForResultIntent) 28 | viewBinding.startActivityForResultSyncTarget.setOnClickListener(::startActivityForResultSyncTarget) 29 | viewBinding.startActivityForResultSyncIntent.setOnClickListener(::startActivityForResultSyncIntent) 30 | viewBinding.startTestFragment.setOnClickListener(::startTestFragment) 31 | viewBinding.startActivityResultContracts.setOnClickListener { 32 | startActivity(ActivityResultContractsActivity::class) 33 | } 34 | 35 | viewModel.logString.observe(this) { result -> 36 | result ?: return@observe 37 | showCodeData(result.resultCode, result.data) 38 | } 39 | } 40 | 41 | private fun startActivityTarget(v: View) { 42 | startActivity(SecondActivity::class, { v.randomOptionsCompat }) { 43 | putExtra(SecondActivity.EXTRA_DATA, this@MainActivity.extraMessage()) 44 | } 45 | } 46 | 47 | private fun startActivityForResultTarget(v: View) { 48 | viewBinding.tvLogcat.text = "" 49 | startActivityForResult(SecondActivity::class, { 50 | putExtra(SecondActivity.EXTRA_DATA, this@MainActivity.extraMessage()) 51 | }, { v.randomOptionsCompat }) { code, data -> 52 | viewModel.printLog(code, data) 53 | } 54 | } 55 | 56 | private fun startActivityForResultIntent(v: View) { 57 | viewBinding.tvLogcat.text = "" 58 | startActivityForResult(SecondActivity.buildIntent(this, extraMessage()), 59 | { v.randomOptionsCompat }) { code, data -> 60 | viewModel.printLog(code, data) 61 | } 62 | } 63 | 64 | private fun startActivityForResultSyncTarget(v: View) { 65 | viewBinding.tvLogcat.text = "" 66 | lifecycleScope.launch { 67 | val (code, data) = startActivityForResultSync(SecondActivity::class, 68 | { v.randomOptionsCompat }) { 69 | putExtra(SecondActivity.EXTRA_DATA, this@MainActivity.extraMessage()) 70 | } 71 | viewModel.printLog(code, data) 72 | } 73 | } 74 | 75 | private fun startActivityForResultSyncIntent(v: View) { 76 | viewBinding.tvLogcat.text = "" 77 | lifecycleScope.launch { 78 | val (code, data) = startActivityForResultSync( 79 | SecondActivity.buildIntent(this@MainActivity, extraMessage()) 80 | ){ v.randomOptionsCompat } 81 | viewModel.printLog(code, data) 82 | } 83 | } 84 | 85 | private fun startTestFragment(v: View) { 86 | viewBinding.tvLogcat.text = "" 87 | startActivityForResult(FragmentActivity::class, 88 | options = { v.randomOptionsCompat }) { code, data -> 89 | viewModel.printLog(code, data) 90 | } 91 | } 92 | 93 | private fun showCodeData(code: Int, data: Intent?) { 94 | viewBinding.tvLogcat.text = """ 95 | code: $code 96 | data: ${data?.getStringExtra(SecondActivity.EXTRA_DATA)} 97 | """.trimIndent() 98 | } 99 | 100 | private fun String.toast() { 101 | Toast.makeText(this@MainActivity, this, Toast.LENGTH_SHORT).show() 102 | } 103 | 104 | private val View.randomOptionsCompat: ActivityOptionsCompat? 105 | get() = when ((0..6).random()) { 106 | 0 -> { 107 | "Custom animation".toast() 108 | ActivityOptionsCompat.makeCustomAnimation(this@MainActivity, R.anim.bottom_in, R.anim.bottom_out) 109 | } 110 | 1 -> { 111 | "ScaleUp animation".toast() 112 | ActivityOptionsCompat.makeScaleUpAnimation(this, height / 2, width / 2, 0, 0) 113 | } 114 | 2 -> { 115 | "ClipReveal animation".toast() 116 | ActivityOptionsCompat.makeClipRevealAnimation(this, height / 2, width / 2, 0, 0) 117 | } 118 | 3 -> { 119 | "ThumbnailScaleUp animation".toast() 120 | ActivityOptionsCompat.makeThumbnailScaleUpAnimation(this, BitmapFactory.decodeResource(resources,R.mipmap.logo), height / 2, width / 2) 121 | } 122 | else -> { 123 | "No animation".toast() 124 | null 125 | } 126 | } 127 | } -------------------------------------------------------------------------------- /app/src/main/java/com/itxca/sample/msa/SampleActivity.kt: -------------------------------------------------------------------------------- 1 | package com.itxca.sample.msa 2 | 3 | import android.content.Intent 4 | import android.os.Bundle 5 | import androidx.appcompat.app.AppCompatActivity 6 | import androidx.lifecycle.lifecycleScope 7 | import com.itxca.msa.IMsa 8 | import com.itxca.msa.msa 9 | import kotlinx.coroutines.launch 10 | 11 | /** 12 | * 13 | * Project Name : ManageStartActivity 14 | * Package Name : com.itxca.sample.msa 15 | * Create Time : 2021-09-23 21:29 16 | * Create By : @author xIao 17 | * Version : 1.0.0 18 | * 19 | **/ 20 | 21 | // 注意实现 `IMsa by msa()` 委托 22 | @Suppress("unused") 23 | class SampleActivity : AppCompatActivity(), IMsa by msa() { 24 | 25 | override fun onCreate(savedInstanceState: Bundle?) { 26 | super.onCreate(savedInstanceState) 27 | // 从1.0.4开始, 必须在super.onCreate()后初始化 initManageStartActivity 28 | // 因为需要使用 SavedStateRegistry 来保存回调状态 29 | initManageStartActivity() 30 | } 31 | 32 | /** 33 | * 直接启动Activity 34 | * 3 种方式 35 | */ 36 | fun startActivity() { 37 | // Android习惯模式,传入KClass即可 38 | startActivity(MainActivity::class, { 39 | // 自定义返回 ActivityOptionsCompat, 详细请查看README完整API说明 40 | null 41 | }) { 42 | putExtra("key", "value") 43 | } 44 | 45 | // KClass扩展方法模式 46 | MainActivity::class.start { 47 | putExtra("key", "value") 48 | } 49 | 50 | // Intent扩展方法模式 51 | Intent(this, MainActivity::class.java).apply { 52 | putExtra("key", "value") 53 | }.start() 54 | } 55 | 56 | /** 57 | * 启动Activity并需要回调结果 58 | * 4 种方法 59 | */ 60 | fun startActivityForResult() { 61 | // Android习惯模式,传入KClass即可 62 | startActivityForResult(MainActivity::class, { 63 | putExtra("key", "value") 64 | }) { code: Int, data: Intent? -> 65 | // code = resultCode 66 | } 67 | 68 | // KClass扩展方法模式 69 | MainActivity::class.startForResult({ 70 | putExtra("key", "value") 71 | }) {code: Int, data: Intent? -> 72 | // code = resultCode 73 | } 74 | 75 | // Android习惯模式,传入Intent即可 76 | startActivityForResult(Intent(this, MainActivity::class.java).apply { 77 | putExtra("key", "value") 78 | }){ code: Int, data: Intent? -> 79 | // code = resultCode 80 | } 81 | 82 | // Intent扩展方法模式 83 | Intent(this, MainActivity::class.java).apply { 84 | putExtra("key", "value") 85 | }.startForResult { code: Int, data: Intent? -> 86 | // code = resultCode 87 | } 88 | } 89 | 90 | /** 91 | * Kotlin协程挂起函数 92 | * 4 种方式 93 | */ 94 | fun startActivityForResultCoroutine() { 95 | lifecycleScope.launch { 96 | // Android习惯模式,传入KClass即可 97 | val (code1: Int, data1: Intent?) = startActivityForResultSync(MainActivity::class) { 98 | putExtra("key", "value") 99 | } 100 | 101 | // KClass扩展方法模式 102 | val (code2: Int, data2: Intent?) = MainActivity::class.startForResultSync { 103 | putExtra("key", "value") 104 | } 105 | 106 | // Android习惯模式,传入Intent即可 107 | val (code3: Int, data3: Intent?) = startActivityForResultSync(Intent(this@SampleActivity, MainActivity::class.java).apply { 108 | putExtra("key", "value") 109 | }) 110 | 111 | // Intent扩展方法模式 112 | val (code4: Int, data4: Intent?) = Intent(this@SampleActivity, MainActivity::class.java).apply { 113 | putExtra("key", "value") 114 | }.startForResultSync() 115 | } 116 | } 117 | } -------------------------------------------------------------------------------- /app/src/main/java/com/itxca/sample/msa/SecondActivity.kt: -------------------------------------------------------------------------------- 1 | package com.itxca.sample.msa 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import android.os.Bundle 6 | import android.view.View 7 | import com.itxca.sample.msa.base.BaseActivity 8 | import com.itxca.sample.msa.databinding.ActivitySecondBinding 9 | 10 | /** 11 | * 12 | * Project Name : ManageStartActivity 13 | * Package Name : com.itxca.sample.msa 14 | * Create Time : 2021-09-23 14:56 15 | * Create By : @author xIao 16 | * Version : 1.0.0 17 | * 18 | **/ 19 | 20 | class SecondActivity : BaseActivity() { 21 | private val viewBinding by lazy { 22 | ActivitySecondBinding.inflate(layoutInflater) 23 | } 24 | 25 | override fun onCreate(savedInstanceState: Bundle?) { 26 | super.onCreate(savedInstanceState) 27 | setContentView(viewBinding.root) 28 | title = this::class.java.simpleName 29 | 30 | viewBinding.finish.setOnClickListener(::finish) 31 | viewBinding.finishResultOk.setOnClickListener(::finishResultOk) 32 | 33 | loadIntentData() 34 | } 35 | 36 | private fun loadIntentData() { 37 | intent?.getStringExtra(EXTRA_DATA)?.let { 38 | viewBinding.tvLogcat.text = it 39 | } 40 | } 41 | 42 | private fun finish(v: View) { 43 | finish() 44 | } 45 | 46 | private fun finishResultOk(v: View) { 47 | setResult(RESULT_OK, Intent().apply { 48 | putExtra(EXTRA_DATA, this@SecondActivity.extraMessage()) 49 | }) 50 | finish() 51 | } 52 | 53 | companion object { 54 | const val EXTRA_DATA = "extra_data" 55 | 56 | fun buildIntent(context: Context, data: String): Intent = 57 | Intent(context, SecondActivity::class.java).also { intent -> 58 | intent.putExtra(EXTRA_DATA, data) 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /app/src/main/java/com/itxca/sample/msa/TestFragment.kt: -------------------------------------------------------------------------------- 1 | package com.itxca.sample.msa 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Intent 5 | import android.os.Bundle 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import androidx.appcompat.app.AppCompatActivity 10 | import androidx.fragment.app.viewModels 11 | import com.itxca.sample.msa.base.BaseFragment 12 | import com.itxca.sample.msa.databinding.FragmentTestBinding 13 | 14 | /** 15 | * 16 | * Project Name : ManageStartActivity 17 | * Package Name : com.itxca.sample.msa 18 | * Create Time : 2021-09-23 16:12 19 | * Create By : @author xIao 20 | * Version : 1.0.0 21 | * 22 | **/ 23 | @SuppressLint("SetTextI18n") 24 | class TestFragment : BaseFragment() { 25 | 26 | private lateinit var viewBinding: FragmentTestBinding 27 | private val viewModel: LogViewModel by viewModels() 28 | 29 | override fun onCreateView( 30 | inflater: LayoutInflater, 31 | container: ViewGroup?, 32 | savedInstanceState: Bundle? 33 | ): View = FragmentTestBinding.inflate(inflater, container, false) 34 | .also { vb -> viewBinding = vb }.root 35 | 36 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 37 | super.onViewCreated(view, savedInstanceState) 38 | 39 | viewBinding.tvFlag.text = arguments?.getString(FLAG_DATA) ?: "null" 40 | 41 | viewBinding.finish.setOnClickListener(::finish) 42 | viewBinding.finishResultOk.setOnClickListener(::finishResultOk) 43 | viewBinding.startActivityForResultIntent.setOnClickListener(::startActivityForResultIntent) 44 | 45 | viewModel.logString.observe(viewLifecycleOwner){ result -> 46 | result ?: return@observe 47 | viewBinding.tvLogcat.text = """ 48 | code: ${result.resultCode} 49 | data: ${result.data?.getStringExtra(SecondActivity.EXTRA_DATA)} 50 | """.trimIndent() 51 | } 52 | } 53 | 54 | private fun finish(v: View) { 55 | requireActivity().finish() 56 | } 57 | 58 | private fun finishResultOk(v: View) { 59 | requireActivity().setResult(AppCompatActivity.RESULT_OK, Intent().apply { 60 | putExtra(SecondActivity.EXTRA_DATA, this@TestFragment.extraMessage()) 61 | }) 62 | requireActivity().finish() 63 | } 64 | 65 | private fun startActivityForResultIntent(v: View) { 66 | viewBinding.tvLogcat.text = "" 67 | startActivityForResult(SecondActivity.buildIntent(requireContext(), extraMessage())) 68 | { code, data -> 69 | viewModel.printLog(code, data) 70 | } 71 | } 72 | 73 | companion object { 74 | private const val FLAG_DATA = "flag_data" 75 | 76 | fun newInstance(flag: String): TestFragment = 77 | TestFragment().also { fm -> 78 | fm.arguments = Bundle().apply { putString(FLAG_DATA, flag) } 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /app/src/main/java/com/itxca/sample/msa/Utils.kt: -------------------------------------------------------------------------------- 1 | package com.itxca.sample.msa 2 | 3 | /** 4 | * 5 | * Project Name : ManageStartActivity 6 | * Package Name : com.itxca.sample.msa 7 | * Create Time : 2021-09-23 16:20 8 | * Create By : @author xIao 9 | * Version : 1.0.0 10 | * 11 | **/ 12 | 13 | fun Any.extraMessage(): String = "${this::class.java.name}: ${System.currentTimeMillis()}" 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/itxca/sample/msa/base/BaseActivity.kt: -------------------------------------------------------------------------------- 1 | package com.itxca.sample.msa.base 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatActivity 5 | import com.itxca.msa.IMsa 6 | import com.itxca.msa.msa 7 | 8 | /** 9 | * 10 | * Project Name : ManageStartActivity 11 | * Package Name : com.itxca.sample.msa.base 12 | * Create Time : 2021-09-23 14:40 13 | * Create By : @author xIao 14 | * Version : 1.0.0 15 | * 16 | **/ 17 | 18 | abstract class BaseActivity : AppCompatActivity(), IMsa by msa() { 19 | override fun onCreate(savedInstanceState: Bundle?) { 20 | super.onCreate(savedInstanceState) 21 | initManageStartActivity() 22 | } 23 | } -------------------------------------------------------------------------------- /app/src/main/java/com/itxca/sample/msa/base/BaseFragment.kt: -------------------------------------------------------------------------------- 1 | package com.itxca.sample.msa.base 2 | 3 | import android.os.Bundle 4 | import androidx.fragment.app.Fragment 5 | import com.itxca.msa.IMsa 6 | import com.itxca.msa.msa 7 | 8 | /** 9 | * 10 | * Project Name : ManageStartActivity 11 | * Package Name : com.itxca.sample.msa.base 12 | * Create Time : 2021-09-23 14:42 13 | * Create By : @author xIao 14 | * Version : 1.0.0 15 | * 16 | **/ 17 | 18 | 19 | abstract class BaseFragment : Fragment(), IMsa by msa() { 20 | override fun onCreate(savedInstanceState: Bundle?) { 21 | super.onCreate(savedInstanceState) 22 | initManageStartActivity() 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/res/anim/bottom_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/anim/bottom_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_activity_result_contracts.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 20 | 21 | 24 | 25 | 29 | 30 |