关于音乐播放器与系统功能联动功能梳理

news2024/11/26 4:53:29

主要实现功能:

一、通知栏播放显示和控制

二、系统下拉栏中播放模块显示同步

三、与其他播放器状态同步:本应用播放时暂停其他应用播放,进入其他应用播放时,暂停本应用的后台播放

通知栏播放的显示和控制:

通过Notification + RemoteViews + 广播实现,主要代码如下:
    /**
     * 初始化自定义通知栏 的按钮点击事件
     */
    private void initRemoteViews() {
        remoteViews = new RemoteViews(this.getPackageName(), R.layout.notification);

        //通知栏控制器上一首按钮广播操作
        Intent intentPrev = new Intent(ACTION_PRE_SONG);
        PendingIntent prevPendingIntent = PendingIntent.getBroadcast(this, 5100, intentPrev, PendingIntent.FLAG_CANCEL_CURRENT);
        //为prev控件注册事件
        remoteViews.setOnClickPendingIntent(R.id.btn_notification_previous, prevPendingIntent);


        //通知栏控制器播放暂停按钮广播操作  //用于接收广播时过滤意图信息
        Intent intentPlay = new Intent(ACTION_PAUSE);
        PendingIntent playPendingIntent = PendingIntent.getBroadcast(this, 5101, intentPlay, PendingIntent.FLAG_CANCEL_CURRENT);
        //为play控件注册事件
        remoteViews.setOnClickPendingIntent(R.id.btn_notification_play, playPendingIntent);

        //通知栏控制器下一首按钮广播操作
        Intent intentNext = new Intent(ACTION_NEXT_SONG);
        PendingIntent nextPendingIntent = PendingIntent.getBroadcast(this, 5102, intentNext, PendingIntent.FLAG_CANCEL_CURRENT);
        //为next控件注册事件
        remoteViews.setOnClickPendingIntent(R.id.btn_notification_next, nextPendingIntent);

        //通知栏控制器关闭按钮广播操作
        Intent intentClose = new Intent(ACTION_PLAY_CLOSE);
        PendingIntent closePendingIntent = PendingIntent.getBroadcast(this, 5103, intentClose, 0);
        //为close控件注册事件
        remoteViews.setOnClickPendingIntent(R.id.btn_notification_close, closePendingIntent);


    }




 /**
     * 初始化通知
     */
    @SuppressLint("NotificationTrampoline")
    private void initNotification() {
        String channelId = "play_control";
        String channelName = "播放控制";
        int importance = NotificationManager.IMPORTANCE_HIGH;
        createNotificationChannel(channelId, channelName, importance);

        //点击整个通知时发送广播
        Intent intent = new Intent(getApplicationContext(), NotificationClickReceiver.class);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0,
                intent, FLAG_UPDATE_CURRENT);

        //初始化通知
        notification = new NotificationCompat.Builder(this, "play_control")
                .setContentIntent(pendingIntent)
                .setWhen(System.currentTimeMillis())
                .setSmallIcon(R.mipmap.ic_launcher)
                .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
             //   .setCustomContentView(remoteViews)
                .setCustomBigContentView(remoteViews)
                .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
               // .setStyle(new NotificationCompat.BigTextStyle())
              //  .setStyle(new NotificationCompat.InboxStyle())
                .setDefaults(NotificationCompat.DEFAULT_ALL)
                .setAutoCancel(false)
                .setOnlyAlertOnce(true)
                .setOngoing(true)
                .build();
    }


    /**
     * 创建通知渠道
     *
     * @param channelId   渠道id
     * @param channelName 渠道名称
     * @param importance  渠道重要性
     */
    @TargetApi(Build.VERSION_CODES.O)
    private void createNotificationChannel(String channelId, String channelName, int importance) {
        NotificationChannel channel = new NotificationChannel(channelId, channelName, importance);
        channel.enableLights(false);
        channel.enableVibration(false);
        channel.setVibrationPattern(new long[]{0});
        channel.setSound(null, null);
        manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        manager.createNotificationChannel(channel);
    }

通知栏整体点击跳转到播放界面:注册全局监听广播

public class NotificationClickReceiver extends BroadcastReceiver {

    public static final String TAG = "NotificationClickReceiver";

    @Override
    public void onReceive(Context context, Intent intent) {
        LogUtil.showLog(TAG,"通知栏点击");
        //获取栈顶的Activity
       // Activity currentActivity = ActivityManager.getCurrentActivity();
        intent = new Intent(Intent.ACTION_MAIN);
        intent.addCategory(Intent.CATEGORY_LAUNCHER);
        intent.setClass(context, MusicPlayerActivity.class);
        intent.putExtra("from","notify");
       // intent.putExtra("file",MyApplication.currentPlayMusic);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
        context.startActivity(intent);
    }
}

系统下拉栏媒体播放信息和状态同步:

通过MediaSession + MediaMetadata + PlaybackState实现


    public void initMediaSession(){
        mediaSession = new MediaSession(this, "music_player_session");
        mediaSession.setCallback(new MediaSession.Callback() {
            // 覆盖必要的回调方法,如onPlay, onPause等

            @Override
            public void onPause() {
                super.onPause();
                mediaSession.setPlaybackState(stateBuilder.setState(PlaybackState.STATE_PAUSED,mPlayer.getCurrentPosition(), 0.0f).build());
                sendBroadcast(new Intent(ACTION_PAUSE));
            }

            @Override
            public void onPlay() {
                super.onPlay();
                mediaSession.setPlaybackState(stateBuilder.setState(PlaybackState.STATE_PLAYING, mPlayer.getCurrentPosition(), 0.0f).build());
                sendBroadcast(new Intent(ACTION_PLAY_SONG));
            }

            @Override
            public void onSkipToNext() {
                super.onSkipToNext();
                sendBroadcast(new Intent(ACTION_NEXT_SONG));
            }

            @Override
            public void onSkipToPrevious() {
                super.onSkipToPrevious();
                sendBroadcast(new Intent(ACTION_PRE_SONG));
            }
        });
        mediaSession.setActive(true);

        metaDataBuilder = new MediaMetadata.Builder();
        //播放状态
        stateBuilder = new PlaybackState.Builder();
        stateBuilder.setActions(PlaybackState.ACTION_PLAY | PlaybackState.ACTION_PAUSE
                | PlaybackState.ACTION_SKIP_TO_NEXT | PlaybackState.ACTION_SKIP_TO_PREVIOUS);

    }

状态和信息同步:

    /**
     * 更改通知的信息和UI
     */
    private Intent intentPlay;
    private PendingIntent playPendingIntent;
    public void updateNotificationShow() {
        //通知栏控制器播放暂停按钮广播操作  //用于接收广播时过滤意图信息
         intentPlay = new Intent(mPlayer.isPlaying()?ACTION_PAUSE:ACTION_PLAY_SONG);
         playPendingIntent = PendingIntent.getBroadcast(this, 5101, intentPlay, PendingIntent.FLAG_CANCEL_CURRENT);
        //为play控件注册事件
        remoteViews.setOnClickPendingIntent(R.id.btn_notification_play, playPendingIntent);
        //播放状态判断
        if (mPlayer.isPlaying()) {
            remoteViews.setImageViewResource(R.id.btn_notification_play, R.mipmap.notify_pause);
        } else {
            remoteViews.setImageViewResource(R.id.btn_notification_play, R.mipmap.notify_play);
        }
        //封面专辑
        remoteViews.setImageViewResource(R.id.iv_album_cover,R.mipmap.ic_launcher);
        //歌曲名
        remoteViews.setTextViewText(R.id.tv_notification_song_name,defaultSongName.substring(0,defaultSongName.lastIndexOf(".")));
        //歌手名
        remoteViews.setTextViewText(R.id.tv_notification_singer,"");
        remoteViews.setTextViewText(R.id.tv_duration,StringUtil.formatDuration(mPlayer.getDuration()));
        remoteViews.setTextViewText(R.id.tv_current_time,StringUtil.formatDuration(mPlayer.getCurrentPosition()));
        if(mPlayer.getDuration() > 0)
            remoteViews.setProgressBar(R.id.seekbar,100,mPlayer.getCurrentPosition()*100/mPlayer.getDuration(),false);
        //发送通知
        manager.notify(NOTIFICATION_ID,notification);
        WindowUtils.isNotifyShow = true;

        //同步下拉栏播放控制区信息
        metaDataBuilder.putString(MediaMetadata.METADATA_KEY_TITLE,defaultSongName.substring(0,defaultSongName.lastIndexOf(".")));
        mediaSession.setMetadata(metaDataBuilder.build());

    }

