Jelajahi Sumber

添加音频播放器库

杨充 4 tahun lalu
induk
melakukan
0ce11ad1ab
100 mengubah file dengan 4468 tambahan dan 70 penghapusan
  1. 13 7
      Demo/build.gradle
  2. 5 4
      Demo/src/main/AndroidManifest.xml
  3. 2 0
      Demo/src/main/java/com/yc/ycvideoplayer/BaseApplication.java
  4. 99 0
      Demo/src/main/java/com/yc/ycvideoplayer/MainActivity.java
  5. 258 0
      Demo/src/main/java/com/yc/ycvideoplayer/music/MusicPlayerActivity.java
  6. 459 0
      Demo/src/main/java/com/yc/ycvideoplayer/music/PlayMusicFragment.java
  7. 4 6
      Demo/src/main/java/com/yc/ycvideoplayer/oldPlayer/FloatPlayerView.java
  8. 4 5
      Demo/src/main/java/com/yc/ycvideoplayer/oldPlayer/TestWindowActivity.java
  9. 7 0
      Demo/src/main/res/anim/fragment_slide_down.xml
  10. 7 0
      Demo/src/main/res/anim/fragment_slide_up.xml
  11. TEMPAT SAMPAH
      Demo/src/main/res/drawable-xhdpi/ic_volume.png
  12. TEMPAT SAMPAH
      Demo/src/main/res/drawable-xxhdpi/ic_play_bar_btn_next.png
  13. TEMPAT SAMPAH
      Demo/src/main/res/drawable-xxhdpi/ic_play_bar_btn_pause.png
  14. TEMPAT SAMPAH
      Demo/src/main/res/drawable-xxhdpi/ic_play_bar_btn_play.png
  15. TEMPAT SAMPAH
      Demo/src/main/res/drawable-xxhdpi/ic_play_bar_playlist.png
  16. TEMPAT SAMPAH
      Demo/src/main/res/drawable-xxhdpi/ic_play_btn_cmt.png
  17. TEMPAT SAMPAH
      Demo/src/main/res/drawable-xxhdpi/ic_play_btn_cmt_pre.png
  18. TEMPAT SAMPAH
      Demo/src/main/res/drawable-xxhdpi/ic_play_btn_delete.png
  19. TEMPAT SAMPAH
      Demo/src/main/res/drawable-xxhdpi/ic_play_btn_deleted.png
  20. TEMPAT SAMPAH
      Demo/src/main/res/drawable-xxhdpi/ic_play_btn_loop.png
  21. TEMPAT SAMPAH
      Demo/src/main/res/drawable-xxhdpi/ic_play_btn_loop_pressed.png
  22. TEMPAT SAMPAH
      Demo/src/main/res/drawable-xxhdpi/ic_play_btn_love.png
  23. TEMPAT SAMPAH
      Demo/src/main/res/drawable-xxhdpi/ic_play_btn_loved.png
  24. TEMPAT SAMPAH
      Demo/src/main/res/drawable-xxhdpi/ic_play_btn_next.png
  25. TEMPAT SAMPAH
      Demo/src/main/res/drawable-xxhdpi/ic_play_btn_next_pressed.png
  26. TEMPAT SAMPAH
      Demo/src/main/res/drawable-xxhdpi/ic_play_btn_one.png
  27. TEMPAT SAMPAH
      Demo/src/main/res/drawable-xxhdpi/ic_play_btn_one_pressed.png
  28. TEMPAT SAMPAH
      Demo/src/main/res/drawable-xxhdpi/ic_play_btn_pause.png
  29. TEMPAT SAMPAH
      Demo/src/main/res/drawable-xxhdpi/ic_play_btn_pause_pressed.png
  30. TEMPAT SAMPAH
      Demo/src/main/res/drawable-xxhdpi/ic_play_btn_play.png
  31. TEMPAT SAMPAH
      Demo/src/main/res/drawable-xxhdpi/ic_play_btn_play_pressed.png
  32. TEMPAT SAMPAH
      Demo/src/main/res/drawable-xxhdpi/ic_play_btn_prev.png
  33. TEMPAT SAMPAH
      Demo/src/main/res/drawable-xxhdpi/ic_play_btn_prev_pressed.png
  34. TEMPAT SAMPAH
      Demo/src/main/res/drawable-xxhdpi/ic_play_btn_shuffle.png
  35. TEMPAT SAMPAH
      Demo/src/main/res/drawable-xxhdpi/ic_play_btn_shuffle_pressed.png
  36. TEMPAT SAMPAH
      Demo/src/main/res/drawable-xxhdpi/ic_play_btn_src_prs.png
  37. TEMPAT SAMPAH
      Demo/src/main/res/drawable-xxhdpi/ic_seek_bar_progress_btn.png
  38. TEMPAT SAMPAH
      Demo/src/main/res/drawable-xxhdpi/ic_seek_bar_volume_btn.png
  39. TEMPAT SAMPAH
      Demo/src/main/res/drawable-xxhdpi/play_icn_more.png
  40. TEMPAT SAMPAH
      Demo/src/main/res/drawable-xxhdpi/play_page_default_bg.jpg
  41. 5 0
      Demo/src/main/res/drawable/btn_action_bg_selector.xml
  42. 6 0
      Demo/src/main/res/drawable/play_bar_btn_play_pause_selector.xml
  43. 5 0
      Demo/src/main/res/drawable/play_btn_loop_selector.xml
  44. 6 0
      Demo/src/main/res/drawable/play_btn_next_selector.xml
  45. 5 0
      Demo/src/main/res/drawable/play_btn_one_selector.xml
  46. 6 0
      Demo/src/main/res/drawable/play_btn_play_pause_selector.xml
  47. 6 0
      Demo/src/main/res/drawable/play_btn_play_selector.xml
  48. 6 0
      Demo/src/main/res/drawable/play_btn_prev_selector.xml
  49. 5 0
      Demo/src/main/res/drawable/play_btn_shuffle_selector.xml
  50. 12 0
      Demo/src/main/res/drawable/play_mode_level_list.xml
  51. 25 0
      Demo/src/main/res/drawable/seek_bar_progress_style.xml
  52. 17 0
      Demo/src/main/res/drawable/seek_bar_volume_style.xml
  53. 8 0
      Demo/src/main/res/layout/activity_main.xml
  54. 122 0
      Demo/src/main/res/layout/activity_music_player.xml
  55. 122 0
      Demo/src/main/res/layout/fragment_play_music.xml
  56. 85 0
      Demo/src/main/res/layout/include_play_bar.xml
  57. 128 0
      Demo/src/main/res/layout/include_play_page_controller.xml
  58. 119 8
      MusicPlayer/build.gradle
  59. 21 1
      MusicPlayer/src/main/AndroidManifest.xml
  60. 17 0
      MusicPlayer/src/main/java/com/yc/music/config/MusicConstant.java
  61. 27 0
      MusicPlayer/src/main/java/com/yc/music/config/MusicPlayAction.java
  62. 42 0
      MusicPlayer/src/main/java/com/yc/music/config/PlayModeEnum.java
  63. 6 0
      MusicPlayer/src/main/java/com/yc/music/inter/EventCallback.java
  64. 46 0
      MusicPlayer/src/main/java/com/yc/music/inter/OnPlayerEventListener.java
  65. 133 0
      MusicPlayer/src/main/java/com/yc/music/manager/AudioFocusManager.java
  66. 59 0
      MusicPlayer/src/main/java/com/yc/music/manager/AudioSoundManager.java
  67. 107 0
      MusicPlayer/src/main/java/com/yc/music/manager/MediaSessionManager.java
  68. 150 0
      MusicPlayer/src/main/java/com/yc/music/model/AudioBean.java
  69. 44 0
      MusicPlayer/src/main/java/com/yc/music/receiver/AudioBroadcastReceiver.java
  70. 35 0
      MusicPlayer/src/main/java/com/yc/music/receiver/AudioEarPhoneReceiver.java
  71. 41 0
      MusicPlayer/src/main/java/com/yc/music/receiver/EarphoneControlReceiver.java
  72. 45 0
      MusicPlayer/src/main/java/com/yc/music/receiver/NotificationStatusBarReceiver.java
  73. 866 0
      MusicPlayer/src/main/java/com/yc/music/service/PlayService.java
  74. 90 0
      MusicPlayer/src/main/java/com/yc/music/tool/BaseAppHelper.java
  75. 76 0
      MusicPlayer/src/main/java/com/yc/music/tool/QuitTimerHelper.java
  76. 71 0
      MusicPlayer/src/main/java/com/yc/music/utils/MusicLogUtils.java
  77. 416 0
      MusicPlayer/src/main/java/com/yc/music/utils/MusicSpUtils.java
  78. 158 0
      MusicPlayer/src/main/java/com/yc/music/utils/NotificationHelper.java
  79. 372 0
      MusicPlayer/src/main/java/com/yc/music/utils/NotificationUtils.java
  80. TEMPAT SAMPAH
      MusicPlayer/src/main/res/drawable-xhdpi/default_cover.png
  81. TEMPAT SAMPAH
      MusicPlayer/src/main/res/drawable-xhdpi/notify_btn_dark_next_normal.png
  82. TEMPAT SAMPAH
      MusicPlayer/src/main/res/drawable-xhdpi/notify_btn_dark_pause2_normal.png
  83. TEMPAT SAMPAH
      MusicPlayer/src/main/res/drawable-xhdpi/notify_btn_dark_pause_normal.png
  84. TEMPAT SAMPAH
      MusicPlayer/src/main/res/drawable-xhdpi/notify_btn_dark_play_normal.png
  85. TEMPAT SAMPAH
      MusicPlayer/src/main/res/drawable-xhdpi/notify_btn_dark_prev_normal.png
  86. 66 0
      MusicPlayer/src/main/res/layout/notification_player.xml
  87. 4 0
      MusicPlayer/src/main/res/values/colors.xml
  88. 4 0
      MusicPlayer/src/main/res/values/strings.xml
  89. 5 0
      MusicPlayer/src/main/res/values/styles.xml
  90. 5 25
      VideoView/build.gradle
  91. 3 1
      VideoView/src/main/AndroidManifest.xml
  92. 1 1
      VideoView/src/main/java/com/yc/videoview/FloatLifecycle.java
  93. 1 1
      VideoView/src/main/java/com/yc/videoview/FloatPhone.java
  94. 1 1
      VideoView/src/main/java/com/yc/videoview/FloatToast.java
  95. 1 1
      VideoView/src/main/java/com/yc/videoview/FloatView.java
  96. 5 5
      VideoView/src/main/java/com/yc/videoview/FloatWindow.java
  97. 1 1
      VideoView/src/main/java/com/yc/videoview/IFloatWindow.java
  98. 1 1
      VideoView/src/main/java/com/yc/videoview/IFloatWindowImpl.java
  99. 1 1
      VideoView/src/main/java/com/yc/videoview/LifecycleListener.java
  100. 1 1
      VideoView/src/main/java/com/yc/videoview/MoveType.java

+ 13 - 7
Demo/build.gradle

@@ -58,16 +58,22 @@ dependencies {
     implementation 'com.github.ctiao:DanmakuFlameMaster:0.9.25'
     implementation 'com.github.ctiao:ndkbitmap-armv7a:0.9.21'
 
-    //自己封装的库,都有对应的案例项目【欢迎star】:https://github.com/yangchong211
-    implementation 'com.yc:PagerLib:1.0.4'
-    implementation 'cn.yc:YCStateLib:1.2.2'                                  //状态管理
-//    implementation project(':VideoCache')
-//    implementation project(':VideoPlayer')
-//    implementation project(':VideoKernel')
 
+//    implementation project(path: ':VideoCache')
+//    implementation project(path: ':VideoPlayer')
+//    implementation project(path: ':VideoKernel')
+//    implementation project(path: ':VideoView')
+    implementation project(path: ':MusicPlayer')
+
+    //implementation 'cn.yc:MusicPlayer:1.0.0'
     implementation 'cn.yc:VideoPlayer:3.0.9'
     implementation 'cn.yc:VideoCache:3.0.5'
     implementation 'cn.yc:VideoKernel:3.0.5'
+    implementation 'cn.yc:VideoView:3.0.5'
+
+    //自己封装的库,都有对应的案例项目【欢迎star】:https://github.com/yangchong211
     implementation 'cn.yc:YCStatusBarLib:1.5.0'
- //状态栏
+    implementation 'com.yc:PagerLib:1.0.4'
+    implementation 'cn.yc:YCStateLib:1.2.2'                                 
+
 }

+ 5 - 4
Demo/src/main/AndroidManifest.xml

@@ -18,7 +18,7 @@
         android:networkSecurityConfig="@xml/network_security_config"
         android:theme="@style/AppTheme"
         tools:ignore="GoogleAppIndexingWarning">
-        <activity android:name="com.yc.ycvideoplayer.newPlayer.activity.TypeActivity"
+        <activity android:name="com.yc.ycvideoplayer.MainActivity"
             android:configChanges="orientation|keyboardHidden|screenSize"
             android:screenOrientation="portrait">
             <intent-filter>
@@ -62,9 +62,9 @@
         <activity android:name="com.yc.ycvideoplayer.newPlayer.surface.TestSurfaceActivity"
             android:configChanges="orientation|keyboardHidden|screenSize"
             android:screenOrientation="portrait"/>
-<!--        <activity android:name="com.yc.ycvideoplayer.newPlayer.activity.TypeActivity"-->
-<!--            android:configChanges="orientation|keyboardHidden|screenSize"-->
-<!--            android:screenOrientation="portrait"/>-->
+        <activity android:name="com.yc.ycvideoplayer.newPlayer.activity.TypeActivity"
+            android:configChanges="orientation|keyboardHidden|screenSize"
+            android:screenOrientation="portrait"/>
         <activity android:name="com.yc.ycvideoplayer.newPlayer.activity.NormalActivity"
             android:configChanges="orientation|keyboardHidden|screenSize"
             android:screenOrientation="portrait"/>
@@ -117,6 +117,7 @@
         <activity android:name="com.yc.ycvideoplayer.demo.ExoActivity"
             android:configChanges="orientation|keyboardHidden|screenSize"
             android:screenOrientation="portrait"/>
+        <activity android:name="com.yc.ycvideoplayer.music.MusicPlayerActivity"/>
     </application>
 
 </manifest>

+ 2 - 0
Demo/src/main/java/com/yc/ycvideoplayer/BaseApplication.java

@@ -10,6 +10,7 @@ import com.yc.kernel.factory.PlayerFactory;
 import com.yc.kernel.utils.PlayerConstant;
 import com.yc.kernel.utils.PlayerFactoryUtils;
 
+import com.yc.music.utils.MusicSpUtils;
 import com.yc.video.config.VideoPlayerConfig;
 import com.yc.video.player.VideoViewManager;
 import com.yc.video.surface.SurfaceViewFactory;
@@ -70,6 +71,7 @@ public class BaseApplication extends Application {
                 //创建SurfaceView
                 //.setRenderViewFactory(SurfaceViewFactory.create())
                 .build());
+        MusicSpUtils.init(this);
     }
 
     /**

+ 99 - 0
Demo/src/main/java/com/yc/ycvideoplayer/MainActivity.java

@@ -1,8 +1,12 @@
 package com.yc.ycvideoplayer;
 
+import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
+import android.content.ServiceConnection;
 import android.graphics.Color;
 import android.os.Bundle;
+import android.os.IBinder;
 import android.view.View;
 import android.widget.TextView;
 
@@ -12,10 +16,17 @@ import androidx.appcompat.app.AppCompatActivity;
 
 import org.yc.ycvideoplayer.R;
 
+import com.yc.kernel.utils.VideoLogUtils;
+import com.yc.music.model.AudioBean;
+import com.yc.music.service.PlayService;
+import com.yc.music.tool.BaseAppHelper;
 import com.yc.ycvideoplayer.demo.DemoActivity;
+import com.yc.ycvideoplayer.music.MusicPlayerActivity;
 import com.yc.ycvideoplayer.newPlayer.activity.TypeActivity;
 import com.yc.ycvideoplayer.oldPlayer.OldActivity;
 
+import java.util.List;
+
 import cn.ycbjie.ycstatusbarlib.bar.StateAppBar;
 
 /**
@@ -32,6 +43,17 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
     private TextView mTv1;
     private TextView mTv2;
     private TextView mTv3;
+    private TextView mTv4;
+    private PlayServiceConnection mPlayServiceConnection;
+
+
+    @Override
+    protected void onDestroy() {
+        if (mPlayServiceConnection != null) {
+            unbindService(mPlayServiceConnection);
+        }
+        super.onDestroy();
+    }
 
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -45,10 +67,12 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
         mTv1 = (TextView) findViewById(R.id.tv_1);
         mTv2 = (TextView) findViewById(R.id.tv_2);
         mTv3 = (TextView) findViewById(R.id.tv_3);
+        mTv4 = (TextView) findViewById(R.id.tv_4);
 
         mTv1.setOnClickListener(this);
         mTv2.setOnClickListener(this);
         mTv3.setOnClickListener(this);
+        mTv4.setOnClickListener(this);
     }
 
     @Override
@@ -63,10 +87,85 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
             case R.id.tv_3:
                 startActivity(OldActivity.class);
                 break;
+            case R.id.tv_4:
+                startCheckService();
+                startActivity(MusicPlayerActivity.class);
+                break;
         }
     }
 
     private void startActivity(Class c){
         startActivity(new Intent(this,c));
     }
+
+
+
+    /**
+     * 检测服务
+     */
+    private void startCheckService() {
+        if (BaseAppHelper.get().getPlayService() == null) {
+            startService();
+            mTv1.postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    bindService();
+                }
+            },500);
+        }
+    }
+
+    /**
+     * 开启服务
+     */
+    private void startService() {
+        Intent intent = new Intent(this, PlayService.class);
+        startService(intent);
+    }
+
+
+    /**
+     * 绑定服务
+     * 注意对于绑定服务一定要解绑
+     */
+    private void bindService() {
+        Intent intent = new Intent();
+        intent.setClass(this, PlayService.class);
+        mPlayServiceConnection = new PlayServiceConnection();
+        bindService(intent, mPlayServiceConnection, Context.BIND_AUTO_CREATE);
+    }
+
+
+    private class PlayServiceConnection implements ServiceConnection {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            VideoLogUtils.e("onServiceConnected"+name);
+            final PlayService playService = ((PlayService.PlayBinder) service).getService();
+            BaseAppHelper.get().setPlayService(playService);
+            List<AudioBean> musicList = BaseAppHelper.get().getMusicList();
+            AudioBean audioBean1 = new AudioBean();
+            audioBean1.setPath("http://img.zhugexuetang.com/lleXB2SNF5UFp1LfNpPI0hsyQjNs");
+            audioBean1.setId("1");
+            audioBean1.setTitle("音频1");
+            musicList.add(audioBean1);
+            AudioBean audioBean2 = new AudioBean();
+            audioBean2.setPath("http://img.zhugexuetang.com/ljUa-X-oDbLHu7n9AhkuMLu2Yz3k");
+            audioBean2.setId("2");
+            audioBean2.setTitle("音频2");
+            musicList.add(audioBean2);
+            AudioBean audioBean3 = new AudioBean();
+            audioBean3.setPath("http://vfx.mtime.cn/Video/2019/02/04/mp4/190204084208765161.mp4");
+            audioBean3.setId("3");
+            audioBean3.setTitle("音频3");
+            musicList.add(audioBean3);
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            VideoLogUtils.e("onServiceDisconnected"+name);
+        }
+    }
+
+
+
 }

+ 258 - 0
Demo/src/main/java/com/yc/ycvideoplayer/music/MusicPlayerActivity.java

