Jun_20.md

今日任务

  • PlayController 分析

Que 0x01 PlayController 声明

public class PlayController implements OnMediaChangeObserver, com.samsung.android.app.music.core.service.mediacenter.OnMediaChangeObserver

从上面可以看出 PlayController 实现了 OnMediaChangeObservercom.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 实现了 OnAirViewPopupListenergetAirView 接口。

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 来获取状态

你可能感兴趣的:(Jun_20.md)