今日任务
- PlayController 分析
Que 0x01 PlayController 声明
public class PlayController implements OnMediaChangeObserver, com.samsung.android.app.music.core.service.mediacenter.OnMediaChangeObserver
从上面可以看出 PlayController 实现了 OnMediaChangeObserver
和 com.samsung.android.app.music.core.service.mediacenter.OnMediaChangeObserver
中的接口,所以这里先看看定义了哪些接口。
public interface OnMediaChangeObserver {
void onExtraChanged(String str, Bundle bundle);
void onMetaChanged(Meta meta, PlayState playState);
void onPlayStateChanged(PlayState playState);
}
public interface OnMediaChangeObserver {
void onExtrasChanged(String str, Bundle bundle);
void onMetadataChanged(MusicMetadata musicMetadata);
void onPlaybackStateChanged(MusicPlaybackState musicPlaybackState);
void onQueueChanged(List list, Bundle bundle);
}
这里先看看几个接口的实现。
Que 0x02 onExtraChanged
实现代码如下:
public void onExtraChanged(String action, Bundle data) {
if ("com.samsung.musicplus.action.DRM_REQUEST".equals(action)) {
updatePlayState(this.mPlayerController.isPlaying());
}
}
从字面上来看,这个接口的功能是:判断当播放器处于 DRM_REQUEST
状态的时候,更新播放器状态。其中最为核心的方法是 updatePlayState
updatePlayState
private void updatePlayState(boolean isPlaying) {
TalkBackUtils.setContentDescriptionAll(this.mContext, this.mPlay, isPlaying ? R.string.tts_pause : R.string.tts_play);
if (this.mPlay.isActivated() != isPlaying) {
this.mPlay.setActivated(isPlaying);
if (this.mPlayToPauseAnimationResId != -1 && this.mPauseToPlayAnimationResId != -1) {
ImageView iv = (ImageView) this.mPlay.findViewById(R.id.play_pause_icon);
if (isPlaying) {
iv.setImageResource(this.mPlayToPauseAnimationResId);
} else {
iv.setImageResource(this.mPauseToPlayAnimationResId);
}
AnimationDrawable d = (AnimationDrawable) iv.getDrawable();
if (this.mPlay.isLaidOut()) {
d.start();
} else {
iv.setImageDrawable(d.getFrame(d.getNumberOfFrames() - 1));
}
}
}
}
TalkBackUtils.setContentDescriptionAll
public static void setContentDescriptionAll(Context context, View v, int stringResId) {
v.setContentDescription(getButtonDescription(context, stringResId));
if (DefaultUiUtils.isHoverUiEnabled(context)) {
HoverPopupWindowCompat.setContent(v, context.getString(stringResId));
}
}
这里 v 是 view,Android 中的一种基础控件,具体是什么暂时不明,setContentDescription
的功能是设置 View 的备注说明,作为一种辅助功能提供,为一些没有文字描述的View提供说明 ;最终经过分析,这里主要实现功能如下:
- 设置 View 的备注说明
- 判断是否支持某种和 UI 相关的状态,如果满足则设置 content,并且在设置 content 中也会判断是否支持三星 SEP
- content 是用来访问全局信息的接口
综上
-
updatePlayState
的功能是更新播放 - 暂停
按钮的状态 -
onExtraChanged
的功能是当收到com.samsung.musicplus.action.DRM_REQUEST
请求的时候,调用updatePlayState
Que 0x03 onMetaChanged
实现代码如下:
public void onMetaChanged(Meta m, PlayState s) {
updatePlayState(s.isPlaying);
setPrevNextEnabled(m.listCount != 0);
}
从上面的分析已经知道 updatePlayState
的功能是更新 播放 - 暂停
按钮的状态,而从命名上看,setPrevNextEnabled
应该是设置 前后
按钮的状态,这里看看实现:
private void setPrevNextEnabled(boolean enabled) {
float prevNextAlpha = enabled ? 1.0f : 0.37f;
if (this.mPrev != null) {
this.mPrev.setEnabled(enabled);
this.mPrev.setAlpha(prevNextAlpha);
}
if (this.mNext != null) {
this.mNext.setEnabled(enabled);
this.mNext.setAlpha(prevNextAlpha);
}
}
这里的功能显然是设置 前后
按钮是否可按的状态,如果可以则设置可按,并且设置图标为全实,否则设置不可按,并且设置图标为灰色。
Que 0x04 onPlaybackStateChanged
实现代码如下:
public void onPlaybackStateChanged(MusicPlaybackState s) {
updatePlayState(s.isSupposedToPlaying());
}
可以看到这个借口只是调用 updatePlayState
来更新 播放 - 暂停
按钮的状态。
Que 0x05 onQueueChanged
实现代码如下
public void onQueueChanged(List list, Bundle extras) {
}
这个接口只是一个空的方法,并没有实际意义。
Que 0x06 OnAirViewPopupListenerImpl
在 PlayController
中定义的第一个类就是 OnAirViewPopupListenerImpl
,声明代码如下:
private static class OnAirViewPopupListenerImpl implements OnAirViewPopupListener
public interface OnAirViewPopupListener {
View getAirView(View view);
}
可以看到,OnAirViewPopupListenerImpl
实现了 OnAirViewPopupListener
的 getAirView
接口。
Que 0x07 getAirView
实现代码如下:
public View getAirView(View v) {
Context context = this.mActivity.getApplicationContext();
switch (v.getId()) {
case R.id.next_btn:
String nextTitle = UiUtils.getTitle(context, this.mPlayerController.getNextUri());
if (nextTitle == null) {
nextTitle = TalkBackUtils.getButtonDescription(context, (int) R.string.tts_next);
}
return UiUtils.getAirTextView(this.mActivity, nextTitle);
case R.id.prev_btn:
String prevTitle = UiUtils.getTitle(context, this.mPlayerController.getPrevUri());
if (prevTitle == null) {
prevTitle = TalkBackUtils.getButtonDescription(context, (int) R.string.tts_previous);
}
return UiUtils.getAirTextView(this.mActivity, prevTitle);
default:
return null;
}
}
大致逻辑如下:
- 1 获取应用上下文
- 2 获取 view.id
- 3 判断是 next 或是 prev
- 4 next 或者 prev 都是首先获取标题
- 5 如果标题为空则通过另一种方式获取标题
- 6 返回指定的 view
- 7 那么猜测这里应该是获取播放的标题
Que 0x08 PlayController
实现代码如下:
public PlayController(Activity activity, View view, IPlayerController playerController, ForwardRewindInputListener forwardRewindInputListener, MediaChangeObservable mediaChangeObservable, com.samsung.android.app.music.core.service.mediacenter.MediaChangeObservable coreMediaChangeObservable) {
this.mContext = activity.getApplicationContext();
this.mPlayerController = playerController;
this.mPrev = view.findViewById(R.id.prev_btn);
this.mNext = view.findViewById(R.id.next_btn);
this.mPlay = view.findViewById(R.id.play_pause_btn);
ConvertTouchEventListener convertTouchEventListener = new ConvertTouchEventListener();
if (this.mPrev != null) {
this.mPrev.setOnKeyListener(convertTouchEventListener);
this.mPrev.setOnTouchListener(forwardRewindInputListener);
TalkBackUtils.setContentDescriptionAll(this.mContext, this.mPrev, R.string.tts_previous);
}
if (this.mNext != null) {
this.mNext.setOnKeyListener(convertTouchEventListener);
this.mNext.setOnTouchListener(forwardRewindInputListener);
TalkBackUtils.setContentDescriptionAll(this.mContext, this.mNext, R.string.tts_next);
}
this.mPlay.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
PlayController.this.mPlayerController.togglePlay();
if (PlayController.this.mOnPlayClickListener != null) {
PlayController.this.mOnPlayClickListener.onClick(v);
}
}
});
setAirView(activity);
mediaChangeObservable.registerMediaChangeObserver(this);
}
大致逻辑如下:
- 设置 prev 按钮的事件,备注等
- 设置 next 按钮的事件,备注等
- 设置 play 按钮的事件,备注等
- 设置标题
Que 0x09 mPlayerController
从目前的分析来看,这是一个至关重要的方法,声明如下:
private final IPlayerController mPlayerController;
public interface IPlayerController {
void forward();
int getBuffering();
String getNextUri();
long getPosition();
String getPrevUri();
boolean isPlaying();
void next();
void prev();
void rewind();
void seek(long j);
void togglePlay();
}
这里定义了很多接口,不过没能顺着一条线找到这些接口的实现。
通过全局搜索关键字的方法定位到了接口的实现。
forward
实现代码如下:
public void forward() {
this.mContext.sendBroadcast(new Intent("com.samsung.android.app.music.core.action.PLAYBACK_FORWARD"));
}
- 这里的作用是发送一个广播
getBuffering
实现代码如下:
public int getBuffering() {
return ServiceUtils.getBuffering();
}
public static int getBuffering() {
try {
if (sService != null) {
return sService.buffering();
}
return -1;
} catch (RemoteException e) {
e.printStackTrace();
return -1;
}
}
public int buffering() throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken(Stub.DESCRIPTOR);
this.mRemote.transact(23, _data, _reply, 0);
_reply.readException();
int _result = _reply.readInt();
return _result;
} finally {
_reply.recycle();
_data.recycle();
}
}
- buffering 主要实现进程之间的通信
- 使用的方法号为 23:
static final int TRANSACTION_buffering = 23
getNextUri
实现代码如下:
public String getNextUri() {
return ServiceUtils.getNextUri();
}
public static String getNextUri() {
if (sService == null) {
return null;
}
String uri = null;
try {
return sService.getNextUri();
} catch (RemoteException e) {
e.printStackTrace();
return uri;
}
}
public String getNextUri() throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken(Stub.DESCRIPTOR);
this.mRemote.transact(51, _data, _reply, 0);
_reply.readException();
String _result = _reply.readString();
return _result;
} finally {
_reply.recycle();
_data.recycle();
}
}
- 同 getBuffering
- 使用 52 号方法进行通信:
static final int TRANSACTION_getNextUri = 51
getPosition
实现代码如下:
public long getPosition() {
return ServiceUtils.getPosition();
}
public static long getPosition() {
try {
if (sService != null) {
return sService.position();
}
return -1;
} catch (RemoteException e) {
e.printStackTrace();
return -1;
}
}
public long position() throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken(Stub.DESCRIPTOR);
this.mRemote.transact(22, _data, _reply, 0);
_reply.readException();
long _result = _reply.readLong();
return _result;
} finally {
_reply.recycle();
_data.recycle();
}
}
- 同 getBuffering
- 使用 22 号方法进行通信:
static final int TRANSACTION_position = 22
getPrevUri
实现代码如下:
public String getPrevUri() {
return ServiceUtils.getPrevUri();
}
public static String getPrevUri() {
if (sService == null) {
return null;
}
String uri = null;
try {
return sService.getPrevUri();
} catch (RemoteException e) {
e.printStackTrace();
return uri;
}
}
public String getPrevUri() throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken(Stub.DESCRIPTOR);
this.mRemote.transact(52, _data, _reply, 0);
_reply.readException();
String _result = _reply.readString();
return _result;
} finally {
_reply.recycle();
_data.recycle();
}
}
- 同 getBuffering
- 使用 52 号方法进行通信:
static final int TRANSACTION_getPrevUri = 52
isPlaying
实现代码如下:
public boolean isPlaying() {
return ServiceUtils.isPlaying();
}
public static boolean isPlaying() {
if (sService != null) {
try {
return sService.isPlaying();
} catch (RemoteException e) {
e.printStackTrace();
}
}
return false;
}
public boolean isPlaying() throws RemoteException {
boolean _result = false;
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken(Stub.DESCRIPTOR);
this.mRemote.transact(25, _data, _reply, 0);
_reply.readException();
if (_reply.readInt() != 0) {
_result = true;
}
_reply.recycle();
_data.recycle();
return _result;
} catch (Throwable th) {
_reply.recycle();
_data.recycle();
}
}
- 同 getBuffering
- 使用 52 号方法进行通信:
static final int TRANSACTION_isPlaying = 25
Next
实现代码如下:
public void next() {
ServiceUtils.playNext();
}
public static void playNext() {
if (sService != null) {
new Thread() {
public void run() {
if (ServiceUtils.sService != null) {
try {
ServiceUtils.sService.next();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
public void next() throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken(Stub.DESCRIPTOR);
this.mRemote.transact(18, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
- 看起来好像不一样,其实只是多加了判断和多线程而已
- 使用 18 号方法进行通信:
static final int TRANSACTION_next = 18
prev
实现代码如下:
public void prev() {
ServiceUtils.playPrev(false);
}
public static void playPrev(final boolean force) {
if (sService != null) {
new Thread() {
public void run() {
try {
if (ServiceUtils.sService != null) {
ServiceUtils.sService.prev(force);
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
}.start();
}
}
public void prev(boolean force) throws RemoteException {
int i = 0;
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken(Stub.DESCRIPTOR);
if (force) {
i = 1;
}
_data.writeInt(i);
this.mRemote.transact(19, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
- 使用 19 号方法进行通信:
static final int TRANSACTION_prev = 19
rewind
实现代码如下:
public void rewind() {
this.mContext.sendBroadcast(new Intent("com.samsung.android.app.music.core.action.PLAYBACK_REWIND"));
}
- 这里的作用是发送一个广播
seek
实现代码如下:
public void seek(long position) {
ServiceUtils.seek(position);
}
public static long seek(long position) {
if (sService == null) {
return 0;
}
try {
return sService.seek(position);
} catch (RemoteException ex) {
ex.printStackTrace();
return position;
}
}
public long seek(long pos) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken(Stub.DESCRIPTOR);
_data.writeLong(pos);
this.mRemote.transact(20, _data, _reply, 0);
_reply.readException();
long _result = _reply.readLong();
return _result;
} finally {
_reply.recycle();
_data.recycle();
}
}
- 使用 20 号方法进行通信:
static final int TRANSACTION_seek = 20
togglePlay
实现代码如下:
public void togglePlay() {
ServiceUtils.togglePlay();
}
public static void togglePlay() {
if (sService != null) {
try {
if (sService.isPlaying()) {
sService.pause();
} else {
sService.play();
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
public void play() throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken(Stub.DESCRIPTOR);
this.mRemote.transact(16, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
public void pause() throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken(Stub.DESCRIPTOR);
this.mRemote.transact(17, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
- 这里略有不同,需要根据情况来选择使用 play 方法还是 pause 方法
- 通过 isPlaying 来获取状态