@@ -0,0 +1,258 @@
+package com.yc.ycvideoplayer.music;
+
+import android.os.Bundle;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+import androidx.fragment.app.FragmentTransaction;
+
+import com.yc.music.inter.OnPlayerEventListener;
+import com.yc.music.model.AudioBean;
+import com.yc.music.tool.BaseAppHelper;
+import org.yc.ycvideoplayer.R;
+
+public class MusicPlayerActivity extends AppCompatActivity implements View.OnClickListener {
+
+    private Toolbar mToolbar;
+    private TextView mTv1;
+    private TextView mTv2;
+    private TextView mTv3;
+    private TextView mTvStart;
+    private TextView mTvStop;
+    private TextView mTvNext;
+    private TextView mTvPre;
+    private FrameLayout mFlPlayBar;
+    private ImageView mIvPlayBarCover;
+    private TextView mTvPlayBarTitle;
+    private TextView mTvPlayBarArtist;
+    private ImageView mIvPlayBarList;
+    private ImageView mIvPlayBarPlay;
+    private ImageView mIvPlayBarNext;
+    private ProgressBar mPbPlayBar;
+    private boolean isPlayFragmentShow = false;
+    private PlayMusicFragment mPlayFragment;
+
+    @Override
+    public void onBackPressed() {
+        if (mPlayFragment != null && isPlayFragmentShow) {
+            hidePlayingFragment();
+            return;
+        }
+        super.onBackPressed();
+    }
+
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_music_player);
+        initFindViewById();
+        initListener();
+        mTv1.postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                initPlayServiceListener();
+            }
+        },1000);
+    }
+
+    private void initFindViewById() {
+        mToolbar = findViewById(R.id.toolbar);
+        mTv1 = findViewById(R.id.tv_1);
+        mTv2 = findViewById(R.id.tv_2);
+        mTv3 = findViewById(R.id.tv_3);
+        mTvStart = findViewById(R.id.tv_start);
+        mTvStop = findViewById(R.id.tv_stop);
+        mTvNext = findViewById(R.id.tv_next);
+        mTvPre = findViewById(R.id.tv_pre);
+        mFlPlayBar = findViewById(R.id.fl_play_bar);
+        mIvPlayBarCover = findViewById(R.id.iv_play_bar_cover);
+        mTvPlayBarTitle = findViewById(R.id.tv_play_bar_title);
+        mTvPlayBarArtist = findViewById(R.id.tv_play_bar_artist);
+        mIvPlayBarList = findViewById(R.id.iv_play_bar_list);
+        mIvPlayBarPlay = findViewById(R.id.iv_play_bar_play);
+        mIvPlayBarNext = findViewById(R.id.iv_play_bar_next);
+        mPbPlayBar = findViewById(R.id.pb_play_bar);
+
+    }
+
+    private void initListener() {
+        mTv1.setOnClickListener(this);
+        mTv2.setOnClickListener(this);
+        mTv3.setOnClickListener(this);
+        mTvStart.setOnClickListener(this);
+        mTvStop.setOnClickListener(this);
+        mTvNext.setOnClickListener(this);
+        mTvPre.setOnClickListener(this);
+        mIvPlayBarPlay.setOnClickListener(this);
+        mFlPlayBar.setOnClickListener(this);
+    }
+
+    @Override
+    public void onClick(View v) {
+        switch (v.getId()){
+            case R.id.tv_1:
+                BaseAppHelper.get().getMusicService().play(0);
+                break;
+            case R.id.tv_2:
+
+                break;
+            case R.id.tv_3:
+
+                break;
+            case R.id.tv_start:
+                BaseAppHelper.get().getMusicService().start();
+                break;
+            case R.id.tv_stop:
+                BaseAppHelper.get().getMusicService().stop();
+                break;
+            case R.id.tv_next:
+                BaseAppHelper.get().getMusicService().next();
+                break;
+            case R.id.tv_pre:
+                BaseAppHelper.get().getMusicService().prev();
+                break;
+            case R.id.fl_play_bar:
+                showPlayingFragment();
+                break;
+            case R.id.iv_play_bar_play:
+                if (BaseAppHelper.get().getMusicService().isDefault()) {
+                    if (BaseAppHelper.get().getMusicList().size() > 0) {
+                        int mPlayPosition;
+                        if (BaseAppHelper.get().getMusicService().getPlayingMusic() != null &&
+                                BaseAppHelper.get().getMusicService().getPlayingMusic().getType()
+                                        == AudioBean.Type.LOCAL) {
+                            mPlayPosition = BaseAppHelper.get().getMusicService().getPlayingPosition();
+                        } else {
+                            mPlayPosition = 0;
+                        }
+                        BaseAppHelper.get().getMusicService().play(BaseAppHelper.get()
+                                .getMusicList().get(mPlayPosition));
+                    }
+                } else {
+                    BaseAppHelper.get().getMusicService().playPause();
+                }
+                break;
+        }
+    }
+
+    /**
+     * 初始化服务播放音频播放进度监听器
+     * 这个是要是通过监听即时更新主页面的底部控制器视图
+     * 同时还要同步播放详情页面mPlayFragment的视图
+     */
+    public void initPlayServiceListener() {
+        if (BaseAppHelper.get().getMusicService() == null) {
+            return;
+        }
+        BaseAppHelper.get().getMusicService().setOnPlayEventListener(new OnPlayerEventListener() {
+            /**
+             * 切换歌曲
+             * 主要是切换歌曲的时候需要及时刷新界面信息
+             */
+            @Override
+            public void onChange(AudioBean music) {
+                onChangeImpl(music);
+            }
+
+            /**
+             * 继续播放
+             * 主要是切换歌曲的时候需要及时刷新界面信息,比如播放暂停按钮
+             */
+            @Override
+            public void onPlayerStart() {
+                mIvPlayBarPlay.setSelected(true);
+            }
+
+            /**
+             * 暂停播放
+             * 主要是切换歌曲的时候需要及时刷新界面信息,比如播放暂停按钮
+             */
+            @Override
+            public void onPlayerPause() {
+                mIvPlayBarPlay.setSelected(false);
+            }
+
+            /**
+             * 更新进度
+             * 主要是播放音乐或者拖动进度条时,需要更新进度
+             */
+            @Override
+            public void onUpdateProgress(int progress) {
+                mPbPlayBar.setProgress(progress);
+            }
+
+            @Override
+            public void onBufferingUpdate(int percent) {
+
+            }
+
+            /**
+             * 更新定时停止播放时间
+             */
+            @Override
+            public void onTimer(long remain) {
+
+            }
+        });
+    }
+
+    /**
+     * 当在播放音频详细页面切换歌曲的时候,需要刷新底部控制器,和音频详细页面的数据
+     * 之前关于activity,Fragment,service之间用EventBus通信
+     * 案例:https://github.com/yangchong211/LifeHelper
+     * 本项目中直接通过定义接口来实现功能,尝试中……
+     *
+     * @param music LocalMusic
+     */
+    private void onChangeImpl(AudioBean music) {
+        if (music == null) {
+            return;
+        }
+        mTvPlayBarTitle.setText(music.getTitle());
+        mIvPlayBarPlay.setSelected(BaseAppHelper.get().getMusicService().isPlaying() || BaseAppHelper.get().getMusicService().isPreparing());
+        //更新进度条
+        mPbPlayBar.setMax((int) music.getDuration());
+        mPbPlayBar.setProgress((int) BaseAppHelper.get().getMusicService().getCurrentPosition());
+    }
+
+
+    /**
+     * 展示页面
+     */
+    private void showPlayingFragment() {
+        if (isPlayFragmentShow) {
+            return;
+        }
+        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+        ft.setCustomAnimations(R.anim.fragment_slide_up, 0);
+        if (mPlayFragment == null) {
+            mPlayFragment = PlayMusicFragment.newInstance("OnLine");
+            ft.replace(android.R.id.content, mPlayFragment);
+        } else {
+            ft.show(mPlayFragment);
+        }
+        ft.commitAllowingStateLoss();
+        isPlayFragmentShow = true;
+    }
+
+
+    /**
+     * 隐藏页面
+     */
+    private void hidePlayingFragment() {
+        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+        ft.setCustomAnimations(0, R.anim.fragment_slide_down);
+        ft.hide(mPlayFragment);
+        ft.commitAllowingStateLoss();
+        isPlayFragmentShow = false;
+    }
+
+
+}

+ 459 - 0
Demo/src/main/java/com/yc/ycvideoplayer/music/PlayMusicFragment.java

@@ -0,0 +1,459 @@
+package com.yc.ycvideoplayer.music;
+
+import android.annotation.SuppressLint;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.AudioManager;
+import android.os.Build;
+import android.os.Bundle;
+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 androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+
+import com.yc.music.config.MusicConstant;
+import com.yc.music.config.PlayModeEnum;
+import com.yc.music.inter.OnPlayerEventListener;
+import com.yc.music.model.AudioBean;
+import com.yc.music.tool.BaseAppHelper;
+import com.yc.music.utils.MusicLogUtils;
+import com.yc.music.utils.MusicSpUtils;
+import com.yc.video.tool.PlayerUtils;
+
+import org.yc.ycvideoplayer.R;
+
+import java.io.File;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+
+
+public class PlayMusicFragment extends Fragment implements View.OnClickListener, OnPlayerEventListener {
+
+    private ImageView ivPlayPageBg;
+    private LinearLayout llContent;
+    private ImageView ivBack;
+    private TextView tvTitle;
+    private TextView tvArtist;
+    private ImageView ivShare;
+    private SeekBar sbVolume;
+    private LinearLayout llMusicTool;
+    private ImageView ivPlayingFav;
+    private ImageView ivPlayingDown;
+    private ImageView ivPlayingCmt;
+    private ImageView ivPlayingMore;
+    private TextView tvCurrentTime;
+    private SeekBar sbProgress;
+    private TextView tvTotalTime;
+    private ImageView ivMode;
+    private ImageView ivPrev;
+    private ImageView ivPlay;
+    private ImageView ivNext;
+    private ImageView ivOther;
+    private FragmentActivity activity;
+    private int mLastProgress;
+    /**
+     * 是否拖进度,默认是false
+     */
+    private boolean isDraggingProgress;
+    private AudioManager mAudioManager;
+    private static final String TAG = "DetailAudioFragment";
+    private String type;
+
+    /**
+     * 使用FragmentPagerAdapter+ViewPager时,
+     * 切换回上一个Fragment页面时(已经初始化完毕),
+     * 不会回调任何生命周期方法以及onHiddenChanged(),
+     * 只有setUserVisibleHint(boolean isVisibleToUser)会被回调,
+     * 所以如果你想进行一些懒加载,需要在这里处理。
+     * @param isVisibleToUser               是否显示
+     */
+    @Override
+    public void setUserVisibleHint(boolean isVisibleToUser) {
+        super.setUserVisibleHint(isVisibleToUser);
+        MusicLogUtils.e(TAG+"setUserVisibleHint"+isVisibleToUser);
+    }
+
+    /**
+     * 当使用add()+show(),hide()跳转新的Fragment时,旧的Fragment回调onHiddenChanged(),
+     * 不会回调onStop()等生命周期方法,
+     * 而新的Fragment在创建时是不会回调onHiddenChanged(),这点要切记
+     * @param hidden                        是否显示
+     */
+    @Override
+    public void onHiddenChanged(boolean hidden) {
+        super.onHiddenChanged(hidden);
+        MusicLogUtils.e(TAG+"onHiddenChanged"+hidden);
+        if (!hidden){
+            initData();
+        }
+    }
+
+    @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+        activity = (FragmentActivity) context;
+    }
+
+    @Override
+    public void onDetach() {
+        super.onDetach();
+        activity = null;
+    }
+
+    @Nullable
+    @Override
+    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+        View view = inflater.inflate(getContentView(), container , false);
+        return view;
+    }
+
+    @Override
+    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        initView(view);
+        initListener();
+    }
+
+    @Override
+    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        initData();
+    }
+
+
+    /**
+     * 对Fragment传递数据,建议使用setArguments(Bundle args),而后在onCreate中使用getArguments()取出,
+     * 在 “内存重启”前,系统会帮你保存数据,不会造成数据的丢失。和Activity的Intent恢复机制类似。
+     * @param type                          type
+     * @return                              PlayMusicFragment实例对象
+     */
+    public static PlayMusicFragment newInstance(String type) {
+        Bundle bundle = new Bundle();
+        bundle.putString(TAG, type);
+        PlayMusicFragment fragment = new PlayMusicFragment();
+        fragment.setArguments(bundle);
+        return fragment;
+    }
+
+
+    /**
+     * 返回监听
+     */
+    private void onBackPressed() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+            Objects.requireNonNull(getActivity()).onBackPressed();
+        }
+        ivBack.setEnabled(false);
+        ivBack.postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                ivBack.setEnabled(true);
+            }
+        },300);
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+            Objects.requireNonNull(getContext()).unregisterReceiver(mVolumeReceiver);
+        }
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        IntentFilter filter = new IntentFilter();
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+            Objects.requireNonNull(getContext()).registerReceiver(mVolumeReceiver, filter);
+        }
+    }
+
+
+    public int getContentView() {
+        return R.layout.fragment_play_music;
+    }
+
+
+    public void initView(View view) {
+        if (getArguments() != null) {
+            type = getArguments().getString(TAG);
+        }
+        initFindById(view);
+        initPlayMode();
+        initVolume();
+    }
+
+    private void initFindById(View view) {
+        llContent = view.findViewById(R.id.ll_content);
+        ivBack = view.findViewById(R.id.iv_back);
+        tvTitle = view.findViewById(R.id.tv_title);
+        tvArtist = view.findViewById(R.id.tv_artist);
+        ivShare = view.findViewById(R.id.iv_share);
+        sbVolume = view.findViewById(R.id.sb_volume);
+        llMusicTool = view.findViewById(R.id.ll_music_tool);
+        ivPlayingFav = view.findViewById(R.id.iv_playing_fav);
+        ivPlayingDown = view.findViewById(R.id.iv_playing_down);
+        ivPlayingCmt = view.findViewById(R.id.iv_playing_cmt);
+        ivPlayingMore = view.findViewById(R.id.iv_playing_more);
+        tvCurrentTime = view.findViewById(R.id.tv_current_time);
+        sbProgress = view.findViewById(R.id.sb_progress);
+        tvTotalTime = view.findViewById(R.id.tv_total_time);
+        ivMode = view.findViewById(R.id.iv_mode);
+        ivPrev = view.findViewById(R.id.iv_prev);
+        ivPlay = view.findViewById(R.id.iv_play);
+        ivNext = view.findViewById(R.id.iv_next);
+        ivOther = view.findViewById(R.id.iv_other);
+
+    }
+
+    public void initListener() {
+        ivBack.setOnClickListener(this);
+        ivMode.setOnClickListener(this);
+        ivPlay.setOnClickListener(this);
+        ivPrev.setOnClickListener(this);
+        ivNext.setOnClickListener(this);
+        ivOther.setOnClickListener(this);
+        initSeekBarListener();
+    }
+
+
+    private void initSeekBarListener() {
+        SeekBar.OnSeekBarChangeListener onSeekBarChangeListener = new SeekBar.OnSeekBarChangeListener() {
+            @Override
+            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+                if (seekBar == sbProgress) {
+                    if (Math.abs(progress - mLastProgress) >= DateUtils.SECOND_IN_MILLIS) {
+                        tvCurrentTime.setText(PlayerUtils.formatTime(progress));
+                        mLastProgress = progress;
+                    }
+                }
+            }
+
+            /**
+             * 通知用户已启动触摸手势,开始触摸时调用
+             * @param seekBar               seekBar
+             */
+            @Override
+            public void onStartTrackingTouch(SeekBar seekBar) {
+                if (seekBar == sbProgress) {
+                    isDraggingProgress = true;
+                }
+            }
+
+
+            /**
+             * 通知用户已结束触摸手势,触摸结束时调用
+             * @param seekBar               seekBar
+             */
+            @Override
+            public void onStopTrackingTouch(SeekBar seekBar) {
+                if (seekBar == sbProgress) {
+                    isDraggingProgress = false;
+                    //如果是正在播放,或者暂停,那么直接拖动进度
+                    if (BaseAppHelper.get().getMusicService().isPlaying() || BaseAppHelper.get().getMusicService().isPausing()) {
+                        //获取进度
+                        int progress = seekBar.getProgress();
+                        //直接移动进度
+                        BaseAppHelper.get().getMusicService().seekTo(progress);
+                    } else {
+                        //其他情况,直接设置进度为0
+                        seekBar.setProgress(0);
+                    }
+                } else if (seekBar == sbVolume) {
+                    mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, seekBar.getProgress(),
+                            AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
+                }
+            }
+        };
+        sbProgress.setOnSeekBarChangeListener(onSeekBarChangeListener);
+        sbVolume.setOnSeekBarChangeListener(onSeekBarChangeListener);
+    }
+
+    public void initData() {
+        setViewData(BaseAppHelper.get().getMusicService().getPlayingMusic());
+    }
+
+    @Override
+    public void onClick(View v) {
+        int i = v.getId();
+        if (i == R.id.iv_back) {
+            onBackPressed();
+
+        } else if (i == R.id.iv_mode) {
+            switchPlayMode();
+
+        } else if (i == R.id.iv_play) {
+            play();
+
+        } else if (i == R.id.iv_next) {
+            next();
+
+        } else if (i == R.id.iv_prev) {
+            prev();
+
+        } else if (i == R.id.iv_other) {
+
+        } else {
+        }
+    }
+
+    private void prev() {
+        if (BaseAppHelper.get().getMusicService() != null) {
+            BaseAppHelper.get().getMusicService().prev();
+        }
+    }
+
+    private void next() {
+        if (BaseAppHelper.get().getMusicService() != null) {
+            BaseAppHelper.get().getMusicService().next();
+        }
+    }
+
+    private void play() {
+        if (BaseAppHelper.get().getMusicService() != null) {
+            BaseAppHelper.get().getMusicService().playPause();
+        }
+    }
+
+    private void switchPlayMode() {
+        int playMode = MusicSpUtils.getInstance(MusicConstant.SP_NAME).getInt(MusicConstant.PLAY_MODE, 0);
+        PlayModeEnum mode = PlayModeEnum.valueOf(playMode);
+        switch (mode) {
+            case LOOP:
+                mode = PlayModeEnum.SHUFFLE;
+                break;
+            case SHUFFLE:
+                mode = PlayModeEnum.SINGLE;
+                break;
+            case SINGLE:
+                mode = PlayModeEnum.LOOP;
+                break;
+            default:
+                break;
+        }
+        MusicSpUtils.getInstance(MusicConstant.SP_NAME).put(MusicConstant.PLAY_MODE, mode.value());
+        initPlayMode();
+    }
+
+
+    private void initPlayMode() {
+        int playMode = MusicSpUtils.getInstance(MusicConstant.SP_NAME).getInt(MusicConstant.PLAY_MODE, 0);
+        ivMode.setImageLevel(playMode);
+    }
+
+    /**
+     * 初始化音量
+     */
+    private void initVolume() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+            mAudioManager = (AudioManager) Objects.requireNonNull(getContext())
+                    .getSystemService(Context.AUDIO_SERVICE);
+        }
+        if (mAudioManager != null) {
+            sbVolume.setMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC));
+            sbVolume.setProgress(mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC));
+        }
+    }
+
+    /**
+     * 发送广播接收者
+     */
+    private BroadcastReceiver mVolumeReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            sbVolume.setProgress(mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC));
+        }
+    };
+
+
+    /**
+     * 填充页面数据
+     *
+     * @param playingMusic 正在播放的音乐
+     */
+    @SuppressLint("SetTextI18n")
+    private void setViewData(AudioBean playingMusic) {
+        if (playingMusic == null) {
+            return;
+        }
+        tvTitle.setText(playingMusic.getTitle());
+        tvArtist.setText(playingMusic.getArtist());
+        sbProgress.setProgress((int) BaseAppHelper.get().getMusicService().getCurrentPosition());
+        sbProgress.setSecondaryProgress(0);
+        sbProgress.setMax((int) playingMusic.getDuration());
+        MusicLogUtils.e("-----------------------"+(int) playingMusic.getDuration());
+        mLastProgress = 0;
+        tvCurrentTime.setText("00:00");
+        tvTotalTime.setText(PlayerUtils.formatTime(playingMusic.getDuration()));
+        setCoverAndBg(playingMusic);
+        if (BaseAppHelper.get().getMusicService().isPlaying() || BaseAppHelper.get().getMusicService().isPreparing()) {
+            ivPlay.setSelected(true);
+            //mAlbumCoverView.start();
+        } else {
+            ivPlay.setSelected(false);
+            //mAlbumCoverView.pause();
+        }
+    }
+
+    private void setCoverAndBg(AudioBean music) {
+        //mAlbumCoverView.setCoverBitmap(CoverLoader.getInstance().loadRound(music));
+        //ivPlayPageBg.setImageBitmap(CoverLoader.getInstance().loadBlur(music));
+    }
+
+
+
+    /**
+     * ---------------通过MainActivity进行调用-----------------------------
+     **/
+    @Override
+    public void onChange(AudioBean music) {
+        setViewData(music);
+    }
+
+    @Override
+    public void onPlayerStart() {
+        ivPlay.setSelected(true);
+    }
+
+    @Override
+    public void onPlayerPause() {
+        ivPlay.setSelected(false);
+    }
+
+    @Override
+    public void onUpdateProgress(int progress) {
+        if(progress>0){
+            //如果没有拖动进度,则开始更新进度条进度
+            if (!isDraggingProgress) {
+                sbProgress.setProgress(progress);
+            }
+        }
+    }
+
+    @Override
+    public void onBufferingUpdate(int percent) {
+        if(sbProgress.getMax()>0 && percent>0){
+            MusicLogUtils.e("setOnPlayEventListener---percent---"+ sbProgress.getMax() + "-----" +percent);
+            sbProgress.setSecondaryProgress(sbProgress.getMax() * 100 / percent);
+        }
+    }
+
+    @Override
+    public void onTimer(long remain) {
+
+    }
+
+
+}