与其他应用播放器状态同步

通过AudioManager监听onAudioFocusChange音频焦点变化实现

 /** 监测其他应用播放音视频 */
        mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
        mListener = new AudioManager.OnAudioFocusChangeListener() {
            @Override
            public void onAudioFocusChange(int focusChange) {
                LogUtil.showLog(TAG,"==onAudioFocusChange=="+focusChange);
                abandonAudioFocus(); //禁用音频
                sendBroadcast(new Intent(ACTION_PAUSE));
                switch (focusChange) {
                    case AudioManager.AUDIOFOCUS_GAIN:
                        // TBD 继续播放
                        break;
                    case AudioManager.AUDIOFOCUS_LOSS:
                        // TBD 停止播放
                        break;
                    case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                        // TBD 暂停播放
                        break;
                    case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                        // TBD 混音播放
                        break;
                    default:
                        break;
                }

            }
        };
        //android 版本 5.0
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            mAttribute = new AudioAttributes.Builder()
                    .setUsage(AudioAttributes.USAGE_MEDIA)
                    .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                    .build();
        }
        //android 版本 8.0
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            mFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT)
                    .setWillPauseWhenDucked(true)
                    .setAcceptsDelayedFocusGain(true)
                    .setOnAudioFocusChangeListener(mListener, mHandler)
                    .setAudioAttributes(mAttribute)
                    .build();
        }
        requestAudioFocus();//启动获取音频

效果图:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1803431.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2024 IDEA最新永久使用码教程(2099版)

本篇文章我就来分享一下2024年当前最新版 IntelliJ IDEA 最新注册码,教程如下,可免费永久,亲测有效,适合Windows和Mac。 本教程适用于 J B 全系列产品,包括 Pycharm、IDEA、WebStorm、Phpstorm、Datagrip、RubyMine、…

Nginx(openresty) 查看连接数和并发送

1 通过浏览器查看 #修改nginx配置文件 location /status {stub_status on;access_log off;allow 192.168.50.0/24;deny all;} #重新加载 sudo /usr/local/openresty/nginx/sbin/nginx -s reloadActive connections //当前 Nginx 当前处理的活动连接数。 server accepts handl…

FreeSurFer的recon-all处理流——学习记录

官方网址:ReconAllTableStableV6.0 - Free Surfer Wiki (1)颅骨剥离skullstrip 颅骨剥离后生成文件:/mri/brainmask.mgz (2)图像配准canorm Freesurfer图像配准:将 mri/nu.mgz 体积与 FREESU…

【旅行】关于毕业旅行与长期旅行计划(城市、攻略、预算、交通、面基等)

【旅行】关于毕业旅行与长期旅行计划(城市、攻略、预算、交通、面基等) 文章目录 一、目的地与去哪儿玩1、可能2、人民币3、国家地理4、省份与城市5、环球旅行 二、攻略之怎么玩(旅行预算、攻略)1、旅行预算之交通、住宿、门票等2…

QT Udp广播实现设备发现

测试环境 本文选用pc1作为客户端,pc2,以及一台虚拟机作为服务端。 pc1,pc2(客户端): 虚拟机(服务端): 客户端 原理:客户端通过发送广播消息信息到ip:255.255.255.255(QHostAddress::Broadcast),局域网…

Vue前端在线预览文件插件

