这是来到公司的第一个小项目。做一个音乐播放器,内容如下
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->权限管理->浮窗权限)否则,浮窗默认是不显示的。