+ 4 - 6
VideoPlayer/src/main/java/com/yc/video/ui/window/FloatPlayerView.java → Demo/src/main/java/com/yc/ycvideoplayer/oldPlayer/FloatPlayerView.java

@@ -1,4 +1,4 @@
-package com.yc.video.ui.window;
+package com.yc.ycvideoplayer.oldPlayer;
 
 import android.content.Context;
 import android.graphics.Color;
@@ -10,12 +10,10 @@ import android.widget.FrameLayout;
 import com.yc.kernel.utils.VideoLogUtils;
 import com.yc.video.config.ConstantKeys;
 import com.yc.video.old.controller.VideoPlayerController;
-
-import com.yc.video.R;
-
 import com.yc.video.old.listener.OnPlayerStatesListener;
 import com.yc.video.old.other.VideoPlayerManager;
 import com.yc.video.old.player.OldVideoPlayer;
+import com.yc.videoview.SmallWindowTouch;
 
 
 /**
@@ -51,8 +49,8 @@ public class FloatPlayerView extends FrameLayout {
                 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
         View view ;
         if (inflater != null) {
-            view = inflater.inflate(R.layout.old_view_window_dialog, this);
-            mVideoPlayer = view.findViewById(R.id.video_player);
+            view = inflater.inflate(com.yc.video.R.layout.old_view_window_dialog, this);
+            mVideoPlayer = view.findViewById(com.yc.video.R.id.video_player);
             mVideoPlayer.setUp(path,null);
             mVideoPlayer.setPlayerType(ConstantKeys.VideoPlayerType.TYPE_IJK);
             //创建视频控制器

+ 4 - 5
Demo/src/main/java/com/yc/ycvideoplayer/oldPlayer/TestWindowActivity.java

@@ -10,17 +10,16 @@ import android.widget.Button;
 
 import androidx.annotation.RequiresApi;
 
+import com.yc.videoview.FloatWindow;
+import com.yc.videoview.MoveType;
+import com.yc.videoview.WindowScreen;
+import com.yc.videoview.WindowUtil;
 import com.yc.ycvideoplayer.BaseActivity;
 import com.yc.ycvideoplayer.newPlayer.tiny.TestFullActivity;
 
 import org.yc.ycvideoplayer.R;
 import com.yc.video.old.other.VideoPlayerManager;
-import com.yc.video.ui.window.FloatPlayerView;
 import com.yc.video.old.player.OldVideoPlayer;
-import com.yc.video.ui.window.FloatWindow;
-import com.yc.video.ui.window.MoveType;
-import com.yc.video.ui.window.WindowScreen;
-import com.yc.video.ui.window.WindowUtil;
 
 
 /**

+ 7 - 0
Demo/src/main/res/anim/fragment_slide_down.xml

@@ -0,0 +1,7 @@
+<?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>

+ 7 - 0
Demo/src/main/res/anim/fragment_slide_up.xml

@@ -0,0 +1,7 @@
+<?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>

TEMPAT SAMPAH
Demo/src/main/res/drawable-xhdpi/ic_volume.png


TEMPAT SAMPAH
Demo/src/main/res/drawable-xxhdpi/ic_play_bar_btn_next.png


TEMPAT SAMPAH
Demo/src/main/res/drawable-xxhdpi/ic_play_bar_btn_pause.png


TEMPAT SAMPAH
Demo/src/main/res/drawable-xxhdpi/ic_play_bar_btn_play.png


TEMPAT SAMPAH
Demo/src/main/res/drawable-xxhdpi/ic_play_bar_playlist.png


TEMPAT SAMPAH
Demo/src/main/res/drawable-xxhdpi/ic_play_btn_cmt.png


TEMPAT SAMPAH
Demo/src/main/res/drawable-xxhdpi/ic_play_btn_cmt_pre.png


TEMPAT SAMPAH
Demo/src/main/res/drawable-xxhdpi/ic_play_btn_delete.png


TEMPAT SAMPAH
Demo/src/main/res/drawable-xxhdpi/ic_play_btn_deleted.png


TEMPAT SAMPAH
Demo/src/main/res/drawable-xxhdpi/ic_play_btn_loop.png


TEMPAT SAMPAH
Demo/src/main/res/drawable-xxhdpi/ic_play_btn_loop_pressed.png


TEMPAT SAMPAH
Demo/src/main/res/drawable-xxhdpi/ic_play_btn_love.png


TEMPAT SAMPAH
Demo/src/main/res/drawable-xxhdpi/ic_play_btn_loved.png


TEMPAT SAMPAH
Demo/src/main/res/drawable-xxhdpi/ic_play_btn_next.png


TEMPAT SAMPAH
Demo/src/main/res/drawable-xxhdpi/ic_play_btn_next_pressed.png


TEMPAT SAMPAH
Demo/src/main/res/drawable-xxhdpi/ic_play_btn_one.png


TEMPAT SAMPAH
Demo/src/main/res/drawable-xxhdpi/ic_play_btn_one_pressed.png


TEMPAT SAMPAH
Demo/src/main/res/drawable-xxhdpi/ic_play_btn_pause.png


TEMPAT SAMPAH
Demo/src/main/res/drawable-xxhdpi/ic_play_btn_pause_pressed.png


TEMPAT SAMPAH
Demo/src/main/res/drawable-xxhdpi/ic_play_btn_play.png


TEMPAT SAMPAH
Demo/src/main/res/drawable-xxhdpi/ic_play_btn_play_pressed.png


TEMPAT SAMPAH
Demo/src/main/res/drawable-xxhdpi/ic_play_btn_prev.png


TEMPAT SAMPAH
Demo/src/main/res/drawable-xxhdpi/ic_play_btn_prev_pressed.png


TEMPAT SAMPAH
Demo/src/main/res/drawable-xxhdpi/ic_play_btn_shuffle.png


TEMPAT SAMPAH
Demo/src/main/res/drawable-xxhdpi/ic_play_btn_shuffle_pressed.png


TEMPAT SAMPAH
Demo/src/main/res/drawable-xxhdpi/ic_play_btn_src_prs.png


TEMPAT SAMPAH
Demo/src/main/res/drawable-xxhdpi/ic_seek_bar_progress_btn.png


TEMPAT SAMPAH
Demo/src/main/res/drawable-xxhdpi/ic_seek_bar_volume_btn.png


TEMPAT SAMPAH
Demo/src/main/res/drawable-xxhdpi/play_icn_more.png


TEMPAT SAMPAH
Demo/src/main/res/drawable-xxhdpi/play_page_default_bg.jpg


+ 5 - 0
Demo/src/main/res/drawable/btn_action_bg_selector.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@color/alpha_10_black"
+        android:state_pressed="true" />
+</selector>

+ 6 - 0
Demo/src/main/res/drawable/play_bar_btn_play_pause_selector.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/ic_play_bar_btn_pause"
+        android:state_selected="true" />
+    <item android:drawable="@drawable/ic_play_bar_btn_play" />
+</selector>

+ 5 - 0
Demo/src/main/res/drawable/play_btn_loop_selector.xml

@@ -0,0 +1,5 @@
+<?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>

+ 6 - 0
Demo/src/main/res/drawable/play_btn_next_selector.xml

@@ -0,0 +1,6 @@
+<?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>

+ 5 - 0
Demo/src/main/res/drawable/play_btn_one_selector.xml

@@ -0,0 +1,5 @@
+<?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>

+ 6 - 0
Demo/src/main/res/drawable/play_btn_play_pause_selector.xml

@@ -0,0 +1,6 @@
+<?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"
+        android:state_selected="true" />
+    <item android:drawable="@drawable/ic_play_btn_play" />
+</selector>

+ 6 - 0
Demo/src/main/res/drawable/play_btn_play_selector.xml

@@ -0,0 +1,6 @@
+<?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>

+ 6 - 0
Demo/src/main/res/drawable/play_btn_prev_selector.xml

@@ -0,0 +1,6 @@
+<?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>

+ 5 - 0
Demo/src/main/res/drawable/play_btn_shuffle_selector.xml

@@ -0,0 +1,5 @@
+<?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>

+ 12 - 0
Demo/src/main/res/drawable/play_mode_level_list.xml

@@ -0,0 +1,12 @@
+<?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>

+ 25 - 0
Demo/src/main/res/drawable/seek_bar_progress_style.xml

@@ -0,0 +1,25 @@
+<?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/alpha_20_black" />
+        </shape>
+    </item>
+    <item android:id="@android:id/progress">
+        <clip>
+            <shape>
+                <corners android:radius="1dp" />
+                <solid android:color="@color/redTab" />
+            </shape>
+        </clip>
+    </item>
+    <item android:id="@android:id/secondaryProgress">
+        <clip>
+            <shape>
+                <corners android:radius="1dp" />
+                <solid android:color="@color/redTab" />
+            </shape>
+        </clip>
+    </item>
+</layer-list>

+ 17 - 0
Demo/src/main/res/drawable/seek_bar_volume_style.xml

@@ -0,0 +1,17 @@
+<?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/alpha_35_black" />
+        </shape>
+    </item>
+    <item android:id="@android:id/progress">
+        <clip>
+            <shape>
+                <corners android:radius="1dp" />
+                <solid android:color="@color/colorWhite" />
+            </shape>
+        </clip>
+    </item>
+</layer-list>

+ 8 - 0
Demo/src/main/res/layout/activity_main.xml

@@ -59,6 +59,14 @@
                 android:padding="10dp"
                 android:background="@color/colorAccent"
                 android:text="3.原老播放器案例"/>
+            <TextView
+                android:id="@+id/tv_4"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="10dp"
+                android:padding="10dp"
+                android:background="@color/colorAccent"
+                android:text="4.音频播放器案例"/>
         </LinearLayout>
     </ScrollView>
 

+ 122 - 0
Demo/src/main/res/layout/activity_music_player.xml

@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@color/whiteBg">
+
+    <androidx.appcompat.widget.Toolbar
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:app="http://schemas.android.com/apk/res-auto"
+        android:id="@+id/toolbar"
+        android:layout_width="match_parent"
+        android:layout_height="50dp"
+        android:background="@color/colorTheme"
+        android:theme="@style/AppTheme.ActionBar"
+        app:contentInsetStart="0.0dp"
+        app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
+        android:visibility="visible">
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:text="音频播放器的原生案例"
+            android:textSize="18sp"
+            android:textColor="@color/blackText"/>
+    </androidx.appcompat.widget.Toolbar>
+
+
+    <ScrollView
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1">
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:padding="10dp">
+            <TextView
+                android:id="@+id/tv_1"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="10dp"
+                android:padding="10dp"
+                android:background="@color/colorAccent"
+                android:text="1.最原生的播放器"/>
+            <TextView
+                android:id="@+id/tv_2"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="10dp"
+                android:padding="10dp"
+                android:background="@color/colorAccent"
+                android:text="2.ijk原生播放器"/>
+            <TextView
+                android:id="@+id/tv_3"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="10dp"
+                android:padding="10dp"
+                android:background="@color/colorAccent"
+                android:text="3.exo原生播放器"/>
+
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal">
+
+                <TextView
+                    android:id="@+id/tv_start"
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="10dp"
+                    android:layout_marginRight="10dp"
+                    android:gravity="center"
+                    android:layout_weight="1"
+                    android:background="@color/colorAccent"
+                    android:padding="10dp"
+                    android:text="播放" />
+
+                <TextView
+                    android:id="@+id/tv_stop"
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="10dp"
+                    android:layout_marginRight="10dp"
+                    android:layout_weight="1"
+                    android:gravity="center"
+                    android:background="@color/colorAccent"
+                    android:padding="10dp"
+                    android:text="暂停" />
+
+                <TextView
+                    android:id="@+id/tv_next"
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="10dp"
+                    android:layout_marginRight="10dp"
+                    android:layout_weight="1"
+                    android:gravity="center"
+                    android:background="@color/colorAccent"
+                    android:padding="10dp"
+                    android:text="下一首" />
+
+                <TextView
+                    android:id="@+id/tv_pre"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="10dp"
+                    android:background="@color/colorAccent"
+                    android:gravity="center"
+                    android:padding="10dp"
+                    android:text="上一首" />
+            </LinearLayout>
+
+        </LinearLayout>
+    </ScrollView>
+
+    <include layout="@layout/include_play_bar"/>
+
+</LinearLayout>

+ 122 - 0
Demo/src/main/res/layout/fragment_play_music.xml

@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    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"
+        android:focusable="true" />
+
+    <LinearLayout
+        android:id="@+id/ll_content"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@color/alpha_15_black"
+        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:background="@drawable/btn_action_bg_selector"
+                android:scaleType="centerInside"
+                android:src="@drawable/ic_back_close" />
+
+            <LinearLayout
+                android:layout_width="0dp"
+                android:layout_weight="1"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="5dp"
+                android:gravity="center_vertical"
+                android:orientation="horizontal">
+                <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/colorWhite"
+                    android:textSize="18sp" />
+                <TextView
+                    android:id="@+id/tv_artist"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginStart="5dp"
+                    android:ellipsize="end"
+                    android:singleLine="true"
+                    android:text="演唱者"
+                    android:textColor="@color/colorWhite"
+                    android:textSize="12sp" />
+            </LinearLayout>
+            <ImageView
+                android:id="@+id/iv_share"
+                android:layout_width="?attr/actionBarSize"
+                android:layout_height="?attr/actionBarSize"
+                android:background="@drawable/btn_action_bg_selector"
+                android:scaleType="centerInside"
+                android:src="@drawable/ic_palyer_share" />
+        </LinearLayout>
+
+        <View
+            android:layout_width="match_parent"
+            android:layout_height="1px"
+            android:background="@color/alpha_15_white"/>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center_vertical"
+            android:orientation="horizontal"
+            android:paddingLeft="15dp"
+            android:paddingRight="15dp"
+            android:paddingTop="10dp"
+            android:paddingBottom="10dp">
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textColor="@color/colorWhite"
+                android:gravity="center"
+                android:textSize="12sp"
+                android:text="音量调节"
+                android:drawablePadding="5dp"
+                android:drawableEnd="@drawable/ic_volume"/>
+
+            <SeekBar
+                android:id="@+id/sb_volume"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:progress="0"
+                android:maxHeight="2dp"
+                android:minHeight="2dp"
+                android:paddingBottom="4dp"
+                android:paddingTop="4dp"
+                android:progressDrawable="@drawable/seek_bar_volume_style"
+                android:thumb="@drawable/ic_seek_bar_volume_btn" />
+        </LinearLayout>
+
+        <!--中间暂时放着,后面可以参考酷狗-->
+        <FrameLayout
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:layout_weight="1">
+        </FrameLayout>
+
+        <include layout="@layout/include_play_page_controller" />
+    </LinearLayout>
+
+</FrameLayout>

+ 85 - 0
Demo/src/main/res/layout/include_play_bar.xml

@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/fl_play_bar"
+    android:layout_width="match_parent"
+    android:layout_height="55dp"
+    android:visibility="visible"
+    android:background="@color/alpha_35_black">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_gravity="center_vertical"
+        android:gravity="center_vertical"
+        android:orientation="horizontal"
+        android:paddingLeft="10dp"
+        android:paddingRight="10dp">
+
+        <ImageView
+            android:id="@+id/iv_play_bar_cover"
+            android:layout_width="45dp"
+            android:layout_height="45dp"
+            android:contentDescription="@null"
+            android:scaleType="fitXY"
+            android:src="@drawable/default_cover" />
+
+        <LinearLayout
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="10dp"
+            android:layout_marginRight="10dp"
+            android:layout_weight="1"
+            android:orientation="vertical">
+
+            <TextView
+                android:id="@+id/tv_play_bar_title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:ellipsize="end"
+                android:singleLine="true"
+                android:text="无音乐"
+                android:textColor="@color/colorWhite"
+                android:textSize="16sp" />
+
+            <TextView
+                android:id="@+id/tv_play_bar_artist"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:ellipsize="end"
+                android:singleLine="true"
+                android:layout_marginTop="3dp"
+                android:text="时间"
+                android:textColor="@color/colorWhite"
+                android:textSize="12sp" />
+        </LinearLayout>
+
+
+        <ImageView
+            android:id="@+id/iv_play_bar_list"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:contentDescription="@null"
+            android:src="@drawable/ic_play_bar_playlist" />
+        <ImageView
+            android:id="@+id/iv_play_bar_play"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:contentDescription="@null"
+            android:src="@drawable/play_bar_btn_play_pause_selector" />
+        <ImageView
+            android:id="@+id/iv_play_bar_next"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:contentDescription="@null"
+            android:src="@drawable/ic_play_bar_btn_next" />
+    </LinearLayout>
+
+    <ProgressBar
+        android:id="@+id/pb_play_bar"
+        style="?android:attr/progressBarStyleHorizontal"
+        android:layout_width="match_parent"
+        android:layout_height="2dp"
+        android:layout_gravity="top"
+        android:progressDrawable="@drawable/progress_bar_style" />
+</FrameLayout>

+ 128 - 0
Demo/src/main/res/layout/include_play_page_controller.xml

@@ -0,0 +1,128 @@
+<?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:id="@+id/ll_music_tool"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="40dp"
+        android:layout_marginEnd="40dp"
+        android:orientation="horizontal">
+        <ImageView
+            android:id="@+id/iv_playing_fav"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:layout_weight="1"
+            android:src="@drawable/ic_play_btn_love" />
+        <ImageView
+            android:id="@+id/iv_playing_down"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:layout_weight="1"
+            android:src="@drawable/ic_play_btn_delete" />
+        <ImageView
+            android:id="@+id/iv_playing_cmt"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:layout_weight="1"
+            android:src="@drawable/ic_play_btn_cmt_pre" />
+        <ImageView
+            android:id="@+id/iv_playing_more"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:layout_weight="1"
+            android:src="@drawable/play_icn_more" />
+    </LinearLayout>
+
+
+
+    <!--时间进度条-->
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center_vertical"
+        android:orientation="horizontal"
+        android:paddingLeft="15dp"
+        android:paddingRight="15dp">
+        <TextView
+            android:id="@+id/tv_current_time"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="00:00"
+            android:textColor="@color/colorWhite"
+            android:textSize="12sp" />
+        <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="00:00"
+            android:textColor="@color/whiteBg"
+            android:textSize="12sp" />
+    </LinearLayout>
+
+
+
+    <!--播放器的按钮-->
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center">
+        <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" />
+        <ImageView
+            android:id="@+id/iv_other"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerVertical="true"
+            android:layout_alignParentEnd="true"
+            android:src="@drawable/ic_play_btn_src_prs" />
+    </RelativeLayout>
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="end|center_vertical"
+        android:padding="10dp"
+        android:textColor="@color/colorWhite"
+        android:textSize="10sp"
+        android:text="制作人:潇湘剑雨"/>
+</LinearLayout>

+ 119 - 8
MusicPlayer/build.gradle

@@ -8,10 +8,7 @@ android {
         minSdkVersion 17
         targetSdkVersion 29
         versionCode 1
-        versionName "1.0"
-
-        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
-        consumerProguardFiles "consumer-rules.pro"
+        versionName "1.0.0"
     }
 
     buildTypes {
@@ -25,8 +22,122 @@ android {
 dependencies {
     implementation fileTree(dir: "libs", include: ["*.jar"])
     implementation 'androidx.appcompat:appcompat:1.2.0'
-    testImplementation 'junit:junit:4.12'
-    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
-    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
+    implementation 'androidx.media:media:1.0.1'
+}
+
+/** 以下开始是将Android Library上传到jcenter的相关配置**/
+apply plugin: 'com.github.dcendents.android-maven'
+apply plugin: 'com.jfrog.bintray'
+
+//项目主页
+def siteUrl = 'https://github.com/yangchong211/YCVideoPlayer'    // project homepage
+//项目的版本控制地址
+def gitUrl = 'https://github.com/yangchong211/YCVideoPlayer.git' // project git
+
+//发布到组织名称名字,必须填写
+group = "cn.yc"
+//发布到JCenter上的项目名字,必须填写
+def libName = "MusicPlayer"
+// 版本号,下次更新是只需要更改版本号即可
+version = "1.0.0"
+
+//生成源文件
+task sourcesJar(type: Jar) {
+    from android.sourceSets.main.java.srcDirs
+    classifier = 'sources'
+}
+//生成文档
+task javadoc(type: Javadoc) {
+    source = android.sourceSets.main.java.srcDirs
+    classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
+    options.encoding "UTF-8"
+    options.charSet 'UTF-8'
+    options.author true
+    options.version true
+    options.links "https://github.com/linglongxin24/FastDev/tree/master/mylibrary/docs/javadoc"
+    failOnError false
+}
+
+//文档打包成jar
+task javadocJar(type: Jar, dependsOn: javadoc) {
+    classifier = 'javadoc'
+    from javadoc.destinationDir
+}
+//拷贝javadoc文件
+task copyDoc(type: Copy) {
+    from "${buildDir}/docs/"
+    into "docs"
+}
+
+//上传到jcenter所需要的源码文件
+artifacts {
+    archives javadocJar
+    archives sourcesJar
+}
+
+// 配置maven库,生成POM.xml文件
+install {
+    repositories.mavenInstaller {
+        // This generates POM.xml with proper parameters
+        pom {
+            project {
+                packaging 'aar'
+                //项目描述,自由填写
+                name 'This is music player lib'
+                url siteUrl
+                licenses {
+                    license {
+                        //开源协议
+                        name 'The Apache Software License, Version 2.0'
+                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
+                    }
+                }
+                developers {
+                    developer {
+                        //开发者的个人信息,根据个人信息填写
+                        id 'yangchong'
+                        name 'yc'
+                        email 'yangchong211@163.com'
+                    }
+                }
+                scm {
+                    connection gitUrl
+                    developerConnection gitUrl
+                    url siteUrl
+                }
+            }
+        }
+    }
+}
+
+//上传到jcenter
+Properties properties = new Properties()
+properties.load(project.rootProject.file('local.properties').newDataInputStream())
+bintray {
+    user = properties.getProperty("bintray.user")    //读取 local.properties 文件里面的 bintray.user
+    key = properties.getProperty("bintray.apikey")  //读取 local.properties 文件里面的 bintray.apikey
+    configurations = ['archives']
+    pkg {
+        repo = "maven"
+        name = libName    //发布到JCenter上的项目名字,必须填写
+        desc = 'android music player'    //项目描述
+        websiteUrl = siteUrl
+        vcsUrl = gitUrl
+        licenses = ["Apache-2.0"]
+        publish = true
+    }
+}
+
+javadoc {
+    options {
+        //如果你的项目里面有中文注释的话,必须将格式设置为UTF-8,不然会出现乱码
+        encoding "UTF-8"
+        charSet 'UTF-8'
+        author true
+        version true
+        links "http://docs.oracle.com/javase/7/docs/api"
+    }
+}
+
+
 
