Jun_20.md

作者: 深蓝Yearth | 来源:发表于2018-06-20 18:19 被阅读0次

    今日任务

    • 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<QueueItem> 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<QueueItem> 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

          本文链接:https://www.haomeiwen.com/subject/ciwxyftx.html