Android音乐浮窗播放器

这是来到公司的第一个小项目。做一个音乐播放器,内容如下

Float Music(浮窗音乐播放器)
考点: 服务, 音乐播放, 浮窗界面, 列表界面, 自定义view, 手势操作
功能描述:
1.音乐列表(主界面)
(1)音乐列表从系统媒体库中查询, 按音频媒体库中的添加时间排列
(2)音乐列表的每一项显示歌曲名及播放总时长
(3)点击列表中的一项即开始播放点击的歌曲, 歌曲使用服务在后台播放, 在播放时显示浮动播放控制器(见功能点2)

这里写图片描述

图1.1

2.浮动音乐播放控制器
(1)音乐播放控制器以浮窗的形式显示, 音乐播放时一直显示
(2)以自定义View的方式实现一个圈形进度显示, 根据播放的当前进度显示相应的弧度
(3)控制器中心显示应用的启动图标, 音乐播放时慢慢旋转
(4)点击控制器时切换播放与暂停, 歌曲单曲循环形式播放
(5)浮动控制器可以随手势拖动到屏幕任意位置, 但必须在屏幕内完整显示
(6)长按(>=3s)播放控制器停止播放, 关闭播放服务且关闭浮窗
这里写图片描述

最终,实现效果如下:
这里写图片描述

先附上项目的流程图,便于大家理解。
这里写图片描述

接着附上项目的UML图。
这里写图片描述

这里写图片描述

这里写图片描述

为了实现以上效果,首先,我们肯定是要先获取手机中的所有音乐。那么,我们可以通过Content Provider获取。
首先我们获取手机的跟路径,然后查找该路径下的音乐。

public class MusicUtil {

    public static String getBaseDir(){
        String dir = null;
                                 if(!Environment.getExternalStorageState().equals(Environment.MEDIA_UNKNOWN)){
            dir=Environment.getExternalStorageDirectory() + File.separator;
        }else {
            dir = App.sContext.getFilesDir() + File.separator;
        }

        return dir;
    }
}

然后,创建一个music的bean类,参数如下,省略get、set方法

public class Music {

    private int id; // 音乐id
    private String title; // 音乐标题
    private String album;//专辑名称
    private String uri; // 音乐路径
    private int duration; // 时长
    private int size;//大小
    private String year;//发行时间
    private String image; // icon
    private String artist; // 艺术家
}

接着通过ContentProvider获取音乐,并按添加时间排序。

public static ArrayList<Music> queryMusic(String dirName){

        //select * from xx where data like dirName order by modify_time
        ArrayList<Music> musicList = new ArrayList<>();
        Cursor cursor = App.sContext.getContentResolver().query(
                MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,null,
                MediaStore.Audio.Media.DATA + " like ?",
                new String[]{dirName + "%"},
                "date_modified"+" desc");

        if (cursor == null)
            return musicList;

        Music music;
        for (cursor.moveToFirst();!cursor.isAfterLast();cursor.moveToNext()){
            // 如果不是音乐
            String isMusic = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.IS_MUSIC));
            if (isMusic != null && isMusic.equals(""))
                continue;

            String title = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE));
            String artist = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST));

            if(isRepeat(title, artist)) continue;

            music = new Music();
            music.setId(cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID)));
            music.setTitle(title);
            music.setArtist(artist);
            music.setUri(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA)));
            music.setDuration(cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION)));
            music.setYear(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.YEAR)));
            music.setSize(cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.SIZE)));
            music.setAlbum(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM)));
            music.setImage(getAlbumImage(cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM_ID))));
            music.setDuration(music.getDuration()==0?1:music.getDuration());
            musicList.add(music);
        }
        cursor.close();
        return musicList;
    }

通过上述代码,我们可以获取的到手机上的音乐列表。
那么,接下去,我们便要创建一个播放的service了。主要功能如下:
1.初始化音乐列表,通过MusicUtil.initMusiclist();获取音乐列表
2.创建电源锁,防止锁屏服务被关闭
3.创建MediaPlayer对象,并添加相关监听事件(播放进度,换曲监听,上下曲点击、暂停、播放)
4.创建notification,创建广播对对应按钮进行事件监听
5.创建线程mPublishProgressRunnable,对播放进度进行广播

