Commit d254304b by 刘鹏

Merge remote-tracking branch 'origin/feat/qj/new_nim2' into feat/lp/lp_nim2

# Conflicts:
#	app/src/main/java/com/ydl/component/MainActivity.kt
#	build.gradle
#	m-im/build.gradle
#	m-im/src/main/java/com/yidianling/im/helper/NimUICallInit.java
#	m-muse/build.gradle
#	settings.gradle
#	ydl-media/build.gradle
parents 40a5a6e7 385cfcd1
...@@ -177,6 +177,7 @@ android { ...@@ -177,6 +177,7 @@ android {
dependencies { dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.github.feeeei:CircleSeekbar:v1.1.2'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation(rootProject.ext.dependencies["appcompat-v7"]) implementation(rootProject.ext.dependencies["appcompat-v7"])
testImplementation 'junit:junit:4.13.2' testImplementation 'junit:junit:4.13.2'
......
...@@ -6,13 +6,21 @@ import android.content.ComponentName ...@@ -6,13 +6,21 @@ import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.ServiceConnection import android.content.ServiceConnection
import android.graphics.PixelFormat
import android.media.MediaPlayer
import android.net.Uri import android.net.Uri
import android.os.Build
import android.os.IBinder import android.os.IBinder
import android.provider.Settings import android.provider.Settings
import android.text.TextUtils import android.text.TextUtils
import android.util.DisplayMetrics
import android.util.Log import android.util.Log
import android.view.*
import android.widget.ImageView
import android.widget.TextView
import com.alibaba.android.arouter.facade.annotation.Route import com.alibaba.android.arouter.facade.annotation.Route
import com.alibaba.android.arouter.launcher.ARouter import com.alibaba.android.arouter.launcher.ARouter
import com.bumptech.glide.Glide
import com.tbruyelle.rxpermissions2.RxPermissions import com.tbruyelle.rxpermissions2.RxPermissions
import com.tencent.qcloud.tuicore.calling.trtccalling.model.TRTCCalling import com.tencent.qcloud.tuicore.calling.trtccalling.model.TRTCCalling
import com.tencent.qcloud.tuicore.calling.videocall.Constant import com.tencent.qcloud.tuicore.calling.videocall.Constant
...@@ -26,16 +34,23 @@ import com.ydl.component.route.PlatformTempCommonRouteImpl ...@@ -26,16 +34,23 @@ import com.ydl.component.route.PlatformTempCommonRouteImpl
import com.ydl.component.rtc.MDTLoginActivity import com.ydl.component.rtc.MDTLoginActivity
import com.ydl.confide.home.ConfideHomeActivity import com.ydl.confide.home.ConfideHomeActivity
import com.ydl.media.audio.PlayService import com.ydl.media.audio.PlayService
import com.ydl.ydlcommon.data.http.ThrowableConsumer
import com.ydl.ydlcommon.modular.ModularServiceManager import com.ydl.ydlcommon.modular.ModularServiceManager
import com.ydl.ydlcommon.mvp.lce.BaseLceActivity import com.ydl.ydlcommon.mvp.lce.BaseLceActivity
import com.ydl.ydlcommon.router.YdlCommonRouterManager import com.ydl.ydlcommon.router.YdlCommonRouterManager
import com.ydl.ydlcommon.utils.actionutil.ActionCountUtils
import com.yidianling.common.tools.LogUtil import com.yidianling.common.tools.LogUtil
import com.yidianling.common.tools.ToastUtil import com.yidianling.common.tools.ToastUtil
import com.yidianling.consultant.api.IConsultantService import com.yidianling.consultant.api.IConsultantService
import com.yidianling.fm.api.service.IFMService import com.yidianling.fm.api.service.IFMService
import com.yidianling.home.http.MuseHttp
import com.yidianling.muse.activity.PlayMeditationActivity
import com.yidianling.muse.bean.MeditationPlayModuleBean
import com.yidianling.muse.constants.MuseBIConstants
import com.yidianling.muse.event.MeditationFloatEvent import com.yidianling.muse.event.MeditationFloatEvent
import com.yidianling.muse.helper.FloatViewTouchListener
import com.yidianling.muse.helper.MediaPlayerManager import com.yidianling.muse.helper.MediaPlayerManager
import com.yidianling.muse.service.MeditationWindowService import com.yidianling.muse.utils.MediaPlayerTimeUtil
import com.yidianling.tests.home.NewTestHomeActivity import com.yidianling.tests.home.NewTestHomeActivity
import com.yidianling.user.api.service.IUserService import com.yidianling.user.api.service.IUserService
import com.yidianling.user.ui.collect.CollectSexAndBirthActivity import com.yidianling.user.ui.collect.CollectSexAndBirthActivity
...@@ -43,7 +58,16 @@ import com.yidianling.user.ui.login.OneKeyLoginHelp ...@@ -43,7 +58,16 @@ import com.yidianling.user.ui.login.OneKeyLoginHelp
import com.yidianling.user.widget.SecretDialog import com.yidianling.user.widget.SecretDialog
import com.yidianling.user.widget.SecretDialog.OnSecretDialogListener import com.yidianling.user.widget.SecretDialog.OnSecretDialogListener
import de.greenrobot.event.EventBus import de.greenrobot.event.EventBus
import io.feeeei.circleseekbar.CircleSeekBar
import io.reactivex.Observable
import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.activity_main.*
import java.util.*
import java.util.concurrent.TimeUnit
import kotlin.math.roundToInt
/** /**
...@@ -56,12 +80,10 @@ class MainActivity : BaseLceActivity<DemoContract.View, DemoContract.Presenter>( ...@@ -56,12 +80,10 @@ class MainActivity : BaseLceActivity<DemoContract.View, DemoContract.Presenter>(
// private var secretDescriptionDialog: SecretDescriptionDialog? = null // private var secretDescriptionDialog: SecretDescriptionDialog? = null
private var serviceConnection: ServiceConnection? = null private var serviceConnection: ServiceConnection? = null
private var meditationServiceConnection: ServiceConnection? = null
private var secretDialog: SecretDialog? = null private var secretDialog: SecretDialog? = null
protected var playService: PlayService? = null protected var playService: PlayService? = null
protected var meditationService: MeditationWindowService? = null private var mFloatDisposable:Disposable?=null
override fun getContentViewId(): Int { override fun getContentViewId(): Int {
return R.id.lce_content_view return R.id.lce_content_view
...@@ -99,7 +121,8 @@ class MainActivity : BaseLceActivity<DemoContract.View, DemoContract.Presenter>( ...@@ -99,7 +121,8 @@ class MainActivity : BaseLceActivity<DemoContract.View, DemoContract.Presenter>(
2 2
) )
bindService() bindService()
bindMeditationService()
initMeditationObserver()
reLoadData() reLoadData()
requestPermission() requestPermission()
...@@ -277,11 +300,18 @@ class MainActivity : BaseLceActivity<DemoContract.View, DemoContract.Presenter>( ...@@ -277,11 +300,18 @@ class MainActivity : BaseLceActivity<DemoContract.View, DemoContract.Presenter>(
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE) bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
} }
private fun bindMeditationService() { private fun initMeditationObserver(){
val intent = Intent() mFloatDisposable = Observable.interval(500,TimeUnit.MILLISECONDS)
intent.setClass(this, MeditationWindowService::class.java) .filter { true }
meditationServiceConnection = MeditationServiceConnection() .observeOn(AndroidSchedulers.mainThread())
bindService(intent, meditationServiceConnection, Context.BIND_AUTO_CREATE) .subscribe {
if (!windowIsShow && mToShowWindow){
showWindow()
}else if (windowIsShow && !mToShowWindow){
hideFloatWindow()
}
mCurrentMeditation?.let { it1 -> updateFloatView(it1) }
}
} }
override fun onResume() { override fun onResume() {
...@@ -318,11 +348,14 @@ class MainActivity : BaseLceActivity<DemoContract.View, DemoContract.Presenter>( ...@@ -318,11 +348,14 @@ class MainActivity : BaseLceActivity<DemoContract.View, DemoContract.Presenter>(
if (serviceConnection != null) { if (serviceConnection != null) {
unbindService(serviceConnection) unbindService(serviceConnection)
} }
if (meditationServiceConnection != null) {
EventBus.getDefault().post(MeditationFloatEvent(false)) MediaPlayerManager.getInstance(this)?.stop()
MediaPlayerManager.getInstance(this)?.stop()
unbindService(meditationServiceConnection) hideFloatWindow()
}
mDisposable?.dispose()
mFloatDisposable?.dispose()
if (EventBus.getDefault().isRegistered(this)) { if (EventBus.getDefault().isRegistered(this)) {
EventBus.getDefault().unregister(this) EventBus.getDefault().unregister(this)
} }
...@@ -339,17 +372,6 @@ class MainActivity : BaseLceActivity<DemoContract.View, DemoContract.Presenter>( ...@@ -339,17 +372,6 @@ class MainActivity : BaseLceActivity<DemoContract.View, DemoContract.Presenter>(
} }
} }
private inner class MeditationServiceConnection : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
meditationService = (service as MeditationWindowService.MeditationBinder).service
}
override fun onServiceDisconnected(name: ComponentName?) {
}
}
private fun showEnsureDialog() { private fun showEnsureDialog() {
secretDialog = SecretDialog(this, object : OnSecretDialogListener { secretDialog = SecretDialog(this, object : OnSecretDialogListener {
override fun onCancel() { override fun onCancel() {
...@@ -367,4 +389,333 @@ class MainActivity : BaseLceActivity<DemoContract.View, DemoContract.Presenter>( ...@@ -367,4 +389,333 @@ class MainActivity : BaseLceActivity<DemoContract.View, DemoContract.Presenter>(
secretDialog!!.show() secretDialog!!.show()
} }
private lateinit var mWindowManager: WindowManager
private var floatRootView: View? = null
private var ivClose: ImageView? = null
private var ivCover: ImageView? = null
private var tvTitle: TextView? = null
private var ivProgress: CircleSeekBar? = null
private var ivPlayOrPause: ImageView? = null
private var mMediaPlayer: MediaPlayer? = null
private var mTimer = Timer()
private var mCurrentMeditation: MeditationPlayModuleBean.MeditationDetail? = null
private var mMeditations = mutableListOf<MeditationPlayModuleBean.MeditationDetail>()
private var windowIsShow = false
private var mToShowWindow = false
private var mUpdate = false
private var mObservable: Observable<Long>? = null
private var mObserver: Observer<Long>? = null
private var currentMediaId:Long? = null
private var mDisposable:Disposable?=null
private var currentMeditationIndex = 0
private fun showWindow() {
mWindowManager = getSystemService(WINDOW_SERVICE) as WindowManager
val outMetrics = DisplayMetrics()
mWindowManager.defaultDisplay.getMetrics(outMetrics)
var layoutParams = WindowManager.LayoutParams().apply {
type = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
} else {
WindowManager.LayoutParams.TYPE_PHONE
}
flags =
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
format = PixelFormat.RGBA_8888
width = outMetrics.widthPixels - dp2px(32F)
height = ViewGroup.LayoutParams.WRAP_CONTENT
gravity = Gravity.LEFT or Gravity.TOP
x = outMetrics.widthPixels /2 - width / 2
y = outMetrics.heightPixels - dp2px(140F)
}
floatRootView =
LayoutInflater.from(this).inflate(com.yidianling.muse.R.layout.layout_meditation_play_float_view, null)
ivClose = floatRootView?.findViewById(com.yidianling.muse.R.id.iv_close)
ivCover = floatRootView?.findViewById(com.yidianling.muse.R.id.iv_cover)
tvTitle = floatRootView?.findViewById(com.yidianling.muse.R.id.tv_title)
ivProgress = floatRootView?.findViewById(com.yidianling.muse.R.id.progress_bar)
ivPlayOrPause = floatRootView?.findViewById(com.yidianling.muse.R.id.iv_play_status)
floatRootView?.setOnTouchListener(FloatViewTouchListener(layoutParams, mWindowManager))
mWindowManager.addView(floatRootView, layoutParams)
windowIsShow = true
}
fun onEventMainThread(event: Any) {
if (event is com.ydl.ydlcommon.event.MeditationFloatEvent){
mToShowWindow = event.show
if (event.show!=null && !event.show) {
if (event.stop == true){
try {
mMediaPlayer?.stop()
}catch (e:Exception){
}
}
}
}
if (event is MeditationFloatEvent){
mToShowWindow = event.show
if (event.time!=null){
if (event.time!! >0){
initRxTimeOff(event.time!!)
}else{
mDisposable?.dispose()
}
}else{
if (event.show!=null && !event.show) {
if (event.stop == true){
try {
mMediaPlayer?.stop()
}catch (e:Exception){
}
}
}
}
if (event.show!=null && event.show && event.meditation != null && event.meditations!=null) {
mCurrentMeditation = event.meditation
mMeditations.addAll(event.meditations!!)
}
}
}
private fun updateFloatView(meditation: MeditationPlayModuleBean.MeditationDetail) {
currentMediaId = meditation.mediaId
MediaPlayerManager.getInstance(this)?.setMediaId(currentMediaId)
mMediaPlayer = MediaPlayerManager.getInstance(this)?.getMediaPlayer()
tvTitle?.setOnClickListener {
//悬浮窗暂停点击事件
ActionCountUtils.count(MuseBIConstants.YDL_MUSE_MEDITATION_WINDOW_CLICK,"")
val intent = Intent(this, PlayMeditationActivity::class.java)
intent.putExtra("MEDIA_ID", meditation.mediaId)
intent.putExtra("MEDITATION_ID", meditation.meditationId)
intent.putExtra("MEDITATION_TYPE", meditation.meditationType)
intent.putExtra("MEDIA_COVER_URL", meditation.coverImageUrl)
intent.putExtra("MEDIA_URL", meditation.mediaUrl)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(intent)
}
ivCover?.setOnClickListener {
//悬浮窗暂停点击事件
ActionCountUtils.count(MuseBIConstants.YDL_MUSE_MEDITATION_WINDOW_CLICK,"")
val intent = Intent(this, PlayMeditationActivity::class.java)
intent.putExtra("MEDIA_ID", meditation.mediaId)
intent.putExtra("MEDITATION_ID", meditation.meditationId)
intent.putExtra("MEDITATION_TYPE", meditation.meditationType)
intent.putExtra("MEDIA_COVER_URL", meditation.coverImageUrl)
intent.putExtra("MEDIA_URL", meditation.mediaUrl)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(intent)
}
if (mMediaPlayer?.isPlaying == true) {
ivPlayOrPause?.setImageResource(com.yidianling.muse.R.drawable.icon_meditation_float_play)
} else {
ivPlayOrPause?.setImageResource(com.yidianling.muse.R.drawable.icon_meditation_float_pause)
}
ivProgress?.maxProcess = mMediaPlayer?.duration ?: 0
ivProgress?.curProcess = mMediaPlayer?.currentPosition ?: 0
Glide.with(floatRootView!!)
.load(meditation.coverImageUrl)
.into(ivCover!!)
tvTitle?.text = meditation.title
ivPlayOrPause?.setOnClickListener {
if (mMediaPlayer?.isPlaying == true) {
//悬浮窗暂停点击事件
ActionCountUtils.count(MuseBIConstants.YDL_MUSE_MEDITATION_WINDOW_PAUSE_CLICK,"")
MediaPlayerManager.getInstance(this)?.pause()
if (meditation.meditationId != null && meditation.mediaId != null) {
MuseHttp.getInstance().postMeditationPlayRecord(
meditationId = meditation.meditationId!!.toInt(),
isQuit = 1, mediaId = meditation.mediaId!!,
playTime = (ivProgress?.curProcess?.div(1000.00))?.roundToInt() ?: 0,
isComplete = 0
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
}, {
object : ThrowableConsumer() {
override fun accept(msg: String) {
}
}
})
}
} else {
//悬浮窗播放点击事件
ActionCountUtils.count(MuseBIConstants.YDL_MUSE_MEDITATION_WINDOW_PLAY_CLICK,"")
MediaPlayerManager.getInstance(this)?.play()
}
if (mMediaPlayer?.isPlaying == true) {
ivPlayOrPause?.setImageResource(com.yidianling.muse.R.drawable.icon_meditation_float_play)
} else {
ivPlayOrPause?.setImageResource(com.yidianling.muse.R.drawable.icon_meditation_float_pause)
}
}
mTimer.schedule(object : TimerTask() {
override fun run() {
ivProgress?.curProcess = mMediaPlayer?.currentPosition ?: 0
}
}, 0, 50)
mMediaPlayer?.setOnCompletionListener {
val duration = meditation?.duration?.toInt()?:0
val currentDuration = (mMediaPlayer?.currentPosition?:0)/1000
if(meditation.mediaId!=null && meditation.meditationId!=null){
val playTime = (ivProgress?.curProcess?.div(1000.00))?.roundToInt() ?: 0
if(duration == currentDuration){
MediaPlayerTimeUtil.uploadPlayRecord(
meditationId = meditation.meditationId.toInt(),
mediaId = meditation.mediaId,
isQuit = 0,
playTime = playTime,
isComplete = 1
)
}
}
switchSound()
if (mMediaPlayer?.isPlaying == true) {
ivPlayOrPause?.setImageResource(com.yidianling.muse.R.drawable.icon_meditation_float_play)
} else {
ivPlayOrPause?.setImageResource(com.yidianling.muse.R.drawable.icon_meditation_float_pause)
}
}
ivClose?.setOnClickListener {
//悬浮窗关闭点击事件
ActionCountUtils.count(MuseBIConstants.YDL_MUSE_MEDITATION_WINDOW_CLOSE_CLICK,"")
if(meditation.mediaId!=null && meditation.meditationId!=null){
val playTime = (ivProgress?.curProcess?.div(1000.00))?.roundToInt() ?: 0
MediaPlayerTimeUtil.uploadPlayRecord(
meditationId = meditation.meditationId.toInt(),
mediaId = meditation.mediaId,
isQuit = 0,
playTime = playTime,
isComplete = 0
)
}
MediaPlayerManager.getInstance(this)?.stop()
hideFloatWindow()
}
mUpdate = false
}
private fun hideFloatWindow() {
if (floatRootView != null && floatRootView?.windowToken != null) {
if (mWindowManager != null) {
mWindowManager.removeViewImmediate(floatRootView)
}
}
windowIsShow = false
}
private fun switchSound() {
mMeditations?.forEachIndexed { index, meditationDetail ->
if (meditationDetail.mediaId == mCurrentMeditation?.mediaId) {
currentMeditationIndex = index
}
}
if (currentMeditationIndex < mMeditations.size - 1) {
mCurrentMeditation = mMeditations[currentMeditationIndex + 1]
} else {
mCurrentMeditation = mMeditations[0]
}
mUpdate = true
updateFloatView(mCurrentMeditation!!)
initMediaPlayer(mCurrentMeditation!!.mediaUrl)
}
private fun initMediaPlayer(path: String) {
MediaPlayerManager.getInstance(this)?.setAudioPath(path)
MediaPlayerManager.getInstance(this)
?.setOnPreparedListener(object : MediaPlayerManager.OnMediaPlayerManagerListener {
override fun onPrepared(mediaPlayer: MediaPlayer) {
MediaPlayerManager.getInstance(this@MainActivity)?.play()
}
})
}
private fun dp2px(dp: Float): Int {
val scale = resources.displayMetrics.density
return (dp * scale + 0.5f).toInt()
}
private fun initRxTimeOff(time: Long) {
mObservable = Observable.interval(0, 1, TimeUnit.SECONDS)
.take(time / 1000 + 1)
.map { t -> time - t * 1000 }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
mObserver = object : Observer<Long> {
override fun onSubscribe(d: Disposable) {
mDisposable = d
}
override fun onNext(t: Long) {
}
override fun onError(e: Throwable) {
}
override fun onComplete() {
if (mMediaPlayer?.isPlaying == true) {
mMediaPlayer?.stop()
ivPlayOrPause?.setImageResource(com.yidianling.muse.R.drawable.icon_meditation_float_pause)
}
hideFloatWindow()
}
}
if (mObserver != null && mObserver is Observer<Long>) {
mObservable?.subscribe(mObserver as Observer<Long>)
}
if (mDisposable?.isDisposed == true && mObserver != null && mObserver is Observer<Long>) {
mObservable?.subscribe(mObserver as Observer<Long>)
}
}
} }
...@@ -14,7 +14,7 @@ buildscript { ...@@ -14,7 +14,7 @@ buildscript {
ydlrouter_version = '1.2.3' ydlrouter_version = '1.2.3'
constrait_support_version = '1.0.2' constrait_support_version = '1.0.2'
componentVersion = "0.3.0.4-SNAPSHOT" componentVersion = "0.3.0.5-SNAPSHOT"
} }
repositories { repositories {
mavenCentral() mavenCentral()
...@@ -51,6 +51,7 @@ allprojects { p -> ...@@ -51,6 +51,7 @@ allprojects { p ->
url 'http://maven.aliyun.com/nexus/content/groups/public/' url 'http://maven.aliyun.com/nexus/content/groups/public/'
allowInsecureProtocol true allowInsecureProtocol true
} }
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
mavenLocal() mavenLocal()
} }
} }
......
...@@ -63,7 +63,11 @@ dependencies { ...@@ -63,7 +63,11 @@ dependencies {
implementation rootProject.ext.dependencies["BaseRecyclerViewAdapterHelper"] implementation rootProject.ext.dependencies["BaseRecyclerViewAdapterHelper"]
api 'com.tencent.tbs.tbssdk:sdk:43903' api 'com.tencent.tbs.tbssdk:sdk:43903'
implementation('com.netease.yunxin.kit:call-ui:1.5.6') implementation('com.netease.yunxin.kit:call-ui:1.5.9-SNAPSHOT') {
exclude group: "com.netease.nimlib", module: "avsignalling"
exclude group: "com.netease.nimlib", module: "basesdk"
}
implementation "com.netease.nimlib:avsignalling:9.1.1"
api project(':ydl-webview') api project(':ydl-webview')
api project(':ydl-platform') api project(':ydl-platform')
......
...@@ -31,7 +31,7 @@ import java.io.IOException; ...@@ -31,7 +31,7 @@ import java.io.IOException;
* 云信sdk 自定义的SDK选项设置 * 云信sdk 自定义的SDK选项设置
*/ */
class NimSDKOptionConfig { public class NimSDKOptionConfig {
static SDKOptions prepareSDKOptions(Context context, Class activity, IMInitConfigBean configBean) { static SDKOptions prepareSDKOptions(Context context, Class activity, IMInitConfigBean configBean) {
SDKOptions options = new SDKOptions(); SDKOptions options = new SDKOptions();
...@@ -93,7 +93,7 @@ class NimSDKOptionConfig { ...@@ -93,7 +93,7 @@ class NimSDKOptionConfig {
* 配置 APP 保存图片/语音/文件/log等数据的目录 * 配置 APP 保存图片/语音/文件/log等数据的目录
* 这里示例用SD卡的应用扩展存储目录 * 这里示例用SD卡的应用扩展存储目录
*/ */
static String getAppCacheDir(Context context) { public static String getAppCacheDir(Context context) {
String storageRootPath = null; String storageRootPath = null;
try { try {
// SD卡应用扩展存储区(APP卸载后,该目录下被清除,用户也可以在设置界面中手动清除),请根据APP对数据缓存的重要性及生命周期来决定是否采用此缓存目录. // SD卡应用扩展存储区(APP卸载后,该目录下被清除,用户也可以在设置界面中手动清除),请根据APP对数据缓存的重要性及生命周期来决定是否采用此缓存目录.
......
...@@ -19,6 +19,7 @@ import com.ydl.ydlcommon.modular.ModularServiceManagerKt; ...@@ -19,6 +19,7 @@ import com.ydl.ydlcommon.modular.ModularServiceManagerKt;
import com.ydl.ydlcommon.utils.log.XLog; import com.ydl.ydlcommon.utils.log.XLog;
import com.yidianling.im.R; import com.yidianling.im.R;
import com.yidianling.im.config.NimApplication; import com.yidianling.im.config.NimApplication;
import com.yidianling.im.config.NimSDKOptionConfig;
import com.yidianling.im.http.ImHttpImpl; import com.yidianling.im.http.ImHttpImpl;
import com.yidianling.user.api.bean.UserResponseBean; import com.yidianling.user.api.bean.UserResponseBean;
import com.yidianling.user.api.service.IUserService; import com.yidianling.user.api.service.IUserService;
...@@ -46,6 +47,7 @@ public class NimUICallInit { ...@@ -46,6 +47,7 @@ public class NimUICallInit {
CallKitUIOptions options = new CallKitUIOptions.Builder() CallKitUIOptions options = new CallKitUIOptions.Builder()
// 必要:音视频通话 sdk appKey,用于通话中使用 // 必要:音视频通话 sdk appKey,用于通话中使用
.rtcAppKey(appKey) .rtcAppKey(appKey)
.logRootPath(NimSDKOptionConfig.getAppCacheDir(context) + "/yidianling")
// 必要:当前用户 AccId // 必要:当前用户 AccId
.currentUserAccId(userId) .currentUserAccId(userId)
.currentUserRtcUId(Long.parseLong(userId)) .currentUserRtcUId(Long.parseLong(userId))
......
...@@ -26,7 +26,5 @@ ...@@ -26,7 +26,5 @@
android:screenOrientation="portrait" android:screenOrientation="portrait"
android:theme="@style/un_full_screen_activity"/> android:theme="@style/un_full_screen_activity"/>
<service android:name=".service.MeditationWindowService"/>
</application> </application>
</manifest> </manifest>
...@@ -453,7 +453,7 @@ class PlayMeditationActivity : BaseActivity() { ...@@ -453,7 +453,7 @@ class PlayMeditationActivity : BaseActivity() {
meditation = currentMeditation?.copy( meditation = currentMeditation?.copy(
meditationType = mMeditationType, meditationId = meditationType = mMeditationType, meditationId =
mMeditationId ?: 0 mMeditationId ?: 0
) ),meditations = meditations
) )
EventBus.getDefault().post(event) EventBus.getDefault().post(event)
...@@ -1070,7 +1070,7 @@ class PlayMeditationActivity : BaseActivity() { ...@@ -1070,7 +1070,7 @@ class PlayMeditationActivity : BaseActivity() {
} }
override fun onBackPressed() { override fun onBackPressed() {
val event = MeditationFloatEvent(true, meditation = currentMeditation) val event = MeditationFloatEvent(true, meditation = currentMeditation,meditations = meditations)
EventBus.getDefault().post(event) EventBus.getDefault().post(event)
super.onBackPressed() super.onBackPressed()
} }
......
...@@ -7,6 +7,6 @@ class MeditationFloatEvent( ...@@ -7,6 +7,6 @@ class MeditationFloatEvent(
val stop: Boolean? = null, val stop: Boolean? = null,
val time: Long? = null, val time: Long? = null,
val meditation: MeditationPlayModuleBean.MeditationDetail? = null, val meditation: MeditationPlayModuleBean.MeditationDetail? = null,
val meditations: ArrayList<MeditationPlayModuleBean.MeditationDetail>? = null val meditations: MutableList<MeditationPlayModuleBean.MeditationDetail>? = null
) )
package com.yidianling.muse.service
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.graphics.PixelFormat
import android.media.MediaPlayer
import android.os.Binder
import android.os.Build
import android.os.IBinder
import android.util.DisplayMetrics
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.view.WindowManager
import android.widget.ImageView
import android.widget.TextView
import androidx.core.app.NotificationCompat
import com.bumptech.glide.Glide
import com.ydl.ydlcommon.data.http.ThrowableConsumer
import com.ydl.ydlcommon.utils.actionutil.ActionCountUtils
import com.yidianling.home.http.MuseHttp
import com.yidianling.muse.R
import com.yidianling.muse.activity.PlayMeditationActivity
import com.yidianling.muse.bean.MeditationPlayModuleBean
import com.yidianling.muse.constants.MuseBIConstants
import com.yidianling.muse.event.MeditationFloatEvent
import com.yidianling.muse.helper.FloatViewTouchListener
import com.yidianling.muse.helper.MediaPlayerManager
import com.yidianling.muse.utils.MediaPlayerTimeUtil
import de.greenrobot.event.EventBus
import io.feeeei.circleseekbar.CircleSeekBar
import io.reactivex.Observable
import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import kotlinx.android.synthetic.main.activity_play_meditation.*
import kotlinx.android.synthetic.main.player_control_view.*
import java.util.*
import java.util.concurrent.TimeUnit
import kotlin.math.roundToInt
class MeditationWindowService : Service() {
private lateinit var mWindowManager: WindowManager
private var floatRootView: View? = null
private var ivClose: ImageView? = null
private var ivCover: ImageView? = null
private var tvTitle: TextView? = null
private var ivProgress: CircleSeekBar? = null
private var ivPlayOrPause: ImageView? = null
private var mMediaPlayer: MediaPlayer? = null
private var mTimer = Timer()
private var mCurrentMeditation: MeditationPlayModuleBean.MeditationDetail? = null
private var mMeditations = mutableListOf<MeditationPlayModuleBean.MeditationDetail>()
private var windowIsShow = false
private var mDisposable: Disposable? = null
private var mObservable: Observable<Long>? = null
private var mObserver: Observer<Long>? = null
private var currentMediaId:Long? = null
inner class MeditationBinder : Binder() {
val service: MeditationWindowService
get() = this@MeditationWindowService
}
override fun onCreate() {
super.onCreate()
EventBus.getDefault().register(this)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val channel = NotificationChannel("壹点灵", "play", NotificationManager.IMPORTANCE_HIGH)
manager.createNotificationChannel(channel)
val notification = NotificationCompat.Builder(this, "壹点灵").build()
startForeground(1, notification)
}
}
private fun showWindow() {
mWindowManager = getSystemService(WINDOW_SERVICE) as WindowManager
val outMetrics = DisplayMetrics()
mWindowManager.defaultDisplay.getMetrics(outMetrics)
var layoutParams = WindowManager.LayoutParams().apply {
type = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
} else {
WindowManager.LayoutParams.TYPE_PHONE
}
flags =
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
format = PixelFormat.RGBA_8888
width = outMetrics.widthPixels - dp2px(32F)
height = WRAP_CONTENT
gravity = Gravity.LEFT or Gravity.TOP
x = outMetrics.widthPixels /2 - width / 2
y = outMetrics.heightPixels - dp2px(140F)
}
floatRootView =
LayoutInflater.from(this).inflate(R.layout.layout_meditation_play_float_view, null)
ivClose = floatRootView?.findViewById(R.id.iv_close)
ivCover = floatRootView?.findViewById(R.id.iv_cover)
tvTitle = floatRootView?.findViewById(R.id.tv_title)
ivProgress = floatRootView?.findViewById(R.id.progress_bar)
ivPlayOrPause = floatRootView?.findViewById(R.id.iv_play_status)
floatRootView?.setOnTouchListener(FloatViewTouchListener(layoutParams, mWindowManager))
mWindowManager.addView(floatRootView, layoutParams)
windowIsShow = true
}
fun onEventMainThread(event: Any) {
if (event is com.ydl.ydlcommon.event.MeditationFloatEvent){
if (event.show!=null && !event.show) {
if (event.stop == true){
try {
mMediaPlayer?.stop()
}catch (e:Exception){
}
hideFloatWindow()
}
hideFloatWindow()
}
}
if (event is MeditationFloatEvent){
if (event.time!=null){
if (event.time>0){
initRxTimeOff(event.time)
}else{
mDisposable?.dispose()
}
}else{
if (event.show!=null && !event.show) {
if (event.stop == true){
try {
mMediaPlayer?.stop()
}catch (e:Exception){
}
hideFloatWindow()
}
hideFloatWindow()
}
if (event.show!=null && event.show && event.meditation != null) {
if (!windowIsShow) {
showWindow()
}
updateFloatView(event.meditation)
}
}
}
}
private fun updateFloatView(meditation: MeditationPlayModuleBean.MeditationDetail) {
currentMediaId = meditation.mediaId
MediaPlayerManager.getInstance(this)?.setMediaId(currentMediaId)
mMediaPlayer = MediaPlayerManager.getInstance(this)?.getMediaPlayer()
tvTitle?.setOnClickListener {
//悬浮窗暂停点击事件
ActionCountUtils.count(MuseBIConstants.YDL_MUSE_MEDITATION_WINDOW_CLICK,"")
val intent = Intent(this, PlayMeditationActivity::class.java)
intent.putExtra("MEDIA_ID", meditation.mediaId)
intent.putExtra("MEDITATION_ID", meditation.meditationId)
intent.putExtra("MEDITATION_TYPE", meditation.meditationType)
intent.putExtra("MEDIA_COVER_URL", meditation.coverImageUrl)
intent.putExtra("MEDIA_URL", meditation.mediaUrl)
intent.flags = FLAG_ACTIVITY_NEW_TASK
startActivity(intent)
}
ivCover?.setOnClickListener {
//悬浮窗暂停点击事件
ActionCountUtils.count(MuseBIConstants.YDL_MUSE_MEDITATION_WINDOW_CLICK,"")
val intent = Intent(this, PlayMeditationActivity::class.java)
intent.putExtra("MEDIA_ID", meditation.mediaId)
intent.putExtra("MEDITATION_ID", meditation.meditationId)
intent.putExtra("MEDITATION_TYPE", meditation.meditationType)
intent.putExtra("MEDIA_COVER_URL", meditation.coverImageUrl)
intent.putExtra("MEDIA_URL", meditation.mediaUrl)
intent.flags = FLAG_ACTIVITY_NEW_TASK
startActivity(intent)
}
if (mMediaPlayer?.isPlaying == true) {
ivPlayOrPause?.setImageResource(R.drawable.icon_meditation_float_play)
} else {
ivPlayOrPause?.setImageResource(R.drawable.icon_meditation_float_pause)
}
ivProgress?.maxProcess = mMediaPlayer?.duration ?: 0
ivProgress?.curProcess = mMediaPlayer?.currentPosition ?: 0
Glide.with(floatRootView!!)
.load(meditation.coverImageUrl)
.into(ivCover!!)
tvTitle?.text = meditation.title
ivPlayOrPause?.setOnClickListener {
if (mMediaPlayer?.isPlaying == true) {
//悬浮窗暂停点击事件
ActionCountUtils.count(MuseBIConstants.YDL_MUSE_MEDITATION_WINDOW_PAUSE_CLICK,"")
MediaPlayerManager.getInstance(this)?.pause()
if (meditation.meditationId != null && meditation.mediaId != null) {
MuseHttp.getInstance().postMeditationPlayRecord(
meditationId = meditation.meditationId!!.toInt(),
isQuit = 1, mediaId = meditation.mediaId!!,
playTime = (ivProgress?.curProcess?.div(1000.00))?.roundToInt() ?: 0,
isComplete = 0
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
}, {
object : ThrowableConsumer() {
override fun accept(msg: String) {
}
}
})
}
} else {
//悬浮窗播放点击事件
ActionCountUtils.count(MuseBIConstants.YDL_MUSE_MEDITATION_WINDOW_PLAY_CLICK,"")
MediaPlayerManager.getInstance(this)?.play()
}
if (mMediaPlayer?.isPlaying == true) {
ivPlayOrPause?.setImageResource(R.drawable.icon_meditation_float_play)
} else {
ivPlayOrPause?.setImageResource(R.drawable.icon_meditation_float_pause)
}
}
mTimer.schedule(object : TimerTask() {
override fun run() {
ivProgress?.curProcess = mMediaPlayer?.currentPosition ?: 0
}
}, 0, 50)
mMediaPlayer?.setOnCompletionListener {
val duration = meditation?.duration?.toInt()?:0
val currentDuration = (mMediaPlayer?.currentPosition?:0)/1000
if(meditation.mediaId!=null && meditation.meditationId!=null){
val playTime = (ivProgress?.curProcess?.div(1000.00))?.roundToInt() ?: 0
if(duration == currentDuration){
MediaPlayerTimeUtil.uploadPlayRecord(
meditationId = meditation.meditationId.toInt(),
mediaId = meditation.mediaId,
isQuit = 0,
playTime = playTime,
isComplete = 1
)
}
}
if (mMediaPlayer?.isPlaying == true) {
ivPlayOrPause?.setImageResource(R.drawable.icon_meditation_float_play)
} else {
ivPlayOrPause?.setImageResource(R.drawable.icon_meditation_float_pause)
}
}
ivClose?.setOnClickListener {
//悬浮窗关闭点击事件
ActionCountUtils.count(MuseBIConstants.YDL_MUSE_MEDITATION_WINDOW_CLOSE_CLICK,"")
if(meditation.mediaId!=null && meditation.meditationId!=null){
val playTime = (ivProgress?.curProcess?.div(1000.00))?.roundToInt() ?: 0
MediaPlayerTimeUtil.uploadPlayRecord(
meditationId = meditation.meditationId.toInt(),
mediaId = meditation.mediaId,
isQuit = 0,
playTime = playTime,
isComplete = 0
)
}
MediaPlayerManager.getInstance(this)?.stop()
hideFloatWindow()
}
}
private fun hideFloatWindow() {
if (floatRootView != null && floatRootView?.windowToken != null) {
if (mWindowManager != null) {
windowIsShow = false
mWindowManager.removeViewImmediate(floatRootView)
}
}
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
return super.onStartCommand(intent, flags, startId)
}
override fun startForegroundService(service: Intent?): ComponentName? {
return super.startForegroundService(service)
}
override fun onDestroy() {
super.onDestroy()
if (EventBus.getDefault().isRegistered(this)) {
EventBus.getDefault().unregister(this)
}
mDisposable?.dispose()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR) {
stopForeground(true)
}
}
override fun onBind(intent: Intent?): IBinder? {
return MeditationBinder()
}
private fun dp2px(dp: Float): Int {
val scale = resources.displayMetrics.density
return (dp * scale + 0.5f).toInt()
}
private fun initRxTimeOff(time: Long) {
mObservable = Observable.interval(0, 1, TimeUnit.SECONDS)
.take(time / 1000 + 1)
.map { t -> time - t * 1000 }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
mObserver = object : Observer<Long> {
override fun onSubscribe(d: Disposable) {
mDisposable = d
}
override fun onNext(t: Long) {
}
override fun onError(e: Throwable) {
}
override fun onComplete() {
if (mMediaPlayer?.isPlaying == true) {
mMediaPlayer?.stop()
ivPlayOrPause?.setImageResource(R.drawable.icon_meditation_float_pause)
}
hideFloatWindow()
}
}
if (mObserver != null && mObserver is Observer<Long>) {
mObservable?.subscribe(mObserver as Observer<Long>)
}
if (mDisposable?.isDisposed == true && mObserver != null && mObserver is Observer<Long>) {
mObservable?.subscribe(mObserver as Observer<Long>)
}
}
}
\ No newline at end of file
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