Vue前端在线预览文件插件 一、使用场景 1.1.像文档资料等,只想让他人在线预览,但不能下载。此等场景需求可以用到此插件。 二、此文档介绍两种插件 1.view.xdocin插件 (上线后免费几天,然后收费,添加作者后,可以延…

数字孪生技术体系和核心能力整理

最近对数字孪生技术进行了跟踪调研学习,整理形成了调研成果,供大家参考。通过学习,发现数字孪生技术的构建过程其实就是数字孪生体的构建与应用过程,数字孪生体的构建是一个体系化的系统工程,数字化转型的最终形态应该就是数实融合互动互联的终极状态。数实融合是每个行业…

论文复现:Track to Detect and Segment: An Online Multi-Object Tracker

论文下载链接:链接 简单介绍:大多数在线多目标跟踪器在神经网络中独立执行目标检测,无需任何跟踪输入。在本文中提出了一种新的在线联合检测和跟踪模型TraDeS(TRAck to DEtect and Segment),利用跟踪线索…

【SpringCloud学习笔记】Docker(上篇)

Docker 1. 前置准备 在学习Docker之前我们需要具备以下环境: Linux云服务器 / 虚拟机安装并配置Docker环境(命令行中输入docker -v能够显示对应版本证明安装成功) 2. 快速入门 要求: 我们先来尝试使用Docker创建MySQL服务&am…

Linux CGroup资源限制(概念限制进程CPU使用)

Linux CGroup资源限制(详解) 最近客户认为我们程序占用cpu过高,希望我们限制,排查之后发现是因为程序频繁gc导致,为了精细化、灵活的的限制,想到了使用Linux CGroup。 0 前置知识 ①概念及作用 官网&#…

【python报错】TypeError: can only concatenate str (not “int“) to str

【Python报错】TypeError: can only concatenate str (not “int”) to str 在Python编程中,字符串连接是一种基本且频繁的操作。然而,如果你尝试将整数(int)与字符串(str)直接连接,会遇到TypeE…

从记忆到想象:探索AI的智能未来

引言 人工智能(AI)在信息处理、数据分析和任务自动化等方面展现了强大的能力。然而,在人类独有的记忆和想象力领域,AI仍然有很长的路要走。加利福尼亚大学戴维斯分校的心理学和神经科学教授查兰兰加纳特(Charan Ranga…

内存管理--4.用幻灯片讲解内存分配器Allocator

用幻灯片讲解内存分配器Allocators Allocators 内存分配器 提供内存分配策略的通用接口委托给 C 运行时:new / delete使用块内存池管理内存使用不同大小的块内存池管理内存 为什么用分配器? 将容器逻辑与内存分配策略解耦速度:内存分配速度慢确保…

【玩转C语言】第三讲---> scanf 和 printf 函数详解(非常重要)!

🔥博客主页🔥:【 坊钰_CSDN博客 】 欢迎各位点赞👍评论✍收藏⭐ 引言: 大家好,我是坊钰,为了让大家深入了解C语言,我开创了【玩转C语言系列】,将为大家介绍C语言相关知识…

【AI】你要的U-KAN来了

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 U-KAN来了,快是真的快的,上个月才出的KAN,不得不说快。 先占个坑,有时间细看。 下面放上摘要 1. 正文 …

Bio-Info 每日一题:Rosalind-04-Rabbits and Recurrence Relations

🎉 进入生物信息学的世界,与Rosalind一起探索吧!🧬 Rosalind是一个在线平台,专为学习和实践生物信息学而设计。该平台提供了一系列循序渐进的编程挑战,帮助用户从基础到高级掌握生物信息学知识。无论你是初…

八、C语言:操作符详解

一、移位操作符 1.1左移操作 左边丢弃,右边补0 1.2右移操作 算数右移:右边丢弃,左边补原符号位 逻辑右移:右边丢弃,左边补0 int main() {int a -1;int b a >> 1;printf("b%d\n",b);return 0; } 原码…

Science刊发!乌普萨拉大学最新神经形态触觉人造皮肤可快速精准识别物体

当前,人形机器人使用的传统电子皮肤在处理触觉感知信息方面的能力并不强,尤其是在时间信息编码和快速特征提取方面存在一定的局限性。简单来说就是机器人无法完成在接触到物品的瞬间,判断用怎样的力度去对该物品做出反应。尽管多模态大模型和…

解决Android Studio Iguana版本不显示原创的GradleTask问题

问题描述: 下面是我的AndroidStudio版本号,升级后我发现项目里面自定义的gradletask找不到了??? 解决方案: 1、去setting里面把下面红框里面的选项勾选一下,缺点就是sync的时候会慢一些。 2、…

电子电气架构——车载诊断DTC一文通

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 没有人关注你。也无需有人关注你。你必须承认自己的价值,你不能站在他人的角度来反对自己。人生在世,最怕的就是把别人的眼光当成自己生活的唯一标…