public class PlayService extends Service implements MediaPlayer.OnCompletionListener{
        ............
        @Override
    public void onCreate() {
        super.onCreate();
        acquireWakeLock();

        MusicUtil.initMusicList();
        mPlayingPosition = (int) SpUtils.get(this, Constants.PLAY_POS,0);

        try {
            Uri uri = Uri.parse(MusicUtil.sMusicList.get(
                    mPlayingPosition).getUri());
            mMediaPlayer = MediaPlayer.create(PlayService.this, uri);
            mMediaPlayer.setOnCompletionListener(this);

            mProgressUpdatedListener.execute(mPublishProgressRunnable);

            PendingIntent pendingIntent = PendingIntent
                    .getActivity(PlayService.this, 0,
                            new Intent(PlayService.this, PlayActivity.class), 0);

            remoteViews = new RemoteViews(getPackageName(),
                    R.layout.play_notification);
            notification = new Notification(R.drawable.ic_audiotrack_red_300_48dp,
                    R.string.palying_music + "", System.currentTimeMillis());
            notification.contentIntent = pendingIntent;
            notification.contentView = remoteViews;
            //一直存在
            notification.flags = Notification.FLAG_ONGOING_EVENT;

            Intent intent = new Intent(PlayService.class.getSimpleName());
            intent.putExtra(Constants.BUTTON_NOTI, PRE);
            PendingIntent preIntent = PendingIntent.getBroadcast(
                    PlayService.this, PRE, intent,
                    PendingIntent.FLAG_UPDATE_CURRENT);
            remoteViews.setOnClickPendingIntent(
                    R.id.music_play_pre, preIntent
            );

            intent.putExtra(Constants.BUTTON_NOTI, PAUSE);
            PendingIntent pauseIntent = PendingIntent.getBroadcast(
                    PlayService.this, PAUSE, intent,
                    PendingIntent.FLAG_UPDATE_CURRENT);
            remoteViews.setOnClickPendingIntent(
                    R.id.music_play_pause, pauseIntent);

            intent.putExtra(Constants.BUTTON_NOTI, NEXT);
            PendingIntent nextIntent = PendingIntent.getBroadcast(
                    PlayService.this, NEXT, intent,
                    PendingIntent.FLAG_UPDATE_CURRENT);
            remoteViews.setOnClickPendingIntent(
                    R.id.music_play_next, nextIntent);

            intent.putExtra(Constants.BUTTON_NOTI, EXIT);
            PendingIntent exit = PendingIntent.getBroadcast(
                    PlayService.this, EXIT, intent,
                    PendingIntent.FLAG_UPDATE_CURRENT);
            remoteViews.setOnClickPendingIntent(
                    R.id.music_play_notifi_exit, exit);

            notificationManager = (NotificationManager)
                    getSystemService(NOTIFICATION_SERVICE);
            setRemoteViews();

            /**
             * 注册广播接收者
             * 功能:
             * 监听通知栏按钮点击事件
             */
            IntentFilter filter = new IntentFilter(
                    PlayService.class.getSimpleName());
            PlayBroadCastReceiver receiver = new PlayBroadCastReceiver();
            registerReceiver(receiver, filter);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    ..........
}

那么,大概的播放服务类已经完成了。接下去就是浮窗的创建了。
由于浮窗的特殊性,我们需要创建第二个service类。

public class FxService extends Service {

    LinearLayout mFloatLayout;
    WindowManager.LayoutParams mWparams;
    WindowManager mWindowManager;
    RotateView mRotateView;
    private FloatingViewClickListener mListener;
    private int pointDownX;
    private int pointDownY;
    private int pointUpX;
    private int pointUpY;
    private long pointDownTime;
    private long lastDownTime;
    public final static int DISTANCE = 15;
    public final static int LONG_CLICK_TIME = 3000;
    private ProgressRecevier mReceiver;

    private void createFloatView() {
        mWparams = new WindowManager.LayoutParams();
        mWindowManager = (WindowManager) getApplication().getSystemService(getApplication().WINDOW_SERVICE);
        mWparams.type = WindowManager.LayoutParams.TYPE_PHONE;
        mWparams.format = PixelFormat.RGBA_8888;
        mWparams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
        mWparams.gravity = Gravity.LEFT|Gravity.TOP;
        mWparams.x = 0;
        mWparams.y = 0;

        mWparams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        mWparams.height = WindowManager.LayoutParams.WRAP_CONTENT;

        LayoutInflater inflater = LayoutInflater.from(getApplication());
        mFloatLayout = (LinearLayout) inflater.inflate(R.layout.activity_play,null,false);
        mWindowManager.addView(mFloatLayout,mWparams);
        mRotateView = (RotateView) mFloatLayout.findViewById(R.id.cdView);
        Bitmap bmp = BitmapFactory.decodeResource(getResources(),
                R.mipmap.ic_launcher);
        mRotateView.setCdImage(bmp,0.3);
        //mRotateView.setRingWidth((float) (App.sScreenWidth*0.3-15));

        mRotateView.startRoll();
        //mRotateView.setOnTouchListener(this);
        mRotateView.setOnTouchListener(new View.OnTouchListener()
        {

            @Override
            public boolean onTouch(View v, MotionEvent event)
            {
                //getRawX是触摸位置相对于屏幕的坐标,getX是相对于按钮的坐标
                mWparams.x = (int) event.getRawX() - mRotateView.getMeasuredWidth()/2;
                mWparams.y = (int) event.getRawY() - mRotateView.getMeasuredHeight()/2 - 25;
                //刷新
                mWindowManager.updateViewLayout(mFloatLayout, mWparams);

                int x = (int) event.getRawX();
                int y = (int) event.getRawY();
                switch (event.getAction()){
                    case MotionEvent.ACTION_DOWN:
                        pointDownX = x;
                        pointDownY = y;
                        pointDownTime = System.currentTimeMillis();
                        break;
                    case MotionEvent.ACTION_MOVE:
                        lastDownTime = System.currentTimeMillis();
                        //Log.w("haha",pointDownX+"!!"+pointDownY+"!!!x="+x+"!!!y="+y);
                        if (isLongPressed(pointDownX,pointDownY,x,y,pointDownTime,lastDownTime)) {
                            LongClick();
                            return true;
                        }
                        break;
                    case MotionEvent.ACTION_UP:
                        pointUpX = x;
                        pointUpY = y;
                        if (Math.abs(pointDownX-pointUpX)<DISTANCE&&
                                Math.abs(pointUpY-pointDownY)<DISTANCE) {
                            onViewClick();
                            return true;
                        }
                        break;
                }
                return true;
            }
        });

        mReceiver = new ProgressRecevier();
        IntentFilter intentFilter = new IntentFilter();

        intentFilter.addAction("com.jiepier.floatmusic.RECEVER");
        registerReceiver(mReceiver, intentFilter);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new FxBinder();
    }

    public class FxBinder extends Binder {

        public FxService getService(){
            return FxService.this;
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mFloatLayout != null){
            mWindowManager.removeView(mFloatLayout);
        }
        if (mReceiver!=null)
        unregisterReceiver(mReceiver);
    }


    private void LongClick() {
        if (mListener!=null)
            mListener.onLongClick();
        //stopService(new Intent(App.sContext,FxService.class));
    }

    private void onViewClick() {
        if (mListener!=null)
            mListener.OnClick();
    }

    boolean isLongPressed(float lastX, float lastY, float thisX,
                          float thisY, long lastDownTime, long thisEventTime) {
        float offsetX = Math.abs(thisX - lastX);
        float offsetY = Math.abs(thisY - lastY);
        long intervalTime = thisEventTime - lastDownTime;
        if (offsetX <= DISTANCE && offsetY <= DISTANCE && intervalTime >= LONG_CLICK_TIME) {
            return true;
        }
        return false;
    }

    public void setRotateAngle(int angle){
        mRotateView.rotate(angle);
    }

    public void setmListener(FloatingViewClickListener mListener) {
        this.mListener = mListener;
    }

    public interface FloatingViewClickListener{
        void OnClick();

        void onLongClick();
    }

    @Override
    public void onCreate() {
        super.onCreate();
        createFloatView();
    }

    public class ProgressRecevier extends BroadcastReceiver{

        @Override
        public void onReceive(Context context, Intent intent) {
            int progress = intent.getIntExtra("progress",0);
            mRotateView.rotate(progress);
        }
    }
}

通过上述代码,我们可以发现,创建浮窗的代码也很简单。RotateView是我自定义的一个view,是一个旋转安卓机器人和圆形进度图的view,稍后我会进行代码介绍。
1.对显示的view进行边界检测,通过重写setOnTouch事件,对位移进行判断,如果超出边界,则不移动,否则,移动响应的位移。
2.对单击和长按进行区分,通过接口进行实现。
3.接受来自playservice的播放进度的广播,对自定义view进行进度控制,因为当app退出的时候,两个service无法通过activity进行回调,所以我通过广播进行进度的监听。(不给用第三方,所以不能用rxbus了。。。)

现在,核心的两个service类都已经完成了。接下来,看看主页面的代码吧。
让我们看看我们的BaseActivity类,关键代码如下

public abstract class BaseActivity extends AppCompatActivity {

    protected PlayService mPlayService;
    protected FxService mFxService;
    private boolean isBound = false;
    public boolean isFirst = true;

    public void allowBindService(){
        getApplicationContext().bindService(new Intent(this,PlayService.class),
                mPlayServiceConnection, Context.BIND_AUTO_CREATE);
    }

    public void allowUnBindService(){
        getApplicationContext().unbindService(mPlayServiceConnection);
    }

    public void BindFxService(){
        isBound = getApplicationContext().bindService(new Intent(this,FxService.class),
                mFxServiceConnection, Context.BIND_AUTO_CREATE);
    }

    public void unBindFxService(){
        if (isBound) {
            isBound = false;
            isFirst = true;
            getApplicationContext().unbindService(mFxServiceConnection);
            stopService(new Intent(this,FxService.class));
        }
    }

    public PlayService getPlayService(){
        return mPlayService;
    }

    private ServiceConnection mPlayServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            mPlayService = ((PlayService.PlayBinder) iBinder).getService();
            mPlayService.setOnMusicEventListener(mMusicEventListener);
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            mPlayService = null;
        }
    };