-}

+ 21 - 1
MusicPlayer/src/main/AndroidManifest.xml

@@ -1,5 +1,25 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.yc.music">
 
-    /
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+
+    <application>
+        <service android:name=".service.PlayService" />
+        <receiver android:name=".receiver.NotificationStatusBarReceiver">
+            <intent-filter>
+                <action android:name="YC_ACTION_STATUS_BAR" />
+            </intent-filter>
+        </receiver>
+        <receiver android:name=".receiver.EarphoneControlReceiver">
+            <intent-filter>
+                <action android:name="android.intent.action.MEDIA_BUTTON" />
+            </intent-filter>
+        </receiver>
+        <receiver android:name=".receiver.AudioBroadcastReceiver">
+            <intent-filter>
+                <action android:name="cn.ycbjie.lock" />
+            </intent-filter>
+        </receiver>
+    </application>
+
 </manifest>

+ 17 - 0
MusicPlayer/src/main/java/com/yc/music/config/MusicConstant.java

@@ -0,0 +1,17 @@
+package com.yc.music.config;
+
+public class MusicConstant {
+
+    public static final String SP_NAME = "yc";
+    public static final String EXTRA_NOTIFICATION = "extra_notification";
+    public static final String LOCK_SCREEN = "lock_screen";
+    public static final String LOCK_SCREEN_ACTION = "cn.ycbjie.lock";
+    public static final String FILTER_SIZE = "filter_size";
+    public static final String FILTER_TIME = "filter_time";
+    public static final String MUSIC_ID = "music_id";
+    public static final String PLAY_MODE = "play_mode";
+    public static final String IS_SCREEN_LOCK = "is_screen_lock";
+    public static final String APP_OPEN_COUNT = "app_open_count";
+    public static final String PLAY_POSITION = "play_position";
+
+}

+ 27 - 0
MusicPlayer/src/main/java/com/yc/music/config/MusicPlayAction.java

@@ -0,0 +1,27 @@
+package com.yc.music.config;
+
+
+public class MusicPlayAction {
+
+    /**--------------播放类型--------------------------------*/
+
+    /** 点击了上一首按钮*/
+    public static final String TYPE_PRE = "TYPE_PRE";
+    /** 点击了下一首按钮*/
+    public static final String TYPE_NEXT = "TYPE_NEXT";
+    /** 点击了播放暂停按钮*/
+    public static final String TYPE_START_PAUSE = "TYPE_START_PAUSE";
+
+
+    /**--------------播放状态--------------------------------*/
+
+    /** 默认状态*/
+    public static final int STATE_IDLE = 100;
+    /** 正在准备中*/
+    public static final int STATE_PREPARING = 101;
+    /** 正在播放中*/
+    public static final int STATE_PLAYING = 102;
+    /** 暂停状态*/
+    public static final int STATE_PAUSE = 103;
+
+}

+ 42 - 0
MusicPlayer/src/main/java/com/yc/music/config/PlayModeEnum.java

@@ -0,0 +1,42 @@
+package com.yc.music.config;
+
+/**
+ * 播放模式
+ */
+public enum PlayModeEnum {
+
+    /**
+     * 顺序播放,默认的播放模式
+     */
+    LOOP(0),
+    /**
+     * 随机播放
+     */
+    SHUFFLE(1),
+    /**
+     * 单曲循环
+     */
+    SINGLE(2);
+
+    private int value;
+
+    PlayModeEnum(int value) {
+        this.value = value;
+    }
+
+    public static PlayModeEnum valueOf(int value) {
+        switch (value) {
+            case 1:
+                return SHUFFLE;
+            case 2:
+                return SINGLE;
+            case 0:
+            default:
+                return LOOP;
+        }
+    }
+
+    public int value() {
+        return value;
+    }
+}

+ 6 - 0
MusicPlayer/src/main/java/com/yc/music/inter/EventCallback.java

@@ -0,0 +1,6 @@
+package com.yc.music.inter;
+
+
+public interface EventCallback<T> {
+    void onEvent(T t);
+}

+ 46 - 0
MusicPlayer/src/main/java/com/yc/music/inter/OnPlayerEventListener.java

@@ -0,0 +1,46 @@
+package com.yc.music.inter;
+
+
+import com.yc.music.model.AudioBean;
+
+/**
+ * 播放进度监听器
+ */
+public interface OnPlayerEventListener {
+
+    /**
+     * 切换歌曲
+     * 主要是切换歌曲的时候需要及时刷新界面信息
+     */
+    void onChange(AudioBean music);
+
+
+    /**
+     * 继续播放
+     * 主要是切换歌曲的时候需要及时刷新界面信息,比如播放暂停按钮
+     */
+    void onPlayerStart();
+
+    /**
+     * 暂停播放
+     * 主要是切换歌曲的时候需要及时刷新界面信息,比如播放暂停按钮
+     */
+    void onPlayerPause();
+
+    /**
+     * 更新进度
+     * 主要是播放音乐或者拖动进度条时,需要更新进度
+     */
+    void onUpdateProgress(int progress);
+
+    /**
+     * 缓冲百分比
+     */
+    void onBufferingUpdate(int percent);
+
+    /**
+     * 更新定时停止播放时间
+     */
+    void onTimer(long remain);
+
+}

+ 133 - 0
MusicPlayer/src/main/java/com/yc/music/manager/AudioFocusManager.java

@@ -0,0 +1,133 @@
+package com.yc.music.manager;
+
+import android.media.AudioManager;
+
+import androidx.annotation.NonNull;
+
+import com.yc.music.service.PlayService;
+
+import static android.content.Context.AUDIO_SERVICE;
+
+
+public class AudioFocusManager implements AudioManager.OnAudioFocusChangeListener {
+
+
+    private PlayService mPlayService;
+    private AudioManager mAudioManager;
+    /**
+     * 是否因聚焦丢失瞬变而暂停
+     */
+    private boolean isPausedByFocusLossTransient;
+    private int mVolumeWhenFocusLossTransientCanDuck;
+
+
+    /**
+     * 初始化操作
+     * @param content           playService对象
+     */
+    public AudioFocusManager(@NonNull PlayService content) {
+        mPlayService = content;
+        mAudioManager = (AudioManager) content.getSystemService(AUDIO_SERVICE);
+    }
+
+
+    /**
+     * 请求音频焦点,开始播放时候调用
+     * @return                  是否抢占焦点
+     */
+    public boolean requestAudioFocus() {
+        return mAudioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC,
+                AudioManager.AUDIOFOCUS_GAIN) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
+    }
+
+
+    /**
+     * 放弃音频焦点,销毁播放时候调用
+     */
+    public void abandonAudioFocus() {
+        mAudioManager.abandonAudioFocus(this);
+    }
+
+
+    /**
+     * 当音频焦点发生变化的时候调用这个方法,在这里可以处理逻辑
+     * 欢迎访问我的GitHub:https://github.com/yangchong211
+     * 如果可以的话,请star吧
+     * @param focusChange       焦点改变
+     */
+    @Override
+    public void onAudioFocusChange(int focusChange) {
+        int volume;
+        switch (focusChange) {
+            // 重新获得焦点
+            case AudioManager.AUDIOFOCUS_GAIN:
+                if (!willPlay() && isPausedByFocusLossTransient) {
+                    // 通话结束,恢复播放
+                    mPlayService.playPause();
+                }
+                //获取音量
+                volume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
+                if (mVolumeWhenFocusLossTransientCanDuck > 0 && volume ==
+                        mVolumeWhenFocusLossTransientCanDuck / 2) {
+                    // 恢复音量
+                    mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC,
+                            mVolumeWhenFocusLossTransientCanDuck, AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
+                }
+
+                isPausedByFocusLossTransient = false;
+                mVolumeWhenFocusLossTransientCanDuck = 0;
+                break;
+            // 永久丢失焦点,如被其他播放器抢占
+            case AudioManager.AUDIOFOCUS_LOSS:
+                // 失去audio focus很长一段时间,必须停止所有的audio播放,清理资源
+                if (willPlay()) {
+                    forceStop();
+                }
+                break;
+            // 短暂丢失焦点,比如来了电话或者微信视频音频聊天等等
+            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
+                // 暂时失去audio focus,但是很快就会重新获得,在此状态应该暂停所有音频播放,但是不能清除资源
+                if (willPlay()) {
+                    forceStop();
+                    isPausedByFocusLossTransient = true;
+                }
+                break;
+            // 瞬间丢失焦点,如通知
+            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
+                // 暂时失去 audio focus,但是允许持续播放音频(以很小的声音),不需要完全停止播放。
+                // 音量减小为一半
+                volume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
+                if (willPlay() && volume > 0) {
+                    mVolumeWhenFocusLossTransientCanDuck = volume;
+                    mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC,
+                            mVolumeWhenFocusLossTransientCanDuck / 2,
+                            AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
+                }
+                break;
+            default:
+                break;
+        }
+    }
+
+
+    /**
+     * 判断是否在播放或者准备播放
+     */
+    private boolean willPlay() {
+        //当正在准备播放或者播放,则返回为true
+        return mPlayService.isPreparing() || mPlayService.isPlaying();
+    }
+
+
+    private void forceStop() {
+        //当准备播放时,则停止播放
+        if (mPlayService.isPreparing()) {
+            mPlayService.stop();
+            //当正在播放时,则暂停播放
+        } else if (mPlayService.isPlaying()) {
+            mPlayService.pause();
+        }
+    }
+
+
+}

+ 59 - 0
MusicPlayer/src/main/java/com/yc/music/manager/AudioSoundManager.java

@@ -0,0 +1,59 @@
+package com.yc.music.manager;
+
+import android.annotation.SuppressLint;
+import android.media.AudioManager;
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+
+import com.yc.music.service.PlayService;
+
+import static android.content.Context.AUDIO_SERVICE;
+
+public class AudioSoundManager  {
+
+
+    private AudioManager mAudioManager;
+
+
+    /**
+     * 初始化操作
+     * @param content           playService对象
+     */
+    public AudioSoundManager(@NonNull PlayService content) {
+        mAudioManager = (AudioManager) content.getSystemService(AUDIO_SERVICE);
+    }
+
+
+    /**
+     * 切换到外放
+     */
+    public void changeToSpeaker(){
+        mAudioManager.setMode(AudioManager.MODE_NORMAL);
+        mAudioManager.setSpeakerphoneOn(true);
+    }
+
+
+    /**
+     * 切换到耳机模式
+     */
+    public void changeToHeadset(){
+        mAudioManager.setSpeakerphoneOn(false);
+    }
+
+
+    /**
+     * 切换到听筒
+     */
+    @SuppressLint("ObsoleteSdkInt")
+    public void changeToReceiver(){
+        mAudioManager.setSpeakerphoneOn(false);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
+            mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
+        } else {
+            mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+        }
+    }
+
+
+}

+ 107 - 0
MusicPlayer/src/main/java/com/yc/music/manager/MediaSessionManager.java

@@ -0,0 +1,107 @@
+package com.yc.music.manager;
+
+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.yc.music.model.AudioBean;
+import com.yc.music.service.PlayService;
+import com.yc.music.tool.BaseAppHelper;
+
+
+public class MediaSessionManager {
+
+    private static final String TAG = "MediaSessionManager";
+    private static final long MEDIA_SESSION_ACTIONS = PlaybackStateCompat.ACTION_PLAY
+            | PlaybackStateCompat.ACTION_PAUSE
+            | PlaybackStateCompat.ACTION_PLAY_PAUSE
+            | PlaybackStateCompat.ACTION_SKIP_TO_NEXT
+            | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
+            | PlaybackStateCompat.ACTION_STOP
+            | PlaybackStateCompat.ACTION_SEEK_TO;
+
+    private PlayService mPlayService;
+    private MediaSessionCompat mMediaSession;
+
+    public MediaSessionManager(PlayService playService) {
+        mPlayService = playService;
+        setupMediaSession();
+    }
+
+    private void setupMediaSession() {
+        mMediaSession = new MediaSessionCompat(mPlayService, TAG);
+        mMediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS | MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS);
+        mMediaSession.setCallback(callback);
+        mMediaSession.setActive(true);
+    }
+
+    public void updatePlaybackState() {
+        int state = (mPlayService.isPlaying() ||
+                mPlayService.isPreparing()) ? PlaybackStateCompat.STATE_PLAYING :
+                PlaybackStateCompat.STATE_PAUSED;
+        mMediaSession.setPlaybackState(
+                new PlaybackStateCompat.Builder()
+                        .setActions(MEDIA_SESSION_ACTIONS)
+                        .setState(state, mPlayService.getCurrentPosition(), 1)
+                        .build());
+    }
+
+    public void updateMetaData(AudioBean music) {
+        if (music == null) {
+            mMediaSession.setMetadata(null);
+            return;
+        }
+
+        MediaMetadataCompat.Builder metaData = new MediaMetadataCompat.Builder()
+                .putString(MediaMetadataCompat.METADATA_KEY_TITLE, music.getTitle())
+                .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, music.getArtist())
+                .putString(MediaMetadataCompat.METADATA_KEY_ALBUM, music.getAlbum())
+                .putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST, music.getArtist())
+                .putLong(MediaMetadataCompat.METADATA_KEY_DURATION, music.getDuration());
+                //.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, CoverLoader.getInstance().loadThumbnail(music));
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            metaData.putLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS,
+                    BaseAppHelper.get().getMusicList().size());
+        }
+        mMediaSession.setMetadata(metaData.build());
+    }
+
+    public void release() {
+        mMediaSession.setCallback(null);
+        mMediaSession.setActive(false);
+        mMediaSession.release();
+    }
+
+    private MediaSessionCompat.Callback callback = new MediaSessionCompat.Callback() {
+        @Override
+        public void onPlay() {
+            mPlayService.playPause();
+        }
+
+        @Override
+        public void onPause() {
+            mPlayService.playPause();
+        }
+
+        @Override
+        public void onSkipToNext() {
+            mPlayService.next();
+        }
+
+        @Override
+        public void onSkipToPrevious() {
+            mPlayService.prev();
+        }
+
+        @Override
+        public void onStop() {
+            mPlayService.stop();
+        }
+
+        @Override
+        public void onSeekTo(long pos) {
+            mPlayService.seekTo((int) pos);
+        }
+    };
+}

