[Android] (Android video floating window)

He recently Android-related projects to do, so retired and sit a bit of time to explore the floating window to play the video.
# 1 [ preparation ]
## 1.1 MediaPlayer is used
since it is floating window to play the video, then we must first understand how Android video player. Here to do a brief introduction.
Video playback invoking step:
(1) loading playing resource MediaPlayer.setDataSource (), also why the use MediaPlay.oncreate (context, R.raw.video);
(2) create in layout.xml surfaceView; and.
(3) call MediaPlayer.setDisplay () provided surfaceHolder, surfaceHolder () method was adopted surfaceView.getHolder;
. (. 4) calls MediaPlayer.prepare () prepared;
. (5) calls MediaPlayer.start () to play the video.
Before the third step to ensure surfaceHolder ready. It is necessary to set up a surfaceHolder callBack, call addCallback (), call addCallBack () method callBack has three callback functions:

     	SurfaceHolder.Callback {
        @Override
     	// 待surfaceCreated回调,做MediaPlayer.setDisplay()
        public void surfaceCreated(SurfaceHolder holder) {
        		MediaPlayer.setDisplay();
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
        }
    }     
以上是准备工作,简介介绍,如果看不懂可以参考  https://www.cnblogs.com/yxx123/p/5720907.html, 也可以后面看源码。

[ The principle ]
1.1 design ideas:
floating window screens need to inherit Service, rather than the Activity, Activity quit once it onStop out, so we need to inherit Service running in the background.
1.2 permission settings and application:
After Android 6.0 Permissions are most needed its own application and can not obtain. Here we need to apply two rights, a floating window is to start, because we play another network resource, you need permission.
1.3 suspension window settings:
This is another pit after Android 8.0, need to be adapted to different versions of Android
IF (Build.VERSION.SDK_INT> = Build.VERSION_CODES.O) {
layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY ;
} the else {
layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;
}
before Android 8.0, the suspension may be a window set TYPE_PHONE, this type of user interaction for providing a non-application window.
The Android 8.0 system and API behavior has been modified, including the use of SYSTEM_ALERT_WINDOW rights no longer apply to the following types of window displays and other applications reminder window in the top of the window.

  • TYPE_PHONE
  • TYPE_PRIORITY_PHONE
  • TYPE_SYSTEM_ALERT
  • TYPE_SYSTEM_OVERLAY
  • TYPE_SYSTEM_ERROR
    If you want a reminder window is displayed at the top of the window and other applications, which must be a new type of TYPE_APPLICATION_OVERLAY.
      If you are still using Android 8.0 or later TYPE_PHONE type of floating window, the following message appears abnormal
      android.view.WindowManager B a d T O k e n E x c e p t i O n : The n a b l e t O a d d w i n d O w a n d r O i d . v i e w . V i e w R o o t I m p l BadTokenException: Unable to add window android.view.ViewRootImpl W is @ f8ec928 - permission denied for window type 2006
    [realization]
    3.1 start floating window and open the permissions in MainActivity
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 获取权限。如果当前获取了权限,直接就启动悬浮窗画面FloatingWindowService.class
        if (!Settings.canDrawOverlays(this)) {
            Toast.makeText(this, "当前无权限,请授权", Toast.LENGTH_SHORT);
            startActivityForResult(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())), 1111);
        } else {
            startService(new Intent(MainActivity.this, FloatingWindowService.class));
        }
      // 炮灰activity,启动悬浮窗之后就退掉
        finish();
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // 判断获取权限是否成功
    if (requestCode == 1111) {
        if (!Settings.canDrawOverlays(this)) {
            Toast.makeText(this, "授权失败", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(this, "授权成功", Toast.LENGTH_SHORT).show();
            startService(new Intent(MainActivity.this, FloatingService.class));
        }
    }

Floating window screen 3.2

public class FloatingWindowService extends Service {

    private WindowManager windowManager;
    private WindowManager.LayoutParams layoutParams;
    private View display;
    private SurfaceHolder surfaceHolder;
    private SurfaceView surfaceView;

    public FloatingWindowService() {
    }

    @Override
    public void onCreate() {
        super.onCreate();
        windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
        layoutParams = new WindowManager.LayoutParams();
        Log.d("悬浮窗", "Build.VERSION.SDK_INT" + Build.VERSION.SDK_INT);
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
        // android 8.0及以后使用
            layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
        } else {
        // android 8.0以前使用
            layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;
        }
        layoutParams.gravity = Gravity.LEFT | Gravity.TOP;
        //该flags描述的是窗口的模式,是否可以触摸,可以聚焦等
        layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
        // 设置视频的播放窗口大小
        layoutParams.width = 800;
        layoutParams.height = 450;
        layoutParams.x = 300;
        layoutParams.y = 300;
    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        showFloatingWindow();
        return super.onStartCommand(intent, flags, startId);

    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return null;
    }

    private void showFloatingWindow(){
    	
        if (Settings.canDrawOverlays(this)) {
            LayoutInflater layoutInflater = LayoutInflater.from(this);
            display = layoutInflater.inflate(R.layout.video_display, null);
            surfaceView = display.findViewById(R.id.videoplayer_display);
            // 获取surfaceView的sourfaceHolder
            surfaceHolder = surfaceView.getHolder();
            final MediaPlayer mediaPlayer = new MediaPlayer();
            // 设置视频播放流类型
            mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
            // 视频资源网址
            Uri uri = Uri.parse("https://raw.githubusercontent.com/dongzhong/ImageAndVideoStore/master/Bruno%20Mars%20-%20Treasure.mp4");

            try {
            // 设置视频播放资源,这里如果前面调用了MediaPlayer.create(contex, R.raw.video),就不用再次调用了
                mediaPlayer.setDataSource(this,uri);
            } catch (IOException e) {
                e.printStackTrace();
            }

            surfaceHolder.addCallback(new SurfaceHolder.Callback() {
                @Override
                public void surfaceCreated(SurfaceHolder holder) {
                // 视频播放设置
                    mediaPlayer.setDisplay(surfaceHolder);
                }

                @Override
                public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

                }

                @Override
                public void surfaceDestroyed(SurfaceHolder holder) {

                }
            });
            mediaPlayer.prepareAsync();

            mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
                @Override
                public void onPrepared(MediaPlayer mp) {
                // 待视频资源准备好了,回调中播放视频资源,
                    mediaPlayer.start();
                    //循环播放
                    mediaPlayer.setLooping(true);
                }
            });
            windowManager.addView(display, layoutParams);
            display.setOnTouchListener(new FloatingOnTouchListener());

        }
    }