    private ServiceConnection mFxServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            mFxService = ((FxService.FxBinder) iBinder).getService();
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            mFxService = null;
        }
    };

    public FxService getFxService(){
        return mFxService;
    }

    private PlayService.OnMusicEventListener mMusicEventListener = new PlayService.OnMusicEventListener() {
        @Override
        public void onPublish(int percent) {
            BaseActivity.this.onPublish(percent*100/ MusicUtil.sMusicList.get(mPlayService.getPlayingPosition()).getDuration());
        }

        @Override
        public void onChange(int position) {
            BaseActivity.this.onChange(position);
        }
    };

    public abstract void onPublish(int percent);

    public abstract void onChange(int position);
}

BaseActivity的主要功能有:
1.建立Activity和service之间的连接
2.提供接触绑定和绑定服务的方法
3.播放进度的回调监听

接下来,就是主页面的Activity了。关键代码如下:

public class MainActivity extends BaseActivity {
@Override
    public void initUiAndListener() {

        mMusicAdapter = new MusicAdapter();
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setItemAnimator(new DefaultItemAnimator());
        recyclerView.setAdapter(mMusicAdapter);
        recyclerView.addItemDecoration(new RecyclerViewDivider(
                this, RecyclerViewDivider.VERTICAL_LIST));

        mMusicAdapter.setOnItemClickLisetener(
                new MusicAdapter.OnItemClickLisetener() {
                    @Override
                    public void onItemClick(int position) {

                        mPlayService.play(position);
                        startService(new Intent(App.sContext, FxService.class));
                        BindFxService();
                        playProgress.setMax(MusicUtil.sMusicList.get(position).getDuration());
                    }
                });

        playProgress.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int i, boolean b) {

            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                int progress = seekBar.getProgress();
                mPlayService.seek(progress);
            }
        });

    }
}