+ 150 - 0
MusicPlayer/src/main/java/com/yc/music/model/AudioBean.java

@@ -0,0 +1,150 @@
+package com.yc.music.model;
+
+import java.io.Serializable;
+
+/**
+ * <pre>
+ *     @author yangchong
+ *     blog  : www.pedaily.cn
+ *     time  : 2018/03/22
+ *     desc  : 音频单曲信息
+ *     revise:
+ * </pre>
+ */
+public class AudioBean implements Serializable {
+
+    // 歌曲类型:本地/网络
+    private Type type;
+    // [本地歌曲]歌曲id
+    private String id;
+    // 音乐标题
+    private String title;
+    // 艺术家
+    private String artist;
+    // 专辑
+    private String album;
+    // [本地歌曲]专辑ID
+    private long albumId;
+    // [在线歌曲]专辑封面路径
+    private String coverPath;
+    // 持续时间
+    private long duration;
+    // 音乐路径
+    private String path;
+    // 文件名
+    private String fileName;
+    // 文件大小
+    private long fileSize;
+
+    public enum Type {
+        LOCAL,
+        ONLINE
+    }
+
+    public Type getType() {
+        return type;
+    }
+
+    public void setType(Type type) {
+        this.type = type;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getArtist() {
+        return artist;
+    }
+
+    public void setArtist(String artist) {
+        this.artist = artist;
+    }
+
+    public String getAlbum() {
+        return album;
+    }
+
+    public void setAlbum(String album) {
+        this.album = album;
+    }
+
+    public long getAlbumId() {
+        return albumId;
+    }
+
+    public void setAlbumId(long albumId) {
+        this.albumId = albumId;
+    }
+
+    public String getCoverPath() {
+        return coverPath;
+    }
+
+    public void setCoverPath(String coverPath) {
+        this.coverPath = coverPath;
+    }
+
+    public long getDuration() {
+        return duration;
+    }
+
+    public void setDuration(long duration) {
+        this.duration = duration;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public void setPath(String path) {
+        this.path = path;
+    }
+
+    public String getFileName() {
+        return fileName;
+    }
+
+    public void setFileName(String fileName) {
+        this.fileName = fileName;
+    }
+
+    public long getFileSize() {
+        return fileSize;
+    }
+
+    public void setFileSize(long fileSize) {
+        this.fileSize = fileSize;
+    }
+
+    /**
+     * 思考为什么要重写这两个方法
+     * 对比本地歌曲是否相同
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof AudioBean) {
+            AudioBean bean = (AudioBean) obj;
+            return this.id.equals(bean.getId());
+        }
+        return super.equals(obj);
+    }
+
+    @Override
+    public int hashCode() {
+        return this.id.hashCode();
+    }
+
+}

+ 44 - 0
MusicPlayer/src/main/java/com/yc/music/receiver/AudioBroadcastReceiver.java

@@ -0,0 +1,44 @@
+package com.yc.music.receiver;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import com.yc.music.config.MusicConstant;
+import com.yc.music.service.PlayService;
+import com.yc.music.utils.MusicLogUtils;
+
+
+/**
+ * 屏幕亮了,灭了,弹出锁屏页面逻辑
+ * 其实这个跟通知处理逻辑一样
+ */
+public class AudioBroadcastReceiver extends BroadcastReceiver {
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        final String action = intent.getAction();
+        if(action!=null && action.length()>0){
+            switch (action){
+                //锁屏时处理的逻辑
+                case MusicConstant.LOCK_SCREEN_ACTION:
+                    PlayService.startCommand(context, MusicConstant.LOCK_SCREEN_ACTION);
+                    MusicLogUtils.e("AudioBroadcastReceiver"+"---LOCK_SCREEN");
+                    break;
+                //当屏幕灭了
+                case Intent.ACTION_SCREEN_OFF:
+                    PlayService.startCommand(context,Intent.ACTION_SCREEN_OFF);
+                    MusicLogUtils.e("AudioBroadcastReceiver"+"---当屏幕灭了");
+                    break;
+                //当屏幕亮了
+                case Intent.ACTION_SCREEN_ON:
+                    PlayService.startCommand(context,Intent.ACTION_SCREEN_ON);
+                    MusicLogUtils.e("AudioBroadcastReceiver"+"---当屏幕亮了");
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+
+}

+ 35 - 0
MusicPlayer/src/main/java/com/yc/music/receiver/AudioEarPhoneReceiver.java

@@ -0,0 +1,35 @@
+package com.yc.music.receiver;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.media.AudioManager;
+
+import com.yc.music.config.MusicPlayAction;
+import com.yc.music.service.PlayService;
+
+
+/**
+ * 来电/耳机拔出时暂停播放
+ * 其实这个跟通知处理逻辑一样
+ */
+public class AudioEarPhoneReceiver extends BroadcastReceiver {
+
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        final String action = intent.getAction();
+        if(action!=null && action.length()>0){
+            switch (action){
+                //来电/耳机拔出时暂停播放
+                case AudioManager.ACTION_AUDIO_BECOMING_NOISY:
+                    PlayService.startCommand(context, MusicPlayAction.TYPE_START_PAUSE);
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+
+
+}

+ 41 - 0
MusicPlayer/src/main/java/com/yc/music/receiver/EarphoneControlReceiver.java

@@ -0,0 +1,41 @@
+package com.yc.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.yc.music.config.MusicPlayAction;
+import com.yc.music.service.PlayService;
+
+
+/**
+ * 耳机线控,仅在5.0以下有效,5.0以上被{@link MediaSessionCompat}接管。
+ */
+public class EarphoneControlReceiver extends BroadcastReceiver {
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        KeyEvent event = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
+        if (event == null || event.getAction() != KeyEvent.ACTION_UP) {
+            return;
+        }
+        switch (event.getKeyCode()) {
+            case KeyEvent.KEYCODE_MEDIA_PLAY:
+            case KeyEvent.KEYCODE_MEDIA_PAUSE:
+            case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+            case KeyEvent.KEYCODE_HEADSETHOOK:
+                PlayService.startCommand(context, MusicPlayAction.TYPE_START_PAUSE);
+                break;
+            case KeyEvent.KEYCODE_MEDIA_NEXT:
+                PlayService.startCommand(context, MusicPlayAction.TYPE_NEXT);
+                break;
+            case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
+                PlayService.startCommand(context, MusicPlayAction.TYPE_PRE);
+                break;
+            default:
+                break;
+        }
+    }
+}

+ 45 - 0
MusicPlayer/src/main/java/com/yc/music/receiver/NotificationStatusBarReceiver.java

@@ -0,0 +1,45 @@
+package com.yc.music.receiver;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.text.TextUtils;
+
+import com.yc.music.config.MusicPlayAction;
+import com.yc.music.service.PlayService;
+import com.yc.music.tool.BaseAppHelper;
+import com.yc.music.utils.MusicLogUtils;
+
+
+public class NotificationStatusBarReceiver extends BroadcastReceiver {
+
+    public static final String ACTION_STATUS_BAR = "YC_ACTION_STATUS_BAR";
+    public static final String EXTRA = "extra";
+
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (intent == null || TextUtils.isEmpty(intent.getAction())) {
+            return;
+        }
+        String extra = intent.getStringExtra(EXTRA);
+        if (TextUtils.equals(extra, MusicPlayAction.TYPE_NEXT)) {
+            PlayService.startCommand(context, MusicPlayAction.TYPE_NEXT);
+            MusicLogUtils.e("NotifiyStatusBarReceiver"+"下一首");
+        } else if (TextUtils.equals(extra, MusicPlayAction.TYPE_START_PAUSE)) {
+            if(BaseAppHelper.get().getPlayService()!=null){
+                boolean playing = BaseAppHelper.get().getPlayService().isPlaying();
+                if(playing){
+                    MusicLogUtils.e("NotifiyStatusBarReceiver"+"暂停");
+                }else {
+                    MusicLogUtils.e("NotifiyStatusBarReceiver"+"播放");
+                }
+                PlayService.startCommand(context, MusicPlayAction.TYPE_START_PAUSE);
+            }
+
+        }else if(TextUtils.equals(extra, MusicPlayAction.TYPE_PRE)){
+            PlayService.startCommand(context, MusicPlayAction.TYPE_PRE);
+            MusicLogUtils.e("NotifiyStatusBarReceiver"+"上一首");
+        }
+    }
+}

+ 866 - 0
MusicPlayer/src/main/java/com/yc/music/service/PlayService.java

@@ -0,0 +1,866 @@
+package com.yc.music.service;
+
+import android.annotation.SuppressLint;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import com.yc.music.config.MusicPlayAction;
+import com.yc.music.config.MusicConstant;
+import com.yc.music.config.PlayModeEnum;
+import com.yc.music.inter.EventCallback;
+import com.yc.music.inter.OnPlayerEventListener;
+import com.yc.music.manager.AudioFocusManager;
+import com.yc.music.manager.MediaSessionManager;
+import com.yc.music.model.AudioBean;
+import com.yc.music.receiver.AudioBroadcastReceiver;
+import com.yc.music.receiver.AudioEarPhoneReceiver;
+import com.yc.music.tool.BaseAppHelper;
+import com.yc.music.utils.MusicLogUtils;
+import com.yc.music.utils.NotificationHelper;
+import com.yc.music.tool.QuitTimerHelper;
+import com.yc.music.utils.MusicSpUtils;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Random;
+
+
+/**
+ * Service就是用来在后台完成一些不需要和用户交互的动作
+ */
+public class PlayService extends Service {
+
+    /**
+     * 正在播放的歌曲的序号
+     */
+    private int mPlayingPosition = -1;
+    /**
+     * 正在播放的歌曲[本地|网络]
+     */
+    private AudioBean mPlayingMusic;
+    /**
+     * 音频list集合
+     */
+    private List<AudioBean> audioMusics;
+    /**
+     * 播放状态
+     */
+    private int mPlayState = MusicPlayAction.STATE_IDLE;
+    /**
+     * 播放器
+     */
+    private MediaPlayer mPlayer;
+    /**
+     * 播放进度监听器
+     */
+    private OnPlayerEventListener mListener;
+    /**
+     * 更新播放进度的显示,时间的显示
+     */
+    private static final int UPDATE_PLAY_PROGRESS_SHOW = 0;
+    /**
+     * 允许与媒体控制器、音量键、媒体按钮和传输控件交互
+     */
+    private MediaSessionManager mMediaSessionManager;
+    /**
+     * 捕获/丢弃音乐焦点处理
+     */
+    private AudioFocusManager mAudioFocusManager;
+    /**
+     * 是否锁屏了,默认是false
+     */
+    private boolean mIsLocked = false;
+    /**
+     * 来电/耳机拔出时暂停播放
+     * 在播放时调用,在暂停时注销
+     */
+    private final AudioEarPhoneReceiver mNoisyReceiver = new AudioEarPhoneReceiver();
+    private final IntentFilter mFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
+    /**
+     * 其他广播
+     * 比如:屏幕灭了后再次亮了,会显示锁屏页面
+     * 这个在onCreate中创建,在onDestroy中销毁
+     */
+    private final AudioBroadcastReceiver mAudioReceiver = new AudioBroadcastReceiver();
+    /**
+     * 广播接受者标识,避免多次注册广播
+     */
+    private boolean mReceiverTag = false;
+
+    @SuppressLint("HandlerLeak")
+    private Handler handler = new Handler(){
+        @Override
+        public void handleMessage(@NonNull Message msg) {
+            super.handleMessage(msg);
+            switch (msg.what){
+                case UPDATE_PLAY_PROGRESS_SHOW:
+                    updatePlayProgressShow();
+                    break;
+                default:
+                    break;
+            }
+        }
+    };
+
+
+
+    /**
+     * 绑定服务时才会调用
+     * 必须要实现的方法
+     * @param intent        intent
+     * @return              IBinder对象
+     */
+    @Nullable
+    @Override
+    public IBinder onBind(Intent intent) {
+        return new PlayBinder();
+    }
+
+
+    /**
+     * 比如,广播,耳机声控,通知栏广播,来电或者拔下耳机广播开启服务
+     * @param context       上下文
+     * @param type          类型
+     */
+    public static void startCommand(Context context, String type) {
+        Intent intent = new Intent(context, PlayService.class);
+        intent.setAction(type);
+        context.startService(intent);
+    }
+
+
+    public class PlayBinder extends Binder {
+        public PlayService getService() {
+            return PlayService.this;
+        }
+    }
+
+
+    /**
+     * 首次创建服务时,系统将调用此方法来执行一次性设置程序(在调用 onStartCommand() 或 onBind() 之前)。
+     * 如果服务已在运行,则不会调用此方法。该方法只被调用一次
+     */
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        NotificationHelper.get().init(this);
+        createMediaPlayer();
+        initMediaSessionManager();
+        initAudioFocusManager();
+        initEarPhoneBroadcastReceiver();
+        initAudioBroadcastReceiver();
+        initQuitTimer();
+    }
+
+    /**
+     * 服务在销毁时调用该方法
+     */
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        //销毁handler
+        if(handler!=null){
+            handler.removeCallbacksAndMessages(null);
+            handler = null;
+        }
+        //销毁MediaPlayer
+        mPlayer.reset();
+        mPlayer.release();
+        mPlayer = null;
+        //放弃音频焦点
+        mAudioFocusManager.abandonAudioFocus();
+        mMediaSessionManager.release();
+        //注销广播接收者
+        unregisterReceiver(mAudioReceiver);
+        //结束notification通知
+        NotificationHelper.get().cancelAll();
+        //设置service为null
+        BaseAppHelper.get().setPlayService(null);
+    }
+
+
+    /**
+     * 每次通过startService()方法启动Service时都会被回调。
+     * @param intent                intent
+     * @param flags                 flags
+     * @param startId               startId
+     * @return
+     * onStartCommand方法返回值作用:
+     * START_STICKY:粘性,service进程被异常杀掉,系统重新创建进程与服务,会重新执行onCreate()、onStartCommand(Intent)
+     * START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但不保证服务被kill后一定能重启。
+     * START_NOT_STICKY:非粘性,Service进程被异常杀掉,系统不会自动重启该Service。
+     * START_REDELIVER_INTENT:重传Intent。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,并将Intent的值传入。
+     */
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        if (intent != null && intent.getAction() != null) {
+            switch (intent.getAction()) {
+                //上一首
+                case MusicPlayAction.TYPE_PRE:
+                    prev();
+                    break;
+                //下一首
+                case MusicPlayAction.TYPE_NEXT:
+                    next();
+                    break;
+                //播放或暂停
+                case MusicPlayAction.TYPE_START_PAUSE:
+                    playPause();
+                    break;
+                //添加锁屏界面
+                case MusicConstant.LOCK_SCREEN_ACTION:
+                    MusicLogUtils.e("PlayService"+"---LOCK_SCREEN"+mIsLocked);
+                    break;
+                //当屏幕灭了,添加锁屏页面
+                case Intent.ACTION_SCREEN_OFF:
+                    startLockAudioActivity();
+                    MusicLogUtils.e("PlayService"+"---当屏幕灭了");
+                    break;
+                case Intent.ACTION_SCREEN_ON:
+                    MusicLogUtils.e("PlayService"+"---当屏幕亮了");
+                    break;
+                default:
+                    break;
+            }
+        }
+        return START_NOT_STICKY;
+    }
+
+
+
+    /**
+     * 创建MediaPlayer对象
+     */
+    private void createMediaPlayer() {
+        if(mPlayer==null){
+            //MediaCodec codec = new MediaCodec();
+            mPlayer = new MediaPlayer();
+        }
+    }
+
+
+    /**
+     * 允许与媒体控制器、音量键、媒体按钮和传输控件交互。
+     * 播放器除了播放了音乐之外什么都没做,就可以分别在任务管理、锁屏、负一屏控制我的播放器
+     */
+    private void initMediaSessionManager() {
+        mMediaSessionManager = new MediaSessionManager(this);
+    }
+
+    /**
+     * 捕获/丢弃音乐焦点处理
+     */
+    private void initAudioFocusManager() {
+        mAudioFocusManager = new AudioFocusManager(this);
+    }
+
+
+    /**
+     * 初始化耳机插入和拔出监听
+     */
+    private void initEarPhoneBroadcastReceiver() {
+        //这块直接在清单文件注册
+    }
+
+
+    /**
+     * 初始化IntentFilter添加action意图
+     * 主要是监听屏幕亮了与灭了
+     */
+    private void initAudioBroadcastReceiver() {
+        final IntentFilter filter = new IntentFilter();
+        //来电/耳机
+        filter.addAction(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
+        //锁屏
+        filter.addAction(MusicConstant.LOCK_SCREEN_ACTION);
+        //当屏幕灭了
+        filter.addAction(Intent.ACTION_SCREEN_OFF);
+        //当屏幕亮了
+        filter.addAction(Intent.ACTION_SCREEN_ON);
+        registerReceiver(mAudioReceiver, filter);
+    }
+
+
+    /**
+     * 初始化计时器
+     */
+    private void initQuitTimer() {
+        QuitTimerHelper.getInstance().init(this, handler, new EventCallback<Long>() {
+            @Override
+            public void onEvent(Long aLong) {
+                if (mListener != null) {
+                    mListener.onTimer(aLong);
+                }
+            }
+        });
+    }
+
+
+
+    /**---------------------播放或暂停,上一首,下一首-----------------------------------------*/
+
+    /**
+     * 播放或暂停
+     * 逻辑:
+     * 1.如果正在准备,点击则是停止播放
+     * 2.如果是正在播放,点击则是暂停
+     * 3.如果是暂停状态,点击则是开始播放
+     * 4.其他情况是直接播放
+     */
+    public void playPause() {
+        if (isPreparing()) {
+            stop();
+        } else if (isPlaying()) {
+            pause();
+        } else if (isPausing()) {
+            start();
+        } else {
+            play(getPlayingPosition());
+        }
+    }
+
+
+    /**
+     * 上一首
+     * 记住有播放类型,单曲循环,顺序循环,随机播放
+     * 逻辑:如果不是第一首,则还有上一首;如果没有上一首,则切换到最后一首
+     */
+    public void prev() {
+        //建议都添加这个判断
+        if (audioMusics.isEmpty()) {
+            return;
+        }
+        int playMode = MusicSpUtils.getInstance(MusicConstant.SP_NAME).getInt(MusicConstant.PLAY_MODE, 0);
+        int size = audioMusics.size();
+        PlayModeEnum mode = PlayModeEnum.valueOf(playMode);
+        switch (mode) {
+            //随机
+            case SHUFFLE:
+                mPlayingPosition = new Random().nextInt(size);
+                play(mPlayingPosition);
+                break;
+            //单曲
+            case SINGLE:
+                play(mPlayingPosition);
+                break;
+            //顺序播放并且循环
+            case LOOP:
+            default:
+                if(mPlayingPosition != 0){
+                    // 如果不是第一首,则还有上一首
+                    mPlayingPosition--;
+                } else {
+                    // 如果没有上一首,则切换到最后一首
+                    mPlayingPosition = size;
+                }
+                play(mPlayingPosition);
+                break;
+        }
+    }
+
+
+    /**
+     * 下一首
+     * 记住有播放类型,单曲循环,顺序循环,随机播放
+     * 逻辑:如果不是最后一首,则还有下一首;如果是最后一首,则切换回第一首
+     */
+    public void next() {
+        //建议都添加这个判断
+        if (audioMusics.isEmpty()) {
+            return;
+        }
+        int playMode = MusicSpUtils.getInstance(MusicConstant.SP_NAME).getInt(MusicConstant.PLAY_MODE, 0);
+        int size = audioMusics.size();
+        PlayModeEnum mode = PlayModeEnum.valueOf(playMode);
+        switch (mode) {
+            //随机
+            case SHUFFLE:
+                mPlayingPosition = new Random().nextInt(size);
+                play(mPlayingPosition);
+                break;
+            //单曲
+            case SINGLE:
+                play(mPlayingPosition);
+                break;
+            //顺序播放并且循环
+            case LOOP:
+            default:
+                if (mPlayingPosition != size - 1) {
+                    // 如果不是最后一首,则还有下一首
+                    mPlayingPosition++;
+                } else {
+                    // 如果是最后一首,则切换回第一首
+                    mPlayingPosition = 0;
+                }
+                MusicLogUtils.e("PlayService"+"----mPlayingPosition----"+ mPlayingPosition);
+                play(mPlayingPosition);
+                break;
+        }
+    }
+
+    /**---------------------开始播放,暂停播放,停止播放等-----------------------------------------*/
+
+
+    /**
+     * 开始播放
+     */
+    public void start() {
+        if (!isPreparing() && !isPausing()) {
+            return;
+        }
+        if(mPlayingMusic==null){
+            return;
+        }
+        if(mAudioFocusManager.requestAudioFocus()){
+            if(mPlayer!=null){
+                mPlayer.start();
+                mPlayState = MusicPlayAction.STATE_PLAYING;
+                //开始发送消息,执行进度条进度更新
+                handler.sendEmptyMessage(UPDATE_PLAY_PROGRESS_SHOW);
+                if (mListener != null) {
+                    mListener.onPlayerStart();
+                }
+                //当点击播放按钮时(播放详情页面或者底部控制栏),同步通知栏中播放按钮状态
+                NotificationHelper.get().showPlay(mPlayingMusic);
+                //注册监听来电/耳机拔出时暂停播放广播
+                if(!mReceiverTag){
+                    mReceiverTag = true;
+                    registerReceiver(mNoisyReceiver, mFilter);
+                }
+                mMediaSessionManager.updatePlaybackState();
+            }
+        }
+    }
+
+
+    /**
+     * 暂停
+     */
+    public void pause() {
+        if(mPlayer!=null){
+            //暂停
+            mPlayer.pause();
+            //切换状态
+            mPlayState = MusicPlayAction.STATE_PAUSE;
+            //移除,注意一定要移除,否则一直走更新方法
+            handler.removeMessages(UPDATE_PLAY_PROGRESS_SHOW);
+            //监听
+            if (mListener != null) {
+                mListener.onPlayerPause();
+            }
+            //当点击暂停按钮时(播放详情页面或者底部控制栏),同步通知栏中暂停按钮状态
+            NotificationHelper.get().showPause(mPlayingMusic);
+            //注销监听来电/耳机拔出时暂停播放广播
+            //判断广播是否注册
+            if (mReceiverTag) {
+                //Tag值 赋值为false 表示该广播已被注销
+                mReceiverTag = false;
+                unregisterReceiver(mNoisyReceiver);
+            }
+
+            mMediaSessionManager.updatePlaybackState();
+        }
+    }
+
+
+    /**
+     * 停止播放
+     */
+    public void stop() {
+        if (isDefault()) {
+            return;
+        }
+        pause();
+        if(mPlayer!=null){
+            mPlayer.reset();
+            mPlayState = MusicPlayAction.STATE_IDLE;
+        }
+    }
+
+
+    /**
+     * 播放索引为position的音乐
+     * @param position              索引
+     */
+    public void play(int position) {
+        audioMusics = BaseAppHelper.get().getMusicList();
+        if (audioMusics.isEmpty()) {
+            return;
+        }
+
+        if (position < 0) {
+            position = audioMusics.size() - 1;
+        } else if (position >= audioMusics.size()) {
+            //如果是最后一首音乐,则播放时直接播放第一首音乐
+            position = 0;
+        }
+
+        mPlayingPosition = position;
+        AudioBean music = audioMusics.get(mPlayingPosition);
+        String id = music.getId();
+        MusicLogUtils.e("PlayService"+"----id----"+ id);
+        //保存当前播放的musicId,下次进来可以记录状态
+        long musicId = Long.parseLong(id);
+        MusicSpUtils.getInstance(MusicConstant.SP_NAME).put(MusicConstant.MUSIC_ID,musicId);
+        play(music);
+    }
+
+
+    /**
+     * 拖动seekBar时,调节进度
+     * @param progress          进度
+     */
+    public void seekTo(int progress) {
+        //只有当播放或者暂停的时候才允许拖动bar
+        if (isPlaying() || isPausing()) {
+            mPlayer.seekTo(progress);
+            if(mListener!=null){
+                mListener.onUpdateProgress(progress);
+            }
+            mMediaSessionManager.updatePlaybackState();
+        }
+    }
+
+    /**
+     * 播放,这种是直接传音频实体类
+     * @param music         music
+     */
+    public void play(AudioBean music) {
+        mPlayingMusic = music;
+        createMediaPlayer();
+        try {
+            mPlayer.reset();
+            //把音频路径传给播放器
+            mPlayer.setDataSource(mPlayingMusic.getPath());
+            //准备
+            mPlayer.prepareAsync();
+            //设置状态为准备中
+            mPlayState = MusicPlayAction.STATE_PREPARING;
+            //监听
+            mPlayer.setOnPreparedListener(mOnPreparedListener);
+            mPlayer.setOnBufferingUpdateListener(mOnBufferingUpdateListener);
+            mPlayer.setOnCompletionListener(mOnCompletionListener);
+            mPlayer.setOnSeekCompleteListener(mOnSeekCompleteListener);
+            mPlayer.setOnErrorListener(mOnErrorListener);
+            mPlayer.setOnInfoListener(mOnInfoListener);
+            //当播放的时候,需要刷新界面信息
+            if (mListener != null) {
+                mListener.onChange(mPlayingMusic);
+            }
+            //更新通知栏
+            NotificationHelper.get().showPlay(mPlayingMusic);
+
+            //更新
+            mMediaSessionManager.updateMetaData(mPlayingMusic);
+            mMediaSessionManager.updatePlaybackState();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+
+    /**
+     * 更新播放进度的显示,时间的显示
+     */
+    private void updatePlayProgressShow() {
+        if (isPlaying() && mListener != null) {
+            int currentPosition =  mPlayer.getCurrentPosition();
+            mListener.onUpdateProgress(currentPosition);
+        }
+        MusicLogUtils.e("updatePlayProgressShow");
+        // 每30毫秒更新一下显示的内容,注意这里时间不要太短,因为这个是一个循环
+        // 经过测试,60毫秒更新一次有点卡,30毫秒最为顺畅
+        handler.sendEmptyMessageDelayed(UPDATE_PLAY_PROGRESS_SHOW, 300);
+    }
+
+
+    /** 音频准备好的监听器 */
+    private MediaPlayer.OnPreparedListener mOnPreparedListener = new MediaPlayer.OnPreparedListener() {
+        /** 当音频准备好可以播放了,则这个方法会被调用  */
+        @Override
+        public void onPrepared(MediaPlayer mp) {
+            if (isPreparing()) {
+                start();
+            }
+        }
+    };
+
+
+    /** 当音频播放结束的时候的监听器 */
+    private MediaPlayer.OnCompletionListener mOnCompletionListener = new MediaPlayer.OnCompletionListener() {
+        /** 当音频播放结果的时候这个方法会被调用 */
+        @Override
+        public void onCompletion(MediaPlayer mp) {
+            next();
+        }
+    };
+
+
+    /** 当音频缓冲的监听器 */
+    private MediaPlayer.OnBufferingUpdateListener mOnBufferingUpdateListener = new MediaPlayer.OnBufferingUpdateListener() {
+        @Override
+        public void onBufferingUpdate(MediaPlayer mp, int percent) {
+            if (mListener != null) {
+                // 缓冲百分比
+                mListener.onBufferingUpdate(percent);
+            }
+        }
+    };
+
+
+    /** 跳转完成时的监听 */
+    private MediaPlayer.OnSeekCompleteListener mOnSeekCompleteListener = new MediaPlayer.OnSeekCompleteListener() {
+        @Override
+        public void onSeekComplete(MediaPlayer mp) {
+
+        }
+    };
+
+    /**
+     * 播放错误的监听
+     */
+    private MediaPlayer.OnErrorListener mOnErrorListener = new MediaPlayer.OnErrorListener() {
+        @Override
+        public boolean onError(MediaPlayer mp, int what, int extra) {
+            return false;
+        }
+    };
+
+    /**
+     * 设置音频信息监听器
+     */
+    private MediaPlayer.OnInfoListener mOnInfoListener = new MediaPlayer.OnInfoListener() {
+        @Override
+        public boolean onInfo(MediaPlayer mp, int what, int extra) {
+            return false;
+        }
+    };
+
+    /**
+     * 是否正在播放
+     * @return          true表示正在播放
+     */
+    public boolean isPlaying() {
+        return mPlayState == MusicPlayAction.STATE_PLAYING;
+    }
+
+
+    /**
+     * 是否暂停
+     * @return          true表示暂停
+     */
+    public boolean isPausing() {
+        return mPlayState == MusicPlayAction.STATE_PAUSE;
+    }
+
+
+    /**
+     * 是否正在准备中
+     * @return          true表示正在准备中
+     */
+    public boolean isPreparing() {
+        return mPlayState == MusicPlayAction.STATE_PREPARING;
+    }
+
+
+    /**
+     * 是否正在准备中
+     * @return          true表示正在准备中
+     */
+    public boolean isDefault() {
+        return mPlayState == MusicPlayAction.STATE_IDLE;
+    }
+
+    /**------------------------------------------------------------------------------------------*/
+
+    /**
+     * 退出时候调用
+     */
+    public void quit() {
+        // 先停止播放
+        stop();
+        // 移除定时器
+        QuitTimerHelper.getInstance().stop();
+        // 当另一个组件(如 Activity)通过调用 startService() 请求启动服务时,系统将调用onStartCommand。
+        // 一旦执行此方法,服务即会启动并可在后台无限期运行。 如果自己实现此方法,则需要在服务工作完成后,
+        // 通过调用 stopSelf() 或 stopService() 来停止服务。
+        stopSelf();
+    }
+
+
+    /**
+     * 获取正在播放的本地歌曲的序号
+     */
+    public int getPlayingPosition() {
+        return mPlayingPosition;
+    }
+
+
+    /**
+     * 获取正在播放的歌曲[本地|网络]
+     */
+    public AudioBean getPlayingMusic() {
+        return mPlayingMusic;
+    }
+
+
+    /**
+     * 获取播放的进度
+     * @return          long类型值
+     */
+    public long getCurrentPosition() {
+        if (isPlaying() || isPausing()) {
+            return mPlayer.getCurrentPosition();
+        } else {
+            return 0;
+        }
+    }
+
+    /**
+     * 判斷是否有上一首音頻
+     * @return          true表示有
+     */
+    public boolean isHavePre() {
+        if(audioMusics !=null && audioMusics.size()>0){
+            if(mPlayingPosition != 0){
+                // 如果不是第一首,则还有上一首
+                return true;
+            } else {
+                return false;
+            }
+        }else {
+            return false;
+        }
+    }
+
+    /**
+     * 判斷是否有下一首音頻
+     * @return          true表示有
+     */
+    public boolean isHaveNext() {
+        if(audioMusics !=null && audioMusics.size()>0){
+            if (mPlayingPosition != audioMusics.size() - 1) {
+                // 如果不是最后一首,则还有下一首
+                return true;
+            } else {
+                // 如果是最后一首,则切换回第一首
+                return false;
+            }
+        }else {
+            return false;
+        }
+    }
+
+
+//
+//    /**
+//     * 扫描音乐
+//     */
+//    @SuppressLint("StaticFieldLeak")
+//    public void updateMusicList(final EventCallback<Void> callback) {
+//        new AsyncTask<Void, Void, List<AudioBean>>() {
+//            @Override
+//            protected List<AudioBean> doInBackground(Void... params) {
+//                return FileMusicScanManager.getInstance().scanMusic(PlayService.this);
+//            }
+//
+//            @Override
+//            protected void onPostExecute(List<AudioBean> musicList) {
+//                //首先先清空
+//                //然后添加所有扫描到的音乐
+//                BaseAppHelper.get().setMusicList(musicList);
+//
+//                //如果获取音乐数据集合不为空
+//                if (!BaseAppHelper.get().getMusicList().isEmpty()) {
+//                    //音频的集合
+//                    audioMusics = BaseAppHelper.get().getMusicList();
+//                    //刷新正在播放的本地歌曲的序号
+//                    updatePlayingPosition();
+//                    //获取正在播放的音乐
+//                    if(mPlayingPosition>=0){
+//                        mPlayingMusic = BaseAppHelper.get().getMusicList().get(mPlayingPosition);
+//                    }
+//                }
+//                if (callback != null) {
+//                    callback.onEvent(null);
+//                }
+//            }
+//        }.execute();
+//    }
+
+
+    /**
+     * 删除或下载歌曲后刷新正在播放的本地歌曲的序号
+     */
+    public void updatePlayingPosition() {
+        int position = 0;
+        long id = MusicSpUtils.getInstance(MusicConstant.SP_NAME).getLong(MusicConstant.MUSIC_ID,-1);
+        if(audioMusics.isEmpty()){
+            return;
+        }
+        for (int i = 0; i < audioMusics.size(); i++) {
+            String musicId = audioMusics.get(i).getId();
+            MusicLogUtils.e("PlayService"+"----musicId----"+ musicId);
+            if (Long.parseLong(musicId) == id) {
+                position = i;
+                break;
+            }
+        }
+        mPlayingPosition = position;
+        long musicId = Long.parseLong(audioMusics.get(mPlayingPosition).getId());
+        MusicSpUtils.getInstance(MusicConstant.SP_NAME).put(MusicConstant.MUSIC_ID,musicId);
+    }
+
+
+    /**
+     * 获取播放进度监听器对象
+     * @return                  OnPlayerEventListener对象
+     */
+    public OnPlayerEventListener getOnPlayEventListener() {
+        return mListener;
+    }
+
+    /**
+     * 设置播放进度监听器
+     * @param listener          listener
+     */
+    public void setOnPlayEventListener(OnPlayerEventListener listener) {
+        mListener = listener;
+    }
+
+
+    /**-------------------------------------添加锁屏界面----------------------------------------*/
+
+
+    /**
+     * 打开锁屏页面,这块伤透了脑筋
+     * 不管是播放状态是哪一个,只要屏幕灭了到亮了,就展现这个锁屏页面
+     * 有些APP限制了状态,比如只有播放时才走这个逻辑
+     */
+    private void startLockAudioActivity() {
+        if(!mIsLocked && isPlaying()){
+//            Intent lockScreen = new Intent(this, LockAudioActivity.class);
+//            lockScreen.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+//            startActivity(lockScreen);
+//            BaseConfig.INSTANCE.setLocked(true);
+        }
+    }
+
+
+
+    /**-------------------------------------播放list----------------------------------------*/
+
+
+
+
+}

+ 90 - 0
MusicPlayer/src/main/java/com/yc/music/tool/BaseAppHelper.java

@@ -0,0 +1,90 @@
+package com.yc.music.tool;
+
+import android.annotation.SuppressLint;
+
+import com.yc.music.model.AudioBean;
+import com.yc.music.service.PlayService;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * <pre>
+ *     @author yangchong
+ *     blog  : https://github.com/yangchong211
+ *     time  : 2017/03/22
+ *     desc  : BaseAppHelper
+ *     revise:
+ * </pre>
+ */
+public class BaseAppHelper {
+
+    /**
+     * 播放音乐service
+     */
+    private PlayService mPlayService;
+    /**
+     * 本地歌曲列表
+     */
+    private final List<AudioBean> mMusicList = new ArrayList<>();
+
+    private BaseAppHelper() {
+        //这里可以做一些初始化的逻辑
+    }
+
+    private static class SingletonHolder {
+        @SuppressLint("StaticFieldLeak")
+        private final static BaseAppHelper INSTANCE = new BaseAppHelper();
+    }
+
+    public static BaseAppHelper get() {
+        return SingletonHolder.INSTANCE;
+    }
+
+    /**
+     * 获取PlayService对象
+     * @return              返回PlayService对象
+     */
+    public PlayService getPlayService() {
+        return mPlayService;
+    }
+
+    /**
+     * 设置PlayService服务
+     */
+    public void setPlayService(PlayService service) {
+        mPlayService = service;
+    }
+
+    /**
+     * 获取扫描到的音乐数据集合
+     * @return              返回list集合
+     */
+    public List<AudioBean> getMusicList() {
+        return mMusicList;
+    }
+
+
+    /**
+     * 设置音频结合
+     * @param list              音频集合
+     */
+    public void setMusicList(List<AudioBean> list) {
+        mMusicList.clear();
+        mMusicList.addAll(list);
+    }
+
+    /**
+     * 获取到播放音乐的服务
+     * @return              PlayService对象
+     */
+    public PlayService getMusicService () {
+        PlayService playService = BaseAppHelper.get().getPlayService();
+        if (playService == null) {
+            //待解决:当长期处于后台,如何保活?避免service被杀死……
+            throw new NullPointerException("play service is null");
+        }
+        return playService;
+    }
+
+}

+ 76 - 0
MusicPlayer/src/main/java/com/yc/music/tool/QuitTimerHelper.java

@@ -0,0 +1,76 @@
+package com.yc.music.tool;
+
+import android.os.Handler;
+import android.text.format.DateUtils;
+
+import androidx.annotation.NonNull;
+
+import com.yc.music.inter.EventCallback;
+import com.yc.music.service.PlayService;
+
+
+/**
+ * 定时器
+ */
+public class QuitTimerHelper {
+
+    private PlayService mPlayService;
+    private EventCallback<Long> mTimerCallback;
+    private Handler mHandler;
+    private long mTimerRemain;
+
+    public static QuitTimerHelper getInstance() {
+        return SingletonHolder.QUIT_TIMER_INSTANCE;
+    }
+
+    private static class SingletonHolder {
+        private static final QuitTimerHelper QUIT_TIMER_INSTANCE = new QuitTimerHelper();
+    }
+
+    private QuitTimerHelper() {}
+
+    /**
+     * 初始化      用@NonNull注解,表示不能为null
+     * @param playService               playService
+     * @param handler                   handler
+     * @param timerCallback             timerCallback
+     */
+    public void init(@NonNull PlayService playService, @NonNull Handler handler,
+                     @NonNull EventCallback<Long> timerCallback) {
+        mPlayService = playService;
+        mHandler = handler;
+        mTimerCallback = timerCallback;
+    }
+
+    public void start(long milli) {
+        if(mHandler==null){
+            //ToastUtils.showShort("请先进行初始化");
+            return;
+        }
+        stop();
+        if (milli > 0) {
+            mTimerRemain = milli + DateUtils.SECOND_IN_MILLIS;
+            mHandler.post(mQuitRunnable);
+        } else {
+            mTimerRemain = 0;
+            mTimerCallback.onEvent(mTimerRemain);
+        }
+    }
+
+    public void stop() {
+        mHandler.removeCallbacks(mQuitRunnable);
+    }
+
+    private Runnable mQuitRunnable = new Runnable() {
+        @Override
+        public void run() {
+            mTimerRemain -= DateUtils.SECOND_IN_MILLIS;
+            if (mTimerRemain > 0) {
+                mTimerCallback.onEvent(mTimerRemain);
+                mHandler.postDelayed(this, DateUtils.SECOND_IN_MILLIS);
+            } else {
+                mPlayService.quit();
+            }
+        }
+    };
+}

+ 71 - 0
MusicPlayer/src/main/java/com/yc/music/utils/MusicLogUtils.java

@@ -0,0 +1,71 @@
+/*
+Copyright 2017 yangchong211(github.com/yangchong211)
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package com.yc.music.utils;
+
+import android.util.Log;
+
+/**
+ * <pre>
+ *     @author yangchong
+ *     blog  : https://github.com/yangchong211
+ *     time  : 2017/10/21
+ *     desc  : log工具
+ *     revise:
+ * </pre>
+ */
+public final class MusicLogUtils {
+
+    private static final String TAG = "MusicPlayer";
+    private static boolean isLog = false;
+
+    /**
+     * 设置是否开启日志
+     * @param isLog                 是否开启日志
+     */
+    public static void setIsLog(boolean isLog) {
+        MusicLogUtils.isLog = isLog;
+    }
+
+    public static boolean isIsLog() {
+        return isLog;
+    }
+
+    public static void d(String message) {
+        if(isLog){
+            Log.d(TAG, message);
+        }
+    }
+
+    public static void i(String message) {
+        if(isLog){
+            Log.i(TAG, message);
+        }
+
+    }
+
+    public static void e(String msg) {
+        if (isLog) {
+            Log.e(TAG, msg);
+        }
+    }
+
+    public static void e(String message, Throwable throwable) {
+        if(isLog){
+            Log.e(TAG, message, throwable);
+        }
+    }
+
+}

+ 416 - 0
MusicPlayer/src/main/java/com/yc/music/utils/MusicSpUtils.java

@@ -0,0 +1,416 @@
+package com.yc.music.utils;
+
+import android.annotation.SuppressLint;
+import android.app.Application;
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import androidx.annotation.NonNull;
+import androidx.collection.SimpleArrayMap;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+
+@SuppressLint("ApplySharedPref")
+public final class MusicSpUtils {
+
+    private static SimpleArrayMap<String, MusicSpUtils> SP_UTILS_MAP = new SimpleArrayMap<>();
+    private SharedPreferences sp;
+    private static Context context;
+
+    public static void init(Application application){
+        context = application;
+    }
+
+    /**
+     * 获取 SP 实例
+     *
+     * @return {@link MusicSpUtils}
+     */
+    public static MusicSpUtils getInstance() {
+        return getInstance("");
+    }
+
+    /**
+     * 获取 SP 实例
+     *
+     * @param spName sp 名
+     * @return {@link MusicSpUtils}
+     */
+    public static MusicSpUtils getInstance(String spName) {
+        if (isSpace(spName)) spName = "spUtils";
+        MusicSpUtils spUtils = SP_UTILS_MAP.get(spName);
+        if (spUtils == null) {
+            spUtils = new MusicSpUtils(spName);
+            SP_UTILS_MAP.put(spName, spUtils);
+        }
+        return spUtils;
+    }
+
+    private MusicSpUtils(final String spName) {
+        sp = context.getSharedPreferences(spName, Context.MODE_PRIVATE);
+    }
+
+    /**
+     * SP 中写入 String
+     *
+     * @param key   键
+     * @param value 值
+     */
+    public void put(@NonNull final String key, @NonNull final String value) {
+        put(key, value, false);
+    }
+
+    /**
+     * SP 中写入 String
+     *
+     * @param key      键
+     * @param value    值
+     * @param isCommit {@code true}: {@link SharedPreferences.Editor#commit()}<br>
+     *                 {@code false}: {@link SharedPreferences.Editor#apply()}
+     */
+    public void put(@NonNull final String key,
+                    @NonNull final String value,
+                    final boolean isCommit) {
+        if (isCommit) {
+            sp.edit().putString(key, value).commit();
+        } else {
+            sp.edit().putString(key, value).apply();
+        }
+    }
+
+    /**
+     * SP 中读取 String
+     *
+     * @param key 键
+     * @return 存在返回对应值,不存在返回默认值{@code ""}
+     */
+    public String getString(@NonNull final String key) {
+        return getString(key, "");
+    }
+
+    /**
+     * SP 中读取 String
+     *
+     * @param key          键
+     * @param defaultValue 默认值
+     * @return 存在返回对应值,不存在返回默认值{@code defaultValue}
+     */
+    public String getString(@NonNull final String key, @NonNull final String defaultValue) {
+        return sp.getString(key, defaultValue);
+    }
+
+    /**
+     * SP 中写入 int
+     *
+     * @param key   键
+     * @param value 值
+     */
+    public void put(@NonNull final String key, final int value) {
+        put(key, value, false);
+    }
+
+    /**
+     * SP 中写入 int
+     *
+     * @param key      键
+     * @param value    值
+     * @param isCommit {@code true}: {@link SharedPreferences.Editor#commit()}<br>
+     *                 {@code false}: {@link SharedPreferences.Editor#apply()}
+     */
+    public void put(@NonNull final String key, final int value, final boolean isCommit) {
+        if (isCommit) {
+            sp.edit().putInt(key, value).commit();
+        } else {
+            sp.edit().putInt(key, value).apply();
+        }
+    }
+
+    /**
+     * SP 中读取 int
+     *
+     * @param key 键
+     * @return 存在返回对应值,不存在返回默认值-1
+     */
+    public int getInt(@NonNull final String key) {
+        return getInt(key, -1);
+    }
+
+    /**
+     * SP 中读取 int
+     *
+     * @param key          键
+     * @param defaultValue 默认值
+     * @return 存在返回对应值,不存在返回默认值{@code defaultValue}
+     */
+    public int getInt(@NonNull final String key, final int defaultValue) {
+        return sp.getInt(key, defaultValue);
+    }
+
+    /**
+     * SP 中写入 long
+     *
+     * @param key   键
+     * @param value 值
+     */
+    public void put(@NonNull final String key, final long value) {
+        put(key, value, false);
+    }
+
+    /**
+     * SP 中写入 long
+     *
+     * @param key      键
+     * @param value    值
+     * @param isCommit {@code true}: {@link SharedPreferences.Editor#commit()}<br>
+     *                 {@code false}: {@link SharedPreferences.Editor#apply()}
+     */
+    public void put(@NonNull final String key, final long value, final boolean isCommit) {
+        if (isCommit) {
+            sp.edit().putLong(key, value).commit();
+        } else {
+            sp.edit().putLong(key, value).apply();
+        }
+    }
+
+    /**
+     * SP 中读取 long
+     *
+     * @param key 键
+     * @return 存在返回对应值,不存在返回默认值-1
+     */
+    public long getLong(@NonNull final String key) {
+        return getLong(key, -1L);
+    }
+
+    /**
+     * SP 中读取 long
+     *
+     * @param key          键
+     * @param defaultValue 默认值
+     * @return 存在返回对应值,不存在返回默认值{@code defaultValue}
+     */
+    public long getLong(@NonNull final String key, final long defaultValue) {
+        return sp.getLong(key, defaultValue);
+    }
+
+    /**
+     * SP 中写入 float
+     *
+     * @param key   键
+     * @param value 值
+     */
+    public void put(@NonNull final String key, final float value) {
+        put(key, value, false);
+    }
+
+    /**
+     * SP 中写入 float
+     *
+     * @param key      键
+     * @param value    值
+     * @param isCommit {@code true}: {@link SharedPreferences.Editor#commit()}<br>
+     *                 {@code false}: {@link SharedPreferences.Editor#apply()}
+     */
+    public void put(@NonNull final String key, final float value, final boolean isCommit) {
+        if (isCommit) {
+            sp.edit().putFloat(key, value).commit();
+        } else {
+            sp.edit().putFloat(key, value).apply();
+        }
+    }
+
+    /**
+     * SP 中读取 float
+     *
+     * @param key 键
+     * @return 存在返回对应值,不存在返回默认值-1
+     */
+    public float getFloat(@NonNull final String key) {
+        return getFloat(key, -1f);
+    }
+
+    /**
+     * SP 中读取 float
+     *
+     * @param key          键
+     * @param defaultValue 默认值
+     * @return 存在返回对应值,不存在返回默认值{@code defaultValue}
+     */
+    public float getFloat(@NonNull final String key, final float defaultValue) {
+        return sp.getFloat(key, defaultValue);
+    }
+
+    /**
+     * SP 中写入 boolean
+     *
+     * @param key   键
+     * @param value 值
+     */
+    public void put(@NonNull final String key, final boolean value) {
+        put(key, value, false);
+    }
+
+    /**
+     * SP 中写入 boolean
+     *
+     * @param key      键
+     * @param value    值
+     * @param isCommit {@code true}: {@link SharedPreferences.Editor#commit()}<br>
+     *                 {@code false}: {@link SharedPreferences.Editor#apply()}
+     */
+    public void put(@NonNull final String key, final boolean value, final boolean isCommit) {
+        if (isCommit) {
+            sp.edit().putBoolean(key, value).commit();
+        } else {
+            sp.edit().putBoolean(key, value).apply();
+        }
+    }
+
+    /**
+     * SP 中读取 boolean
+     *
+     * @param key 键
+     * @return 存在返回对应值,不存在返回默认值{@code false}
+     */
+    public boolean getBoolean(@NonNull final String key) {
+        return getBoolean(key, false);
+    }
+
+    /**
+     * SP 中读取 boolean
+     *
+     * @param key          键
+     * @param defaultValue 默认值
+     * @return 存在返回对应值,不存在返回默认值{@code defaultValue}
+     */
+    public boolean getBoolean(@NonNull final String key, final boolean defaultValue) {
+        return sp.getBoolean(key, defaultValue);
+    }
+
+    /**
+     * SP 中写入 String 集合
+     *
+     * @param key    键
+     * @param values 值
+     */
+    public void put(@NonNull final String key, @NonNull final Set<String> values) {
+        put(key, values, false);
+    }
+
+    /**
+     * SP 中写入 String 集合
+     *
+     * @param key      键
+     * @param values   值
+     * @param isCommit {@code true}: {@link SharedPreferences.Editor#commit()}<br>
+     *                 {@code false}: {@link SharedPreferences.Editor#apply()}
+     */
+    public void put(@NonNull final String key,
+                    @NonNull final Set<String> values,
+                    final boolean isCommit) {
+        if (isCommit) {
+            sp.edit().putStringSet(key, values).commit();
+        } else {
+            sp.edit().putStringSet(key, values).apply();
+        }
+    }
+
+    /**
+     * SP 中读取 StringSet
+     *
+     * @param key 键
+     * @return 存在返回对应值,不存在返回默认值{@code Collections.<String>emptySet()}
+     */
+    public Set<String> getStringSet(@NonNull final String key) {
+        return getStringSet(key, Collections.<String>emptySet());
+    }
+
+    /**
+     * SP 中读取 StringSet
+     *
+     * @param key          键
+     * @param defaultValue 默认值
+     * @return 存在返回对应值,不存在返回默认值{@code defaultValue}
+     */
+    public Set<String> getStringSet(@NonNull final String key,
+                                    @NonNull final Set<String> defaultValue) {
+        return sp.getStringSet(key, defaultValue);
+    }
+
+    /**
+     * SP 中获取所有键值对
+     *
+     * @return Map 对象
+     */
+    public Map<String, ?> getAll() {
+        return sp.getAll();
+    }
+
+    /**
+     * SP 中是否存在该 key
+     *
+     * @param key 键
+     * @return {@code true}: 存在<br>{@code false}: 不存在
+     */
+    public boolean contains(@NonNull final String key) {
+        return sp.contains(key);
+    }
+
+    /**
+     * SP 中移除该 key
+     *
+     * @param key 键
+     */
+    public void remove(@NonNull final String key) {
+        remove(key, false);
+    }
+
+    /**
+     * SP 中移除该 key
+     *
+     * @param key      键
+     * @param isCommit {@code true}: {@link SharedPreferences.Editor#commit()}<br>
+     *                 {@code false}: {@link SharedPreferences.Editor#apply()}
+     */
+    public void remove(@NonNull final String key, final boolean isCommit) {
+        if (isCommit) {
+            sp.edit().remove(key).commit();
+        } else {
+            sp.edit().remove(key).apply();
+        }
+    }
+
+    /**
+     * SP 中清除所有数据
+     */
+    public void clear() {
+        clear(false);
+    }
+
+    /**
+     * SP 中清除所有数据
+     *
+     * @param isCommit {@code true}: {@link SharedPreferences.Editor#commit()}<br>
+     *                 {@code false}: {@link SharedPreferences.Editor#apply()}
+     */
+    public void clear(final boolean isCommit) {
+        if (isCommit) {
+            sp.edit().clear().commit();
+        } else {
+            sp.edit().clear().apply();
+        }
+    }
+
+    private static boolean isSpace(final String s) {
+        if (s == null) return true;
+        for (int i = 0, len = s.length(); i < len; ++i) {
+            if (!Character.isWhitespace(s.charAt(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+}

+ 158 - 0
MusicPlayer/src/main/java/com/yc/music/utils/NotificationHelper.java

@@ -0,0 +1,158 @@
+package com.yc.music.utils;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.widget.RemoteViews;
+
+import com.yc.music.R;
+import com.yc.music.model.AudioBean;
+import com.yc.music.receiver.NotificationStatusBarReceiver;
+import com.yc.music.service.PlayService;
+
+
+public class NotificationHelper {
+
+
+    private PlayService playService;
+    private NotificationManager notificationManager;
+    private static final int NOTIFICATION_ID = 0x111;
+
+    public static NotificationHelper get() {
+        return SingletonHolder.instance;
+    }
+
+    private static class SingletonHolder {
+        private static NotificationHelper instance = new NotificationHelper();
+    }
+
+    private NotificationHelper() {
+
+    }
+
+    /**
+     * 1.创建一个NotificationManager的引用
+     * @param playService           PlayService对象
+     */
+    public void init(PlayService playService) {
+        this.playService = playService;
+        notificationManager = (NotificationManager) playService.getSystemService(Context.NOTIFICATION_SERVICE);
+    }
+
+    /**
+     * 开始播放
+     * @param music             music
+     */
+    public void showPlay(AudioBean music) {
+        if (music == null) {
+            return;
+        }
+        playService.startForeground(NOTIFICATION_ID, buildNotification(playService, music, true));
+        //这个方法是启动Notification到前台
+    }
+
+
+    /**
+     * 暂停
+     * @param music             music
+     */
+    public void showPause(AudioBean music) {
+        //这个方法是停止Notification
+        if (music == null) {
+            return;
+        }
+        playService.stopForeground(false);
+        notificationManager.notify(NOTIFICATION_ID, buildNotification(playService, music, false));
+    }
+
+
+    /**
+     * 结束所有的
+     */
+    public void cancelAll() {
+        notificationManager.cancelAll();
+    }
+
+    private Notification buildNotification(Context context, AudioBean music, boolean isPlaying) {
+//        Intent intent = new Intent(context, MusicActivity.class);
+//        intent.putExtra(Constant.EXTRA_NOTIFICATION, true);
+//        intent.setAction(Intent.ACTION_VIEW);
+//        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+//        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+//        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+//        PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+
+
+        NotificationUtils.isVibration = false;
+        NotificationUtils notificationUtils = new NotificationUtils(context);
+        notificationUtils
+//                .setContentIntent(pendingIntent)
+                .setPriority(Notification.PRIORITY_DEFAULT)
+                .setTicker("叮咚音乐")
+                .setContent(getCustomViews(context, music, isPlaying))
+                .setOngoing(true);
+        Notification notification = notificationUtils.getNotification(music.getTitle(), music.getArtist(), R.drawable.default_cover);
+        return notification;
+    }
+
+
+    /**
+     * 设置自定义通知栏布局
+     * @param context                   上下文
+     * @param music
+     * @return                          RemoteViews
+     */
+    private RemoteViews getCustomViews(Context context, AudioBean music, boolean isPlaying) {
+        String title = music.getTitle();
+        String subtitle = "";
+        Bitmap cover = null;
+
+        RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.notification_player);
+        if (cover != null) {
+            remoteViews.setImageViewBitmap(R.id.iv_image, cover);
+        } else {
+            remoteViews.setImageViewResource(R.id.iv_image, R.drawable.default_cover);
+        }
+        remoteViews.setTextViewText(R.id.tv_title, title);
+        remoteViews.setTextViewText(R.id.tv_artist, subtitle);
+        if(isPlaying){
+            remoteViews.setImageViewResource(R.id.btn_start,R.drawable.notify_btn_dark_pause_normal);
+        }else {
+            remoteViews.setImageViewResource(R.id.btn_start,R.drawable.notify_btn_dark_play_normal);
+        }
+
+        // 设置 点击通知栏的上一首按钮时要执行的意图
+//        remoteViews.setOnClickPendingIntent(R.id.btn_pre, getReceiverPendingIntent(context, MusicPlayAction.TYPE_PRE,1));
+//        // 设置 点击通知栏的下一首按钮时要执行的意图
+//        remoteViews.setOnClickPendingIntent(R.id.btn_next, getReceiverPendingIntent(context, MusicPlayAction.TYPE_NEXT,2));
+//        // 设置 点击通知栏的播放暂停按钮时要执行的意图
+//        remoteViews.setOnClickPendingIntent(R.id.btn_start, getReceiverPendingIntent(context, MusicPlayAction.TYPE_START_PAUSE,3));
+//        // 设置 点击通知栏的根容器时要执行的意图
+//        remoteViews.setOnClickPendingIntent(R.id.ll_root, getActivityPendingIntent(context));
+        return remoteViews;
+    }
+
+
+    private PendingIntent getActivityPendingIntent(Context context) {
+        Intent intent = new Intent(context, null);
+//        Intent intent = new Intent(context, MusicActivity.class);
+//        intent.putExtra(Constant.EXTRA_NOTIFICATION, true);
+//        intent.setAction(Intent.ACTION_VIEW);
+//        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+//        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+//        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+    }
+
+
+    private PendingIntent getReceiverPendingIntent(Context context, String type , int code) {
+        Intent intent = new Intent(NotificationStatusBarReceiver.ACTION_STATUS_BAR);
+        intent.putExtra(NotificationStatusBarReceiver.EXTRA, type);
+        return PendingIntent.getBroadcast(context, code, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+    }
+
+
+}

+ 372 - 0
MusicPlayer/src/main/java/com/yc/music/utils/NotificationUtils.java

@@ -0,0 +1,372 @@
+package com.yc.music.utils;
+
+import android.annotation.TargetApi;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.graphics.Color;
+import android.net.Uri;
+import android.os.Build;
+import android.widget.RemoteViews;
+
+import androidx.annotation.RequiresApi;
+import androidx.core.app.NotificationCompat;
+
+import static androidx.core.app.NotificationCompat.PRIORITY_DEFAULT;
+import static androidx.core.app.NotificationCompat.VISIBILITY_SECRET;
+
+/**
+ * <pre>
+ *     @author yangchong
+ *     blog  : https://www.jianshu.com/p/514eb6193a06
+ *     time  : 2018/2/10
+ *     desc  : 通知栏工具类
+ *     revise:
+ * </pre>
+ */
+public class NotificationUtils extends ContextWrapper {
+
+
+    public static final String CHANNEL_ID = "default";
+    private static final String CHANNEL_NAME = "Default_Channel";
+    public static boolean isVibration = false;
+    private NotificationManager mManager;
+    private int[] flags;
+
+    public NotificationUtils(Context base) {
+        super(base);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            //android 8.0以上需要特殊处理,也就是targetSDKVersion为26以上
+            createNotificationChannel();
+        }
+    }
+
+    @TargetApi(Build.VERSION_CODES.O)
+    private void createNotificationChannel() {
+        //第一个参数:channel_id
+        //第二个参数:channel_name
+        //第三个参数:设置通知重要性级别
+        //注意:该级别必须要在 NotificationChannel 的构造函数中指定,总共要五个级别;
+        //范围是从 NotificationManager.IMPORTANCE_NONE(0) ~ NotificationManager.IMPORTANCE_HIGH(4)
+        NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME,
+                NotificationManager.IMPORTANCE_DEFAULT);
+        channel.canBypassDnd();//是否绕过请勿打扰模式
+        channel.enableLights(true);//闪光灯
+        channel.setLockscreenVisibility(VISIBILITY_SECRET);//锁屏显示通知
+        channel.setLightColor(Color.RED);//闪关灯的灯光颜色
+        channel.canShowBadge();//桌面launcher的消息角标
+        channel.enableVibration(isVibration);//是否允许震动
+        channel.getAudioAttributes();//获取系统通知响铃声音的配置
+        channel.getGroup();//获取通知取到组
+        channel.setBypassDnd(true);//设置可绕过 请勿打扰模式
+        channel.setVibrationPattern(new long[]{100, 100, 200});//设置震动模式
+        channel.shouldShowLights();//是否会有灯光
+        getManager().createNotificationChannel(channel);
+    }
+
+    /**
+     * 获取创建一个NotificationManager的对象
+     * @return                          NotificationManager对象
+     */
+    public NotificationManager getManager() {
+        if (mManager == null) {
+            mManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+        }
+        return mManager;
+    }
+
+    /**
+     * 清空所有的通知
+     */
+    public void clearNotification(){
+        getManager().cancelAll();
+    }
+
+    /**
+     * 获取Notification
+     * @param title                     title
+     * @param content                   content
+     */
+    public Notification getNotification(String title, String content , int icon){
+        Notification build;
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            //android 8.0以上需要特殊处理,也就是targetSDKVersion为26以上
+            //通知用到NotificationCompat()这个V4库中的方法。但是在实际使用时发现书上的代码已经过时并且Android8.0已经不支持这种写法
+            Notification.Builder builder = getChannelNotification(title, content, icon);
+            build = builder.build();
+        } else {
+            NotificationCompat.Builder builder = getNotificationCompat(title, content, icon);
+            build = builder.build();
+        }
+        if (flags!=null && flags.length>0){
+            for (int a=0 ; a<flags.length ; a++){
+                build.flags |= flags[a];
+            }
+        }
+        return build;
+    }
+
+    /**
+     * 建议使用这个发送通知
+     * 调用该方法可以发送通知
+     * @param notifyId                  notifyId
+     * @param title                     title
+     * @param content                   content
+     */
+    public void sendNotification(int notifyId, String title, String content , int icon) {
+        Notification build;
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            //android 8.0以上需要特殊处理,也就是targetSDKVersion为26以上
+            //通知用到NotificationCompat()这个V4库中的方法。但是在实际使用时发现书上的代码已经过时并且Android8.0已经不支持这种写法
+            Notification.Builder builder = getChannelNotification(title, content, icon);
+            build = builder.build();
+        } else {
+            NotificationCompat.Builder builder = getNotificationCompat(title, content, icon);
+            build = builder.build();
+        }
+        if (flags!=null && flags.length>0){
+            for (int a=0 ; a<flags.length ; a++){
+                build.flags |= flags[a];
+            }
+        }
+        getManager().notify(notifyId, build);
+    }
+
+    /**
+     * 调用该方法可以发送通知
+     * @param notifyId                  notifyId
+     * @param title                     title
+     * @param content                   content
+     */
+    public void sendNotificationCompat(int notifyId, String title, String content , int icon) {
+        NotificationCompat.Builder builder = getNotificationCompat(title, content, icon);
+        Notification build = builder.build();
+        if (flags!=null && flags.length>0){
+            for (int a=0 ; a<flags.length ; a++){
+                build.flags |= flags[a];
+            }
+        }
+        getManager().notify(notifyId, build);
+    }
+
+
+    private NotificationCompat.Builder getNotificationCompat(String title, String content, int icon) {
+        NotificationCompat.Builder builder;
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            builder = new NotificationCompat.Builder(getApplicationContext(), CHANNEL_ID);
+        } else {
+            //注意用下面这个方法,在8.0以上无法出现通知栏。8.0之前是正常的。这里需要增强判断逻辑
+            builder = new NotificationCompat.Builder(getApplicationContext());
+            builder.setPriority(PRIORITY_DEFAULT);
+        }
+        builder.setContentTitle(title);
+        builder.setContentText(content);
+        builder.setSmallIcon(icon);
+        builder.setPriority(priority);
+        builder.setOnlyAlertOnce(onlyAlertOnce);
+        builder.setOngoing(ongoing);
+        if (remoteViews!=null){
+            builder.setContent(remoteViews);
+        }
+        if (intent!=null){
+            builder.setContentIntent(intent);
+        }
+        if (ticker!=null && ticker.length()>0){
+            builder.setTicker(ticker);
+        }
+        if (when!=0){
+            builder.setWhen(when);
+        }
+        if (sound!=null){
+            builder.setSound(sound);
+        }
+        if (defaults!=0){
+            builder.setDefaults(defaults);
+        }
+        //点击自动删除通知
+        builder.setAutoCancel(true);
+        return builder;
+    }
+
+
+    @RequiresApi(api = Build.VERSION_CODES.O)
+    private Notification.Builder getChannelNotification(String title, String content, int icon){
+        Notification.Builder builder = new Notification.Builder(getApplicationContext(), CHANNEL_ID);
+        Notification.Builder notificationBuilder = builder
+                //设置标题
+                .setContentTitle(title)
+                //消息内容
+                .setContentText(content)
+                //设置通知的图标
+                .setSmallIcon(icon)
+                //让通知左右滑的时候是否可以取消通知
+                .setOngoing(ongoing)
+                //设置优先级
+                .setPriority(priority)
+                //是否提示一次.true - 如果Notification已经存在状态栏即使在调用notify函数也不会更新
+                .setOnlyAlertOnce(onlyAlertOnce)
+                .setAutoCancel(true);
+        if (remoteViews!=null){
+            //设置自定义view通知栏
+            notificationBuilder.setContent(remoteViews);
+        }
+        if (intent!=null){
+            notificationBuilder.setContentIntent(intent);
+        }
+        if (ticker!=null && ticker.length()>0){
+            //设置状态栏的标题
+            notificationBuilder.setTicker(ticker);
+        }
+        if (when!=0){
+            //设置通知时间,默认为系统发出通知的时间,通常不用设置
+            notificationBuilder.setWhen(when);
+        }
+        if (sound!=null){
+            //设置sound
+            notificationBuilder.setSound(sound);
+        }
+        if (defaults!=0){
+            //设置默认的提示音
+            notificationBuilder.setDefaults(defaults);
+        }
+        if (pattern!=null){
+            //自定义震动效果
+            notificationBuilder.setVibrate(pattern);
+        }
+        return notificationBuilder;
+    }
+
+
+
+    private boolean ongoing = false;
+    private RemoteViews remoteViews = null;
+    private PendingIntent intent = null;
+    private String ticker = "";
+    private int priority = Notification.PRIORITY_DEFAULT;
+    private boolean onlyAlertOnce = false;
+    private long when = 0;
+    private Uri sound = null;
+    private int defaults = 0;
+    private long[] pattern = null;
+
+    /**
+     * 让通知左右滑的时候是否可以取消通知
+     * @param ongoing                   是否可以取消通知
+     * @return
+     */
+    public NotificationUtils setOngoing(boolean ongoing){
+        this.ongoing = ongoing;
+        return this;
+    }
+
+    /**
+     * 设置自定义view通知栏布局
+     * @param remoteViews               view
+     * @return
+     */
+    public NotificationUtils setContent(RemoteViews remoteViews){
+        this.remoteViews = remoteViews;
+        return this;
+    }
+
+    /**
+     * 设置内容点击
+     * @param intent                    intent
+     * @return
+     */
+    public NotificationUtils setContentIntent(PendingIntent intent){
+        this.intent = intent;
+        return this;
+    }
+
+    /**
+     * 设置状态栏的标题
+     * @param ticker                    状态栏的标题
+     * @return
+     */
+    public NotificationUtils setTicker(String ticker){
+        this.ticker = ticker;
+        return this;
+    }
+
+
+    /**
+     * 设置优先级
+     * 注意:
+     * Android 8.0以及上,在 NotificationChannel 的构造函数中指定,总共要五个级别;
+     * Android 7.1(API 25)及以下的设备,还得调用NotificationCompat 的 setPriority方法来设置
+     *
+     * @param priority                  优先级,默认是Notification.PRIORITY_DEFAULT
+     * @return
+     */
+    public NotificationUtils setPriority(int priority){
+        this.priority = priority;
+        return this;
+    }
+
+    /**
+     * 是否提示一次.true - 如果Notification已经存在状态栏即使在调用notify函数也不会更新
+     * @param onlyAlertOnce             是否只提示一次,默认是false
+     * @return
+     */
+    public NotificationUtils setOnlyAlertOnce(boolean onlyAlertOnce){
+        this.onlyAlertOnce = onlyAlertOnce;
+        return this;
+    }
+
+    /**
+     * 设置通知时间,默认为系统发出通知的时间,通常不用设置
+     * @param when                      when
+     * @return
+     */
+    public NotificationUtils setWhen(long when){
+        this.when = when;
+        return this;
+    }
+
+    /**
+     * 设置sound
+     * @param sound                     sound
+     * @return
+     */
+    public NotificationUtils setSound(Uri sound){
+        this.sound = sound;
+        return this;
+    }
+
+
+    /**
+     * 设置默认的提示音
+     * @param defaults                  defaults
+     * @return
+     */
+    public NotificationUtils setDefaults(int defaults){
+        this.defaults = defaults;
+        return this;
+    }
+
+    /**
+     * 自定义震动效果
+     * @param pattern                  pattern
+     * @return
+     */
+    public NotificationUtils setVibrate(long[] pattern){
+        this.pattern = pattern;
+        return this;
+    }
+
+    /**
+     * 设置flag标签
+     * @param flags                     flags
+     * @return
+     */
+    public NotificationUtils setFlags(int... flags){
+        this.flags = flags;
+        return this;
+    }
+
+}
+

TEMPAT SAMPAH
MusicPlayer/src/main/res/drawable-xhdpi/default_cover.png


TEMPAT SAMPAH
MusicPlayer/src/main/res/drawable-xhdpi/notify_btn_dark_next_normal.png


TEMPAT SAMPAH
MusicPlayer/src/main/res/drawable-xhdpi/notify_btn_dark_pause2_normal.png


TEMPAT SAMPAH
MusicPlayer/src/main/res/drawable-xhdpi/notify_btn_dark_pause_normal.png


TEMPAT SAMPAH
MusicPlayer/src/main/res/drawable-xhdpi/notify_btn_dark_play_normal.png


TEMPAT SAMPAH
MusicPlayer/src/main/res/drawable-xhdpi/notify_btn_dark_prev_normal.png


+ 66 - 0
MusicPlayer/src/main/res/layout/notification_player.xml

@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="horizontal"
+    android:layout_width="match_parent"
+    android:id="@+id/ll_root"
+    android:layout_height="wrap_content"
+    android:gravity="center_vertical"
+    android:padding="10dp">
+
+    <ImageView
+        android:id="@+id/iv_image"
+        android:layout_width="50dp"
+        android:layout_height="50dp"
+        android:src="@drawable/default_cover"
+        android:scaleType="fitXY"/>
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="10dp"
+        android:layout_weight="1"
+        android:orientation="vertical">
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="这个是歌曲标题"
+            android:textSize="16sp"
+            android:singleLine="true"
+            android:maxLength="8"
+            android:ellipsize="end"
+            android:id="@+id/tv_title" />
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="8dp"
+            android:text="艺术家"
+            android:textSize="12sp"
+            android:maxEms="12"
+            android:singleLine="true"
+            android:ellipsize="end"
+            android:id="@+id/tv_artist" />
+    </LinearLayout>
+
+    <ImageView
+        android:layout_width="45dp"
+        android:layout_height="60dp"
+        android:src="@drawable/notify_btn_dark_prev_normal"
+        android:id="@+id/btn_pre" />
+
+    <ImageView
+        android:layout_width="60dp"
+        android:layout_height="60dp"
+        android:layout_marginStart="5dp"
+        android:layout_marginEnd="5dp"
+        android:src="@drawable/notify_btn_dark_pause_normal"
+        android:id="@+id/btn_start" />
+
+    <ImageView
+        android:layout_width="45dp"
+        android:layout_height="60dp"
+        android:layout_marginEnd="5dp"
+        android:src="@drawable/notify_btn_dark_next_normal"
+        android:id="@+id/btn_next" />
+
+</LinearLayout>

+ 4 - 0
MusicPlayer/src/main/res/values/colors.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+</resources>

+ 4 - 0
MusicPlayer/src/main/res/values/strings.xml

@@ -0,0 +1,4 @@
+<resources>
+    <string name="app_name">MusicPlayer</string>
+
+</resources>

+ 5 - 0
MusicPlayer/src/main/res/values/styles.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+
+</resources>

+ 5 - 25
VideoView/build.gradle

@@ -6,8 +6,8 @@ android {
     defaultConfig {
         minSdkVersion 17
         targetSdkVersion 29
-        versionCode 30
-        versionName "3.0.0"
+        versionCode 35
+        versionName "3.0.5"
     }
 
     buildTypes {
@@ -21,26 +21,6 @@ android {
 dependencies {
     implementation fileTree(dir: 'libs', include: ['*.jar'])
     implementation 'androidx.appcompat:appcompat:1.2.0'
-    implementation 'androidx.annotation:annotation:1.1.0'
-    implementation 'androidx.cardview:cardview:1.0.0'
-    implementation project(':VideoKernel')
-
-    /*//这两个是必须要加的,其它的可供选择
-    //ijk播放器
-    implementation 'tv.danmaku.ijk.media:ijkplayer-java:0.8.8'
-    implementation 'tv.danmaku.ijk.media:ijkplayer-armv7a:0.8.4'
-    //其他库文件
-    //implementation 'tv.danmaku.ijk.media:ijkplayer-armv5:0.8.8'
-    //implementation 'tv.danmaku.ijk.media:ijkplayer-arm64:0.8.8'
-    //implementation 'tv.danmaku.ijk.media:ijkplayer-x86:0.8.8'
-    //implementation 'tv.danmaku.ijk.media:ijkplayer-x86_64:0.8.8'
-    //谷歌播放器
-    implementation  "com.google.android.exoplayer:exoplayer:2.11.3"
-    implementation "com.google.android.exoplayer:exoplayer-core:2.11.3"
-    implementation "com.google.android.exoplayer:exoplayer-dash:2.11.3"
-    implementation "com.google.android.exoplayer:exoplayer-hls:2.11.3"
-    implementation "com.google.android.exoplayer:exoplayer-smoothstreaming:2.11.3"
-    implementation "com.google.android.exoplayer:extension-rtmp:2.11.3"*/
 }
 
 /** 以下开始是将Android Library上传到jcenter的相关配置**/
@@ -55,7 +35,7 @@ def gitUrl = 'https://github.com/yangchong211/YCVideoPlayer.git' // project git
 //发布到组织名称名字,必须填写
 group = "cn.yc"
 //发布到JCenter上的项目名字,必须填写
-def libName = "YCVideoViewLib"
+def libName = "VideoView"
 // 版本号,下次更新是只需要更改版本号即可
 version = "3.0.5"
 
@@ -101,7 +81,7 @@ install {
             project {
                 packaging 'aar'
                 //项目描述,自由填写
-                name 'This is videoPlayer lib'
+                name 'This is video float view lib'
                 url siteUrl
                 licenses {
                     license {
@@ -138,7 +118,7 @@ bintray {
     pkg {
         repo = "maven"
         name = libName    //发布到JCenter上的项目名字,必须填写
-        desc = 'android videoPlayer'    //项目描述
+        desc = 'android video float view'    //项目描述
         websiteUrl = siteUrl
         vcsUrl = gitUrl
         licenses = ["Apache-2.0"]

+ 3 - 1
VideoView/src/main/AndroidManifest.xml

@@ -1,5 +1,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.yc.videoview">
 
-    /
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+
+
 </manifest>

+ 1 - 1
VideoPlayer/src/main/java/com/yc/video/ui/window/FloatLifecycle.java → VideoView/src/main/java/com/yc/videoview/FloatLifecycle.java

@@ -1,4 +1,4 @@
-package com.yc.video.ui.window;
+package com.yc.videoview;
 
 import android.app.Activity;
 import android.app.Application;

+ 1 - 1
VideoPlayer/src/main/java/com/yc/video/ui/window/FloatPhone.java → VideoView/src/main/java/com/yc/videoview/FloatPhone.java

@@ -1,4 +1,4 @@
-package com.yc.video.ui.window;
+package com.yc.videoview;
 
 import android.content.Context;
 import android.graphics.PixelFormat;

+ 1 - 1
VideoPlayer/src/main/java/com/yc/video/ui/window/FloatToast.java → VideoView/src/main/java/com/yc/videoview/FloatToast.java

@@ -1,4 +1,4 @@
-package com.yc.video.ui.window;
+package com.yc.videoview;
 
 import android.content.Context;
 import android.view.View;

+ 1 - 1
VideoPlayer/src/main/java/com/yc/video/ui/window/FloatView.java → VideoView/src/main/java/com/yc/videoview/FloatView.java

@@ -1,4 +1,4 @@
-package com.yc.video.ui.window;
+package com.yc.videoview;
 
 import android.view.View;
 

+ 5 - 5
VideoPlayer/src/main/java/com/yc/video/ui/window/FloatWindow.java → VideoView/src/main/java/com/yc/videoview/FloatWindow.java

@@ -1,16 +1,16 @@
-package com.yc.video.ui.window;
+package com.yc.videoview;
 
 import android.animation.TimeInterpolator;
 import android.content.Context;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
 
 import androidx.annotation.LayoutRes;
 import androidx.annotation.MainThread;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
 
 import java.util.HashMap;
 import java.util.Map;

+ 1 - 1
VideoPlayer/src/main/java/com/yc/video/ui/window/IFloatWindow.java → VideoView/src/main/java/com/yc/videoview/IFloatWindow.java

@@ -1,4 +1,4 @@
-package com.yc.video.ui.window;
+package com.yc.videoview;
 
 import android.view.View;
 

+ 1 - 1
VideoPlayer/src/main/java/com/yc/video/ui/window/IFloatWindowImpl.java → VideoView/src/main/java/com/yc/videoview/IFloatWindowImpl.java

@@ -1,4 +1,4 @@
-package com.yc.video.ui.window;
+package com.yc.videoview;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;

+ 1 - 1
VideoPlayer/src/main/java/com/yc/video/ui/window/LifecycleListener.java → VideoView/src/main/java/com/yc/videoview/LifecycleListener.java

@@ -1,4 +1,4 @@
-package com.yc.video.ui.window;
+package com.yc.videoview;
 
 
 interface LifecycleListener {

+ 1 - 1
VideoPlayer/src/main/java/com/yc/video/ui/window/MoveType.java → VideoView/src/main/java/com/yc/videoview/MoveType.java

@@ -1,4 +1,4 @@
-package com.yc.video.ui.window;
+package com.yc.videoview;
 
 
 import androidx.annotation.IntDef;

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini