woong's

Android VideoView Window , Service 이용 사용하기 본문

Develop/Android

Android VideoView Window , Service 이용 사용하기

dlsdnd345 2016. 2. 14. 18:21

Android VideoView Window , Service 이용 사용하기


안녕하세요. 이번에 안드로이드 비디오뷰를 앱을 종료 후 Task Kill 후에도 사용할 일이 있어 구현해보았습니다.


Service Aidl 을 이용하여 통신을 구현 하였고 , Window 를 통해서 앱이 죽어도 실행되는 비디오 뷰를 만들었습니다.


비디오뷰 사이즈 조정 기능

비디오뷰 앱 종료시도 동작 기능

비디오뷰 싱글 탭시 앱이동 기능


코드는 Aidl 이 들어있어야 해서 첨부파일에 함께 첨부 하도록 하겠습니다.


주요코드


 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
package com.example.videoviewwindow;
 
import android.app.Activity;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
 
import com.example.service.IMessageService;
import com.example.service.IMessageServiceCallback;
import com.example.service.VideoService;
 
public class MainActivity extends Activity {
 
    /**
     * 서비스 Bind 체크
     */
    private boolean isBind;
    
    private int deviceWidth;
    private int deviceHeight;
    
    private Intent intent;
    private Button btnOnAir;
    private Button btnOffAir;
 
    private IMessageService            messageService;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        initLayout();
        initData();
    }
 
    @Override
    protected void onDestroy() {
        super.onDestroy();
        
        isBind = false;
    }
    
    /**
     * 레이아웃초기화
     */
    private void initLayout() {
        
        btnOnAir     = (Button) this.findViewById(R.id.btnOnAir);
        btnOffAir     = (Button) this.findViewById(R.id.btnOffAir);
 
        btnOnAir.setOnClickListener(mOnClickListener);
        btnOffAir.setOnClickListener(mOnClickListener);
    }
 
    /**
     * 데이터 초기화
     */
    private void initData(){
        
        //해상도 반환
        getresolation();
    }
 
    /**
     * 앱종료
     */
    private void finishApp(){
        finish();
    }
    
    /**
     * 해상도 값 반환
     */
    private void getresolation() {
        DisplayMetrics displayMetrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
        deviceWidth = displayMetrics.widthPixels;
        deviceHeight = displayMetrics.heightPixels;
    }
 
    /**
     * 서비스 시작
     */
    private void startService(){
        // 명시적 등록
        intent = new Intent(MainActivity.this, VideoService.class);
        //서비스 시작
        startService(intent);
        //서비스와 통신을 하기 위함.
        isBind = bindService(intent,conn, Context.BIND_AUTO_CREATE);
    }
 
    /**
     * 서비스종료
     */
    private void finishService(){
        //서비스 종료
        if(intent != null){
            if(isBind){
                unbindService(conn);
            }
            stopService(intent);
            intent = null;
        }
    }
 
    /**
     * 액티비티에서 서비스 호출할때 사용
     */
    private ServiceConnection conn = new ServiceConnection (){
        public void onServiceDisconnected(ComponentName name){}
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            messageService = IMessageService.Stub.asInterface(service);
 
            try { 
                //서비스 와 연결 작업
                messageService.registerCallback(callback);
            } catch (RemoteException e1) {
                e1.printStackTrace();
            }
 
            //메세지 전송
            try {
                messageService.sendMessage("3:53",deviceWidth,deviceHeight);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    };
 
    /**
     * 서비스에서 액티비티 호출시 실행되는 리스너
     */
    private IMessageServiceCallback callback = new IMessageServiceCallback.Stub() 
    {
        @Override
        public void finishCallback() throws RemoteException {
 
            System.out.println("서비스에서 호출 ");
            finishService();
        }
 
        @Override
        public void startAppCallback() throws RemoteException {
            
            finishService();
            Intent intent = new Intent(MainActivity.this,MainActivity.class);
            startActivity(intent);
        }
 
        @Override
        public void finishAppCallback() throws RemoteException {
            finishApp();
        }
    };
 
    /**
     * 클릭 리스너
     */
    private OnClickListener mOnClickListener = new OnClickListener() {
 
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                //시작
            case R.id.btnOnAir:
                startService();
                break;
                //종료
            case R.id.btnOffAir:
                finishService();
                break;
            }
        }
    };
 
}
 
 
cs


​Service 코드 


 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
 
package com.example.service;
 
import android.annotation.SuppressLint;
import android.app.Notification;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnPreparedListener;
import android.net.Uri;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.FloatMath;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.RelativeLayout;
import android.widget.VideoView;
 
import com.example.videoviewwindow.R;
 
public class VideoService extends Service {
 
    private static final int MODE_NONE = 0;
    private static final int MODE_ONE_TOUCH = 1;
    private static final int MODE_DOUBLE_TOUCH = 2;
 
    private int mTouchMode = MODE_NONE;
 
    /**
     * 터치시 탭 모션 인지
     */
    private boolean isSingleTap;
    
    private float START_X;
    private float START_Y;
 
    private float distance;
    
    private int PREV_X;
    private int PREV_Y;
 
    /**
     * 해상도 값
     */
    private int mWidth;
    private int mHeight;
 
    /**
     * 비디오 뷰 크기
     */
    private int videoWidth;
    private int videoHeight;
 
    private WindowManager mWindowManager;
    private WindowManager.LayoutParams windowParams;
 
    private Button btnClose;
 
    private RelativeLayout mOverlay;
    private RelativeLayout layoutVideo;
 
    private VideoView videoViewPlayer;
 
    private RemoteCallbackList<IMessageServiceCallback> callbackList =
            new RemoteCallbackList<IMessageServiceCallback> ();
 
    @Override
    public IBinder onBind(Intent intent) {
        return serviceStub;
    }
 
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
 
        /**
         * 서비스 런처에서 킬해도 죽지 않도록 설정
         */
        startForeground(startId, new Notification());
        return super.onStartCommand(intent, flags, startId);
    }
 
    @Override
    public void onDestroy() {
        super.onDestroy();
        finishVideo();
        stopForeground(true);
    }    
 
    /**
     * 비디오 종료
     */
    private void finishVideo(){
        mWindowManager.removeView(mOverlay);
    }
 
    /**
     * 윈도우 매니저 init하는 메소드
     */
    private void initWindowManager() {
        windowParams = new WindowManager.LayoutParams(
                mWidth/2,
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.TYPE_PHONE, // 항상 최 상위. 터치 이벤트 받을 수
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, // 포커스를 가지지 않음
                PixelFormat.TRANSLUCENT);
        windowParams.gravity = Gravity.LEFT | Gravity.TOP;
        mWindowManager = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
    }
 
    /**
     * 레이아웃초기화
     */
    private void initLayout() {
 
        // 윈도우 Layout init 하는 부분
        LayoutInflater mInflater = LayoutInflater.from(this);
 
        mOverlay         = (RelativeLayout) mInflater.inflate(R.layout.activity_video_window, null);
        layoutVideo        = (RelativeLayout) mOverlay.findViewById(R.id.layoutVideo);
        btnClose         = (Button) mOverlay.findViewById(R.id.btnClose);
        videoViewPlayer = (VideoView) mOverlay.findViewById(R.id.videoViewPlayer);
        
        mWindowManager.addView(mOverlay, windowParams);
 
        btnClose.setOnClickListener(mOnClickListener);
        videoViewPlayer.setOnTouchListener(mOnTouchListener);        
        videoViewPlayer.setOnPreparedListener(mNativePreparedListener);
 
    }
 
 
    /**
     * 데이터 초기화
     */
    private void initData(){
        videoDataInit();
    }
 
    /**
     * 비디오뷰 데이터 초기화
     */
    private void videoDataInit() {
        String tempVideoURL = "http://www.boisestatefootball.com/sites/default/files/videos/original/01%20-%20coach%20pete%20bio_4.mp4";
        Uri uri = Uri.parse(tempVideoURL);
        videoViewPlayer.setVideoURI(uri);
    }
 
 
    @SuppressLint("FloatMath")
    private float doubleTouchDistance(MotionEvent event) {
        
        float x = event.getX(0) - event.getX(1);
        float y = event.getY(0) - event.getY(1);
        distance = (float) (FloatMath.sqrt(x * x + y * y));
        
        //화면 밖으로 나가지 못하도록 Block
        if(distance >= mWidth){
            distance = mWidth ;
            return distance;
        }
        
        return distance;
    }
 
    /**
     * 액티비티에서 넘어온 데이터 수신
     */
    public IMessageService.Stub serviceStub = new IMessageService.Stub() {
 
        @Override
        public void sendMessage(String message,int width , int height) throws RemoteException {
 
            mWidth = width;
            mHeight = height;
 
            initWindowManager();
            initLayout();
            initData();
        }
 
        @Override
        public void registerCallback(IMessageServiceCallback callback)throws RemoteException {
            callbackList.register(callback);
        }
 
        @Override
        public void unregisterCallback(IMessageServiceCallback callback)throws RemoteException {
            callbackList.unregister(callback);
        }
 
    };
 
    /**
     * 비디오 로드가 완료 되면 불리는 리스너
     */
    private OnPreparedListener mNativePreparedListener = new OnPreparedListener() {
 
        @Override
        public void onPrepared(MediaPlayer mp) {
            
            layoutVideo.setVisibility(View.VISIBLE);
            
            videoViewPlayer.start();
 
            videoWidth  = videoViewPlayer.getWidth();
            videoHeight = videoViewPlayer.getHeight();
 
            int cnt = callbackList.beginBroadcast();
            for (int i = 0 ; i < cnt ; i++ ){
                try {
                    callbackList.getBroadcastItem(i).finishAppCallback();
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
            callbackList.finishBroadcast();
        }
    };
 
    /**
     * 클릭 리스너
     */
    private Button.OnClickListener mOnClickListener = new View.OnClickListener() {
 
        @Override
        public void onClick(View v) {
 
            switch (v.getId()) {
            // 비디오뷰 종료 버튼
            case R.id.btnClose:
 
                int cnt = callbackList.beginBroadcast();
                for (int i = 0 ; i < cnt ; i++ ){
                    try {
                        callbackList.getBroadcastItem(i).finishCallback();
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
                callbackList.finishBroadcast();
 
                break;
            }
        }
    };
 
    /**
     * 터치 리스너
     */
    private OnTouchListener mOnTouchListener = new OnTouchListener() {
 
        @Override
        public boolean onTouch(View v, MotionEvent event) {
 
            switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
 
                isSingleTap = true;
                mTouchMode = MODE_ONE_TOUCH;
                btnClose.setVisibility(View.INVISIBLE);
 
                START_X = event.getRawX();
                START_Y = event.getRawY();
                PREV_X = windowParams.x;
                PREV_Y = windowParams.y;
                
                break;
 
            case MotionEvent.ACTION_MOVE:    // 이동 시
                
                if(mTouchMode == MODE_ONE_TOUCH){
                    int x = (int) (event.getRawX() - START_X);
                    int y = (int) (event.getRawY() - START_Y);
 
                    windowParams.x = PREV_X + x;
                    windowParams.y = PREV_Y + y;
 
                    mWindowManager.updateViewLayout(mOverlay, windowParams);    
                }
                if(mTouchMode == MODE_DOUBLE_TOUCH){
                    
                    isSingleTap = false;
                    float distance = doubleTouchDistance(event);
                    
                    windowParams.width = (int) distance;
 
                    mWindowManager.updateViewLayout(mOverlay, windowParams);
                }
                break;
 
            case MotionEvent.ACTION_UP:
                
                btnClose.setVisibility(View.VISIBLE);
                
                //싱글탭
                if( (PREV_X - 0.1 < windowParams.x  && windowParams.x < PREV_X +0.1 )  && isSingleTap){
                    
                    int cnt = callbackList.beginBroadcast();
                    for (int i = 0 ; i < cnt ; i++ ){
                        try {
                            callbackList.getBroadcastItem(i).startAppCallback();
                        } catch (RemoteException e) {
                            e.printStackTrace();
                        }
                    }
                    callbackList.finishBroadcast();
                }
                
                mWindowManager.updateViewLayout(mOverlay, windowParams);
                btnClose.setVisibility(View.VISIBLE);
                break;
 
            case MotionEvent.ACTION_POINTER_DOWN:    // 두 손가락 터치 시
                mTouchMode = MODE_DOUBLE_TOUCH;
                
                isSingleTap = false;
                
                break;
 
            case MotionEvent.ACTION_POINTER_UP:        // 두 손가락을 떼었을 시
                
                isSingleTap = false;
                mTouchMode = MODE_NONE;
                break;
            }
            return true;
        }
    };
 
}
 
cs
 





 


Comments