主页面主要功能是:
1.通过recyclerview对音乐列表进行显示,通过MusicAdapter对对应的item进行点击监听
2.页面底部有播放进度的seekbar,还有上下曲的按钮,通过拉动seekbar可对音乐进度进行调整
3.让fxservice和Activity进行绑定,让悬浮窗显示

接下来,我们说说刚才说到到RotateView

public class RotateView extends View{

    private static final int MSG_RUN = 0x00000100;
    private static final int TIME_UPDATE = 16;

    private Bitmap mClipBitmap;//cd图片

    private Matrix mMatrix;
    private float mRotation = 0.0f;
    private volatile boolean isRunning;

    private Paint paint;
    private int ringColor;
    private int ringProgressColor;
    private float ringWidth;

    private double scale;

    public RotateView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        mMatrix = new Matrix();

        paint = new Paint();
        TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.RoundProgressBar);
        ringColor = mTypedArray.getColor(R.styleable.RoundProgressBar_ringColor,0xff50c0e9);
        ringProgressColor = mTypedArray.getColor(R.styleable.RoundProgressBar_ringProgressColor, 0xffffc641);
        ringWidth = mTypedArray.getDimension(R.styleable.RoundProgressBar_ringWidth, 20);
        mTypedArray.recycle();
    }

    public RotateView(Context context, AttributeSet attrs) {
        this(context, attrs ,0);
    }

    public RotateView(Context context) {
        this(context, null);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (mClipBitmap == null){
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            return;
        }

            int width = 0;
            int height = 0;

            int widthMode = MeasureSpec.getMode(widthMeasureSpec);
            int heightMode = MeasureSpec.getMode(heightMeasureSpec);

            int widthSize = MeasureSpec.getSize(widthMeasureSpec);
            int heightSize = MeasureSpec.getSize(heightMeasureSpec);

            if (widthMode == MeasureSpec.EXACTLY){
                width = widthSize;
            }else {
            width = mClipBitmap.getWidth();
            //子view不能大于父类
            if (widthMode == MeasureSpec.AT_MOST){
                width = Math.min(width,widthSize);
            }
        }

        if (heightMode == MeasureSpec.EXACTLY) {
            height = heightSize;
        } else {
            height = mClipBitmap.getHeight();
            if (heightMode == MeasureSpec.AT_MOST) {
                height = Math.min(height, heightSize);
            }
        }
        setMeasuredDimension(width, height);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (mClipBitmap == null)
            return;

        canvas.save();

        mMatrix.setRotate(mRotation, (float) (App.sScreenWidth/10*1.5), (float) (App.sScreenWidth/10*1.5));
        canvas.drawBitmap(mClipBitmap,mMatrix,null);
        canvas.restore();

        int center = (int)(App.sScreenWidth/2*scale);//圆心的x坐标
        int radius = (int)(center-ringWidth/2);

        /**
         * 画最外层的大圆环
         */
        paint.setColor(ringColor);//设置圆环的颜色
        paint.setStyle(Paint.Style.STROKE);//设置空心
        paint.setStrokeWidth(ringWidth); //设置圆环的宽度
        paint.setAntiAlias(true);  //消除锯齿
        canvas.drawCircle(center, center, radius, paint); //画出圆环


        paint.setStrokeWidth(ringWidth);
        paint.setColor(ringProgressColor);
        RectF oval = new RectF(center - radius, center - radius, center
                + radius, center + radius);
        paint.setStyle(Paint.Style.STROKE);
        canvas.drawArc(oval, 0, mRotation, false, paint);  //根据进度画圆弧
    }

    private Bitmap cretaeCircleBitmap(Bitmap src){
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setARGB(255,241,239,229);

        Bitmap target = Bitmap.createBitmap(getMeasuredWidth(),
                getMeasuredHeight(), Bitmap.Config.ARGB_8888);

        Canvas canvas = new Canvas(target);
        canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredWidth() / 2,
                (float) getMeasuredWidth()/2-ringWidth, paint);
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(src, 0, 0, paint);

        return target;
    }

    public void setCdImage(Bitmap bitmap,double scale){
        this.scale = scale;
        bitmap = ImageTools.scaleBitmap(bitmap,
                (int) (App.sScreenWidth * scale));
        int widthSize = bitmap.getWidth();
        int heightSize = bitmap.getHeight();
        int widthSpec = MeasureSpec.makeMeasureSpec(widthSize,
                MeasureSpec.AT_MOST);
        int heightSpec = MeasureSpec.makeMeasureSpec(heightSize,
                MeasureSpec.AT_MOST);

        measure(widthSpec, heightSpec);
        mClipBitmap = cretaeCircleBitmap(bitmap);
        requestLayout();
        invalidate();
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        isRunning = false;
    }

    public void startRoll(){
        if (isRunning)
            return;

        isRunning = true;
        mHandler.sendEmptyMessageDelayed(MSG_RUN, TIME_UPDATE);
    }

    //暂停旋转
    public void pause() {
        if (!isRunning)
            return;
        isRunning = false;
    }

    private Handler mHandler = new Handler() {
        public void handleMessage(Message msg) {
            if (msg.what == MSG_RUN) {
                if (isRunning) {
                    if (mRotation >= 360)
                        mRotation = 0;
                    invalidate();
                    sendEmptyMessageDelayed(MSG_RUN, TIME_UPDATE);
                }
            }
        }
    };

    public void rotate(float angle){
        this.mRotation = (float) (angle*3.6);
    }
}