// touch移动视频窗口
    private class FloatingOnTouchListener implements View.OnTouchListener {
        private int x;
        private int y;
        @Override
        public boolean onTouch(View view, MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    x = (int) event.getRawX();
                    y = (int) event.getRawY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    int nowX = (int) event.getRawX();
                    int nowY = (int) event.getRawY();
                    int movedX = nowX - x;
                    int movedY = nowY - y;
                    Log.d("悬浮窗", "movedX = " + movedX + ", movedY =" + movedY);
                    x = nowX;
                    y = nowY;
                    layoutParams.x = layoutParams.x + movedX;
                    layoutParams.y = layoutParams.y + movedY;
                    windowManager.updateViewLayout(view, layoutParams);
                    break;
                default:
                    break;
            }
            return false;
        }
    }
}

3.3 AndroidManifest.xml

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service
            android:name=".FloatingWindowService"
            android:enabled="true"
            android:exported="true"></service>
    </application>

3.4 video_display.xml

 <FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <SurfaceView
        android:id="@+id/videoplayer_display"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</FrameLayout>

These are the main codes
complete code can be executed directly https://download.csdn.net/download/lisiwei1994/10751626
a reference for https://blog.csdn.net/dongzhong1990/article/details/80512706

Published 14 original articles · won praise 4 · Views 3524

Guess you like

Origin blog.csdn.net/lisiwei1994/article/details/83509715