Repository: finogeeks/finclip-android-demo Branch: master Commit: 2ae719e03e73 Files: 53 Total size: 85.8 KB Directory structure: gitextract_95_p49xo/ ├── .github/ │ └── workflows/ │ ├── issue.yml │ └── pull_request.yml ├── .gitignore ├── .idea/ │ ├── .name │ ├── codeStyles/ │ │ └── Project.xml │ ├── compiler.xml │ ├── deploymentTargetDropDown.xml │ ├── deploymentTargetSelector.xml │ ├── gradle.xml │ ├── jarRepositories.xml │ ├── kotlinc.xml │ ├── migrations.xml │ ├── misc.xml │ ├── runConfigurations.xml │ └── vcs.xml ├── README.md ├── app/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ ├── release/ │ │ └── app-release.apk │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── finogeeks/ │ │ └── mop/ │ │ └── demo/ │ │ └── ExampleInstrumentedTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── finogeeks/ │ │ │ └── mop/ │ │ │ └── demo/ │ │ │ ├── AppletHandler.java │ │ │ ├── InputContentActivity.java │ │ │ ├── MainActivity.java │ │ │ ├── MopApplication.java │ │ │ ├── ScanQRCodeActivity.kt │ │ │ ├── ScanStartAppletActivity.kt │ │ │ └── customapi/ │ │ │ ├── CustomApi.java │ │ │ ├── CustomH5Api.java │ │ │ └── user/ │ │ │ ├── LoginApi.java │ │ │ └── ProfileApi.java │ │ └── res/ │ │ ├── drawable/ │ │ │ └── ic_launcher_background.xml │ │ ├── drawable-v24/ │ │ │ └── ic_launcher_foreground.xml │ │ ├── layout/ │ │ │ ├── activity_input_content.xml │ │ │ ├── activity_main.xml │ │ │ ├── activity_scan_qr_code.xml │ │ │ └── activity_scan_start_applet.xml │ │ ├── mipmap-anydpi-v26/ │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ └── values/ │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test/ │ └── java/ │ └── com/ │ └── finogeeks/ │ └── mop/ │ └── demo/ │ └── ExampleUnitTest.java ├── build.gradle ├── finclip.jks ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat └── settings.gradle ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/issue.yml ================================================ name: Notify on: issues: types: [opened] issue_comment: types: [created] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Notify run: curl --location --request POST 'https://api.finogeeks.club/api/v1/finstore/webhooks/61b331d79b3dad0001f72fa2/postreceive?nonce=jhd2QyrArsc' --header "Content-Type:application/json" --data-raw '{"msg":"仓库 ${{github.repository}} 有新的 issue"}' ================================================ FILE: .github/workflows/pull_request.yml ================================================ name: Notify on: pull_request: branches: [ master ] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Notify run: curl --location --request POST 'https://api.finogeeks.club/api/v1/finstore/webhooks/61b331d79b3dad0001f72fa2/postreceive?nonce=jhd2QyrArsc' --header "Content-Type:application/json" --data-raw '{"msg":"仓库 ${{github.repository}} 有新的 PR ${{ github.event.pull_request._links.html.href }}"}' ================================================ FILE: .gitignore ================================================ *.iml .gradle /local.properties /.idea/caches /.idea/libraries /.idea/modules.xml /.idea/workspace.xml /.idea/navEditor.xml /.idea/assetWizardSettings.xml .DS_Store /build /captures .externalNativeBuild .cxx ================================================ FILE: .idea/.name ================================================ mopdemo ================================================ FILE: .idea/codeStyles/Project.xml ================================================
xmlns:android ^$
xmlns:.* ^$ BY_NAME
.*:id http://schemas.android.com/apk/res/android
.*:name http://schemas.android.com/apk/res/android
name ^$
style ^$
.* ^$ BY_NAME
.* http://schemas.android.com/apk/res/android ANDROID_ATTRIBUTE_ORDER
.* .* BY_NAME
================================================ FILE: .idea/compiler.xml ================================================ ================================================ FILE: .idea/deploymentTargetDropDown.xml ================================================ ================================================ FILE: .idea/deploymentTargetSelector.xml ================================================ ================================================ FILE: .idea/gradle.xml ================================================ ================================================ FILE: .idea/jarRepositories.xml ================================================ ================================================ FILE: .idea/kotlinc.xml ================================================ ================================================ FILE: .idea/migrations.xml ================================================ ================================================ FILE: .idea/misc.xml ================================================ ================================================ FILE: .idea/runConfigurations.xml ================================================ ================================================ FILE: .idea/vcs.xml ================================================ ================================================ FILE: README.md ================================================

FinClip Android DEMO

本项目提供在 Android 环境中运行小程序的 DEMO 样例

👉 https://www.finclip.com/ 👈

[官方网站](https://www.finclip.com/) | [示例小程序](https://www.finclip.com/#/market) | [开发文档](https://www.finclip.com/mop/document/) | [部署指南](https://www.finclip.com/mop/document/introduce/quickStart/cloud-server-deployment-guide.html) | [SDK 集成指南](https://www.finclip.com/mop/document/introduce/quickStart/intergration-guide.html) | [API 列表](https://www.finclip.com/mop/document/develop/api/overview.html) | [组件列表](https://www.finclip.com/mop/document/develop/component/overview.html) | [隐私承诺](https://www.finclip.com/mop/document/operate/safety.html)
----- ## 🤔 FinClip 是什么? 有没有**想过**,开发好的微信小程序能放在自己的 APP 里直接运行,只需要开发一次小程序,就能在不同的应用中打开它,是不是很不可思议? 有没有**试过**,在自己的 APP 中引入一个 SDK ,应用中不仅可以打开小程序,还能自定义小程序接口,修改小程序样式,是不是觉得更不可思议? 这就是 FinClip ,就是有这么多不可思议! ## ⚙️ 操作步骤 ### 第一步 配置 build.gradle 文件 在工程的 `build.gradle` 中添加 maven 仓库的地址: ```bash buildscript { repositories { google() jcenter() } dependencies { classpath "com.android.tools.build:gradle:3.5.2" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.60" } } allprojects { repositories { google() jcenter() maven { url "https://jitpack.io" } maven { url "https://gradle.finogeeks.club/repository/applet/" credentials { username "applet" password "123321" } } } } ``` ### 第二步 在 gradle 中依赖 SDK `implementation 'com.finogeeks.lib:finapplet:+'` ### 第三步 配置混淆规则 集成 SDK 之后,为了避免 SDK 中部分不能被混淆的代码被混淆,需要在工程的混淆规则配置文件中增加以下配置: ``-keep class com.finogeeks.** {*;}`` ### 第四步 SDK初始化 我们强烈建议在 `Application` 中对SDK进行初始化,初始化 SDK 需要传入的各项参数如下: ```java FinAppConfig config = new FinAppConfig.Builder() .setAppKey("SDKKEY") .setAppSecret("SECRET") .setApiUrl("https://api.finclip.com") .setApiPrefix("/api/v1/mop/") .setGlideWithJWT(false) .build(); FinCallback callback = new FinCallback() { @Override public void onSuccess(Object result) { // SDK初始化成功 } @Override public void onError(int code, String error) { // SDK初始化失败 Toast.makeText(AppletApplication.this, "SDK初始化失败", Toast.LENGTH_SHORT).show(); } @Override public void onProgress(int status, String error) { } }; FinAppClient.INSTANCE.init(this, config, callback); ``` SDK 采用多进程机制实现,每个小程序运行在独立的进程中,即一个小程序对应一个进程。在初始化SDK时,要特别注意的一点是:**小程序进程在创建的时候,不需要执行任何初始化操作,即使是小程序SDK的初始化,也不需要在小程序进程中执行。** > 举个例子🌰
应用使用了一些第三方库,这些库需要在应用启动时先初始化,那么在 Application 中执行初始化时,只有当前进程为宿主进程时才需要初始化这些第三方库,小程序进程是不需要初始化这些库的。
因此,在初始化SDK之前,一定要判断当前进程是哪一个进程,如果是小程序进程,就不进行任何操作了: ```java if (FinAppClient.INSTANCE.isFinAppProcess(this)) { return; } ``` ### 第五步 打开小程序 ```java FinAppClient.INSTANCE.getAppletApiManager().startApplet(this, "appid"); ``` - **SDK KEY** 和 **SDK SECRET** 可以从 [FinClip](https://finclip.com/#/home) 获取,点 [这里](https://finclip.com/#/register) 注册账号; - 进入平台后,在「应用管理」页面添加你自己的包名后,点击「复制」即可获得 key\secret\apisever 字段; - **apiServer** 和 **apiPrefix** 是固定字段,请直接参考本 DEMO ; - **小程序 ID** 是管理后台上架的小程序 APP ID,需要在「小程序管理」中创建并在「应用管理」中关联; > 小程序 ID 与 微信小程序ID 不一样哦!(这里是特指 FinClip 平台的 ID ) ## 📋 集成文档 [点击这里](https://www.finclip.com/mop/document/introduce/quickStart/intergration-guide.html#_2-android-%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90) 查看 Android 快速集成文档 ## 📘 目录结构 ``` . ├─.github │ ├─.idea 由IDE自动生成,无需关注 │ ├─app 项目源码主目录 │ │ │ │ build.gradle 应用构建配置 │ │ │ │ proguard-rules.pro 混淆配置 │ │ │ ├─release 构建应用生成的apk目录 │ │ │ └─src │ ├─androidTest 单元测试目录,由IDE自动生成,无需关注 │ │ │ ├─main 应用源码主目录 │ │ │ AndroidManifest.xml 应用清单文件 │ │ │ │ │ ├─java 应用源码目录 │ │ │ │ │ └─res 资源文件目录 │ │ ├─drawable darwable资源目录 │ │ │ │ │ ├─drawable-v24 darwable资源目录 │ │ │ │ │ ├─layout 布局文件目录 │ │ │ │ │ ├─mipmap-anydpi-v26 图片资源目录 │ │ │ │ │ ├─mipmap-hdpi 图片资源目录 │ │ │ │ │ ├─mipmap-mdpi 图片资源目录 │ │ │ │ │ ├─mipmap-xhdpi 图片资源目录 │ │ │ │ │ ├─mipmap-xxhdpi 图片资源目录 │ │ │ │ │ ├─mipmap-xxxhdpi 图片资源目录 │ │ │ │ │ └─values 各项资源值配置目录 │ │ │ └─test 单元测试目录,由IDE自动生成,无需关注 │ └─gradle gradle版本配置目录,一般情况下无需关注 ``` ## 🔗 常用链接 以下内容是您在 FinClip 进行开发与体验时,常见的问题与指引信息 - [FinClip 官网](https://www.finclip.com/#/home) - [示例小程序](https://www.finclip.com/#/market) - [文档中心](https://www.finclip.com/mop/document/) - [SDK 部署指南](https://www.finclip.com/mop/document/introduce/quickStart/intergration-guide.html) - [小程序代码结构](https://www.finclip.com/mop/document/develop/guide/structure.html) - [iOS 集成指引](https://www.finclip.com/mop/document/runtime-sdk/ios/ios-integrate.html) - [Android 集成指引](https://www.finclip.com/mop/document/runtime-sdk/android/android-integrate.html) - [Flutter 集成指引](https://www.finclip.com/mop/document/runtime-sdk/flutter/flutter-integrate.html) ## ☎️ 联系我们 微信扫描下面二维码,关注官方公众号 **「凡泰极客」**,获取更多精彩内容。
微信扫描下面二维码,加入官方微信交流群,获取更多精彩内容。
## Stargazers [![Stargazers repo roster for @finogeeks/finclip-android-demo](https://reporoster.com/stars/finogeeks/finclip-android-demo)](https://github.com/finogeeks/finclip-android-demo/stargazers) ## Forkers [![Forkers repo roster for @finogeeks/finclip-android-demo](https://reporoster.com/forks/finogeeks/finclip-android-demo)](https://github.com/finogeeks/finclip-android-demo/network/members) ================================================ FILE: app/.gitignore ================================================ /build ================================================ FILE: app/build.gradle ================================================ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-kapt' android { compileSdkVersion 29 buildToolsVersion "29.0.2" defaultConfig { applicationId "com.finogeeks.finclip.demo" minSdkVersion 19 targetSdkVersion 29 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" multiDexEnabled true buildConfigField "String", "APP_KEY", "\"22LyZEib0gLTQdU3MUauATBwgfnTCJjdr7FCnywmAEM=\"" // App Secret buildConfigField "String", "APP_SECRET", "\"bdfd76cae24d4313\"" // API服务地址 buildConfigField "String", "API_URL", "\"https://api.finclip.com\"" // API服务前缀 buildConfigField "String", "API_PREFIX", "\"/api/v1/mop/\"" ndk { abiFilters "x86", "armeabi", 'armeabi-v7a', 'arm64-v8a' } } signingConfigs { debug { keyAlias "FinClipDemo" keyPassword "fino123456" storeFile file("../finclip.jks") storePassword "fino123456" } release { keyAlias "FinClipDemo" keyPassword "fino123456" storeFile file("../finclip.jks") storePassword "fino123456" } } buildTypes { debug { minifyEnabled false signingConfig signingConfigs.debug proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } release { minifyEnabled true signingConfig signingConfigs.release proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } packagingOptions { // libsdkcore.so是被加固过的,不能被压缩,否则加载动态库时会报错 doNotStrip "*/x86/libsdkcore.so" doNotStrip "*/x86_64/libsdkcore.so" doNotStrip "*/armeabi/libsdkcore.so" doNotStrip "*/armeabi-v7a/libsdkcore.so" doNotStrip "*/arm64-v8a/libsdkcore.so" } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'androidx.constraintlayout:constraintlayout:2.0.0' implementation 'com.google.android.material:material:1.2.0' testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' implementation 'com.finogeeks.lib:finapplet:2.49.11' implementation 'cn.bingoogolapple:bga-qrcode-zbar:1.3.7' } ================================================ FILE: app/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # You can control the set of applied configuration files using the # proguardFiles setting in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} # Uncomment this to preserve the line number information for # debugging stack traces. #-keepattributes SourceFile,LineNumberTable # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile -keep class com.finogeeks.** {*;} ================================================ FILE: app/src/androidTest/java/com/finogeeks/mop/demo/ExampleInstrumentedTest.java ================================================ package com.finogeeks.mop.demo; import android.content.Context; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.ext.junit.runners.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import static org.junit.Assert.*; /** * Instrumented test, which will execute on an Android device. * * @see Testing documentation */ @RunWith(AndroidJUnit4.class) public class ExampleInstrumentedTest { @Test public void useAppContext() { // Context of the app under test. Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); assertEquals("com.finogeeks.mop.demo", appContext.getPackageName()); } } ================================================ FILE: app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: app/src/main/java/com/finogeeks/mop/demo/AppletHandler.java ================================================ package com.finogeeks.mop.demo; import android.content.Context; import android.graphics.Bitmap; import android.os.Bundle; import android.widget.Toast; import androidx.annotation.NonNull; import com.finogeeks.lib.applet.page.view.moremenu.MoreMenuItem; import com.finogeeks.lib.applet.page.view.moremenu.MoreMenuType; import com.finogeeks.lib.applet.rest.model.GrayAppletVersionConfig; import com.finogeeks.lib.applet.sdk.api.IAppletHandler; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.json.JSONObject; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * {@link IAppletHandler}实现类,用于实现一些业务场景,例如注册"更多"菜单项,转发小程序等。 */ public class AppletHandler implements IAppletHandler { @NonNull private Context mContext; private AppletHandler() { } public AppletHandler(@NonNull Context context) { this.mContext = context; } /** * 获取灰度发布配置参数 * * @param appId 小程序ID * @return 灰度发布配置参数 */ @Nullable @Override public List getGrayAppletVersionConfigs(@NotNull String appId) { return null; } /** * 获取注册的"更多"菜单项 * * @param appId 小程序ID * @return 注册的"更多"菜单项 */ @Nullable @Override public List getRegisteredMoreMenuItems(@NotNull String appId) { List items = new ArrayList<>(); MoreMenuItem item0 = new MoreMenuItem("WXShareAPPFriends", "微信好朋友", MoreMenuType.ON_MINI_PROGRAM); items.add(item0); MoreMenuItem item1 = new MoreMenuItem("WXShareAPPMoments", "微信朋友圈", MoreMenuType.ON_MINI_PROGRAM, true); items.add(item1); MoreMenuItem item2 = new MoreMenuItem("ShareSinaWeibo", "新浪微博", MoreMenuType.ON_MINI_PROGRAM); items.add(item2); MoreMenuItem item3 = new MoreMenuItem("ShareQQFirends", "QQ", MoreMenuType.ON_MINI_PROGRAM); items.add(item3); MoreMenuItem item4 = new MoreMenuItem("ShareDingDing", "Dingding", MoreMenuType.ON_MINI_PROGRAM); items.add(item4); MoreMenuItem item5 = new MoreMenuItem("ShareLinks", "标题以后端配置为准", MoreMenuType.ON_MINI_PROGRAM); items.add(item5); MoreMenuItem item6 = new MoreMenuItem("SharePicture", "SharePicture", MoreMenuType.ON_MINI_PROGRAM); items.add(item6); MoreMenuItem item7 = new MoreMenuItem("Restart", "Restart", MoreMenuType.COMMON); items.add(item7); MoreMenuItem item8 = new MoreMenuItem("Desktop", "Desktop", MoreMenuType.COMMON); items.add(item8); return items; } /** * 获取用户信息 * * @return 用户信息[Map] */ @Nullable @Override public Map getUserInfo() { return null; } /** * 小程序导航栏中的"关闭"按钮被点击 * * @param appId 小程序ID */ @Override public void onNavigationBarCloseButtonClicked(@NotNull String appId) { Toast.makeText(mContext, "点击了小程序 " + appId + " 的导航栏关闭按钮", Toast.LENGTH_SHORT).show(); } /** * 注册的"更多"菜单项被点击 * * @param appId 小程序ID * @param path 小程序页面路径 * @param menuItemId 被点击的菜单条目的ID * @param appInfo 小程序信息,是一串json,包含了小程序id、小程序名称、小程序图标、用户id、转发的数据内容等信息。 * [appInfo]的内容格式如下: * { * "appTitle": "凡泰小程序", * "appAvatar": "https:\/\/www.finogeeks.club\/statics\/images\/swan_mini\/swan_logo.png", * "appId": "5df36b3f687c5c00013e9fd1", * "userId": "finogeeks", * "params": { * "title": "apt-test-tweet-接口测试发布的动态!@#¥%……&*(", * "desc": "您身边的服务专家", * "imageUrl": "finfile:\/\/tmp_fc15edd8-2ff6-4c54-9ee9-fe5ee034033d1576550313667.png", * "path": "pages\/tweet\/tweet-detail.html?fcid=%40staff_staff1%3A000000.finogeeks.com&timelineId=db0c2098-031e-41c4-b9c6-87a5bbcf681d&shareId=3dfa2f78-19fc-42fc-b3a9-4779a6dac654", * "appInfo": { * "weixin": { * "path": "\/studio\/pages\/tweet\/tweet-detail", * "query": { * "fcid": "@staff_staff1:000000.finogeeks.com", * "timelineId": "db0c2098-031e-41c4-b9c6-87a5bbcf681d" * } * } * } * } * } * @param bitmap 小程序封面图片。如果[appInfo].params.imageUrl字段为http、https的链接地址,那么小程序封面图片 * 就取[appInfo].params.imageUrl对应的图片,否则小程序的封面图片取[bitmap]。 * @param callback 转发小程序结果回调。 */ @Override public void onRegisteredMoreMenuItemClicked(@NotNull String appId, @NotNull String path, @NotNull String menuItemId, @Nullable String appInfo, @Nullable Bitmap bitmap, @NotNull IAppletCallback callback) { Toast.makeText(mContext, "小程序" + appId + "的" + path + "页面的菜单" + menuItemId + "被点击了,appInfo : " + appInfo + " bitmap : " + bitmap, Toast.LENGTH_SHORT).show(); callback.onSuccess(null); } /** * 转发小程序 * * @param appInfo 小程序信息,是一串json,包含了小程序id、小程序名称、小程序图标、用户id、转发的数据内容等信息。 * [appInfo]的内容格式如下: * { * "appTitle": "凡泰小程序", * "appAvatar": "https:\/\/www.finogeeks.club\/statics\/images\/swan_mini\/swan_logo.png", * "appId": "5df36b3f687c5c00013e9fd1", * "userId": "finogeeks", * "params": { * "title": "apt-test-tweet-接口测试发布的动态!@#¥%……&*(", * "desc": "您身边的服务专家", * "imageUrl": "finfile:\/\/tmp_fc15edd8-2ff6-4c54-9ee9-fe5ee034033d1576550313667.png", * "path": "pages\/tweet\/tweet-detail.html?fcid=%40staff_staff1%3A000000.finogeeks.com&timelineId=db0c2098-031e-41c4-b9c6-87a5bbcf681d&shareId=3dfa2f78-19fc-42fc-b3a9-4779a6dac654", * "appInfo": { * "weixin": { * "path": "\/studio\/pages\/tweet\/tweet-detail", * "query": { * "fcid": "@staff_staff1:000000.finogeeks.com", * "timelineId": "db0c2098-031e-41c4-b9c6-87a5bbcf681d" * } * } * } * } * } * @param bitmap 小程序封面图片。如果[appInfo].params.imageUrl字段为http、https的链接地址,那么小程序封面图片 * 就取[appInfo].params.imageUrl对应的图片,否则小程序的封面图片取[bitmap]。 * @param callback 转发小程序结果回调。 */ @Override public void shareAppMessage(@NotNull String appInfo, @Nullable Bitmap bitmap, @NotNull IAppletCallback callback) { Toast.makeText(mContext, "点击了转发按钮,去实现您的转发/分享逻辑吧", Toast.LENGTH_SHORT).show(); } @Override public void chooseAvatar(@NonNull IAppletCallback iAppletCallback) { } @Override public boolean contact(@NonNull JSONObject jsonObject) { return false; } @Override public boolean feedback(@NonNull Bundle bundle) { return false; } @Override public void getJSSDKConfig(@NonNull JSONObject jsonObject, @NonNull IAppletCallback iAppletCallback) { } @Override public void getPhoneNumber(@NonNull IAppletCallback iAppletCallback) { } @androidx.annotation.Nullable @Override public Map getWebViewCookie(@NonNull String s) { return null; } @Override public boolean launchApp(@androidx.annotation.Nullable String s) { return false; } } ================================================ FILE: app/src/main/java/com/finogeeks/mop/demo/InputContentActivity.java ================================================ package com.finogeeks.mop.demo; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; /** * 输入内容页面 */ public class InputContentActivity extends AppCompatActivity { public static final String EXTRA_NAME_INPUT_CONTENT = "input_content"; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_input_content); Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); final EditText editTextInputContent = findViewById(R.id.edt_input_content); Button btnConfirm = findViewById(R.id.btn_confirm); btnConfirm.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (editTextInputContent.length() < 1) { Toast.makeText(InputContentActivity.this, getString(R.string.fin_clip_input_content_hint), Toast.LENGTH_SHORT).show(); return; } Intent intent = new Intent(); intent.putExtra(EXTRA_NAME_INPUT_CONTENT, editTextInputContent.getText().toString()); setResult(RESULT_OK, intent); finish(); } }); } } ================================================ FILE: app/src/main/java/com/finogeeks/mop/demo/MainActivity.java ================================================ package com.finogeeks.mop.demo; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.Button; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import com.finogeeks.lib.applet.client.FinAppClient; import com.finogeeks.lib.applet.sdk.api.request.IFinAppletRequest; import java.util.HashMap; import java.util.Map; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); Button btnScan = findViewById(R.id.btn_scan); btnScan.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startActivity(new Intent(MainActivity.this, ScanStartAppletActivity.class)); } }); Button btnCharts = findViewById(R.id.btn_charts); btnCharts.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { FinAppClient.INSTANCE.getAppletApiManager().startApplet(MainActivity.this, IFinAppletRequest.Companion.fromAppId("5facb3a52dcbff00017469bd"),null); } }); Button btnDemo = findViewById(R.id.btn_demo); btnDemo.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { FinAppClient.INSTANCE.getAppletApiManager().startApplet(MainActivity.this,IFinAppletRequest.Companion.fromAppId( "5fa214a29a6a7900019b5cc1"),null); } }); Button btnProfile = findViewById(R.id.btn_profile); btnProfile.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { FinAppClient.INSTANCE.getAppletApiManager().startApplet(MainActivity.this, IFinAppletRequest.Companion.fromAppId("5fa215459a6a7900019b5cc3"),null); } }); Button btnCustomApi = findViewById(R.id.btn_custom_api); btnCustomApi.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Map params = new HashMap<>(); params.put("path", "pages/index/index"); FinAppClient.INSTANCE.getAppletApiManager().startApplet(MainActivity.this, IFinAppletRequest.Companion.fromAppId("5fc8934aefb8c600019e9747").setStartParams(params),null); } }); Button btnH5Api = findViewById(R.id.btn_h5_api); btnH5Api.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Map params = new HashMap<>(); params.put("path", "pages/webview/webview"); FinAppClient.INSTANCE.getAppletApiManager().startApplet(MainActivity.this, "5fc8934aefb8c600019e9747", params,null); } }); Button btnAppletLogin = findViewById(R.id.btn_applet_login); btnAppletLogin.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { FinAppClient.INSTANCE.getAppletApiManager().startApplet(MainActivity.this, "60f051ea525ea10001c0bd22",null); } }); } } ================================================ FILE: app/src/main/java/com/finogeeks/mop/demo/MopApplication.java ================================================ package com.finogeeks.mop.demo; import android.app.Activity; import android.graphics.Color; import android.widget.Toast; import androidx.multidex.MultiDexApplication; import com.finogeeks.lib.applet.client.FinAppClient; import com.finogeeks.lib.applet.client.FinAppConfig; import com.finogeeks.lib.applet.client.FinAppProcessClient; import com.finogeeks.lib.applet.interfaces.FinCallback; import com.finogeeks.lib.applet.interfaces.IApi; import com.finogeeks.lib.applet.sdk.api.IAppletApiManager; import com.finogeeks.mop.demo.customapi.CustomApi; import com.finogeeks.mop.demo.customapi.CustomH5Api; import com.finogeeks.mop.demo.customapi.user.LoginApi; import com.finogeeks.mop.demo.customapi.user.ProfileApi; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.json.JSONException; import org.json.JSONObject; import java.util.ArrayList; import java.util.List; public class MopApplication extends MultiDexApplication { @Override public void onCreate() { super.onCreate(); if (FinAppClient.INSTANCE.isFinAppProcess(this)) { // 小程序进程 // 小程序进程中注册api的方法能获取到小程序所在activity对象,可以用做创建对话框的context参数) FinAppProcessClient.INSTANCE.setCallback(new FinAppProcessClient.Callback() { @Override public List getRegisterExtensionApis(@NotNull Activity activity) { ArrayList apis = new ArrayList<>(); apis.add(new LoginApi(activity)); apis.add(new ProfileApi()); return apis; } @Nullable @Override public List getRegisterExtensionWebApis(@NotNull Activity activity) { return null; } }); return; } FinAppConfig.UIConfig uiConfig = new FinAppConfig.UIConfig(); uiConfig.setHideNavigationBarCloseButton(true); uiConfig.setHideBackHome(true); uiConfig.setHideForwardMenu(true); uiConfig.setHideFeedbackAndComplaints(true); uiConfig.setMoreMenuStyle(FinAppConfig.UIConfig.MORE_MENU_DEFAULT); FinAppConfig.UIConfig.CapsuleConfig capsuleConfig = new FinAppConfig.UIConfig.CapsuleConfig(); capsuleConfig.capsuleWidth = 86f; capsuleConfig.capsuleHeight = 31f; capsuleConfig.capsuleRightMargin = 15f; capsuleConfig.capsuleCornerRadius = 15.5f; capsuleConfig.capsuleBorderWidth = 0.5f; capsuleConfig.capsuleBgLightColor = Color.BLACK; capsuleConfig.capsuleBgDarkColor = Color.WHITE; capsuleConfig.capsuleBorderLightColor = Color.parseColor("#88ffffff"); capsuleConfig.capsuleBorderDarkColor = Color.parseColor("#a5a9b4"); capsuleConfig.moreLightImage = R.mipmap.more_light; capsuleConfig.moreDarkImage = R.mipmap.more_dark; capsuleConfig.moreBtnWidth = 25f; capsuleConfig.moreBtnLeftMargin = 11f; capsuleConfig.closeLightImage = R.mipmap.close_light; capsuleConfig.closeDarkImage = R.mipmap.close_dark; capsuleConfig.closeBtnWidth = 25f; capsuleConfig.closeBtnLeftMargin = 9f; capsuleConfig.capsuleDividerLightColor = Color.parseColor("#88ffffff"); capsuleConfig.capsuleDividerDarkColor = Color.parseColor("#a5a9b4"); uiConfig.setCapsuleConfig(capsuleConfig); FinAppConfig config = new FinAppConfig.Builder() .setSdkKey(BuildConfig.APP_KEY) .setSdkSecret(BuildConfig.APP_SECRET) .setApiUrl(BuildConfig.API_URL) .setApiPrefix(BuildConfig.API_PREFIX) .setDebugMode(BuildConfig.DEBUG) .setUiConfig(uiConfig) .setEncryptionType(FinAppConfig.ENCRYPTION_TYPE_SM) .build(); FinAppClient.INSTANCE.init(this, config, new FinCallback() { @Override public void onSuccess(Object result) { Toast.makeText(MopApplication.this, "SDK初始化成功", Toast.LENGTH_SHORT).show(); // 注册自定义小程序API FinAppClient.INSTANCE.getExtensionApiManager().registerApi(new CustomApi(MopApplication.this)); // 注册自定义H5 API FinAppClient.INSTANCE.getExtensionWebApiManager().registerApi(new CustomH5Api(MopApplication.this)); // 设置IAppletHandler实现类 FinAppClient.INSTANCE.setAppletHandler(new AppletHandler(getApplicationContext())); // 在主进程设置"小程序进程调用主进程"的处理方法 // 开发者也可以选择在主进程其他合适的代码位置设置处理方法 FinAppClient.INSTANCE.getAppletApiManager() .setAppletProcessCallHandler(new IAppletApiManager.AppletProcessCallHandler() { @Override public void onAppletProcessCall(@NotNull String name, @Nullable String params, @Nullable FinCallback callback) { if (callback != null) { if (name.equals(LoginApi.API_NAME_LOGIN)) { // 从主进程获取登录信息,返回给小程序进程 // 这里返回的是虚拟的用户登录信息,开发者请从APP里面自行获取用户登录信息 JSONObject jsonObject = new JSONObject(); try { jsonObject.put("userId", "123"); } catch (JSONException e) { e.printStackTrace(); } callback.onSuccess(jsonObject.toString()); } } } }); } @Override public void onError(int code, String error) { Toast.makeText(MopApplication.this, "SDK初始化失败", Toast.LENGTH_SHORT).show(); } @Override public void onProgress(int status, String error) { } }); } } ================================================ FILE: app/src/main/java/com/finogeeks/mop/demo/ScanQRCodeActivity.kt ================================================ package com.finogeeks.mop.demo import android.Manifest import android.app.Activity import android.content.Intent import android.os.Build import android.os.Bundle import android.os.VibrationEffect import android.os.Vibrator import android.util.Log import android.view.MenuItem import android.view.View import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import cn.bingoogolapple.qrcode.core.QRCodeView import com.finogeeks.lib.applet.modules.permission.checkPermissions import kotlinx.android.synthetic.main.activity_scan_qr_code.* /** * 扫描二维码页面 */ class ScanQRCodeActivity : AppCompatActivity(), QRCodeView.Delegate { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_scan_qr_code) setSupportActionBar(toolbar) supportActionBar?.setDisplayHomeAsUpEnabled(true) } override fun onStart() { super.onStart() checkPermissions(Manifest.permission.CAMERA, granted = { zBarView.visibility = View.VISIBLE zBarView.setDelegate(this) zBarView.startCamera() zBarView.startSpotAndShowRect() }) } override fun onStop() { zBarView.stopCamera() super.onStop() } override fun onDestroy() { zBarView.onDestroy() super.onDestroy() } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) zBarView.showScanRect() } override fun onScanQRCodeSuccess(result: String?) { vibrate() zBarView.startSpot() setResult(Activity.RESULT_OK, Intent().putExtra(EXTRA_RESULT, result)) finish() } override fun onCameraAmbientBrightnessChanged(isDark: Boolean) { var tipText: String = zBarView.scanBoxView.tipText val ambientBrightnessTip = "\n环境过暗,请打开闪光灯" if (isDark) { if (!tipText.contains(ambientBrightnessTip)) { zBarView.scanBoxView.tipText = tipText + ambientBrightnessTip } } else { if (tipText.contains(ambientBrightnessTip)) { tipText = tipText.substring(0, tipText.indexOf(ambientBrightnessTip)) zBarView.scanBoxView.tipText = tipText } } } override fun onScanQRCodeOpenCameraError() { Toast.makeText(this, "打开相机出错", Toast.LENGTH_SHORT).show() } private fun vibrate() { val vibrator = getSystemService(VIBRATOR_SERVICE) as Vibrator if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { @Suppress("DEPRECATION") vibrator.vibrate(200) } else { try { val effect = VibrationEffect.createOneShot( 200, VibrationEffect.DEFAULT_AMPLITUDE ) vibrator.vibrate(effect, null) } catch (iae: IllegalArgumentException) { Log.e(TAG, "Failed to create VibrationEffect", iae) } } } override fun onOptionsItemSelected(item: MenuItem): Boolean { if (item.itemId == android.R.id.home) { onBackPressed() return true } return super.onOptionsItemSelected(item) } companion object { private const val TAG = "ScanQRCodeActivity" const val EXTRA_RESULT = "result" } } ================================================ FILE: app/src/main/java/com/finogeeks/mop/demo/ScanStartAppletActivity.kt ================================================ package com.finogeeks.mop.demo import android.annotation.SuppressLint import android.app.Activity import android.content.Intent import android.os.Bundle import android.view.MenuItem import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import com.finogeeks.lib.applet.client.FinAppClient import com.finogeeks.lib.applet.interfaces.FinCallback import com.finogeeks.lib.applet.sdk.api.request.IFinAppletRequest import kotlinx.android.synthetic.main.activity_scan_start_applet.* /** * 扫码启动小程序的页面 */ @SuppressLint("Registered") open class ScanStartAppletActivity : AppCompatActivity() { private var isStartingApplet = false override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_scan_start_applet) setSupportActionBar(toolbar) supportActionBar?.setDisplayHomeAsUpEnabled(true) btnScan.setOnClickListener { scanQRCode() } } private fun scanQRCode() { startActivityForResult(Intent(this, ScanQRCodeActivity::class.java), REQ_CODE_SCAN_QR_CODE) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == REQ_CODE_SCAN_QR_CODE) { if (resultCode == Activity.RESULT_OK && data != null) { if (!isStartingApplet) FinAppClient.appletApiManager.startApplet(this, IFinAppletRequest.fromQrCode(data.getStringExtra(ScanQRCodeActivity.EXTRA_RESULT)), object : FinCallback { override fun onSuccess(result: String?) { isStartingApplet = false runOnUiThread { Toast.makeText(this@ScanStartAppletActivity, "扫码成功", Toast.LENGTH_SHORT).show() } } override fun onError(code: Int, error: String?) { isStartingApplet = false runOnUiThread { Toast.makeText(this@ScanStartAppletActivity, error, Toast.LENGTH_SHORT).show() } } override fun onProgress(status: Int, info: String?) { } }) /*FinAppClient.appletApiManager.startAppletByQrcode(this, data.getStringExtra(ScanQRCodeActivity.EXTRA_RESULT), object : FinCallback { override fun onSuccess(result: String?) { isStartingApplet = false runOnUiThread { Toast.makeText(this@ScanStartAppletActivity, "扫码成功", Toast.LENGTH_SHORT).show() } } override fun onError(code: Int, error: String?) { isStartingApplet = false runOnUiThread { Toast.makeText(this@ScanStartAppletActivity, error, Toast.LENGTH_SHORT).show() } } override fun onProgress(status: Int, info: String?) { } })*/ } } } override fun onOptionsItemSelected(item: MenuItem): Boolean { if (item.itemId == android.R.id.home) { onBackPressed() return true } return super.onOptionsItemSelected(item) } companion object { private const val TAG = "StartAppletActivity" private const val REQ_CODE_SCAN_QR_CODE = 0x01 } } ================================================ FILE: app/src/main/java/com/finogeeks/mop/demo/customapi/CustomApi.java ================================================ package com.finogeeks.mop.demo.customapi; import android.content.Context; import android.content.Intent; import androidx.annotation.NonNull; import com.finogeeks.lib.applet.api.AbsApi; import com.finogeeks.lib.applet.interfaces.ICallback; import com.finogeeks.mop.demo.InputContentActivity; import com.finogeeks.mop.demo.R; import org.json.JSONException; import org.json.JSONObject; import static android.app.Activity.RESULT_OK; import static com.finogeeks.mop.demo.InputContentActivity.EXTRA_NAME_INPUT_CONTENT; /** * 自定义小程序API * 跳转到原生APP的输入内容页面{@link InputContentActivity},输入内容提交后,把输入的内容回传给小程序 */ public class CustomApi extends AbsApi { private static final int REQ_CODE_INPUT_CONTENT = 0x01; private static final String API_NAME_ON_NATIVE = "onNative"; @NonNull private Context mContext; public CustomApi(@NonNull Context context) { mContext = context; } /** * @return 支持可调用的api名称的数组 */ @Override public String[] apis() { return new String[]{API_NAME_ON_NATIVE}; } /** * 接收到对应的api调用时,会调用此方法,在此方法中处理api调用的功能逻辑 * * @param event 事件名称,即api名称 * @param param 参数 * @param callback 回调接口 */ @Override public void invoke(String event, JSONObject param, ICallback callback) { if (API_NAME_ON_NATIVE.equals(event)) { Intent intent = new Intent(mContext, InputContentActivity.class); callback.startActivityForResult(intent, REQ_CODE_INPUT_CONTENT); } } @Override public void onActivityResult(int requestCode, int resultCode, Intent data, ICallback callback) { super.onActivityResult(requestCode, resultCode, data, callback); if (requestCode == REQ_CODE_INPUT_CONTENT) { if (resultCode == RESULT_OK && data != null) { String inputContent = data.getStringExtra(EXTRA_NAME_INPUT_CONTENT); JSONObject jsonObject = new JSONObject(); try { jsonObject.put("text", inputContent); callback.onSuccess(jsonObject); } catch (JSONException e) { e.printStackTrace(); callback.onFail(); } } else { JSONObject jsonObject = new JSONObject(); try { jsonObject.put("errMsg", mContext.getString(R.string.fin_clip_get_input_content_failed)); callback.onFail(jsonObject); } catch (JSONException e) { e.printStackTrace(); callback.onFail(); } } } } } ================================================ FILE: app/src/main/java/com/finogeeks/mop/demo/customapi/CustomH5Api.java ================================================ package com.finogeeks.mop.demo.customapi; import android.content.Context; import android.content.Intent; import androidx.annotation.NonNull; import com.finogeeks.lib.applet.api.AbsApi; import com.finogeeks.lib.applet.interfaces.ICallback; import com.finogeeks.mop.demo.InputContentActivity; import com.finogeeks.mop.demo.R; import org.json.JSONException; import org.json.JSONObject; import static android.app.Activity.RESULT_OK; import static com.finogeeks.mop.demo.InputContentActivity.EXTRA_NAME_INPUT_CONTENT; /** * 自定义H5 API * 跳转到原生APP的输入内容页面{@link InputContentActivity},输入内容提交后,把输入的内容回传给小程序中的网页 */ public class CustomH5Api extends AbsApi { private static final int REQ_CODE_INPUT_CONTENT = 0x02; private static final String API_NAME_USER_DEFINE_NATIVE = "user_define_native"; @NonNull private Context mContext; public CustomH5Api(@NonNull Context context) { mContext = context; } /** * @return 支持可调用的api名称的数组 */ @Override public String[] apis() { return new String[]{API_NAME_USER_DEFINE_NATIVE}; } /** * 接收到对应的api调用时,会调用此方法,在此方法中处理api调用的功能逻辑 * * @param event 事件名称,即api名称 * @param param 参数 * @param callback 回调接口 */ @Override public void invoke(String event, JSONObject param, ICallback callback) { if (API_NAME_USER_DEFINE_NATIVE.equals(event)) { Intent intent = new Intent(mContext, InputContentActivity.class); callback.startActivityForResult(intent, REQ_CODE_INPUT_CONTENT); } } @Override public void onActivityResult(int requestCode, int resultCode, Intent data, ICallback callback) { super.onActivityResult(requestCode, resultCode, data, callback); if (requestCode == REQ_CODE_INPUT_CONTENT) { if (resultCode == RESULT_OK && data != null) { String inputContent = data.getStringExtra(EXTRA_NAME_INPUT_CONTENT); JSONObject jsonObject = new JSONObject(); try { jsonObject.put("text", inputContent); callback.onSuccess(jsonObject); } catch (JSONException e) { e.printStackTrace(); callback.onFail(); } } else { JSONObject jsonObject = new JSONObject(); try { jsonObject.put("errMsg", mContext.getString(R.string.fin_clip_get_input_content_failed)); callback.onFail(jsonObject); } catch (JSONException e) { e.printStackTrace(); callback.onFail(); } } } } } ================================================ FILE: app/src/main/java/com/finogeeks/mop/demo/customapi/user/LoginApi.java ================================================ package com.finogeeks.mop.demo.customapi.user; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import com.finogeeks.lib.applet.api.AbsApi; import com.finogeeks.lib.applet.client.FinAppProcessClient; import com.finogeeks.lib.applet.interfaces.FinCallback; import com.finogeeks.lib.applet.interfaces.ICallback; import org.json.JSONException; import org.json.JSONObject; public class LoginApi extends AbsApi { public final static String API_NAME_LOGIN = "login"; // 小程序基础库调用的api名称 private final Activity activity; public LoginApi(Activity activity) { this.activity = activity; } @Override public String[] apis() { return new String[]{API_NAME_LOGIN}; } @Override public void invoke(String event, JSONObject param, ICallback callback) { if (event.equals(API_NAME_LOGIN)) { showAuthDialog(callback); } } /** * 显示获取用户登录信息的授权提示对话框 */ private void showAuthDialog(final ICallback callback) { // 是否需要显示授权提示对话框请开发者按照产品需求自行处理 new AlertDialog.Builder(activity) .setTitle("是否同意授权获取用户登录信息?") .setNegativeButton("拒绝", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { callback.onFail(); } }) .setPositiveButton("同意", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { loginMainProcess(callback); } }) .show(); } /** * 从主进程获取用户登录信息 */ private void loginMainProcess(final ICallback callback) { // 小程序进程调用主进程,在主进程获取用户信息后返回给小程序进程 FinAppProcessClient.INSTANCE.getAppletProcessApiManager() .callInMainProcess(API_NAME_LOGIN, null, new FinCallback() { @Override public void onSuccess(final String result) { // 需要在主线程调用callback方法 activity.runOnUiThread(new Runnable() { @Override public void run() { try { callback.onSuccess(new JSONObject(result)); } catch (JSONException e) { e.printStackTrace(); callback.onFail(); } } }); } @Override public void onError(int code, String error) { callback.onFail(); } @Override public void onProgress(int status, String info) { } }); } } ================================================ FILE: app/src/main/java/com/finogeeks/mop/demo/customapi/user/ProfileApi.java ================================================ package com.finogeeks.mop.demo.customapi.user; import com.finogeeks.lib.applet.api.AbsApi; import com.finogeeks.lib.applet.interfaces.ICallback; import org.json.JSONException; import org.json.JSONObject; public class ProfileApi extends AbsApi { private final static String API_NAME_GET_USER_PROFILE = "getUserProfile"; // 小程序基础库调用的api名称 @Override public String[] apis() { return new String[]{API_NAME_GET_USER_PROFILE}; } @Override public void invoke(String event, JSONObject param, ICallback callback) { if (event.equals(API_NAME_GET_USER_PROFILE)) { JSONObject jsonObject = new JSONObject(); try { // 这里返回的是虚拟的用户个人信息,开发者请从APP里面自行获取用户个人信息 jsonObject.put("nickName", "张三"); } catch (JSONException e) { e.printStackTrace(); } callback.onSuccess(jsonObject); } } } ================================================ FILE: app/src/main/res/drawable/ic_launcher_background.xml ================================================ ================================================ FILE: app/src/main/res/drawable-v24/ic_launcher_foreground.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_input_content.xml ================================================