首先,通过自定义参数,我们可以画出一个圆形进度条,然后获取对应的机器人bitmap和宽高大小。
再通过rotate设置旋转的角度并通过Matrix对view进行旋转。

到这里,我们的应用基本就完成了。把应用发给组长,组长后来跟我说运行不了,直接奔溃。。。然后说可能是权限问题,他的手机是6.0的。所以,后来我就添加了一下动态权限申请。

public class SplashActivity extends AppCompatActivity {

    public static final int REQUEST_CODE = 100;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // no title
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        // 全屏
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        setContentView(R.layout.splash_layout);


        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
                && ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
                    REQUEST_CODE);
            return;
        }else {
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    startActivity(new Intent(SplashActivity.this, MainActivity.class));
                    finish();
                }
            }, 2000);
        }
    }

    @Override
    public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_CODE) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // Permission granted.
                requestDrawOverLays();
            } else {
                // User refused to grant permission.
                Toast.makeText(this,"请先给予读写权限,否则app没法用啊",Toast.LENGTH_LONG).show();
            }
        }

    }

    public static int OVERLAY_PERMISSION_REQ_CODE = 1234;
    @TargetApi(Build.VERSION_CODES.M)
    public void requestDrawOverLays() {
        if (!Settings.canDrawOverlays(SplashActivity.this)) {
            Toast.makeText(this, "请手动给予特殊权限", Toast.LENGTH_SHORT).show();
            Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + SplashActivity.this.getPackageName()));
            startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE);
        } else {
            startService(new Intent(this,PlayService.class));
            startActivity(new Intent(this, MainActivity.class));
            finish();
            // Already hold the SYSTEM_ALERT_WINDOW permission, do addview or something.
        }
    }

    @TargetApi(Build.VERSION_CODES.M)
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == OVERLAY_PERMISSION_REQ_CODE) {
            if (!Settings.canDrawOverlays(this)) {
                // SYSTEM_ALERT_WINDOW permission not granted...
                Toast.makeText(this, "给权限啊,大佬。。。", Toast.LENGTH_SHORT).show();
            } else {
                //Toast.makeText(this, "Permission Allowed", Toast.LENGTH_SHORT).show();
                startService(new Intent(this,PlayService.class));
                startActivity(new Intent(this, MainActivity.class));
                finish();
                // Already hold the SYSTEM_ALERT_WINDOW permission, do addview or something.
            }
        }
    }
}

这里我要说明一下,首先呢,读写的权限肯定是必须的。ALERT_WINDOW用于弹窗的显示。

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

读写权限可以通过动态申请获取,但是SYSTEM_ALERT_WINDOW
这个权限比较特殊,官方的解释是在goole play下载的时候,会提示用户是否给予权限,同意了才能下载。下载完应用会即刻拥有SYSTEM_ALERT_WINDOW的权限,即无需手动授权。
然而,对于自己写的应用,只能通过动态权限申请,上述代码在原生的机器上,不管是自带的模拟器还是Genymotion,都可以显示权限申请界面,但是在我们国产的手机(乱改系统)的情况下,很多手机都把SYSTEM_ALERT_WINDOW这个权限默认直接禁止了,所以说我们只能手动赋予权限。(即设置->安装应用->FloatMusic->权限管理->浮窗权限)否则,浮窗默认是不显示的。

最后,附上代码链接:https://github.com/RuijiePan/FinalMusic.git

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值