Commit b7c1cc70 by konghaorui

补充音频播放模块

parent fc95eec4
......@@ -158,24 +158,35 @@ dependencies {
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
kapt rootProject.ext.dependencies["dagger2-compiler"]
// api 'com.ydl:m-user-module-ydl:0.0.6'
// api 'com.ydl:ydl-webview:0.0.3@aar'
// api 'com.ydl:m-user-api:0.0.4'
//
// api('com.ydl:ydl-platform:0.0.5@aar') {
// transitive = true
// }
api rootProject.ext.dependencies["butterknife"]
kapt rootProject.ext.dependencies["butterknife-compiler"]
if (rootProject.ext.dev_mode) {
//开发模式
api project(':m-consultant')
api project(':m-user')
implementation project(':ydl-platform')
api (project(':ydl-platform')){
transitive = true
}
implementation project(':ydl-webview')
implementation project(':ydl-media')
} else {
//发布模式
api 'com.ydl:m-user-module-ydl:0.0.6'
api rootProject.ext.dependencies["ydl-webview"]
api rootProject.ext.dependencies["ydl-m-user-api"]
api(rootProject.ext.dependencies["ydl-platform"]) {
transitive = true
}
}
implementation rootProject.ext.dependencies["retrofit-url-manager"]
kapt 'com.alibaba:arouter-compiler:1.2.2'
}
noTracePoint{
noTracePoint {
outputModifyFile = false
targetPackages = ['com.cxzapp.yidianling',
'com.yidianling.consultant',
......
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" package="com.ydl.component">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
xmlns:tools="http://schemas.android.com/tools"
package="com.ydl.component">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<application
android:name="com.ydl.ydlcommon.base.BaseApp"
......@@ -18,24 +18,26 @@
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
tools:replace="android:allowBackup, android:icon, android:label"
android:theme="@style/platform_CommonTheme"
tools:ignore="GoogleAppIndexingWarning">
<!--<activity android:name="com.yidianling.user.ui.login.RegisterAndLoginActivity"-->
<!--<activity android:name=".MainActivity"/>-->
<activity android:name=".MainActivity"
android:theme="@style/platform_NoTitleTheme"
>
tools:ignore="GoogleAppIndexingWarning"
tools:replace="android:allowBackup, android:icon, android:label">
<activity android:name=".music.MusicPlayActivity"
android:theme="@style/platform_NoTitleTheme" />
<!-- <activity android:name="com.yidianling.user.ui.login.RegisterAndLoginActivity" -->
<!-- <activity android:name=".MainActivity"/> -->
<activity
android:name=".MainActivity"
android:theme="@style/platform_NoTitleTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER"/>
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<meta-data
android:name="com.ydl.component.base.DemoGlobalConfig"
android:value="ModuleConfig"/>
android:value="ModuleConfig" />
<activity
android:name="com.tencent.tauth.AuthActivity"
......@@ -50,7 +52,6 @@
<data android:scheme="tencent1105070461" />
</intent-filter>
</activity>
</application>
</manifest>
\ No newline at end of file
......@@ -2,13 +2,20 @@ package com.ydl.component
import android.Manifest
import android.annotation.SuppressLint
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.net.Uri
import android.os.IBinder
import android.provider.Settings
import android.util.Log
import com.alibaba.android.arouter.launcher.ARouter
import com.tbruyelle.rxpermissions2.RxPermissions
import com.ydl.component.music.MusicPlayActivity
import com.ydl.component.mvp.DemoContract
import com.ydl.component.mvp.DemoPresenter
import com.ydl.media.music.PlayService
import com.ydl.ydlcommon.mvp.lce.BaseLceActivity
import com.yidianling.common.tools.ToastUtil
import com.yidianling.consultant.ExpertSearchActivity.Companion.HOT_SEARCH_DOCTOR_NAME
......@@ -18,7 +25,10 @@ import kotlinx.android.synthetic.main.activity_main.*
* Created by haorui on 2019-09-01 .
* Des:
*/
class MainActivity : BaseLceActivity<DemoContract.View, DemoContract.Presenter>(), DemoContract.View {
class MainActivity : BaseLceActivity<DemoContract.View, DemoContract.Presenter>(),
DemoContract.View {
private var serviceConnection: ServiceConnection? = null
protected var playService: PlayService? = null
override fun setData(data: String) {
tv_content.text = data
......@@ -33,6 +43,7 @@ class MainActivity : BaseLceActivity<DemoContract.View, DemoContract.Presenter>(
}
override fun initDataAndEvent() {
bindService()
loadData()
requestPermission()
tv_user.setOnClickListener {
......@@ -45,16 +56,22 @@ class MainActivity : BaseLceActivity<DemoContract.View, DemoContract.Presenter>(
bt_to_other.setOnClickListener {
ARouter.getInstance().build("/user/login")
.withBoolean("bind_phone",false)
.withBoolean("isFromGuide",true)
.withBoolean("bind_phone", false)
.withBoolean("isFromGuide", true)
.navigation()
}
bt_to_consultant.setOnClickListener {
ARouter.getInstance()
.build("/consult/hot_search")
.withString(HOT_SEARCH_DOCTOR_NAME, this.resources?.getString(R.string.platform_search_hint))
.withString(
HOT_SEARCH_DOCTOR_NAME,
this.resources?.getString(R.string.platform_search_hint)
)
.navigation()
}
bt_to_music.setOnClickListener {
startActivity(Intent(this, MusicPlayActivity::class.java))
}
}
override fun loadData() {
......@@ -72,7 +89,10 @@ class MainActivity : BaseLceActivity<DemoContract.View, DemoContract.Presenter>(
} else if (permission.shouldShowRequestPermissionRationale) {
requestPermission()
} else {
ToastUtil.toastLong(this, getString(R.string.platform_need_storage_permission_hint))
ToastUtil.toastLong(
this,
getString(R.string.platform_need_storage_permission_hint)
)
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
val uri = Uri.fromParts("package", packageName, null)
intent.data = uri
......@@ -82,4 +102,29 @@ class MainActivity : BaseLceActivity<DemoContract.View, DemoContract.Presenter>(
}
}
private fun bindService() {
val intent = Intent()
intent.setClass(this, PlayService::class.java!!)
serviceConnection = PlayServiceConnection()
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
}
override fun onDestroy() {
if (serviceConnection != null) {
unbindService(serviceConnection)
}
super.onDestroy()
}
private inner class PlayServiceConnection : ServiceConnection {
override fun onServiceConnected(name: ComponentName, service: IBinder) {
playService = (service as PlayService.PlayBinder).service
}
override fun onServiceDisconnected(name: ComponentName) {
Log.e(javaClass.simpleName, "service disconnected")
}
}
}
......@@ -2,8 +2,10 @@ package com.ydl.component.api
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import com.ydl.component.model.HomeFMBean
import com.ydl.ydlcommon.base.config.YDL_DOMAIN
import com.ydl.ydlcommon.base.config.YDL_DOMAIN_JAVA
import com.ydl.ydlcommon.data.http.BaseAPIResponse
import io.reactivex.Observable
import retrofit2.http.GET
import retrofit2.http.Headers
......@@ -26,6 +28,12 @@ interface DemoService {
@GET("home/index")
fun getHome1Data(): Observable<JsonObject>
//首页电台请求
@Headers( YDL_DOMAIN+ YDL_DOMAIN_JAVA)
@GET("fm/list/all")
fun getFMData(@Query("perPageRows") perPageRows: String = "4"): Observable<BaseAPIResponse<HomeFMBean>>
companion object {
const val HEADER_API_VERSION = "Accept: application/vnd.github.v3+json"
const val DOMAIN_GITHUB = "github"
......
......@@ -3,7 +3,13 @@ package com.ydl.component.base;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import com.ydl.media.music.PlayService;
import com.ydl.media.music.utils.CoverLoader;
import com.ydl.ydlcommon.base.delegate.IAppLifecycles;
import com.ydl.ydlcommon.utils.YdlBuryPointUtil;
import org.jetbrains.annotations.NotNull;
/**
......@@ -18,7 +24,11 @@ public class DemoAppLifecycles implements IAppLifecycles {
@Override
public void onCreate(@NotNull Application application) {
//数据埋点初始化--一定要放在主进程中
YdlBuryPointUtil.init(application);
CoverLoader.Companion.get().init(application);
Intent intent = new Intent(application, PlayService.class);
application.startService(intent);
}
@Override
......
package com.ydl.component.model
import com.google.gson.annotations.SerializedName
/**
* Created by haorui on 2019/2/14.
* Des:首页电台模块参数
*/
class HomeFMBean{
var endRow: Int = 0
var isHasNextPage: Boolean = false
var isHasPreviousPage: Boolean = false
var isIsFirstPage: Boolean = false
var isIsLastPage: Boolean = false
var navigateFirstPage: Int = 0
var navigateLastPage: Int = 0
var navigatePages: Int = 0
var nextPage: Int = 0
var pageNum: Int = 0
var pageSize: Int = 0
var pages: Int = 0
var prePage: Int = 0
var size: Int = 0
var startRow: Int = 0
var total: Int = 0
var list: List<ListBean>? = null
var navigatepageNums: List<Int>? = null
class ListBean {
var adminId: Int = 0
var appImg: String? = null
@SerializedName("author")
var artist: String? = null
var categoryId: Int = 0
var createTime: String? = null
@SerializedName("description")
var album: String? = null
/**
* 喜欢人数
*/
var favorites: Int = 0
/**
* 电台地址
*/
@SerializedName("fmUrl")
var path: String? = null
/**
* 收听人数
*/
var hits: Int = 0
var id: Int = 0
/**
* 封面地址
*/
@SerializedName("imageUrl")
var coverPath: String? = null
var isOriginal: String? = null
var isShow: String? = null
@SerializedName("name")
var title: String? = null
var orderNum: Int = 0
var period: String? = null
var shareCount: Int = 0
var updateTime: String? = null
override fun toString(): String {
return "ListBean(artist=$artist, album=$album, coverPath=$coverPath, title=$title)"
}
}
}
package com.ydl.component.music
import com.ydl.component.R
import com.ydl.component.mvp.DemoContract
import com.ydl.component.mvp.DemoPresenter
import com.ydl.ydlcommon.mvp.lce.BaseLceActivity
class MusicPlayActivity : BaseLceActivity<DemoContract.View, DemoContract.Presenter>(), DemoContract.View {
private var mPlayFragment: PlayFragment? = null
override fun createPresenter(): DemoContract.Presenter {
return DemoPresenter()
}
override fun layoutResId(): Int {
return R.layout.activity_music_play
}
override fun initDataAndEvent() {
loadData()
}
override fun loadData() {
mPresenter.loadFmMusic()
}
override fun setData(data: String) {
val ft = supportFragmentManager.beginTransaction()
ft.setCustomAnimations(R.anim.fragment_slide_up, 0)
if (mPlayFragment == null) {
mPlayFragment = PlayFragment()
ft.replace(android.R.id.content, mPlayFragment)
} else {
ft.show(mPlayFragment)
}
ft.commitAllowingStateLoss()
}
}
package com.ydl.component.music;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.TextView;
import com.ydl.component.R;
import com.ydl.media.music.AudioPlayer;
import com.ydl.media.music.OnPlayerEventListener;
import com.ydl.media.music.enums.PlayModeEnum;
import com.ydl.media.music.model.Music;
import com.ydl.media.music.utils.CoverLoader;
import com.ydl.media.music.utils.ScreenUtils;
import com.yidianling.common.tools.ToastUtil;
import java.util.Locale;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
/**
* 正在播放界面
* Created by wcy on 2015/11/27.
*/
public class PlayFragment extends Fragment implements View.OnClickListener,
SeekBar.OnSeekBarChangeListener, OnPlayerEventListener {
@BindView(R.id.ll_content)
LinearLayout llContent;
@BindView(R.id.iv_play_page_bg)
ImageView ivPlayingBg;
@BindView(R.id.iv_back)
ImageView ivBack;
@BindView(R.id.tv_title)
TextView tvTitle;
@BindView(R.id.tv_artist)
TextView tvArtist;
@BindView(R.id.sb_progress)
SeekBar sbProgress;
@BindView(R.id.tv_current_time)
TextView tvCurrentTime;
@BindView(R.id.tv_total_time)
TextView tvTotalTime;
@BindView(R.id.iv_mode)
ImageView ivMode;
@BindView(R.id.iv_play)
ImageView ivPlay;
@BindView(R.id.iv_next)
ImageView ivNext;
@BindView(R.id.iv_prev)
ImageView ivPrev;
@BindView(R.id.iv_cover)
ImageView ivCover;
int mLastProgress;
boolean isDraggingProgress;
private Unbinder unbinder;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_play, container, false);
unbinder = ButterKnife.bind(this, rootView);
return rootView ;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
initSystemBar();
initPlayMode();
onChangeImpl(AudioPlayer.Companion.get().getPlayMusic());
AudioPlayer.Companion.get().addOnPlayEventListener(this);
}
@Override
public void onStart() {
super.onStart();
setListener();
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
protected void setListener() {
ivBack.setOnClickListener(this);
ivMode.setOnClickListener(this);
ivPlay.setOnClickListener(this);
ivPrev.setOnClickListener(this);
ivNext.setOnClickListener(this);
sbProgress.setOnSeekBarChangeListener(this);
}
/**
* 沉浸式状态栏
*/
void initSystemBar() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
int top = ScreenUtils.INSTANCE.getStatusBarHeight(getActivity());
llContent.setPadding(0, top, 0, 0);
}
}
void initPlayMode() {
int mode = AudioPlayer.Companion.get().getPlayMode().value();
ivMode.setImageLevel(mode);
}
@Override
public void onChange(Music music) {
onChangeImpl(music);
}
@Override
public void onPlayerStart() {
ivPlay.setSelected(true);
}
@Override
public void onPlayerPause() {
ivPlay.setSelected(false);
}
/**
* 更新播放进度
*/
@Override
public void onPublish(int progress) {
if (!isDraggingProgress) {
sbProgress.setProgress(progress);
}
}
@Override
public void onBufferingUpdate(int percent) {
sbProgress.setSecondaryProgress(sbProgress.getMax() * 100 / percent);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.iv_back:
onBackPressed();
break;
case R.id.iv_mode:
switchPlayMode();
break;
case R.id.iv_play:
play();
break;
case R.id.iv_next:
next();
break;
case R.id.iv_prev:
prev();
break;
}
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (seekBar == sbProgress) {
if (Math.abs(progress - mLastProgress) >= DateUtils.SECOND_IN_MILLIS) {
tvCurrentTime.setText(formatTime(progress));
mLastProgress = progress;
}
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
if (seekBar == sbProgress) {
isDraggingProgress = true;
}
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
if (seekBar == sbProgress) {
isDraggingProgress = false;
if (AudioPlayer.Companion.get().isPlaying() || AudioPlayer.Companion.get().isPausing()) {
int progress = seekBar.getProgress();
AudioPlayer.Companion.get().seekTo(progress);
} else {
seekBar.setProgress(0);
}
}
}
void onChangeImpl(Music music) {
if (music == null) {
return;
}
tvTitle.setText(music.getTitle());
tvArtist.setText(music.getArtist());
sbProgress.setProgress((int) AudioPlayer.Companion.get().getAudioPosition());
sbProgress.setSecondaryProgress(0);
sbProgress.setMax((int) music.getDuration());
mLastProgress = 0;
tvCurrentTime.setText(R.string.play_time_start);
tvTotalTime.setText(formatTime(music.getDuration()));
setCoverAndBg(music);
if (AudioPlayer.Companion.get().isPlaying() || AudioPlayer.Companion.get().isPreparing()) {
ivPlay.setSelected(true);
} else {
ivPlay.setSelected(false);
}
}
void play() {
AudioPlayer.Companion.get().playPause();
}
void next() {
AudioPlayer.Companion.get().next();
}
void prev() {
AudioPlayer.Companion.get().prev();
}
void switchPlayMode() {
PlayModeEnum mode = AudioPlayer.Companion.get().getPlayMode();
switch (mode) {
case LIST_LOOP:
mode = PlayModeEnum.SHUFFLE;
ToastUtil.toastShort(getString(R.string.mode_shuffle));
break;
case SHUFFLE:
mode = PlayModeEnum.SINGLE;
ToastUtil.toastShort(getString(R.string.mode_one));
break;
case SINGLE_LOOP:
mode = PlayModeEnum.LIST_LOOP;
ToastUtil.toastShort(getString(R.string.mode_loop));
break;
}
AudioPlayer.Companion.get().setPlayMode(mode);
initPlayMode();
}
void onBackPressed() {
getActivity().onBackPressed();
ivBack.setEnabled(false);
}
void setCoverAndBg(Music music) {
CoverLoader.Companion.get().loadRound(music, bitmap -> ivCover.setImageBitmap(bitmap));
CoverLoader.Companion.get().loadBlur(music, bitmap -> ivPlayingBg.setImageBitmap(bitmap));
}
String formatTime(long time) {
return formatTime("mm:ss", time);
}
public static String formatTime(String pattern, long milli) {
int m = (int) (milli / DateUtils.MINUTE_IN_MILLIS);
int s = (int) ((milli / DateUtils.SECOND_IN_MILLIS) % 60);
String mm = String.format(Locale.getDefault(), "%02d", m);
String ss = String.format(Locale.getDefault(), "%02d", s);
return pattern.replace("mm", mm).replace("ss", ss);
}
@Override
public void onDestroy() {
AudioPlayer.Companion.get().removeOnPlayEventListener(this);
super.onDestroy();
}
}
......@@ -2,6 +2,8 @@ package com.ydl.component.mvp
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import com.ydl.component.model.HomeFMBean
import com.ydl.ydlcommon.data.http.BaseAPIResponse
import com.ydl.ydlcommon.mvp.base.IModel
import com.ydl.ydlcommon.mvp.base.IPresenter
import com.ydl.ydlcommon.mvp.lce.ILceView
......@@ -19,12 +21,13 @@ interface DemoContract {
interface Presenter : IPresenter<View> {
fun loadUsers()
fun loadHome()
fun loadFmMusic()
}
interface Model : IModel{
fun getUser(): Observable<JsonArray>
fun getHome(): Observable<JsonObject>
fun getFmMusic(): Observable<HomeFMBean>
}
}
\ No newline at end of file
......@@ -5,6 +5,8 @@ import android.arch.lifecycle.OnLifecycleEvent
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import com.ydl.component.api.DemoService
import com.ydl.component.model.HomeFMBean
import com.ydl.ydlcommon.data.http.RxUtils
import com.ydl.ydlcommon.mvp.base.BaseModel
import com.ydl.ydlnet.YDLHttpUtils
import io.reactivex.Observable
......@@ -14,6 +16,12 @@ import io.reactivex.Observable
* Des:
*/
class DemoModel : BaseModel(), DemoContract.Model {
override fun getFmMusic(): Observable<HomeFMBean> {
return YDLHttpUtils.obtainApi(DemoService::class.java)
.getFMData().compose(RxUtils.resultJavaData())
}
override fun getHome(): Observable<JsonObject> {
return YDLHttpUtils.obtainApi(DemoService::class.java)
.getHomeData()
......
......@@ -2,6 +2,9 @@ package com.ydl.component.mvp
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import com.ydl.component.model.HomeFMBean
import com.ydl.media.music.AudioPlayer
import com.ydl.media.music.model.Music
import com.ydl.ydlcommon.mvp.base.BasePresenter
import com.ydl.ydlcommon.utils.RxLifecycleUtils
import com.ydl.ydlnet.builder.interceptor.YDLTransformer
......@@ -13,6 +16,26 @@ import com.ydl.ydlnet.client.observer.CommonObserver
*/
class DemoPresenter : BasePresenter<DemoContract.View,DemoContract.Model>(),
DemoContract.Presenter {
override fun loadFmMusic() {
mModel.getFmMusic()
.compose(YDLTransformer.switchSchedulers(mView))
.compose(RxLifecycleUtils.bindToLifecycle(mView))//使用 Rxlifecycle,使 Disposable 和 Activity 一起销毁
.subscribe(object : CommonObserver<HomeFMBean>() {
override fun onError(errorMsg: String) {
}
override fun onSuccess(homeFMBean: HomeFMBean) {
// var jsonStirng = Gson().toJson(homeFMBean.list)
// val list = Gson().fromJson<ArrayList<Music>>(jsonStirng, object : TypeToken<ArrayList<Music>>() {}.type)
homeFMBean.list?.let {
AudioPlayer.get().addPlayList(stringToMusicPlayerList(it))
mView.setData("")
}
}
})
}
override fun loadHome() = mModel!!.getHome()
.compose(YDLTransformer.switchSchedulers(mView))
.compose(RxLifecycleUtils.bindToLifecycle(mView!!))//使用 Rxlifecycle,使 Disposable 和 Activity 一起销毁
......@@ -43,4 +66,24 @@ class DemoPresenter : BasePresenter<DemoContract.View,DemoContract.Model>(),
return DemoModel()
}
//将列表转为播放器可用列表
private fun stringToMusicPlayerList(list: List<HomeFMBean.ListBean>): ArrayList<Music> {
var nlist = ArrayList<Music>()
try {
for (i in list.indices) {
var be = Music()
be.path = list[i].path
be.coverPath = list[i].coverPath
be.title = list[i].title
be.album = list[i].album
be.artist = list[i].artist
nlist.add(be)
}
} catch (e: Exception) {
e.printStackTrace()
}
return nlist
}
}
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="300"
android:fromYDelta="0"
android:toYDelta="100%p" />
</set>
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="300"
android:fromYDelta="100%p"
android:toYDelta="0" />
</set>
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_play_btn_loop_pressed" android:state_pressed="true" />
<item android:drawable="@drawable/ic_play_btn_loop" />
</selector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_play_btn_next_pressed" android:state_pressed="true" />
<item android:drawable="@drawable/ic_play_btn_next" />
</selector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_play_btn_one_pressed" android:state_pressed="true" />
<item android:drawable="@drawable/ic_play_btn_one" />
</selector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_play_btn_pause_pressed" android:state_pressed="true" />
<item android:drawable="@drawable/ic_play_btn_pause" />
</selector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/play_btn_pause_selector" android:state_selected="true" />
<item android:drawable="@drawable/play_btn_play_selector" />
</selector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_play_btn_play_pressed" android:state_pressed="true" />
<item android:drawable="@drawable/ic_play_btn_play" />
</selector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_play_btn_prev_pressed" android:state_pressed="true" />
<item android:drawable="@drawable/ic_play_btn_prev" />
</selector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_play_btn_shuffle_pressed" android:state_pressed="true" />
<item android:drawable="@drawable/ic_play_btn_shuffle" />
</selector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable="@drawable/play_btn_loop_selector"
android:maxLevel="0" />
<item
android:drawable="@drawable/play_btn_shuffle_selector"
android:maxLevel="1" />
<item
android:drawable="@drawable/play_btn_one_selector"
android:maxLevel="2" />
</level-list>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape>
<corners android:radius="1dp" />
<solid android:color="@color/white_24p" />
</shape>
</item>
<item android:id="@android:id/progress">
<clip>
<shape>
<corners android:radius="1dp" />
<solid android:color="@color/red" />
</shape>
</clip>
</item>
<item android:id="@android:id/secondaryProgress">
<clip>
<shape>
<corners android:radius="1dp" />
<solid android:color="@color/red_30p" />
</shape>
</clip>
</item>
</layer-list>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<LinearLayout
android:orientation="vertical"
android:id="@+id/lce_content_view"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:paddingLeft="30dp"
android:textSize="14dp"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@color/platform_google_blue"
android:gravity="center_vertical"
android:textColor="@color/platform_white"
android:paddingLeft="30dp"
android:text="Multiple Base Url Request Test"
android:background="@color/platform_google_blue"
android:layout_width="match_parent"
android:layout_height="50dp"/>
android:textColor="@color/platform_white"
android:textSize="14dp" />
<TextView
android:layout_margin="30dp"
android:text="CONTENT"
android:id="@+id/tv_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
android:layout_height="wrap_content"
android:layout_margin="30dp"
android:text="CONTENT" />
<LinearLayout
android:layout_marginLeft="30dp"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="80dp"
>
android:layout_marginLeft="30dp"
android:orientation="horizontal">
<TextView
android:layout_marginRight="40dp"
android:background="@android:color/darker_gray"
android:padding="10dp"
android:id="@+id/tv_user"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="User Request!"
/>
<TextView
android:background="@android:color/holo_green_dark"
android:layout_marginRight="40dp"
android:background="@android:color/darker_gray"
android:padding="10dp"
android:text="User Request!" />
<TextView
android:id="@+id/tv_home"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Home Request!"
/>
android:background="@android:color/holo_green_dark"
android:padding="10dp"
android:text="Home Request!" />
</LinearLayout>
<TextView
android:paddingLeft="30dp"
android:textSize="14dp"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@color/platform_google_blue"
android:gravity="center_vertical"
android:textColor="@color/platform_white"
android:paddingLeft="30dp"
android:text="Component Module Test"
android:background="@color/platform_google_blue"
android:layout_width="match_parent"
android:layout_height="50dp"/>
android:textColor="@color/platform_white"
android:textSize="14dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/bt_to_other"
android:layout_marginTop="10dp"
android:layout_marginLeft="30dp"
android:text="Jump to Other"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
android:layout_height="wrap_content"
android:layout_marginLeft="30dp"
android:layout_marginTop="10dp"
android:text="Jump to Other" />
<Button
android:id="@+id/bt_to_consultant"
android:layout_marginTop="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="30dp"
android:text="Jump to consultant"
android:layout_marginTop="10dp"
android:text="Jump to consultant" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/bt_to_music"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
android:layout_height="wrap_content"
android:layout_marginLeft="30dp"
android:layout_marginTop="10dp"
android:text="Jump to Musci" />
</LinearLayout>
......@@ -92,6 +105,6 @@
<com.ydl.ydlcommon.mvp.lce.view.YDLStateView
android:id="@+id/lce_state_view"
android:layout_width="match_parent"
android:layout_height="300dp"/>
android:layout_height="300dp" />
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/lce_content_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"/>
<com.ydl.ydlcommon.mvp.lce.view.YDLStateView
android:id="@+id/lce_state_view"
android:layout_width="match_parent"
android:layout_height="300dp"/>
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/iv_play_page_bg"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:scaleType="centerCrop"
android:src="@drawable/play_page_default_bg" />
<LinearLayout
android:id="@+id/ll_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black_50p"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:id="@+id/iv_back"
android:layout_width="?attr/actionBarSize"
android:layout_height="?attr/actionBarSize"
android:scaleType="centerInside"
android:src="@drawable/ic_arrow_down" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:orientation="vertical">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:singleLine="true"
android:text="无音乐"
android:textColor="@color/white"
android:textSize="@dimen/text_size_common" />
<TextView
android:id="@+id/tv_artist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:singleLine="true"
android:textColor="@color/grey"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>
<RelativeLayout
android:id="@+id/vp_play_page"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:overScrollMode="never">
<ImageView
android:src="@drawable/play_page_disc"
android:id="@+id/iv_cover"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_centerInParent="true" />
</RelativeLayout>
<include layout="@layout/include_play_page_controller" />
</LinearLayout>
</FrameLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin">
<TextView
android:id="@+id/tv_current_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/play_time_start"
android:textColor="@color/white"
android:textSize="10sp" />
<SeekBar
android:id="@+id/sb_progress"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:maxHeight="2dp"
android:minHeight="2dp"
android:progressDrawable="@drawable/seek_bar_progress_style"
android:thumb="@drawable/ic_seek_bar_progress_btn" />
<TextView
android:id="@+id/tv_total_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/play_time_start"
android:textColor="@color/white_50p"
android:textSize="10sp" />
</LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iv_mode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:src="@drawable/play_mode_level_list" />
<ImageView
android:id="@+id/iv_prev"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="@+id/iv_play"
android:src="@drawable/play_btn_prev_selector" />
<ImageView
android:id="@+id/iv_play"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="@drawable/play_btn_play_pause_selector" />
<ImageView
android:id="@+id/iv_next"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toRightOf="@id/iv_play"
android:src="@drawable/play_btn_next_selector" />
</RelativeLayout>
</LinearLayout>
\ No newline at end of file
......@@ -3,4 +3,21 @@
<color name="text_user" tools:ignore="MissingDefaultResource">#5195cb</color>
<color name="black" tools:ignore="MissingDefaultResource">#FF000000</color>
<color name="white">@android:color/white</color>
<color name="white_8p">#14FFFFFF</color>
<color name="white_16p">#29FFFFFF</color>
<color name="white_24p">#3DFFFFFF</color>
<color name="white_50p">#80FFFFFF</color>
<color name="white_96p">#F5FFFFFF</color>
<color name="black_50p">#80000000</color>
<color name="red">#F44336</color>
<color name="red_30p">#4CF44336</color>
<color name="blue">#2196F3</color>
<color name="grey">#9E9E9E</color>
<color name="grey_50p">#809E9E9E</color>
<color name="grey_300">#E0E0E0</color>
<color name="grey_700_96p">#F5616161</color>
<color name="grey_300_96p">#F5E0E0E0</color>
<color name="grey_900">#212121</color>
<color name="grey_900_96p">#F5212121</color>
</resources>
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="text_size_common">16sp</dimen>
<dimen name="play_bar_height">57dp</dimen>
<dimen name="screen_tips_top">80dp</dimen>
<dimen name="divider_height">0.5dp</dimen>
</resources>
<resources>
<string name="play_time_start">00:00</string>
<string name="mode_loop">列表循环</string>
<string name="mode_shuffle">随机播放</string>
<string name="mode_one">单曲循环</string>
</resources>
......@@ -16,14 +16,30 @@ buildscript {
}
repositories {
google()
//壹点灵android maven私服 开发版
maven{
url 'http://nexus.yidianling.com/repository/AndroidRepository/'
credentials {
username "admin"
password "fjoi#1+#@"
}
}
maven{
url 'http://nexus.yidianling.com/repository/AndroidReleases/'
credentials {
username "admin"
password "fjoi#1+#@"
}
jcenter()
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
}
maven{ url 'http://maven.aliyun.com/nexus/content/groups/public/'}
maven {
url 'https://dl.bintray.com/zouyuhan/maven'
}
google()
jcenter()
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.0'
......@@ -45,31 +61,32 @@ buildscript {
allprojects {
repositories {
google()
jcenter()
maven { url 'http://developer.huawei.com/repo/' }
maven { url "https://jitpack.io" }
maven {
url 'https://maven.google.com'
//壹点灵android maven私服 开发版
maven{
url 'http://nexus.yidianling.com/repository/AndroidRepository/'
credentials {
username "admin"
password "fjoi#1+#@"
}
maven {
url 'https://dl.bintray.com/zouyuhan/maven'
}
//壹点灵android maven私服
maven {
maven{
url 'http://nexus.yidianling.com/repository/AndroidReleases/'
credentials {
username "admin"
password "fjoi#1+#@"
}
}
maven{ url 'http://maven.aliyun.com/nexus/content/groups/public/'}
maven { url'http://developer.huawei.com/repo/' }
maven { url "https://jitpack.io" }
maven {
url 'http://nexus.yidianling.com/repository/AndroidRepository/'
credentials {
username "admin"
password "fjoi#1+#@"
url 'https://maven.google.com'
}
maven {
url 'https://dl.bintray.com/zouyuhan/maven'
}
google()
jcenter()
}
}
......
ext {
kotlin_version = '1.3.21'
dev_mode = true
ydl_app = [
appName : "心理咨询壹点灵",
......@@ -173,5 +174,10 @@ ext {
"ydl-utils" : 'com.ydl:ydl-utils:0.0.2',
"ydl-net" : 'com.ydl:ydl-net:0.0.1',
//Modular Api
"ydl-user-router" : 'com.ydl:router:1.0.0-SNAPSHOT@aar',
"ydl-platform" : 'com.ydl:ydl-platform:0.0.10@aar',
"ydl-m-user-api" : 'com.ydl:m-user-api:0.0.5',
"ydl-webview" : 'com.ydl:ydl-webview:0.0.14@aar'
]
}
......@@ -51,21 +51,27 @@ android {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
kapt 'com.alibaba:arouter-compiler:1.2.2'
api'com.ydl:router:1.0.0-SNAPSHOT@aar'
api rootProject.ext.dependencies["ydl-user-router"]
if (rootProject.ext.dev_mode){
//开发时使用
implementation modularPublication('com.ydl:m-user-api')
api project(":ydl-webview")
api project(":ydl-platform")
} else {
//发布时使用
compileOnly rootProject.ext.dependencies["ydl-m-user-api"]
api rootProject.ext.dependencies["ydl-webview"]
api(rootProject.ext.dependencies["ydl-platform"]) {
transitive = true
}
}
// compileOnly 'com.ydl:m-user-api:0.0.4'
// api 'com.ydl:ydl-webview:0.0.12@aar'
// api('com.ydl:ydl-platform:0.0.9@aar') {
// transitive = true
// }
}
......@@ -8,7 +8,7 @@ modular {
groupId = "com.ydl"
artifactId = "m-consultant-module-xlzx"
// 上报的业务模块 aar 包的版本号
version = "0.0.9"
version = "0.0.11"
}
ydl{
......@@ -16,7 +16,7 @@ modular {
groupId = "com.ydl"
artifactId = "m-consultant-module-ydl"
// 上报的业务模块 aar 包的版本号
version = "0.0.9"
version = "0.0.11"
}
}
......
......@@ -109,22 +109,24 @@ class ExpertSearchAdapter(private val context: Context, private val expertSearch
}else{
holder.tvCity.text = ""
}
//优质图标
// if (5 == itemBean.abilityLevel){
// //精英等级为5级显示优质图标
// if (null != itemBean.tagsIcon && !TextUtils.isEmpty(itemBean.tagsIcon.abilityLevelIcon)){
// expertSearchView.showImage(itemBean.tagsIcon.abilityLevelIcon,holder.imgAbilityLevel)
// }else{
// val sp = SimpleImageOpConfiger()
// sp.loadingPic = R.drawable.ability_level
// sp.errorPic = R.drawable.ability_level
// expertSearchView.showImage("",holder.imgAbilityLevel,sp)
// }
//能力等级标签
if (1 == itemBean.abilityLevel){
//实习
holder.imgAbilityLevel.background = context.resources.getDrawable(R.drawable.expert_search_shixi)
holder.imgAbilityLevel.visibility = View.VISIBLE
}else if (2 == itemBean.abilityLevel){
//新手
holder.imgAbilityLevel.background = context.resources.getDrawable(R.drawable.expert_search_xinshou)
holder.imgAbilityLevel.visibility = View.VISIBLE
}
// else if (3 == itemBean.abilityLevel){
// //精英
// holder.imgAbilityLevel.background = context.resources.getDrawable(R.drawable.expert_search_jingying)
// holder.imgAbilityLevel.visibility = View.VISIBLE
// }else{
// holder.imgAbilityLevel.visibility = View.GONE
// }
else{
holder.imgAbilityLevel.visibility = View.GONE
}
//公益图标
if (1 == itemBean.hasServiceFree){
if (null != itemBean.tagsIcon && !TextUtils.isEmpty(itemBean.tagsIcon.serviceFreeIcon)){
......@@ -175,11 +177,13 @@ class ExpertSearchAdapter(private val context: Context, private val expertSearch
if (!TextUtils.isEmpty(itemBean.tags)){
val tagList = itemBean.tags!!.split("|")
for (tag in tagList) {
if (!TextUtils.isEmpty(tag)){
val view = LayoutInflater.from(context).inflate(R.layout.item_tag, holder.ll_tags, false)
view.tvTag.text = tag
holder.ll_tags.addView(view)
}
}
}
//帮助人数
val orderUser = StringBuffer()
holder.tvOrderNum.text = orderUser.append(itemBean.zixunOrderUser).append("").toString()
......
......@@ -35,7 +35,7 @@ data class DoctorServiceItem(/**
*/
val isOnline : Int?,
/**
* 能力等级 5.精英
* 能力等级 1.实习 2.新手 3.精英
*/
val abilityLevel : Int?,
/**
......
......@@ -65,6 +65,7 @@
android:textSize="@dimen/platform_dp_18"
android:textColor="@color/platform_color_242424"
android:layout_marginStart="@dimen/dp_12"
android:gravity="center_vertical"
android:textStyle="bold"
tools:ignore="SpUsage"
tools:text="罗静"
......@@ -81,16 +82,6 @@
android:layout_alignBottom="@+id/tvName"
android:layout_toEndOf="@+id/tvName"
android:layout_marginStart="@dimen/platform_dp_2">
<!--优质图标-->
<ImageView
android:id="@+id/imgAbilityLevel"
android:layout_width="37dp"
android:layout_height="@dimen/dp_12"
android:layout_marginLeft="@dimen/platform_dp_4"
android:scaleType="fitCenter"
tools:background="@drawable/ability_level"
android:layout_marginStart="@dimen/platform_dp_4"
android:visibility="gone"/>
<!--公益图标-->
<ImageView
android:id="@+id/imgServiceFree"
......@@ -108,7 +99,17 @@
android:layout_marginLeft="@dimen/platform_dp_4"
android:scaleType="fitCenter"
tools:background="@drawable/new_enter"
android:layout_marginStart="@dimen/platform_dp_4" />
android:layout_marginStart="@dimen/dp_4" />
<!--优质图标-->
<ImageView
android:id="@+id/imgAbilityLevel"
android:layout_width="26dp"
android:layout_height="@dimen/dp_12"
android:layout_marginLeft="@dimen/platform_dp_4"
android:scaleType="fitCenter"
tools:background="@drawable/expert_search_shixi"
android:layout_marginStart="@dimen/platform_dp_4"
android:visibility="gone"/>
</LinearLayout>
......@@ -178,15 +179,7 @@
android:textSize="@dimen/dp_12"
android:textColor="@color/platform_color_999999"
android:text="条评价" />
<TextView
android:id="@+id/tvCity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="浙江.杭州"
android:layout_weight="1"
android:gravity="right"
android:textColor="@color/platform_color_999999"
android:textSize="@dimen/dp_12"/>
</LinearLayout>
<!--帮助人数-->
......@@ -308,6 +301,18 @@
android:layout_marginBottom="2.5dp"
android:text="起"
android:layout_marginLeft="1dp" />
<TextView
android:id="@+id/tvCity"
android:layout_below="@+id/tvName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/platform_dp_4"
android:layout_alignParentRight="true"
tools:text="浙江.杭州"
android:gravity="right"
android:textColor="@color/platform_color_999999"
android:textSize="@dimen/dp_12"/>
<!--<View-->
<!--android:id="@+id/vLine"-->
<!--android:layout_below="@+id/tvPrice"-->
......
......@@ -60,22 +60,25 @@ android {
dependencies {
api fileTree(dir: 'libs', include: ['*.aar','*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
kapt 'com.alibaba:arouter-compiler:1.2.2'
implementation "org.jetbrains.kotlin:kotlin-script-runtime:1.3.41"
compileOnly 'com.ydl:m-user-api:0.0.4'
api'com.ydl:router:1.0.0-SNAPSHOT@aar'
// api 'com.ydl:ydl-webview:0.0.12@aar'
// api('com.ydl:ydl-platform:0.0.9@aar') {
// transitive = true
// }
api rootProject.ext.dependencies["ydl-user-router"]
if (rootProject.ext.dev_mode){
//开发时使用
api project(':ydl-webview')
api project(':ydl-platform')
implementation modularPublication('com.ydl:m-user-api')
}else {
//发布时使用
compileOnly rootProject.ext.dependencies["ydl-m-user-api"]
api rootProject.ext.dependencies["ydl-webview"]
api(rootProject.ext.dependencies["ydl-platform"]) {
transitive = true
}
}
}
......@@ -8,7 +8,7 @@ modular {
groupId = "com.ydl"
artifactId = "m-user-module-xlzx"
// 上报的业务模块 aar 包的版本号
version = "0.0.17"
version = "0.0.19"
}
ydl{
......@@ -16,7 +16,7 @@ modular {
groupId = "com.ydl"
artifactId = "m-user-module-ydl"
// 上报的业务模块 aar 包的版本号
version = "0.0.17"
version = "0.0.19"
}
}
......@@ -24,7 +24,7 @@ modular {
//发布信息 module/api 通用
groupId = "com.ydl"
artifactId = "m-user-api"
version = "0.0.4"
version = "0.0.5"
// API 层打包时需要引入的依赖
apiDependencies {
implementation "com.google.code.gson:gson:2.8.2"
......
......@@ -37,7 +37,7 @@ object LoginUtils {
//设置异常用户ID
CrashReport.setUserId(userInfo?.uid)
//登录IM聊天
loginIm(userInfo)
// loginIm(userInfo)
//设置极光注册id
LoginHelper.setChannelId()
val loginEvent = UserLoginEvent("login", userInfo?.uid!!)
......
......@@ -26,6 +26,7 @@ import com.yidianling.user.LoginHelper;
import com.yidianling.user.R;
import com.yidianling.user.UserConstants;
import com.yidianling.user.UserHelper;
import com.yidianling.user.api.event.UserBindPhoneEvent;
import com.yidianling.user.event.UpdateBindStatusEvent;
import com.yidianling.user.http.UserHttp;
import com.yidianling.user.http.UserHttpImpl;
......@@ -241,6 +242,8 @@ public class GetIdentifyingCodeActivity extends BaseActivity implements View.OnC
UserHelper.INSTANCE.getUserInfo().getUserInfo().setPhone(phoneNum);
UserHelper.INSTANCE.getUserInfo().getUserInfo().setBind_phone(1);
}
//手机绑定成功事件
EventBus.getDefault().post(new UserBindPhoneEvent());
ToastHelper.Companion.show("手机绑定成功");
finish();
......
......@@ -151,7 +151,7 @@ class SmsLoginActivity : BaseActivity() {
if (it.firstLogin == 1) {
baiduActionBury()
}else {
ActionCountUtils.count(UserBIConstants.ACTION_TYPE_CODE_LOGIN)
ActionCountUtils.countUid(it.uid!!,UserBIConstants.ACTION_TYPE_CODE_LOGIN)
}
ActivityManager.finishActivity(LoginActivity::class.java)
if (ActivityManager.getActivitySize() <= 1) {
......
......@@ -172,11 +172,11 @@ class RegisterAndLoginActivity : BaseMvpActivity<ILoginContract.View, ILoginCont
/**
* 注册流程成功之后,进行百度埋点
*/
override fun baiduActionBury(positionClick: String) {
ActionCountUtils.baiduCount(BIConstants.APP_REGISTER_PAGE, positionClick, 25)
override fun baiduActionBury(uid: String?, positionClick: String) {
ActionCountUtils.baiduCount(uid, BIConstants.APP_REGISTER_PAGE, positionClick, "","", 25)
}
private fun initAuthHelper() {
/*
* 1.init get token callback Listener
......
......@@ -85,7 +85,7 @@ interface ILoginContract {
/**
* 三方登录成功后如果判断是第一次登录,则未注册,进行百度埋点
*/
fun baiduActionBury(type: String)
fun baiduActionBury(uid: String? = "", type: String)
}
interface Presenter : IPresenter< View> {
......
......@@ -101,7 +101,7 @@ class InputPassWordPresenterImpl : BasePresenter<IInputPassWordContract.View, II
mView.baiduActionBury()
UMEventUtils.loginSuccess("InputPassWordActivity|Reg_succ")
} else {
ActionCountUtils.count(UserBIConstants.ACTION_TYPE_PWD_LOGIN)
ActionCountUtils.countUid(UserBIConstants.ACTION_TYPE_PWD_LOGIN,it.data.uid!!)
UMEventUtils.loginSuccess("InputPassWordActivity|Login_succ")
}
mView.closeActivity()
......
......@@ -71,7 +71,7 @@ class LoginPresenterImpl(view: ILoginContract.View) : BasePresenter<ILoginContra
//绑定手机号
saveUserData(it.data)
if (it.data.firstLogin == 1) {//第一次登录:是注册
mView.baiduActionBury(UserBIConstants.POSITION_ALIYUN_REGISTER_CLICK)
mView.baiduActionBury(it.data.uid, UserBIConstants.POSITION_ALIYUN_REGISTER_CLICK)
UMEventUtils.loginSuccess("RegisterAndLoginActivity|Reg_succ")
} else {
ActionCountUtils.count(UserBIConstants.ACTION_TYPE_DIRECT_LOGIN)
......@@ -203,7 +203,7 @@ class LoginPresenterImpl(view: ILoginContract.View) : BasePresenter<ILoginContra
LogHelper.getInstance().writeLogSync(media.getName() + "登录成功")
if (media == SHARE_MEDIA.QQ) {
if (it.data.firstLogin == 1) {//第一次登录:是注册
mView.baiduActionBury(UserBIConstants.POSITION_QQ_REGISTER_CLICK)
mView.baiduActionBury(it.data.uid,UserBIConstants.POSITION_QQ_REGISTER_CLICK)
UMEventUtils.loginSuccess("RegisterAndLoginActivity|Reg_succ_byQQ")
} else {
ActionCountUtils.count(UserHelper.getUserInfo()!!.uid, UserBIConstants.PART_ID_LOGIN_MAIN,
......@@ -213,7 +213,7 @@ class LoginPresenterImpl(view: ILoginContract.View) : BasePresenter<ILoginContra
}
} else {
if (it.data.firstLogin == 1) {//第一次登录:是注册
mView.baiduActionBury(UserBIConstants.POSITION_WX_REGISTER_CLICK)
mView.baiduActionBury(it.data.uid,UserBIConstants.POSITION_WX_REGISTER_CLICK)
UMEventUtils.loginSuccess("RegisterAndLoginActivity|Reg_succ_byWechat")
} else {
ActionCountUtils.count(UserHelper.getUserInfo()!!.uid, UserBIConstants.PART_ID_LOGIN_MAIN,
......
......@@ -147,7 +147,7 @@ class VerificationCodePresenterImpl(view: IVerificationCodeContract.View) : Base
mView.baiduActionBury()
UMEventUtils.loginSuccess("VerificationCodeActivity|Reg_succ")
} else {
ActionCountUtils.count(UserBIConstants.ACTION_TYPE_CODE_LOGIN)
ActionCountUtils.countUid(UserBIConstants.ACTION_TYPE_CODE_LOGIN,it.data.uid!!)
UMEventUtils.loginSuccess("VerificationCodeActivity|Login_succ")
}
mView.closeActivity()
......
package com.yidianling.user.api.event
/**
* Created by haorui on 2019-10-21.
* Des:
*/
class UserBindPhoneEvent
include ':app', ':ydl-net', ':ydl-utils', ':ydl-platform', ':ydl-webview',":m-user", ':m-consultant',":router"
include ':app', ':ydl-net', ':ydl-utils', ':ydl-platform', ':ydl-webview', ':ydl-media',":m-user", ':m-consultant',":router"
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
apply from: "../maven_push.gradle"
kapt {
arguments {
arg("AROUTER_MODULE_NAME", "media")
}
}
android {
compileSdkVersion 28
defaultConfig {
minSdkVersion 17
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
javaCompileOptions {
annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME: "media"]
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
}
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
kapt 'com.alibaba:arouter-compiler:1.2.2'
api rootProject.ext.dependencies["ijkplayer-armv5"]
api rootProject.ext.dependencies["ijkplayer-arm64"]
api rootProject.ext.dependencies["ijkplayer-x86"]
api rootProject.ext.dependencies["ijkplayer-x86_64"]
api(rootProject.ext.dependencies["ijkplayer-jjdxm"]) {
exclude group: 'com.android.support', module: 'appcompat-v7'
}
api rootProject.ext.dependencies["ydl-user-router"]
if (rootProject.ext.dev_mode){
//开发时使用
api project(':ydl-platform')
implementation modularPublication('com.ydl:m-user-api')
}else {
//发布时使用
compileOnly rootProject.ext.dependencies["ydl-m-user-api"]
api(rootProject.ext.dependencies["ydl-platform"]) {
transitive = true
}
}
}
IS_PUBLISH=true
VERSION_NAME=0.0.1
\ No newline at end of file
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.ydl.media">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application>
<service android:name=".music.PlayService" />
<receiver android:name=".music.receiver.RemoteControlReceiver">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</receiver>
<receiver android:name=".music.receiver.StatusBarReceiver">
<intent-filter>
<action android:name="com.ydl.media.STATUS_BAR_ACTIONS" />
</intent-filter>
</receiver>
</application>
</manifest>
\ No newline at end of file
package com.ydl.media.music
import android.content.Context
import android.content.IntentFilter
import android.media.AudioManager
import android.media.MediaPlayer
import android.os.Handler
import android.os.Looper
import com.ydl.media.music.enums.PlayModeEnum
import com.ydl.media.music.manager.AudioFocusManager
import com.ydl.media.music.manager.MediaSessionManager
import com.ydl.media.music.manager.NotifyManager
import com.ydl.media.music.model.Music
import com.ydl.media.music.receiver.NoisyAudioStreamReceiver
import com.yidianling.common.tools.ToastUtil
import java.io.IOException
import java.util.*
import kotlin.collections.ArrayList
/**
* Created by haorui on 2019-10-27 .
* Des:
*/
class AudioPlayer private constructor() {
private var context: Context? = null
private var audioFocusManager: AudioFocusManager? = null
var mediaPlayer: MediaPlayer? = null
private var handler: Handler? = null
private var noisyReceiver: NoisyAudioStreamReceiver? = null
private var noisyFilter: IntentFilter? = null
private var musicList: MutableList<Music> = ArrayList()
private val listeners = ArrayList<OnPlayerEventListener>()
private var state = STATE_IDLE
private val mPublishRunnable = object : Runnable {
override fun run() {
if (isPlaying) {
for (listener in listeners) {
listener.onPublish(mediaPlayer!!.currentPosition)
}
}
handler!!.postDelayed(this, TIME_UPDATE)
}
}
val audioSessionId: Int
get() = mediaPlayer!!.audioSessionId
val audioPosition: Long
get() = if (isPlaying || isPausing) {
mediaPlayer!!.currentPosition.toLong()
} else {
0
}
val playMusic: Music?
get() = if (musicList.isEmpty()) {
null
} else musicList[playPosition]
val isPlaying: Boolean
get() = state == STATE_PLAYING
val isPausing: Boolean
get() = state == STATE_PAUSE
val isPreparing: Boolean
get() = state == STATE_PREPARING
val isIdle: Boolean
get() = state == STATE_IDLE
var playPosition: Int = 0
get() {
if (field < 0 || field >= musicList.size) {
field = 0
}
return field
}
/**
* 设置播放模式
* 默认为列表循环
*/
var playMode = PlayModeEnum.LIST_LOOP
private object SingletonHolder {
val instance = AudioPlayer()
}
/**
* 初始化播放器
*/
fun init(context: Context) {
this.context = context.applicationContext
audioFocusManager = AudioFocusManager(context)
mediaPlayer = MediaPlayer()
handler = Handler(Looper.getMainLooper())
noisyReceiver = NoisyAudioStreamReceiver()
noisyFilter = IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY)
mediaPlayer!!.setOnCompletionListener {
if (playMode == PlayModeEnum.SINGLE) {
stopPlayer()
playMode = PlayModeEnum.LIST_LOOP
return@setOnCompletionListener
}
next()
}
mediaPlayer!!.setOnPreparedListener { mp ->
if (isPreparing) {
startPlayer()
}
}
mediaPlayer!!.setOnBufferingUpdateListener { mp, percent ->
for (listener in listeners) {
listener.onBufferingUpdate(percent)
}
}
}
/**
* 添加播放监听
*/
fun addOnPlayEventListener(listener: OnPlayerEventListener) {
if (!listeners.contains(listener)) {
listeners.add(listener)
}
}
/**
* 移除播放监听
*/
fun removeOnPlayEventListener(listener: OnPlayerEventListener) {
listeners.remove(listener)
}
/**
* 添加播放列表
*/
fun addPlayList(music: ArrayList<Music>) {
if (music.isEmpty()) {
return
}
musicList.clear()
musicList.addAll(music)
}
/**
* 单曲模式播放音乐
*/
fun singlePlay(music: Music) {
musicList.clear()
musicList.add(music)
playMode = PlayModeEnum.SINGLE
play(0)
}
/**
* 添加后自动播放
*/
fun addAndPlay(music: Music) {
var position = musicList.indexOf(music)
if (position < 0) {
musicList.add(music)
position = musicList.size - 1
}
play(position)
}
/**
* 播放第一首
*/
fun play() {
play(0)
}
/**
* 播放指定索引的音乐
*/
fun play(position: Int) {
var position = position
if (musicList.isEmpty()) {
return
}
if (position < 0) {
position = musicList.size - 1
} else if (position >= musicList.size) {
position = 0
}
playPosition = position
val music = playMusic
try {
mediaPlayer!!.reset()
mediaPlayer!!.setDataSource(music!!.path)
mediaPlayer!!.prepareAsync()
state = STATE_PREPARING
for (listener in listeners) {
listener.onChange(music)
}
if (playMode != PlayModeEnum.SINGLE){
NotifyManager.get().showPlay(music)
MediaSessionManager.get().updateMetaData(music)
MediaSessionManager.get().updatePlaybackState()
}
} catch (e: IOException) {
e.printStackTrace()
ToastUtil.toastShort("当前歌曲无法播放")
}
}
/**
* 从列表里移除播放音乐
*/
fun delete(position: Int) {
val music = musicList!!.removeAt(position)
if (playPosition > position) {
playPosition -= 1
} else if (playPosition == position) {
if (isPlaying || isPreparing) {
playPosition -= 1
next()
} else {
stopPlayer()
for (listener in listeners) {
listener.onChange(playMusic!!)
}
}
}
}
/**
* 暂停或者播放音乐
*/
fun playPause() {
if (isPreparing) {
stopPlayer()
} else if (isPlaying) {
pausePlayer()
} else if (isPausing) {
startPlayer()
} else {
play(playPosition)
}
}
/**
*开启播放器
*/
fun startPlayer() {
if (!isPreparing && !isPausing) {
return
}
if (audioFocusManager!!.requestAudioFocus()) {
mediaPlayer!!.start()
state = STATE_PLAYING
handler!!.post(mPublishRunnable)
if (playMode != PlayModeEnum.SINGLE){
NotifyManager.get().showPlay(playMusic)
MediaSessionManager.get().updatePlaybackState()
}
context!!.registerReceiver(noisyReceiver, noisyFilter)
for (listener in listeners) {
listener.onPlayerStart()
}
}
}
/**
* 暂停播放器
*/
@JvmOverloads
fun pausePlayer(abandonAudioFocus: Boolean = true) {
if (!isPlaying) {
return
}
mediaPlayer!!.pause()
state = STATE_PAUSE
handler!!.removeCallbacks(mPublishRunnable)
NotifyManager.get().showPause(playMusic)
MediaSessionManager.get().updatePlaybackState()
context!!.unregisterReceiver(noisyReceiver)
if (abandonAudioFocus) {
audioFocusManager!!.abandonAudioFocus()
}
for (listener in listeners) {
listener.onPlayerPause()
}
}
/**
* 停止播放器
*/
fun stopPlayer() {
if (isIdle) {
return
}
pausePlayer()
mediaPlayer!!.reset()
state = STATE_IDLE
}
/**
* 下一首
*/
operator fun next() {
if (musicList.isEmpty()) {
return
}
when (playMode) {
PlayModeEnum.SHUFFLE -> play(Random().nextInt(musicList.size))
PlayModeEnum.SINGLE_LOOP -> play(playPosition)
PlayModeEnum.LIST_LOOP -> play(playPosition + 1)
else -> play(playPosition + 1)
}
}
/**
* 上一首
*/
fun prev() {
if (musicList.isEmpty()) {
return
}
when (playMode) {
PlayModeEnum.SHUFFLE -> play(Random().nextInt(musicList.size))
PlayModeEnum.SINGLE_LOOP -> play(playPosition)
PlayModeEnum.LIST_LOOP -> play(playPosition - 1)
else -> play(playPosition - 1)
}
}
/**
* 跳转到指定的时间位置
*
* @param msec 时间
*/
fun seekTo(msec: Int) {
if (isPlaying || isPausing) {
mediaPlayer!!.seekTo(msec)
MediaSessionManager.get().updatePlaybackState()
for (listener in listeners) {
listener.onPublish(msec)
}
}
}
/**
* 获取播放列表
*/
fun getMusicList(): List<Music>? {
return musicList
}
companion object {
private val STATE_IDLE = 0
private val STATE_PREPARING = 1
private val STATE_PLAYING = 2
private val STATE_PAUSE = 3
private val TIME_UPDATE = 300L
fun get(): AudioPlayer {
return SingletonHolder.instance
}
}
}
package com.ydl.media.music
import com.ydl.media.music.model.Music
/**
* Created by haorui on 2019-10-27 .
* Des:播放进度监听器
*/
interface OnPlayerEventListener {
/**
* 切换歌曲
*/
fun onChange(music: Music)
/**
* 继续播放
*/
fun onPlayerStart()
/**
* 暂停播放
*/
fun onPlayerPause()
/**
* 更新进度
*/
fun onPublish(progress: Int)
/**
* 缓冲百分比
*/
fun onBufferingUpdate(percent: Int)
}
package com.ydl.media.music
import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.Binder
import android.os.IBinder
import android.util.Log
import com.ydl.media.music.constants.Extras
import com.ydl.media.music.manager.MediaSessionManager
import com.ydl.media.music.manager.NotifyManager
/**
* Created by haorui on 2019-10-27 .
* Des: 音乐播放后台服务
*/
class PlayService : Service() {
inner class PlayBinder : Binder() {
val service: PlayService
get() = this@PlayService
}
override fun onCreate() {
super.onCreate()
Log.i(TAG, "onCreate: " + javaClass.simpleName)
AudioPlayer.get().init(this)
MediaSessionManager.get().init(this)
NotifyManager.get().init(this)
}
override fun onBind(intent: Intent): IBinder? {
return PlayBinder()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (intent != null && intent.action != null) {
when (intent.action) {
Extras.ACTION_STOP -> stop()
}
}
return Service.START_NOT_STICKY
}
private fun stop() {
AudioPlayer.get().stopPlayer()
NotifyManager.get().cancelAll()
}
companion object {
private val TAG = "Service"
fun startCommand(context: Context, action: String) {
val intent = Intent(context, PlayService::class.java)
intent.action = action
context.startService(intent)
}
}
}
package com.ydl.media.music.constants
/**
* Created by haorui on 2019-10-27 .
* Des:
*/
interface Extras {
companion object {
val EXTRA_NOTIFICATION = "com.cxzapp.xinlizixun.notification"
val ACTION_STOP = "com.cxzapp.xinlizixun.ACTION_STOP"
}
}
package com.ydl.media.music.enums
/**
* Created by haorui on 2019-10-27 .
* Des: 播放模式
*/
enum class PlayModeEnum private constructor(private val value: Int) {
//列表循环
LIST_LOOP(0),
//随机播放
SHUFFLE(1),
//单曲循环
SINGLE_LOOP(2),
//单曲播放
SINGLE(3);
fun value(): Int {
return value
}
companion object {
fun valueOf(value: Int): PlayModeEnum {
when (value) {
1 -> return SHUFFLE
2 -> return SINGLE_LOOP
0 -> return LIST_LOOP
else -> return LIST_LOOP
}
}
}
}
package com.ydl.media.music.manager
import android.content.Context
import android.content.Context.AUDIO_SERVICE
import android.media.AudioManager
import com.ydl.media.music.AudioPlayer
/**
* Created by haorui on 2019-10-27 .
* Des: 音频焦点管理器
*/
class AudioFocusManager(context: Context) : AudioManager.OnAudioFocusChangeListener {
private val audioManager: AudioManager
private var isPausedByFocusLossTransient: Boolean = false
init {
audioManager = context.getSystemService(AUDIO_SERVICE) as AudioManager
}
fun requestAudioFocus(): Boolean {
return audioManager.requestAudioFocus(
this,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN
) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED
}
fun abandonAudioFocus() {
audioManager.abandonAudioFocus(this)
}
override fun onAudioFocusChange(focusChange: Int) {
when (focusChange) {
// 重新获得焦点
AudioManager.AUDIOFOCUS_GAIN -> {
if (isPausedByFocusLossTransient) {
// 通话结束,恢复播放
AudioPlayer.get().startPlayer()
}
// 恢复音量
AudioPlayer.get().mediaPlayer!!.setVolume(1f, 1f)
isPausedByFocusLossTransient = false
}
// 永久丢失焦点,如被其他播放器抢占
AudioManager.AUDIOFOCUS_LOSS -> AudioPlayer.get().pausePlayer()
// 短暂丢失焦点,如来电
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
AudioPlayer.get().pausePlayer(false)
isPausedByFocusLossTransient = true
}
// 瞬间丢失焦点,如通知
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK ->
// 音量减小为一半
AudioPlayer.get().mediaPlayer!!.setVolume(0.5f, 0.5f)
}
}
}
package com.ydl.media.music.manager
import android.graphics.Bitmap
import android.os.Build
import android.support.v4.media.MediaMetadataCompat
import android.support.v4.media.session.MediaSessionCompat
import android.support.v4.media.session.PlaybackStateCompat
import com.ydl.media.music.AudioPlayer
import com.ydl.media.music.PlayService
import com.ydl.media.music.model.Music
import com.ydl.media.music.utils.CoverLoader
/**
* Created by haorui on 2019-10-27 .
* Des:
*/
class MediaSessionManager private constructor() {
private var playService: PlayService? = null
private var mediaSession: MediaSessionCompat? = null
private val callback = object : MediaSessionCompat.Callback() {
override fun onPlay() {
AudioPlayer.get().playPause()
}
override fun onPause() {
AudioPlayer.get().playPause()
}
override fun onSkipToNext() {
AudioPlayer.get().next()
}
override fun onSkipToPrevious() {
AudioPlayer.get().prev()
}
override fun onStop() {
AudioPlayer.get().stopPlayer()
}
override fun onSeekTo(pos: Long) {
AudioPlayer.get().seekTo(pos.toInt())
}
}
private object SingletonHolder {
val instance = MediaSessionManager()
}
fun init(playService: PlayService) {
this.playService = playService
setupMediaSession()
}
private fun setupMediaSession() {
mediaSession = MediaSessionCompat(playService!!, TAG)
mediaSession!!.setFlags(MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS or MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS)
mediaSession!!.setCallback(callback)
mediaSession!!.isActive = true
}
fun updatePlaybackState() {
val state =
if (AudioPlayer.get().isPlaying || AudioPlayer.get().isPreparing) PlaybackStateCompat.STATE_PLAYING else PlaybackStateCompat.STATE_PAUSED
mediaSession!!.setPlaybackState(
PlaybackStateCompat.Builder()
.setActions(MEDIA_SESSION_ACTIONS)
.setState(state, AudioPlayer.get().audioPosition, 1f)
.build()
)
}
fun updateMetaData(music: Music?) {
if (music == null) {
mediaSession!!.setMetadata(null)
return
}
CoverLoader.get().loadThumb(music,object :CoverLoader.OnCoverLoadListenre{
override fun onCpmplete(bitmap: Bitmap) {
val metaData = MediaMetadataCompat.Builder()
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, music.title)
.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, music.artist)
.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, music.album)
.putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST, music.artist)
.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, music.duration)
.putBitmap(
MediaMetadataCompat.METADATA_KEY_ALBUM_ART,bitmap
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// TODO: 2019-10-26 by:HaoRui
// metaData.putLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS, AppCache.get().getLocalMusicList().size());
}
mediaSession!!.setMetadata(metaData.build())
}
})
}
companion object {
private val TAG = "MediaSessionManager"
private val MEDIA_SESSION_ACTIONS = (PlaybackStateCompat.ACTION_PLAY
or PlaybackStateCompat.ACTION_PAUSE
or PlaybackStateCompat.ACTION_PLAY_PAUSE
or PlaybackStateCompat.ACTION_SKIP_TO_NEXT
or PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
or PlaybackStateCompat.ACTION_STOP
or PlaybackStateCompat.ACTION_SEEK_TO)
fun get(): MediaSessionManager {
return SingletonHolder.instance
}
}
}
package com.ydl.media.music.manager
import android.annotation.SuppressLint
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.os.Build
import android.support.v4.app.NotificationCompat
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.RemoteViews
import android.widget.TextView
import com.bumptech.glide.Glide
import com.bumptech.glide.request.target.NotificationTarget
import com.ydl.media.R
import com.ydl.media.music.PlayService
import com.ydl.media.music.constants.Extras
import com.ydl.media.music.model.Music
import com.ydl.media.music.receiver.StatusBarReceiver
import java.util.*
/**
* Created by haorui on 2019-10-27 .
* Des: 通知管理器
*/
class NotifyManager private constructor() {
private var playService: PlayService? = null
private var notificationManager: NotificationManager? = null
private object SingletonHolder {
val instance = NotifyManager()
}
fun init(playService: PlayService) {
this.playService = playService
notificationManager =
playService.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
}
fun showPlay(music: Music?) {
if (music == null) {
return
}
playService!!.startForeground(NOTIFICATION_ID, buildNotification(playService!!, music, true))
}
fun showPause(music: Music?) {
if (music == null) {
return
}
playService!!.stopForeground(false)
notificationManager!!.notify(NOTIFICATION_ID, buildNotification(playService!!, music, false))
}
fun cancelAll() {
notificationManager!!.cancelAll()
}
@SuppressLint("WrongConstant")
private fun buildNotification(
context: Context,
music: Music,
isPlaying: Boolean
): Notification {
// TODO: 2019-10-26 by:HaoRui
// Intent intent = new Intent(context, MusicActivity.class);
val intent = Intent()
intent.putExtra(Extras.EXTRA_NOTIFICATION, true)
intent.action = Intent.ACTION_VIEW
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
val pendingIntent =
PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
var remoteViews = getRemoteViews(context, music, isPlaying)
val builder: NotificationCompat.Builder
if (Build.VERSION.SDK_INT >= 26) {
val channelId = "app_play_intent_service_channel_id"
val channelName = "壹点灵心理咨询"
val notificationManager =
context.getSystemService("notification") as NotificationManager
val notificationChannel = NotificationChannel(channelId, channelName, 2)
notificationManager.createNotificationChannel(notificationChannel)
builder = NotificationCompat.Builder(context, channelId)
} else {
builder = NotificationCompat.Builder(context)
}
builder
.setContentIntent(pendingIntent)
.setSmallIcon(R.drawable.ic_notification)
.setCustomContentView(remoteViews)
val notification = builder.build()
val target = NotificationTarget(context, R.id.iv_icon, remoteViews, notification, NOTIFICATION_ID)
Glide.with(context)
.asBitmap()
.load(music.coverPath)
.into(target)
return notification
}
private fun getRemoteViews(context: Context, music: Music, isPlaying: Boolean): RemoteViews {
val title = music.title
// String subtitle = FileUtils.getArtistAndAlbum(music.getArtist(), music.getAlbum());
val subtitle = music.artist + "-" + music.album
val remoteViews = RemoteViews(context.packageName, R.layout.notification)
// val cover = CoverLoader.get().loadThumb(music)
// if (cover != null) {
// remoteViews.setImageViewBitmap(R.id.iv_icon, cover)
// } else {
// remoteViews.setImageViewResource(R.id.iv_icon, R.drawable.ic_launcher)
// }
remoteViews.setTextViewText(R.id.tv_title, title)
remoteViews.setTextViewText(R.id.tv_subtitle, subtitle)
val isLightNotificationTheme = isLightNotificationTheme(playService)
val playIntent = Intent(StatusBarReceiver.ACTION_STATUS_BAR)
playIntent.setPackage("com.cxzapp.xinlizixun")
playIntent.putExtra(StatusBarReceiver.EXTRA, StatusBarReceiver.EXTRA_PLAY_PAUSE)
val playPendingIntent =
PendingIntent.getBroadcast(context, 0, playIntent, PendingIntent.FLAG_UPDATE_CURRENT)
remoteViews.setImageViewResource(
R.id.iv_play_pause,
getPlayIconRes(isLightNotificationTheme, isPlaying)
)
remoteViews.setOnClickPendingIntent(R.id.iv_play_pause, playPendingIntent)
val nextIntent = Intent(StatusBarReceiver.ACTION_STATUS_BAR)
nextIntent.setPackage("com.cxzapp.xinlizixun")
nextIntent.putExtra(StatusBarReceiver.EXTRA, StatusBarReceiver.EXTRA_NEXT)
val nextPendingIntent =
PendingIntent.getBroadcast(context, 1, nextIntent, PendingIntent.FLAG_UPDATE_CURRENT)
remoteViews.setImageViewResource(R.id.iv_next, getNextIconRes(isLightNotificationTheme))
remoteViews.setOnClickPendingIntent(R.id.iv_next, nextPendingIntent)
return remoteViews
}
private fun getPlayIconRes(isLightNotificationTheme: Boolean, isPlaying: Boolean): Int {
return if (isPlaying) {
if (isLightNotificationTheme)
R.drawable.ic_status_bar_pause_dark_selector
else
R.drawable.ic_status_bar_pause_light_selector
} else {
if (isLightNotificationTheme)
R.drawable.ic_status_bar_play_dark_selector
else
R.drawable.ic_status_bar_play_light_selector
}
}
private fun getNextIconRes(isLightNotificationTheme: Boolean): Int {
return if (isLightNotificationTheme)
R.drawable.ic_status_bar_next_dark_selector
else
R.drawable.ic_status_bar_next_light_selector
}
private fun isLightNotificationTheme(context: Context?): Boolean {
val notificationTextColor = getNotificationTextColor(context)
return isSimilarColor(Color.BLACK, notificationTextColor)
}
private fun getNotificationTextColor(context: Context?): Int {
val builder = NotificationCompat.Builder(context)
val notification = builder.build()
val remoteViews = notification.contentView ?: return Color.BLACK
val layoutId = remoteViews.layoutId
val notificationLayout = LayoutInflater.from(context).inflate(layoutId, null) as ViewGroup
val title = notificationLayout.findViewById<TextView>(android.R.id.title)
return title?.currentTextColor ?: findTextColor(notificationLayout)
}
/**
* 如果通过 android.R.id.title 无法获得 title ,
* 则通过遍历 notification 布局找到 textSize 最大的 TextView ,应该就是 title 了。
*/
private fun findTextColor(notificationLayout: ViewGroup): Int {
val textViewList = ArrayList<TextView>()
findTextView(notificationLayout, textViewList)
val maxTextSize = -1f
var maxTextView: TextView? = null
for (textView in textViewList) {
if (textView.textSize > maxTextSize) {
maxTextView = textView
}
}
return maxTextView?.currentTextColor ?: Color.BLACK
}
private fun findTextView(view: View, textViewList: MutableList<TextView>) {
if (view is ViewGroup) {
for (i in 0 until view.childCount) {
findTextView(view.getChildAt(i), textViewList)
}
} else if (view is TextView) {
textViewList.add(view)
}
}
private fun isSimilarColor(baseColor: Int, color: Int): Boolean {
val simpleBaseColor = baseColor or -0x1000000
val simpleColor = color or -0x1000000
val baseRed = Color.red(simpleBaseColor) - Color.red(simpleColor)
val baseGreen = Color.green(simpleBaseColor) - Color.green(simpleColor)
val baseBlue = Color.blue(simpleBaseColor) - Color.blue(simpleColor)
val value =
Math.sqrt((baseRed * baseRed + baseGreen * baseGreen + baseBlue * baseBlue).toDouble())
return value < 180.0
}
companion object {
private val NOTIFICATION_ID = 0x111
fun get(): NotifyManager {
return SingletonHolder.instance
}
}
}
package com.ydl.media.music.model
import android.text.TextUtils
import java.io.Serializable
/**
* Created by haorui on 2019-10-27 .
* Des:单曲信息
*/
class Music : Serializable {
var title: String? = null // 标题
var artist: String? = null // 艺术家
var album: String? = null // 专辑
var coverPath: String? = null // 封面地址
var duration: Long = 0 // 持续时间
var path: String? = null // 播放地址
override fun equals(o: Any?): Boolean {
if (o !is Music) {
return false
}
val music = o as Music
return (TextUtils.equals(music.title, this.title)
&& TextUtils.equals(music.artist, this.artist)
&& TextUtils.equals(music.album, this.album)
&& music.duration == this.duration)
}
companion object {
private const val serialVersionUID: Long = 536871008
}
}
package com.ydl.media.music.receiver
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import com.ydl.media.music.AudioPlayer
/**
* Created by haorui on 2019-10-27 .
* Des: 来电/耳机拔出时暂停播放
*/
class NoisyAudioStreamReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
AudioPlayer.get().playPause()
}
}
package com.ydl.media.music.receiver
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.support.v4.media.session.MediaSessionCompat
import android.view.KeyEvent
import com.ydl.media.music.AudioPlayer
/**
* Created by haorui on 2019-10-27 .
* Des: 耳机线控,仅在5.0以下有效,5.0以上被[MediaSessionCompat]接管。
*/
class RemoteControlReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val event = intent.getParcelableExtra<KeyEvent>(Intent.EXTRA_KEY_EVENT)
if (event == null || event.action != KeyEvent.ACTION_UP) {
return
}
when (event.keyCode) {
KeyEvent.KEYCODE_MEDIA_PLAY, KeyEvent.KEYCODE_MEDIA_PAUSE, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, KeyEvent.KEYCODE_HEADSETHOOK -> AudioPlayer.get().playPause()
KeyEvent.KEYCODE_MEDIA_NEXT -> AudioPlayer.get().next()
KeyEvent.KEYCODE_MEDIA_PREVIOUS -> AudioPlayer.get().prev()
}
}
}
package com.ydl.media.music.receiver
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.text.TextUtils
import com.ydl.media.music.AudioPlayer
/**
* Created by haorui on 2019-10-27 .
* Des: 通知栏事件监听
*/
class StatusBarReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent?) {
if (intent == null || TextUtils.isEmpty(intent.action)) {
return
}
val extra = intent.getStringExtra(EXTRA)
if (TextUtils.equals(extra, EXTRA_NEXT)) {
AudioPlayer.get().next()
} else if (TextUtils.equals(extra, EXTRA_PLAY_PAUSE)) {
AudioPlayer.get().playPause()
}
}
companion object {
val ACTION_STATUS_BAR = "com.ydl.media.STATUS_BAR_ACTIONS"
val EXTRA = "extra"
val EXTRA_NEXT = "next"
val EXTRA_PLAY_PAUSE = "play_pause"
}
}
package com.ydl.media.music.utils
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.drawable.Drawable
import android.text.TextUtils
import com.bumptech.glide.Glide
import com.bumptech.glide.load.DecodeFormat
import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.request.target.SimpleTarget
import com.bumptech.glide.request.transition.Transition
import com.ydl.media.music.model.Music
/**
* Created by haorui on 2019-10-27 .
* Des: 专辑封面图片加载器
*/
class CoverLoader private constructor() {
private var context: Context? = null
private var roundLength = 0
private enum class Type {
THUMB,
ROUND,
BLUR
}
private object SingletonHolder {
val instance = CoverLoader()
}
fun init(context: Context) {
this.context = context.applicationContext
this.roundLength = ScreenUtils.getScreenWidth(context) / 2
}
fun setRoundLength(roundLength: Int) {
if (this.roundLength != roundLength) {
this.roundLength = roundLength
}
}
fun loadThumb(music: Music,onCoverLoadListener: OnCoverLoadListenre) {
loadCover(music, Type.THUMB,onCoverLoadListener)
}
fun loadRound(music: Music,onCoverLoadListener: OnCoverLoadListenre){
loadCover(music, Type.ROUND,onCoverLoadListener)
}
fun loadBlur(music: Music,onCoverLoadListener: OnCoverLoadListenre){
loadCover(music, Type.BLUR,onCoverLoadListener)
}
private fun loadCover(music: Music?, type: Type, onCoverLoadListener: OnCoverLoadListenre){
val key = getKey(music)
if (TextUtils.isEmpty(key)) {
onCoverLoadListener.onCpmplete(getDefaultCover(type)!!)
return
}
loadCoverByType(music!!, type,onCoverLoadListener)
}
private fun getKey(music: Music?): String? {
if (music == null) {
return null
}
return if (!TextUtils.isEmpty(music.coverPath)) {
music.coverPath
} else {
null
}
}
private fun getDefaultCover(type: Type): Bitmap? {
when (type) {
CoverLoader.Type.ROUND -> {
var bitmap: Bitmap? = BitmapFactory.decodeResource(
context!!.resources,
com.ydl.media.R.drawable.play_page_default_cover
)
bitmap = ImageUtils.resizeImage(bitmap, roundLength, roundLength)
return bitmap
}
CoverLoader.Type.BLUR -> return BitmapFactory.decodeResource(
context!!.resources,
com.ydl.media.R.drawable.play_page_default_bg
)
else -> return BitmapFactory.decodeResource(
context!!.resources,
com.ydl.media.R.drawable.default_cover
)
}
}
@SuppressLint("CheckResult")
private fun loadCoverByType(music: Music, type: Type, onCoverLoadListener: OnCoverLoadListenre) {
val disallowHardwareConfig =
RequestOptions().format(DecodeFormat.PREFER_RGB_565).disallowHardwareConfig()
Glide.with(context).asBitmap().apply(disallowHardwareConfig).load(music.coverPath).into(object : SimpleTarget<Bitmap>() {
override fun onResourceReady(resource: Bitmap?, transition: Transition<in Bitmap>?) {
when (type) {
CoverLoader.Type.ROUND -> {
var bitmap = ImageUtils.resizeImage(resource, roundLength, roundLength)
onCoverLoadListener.onCpmplete(ImageUtils.createCircleImage(bitmap)!!)
}
CoverLoader.Type.BLUR -> {
onCoverLoadListener.onCpmplete(ImageUtils.blur(resource)!!)
}
}
}
override fun onLoadFailed(errorDrawable: Drawable?) {
loadCover(null, type,onCoverLoadListener)
}
})
}
companion object {
val THUMBNAIL_MAX_LENGTH = 500
private val KEY_NULL = "null"
fun get(): CoverLoader {
return SingletonHolder.instance
}
}
interface OnCoverLoadListenre {
fun onCpmplete(bitmap: Bitmap)
}
}
package com.ydl.media.music.utils
import android.app.Activity
import android.content.Intent
import android.graphics.*
import com.ydl.media.music.constants.RequestCode
/**
*
* Created by wcy on 2015/11/29.
*/
/**
* Created by haorui on 2019-10-27 .
* Des:
*/
object ImageUtils {
private val BLUR_RADIUS = 50
fun blur(source: Bitmap?): Bitmap? {
if (source == null) {
return null
}
try {
return blur(source, BLUR_RADIUS)
} catch (e: Exception) {
e.printStackTrace()
return source
}
}
/**
* Stack Blur v1.0 from
* http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html
*
*
* Java Author: Mario Klingemann <mario at quasimondo.com>
* http://incubator.quasimondo.com
* created Feburary 29, 2004
* Android port : Yahel Bouaziz <yahel at kayenko.com>
* http://www.kayenko.com
* ported april 5th, 2012
</yahel></mario> *
*
* This is a compromise between Gaussian Blur and Box blur
* It creates much better looking blurs than Box Blur, but is
* 7x faster than my Gaussian Blur implementation.
*
*
* I called it Stack Blur because this describes best how this
* filter works internally: it creates a kind of moving stack
* of colors whilst scanning through the image. Thereby it
* just has to add one new block of color to the right side
* of the stack and remove the leftmost color. The remaining
* colors on the topmost layer of the stack are either added on
* or reduced by one, depending on if they are on the right or
* on the left side of the stack.
*
*
* If you are using this algorithm in your code please add
* the following line:
*
*
* Stack Blur Algorithm by Mario Klingemann <mario></mario>@quasimondo.com>
*/
private fun blur(source: Bitmap, radius: Int): Bitmap? {
val bitmap = source.copy(source.config, true)
if (radius < 1) {
return null
}
val w = bitmap.width
val h = bitmap.height
val pix = IntArray(w * h)
bitmap.getPixels(pix, 0, w, 0, 0, w, h)
val wm = w - 1
val hm = h - 1
val wh = w * h
val div = radius + radius + 1
val r = IntArray(wh)
val g = IntArray(wh)
val b = IntArray(wh)
var rSum: Int
var gSum: Int
var bSum: Int
var x: Int
var y: Int
var i: Int
var p: Int
var yp: Int
var yi: Int
var yw: Int
val vMin = IntArray(Math.max(w, h))
var divSum = div + 1 shr 1
divSum *= divSum
val dv = IntArray(256 * divSum)
i = 0
while (i < 256 * divSum) {
dv[i] = i / divSum
i++
}
yi = 0
yw = yi
val stack = Array(div) { IntArray(3) }
var stackPointer: Int
var stackStart: Int
var sir: IntArray
var rbs: Int
val r1 = radius + 1
var rOutSum: Int
var gOutSum: Int
var bOutSum: Int
var rInSum: Int
var gInSum: Int
var bInSum: Int
y = 0
while (y < h) {
bSum = 0
gSum = bSum
rSum = gSum
bOutSum = rSum
gOutSum = bOutSum
rOutSum = gOutSum
bInSum = rOutSum
gInSum = bInSum
rInSum = gInSum
i = -radius
while (i <= radius) {
p = pix[yi + Math.min(wm, Math.max(i, 0))]
sir = stack[i + radius]
sir[0] = p and 0xff0000 shr 16
sir[1] = p and 0x00ff00 shr 8
sir[2] = p and 0x0000ff
rbs = r1 - Math.abs(i)
rSum += sir[0] * rbs
gSum += sir[1] * rbs
bSum += sir[2] * rbs
if (i > 0) {
rInSum += sir[0]
gInSum += sir[1]
bInSum += sir[2]
} else {
rOutSum += sir[0]
gOutSum += sir[1]
bOutSum += sir[2]
}
i++
}
stackPointer = radius
x = 0
while (x < w) {
r[yi] = dv[rSum]
g[yi] = dv[gSum]
b[yi] = dv[bSum]
rSum -= rOutSum
gSum -= gOutSum
bSum -= bOutSum
stackStart = stackPointer - radius + div
sir = stack[stackStart % div]
rOutSum -= sir[0]
gOutSum -= sir[1]
bOutSum -= sir[2]
if (y == 0) {
vMin[x] = Math.min(x + radius + 1, wm)
}
p = pix[yw + vMin[x]]
sir[0] = p and 0xff0000 shr 16
sir[1] = p and 0x00ff00 shr 8
sir[2] = p and 0x0000ff
rInSum += sir[0]
gInSum += sir[1]
bInSum += sir[2]
rSum += rInSum
gSum += gInSum
bSum += bInSum
stackPointer = (stackPointer + 1) % div
sir = stack[stackPointer % div]
rOutSum += sir[0]
gOutSum += sir[1]
bOutSum += sir[2]
rInSum -= sir[0]
gInSum -= sir[1]
bInSum -= sir[2]
yi++
x++
}
yw += w
y++
}
x = 0
while (x < w) {
bSum = 0
gSum = bSum
rSum = gSum
bOutSum = rSum
gOutSum = bOutSum
rOutSum = gOutSum
bInSum = rOutSum
gInSum = bInSum
rInSum = gInSum
yp = -radius * w
i = -radius
while (i <= radius) {
yi = Math.max(0, yp) + x
sir = stack[i + radius]
sir[0] = r[yi]
sir[1] = g[yi]
sir[2] = b[yi]
rbs = r1 - Math.abs(i)
rSum += r[yi] * rbs
gSum += g[yi] * rbs
bSum += b[yi] * rbs
if (i > 0) {
rInSum += sir[0]
gInSum += sir[1]
bInSum += sir[2]
} else {
rOutSum += sir[0]
gOutSum += sir[1]
bOutSum += sir[2]
}
if (i < hm) {
yp += w
}
i++
}
yi = x
stackPointer = radius
y = 0
while (y < h) {
// Preserve alpha channel: ( 0xff000000 & pix[yi] )
pix[yi] =
-0x1000000 and pix[yi] or (dv[rSum] shl 16) or (dv[gSum] shl 8) or dv[bSum]
rSum -= rOutSum
gSum -= gOutSum
bSum -= bOutSum
stackStart = stackPointer - radius + div
sir = stack[stackStart % div]
rOutSum -= sir[0]
gOutSum -= sir[1]
bOutSum -= sir[2]
if (x == 0) {
vMin[y] = Math.min(y + r1, hm) * w
}
p = x + vMin[y]
sir[0] = r[p]
sir[1] = g[p]
sir[2] = b[p]
rInSum += sir[0]
gInSum += sir[1]
bInSum += sir[2]
rSum += rInSum
gSum += gInSum
bSum += bInSum
stackPointer = (stackPointer + 1) % div
sir = stack[stackPointer]
rOutSum += sir[0]
gOutSum += sir[1]
bOutSum += sir[2]
rInSum -= sir[0]
gInSum -= sir[1]
bInSum -= sir[2]
yi += w
y++
}
x++
}
bitmap.setPixels(pix, 0, w, 0, 0, w, h)
return bitmap
}
/**
* 将图片放大或缩小到指定尺寸
*/
fun resizeImage(source: Bitmap?, dstWidth: Int, dstHeight: Int): Bitmap? {
if (source == null) {
return null
}
return if (source.width == dstWidth && source.height == dstHeight) {
source
} else Bitmap.createScaledBitmap(source, dstWidth, dstHeight, true)
}
/**
* 将图片剪裁为圆形
*/
fun createCircleImage(source: Bitmap?): Bitmap? {
if (source == null) {
return null
}
val length = Math.min(source.width, source.height)
val paint = Paint()
paint.isAntiAlias = true
val target = Bitmap.createBitmap(length, length, Bitmap.Config.ARGB_8888)
val canvas = Canvas(target)
canvas.drawCircle(
(source.width / 2).toFloat(),
(source.height / 2).toFloat(),
(length / 2).toFloat(),
paint
)
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
canvas.drawBitmap(source, 0f, 0f, paint)
return target
}
}
package com.ydl.media.music.utils
import android.annotation.SuppressLint
import android.content.Context
import android.view.WindowManager
/**
* Created by haorui on 2019-10-27 .
* Des:
*/
@SuppressLint("StaticFieldLeak")
object ScreenUtils {
fun getScreenWidth(context: Context): Int{
val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
return wm.defaultDisplay.width
}
/**
* 获取状态栏高度
*/
fun getStatusBarHeight(context:Context): Int{
var result = 0
val resourceId =
context!!.resources.getIdentifier("status_bar_height", "dimen", "android")
if (resourceId > 0) {
result = context!!.resources.getDimensionPixelSize(resourceId)
}
return result
}
fun dp2px(context: Context,dpValue: Float): Int {
val scale = context!!.resources.displayMetrics.density
return (dpValue * scale + 0.5f).toInt()
}
fun px2dp(context: Context,pxValue: Float): Int {
val scale = context!!.resources.displayMetrics.density
return (pxValue / scale + 0.5f).toInt()
}
fun sp2px(context: Context,spValue: Float): Int {
val fontScale = context!!.resources.displayMetrics.scaledDensity
return (spValue * fontScale + 0.5f).toInt()
}
}
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