- 在锁屏时,播放一段时间音乐后,无法及时播放下一首,需等待一段时间或点亮屏幕
- 添加日志记录
- 播放控制窗口歌曲名称处有不应该出现的滚动条
- 大的播放小窗口,显示更大的封面图和更多信息
- 安卓系统检测到文件移动时不会改变歌曲 id,导致歌曲移动后仍会在原来的歌单中显示
- 歌曲播放时,如果将音量调至最低则自动暂停播放,此时如果调高音量则自动继续播放
- 在歌单歌曲页未多选的情况下点击 Music 底部导航,则退回歌单列表
- 在歌曲播放列表页点击 Song 底部导航,画面自动滚动到正在播放的歌曲的位置
- 实现播放睡眠计时功能
- 实现随机播放功能
- Suspend 解决歌单中歌曲顺序不按添加顺序显示的问题,SQL 默认查询的顺序是 unstable 的,需要额外记录顺序
- 添加设置功能及其页面
- Suspend 细化数据库查询得到的数据类型
- 分析音频转发的高延迟问题
- Fine-Tuning 对于迷你播放窗口,优先启动窗口,将读取音乐文件信息延后到下一次循环
- Fix 播放列表结束停止后,再次进入回重新触发播放(此时不知为何会再次收到 STOP 事件,导致 Player 触发播放完成事件并进而触发播放下一首事件)
- Fine-Tuning 将 DAO 的 Flow 改为 LiveData
- Feature 为播放队列添加显示当前播放歌曲的功能
- Fine-Tuning 将 SelectableItemAdapter 改为继承自 ItemAdapter
- Fix 解决 Service 没有被绑定会自己 Destroy 的问题(既如果启动应用,播放音乐,当回到主屏幕,此时 MainActivity 进入 onStop 状态,断开与服务的连接,且其他程序会系统没有连接服务,服务会触发 Destroy。)
- Feature 了解 NFC 的使用
- Fine-Tuning 删除了数据库的索引
- Feature 在播放队列中点击正在播放的歌曲为暂停和继续
- Feature 扫描歌曲后显示新增歌删除的歌曲数
- Feature 为播放队列拖拽调整顺序功能
- Feature 了解 AudioPlaybackCapture 的使用
- Feature 了解蓝牙使用
- Feature 实现音频转发功能
- Feature 在歌单中点击歌曲可预览歌曲
- Fine-Tuning 将 ViewHolder 从 Adapter 中抽离出来
- Feature 为当前播放歌曲提供播放控制页(复用预览歌曲的控制对话框)
- Feature 为播放列表设计删除功能(从左向右滑动删除)
- Fix 播放列表长按但不调整位置会出现读取 -1 项的情况
- Feature 为歌单歌曲列表页 FAB 添加播放歌单功能
- Feature 如果当前播放列表自从载入后没有修改过(删除添加重排)就不触发自动保存
- Fine-Tuning 使用 AAC-LC 传输音频数据
- Fine-Tuning 使用 ConcurrentLinkedQueue 替代 CircularArray
- Fix 下一首或上一首会异常跳歌,这个问题只会在长时间运行或多次切歌后触发。原因是播放下一首歌的 Prepare 时未清除定时器,而播放时又会额外添加一次定时器,定时器在 Preparing 阶段尝试获取播放进度而导致 MediaPlayer 状态错误而播放失败,直接歌曲播放结束触发跳转下一首。
- Fine-Tuning 使用 SingleLiveEvent 管理播放控制事件,由 PlaybackQueueViewModel 管理播放控制细节逻辑。
- Feature 播放列表页无播放歌曲时 FAB 为开始播放按钮
- Feature 歌单歌曲页多选时 FAB 为添加至播放列表
- Feature 添加播放列表循环播放设置
- Feature 播放列表播放结束后播放控制页自动消失
- Feature 支持当播放列表中每首歌恰好被播放一遍后停止播放(目前仅支持顺序播放)
比起手工建,更适合我的使用场景。
当你恰好想听 A 手机上的歌,而又因种种原因不能在 A 手机上放(譬如没有 3.5 毫米耳机接口),这时就可以用音乐转发功能,使用 B 手机听 A 手机上的歌(当然不能是单纯的发文件)。
预想方案:使用 NFC 配对,通过蓝牙或网络建立连接,通过 MediaRecorder
获取设备音频或者直接发送歌曲音频数据,由对方程序接受播放(B 手机可以选择同步播放或不播放(如果可能的实现的话(好像是可以的,我发现录屏时即使不开声音也能录进声音)))。此时可以由 B 手机接管 A 手机的音量控制,如果恰好的也是用此软件播放的,可以接管全部播放控制(类似于 remote develop 的体验)。如果 A 手机当前媒体暴露了 MediaBrowserService
则 B 手机可以转发 MediaButton
到 A 手机。
安卓系统不允许应用程序获取本机蓝牙 MAC 地址,但可以扫描获取其他手机的 MAC 地址。所以使用 NFC 快速配对的实现还是有点麻烦的,有几个想法:
- 第一次连接后通过另一部手机给出本机的 MAC 地址,并记录,之后就可以快速配对了(但是不能保证安卓是不是有匿名蓝牙 MAC 的设计)
- 修改蓝牙名称,查找特定名称的蓝牙,连接后恢复名称
经过查阅资料后发现只能通过安卓 10 引入的 AudioPlaybackCapture API 才能录制手机内部音频,而且可以被目标应用阻止。虽然有很多限制,但还是只能选择这一 API 来实现。
现在音频延迟高达 1 秒以上,除部分场景外基本不可接收。
使用 AAC-LC 传输音频后,虽然延迟还是很高,但是音质有了明显的进步。但是 AAC 数据在传至 MediaCodec 前需要拆分回帧,我预留了两个接收的按钮,图标为警告标志的会开辟一个 128 MB 的数据池用于处理原始 AAC 音频数据,不容易因为数据处理而造成音频的卡顿,但是一旦 128 MB 用完后将无法继续处理数据。
常常会发生睡眠计时结束但还没睡着的情况,这时候如果亮屏去继续播放,就要被亮瞎。所以如果在睡眠计时结束后,音量被调节(不用眼镜和亮屏就可以完成),就会重新开始一次睡眠计时,但是计时的时间会一定程度的减少。
将当前歌曲加入播放列表,保证一定下一首播放,播放后自动从播放列表中移除。
如果不是临时暂停再播放,我肯定更想从一首完整的歌开始播放。
如果当前播放列表被程序逻辑删除(比如播放一个歌单)而不是用户手动删除,会自动保存为歌单。
我看见一首歌没听过,我点一下是只想听这首歌而是整个歌单或搜索结果(还会把我的播放列表给一起弄没了),即使是只把那首添加进播放列表也很麻烦。
强化底部 BottomNavigationView 和 FAB 的功能, FAB 可根据重力感应置与左侧或右侧。仅可能保证基本的操作都可以在单手范围内完成。
UI 由一个主 Activity 和一堆 Fragment 组成。对于组件间的交互逻辑按照以下几条规则执行。
- 由 Fragment/Activity 内部触发的且 Fragment/Activity 自身可以完成的(包括交由子组件)交互由 Fragment/Activity 自行处理。
- 由 Fragment 内部触发的但自身无法完成的交互由 ViewModel 处理。(Fragment 发送数据或调用函数,ViewModel 做数据处理,Activity 或其他 Fragment observe 和执行)。
- 由 Activity 内部触发的需要由当前显示的 Fragment 处理的(主要是 App Bar 的返回和菜单点击以及 Bottom Navigation View 的 Reselect 和 FAB 点击这四个事件),通过
IHandleBackPress
、IhandleMenuItemClick
和IHandleNavigtionReselect
等接口将事件向下传递。
歌单歌曲列表页多选 FAB,可为加入播放队列或添加到其他歌单
歌单歌曲列表页点击歌曲,可为预览歌曲或载入歌单并播放此项
ViewPager 滑动切换,可为关闭或开启
文件夹自动生成的歌单是否包含子文件夹歌曲,可为包是或否
包括歌单列表和歌单中歌曲列表两个页面。歌单列表为默认显示页。
后退按钮 | 菜单内容 | FAB | 标题 | 副标题 | |
---|---|---|---|---|---|
歌单列表页 | 无 | 无 | 新增歌单 | 程序名 | 无 |
歌单列表页多选 | 取消选择 | 删除歌单(由文件夹建立的歌单是无法被删除的) | 无 | 程序名 | 无 |
歌单歌曲列表页 | 返回歌单列表页 | 删除歌单(若为文件夹建立的歌单则无菜单内容) | 播放歌单 | 歌单名称 | 歌单歌曲数 |
歌单歌曲列表页多选 | 取消多选 | 全选、加入播放队列、添加到其他歌单、从歌单中移除(若为文件夹建立的歌单则无此项) | 无 | 歌单名称 | 选择歌曲数 |
显示当前播放歌曲页和播放列表,由于实现一个优雅的 Bottom Sheet 控制栏较为困难,所以放弃实现。
对于播放列表在数据库中的存储方式,采用 https://stackoverflow.com/a/39871377/13151333 的方法。
目前歌曲页内容仅为播放队列。
后退按钮 | 菜单内容 | FAB | 标题 | 副标题 | |
---|---|---|---|---|---|
歌曲页 | 无 | 保存到歌单、清空播放列表 | 显示当前歌曲的播放控制 | 程序名 | 无 |