Commit ddff9e01 by 徐健

Merge branch 'feature/test_home_pager' into dev

# Conflicts:
#	config.gradle
parents fb738cfa a2a50d54
......@@ -178,6 +178,7 @@ dependencies {
if (true) {
//开发模式
implementation fileTree(dir: 'aars', include: ['*.aar'])
api project(':m-user')
implementation modularPublication('com.ydl:m-user-api')
api project(':m-tests')
......@@ -188,6 +189,8 @@ dependencies {
// api "com.ydl:m-consultant-api:0.0.2"
// api 'com.ydl:m-consultant-module-ydl:0.0.18@aar'
implementation project(':ydl-flutter-base')
api (project(':ydl-platform')){
transitive = true
}
......@@ -205,6 +208,7 @@ dependencies {
implementation modularPublication('com.ydl:m-fm-api')
implementation modularPublication('com.ydl:m-audioim-api')
} else {
//发布模式
api 'com.ydl:m-user-module-ydl:0.0.6'
api rootProject.ext.dependencies["ydl-webview"]
......
......@@ -28,7 +28,7 @@ import com.yidianling.common.tools.ToastUtil
import com.yidianling.consultant.ExpertSearchActivity.Companion.HOT_SEARCH_DOCTOR_NAME
import com.yidianling.consultant.api.IConsultantService
import com.yidianling.fm.api.service.IFMService
import com.yidianling.tests.home.TestHomeActivity
import com.yidianling.tests.home.NewTestHomeActivity
import kotlinx.android.synthetic.main.activity_main.*
/**
......@@ -95,7 +95,7 @@ class MainActivity : BaseLceActivity<DemoContract.View, DemoContract.Presenter>(
startActivity(Intent(this, MusicPlayActivity::class.java))
}
bt_to_tests.setOnClickListener {
startActivity(Intent(this, TestHomeActivity::class.java))
startActivity(Intent(this, NewTestHomeActivity::class.java))
}
bt_to_confide.setOnClickListener {
YDLavManager.instances.login("1193016")
......
......@@ -4,7 +4,10 @@ package com.ydl.component.base;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import com.channel.ydl_flutter_base.base.BaseFlutterActivity;
import com.channel.ydl_flutter_base.plugin.YDLCommonPlugin;
import com.ydl.devicesidlib.DeviceIDHelper;
import com.ydl.media.audio.PlayService;
import com.ydl.ydlcommon.base.delegate.IAppLifecycles;
......@@ -13,13 +16,24 @@ import com.yidianling.course.lifeCallback.CoursePlayLifecycle;
import org.jetbrains.annotations.NotNull;
import java.util.concurrent.TimeUnit;
import io.flutter.app.FlutterActivityDelegate;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugins.GeneratedPluginRegistrant;
import io.flutter.view.FlutterMain;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
/**
* Created by haorui on 2019-09-02.
* Des:
*/
public class DemoAppLifecycles implements IAppLifecycles {
@Override
public void attachBaseContext(@NotNull Context base) {
......@@ -35,6 +49,12 @@ public class DemoAppLifecycles implements IAppLifecycles {
//Flutter 初始化需要在主线程中执行
FlutterMain.startInitialization(application);
// FlutterMain.ensureInitializationComplete(application, null);
//
// FlutterEngine flutterEngine = new FlutterEngine(application);
// flutterEngine.getDartExecutor().executeDartEntrypoint(DartExecutor.DartEntrypoint.createDefault());
// YDLCommonPlugin plugin = new YDLCommonPlugin();
// flutterEngine.getLocalizationChannel().channel.setMethodCallHandler(plugin);
Intent intent = new Intent(application, PlayService.class);
application.startService(intent);
......@@ -44,6 +64,5 @@ public class DemoAppLifecycles implements IAppLifecycles {
@Override
public void onTerminate(@NotNull Application application) {
}
}
}
\ No newline at end of file
......@@ -43,14 +43,15 @@ ext {
// -------------- 业务模块 --------------
//第三步 若干
"m-confide" : "0.0.29",
"m-confide" : "0.0.31.10",
"m-consultant" : "0.0.44",
"m-course" : "0.0.32",
"m-fm" : "0.0.21",
"m-muse" : "0.0.19",
"m-tests" : "0.0.12",
"m-user" : "0.0.37",
"m-muse" : "0.0.19.5",
"m-tests" : "0.0.13.5",
"m-course" : "0.0.32",
//-------------- 业务模块 API 层 --------------
"m-audioim-api" : "0.0.5",
......@@ -70,25 +71,28 @@ ext {
"ydl-webview" : "0.0.28",
"ydl-media" : "0.0.14",
"ydl-pay" : "0.0.11",
"m-audioim" : "0.0.29",
"m-audioim" : "0.0.31.11",
//以下 几乎不会动
"router" : "0.0.1",
"ydl-net" : "0.0.3",
"ydl-utils" : "0.0.3",
"ydl-flutter-base": "0.0.3",
"ydl-flutter-base": "0.0.5.17",
]
ydlCompileVersion = [
// -------------- 业务模块 --------------
//第三步 若干
"m-confide" : "0.0.23",
"m-confide" : "0.0.27.1",
"m-consultant" : "0.0.26",
"m-course" : "0.0.22",
"m-fm" : "0.0.15",
"m-muse" : "0.0.7",
"m-tests" : "0.0.4",
"m-user" : "0.0.25",
// 以下为接入flutter的模块
"m-muse" : "0.0.7",
"m-tests" : "0.0.4",
//-------------- 业务模块 API 层 --------------
"m-audioim-api" : "0.0.5",
"m-confide-api" : "0.0.1",
......@@ -107,14 +111,14 @@ ext {
"ydl-webview" : "0.0.28",
"ydl-media" : "0.0.14",
"ydl-pay" : "0.0.11",
"m-audioim" : "0.0.29",
"m-audioim" : "0.0.31.11",
//以下 几乎不会动
"router" : "0.0.1",
"ydl-net" : "0.0.3",
"ydl-utils" : "0.0.3",
"ydl-flutter-base": "0.0.3",
"ydl-flutter-base": "0.0.5.17",
]
dependencies = [
......@@ -251,7 +255,9 @@ ext {
"ydl-utils" : "com.ydl:ydl-utils:${ydlCompileVersion["ydl-utils"]}",
//flutter功能组件升级===>发布ydl-flutter组件===>引用flutter相关的业务模块
"ydl-flutter" : "com.ydl:ydl-flutter:0.0.16@aar",
"ydl-flutter-base" : "com.ydl:ydl-flutter-base:${ydlCompileVersion["ydl-flutter-base"]}", //组件化项目中的flutter base模块
"ydl-flutter" : "com.ydl:ydl-flutter:0.0.16.4@aar", //flutter aar
"ydl-flutter-sp" : "com.ydl:ydl-flutter-sp:0.0.2@aar", //flutter 缓存 aar
//基础组件 <<--- 先发这个,发完改这里的版本号
"ydl-platform" : "com.ydl:ydl-platform:${ydlCompileVersion["ydl-platform"]}@aar",
......
......@@ -75,7 +75,7 @@ dependencies {
kapt "com.alibaba:arouter-compiler:$arouter_compiler"
api "com.alibaba:arouter-api:$arouter_api"
api ('com.ydl:ydl-av:1.1.8@aar'){
api ('com.ydl:ydl-av:1.1.9@aar'){
transitive = true
}
......@@ -83,10 +83,12 @@ dependencies {
//开发时使用
api project(':ydl-platform')
api project(':ydl-webview')
implementation modularPublication('com.ydl:m-user-api')
implementation modularPublication('com.ydl:m-audioim-api')
}else {
//发布时使用
api rootProject.ext.dependencies["ydl-webview"]
compileOnly rootProject.ext.dependencies["ydl-m-user-api"]
compileOnly rootProject.ext.dependencies["ydl-m-audioim-api"]
api(rootProject.ext.dependencies["ydl-platform"]) {
transitive = true
......
......@@ -38,10 +38,7 @@ import com.ydl.ydl_image.config.SimpleImageOpConfiger
import com.ydl.ydl_image.manager.YDLImageCacheManager
import com.ydl.ydl_av.chat.bean.AudioMessageBean
import com.ydl.audioim.contract.IAudioHomeActivityContract
import com.ydl.audioim.http.command.ConnectCommand
import com.ydl.audioim.http.command.ConnectFinishCommand
import com.ydl.audioim.http.command.ConnectStartCommand
import com.ydl.audioim.http.command.NoticePushCommand
import com.ydl.audioim.http.command.*
import com.ydl.audioim.player.AudioPlayer
import com.ydl.audioim.presenter.AudioHomePresenterImpl
import com.ydl.audioim.utils.DateUtils
......@@ -51,10 +48,12 @@ import com.ydl.webview.H5Params
import com.ydl.webview.NewH5Activity
import com.ydl.webview.RefreshWebEvent
import com.ydl.ydlcommon.base.BaseMvpActivity
import com.ydl.ydlcommon.modular.ModularServiceManager
import com.ydl.ydlcommon.router.YdlCommonRouterManager
import com.ydl.ydlcommon.utils.LogUtil
import com.ydl.ydlcommon.utils.StatusBarUtils
import com.ydl.ydlcommon.utils.remind.ToastHelper
import com.yidianling.user.api.service.IUserService
import de.greenrobot.event.EventBus
import io.agora.rtc.IRtcEngineEventHandler
import io.reactivex.Observable
......@@ -174,6 +173,7 @@ class AudioHomeActivity : BaseMvpActivity<IAudioHomeActivityContract.View, IAudi
override fun onWarning(warn: Int) {
super.onWarning(warn)
uploadException("mRtcEventHandler-onWarning:warnCode--%${warn}")
//103:没有可用的频道资源。可能是因为服务端没法分配频道资源
//104:查找频道超时。在加入频道时 SDK 先要查找指定的频道,出现该警告一般是因为网络太差,连接不到服务器
//105:查找频道请求被服务器拒绝。服务器可能没有办法处理这个请求或请求是非法的
......@@ -192,6 +192,7 @@ class AudioHomeActivity : BaseMvpActivity<IAudioHomeActivityContract.View, IAudi
override fun onError(err: Int) {
super.onError(err)
uploadException("mRtcEventHandler-onError:errorCode--%${err}")
//3:SDK 初始化失败。Agora 建议尝试以下处理方法
//7:SDK 尚未初始化,就调用其 API。请确认在调用 API 之前已创建 RtcEngine 对象并完成初始化
//10:API 调用超时。有些 API 调用需要 SDK 返回结果,如果 SDK 处理时间过长,超过 10 秒没有返回,会出现此错误
......@@ -247,6 +248,24 @@ class AudioHomeActivity : BaseMvpActivity<IAudioHomeActivityContract.View, IAudi
onJoinChannelSuccess()
}
override fun onRtcStats(stats: IRtcEngineEventHandler.RtcStats?) {
super.onRtcStats(stats)
//因为用户端直接加入了频道,防止该回调执行时,专家还未加入频道,因此在连接成功之后,才进行频道人数判断
if (isConnectSuccess && null != stats?.users && stats.users == 1) {
com.yidianling.common.tools.ToastUtil.toastShort("专家已挂断")
leaveChannel()
}
}
override fun onConnectionStateChanged(state: Int, reason: Int) {
super.onConnectionStateChanged(state, reason)
// 3 网络连接被服务器中止 该情况现在是因为后端踢人逻辑
if (reason == 3) {
com.yidianling.common.tools.ToastUtil.toastShort("专家已挂断")
leaveChannel()
}
}
override fun onLeaveChannel(stats: IRtcEngineEventHandler.RtcStats?) {
super.onLeaveChannel(stats)
LogUtil.e("[agora]离开频道回调")
......@@ -288,6 +307,17 @@ class AudioHomeActivity : BaseMvpActivity<IAudioHomeActivityContract.View, IAudi
}
//
/**
* 上传错误日志
*/
private fun uploadException(message: String) {
var time: String = (System.currentTimeMillis() / 1000).toString()
var uid: String = ModularServiceManager.provide(IUserService::class.java).getUserInfo()?.uid!!
var payLoad = PayLoad(channelId?:"0", time, uid, "1", "999", message)
var connectException = ConnectExceptionCommand(time, "2", "99", payLoad)
getPresenter().connectException(connectException)
}
override fun createPresenter(): IAudioHomeActivityContract.Presenter {
return AudioHomePresenterImpl()
}
......@@ -379,6 +409,7 @@ class AudioHomeActivity : BaseMvpActivity<IAudioHomeActivityContract.View, IAudi
iv_hang_up.setOnClickListener {
if (isConnectSuccess) {
updateExpertStatus(false, 1)
userCloseCalling()
} else {
userCloseCalling()
}
......@@ -604,19 +635,23 @@ class AudioHomeActivity : BaseMvpActivity<IAudioHomeActivityContract.View, IAudi
dialPhone()
}
if(finishStatus==1){
var param = ConnectFinishCommand(listenerUid!!, relationId!!, "0",
remainTime!!.toInt() - localRemainTime!!, callId!!,
"0","0","$callStartTime",
"${System.currentTimeMillis()}",3)
mPresenter.connectFinish(param)
}else{
//接通开始回调
if (finishStatus == 0) {
callStartTime = System.currentTimeMillis()
var param = ConnectStartCommand(listenerUid!!, relationId!!, callId!!,
"${System.currentTimeMillis()}","3","0","0","0","0")
mPresenter.connectStart(param)
}
// if(finishStatus==1){
// var param = ConnectFinishCommand(listenerUid!!, relationId!!, "0",
// remainTime!!.toInt() - localRemainTime!!, callId!!,
// "0","0","$callStartTime",
// "${System.currentTimeMillis()}",3)
// mPresenter.connectFinish(param)
// }else{
// 接通开始回调
// callStartTime = System.currentTimeMillis()
// var param = ConnectStartCommand(listenerUid!!, relationId!!, callId!!,
// "${System.currentTimeMillis()}","3","0","0","0","0")
// mPresenter.connectStart(param)
// }
}
/**
......@@ -630,7 +665,11 @@ class AudioHomeActivity : BaseMvpActivity<IAudioHomeActivityContract.View, IAudi
}
override fun onClose() {
userCloseCalling()
// 如果声网未连接成功,切换axb的弹框是自动弹出的,当关闭弹框的时候,执行用户挂断操作
// 如果声网连接成功,点击右上角按钮弹出切换axb弹框,但是关闭时用户不执行挂断操作
if (!isConnectSuccess) {
userCloseCalling()
}
}
})
dialog.show()
......
package com.ydl.audioim.contract
import com.ydl.audioim.bean.ConnectBean
import com.ydl.audioim.http.command.ConnectCommand
import com.ydl.audioim.http.command.ConnectFinishCommand
import com.ydl.audioim.http.command.ConnectStartCommand
import com.ydl.audioim.http.command.NoticePushCommand
import com.ydl.audioim.http.command.*
import com.ydl.ydlcommon.data.http.BaseAPIResponse
import com.ydl.ydlcommon.data.http.BaseResponse
import com.ydl.ydlcommon.mvp.base.BasePresenter
......@@ -66,13 +63,18 @@ interface IAudioHomeActivityContract {
* 通话开始(更新专家状态)
* @param param 请求参数
*/
fun connectStart(param: ConnectStartCommand)
// fun connectStart(param: ConnectStartCommand)
/**
* 通话结束(更新专家状态)
* @param param 请求参数
*/
fun connectFinish(param: ConnectFinishCommand)
// fun connectFinish(param: ConnectFinishCommand)
/**
* 通话异常
*/
fun connectException(param: ConnectExceptionCommand)
/**
......@@ -91,12 +93,17 @@ interface IAudioHomeActivityContract {
/**
* 通话开始回调(更新专家状态)
*/
fun connectStart(param: ConnectStartCommand): Observable<BaseAPIResponse<Any>>
// fun connectStart(param: ConnectStartCommand): Observable<BaseAPIResponse<Any>>
/**
* 通话结束(更新专家状态)
*/
fun connectFinish(param: ConnectFinishCommand): Observable<BaseAPIResponse<Any>>
// fun connectFinish(param: ConnectFinishCommand): Observable<BaseAPIResponse<Any>>
/**
* 通话异常
*/
fun connectException(param: ConnectExceptionCommand): Observable<BaseAPIResponse<Any>>
/**
......
......@@ -3,10 +3,7 @@ package com.ydl.audioim.http
import com.google.gson.Gson
import com.ydl.audioim.bean.AgoraTokenResponse
import com.ydl.audioim.bean.ConnectBean
import com.ydl.audioim.http.command.ConnectCommand
import com.ydl.audioim.http.command.NoticePushCommand
import com.ydl.audioim.http.command.ConnectFinishCommand
import com.ydl.audioim.http.command.ConnectStartCommand
import com.ydl.audioim.http.command.*
import com.ydl.ydlcommon.data.http.BaseAPIResponse
import com.ydl.ydlcommon.data.http.BaseResponse
import com.ydl.ydlcommon.data.http.RxUtils
......@@ -41,12 +38,12 @@ class AudioApiRequestUtil {
* @param param
* @return
*/
fun connectStart(param: ConnectStartCommand): Observable<BaseAPIResponse<Any>> {
// val list = YdlRetrofitUtils.getPostList(param)
var str = Gson().toJson(param)
val body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), str) as RequestBody
return YDLHttpUtils.obtainApi(AudioNetAPi::class.java).connectStart(body)
}
// fun connectStart(param: ConnectStartCommand): Observable<BaseAPIResponse<Any>> {
//// val list = YdlRetrofitUtils.getPostList(param)
// var str = Gson().toJson(param)
// val body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), str) as RequestBody
// return YDLHttpUtils.obtainApi(AudioNetAPi::class.java).connectStart(body)
// }
/**
* 通话结束回调
......@@ -54,11 +51,23 @@ class AudioApiRequestUtil {
* @param param
* @return
*/
fun connectFinish(param: ConnectFinishCommand): Observable<BaseAPIResponse<Any>> {
// val list = YdlRetrofitUtils.getPostList(param)
// fun connectFinish(param: ConnectFinishCommand): Observable<BaseAPIResponse<Any>> {
//// val list = YdlRetrofitUtils.getPostList(param)
// var str = Gson().toJson(param)
// val body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), str) as RequestBody
// return YDLHttpUtils.obtainApi(AudioNetAPi::class.java).connectFinish(body)
// }
/**
* 通话警告与异常回调
*
* @param param
* @return
*/
fun connectException(param: ConnectExceptionCommand): Observable<BaseAPIResponse<Any>> {
var str = Gson().toJson(param)
val body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), str) as RequestBody
return YDLHttpUtils.obtainApi(AudioNetAPi::class.java).connectFinish(body)
return YDLHttpUtils.obtainApi(AudioNetAPi::class.java).connectException(body)
}
/**
......
......@@ -21,16 +21,23 @@ interface AudioNetAPi {
/**
* 通话开始回调
*/
@Headers( YDL_DOMAIN+ YDL_DOMAIN_JAVA,"Content-Type:application/json")
@POST("auth/listen-order/callback/agora/start")
fun connectStart(@Body body:RequestBody): Observable<BaseAPIResponse<Any>>
// @Headers( YDL_DOMAIN+ YDL_DOMAIN_JAVA,"Content-Type:application/json")
// @POST("auth/listen-order/callback/agora/start")
// fun connectStart(@Body body:RequestBody): Observable<BaseAPIResponse<Any>>
/**
* 通话结束回调
*/
// @Headers( YDL_DOMAIN + YDL_DOMAIN_JAVA,"Content-Type:application/json")
// @POST("auth/listen-order/callback/agora/finish")
// fun connectFinish(@Body body:RequestBody): Observable<BaseAPIResponse<Any>>
/**
* 通话警告与错误回调
*/
@Headers( YDL_DOMAIN + YDL_DOMAIN_JAVA,"Content-Type:application/json")
@POST("auth/listen-order/callback/agora/finish")
fun connectFinish(@Body body:RequestBody): Observable<BaseAPIResponse<Any>>
@POST("auth/listen-order/callback/agora")
fun connectException(@Body body:RequestBody): Observable<BaseAPIResponse<Any>>
/**
......
package com.ydl.audioim.http.command
import com.ydl.ydlcommon.data.http.BaseCommand
/**
连接异常请求bean
*/
data class ConnectExceptionCommand(
var noticeId: String, // 秒级
var productId: String, // 2
var eventType: String, // 99
var payload: PayLoad
) : BaseCommand()
data class PayLoad(
var channelName: String, // 房间id
var ts: String, // 秒级
var uid: String,
var platform: String,
var reason: String,
var message: String
)
\ No newline at end of file
......@@ -3,10 +3,7 @@ package com.ydl.audioim.model
import com.ydl.audioim.bean.ConnectBean
import com.ydl.audioim.contract.IAudioHomeActivityContract
import com.ydl.audioim.http.AudioApiRequestUtil
import com.ydl.audioim.http.command.ConnectCommand
import com.ydl.audioim.http.command.NoticePushCommand
import com.ydl.audioim.http.command.ConnectFinishCommand
import com.ydl.audioim.http.command.ConnectStartCommand
import com.ydl.audioim.http.command.*
import com.ydl.ydlcommon.data.http.BaseAPIResponse
import com.ydl.ydlcommon.data.http.BaseResponse
import io.reactivex.Observable
......@@ -19,12 +16,16 @@ import io.reactivex.Observable
* @date 2018/10/30
*/
class AudioHomeModelImpl : IAudioHomeActivityContract.Model {
override fun connectStart(param: ConnectStartCommand): Observable<BaseAPIResponse<Any>> {
return AudioApiRequestUtil.connectStart(param)
}
// override fun connectStart(param: ConnectStartCommand): Observable<BaseAPIResponse<Any>> {
// return AudioApiRequestUtil.connectStart(param)
// }
//
// override fun connectFinish(param: ConnectFinishCommand): Observable<BaseAPIResponse<Any>> {
// return AudioApiRequestUtil.connectFinish(param)
// }
override fun connectFinish(param: ConnectFinishCommand): Observable<BaseAPIResponse<Any>> {
return AudioApiRequestUtil.connectFinish(param)
override fun connectException(param: ConnectExceptionCommand): Observable<BaseAPIResponse<Any>> {
return AudioApiRequestUtil.connectException(param)
}
override fun noticeServerPush(param: NoticePushCommand): Observable<BaseResponse<Any>> {
......
......@@ -3,10 +3,7 @@ package com.ydl.audioim.presenter
import com.ydl.audioim.BuildConfig
import com.ydl.ydl_av.chat.config.YDLChatParam
import com.ydl.audioim.contract.IAudioHomeActivityContract
import com.ydl.audioim.http.command.ConnectCommand
import com.ydl.audioim.http.command.ConnectFinishCommand
import com.ydl.audioim.http.command.ConnectStartCommand
import com.ydl.audioim.http.command.NoticePushCommand
import com.ydl.audioim.http.command.*
import com.ydl.audioim.model.AudioHomeModelImpl
import com.ydl.ydlcommon.mvp.base.BasePresenter
import com.ydl.ydlcommon.router.YdlCommonRouterManager
......@@ -23,48 +20,62 @@ import io.reactivex.schedulers.Schedulers
* @date 2018/10/30
*/
class AudioHomePresenterImpl : BasePresenter<IAudioHomeActivityContract.View, IAudioHomeActivityContract.Model>(), IAudioHomeActivityContract.Presenter {
override fun connectStart(param: ConnectStartCommand) {
mModel.connectStart(param)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe {
// override fun connectStart(param: ConnectStartCommand) {
// mModel.connectStart(param)
// .subscribeOn(Schedulers.io())
// .observeOn(AndroidSchedulers.mainThread())
// .doOnSubscribe {
//
// }
// .doAfterTerminate {
//// view.dismissProgressView()
// }
// .subscribe({
//// if (isSwitchAxb) {
//// //切换axb:跳到拨号界面
//// view.dialPhone()
//// }
// }, { e ->
// LogUtil.e(e.message)
// })
// }
}
.doAfterTerminate {
// view.dismissProgressView()
}
.subscribe({
// if (isSwitchAxb) {
// //切换axb:跳到拨号界面
// view.dialPhone()
// }
}, { e ->
LogUtil.e(e.message)
})
}
// override fun connectFinish(param: ConnectFinishCommand) {
// mModel.connectFinish(param)
// .subscribeOn(Schedulers.io())
// .observeOn(AndroidSchedulers.mainThread())
// .doOnSubscribe {
// //通知服务端专家已经接通时,此时用户已在界面通话中,不需要弹进度条
// mView.showProgressView()
// }
// .doAfterTerminate {
// mView.dismissProgressView()
// }
// .subscribe({
//// if (isSwitchAxb) {
//// //切换axb:跳到拨号界面
//// view.dialPhone()
//// }
// //挂断逻辑:不管接口调用成功与否,都关闭界面
// mView.finishActivity()
// }, { e ->
// LogUtil.e(e.message)
// //挂断逻辑:不管接口调用成功与否,都关闭界面
// mView.finishActivity()
// })
// }
override fun connectFinish(param: ConnectFinishCommand) {
mModel.connectFinish(param)
override fun connectException(param: ConnectExceptionCommand) {
mModel.connectException(param)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe {
//通知服务端专家已经接通时,此时用户已在界面通话中,不需要弹进度条
mView.showProgressView()
}
.doAfterTerminate {
mView.dismissProgressView()
}
.subscribe({
// if (isSwitchAxb) {
// //切换axb:跳到拨号界面
// view.dialPhone()
// }
//挂断逻辑:不管接口调用成功与否,都关闭界面
mView.finishActivity()
}, { e ->
LogUtil.e(e.message)
//挂断逻辑:不管接口调用成功与否,都关闭界面
mView.finishActivity()
})
}
......
......@@ -88,9 +88,7 @@ dependencies {
}else {
//发布时使用
api rootProject.ext.dependencies["ydl-media"]
api(rootProject.ext.dependencies["m-audioim"]){
transitive = true
}
api rootProject.ext.dependencies["m-audioim"]
api(rootProject.ext.dependencies["ydl-platform"]) {
transitive = true
}
......
......@@ -73,6 +73,7 @@ dependencies {
api project(":ydl-platform")
api project(":ydl-media")
api project(":ydl-pay")
implementation project(':ydl-flutter-base')
} else {
//发布时使用
......@@ -86,6 +87,7 @@ dependencies {
api(rootProject.ext.dependencies["ydl-platform"]) {
transitive = true
}
implementation rootProject.ext.dependencies["ydl-flutter-base"]
}
api rootProject.ext.dependencies["ydl-flutter"]
}
......@@ -2,12 +2,10 @@ package com.yidianling.course
import CoursePlugin
import android.annotation.SuppressLint
import android.os.Bundle
import android.os.Handler
import android.text.TextUtils
import android.view.View
import com.alibaba.android.arouter.launcher.ARouter
import com.example.fm_plugin.base.BaseFlutterFragment
import com.channel.ydl_flutter_base.base.BaseFlutterFragment
import com.google.gson.Gson
import com.ydl.ydl_router.manager.YDLRouterManager
import com.ydl.ydlcommon.data.http.BaseResponse
......
......@@ -3,7 +3,7 @@ import android.content.Intent
import android.net.Uri
import android.os.Handler
import android.text.TextUtils
import com.example.fm_plugin.base.BaseFlutterFragment
import com.channel.ydl_flutter_base.base.BaseFlutterFragment
import com.lzf.easyfloat.permission.PermissionUtils
import com.ydl.media.audio.AudioPlayer
import com.ydl.media.audio.model.Music
......
package com.yidianling.course.flutterPlugin
import android.util.Log
import com.channel.ydl_flutter_base.base.BaseFlutterFragment
import com.example.fm_plugin.base.FlutterFragment
import io.flutter.plugin.common.EventChannel
import io.flutter.view.FlutterView
......@@ -16,10 +16,10 @@ object CourseSendPlugin {
private var messageChannel: EventChannel? = null
const val CHANNEL: String = "base/channel/native/post"
private var mFragment: FlutterFragment? = null
private var mFragment: BaseFlutterFragment? = null
private var eventSink: EventChannel.EventSink? = null
fun initContext(fragment: FlutterFragment, view: FlutterView): CourseSendPlugin {
fun initContext(fragment: BaseFlutterFragment, view: FlutterView): CourseSendPlugin {
mFragment = fragment
messageChannel = EventChannel(view, CHANNEL)
messageChannel!!.setStreamHandler(object : EventChannel.StreamHandler {
......@@ -39,11 +39,11 @@ object CourseSendPlugin {
}
fun sendRequestData(){
fun sendRequestData() {
eventSink!!.success("requestData")
}
fun sendLoginSuccess(){
fun sendLoginSuccess() {
eventSink!!.success("loginSuccess")
}
......
......@@ -66,6 +66,7 @@ dependencies {
//开发时使用
api project(":ydl-platform")
implementation project(':ydl-media')
implementation project(':ydl-flutter-base')
} else {
//发布时使用
api rootProject.ext.dependencies["ydl-media"]
......@@ -73,6 +74,7 @@ dependencies {
api (rootProject.ext.dependencies["ydl-platform"]) {
transitive = true
}
implementation rootProject.ext.dependencies["ydl-flutter-base"]
}
api rootProject.ext.dependencies["ydl-flutter"]
}
package com.yidianling.muse.activity
import com.alibaba.android.arouter.facade.annotation.Route
import com.example.fm_plugin.base.BaseFlutterActivity
import com.channel.ydl_flutter_base.base.BaseFlutterActivity
import com.ydl.media.audio.AudioPlayer
import com.ydl.ydlcommon.router.IYDLRouterConstant
import com.yidianling.muse.handler.MusePlugin
......
......@@ -75,15 +75,19 @@ dependencies {
//开发时使用
api project(':ydl-webview')
api project(':ydl-platform')
implementation project(':ydl-flutter-base')
implementation modularPublication('com.ydl:m-test-api')
implementation modularPublication('com.ydl:m-user-api')
}else {
//发布时使用
compileOnly rootProject.ext.dependencies["ydl-m-tests-api"]
compileOnly rootProject.ext.dependencies["ydl-m-user-api"]
api rootProject.ext.dependencies["ydl-webview"]
api(rootProject.ext.dependencies["ydl-platform"]) {
transitive = true
}
implementation rootProject.ext.dependencies["ydl-flutter-base"]
}
}
\ No newline at end of file
......@@ -20,7 +20,7 @@
/>
<!--测评首页-->
<activity
android:name=".home.TestHomeActivity"
android:name=".home.NewTestHomeActivity"
android:screenOrientation="portrait"
android:theme="@style/tests_NoTitleTheme"
/>
......
package com.yidianling.tests.home
import com.alibaba.android.arouter.facade.annotation.Route
import com.channel.ydl_flutter_base.base.BaseFlutterActivity
import com.yidianling.tests.home.plugin.TestPlugin
import org.json.JSONObject
/**
* flutter版测评首页
* Created by xj on 2019/11/19.
*/
@Route(path = "/ceshi/home")
class NewTestHomeActivity : BaseFlutterActivity() {
override fun initialRoute(): String {
return "test/home"
}
override fun initChannelPlugin(jsonObject: JSONObject) {
TestPlugin.Companion.rigister(this)
}
}
\ No newline at end of file
......@@ -39,7 +39,7 @@ import org.json.JSONObject
* @Company 壹点灵
* @date 2018/7/26
*/
@Route(path = "/ceshi/home")
//@Route(path = "/ceshi/home")
class TestHomeActivity : BaseMvpActivity<ITestHomeContract.View,ITestHomeContract.Presenter>(), ITestHomeContract.View, SwipeToLoadHelper.LoadMoreListener, SwipeRefreshLayout.OnRefreshListener {
/**
......
package com.yidianling.tests.home.plugin
import android.app.Activity
import android.net.Uri
import android.util.Log
import com.ydl.webview.H5Params
import com.ydl.webview.NewH5Activity
import com.ydl.ydl_router.manager.YDLRouterManager
import com.ydl.ydl_router.manager.YDLRouterParams
import com.ydl.ydlcommon.base.config.HttpConfig
import com.ydl.ydlcommon.router.IYDLRouterConstant
import com.yidianling.tests.list.view.TestCategoryListActivity
import com.yidianling.tests.router.TestsIn
import com.yidianling.tests.search.TestSearchActivity
import io.flutter.app.FlutterActivity
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
/**
* Created by xj on 2019/11/19.
*/
class TestPlugin : MethodChannel.MethodCallHandler {
private var mActivity: FlutterActivity? = null
private constructor(activity: FlutterActivity) {
mActivity = activity
}
companion object {
const val CHANNEL: String = "lib/test_module/channel"
const val GO_BACK = "goBack" // 左上角返回按钮
const val GO_SEARCH = "goSearch" // 查找测评
const val GO_MY_TESTS = "goMyTests" // 我的测评
const val BANNER_CLICK = "bannerClick" // Banner点击
const val CATEGORY_CLICK = "categoryClick" // 分类点击
const val ADVERT_CLICK = "advertClick" // 实时测评点击
const val TEST_LIST_ITEM_CLICK = "testListItemClick" // 测评列表点击
const val LOOK_ALL_TEST = "lookAllTest" // 查看全部测评
fun rigister(activity: FlutterActivity) {
MethodChannel(activity.flutterView, CHANNEL).setMethodCallHandler(TestPlugin(activity))
}
}
override fun onMethodCall(methodCall: MethodCall, result: MethodChannel.Result) {
when (methodCall.method) {
GO_BACK -> {
mActivity?.finish()
}
GO_SEARCH -> {
mActivity?.let {
TestSearchActivity.start(mActivity!!)
}
}
GO_MY_TESTS -> {
if (!TestsIn.isLogin()) {
//如果未登录 走登录逻辑
mActivity?.startActivity(TestsIn.loginWayIntent(mActivity!!))
return
}
val testParam = H5Params(HttpConfig.MH5_URL + "ceshi/my-test", "测试记录")
mActivity?.let {
NewH5Activity.start(mActivity, testParam)
}
}
BANNER_CLICK -> {
var linkUrl: String? = methodCall.argument<String>("linkUrl")
linkUrl?.let {
link(linkUrl)
}
}
CATEGORY_CLICK -> {
mActivity?.let {
var tabName: String = methodCall.argument<String>("tabName")?:""
TestCategoryListActivity.start(mActivity!!, tabName)
}
}
ADVERT_CLICK -> {
var linkUrl: String? = methodCall.argument<String>("linkUrl")
linkUrl?.let {
link(linkUrl)
}
}
TEST_LIST_ITEM_CLICK -> {
var linkUrl: String? = methodCall.arguments.toString()
linkUrl?.let {
link(linkUrl)
}
}
LOOK_ALL_TEST -> {
mActivity?.let {
TestCategoryListActivity.start(mActivity!!)
}
}
}
}
private fun link(linkUrl: String?) {
if (null == linkUrl || linkUrl.isEmpty()) {
return
}
if (linkUrl.startsWith("app")) {
val uri = Uri.parse(linkUrl)
if ("ceshi" == uri.host) {
val id = uri.getQueryParameter("id")
// YDLRouterManager.router(IYDLRouterConstant.ROUTER_TEST_DETAIL, YDLRouterParams().putExtra(IYDLRouterConstant.EXTRA_ID, id))
TestsIn.getTestsImpl().testDetailH5(id)
}
} else if (linkUrl.startsWith("http")) {
YDLRouterManager.router(
IYDLRouterConstant.ROUTER_H5_H5,
YDLRouterParams().putExtra(IYDLRouterConstant.EXTRA_URL, linkUrl), "")
} else {
YDLRouterManager.router(linkUrl)
}
}
}
\ No newline at end of file
......@@ -16,7 +16,6 @@ import com.ydl.ydlcommon.utils.SharedPreferencesEditor
import com.yidianling.common.tools.LogUtil
import com.yidianling.tests.R
import com.yidianling.tests.TestRetrofitApi
import com.yidianling.tests.home.TestHomeActivity
import com.yidianling.tests.home.bean.TestHomeBodyBean
import com.yidianling.tests.home.bean.TestHomeDataBean
import com.yidianling.tests.home.config.ITestHomeConfig
......@@ -145,11 +144,6 @@ class TestHomeUtils {
return dataList
}
fun jumpTestHomeActivity(context: Context) {
val intent = Intent(context, TestHomeActivity::class.java)
context.startActivity(intent)
}
/**
* 返回拼接好的人气字符串
......
/*___Generated_by_IDEA___*/
package com.yidianling.router;
/* This stub is only used by the IDE. It is NOT the BuildConfig class actually packed into the APK */
public final class BuildConfig {
public final static boolean DEBUG = Boolean.parseBoolean(null);
}
\ No newline at end of file
/*___Generated_by_IDEA___*/
package com.yidianling.router;
/* This stub is only used by the IDE. It is NOT the Manifest class actually packed into the APK */
public final class Manifest {
}
\ No newline at end of file
/*___Generated_by_IDEA___*/
package com.yidianling.router;
/* This stub is only used by the IDE. It is NOT the R class actually packed into the APK */
public final class R {
}
\ No newline at end of file
package com.yidianling.router
/**
* 模块枚举
* Created by hgw on 2018/2/10.
*/
enum class RouterEnum {
APP, //主程序模块
TESTS, //测试模块
DYNAMIC, //动态模块
USER, //用户模块
IM, //im模块
CONSULTANT,//咨询模块
COURSE, //课程模块
PHONECALL, //倾诉模块
FM, //fm模块
}
\ No newline at end of file
package com.yidianling.router
import com.yidianling.router.app.IAppRouter
import com.yidianling.router.consultant.IConsultantRouter
import com.yidianling.router.course.ICourseRouter
import com.yidianling.router.dynamic.IDynamicRouter
import com.yidianling.router.fm.IFMRouter
import com.yidianling.router.iRouter.BaseRouter
import com.yidianling.router.im.IIMRouter
import com.yidianling.router.phoneCall.IPhoneCallRouter
import com.yidianling.router.tests.ITestsRouter
import com.yidianling.router.user.IUserRouter
/**
* 模块管理器
* Created by hgw on 2018/3/15.
*/
object RouterManager {
private val routerMap: HashMap<RouterEnum, BaseRouter> = HashMap()
/**
* 注册module
*/
fun registerRouter(router : BaseRouter, moudleEnum: RouterEnum){
routerMap.put(moudleEnum,router)
}
// 获取用户模块路由的实例
fun getUserRouter(): IUserRouter? {
return routerMap[RouterEnum.USER] as IUserRouter
}
fun getImRouter(): IIMRouter {
return routerMap[RouterEnum.IM] as IIMRouter
}
fun getCourseRouter() : ICourseRouter?{
return routerMap[RouterEnum.COURSE] as ICourseRouter
}
fun getConsultantRouter() : IConsultantRouter?{
return routerMap.get(RouterEnum.CONSULTANT) as IConsultantRouter
}
fun getDynamicRouter() : IDynamicRouter?{
return routerMap.get(RouterEnum.DYNAMIC) as IDynamicRouter
}
fun getFMRouter() : IFMRouter?{
return routerMap.get(RouterEnum.FM) as IFMRouter
}
fun getPhoneCallRouter() : IPhoneCallRouter?{
return routerMap.get(RouterEnum.PHONECALL) as IPhoneCallRouter
}
fun getTestsRouter() : ITestsRouter?{
return routerMap.get(RouterEnum.TESTS) as ITestsRouter
}
fun getAppRouter(): IAppRouter? {
return routerMap[RouterEnum.APP] as IAppRouter
}
}
\ No newline at end of file
package com.yidianling.router.app
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.yidianling.router.iRouter.BaseRouter
/**
* author : Zhangwenchao
* e-mail : zhangwch@yidianling.com
* time : 2018/05/04
*/
interface IAppRouter: BaseRouter {
fun mainIntent(activity: Activity): Intent
fun mainIntent(context: Context, selectTab: Int, animation: Boolean): Intent
fun myRedPockIntent(activity: Activity): Intent
fun receiverRedPacketIntent(activity: Activity, param: ReceiveRedPacketParam): Intent
fun personalInfoIntent(activity: Activity): Intent
fun rechargeIntent(activity: Activity): Intent
fun splashIntent(activity: Activity): Intent
fun cleanUnReadnum()
fun feedBackIntent(activity: Activity): Intent
fun sendRedPacketIntent(activity: Activity, toUid: String): Intent
}
\ No newline at end of file
package com.yidianling.router.app
import android.os.Parcel
import android.os.Parcelable
/**
* author : Zhangwenchao
* e-mail : zhangwch@yidianling.com
* time : 2018/05/04
*/
data class ReceiveRedPacketParam(
var status: Int = 0,
var money: String? = null,
var name: String? = null,
var headUrl: String? = null,
var expertUrl: String? = null,
var expertName: String? = null,
var expertTime: String? = null) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readInt(),
parcel.readString(),
parcel.readString(),
parcel.readString(),
parcel.readString(),
parcel.readString(),
parcel.readString()) {
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(status)
parcel.writeString(money)
parcel.writeString(name)
parcel.writeString(headUrl)
parcel.writeString(expertUrl)
parcel.writeString(expertName)
parcel.writeString(expertTime)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<ReceiveRedPacketParam> {
override fun createFromParcel(parcel: Parcel): ReceiveRedPacketParam {
return ReceiveRedPacketParam(parcel)
}
override fun newArray(size: Int): Array<ReceiveRedPacketParam?> {
return arrayOfNulls(size)
}
}
}
\ No newline at end of file
package com.yidianling.router.consultant
import android.app.Activity
import android.content.Intent
import com.yidianling.router.iRouter.BaseRouter
interface IConsultantRouter : BaseRouter{
fun expertSearchIntent(activity: Activity, category: Int, showType: Int, isInitShowHot: Boolean): Intent
fun getHotSearch(): MutableList<Keyworks>
fun setHotSearch(hotSearch: MutableList<Keyworks>)
}
\ No newline at end of file
package com.yidianling.router.consultant
/**
* Created by haorui on 2019/3/3.
* Des:
*/
class Keyworks {
/**
* id
*/
var id: Int = 0
/**
* 跳转地址
*/
var url: String? = null
/**
* 关键字
*/
var keyword: String? = null
}
\ No newline at end of file
package com.yidianling.router.course
import android.app.Activity
import com.yidianling.router.iRouter.BaseRouter
/**
* Created by hgw on 2018/5/3.
*/
interface ICourseRouter : BaseRouter{
/**
* 课程专题页面
*/
fun courseTopic(activity: Activity,id : String)
fun closePlayer()
fun isPlaying(): Boolean
fun startCoursePlayPage(activity: Activity,from : Int) //from 1表示通知栏点进来,2表示右上角快捷按钮
fun play()
fun pause()
// fun courseIntent(activity: Activity, courseId: Int, courseType: Int)
}
\ No newline at end of file
package com.yidianling.router.dynamic
import android.app.Activity
import android.content.Context
import android.content.Intent
import com.yidianling.router.iRouter.BaseRouter
interface IDynamicRouter : BaseRouter {
fun membersIntent(activity: Activity, userId: String): Intent
fun replyInfoIntent(activity: Activity, replyId: String, aid: String): Intent
fun trendsDetailIntent(activity: Activity, trendId: Int): Intent
fun trendsDetailIntent(activity: Activity, trendId: Int, isScrollToZan: Boolean): Intent
fun trendsDetailIntent(activity: Activity, trendId: Int, isScrollToZan: Boolean, lastId: Int): Intent
fun publishTrendIntent(activity: Activity, url: String, cover: String, title: String): Intent
fun publishFmToTrend(activity: Activity, url: String, cover: String, title: String): Intent
fun topicDetailIntent(context: Context, isSplash: Boolean, topicId: String): Intent
fun publishArticleToTrend(activity: Activity, url: String, cover: String, title: String): Intent
}
\ No newline at end of file
package com.yidianling.router.fm
import android.app.Activity
import android.content.Intent
import com.yidianling.router.iRouter.BaseRouter
interface IFMRouter : BaseRouter{
fun fmDetailIntent(activity: Activity, id: Int): Intent
fun fmDetailIntent(activity: Activity, id: Int, isSplash: Boolean): Intent
fun getFmId(): Int
fun closePlayer()
fun isPlaying(): Boolean
fun replay()
fun pause()
}
\ No newline at end of file
package com.yidianling.router.iRouter
/**
* 路由接口
* Created by hgw on 2018/2/10.
*/
interface BaseRouter {
}
\ No newline at end of file
package com.yidianling.router.im
import android.app.Activity
import android.content.Context
import android.support.v7.app.AppCompatActivity
import com.yidianling.router.iRouter.BaseRouter
/**
* author : Zhangwenchao
* e-mail : zhangwch@yidianling.com
* time : 2018/04/23
*
* im 模块对外提供方法的接口
*/
interface IIMRouter : BaseRouter {
// 打开与小壹聊天界面
fun startP2PXiaoYi(context: Context)
// 打开私聊界面
fun startP2PSession(context: AppCompatActivity, toUid: String)
fun setAccount(account: String)
fun setChattingAccountAll()
fun setChattingAccountNone()
fun login(info: IMLoginInfo, callback: IMRequestCallback<IMLoginInfo>?)
/**
* 绑定手机后,重新登录IM
*/
fun imLogin(info: IMLoginInfo)
fun logout()
fun clear()
/**
* 创建一条普通文本消息
* @param sessionId 聊天对象ID
* @param content 文本消息内容
*/
fun createTextMessage(sessionId: String?, content: String, callback: IMRequestCallback<Void>)
fun sendSubscriptionTimeMessage(sessionId: String?, content: String, callback: IMRequestCallback<Void>)
fun showSelector(activity: Activity, requestCode: Int)
fun sendTestResultMessage(uid: String, content: String, title: String?, head: String?, url: String?, id: Int, share_url: String?, callback: IMRequestCallback<Void>)
fun startChat(context : AppCompatActivity ,toUid : String ,flag : Int , canTalk : Int)
}
\ No newline at end of file
package com.yidianling.router.im;
/**
* Created by xj on 2019/6/27.
*/
public class IMDoctorBriefInfo {
public String smallImage = "";
public double feedbackRate = 0.000;
public int orderNum = 0;
public int helpLong = 0;
}
package com.yidianling.router.im;
/**
* Created by hgw on 2018/3/13.
*/
public class IMExpertBuild {
public IMShareDataBuild shareData;
public String tips; //禁言提示内容
public String url;//禁言帮助URL
public IMDoctorBriefInfo doctorBriefInfo = new IMDoctorBriefInfo();
}
\ No newline at end of file
package com.yidianling.router.im
/**
* author : Zhangwenchao
* e-mail : zhangwch@yidianling.com
* time : 2018/03/23
* 登录 im 时传入的参数,对应 im 模块中的 IMLoginInfo
*/
data class IMLoginInfo(val account: String, val passWord: String)
\ No newline at end of file
package com.yidianling.router.im
/**
* author : Zhangwenchao
* e-mail : zhangwch@yidianling.com
* time : 2018/03/23
* 登录 im 时传入的接口,对应 im 模块中的 IMRequestCallback
*/
interface IMRequestCallback<in T> {
fun onSuccess(t: T?)
fun onFailed(i: Int)
fun onException(throwable: Throwable?)
}
\ No newline at end of file
package com.yidianling.router.im;
import com.google.gson.annotations.SerializedName;
/**
* Created by hgw on 2018/3/13.
*/
public class IMShareDataBuild {
public String toUid;
public String doctorId;
public String listenerId;
// public String name;
public String title;
@SerializedName(value = "isOnline", alternate = "is_online")
public int is_online;//专家倾述在线状态 1.在线 2.离线 3通话中 4-继续拨打 ,
public int isChatOnline;//专家私聊在线状态 1.在线 2.离线
public String cover;
public String desc;
@SerializedName(value = "urlShare", alternate = "url_share")
public String url_share;
public String price;
public String unitTxt;
public String url;
public String urlTitle;
public String blackStatus;
@SerializedName(value = "userType", alternate = "user_type")
public int user_type;//1用户,2专家,3助理
public int is_first;//1表示第一次聊天0不是
public paramsBuild params = new paramsBuild();
public int hasAvailableListenOrder;//是否还有未完成的倾诉订单 1、没有 2、有
public int listenOrderCommentStatus;//倾诉订单评论状态 1:未评价 9:已评价 ,
public String listenOrderCommentUrl;//倾诉订单评价页url
public String listenOrderUrl;//订单详情页面url
public String listenOrderDesc;//倾诉订单状态描述
public String listenOrderRemainTime;//未完成倾诉订单剩余时间
@SerializedName(value = "orderUrl", alternate = "order_url")
public String order_url;
@SerializedName(value = "name", alternate = "doctorName")
public String doctorName;
/**
* 是否开启电话倾诉
* 1.开启 2.关闭
*/
public int listenerIsOpen;
public static class paramsBuild {
//php接口此字段返回值始终为0,更换java接口后,服务端去掉了此字段,本地改动太多,先给默认值
public String orderid = "0";
}
public String tag1 = ""; //该字段是私聊改造项目新加的,用于在获取推荐专家列表的时候,catName字段
}
\ No newline at end of file
package com.yidianling.router.phoneCall
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.support.v4.app.DialogFragment
import com.yidianling.router.iRouter.BaseRouter
interface IPhoneCallRouter : BaseRouter{
fun phoneCallIntent(activity: Activity): Intent
fun phoneCallFragment(head: String, callId: String): DialogFragment
fun closePlayer()
//倾诉首页
fun openConfideHome(context: Context)
}
\ No newline at end of file
package com.yidianling.router.tests
import android.app.Activity
import android.content.Intent
import com.yidianling.router.iRouter.BaseRouter
interface ITestsRouter : BaseRouter{
fun testDetailIntent(activity: Activity, testId: Int): Intent
fun testResultIntent(activity: Activity, testResultId: Int): Intent
fun testAnswerIntent(activity: Activity): Intent
fun testDetailH5(testId: String)
fun testH5Result(testResultId: String)
}
\ No newline at end of file
package com.yidianling.router.user
import android.app.Activity
import android.content.Context
import android.content.Intent
import com.yidianling.router.iRouter.BaseRouter
/**
* author : Zhangwenchao
* e-mail : zhangwch@yidianling.com
* time : 2018/04/13
*/
interface IUserRouter : BaseRouter {
// 是否已登录
fun isLogin(): Boolean
fun isFirstLogin(): Boolean
fun setFirstLogin(first: Boolean)
fun isSafePrivacyClicked(): Boolean
fun putSafePrivacyClicked(clicked: Boolean)
fun setUserResponse(userInfo: UserResponse?)
fun getUserInfo(): UserResponse.UserInfo?
fun getUserResponse() : UserResponse?
fun getUserSetting():UserSetting?
fun isBindPhone(): Boolean
fun putUnlockCheckSuccessTime(time: Long)
fun getChatTeamHisShow(): Boolean
fun setChatTeamHisShowed(showed : Boolean)
// 跳转到隐私界面的 Activity
fun privacyIntent(activity: Activity): Intent
fun loginWayIntent(context: Context): Intent
fun inputPhoneIntent(activity: Activity, smsAction: String): Intent
fun safeTipViewGone(): Boolean
fun setTrendsSafeTip(status: Boolean)
fun errorAgainTime(): Long
fun isFirstStart(): Boolean
fun updateUserInfoSp(userInfo: UserResponse.UserInfo?)
fun updateUserSetingSp(userSetting: UserSetting?)
fun clearUserInfo()
}
\ No newline at end of file
package com.yidianling.router.user
import com.google.gson.annotations.SerializedName
import com.yidianling.router.RouterManager
/**
* author : hgw
* time : 2018/02/02
*/
class UserResponse {
var uid: String? = null
var accessToken: String? = null
var firstLogin: Int = 0 //1是 2否
var hxpwd: String? = null
var userInfo: UserInfo? = UserInfo()
set(value) {
field = value
RouterManager.getUserRouter()?.setUserResponse(this)
}
inner class UserInfo {
var uid: String = "0"
@field:SerializedName("userName")
var user_name: String? = null
var accessToken: String? = null//
@field:SerializedName("bindPhone")
var bind_phone: Int? = 0 //1为绑定
var phone: String? = null
set(value) {
if (!(value?.equals(phone) ?: false)) {
field = value
RouterManager.getUserRouter()?.updateUserInfoSp(this)
}
}
@field:SerializedName("realName")
var real_name: String? = null
@field:SerializedName("nickName")
var nick_name: String? = null
set(value) {
if (!(value?.equals(nick_name) ?: false)) {
field = value
RouterManager.getUserRouter()?.updateUserInfoSp(this)
}
}
var head: String? = null
set(value) {
if (!(value?.equals(head) ?: false)) {
field = value
RouterManager.getUserRouter()?.updateUserInfoSp(this)
}
}
var gender: Int = 0
//性别1男2女
set(value) {
if (value != gender) {
field = value
RouterManager.getUserRouter()?.updateUserInfoSp(this)
}
}
var birthday: String? = null
set(value) {
if (!(value?.equals(birthday) ?: false)) {
field = value
RouterManager.getUserRouter()?.updateUserInfoSp(this)
}
}
@field:SerializedName("availableMoney")
var available_money: String? = null
var address: String? = null
@field:SerializedName("unionId")
var union_id: String? = null//微信标识,
@field:SerializedName("openIdQqweb")
var open_id_qqapp: String? = null//qq标识
@field:SerializedName("bindWeixin")
var bind_weixin: Int = 0
//是否绑定微信1绑定0未绑定
set(value) {
if (value != bind_weixin) {
field = value
RouterManager.getUserRouter()?.updateUserInfoSp(this)
}
}
@field:SerializedName("bindQq")
var bind_qq: Int = 0
//是否绑定qq 1绑定0未绑定
set(value) {
if (value != bind_qq) {
field = value
RouterManager.getUserRouter()?.updateUserInfoSp(this)
}
}
@field:SerializedName("userType")
var user_type: Int = 0//1普通用户2心理专家
@field:SerializedName("listenCards")
var listen_cards: Int = 0//收听卡的次数
var profession: Int = 0
set(value) {
if (value != profession) {
field = value
RouterManager.getUserRouter()?.updateUserInfoSp(this)
}
}
var marriage: Int = 0
set(value) {
if (value != marriage) {
field = value
RouterManager.getUserRouter()?.updateUserInfoSp(this)
}
}
@field:SerializedName("countryCode")
var country_code: String? = null
@field:SerializedName("homeBg")
var home_bg: String? = null
//我的封面地址
set(value) {
if (!(value?.equals(home_bg) ?: false)) {
field = value
RouterManager.getUserRouter()?.updateUserInfoSp(this)
}
}
//是否同意过隐私权限 1同意过 0未同意
var privacyAgreementStatus: Int = 1
set(value) {
if (value != privacyAgreementStatus) {
field = value
RouterManager.getUserRouter()?.updateUserInfoSp(this)
}
}
var description: String? = null
//简介
set(value) {
if (!(value?.equals(description) ?: false)) {
field = value
RouterManager.getUserRouter()?.updateUserInfoSp(this)
}
}
@field:SerializedName("isSilenced")
var is_silenced: Int = 0 // 1 正常 2 禁言
set(value) {
if (value != is_silenced) {
field = value
RouterManager.getUserRouter()?.updateUserInfoSp(this)
}
}
var hasCoupon: Int = 0
var firstLogin: Int = 0 //1是2否
set(value) {
if (value != firstLogin) {
field = value
RouterManager.getUserRouter()?.updateUserInfoSp(this)
}
}
var trendNum: Int = 0 //我的动态
set(value) {
if (value != trendNum) {
field = value
RouterManager.getUserRouter()?.updateUserInfoSp(this)
}
}
var fansNum: Int = 0
//我的粉丝
set(value) {
if (value != fansNum) {
field = value
RouterManager.getUserRouter()?.updateUserInfoSp(this)
}
}
var testRecordNum: Int = 0
//测试记录
set(value) {
if (value != testRecordNum) {
field = value
RouterManager.getUserRouter()?.updateUserInfoSp(this)
}
}
var attentionNum: Int = 0
//我的关注
set(value) {
if (value != attentionNum) {
field = value
RouterManager.getUserRouter()?.updateUserInfoSp(this)
}
}
var registTime: String? = null
//你我相识已502天
set(value) {
if (!(value?.equals(registTime) ?: false)) {
field = value
RouterManager.getUserRouter()?.updateUserInfoSp(this)
}
}
var privacyArr: PrivacyArr? = null
override fun toString(): String {
return "UserInfo(uid='$uid', user_name=$user_name, accessToken=$accessToken, bind_phone=$bind_phone, phone=$phone, real_name=$real_name, nick_name=$nick_name, head=$head, gender=$gender, birthday=$birthday, available_money=$available_money, address=$address, union_id=$union_id, open_id_qqapp=$open_id_qqapp, bind_weixin=$bind_weixin, bind_qq=$bind_qq, user_type=$user_type, listen_cards=$listen_cards, profession=$profession, marriage=$marriage, country_code=$country_code, home_bg=$home_bg, description=$description, is_silenced=$is_silenced, hasCoupon=$hasCoupon, firstLogin=$firstLogin, trendNum=$trendNum, fansNum=$fansNum, testRecordNum=$testRecordNum, attentionNum=$attentionNum, registTime=$registTime)"
}
}
inner class PrivacyArr {
var time: String? = null
var content: String? = null
}
override fun toString(): String {
return "UserResponse(uid=$uid, accessToken=$accessToken, firstLogin=$firstLogin, hxpwd=$hxpwd, userInfo=$userInfo)"
}
}
\ No newline at end of file
include ':app',":router", ':ydl-net', ':ydl-utils', ':ydl-platform', ':ydl-webview',
include ':app', ':ydl-flutter-base', ':ydl-net', ':ydl-utils', ':ydl-platform', ':ydl-webview',
':m-confide', ':m-audioim',':ydl-media',":m-user", ':m-consultant', ':m-muse',
':m-fm', ':m-tests',":m-course", ':ydl-pay'
......
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-android-extensions'
apply from: "../maven_push.gradle"
kapt {
arguments {
arg("AROUTER_MODULE_NAME", project.getName())
}
}
android {
compileSdkVersion 28
compileSdkVersion rootProject.ext.android["compileSdkVersion"]
buildToolsVersion rootProject.ext.android["buildToolsVersion"]
defaultConfig {
minSdkVersion 14
targetSdkVersion 28
minSdkVersion rootProject.ext.android["minSdkVersion"]
targetSdkVersion rootProject.ext.android["targetSdkVersion"]
versionCode 1
versionName "1.0"
......@@ -26,19 +36,50 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
publishNonDefault true
compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
api fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
implementation 'com.android.support:appcompat-v7:26.1.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
implementation project(":ydl-platform")
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation "com.alibaba:arouter-api:$arouter_api"
// 注意此处的依赖方式:kotlin中使用和java中使用方式有不同
kapt "com.alibaba:arouter-compiler:$arouter_compiler"
if (rootProject.ext.dev_mode){
//开发时使用
implementation project(":ydl-platform")
// api fileTree(include: ['*.aar'], dir: 'aars')
api rootProject.ext.dependencies["ydl-flutter"]
api rootProject.ext.dependencies["ydl-flutter-sp"]
} else {
//发布时使用
api (rootProject.ext.dependencies["ydl-platform"]) {
transitive = true
}
//以下为flutter端的所有aar
api rootProject.ext.dependencies["ydl-flutter"]
api rootProject.ext.dependencies["ydl-flutter-sp"]
}
}
repositories {
flatDir {
dirs 'libs'
}
mavenCentral()
}
}
\ No newline at end of file
package com.yidianling.router;
package com.channel.xj.ydl_flutter_base;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
......@@ -21,6 +21,6 @@ public class ExampleInstrumentedTest {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.yidianling.router.test", appContext.getPackageName());
assertEquals("com.channel.xj.m_flutter_base.test", appContext.getPackageName());
}
}
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.yidianling.router" />
package="com.channel.ydl_flutter_base" />
package com.channel.ydl_flutter_base.base
import android.content.Context
import android.content.Intent
import android.graphics.PixelFormat
import android.os.Bundle
import android.util.AttributeSet
import android.view.WindowManager
import com.channel.ydl_flutter_base.plugin.YDLCommonPlugin
import io.flutter.app.FlutterActivity
import io.flutter.plugins.GeneratedPluginRegistrant
import io.flutter.view.FlutterView
import org.json.JSONObject
/**
* flutter 页面基类
* Created by xj on 2019/9/30.
*/
abstract class BaseFlutterActivity : FlutterActivity() {
companion object {
const val ROUTE_PAGE = "route" //路由
/**
* 路由传递过来的参数
*/
const val ROUTER_PARAMS = "routerParam"
}
override fun createFlutterView(context: Context?): FlutterView {
val matchParent = WindowManager.LayoutParams(-1, -1)
val nativeView = this.createFlutterNativeView()
val flutterView = FlutterView(this, null as AttributeSet?, nativeView)
flutterView.layoutParams = matchParent
this.setContentView(flutterView)
//这个action必加,不然无法在flutter端获取route参数值
intent.action = Intent.ACTION_RUN
/**
* 在这边初始化route
*/
intent.putExtra(ROUTE_PAGE, initialRoute())
//渲染优化(优化一丢丢几乎看不出来)
flutterView.setZOrderOnTop(true)
flutterView.holder.setFormat(PixelFormat.TRANSLUCENT)
return flutterView
}
public override fun onCreate( savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
GeneratedPluginRegistrant.registerWith(this)
/**
* 获取统一路由传递过来的参数并初始化交互通道
*/
val json = intent.getStringExtra(ROUTER_PARAMS)
var jb = JSONObject()
if (json!=null){
jb = JSONObject(json)
}
YDLCommonPlugin.activityRegister(this)
initChannelPlugin(jb)
}
/**
* 路由,例 "/native/muse/home"
*/
abstract fun initialRoute(): String
/**
* 初始化flutter channel插件
*/
abstract fun initChannelPlugin(jsonObject: JSONObject)
}
\ No newline at end of file
package com.channel.ydl_flutter_base.base
import android.os.Bundle
import android.view.LayoutInflater
import android.view.ViewGroup
import com.channel.ydl_flutter_base.plugin.YDLCommonPlugin
import io.flutter.view.FlutterView
/**
* flutter fragment 基类
* Created by xj on 2019/9/30.
*/
abstract class BaseFlutterFragment : FlutterFragment() {
private var mFlutterView: FlutterView? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): FlutterView {
mFlutterView= Flutter.createView(
activity!!,
lifecycle,
initialRoute())
YDLCommonPlugin.fragmentRegister(this, mFlutterView!!)
initChannelPlugin(mFlutterView!!)
return mFlutterView!!
}
/**
* 路由,例 "quick_reply"
*/
abstract fun initialRoute(): String
/**
* 初始化flutter channel插件
*/
abstract fun initChannelPlugin(flutterView: FlutterView)
}
package com.channel.ydl_flutter_base.base;
import android.app.Activity;
import android.arch.lifecycle.Lifecycle;
import android.arch.lifecycle.LifecycleObserver;
import android.arch.lifecycle.OnLifecycleEvent;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import com.example.fm_plugin.base.FlutterFragment;
import io.flutter.plugin.common.BasicMessageChannel;
import io.flutter.plugin.common.StringCodec;
import io.flutter.plugins.GeneratedPluginRegistrant;
import io.flutter.view.FlutterMain;
import io.flutter.view.FlutterNativeView;
import io.flutter.view.FlutterRunArguments;
import io.flutter.view.FlutterView;
/**
* Main entry point for using Flutter in Android applications.
*
* <p><strong>Warning:</strong> This file is auto-generated by Flutter tooling.
* DO NOT EDIT.</p>
*/
public final class Flutter {
private Flutter() {
// to prevent instantiation
}
/**
* Initiates the Dart VM. Calling this method at an early point may help decreasing time to first
* frame for a subsequently created {@link FlutterView}.
*
* @param applicationContext the application's {@link Context}
*/
public static void startInitialization(@NonNull Context applicationContext) {
FlutterMain.startInitialization(applicationContext);
}
/**
* Creates a {@link com.example.fm_plugin.base.FlutterFragment} managing a {@link FlutterView}. The optional
* initial route string will be made available to the Dart code
* (via {@code window.defaultRouteName}) and may be used to determine which widget
* should be displayed in the view. The default initialRoute is "/".
*
* @param initialRoute an initial route {@link String}, or null
* @return a {@link com.example.fm_plugin.base.FlutterFragment}
*/
@NonNull
public static com.example.fm_plugin.base.FlutterFragment createFragment(String initialRoute) {
final com.example.fm_plugin.base.FlutterFragment fragment = new com.example.fm_plugin.base.FlutterFragment();
final Bundle args = new Bundle();
args.putString(FlutterFragment.ARG_ROUTE, initialRoute);
fragment.setArguments(args);
return fragment;
}
/**
* Creates a {@link FlutterView} linked to the specified {@link Activity} and {@link Lifecycle}.
* The optional initial route string will be made available to the Dart code (via
* {@code window.defaultRouteName}) and may be used to determine which widget should be displayed
* in the view. The default initialRoute is "/".
*
* @param activity an {@link Activity}
* @param lifecycle a {@link Lifecycle}
* @param initialRoute an initial route {@link String}, or null
* @return a {@link FlutterView}
*/
@NonNull
public static FlutterView createView(@NonNull final Activity activity, @NonNull final Lifecycle lifecycle, final String initialRoute) {
FlutterMain.startInitialization(activity.getApplicationContext());
FlutterMain.ensureInitializationComplete(activity.getApplicationContext(), null);
final FlutterNativeView nativeView = new FlutterNativeView(activity);
final FlutterView flutterView = new FlutterView(activity, null, nativeView) {
private final BasicMessageChannel<String> lifecycleMessages = new BasicMessageChannel<>(this, "flutter/lifecycle", StringCodec.INSTANCE);
@Override
public void onFirstFrame() {
super.onFirstFrame();
setAlpha(1.0f);
}
@Override
public void onPostResume() {
// Overriding default behavior to avoid dictating system UI via PlatformPlugin.
lifecycleMessages.send("AppLifecycleState.resumed");
}
};
if (initialRoute != null) {
flutterView.setInitialRoute(initialRoute);
}
lifecycle.addObserver(new LifecycleObserver() {
@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
public void onCreate() {
final FlutterRunArguments arguments = new FlutterRunArguments();
arguments.bundlePath = FlutterMain.findAppBundlePath(activity.getApplicationContext());
arguments.entrypoint = "main";
flutterView.runFromBundle(arguments);
GeneratedPluginRegistrant.registerWith(flutterView.getPluginRegistry());
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
public void onStart() {
flutterView.onStart();
}
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void onResume() {
flutterView.onPostResume();
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
public void onPause() {
flutterView.onPause();
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
public void onStop() {
flutterView.onStop();
}
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
public void onDestroy() {
flutterView.destroy();
}
});
flutterView.setAlpha(0.0f);
return flutterView;
}
}
package com.channel.ydl_flutter_base.base;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import com.example.fm_plugin.base.Flutter;
import io.flutter.view.FlutterView;
/**
* A {@link Fragment} managing a {@link FlutterView}.
*
* <p><strong>Warning:</strong> This file is auto-generated by Flutter tooling.
* DO NOT EDIT.</p>
*/
public class FlutterFragment extends Fragment {
public static final String ARG_ROUTE = "route";
private String mRoute = "/";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mRoute = getArguments().getString(ARG_ROUTE);
}
}
@Override
public void onInflate(Context context, AttributeSet attrs, Bundle savedInstanceState) {
super.onInflate(context, attrs, savedInstanceState);
}
@Override
public FlutterView onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return Flutter.createView(getActivity(), getLifecycle(), mRoute);
}
}
package com.channel.ydl_flutter_base.plugin
import android.content.SharedPreferences
import android.text.TextUtils
import com.channel.ydl_flutter_base.BuildConfig
import com.channel.ydl_flutter_base.base.BaseFlutterFragment
import com.ydl.ydlcommon.base.BaseApp
import com.ydl.ydlcommon.data.PlatformDataManager
import com.ydl.ydlcommon.modular.ModularServiceManager
import com.ydl.ydlcommon.utils.SharedPreferencesEditor
import com.yidianling.common.tools.RxDeviceTool
import io.flutter.app.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.view.FlutterView
/**
* Created by xj on 2019/11/19.
*/
class YDLCommonPlugin : MethodChannel.MethodCallHandler {
constructor()
private var mActivity: FlutterActivity? = null
private constructor(activity: FlutterActivity) {
mActivity = activity
}
private var mFragment: BaseFlutterFragment? = null
private constructor(fragment: BaseFlutterFragment) {
mFragment = fragment
}
companion object {
const val CHANNEL: String = "lib/common/channel"
const val GETPUBLICPARAMAS: String = "getPublicParamas"
//activity注册
fun activityRegister(activity: FlutterActivity) {
MethodChannel(activity.flutterView, CHANNEL).setMethodCallHandler(YDLCommonPlugin(activity))
}
//fragment注册
fun fragmentRegister(fragment: BaseFlutterFragment, flutterView: FlutterView) {
MethodChannel(flutterView, CHANNEL).setMethodCallHandler(YDLCommonPlugin(fragment))
}
}
override fun onMethodCall(methodCall: MethodCall, result: MethodChannel.Result) {
when (methodCall.method) {
GETPUBLICPARAMAS -> {
val loginBean = ModularServiceManager.getPlatformUserService()?.getUser()
val mMap = mutableMapOf<String, Any>()
var uid = loginBean?.userId ?: ""
mMap["isDevelopment"] =BuildConfig.DEBUG
mMap["uid"] = if (TextUtils.isEmpty(uid)) "0" else uid
mMap["accessToken"] = loginBean?.token ?: ""
?: ""
mMap["isFromApp"] = "1"
mMap["ffrom"] = PlatformDataManager.getRam().getChannelName()
mMap["version"] = RxDeviceTool.getAppVersionName(BaseApp.getApp())
mMap["osBuild"] =
RxDeviceTool.getBuildMANUFACTURER() + "," + RxDeviceTool.getBuildBrandModel() + "," + RxDeviceTool.getOsBuileVersion() + "," + RxDeviceTool.getAppVersionName(
BaseApp.getApp()
)
var res = SharedPreferencesEditor.getString("flutter_proxy_sp_ip")
mMap["proxyIp"] = if (TextUtils.isEmpty(res)) "" else res
result.success(mMap)
}
}
}
}
\ No newline at end of file
<resources>
<string name="app_name">router</string>
<string name="app_name">m-flutter-base</string>
</resources>
......@@ -64,7 +64,7 @@ android {
}
dependencies {
api fileTree(include: ['*.aar'], dir: 'libs')
api fileTree(include: ['*.jar'], dir: 'libs')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:28.0.0'
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment