├── .gitignore
├── .idea
├── .name
├── compiler.xml
├── copyright
│ └── profiles_settings.xml
├── dbnavigator.xml
├── encodings.xml
├── gradle.xml
├── libraries
│ ├── appcompat_v7_23_1_1.xml
│ ├── design_23_1_1.xml
│ ├── hamcrest_core_1_3.xml
│ ├── junit_4_12.xml
│ ├── recyclerview_v7_23_1_1.xml
│ ├── support_annotations_23_1_1.xml
│ └── support_v4_23_1_1.xml
├── misc.xml
├── modules.xml
├── vcs.xml
└── workspace.xml
├── LICENSE.TXT
├── README.md
├── VideoChatHeads.iml
├── android-facebook-like-chat-head-master.iml
├── app
├── .gitignore
├── app.iml
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── takeoffandroid
│ │ └── videochatheads
│ │ ├── activities
│ │ └── MainActivity.java
│ │ ├── services
│ │ └── VideoChatHeadService.java
│ │ ├── utils
│ │ ├── Constants.java
│ │ └── Utils.java
│ │ └── views
│ │ ├── GLRoundedGeometry.java
│ │ ├── MultiSampleEGLConfigChooser.java
│ │ └── VideoSurfaceView.java
│ └── res
│ ├── drawable
│ ├── background_shade.png
│ ├── drawable_circle_white.xml
│ ├── ic_close.png
│ ├── ic_pause.xml
│ └── ic_play.xml
│ ├── layout
│ ├── activity_main.xml
│ ├── view_layout_chat_heads.xml
│ └── view_layout_close.xml
│ ├── menu
│ └── menu_main.xml
│ ├── mipmap-hdpi
│ └── ic_launcher.png
│ ├── mipmap-mdpi
│ └── ic_launcher.png
│ ├── mipmap-xhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxxhdpi
│ └── ic_launcher.png
│ ├── raw
│ └── sample_video.mp4
│ ├── values-v21
│ └── styles.xml
│ └── values
│ ├── colors.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── projectFilesBackup
└── .idea
│ └── workspace.xml
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | lt application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the Dalvik VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # Generated files
12 | bin/
13 | gen/
14 |
15 | # Gradle files
16 | .gradle/
17 | build/
18 |
19 | # Local configuration file (sdk path, etc)
20 | local.properties
21 |
22 | # Proguard folder generated by Eclipse
23 | proguard/
24 |
25 | # Log Files
26 | *.log
27 |
28 | # Android Studio Navigation editor temp files
29 | .navigation/
30 |
31 | # Android Studio captures folder
32 | captures/
33 |
34 |
35 |
36 |
37 | .idea/gradle.xml
38 |
39 | .idea/gradle.xml
40 |
41 | .idea/libraries/animated_vector_drawable_23_2_1.xml
42 |
43 | .idea/libraries/appcompat_v7_23_2_1.xml
44 |
45 | .idea/libraries/animated_vector_drawable_23_2_1.xml
46 |
47 | .idea/libraries/design_23_2_1.xml
48 |
49 | .idea/gradle.xml
50 |
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | VideoChatHeads
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/dbnavigator.xml:
--------------------------------------------------------------------------------
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 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/.idea/libraries/appcompat_v7_23_1_1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/libraries/design_23_1_1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/libraries/hamcrest_core_1_3.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/libraries/junit_4_12.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/libraries/recyclerview_v7_23_1_1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/libraries/support_annotations_23_1_1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/libraries/support_v4_23_1_1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
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 | 1.7
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/LICENSE.TXT:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or
4 | distribute this software, either in source code form or as a compiled
5 | binary, for any purpose, commercial or non-commercial, and by any
6 | means.
7 |
8 | In jurisdictions that recognize copyright laws, the author or authors
9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
24 | For more information, please refer to
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | []()
2 |
3 |
4 |
5 | # VideoChatHeads
6 |
7 | Why this repo?
8 | --------------
9 |
10 | This repo enables you to show videos instead of usual profile picture for chat heads. Inspired from Facebook's Video profile picture, I decided to replicate its implementation to show videos in profile picture and to create chat heads for the same.
11 |
12 |
13 |
14 |
15 |
16 | > **Note:** This repo is a custom implementation for VideoChatHeads. Any kinds of suggestions or pull request are welcome and appreciated.
17 |
18 | ```
19 | This is free and unencumbered software released into the public domain.
20 |
21 | Anyone is free to copy, modify, publish, use, compile, sell, or
22 | distribute this software, either in source code form or as a compiled
23 | binary, for any purpose, commercial or non-commercial, and by any
24 | means.
25 |
26 | In jurisdictions that recognize copyright laws, the author or authors
27 | of this software dedicate any and all copyright interest in the
28 | software to the public domain. We make this dedication for the benefit
29 | of the public at large and to the detriment of our heirs and
30 | successors. We intend this dedication to be an overt act of
31 | relinquishment in perpetuity of all present and future rights to this
32 | software under copyright law.
33 |
34 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
35 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
36 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
37 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
38 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
39 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
40 | OTHER DEALINGS IN THE SOFTWARE.
41 |
42 | For more information, please refer to
43 |
44 | ```
45 |
--------------------------------------------------------------------------------
/VideoChatHeads.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/android-facebook-like-chat-head-master.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/app.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | generateDebugSources
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 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 23
5 | buildToolsVersion "23.0.2"
6 |
7 | defaultConfig {
8 | applicationId "com.takeoffandroid.videochatheads"
9 | minSdkVersion 14
10 | targetSdkVersion 23
11 | versionCode 1
12 | versionName "1.0"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | compile fileTree(dir: 'libs', include: ['*.jar'])
24 | testCompile 'junit:junit:4.12'
25 | compile 'com.android.support:appcompat-v7:23.1.1'
26 | compile 'com.android.support:design:23.1.1'
27 | }
28 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in C:\Program Files\Eclipse\sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
16 |
17 |
18 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/app/src/main/java/com/takeoffandroid/videochatheads/activities/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.takeoffandroid.videochatheads.activities;
2 |
3 | import android.content.Intent;
4 | import android.net.Uri;
5 | import android.os.Build;
6 | import android.os.Bundle;
7 | import android.provider.Settings;
8 | import android.support.design.widget.FloatingActionButton;
9 | import android.support.v7.app.AppCompatActivity;
10 | import android.support.v7.widget.Toolbar;
11 | import android.view.View;
12 |
13 | import com.takeoffandroid.videochatheads.R;
14 | import com.takeoffandroid.videochatheads.services.VideoChatHeadService;
15 |
16 | public class MainActivity extends AppCompatActivity {
17 |
18 | // UI
19 | private Toolbar toolbar;
20 | private FloatingActionButton fab;
21 |
22 | @Override
23 | protected void onCreate(Bundle savedInstanceState) {
24 | super.onCreate(savedInstanceState);
25 | setContentView(R.layout.activity_main);
26 |
27 | toolbar = (Toolbar) findViewById(R.id.toolbar_activity_main);
28 |
29 | setUiSettings();
30 | }
31 |
32 | @Override
33 | protected void onResume() {
34 | super.onResume();
35 |
36 | initListeners();
37 |
38 | }
39 |
40 | private void initListeners() {
41 |
42 | if(Build.VERSION.SDK_INT >= 23) {
43 | if (!Settings.canDrawOverlays(MainActivity.this)) {
44 | Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
45 | Uri.parse("package:" + getPackageName()));
46 | startActivityForResult(intent, 1234);
47 | }else{
48 | Intent i = VideoChatHeadService.getIntent(MainActivity.this);
49 | startService(i);
50 | MainActivity.this.finish();
51 | }
52 | }
53 | else
54 | {
55 | Intent i = VideoChatHeadService.getIntent(MainActivity.this);
56 | startService(i);
57 | MainActivity.this.finish();
58 | }
59 |
60 | }
61 |
62 | @Override
63 | public void onActivityReenter(int resultCode, Intent data) {
64 |
65 | if(resultCode == 1234){
66 |
67 | Intent i = VideoChatHeadService.getIntent(MainActivity.this);
68 | startService(i);
69 | MainActivity.this.finish();
70 | }
71 | super.onActivityReenter(resultCode, data);
72 | }
73 |
74 | private void setUiSettings() {
75 | setSupportActionBar(toolbar);
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/app/src/main/java/com/takeoffandroid/videochatheads/services/VideoChatHeadService.java:
--------------------------------------------------------------------------------
1 | package com.takeoffandroid.videochatheads.services;
2 |
3 | import android.annotation.TargetApi;
4 | import android.app.Service;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.graphics.PixelFormat;
8 | import android.media.MediaPlayer;
9 | import android.os.Build;
10 | import android.os.Handler;
11 | import android.os.IBinder;
12 | import android.os.Vibrator;
13 | import android.support.annotation.Nullable;
14 | import android.util.DisplayMetrics;
15 | import android.util.Log;
16 | import android.view.Gravity;
17 | import android.view.LayoutInflater;
18 | import android.view.MotionEvent;
19 | import android.view.SurfaceHolder;
20 | import android.view.View;
21 | import android.view.WindowManager;
22 | import android.widget.ImageView;
23 | import android.widget.Toast;
24 |
25 | import com.takeoffandroid.videochatheads.R;
26 | import com.takeoffandroid.videochatheads.utils.Constants;
27 | import com.takeoffandroid.videochatheads.utils.Utils;
28 | import com.takeoffandroid.videochatheads.views.VideoSurfaceView;
29 |
30 | import java.io.IOException;
31 |
32 |
33 | public class VideoChatHeadService extends Service implements SurfaceHolder.Callback {
34 |
35 | // constants
36 | public static final String BASIC_TAG = VideoChatHeadService.class.getName();
37 |
38 | // variables
39 | private WindowManager mWindowManager;
40 | private Vibrator mVibrator;
41 | private WindowManager.LayoutParams mVideoViewParams;
42 | private WindowManager.LayoutParams mCloseViewParams;
43 | private int windowHeight;
44 | private int windowWidth;
45 |
46 | private View closeView, chatHeadsView;
47 | private LayoutInflater liClose, liChatHeads;
48 |
49 | private ImageView ivCloseView, imgPlayPause;
50 |
51 |
52 | private VideoSurfaceView[] mVideoSurfaceView = new VideoSurfaceView[1];
53 | private MediaPlayer mediaPlayer;
54 | private boolean isMediaPrepared, isPlaying;
55 |
56 |
57 | public static Intent getIntent(Context context) {
58 | Intent intent = new Intent(context, VideoChatHeadService.class);
59 | return intent;
60 | }
61 |
62 | // methods
63 | @Override
64 | public void onCreate() {
65 | super.onCreate();
66 | }
67 |
68 | @Override
69 | public int onStartCommand(Intent intent, int flags, int startId) {
70 | showChatHeades();
71 |
72 | return START_STICKY;
73 | }
74 |
75 | private void showChatHeades() {
76 | mWindowManager = (WindowManager) getSystemService(Service.WINDOW_SERVICE);
77 | mVibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
78 | liChatHeads = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
79 |
80 | if (chatHeadsView != null) {
81 | mWindowManager.removeView(chatHeadsView);
82 | mVideoSurfaceView[0] = null;
83 | }
84 |
85 | mVideoViewParams = new WindowManager.LayoutParams(
86 | WindowManager.LayoutParams.WRAP_CONTENT,
87 | WindowManager.LayoutParams.WRAP_CONTENT,
88 | WindowManager.LayoutParams.TYPE_PHONE,
89 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
90 |
91 | PixelFormat.TRANSLUCENT);
92 |
93 |
94 | DisplayMetrics displaymetrics = new DisplayMetrics();
95 | mWindowManager.getDefaultDisplay().getMetrics(displaymetrics);
96 | windowHeight = displaymetrics.heightPixels;
97 | windowWidth = displaymetrics.widthPixels;
98 |
99 | mVideoViewParams.gravity = Gravity.TOP | Gravity.RIGHT;
100 | mVideoViewParams.height = Constants.VIDEO_VIEW_CIRCLE_SIZE;
101 | mVideoViewParams.width = Constants.VIDEO_VIEW_CIRCLE_SIZE;
102 |
103 |
104 | // final int radius = getResources()
105 | // .getDimensionPixelOffset(R.dimen.corner_radius_video);
106 |
107 |
108 |
109 | chatHeadsView = liChatHeads.inflate(R.layout.view_layout_chat_heads, null);
110 |
111 | mVideoSurfaceView[0] = (VideoSurfaceView) chatHeadsView.findViewById(R.id.video_surface_view);
112 |
113 | imgPlayPause = (ImageView) chatHeadsView.findViewById(R.id.img_play_pause);
114 |
115 |
116 | mVideoSurfaceView[0].setCornerRadius(Constants.VIDEO_VIEW_CORNER_RADIUS);
117 |
118 | mVideoViewParams.x = 0;
119 | mVideoViewParams.y = 50;
120 |
121 | mWindowManager.addView(chatHeadsView, mVideoViewParams);
122 |
123 | mediaPlayer = new MediaPlayer();
124 | final VideoSurfaceView surfaceView = mVideoSurfaceView[0];
125 | final String dataSource = "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4";
126 | try {
127 | mediaPlayer.setDataSource(dataSource);
128 | // the video view will take care of calling prepare and attaching the surface once
129 | // it becomes available
130 | mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
131 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
132 | @Override
133 | public void onPrepared(MediaPlayer mp) {
134 | Utils.setBackground(VideoChatHeadService.this, imgPlayPause, R.drawable.ic_play);
135 |
136 | Handler handler = new Handler();
137 |
138 | handler.postDelayed(new Runnable() {
139 | @Override
140 | public void run() {
141 |
142 | imgPlayPause.setVisibility(View.GONE);
143 | isMediaPrepared = true;
144 |
145 | isPlaying = true;
146 | mediaPlayer.start();
147 | surfaceView.setVideoAspectRatio((float) mediaPlayer.getVideoWidth() /
148 | (float) mediaPlayer.getVideoHeight());
149 |
150 |
151 | }
152 | }, 1000);
153 |
154 |
155 | }
156 |
157 |
158 |
159 | });
160 |
161 | surfaceView.setMediaPlayer(mediaPlayer);
162 | } catch (IOException e) {
163 | e.printStackTrace();
164 | mediaPlayer.release();
165 | }
166 | addViewOnTouchListener();
167 | }
168 |
169 |
170 | private void addViewOnTouchListener() {
171 |
172 |
173 | chatHeadsView.setOnTouchListener(new View.OnTouchListener() {
174 | private int initialX;
175 | private int initialY;
176 | private float initialTouchX;
177 | private float initialTouchY;
178 |
179 | @Override
180 | public boolean onTouch(View v, MotionEvent event) {
181 | switch (event.getAction()) {
182 | case MotionEvent.ACTION_DOWN:
183 |
184 |
185 | initialX = mVideoViewParams.x;
186 | initialY = mVideoViewParams.y;
187 | initialTouchX = event.getRawX();
188 | initialTouchY = event.getRawY();
189 | // add closeview when moving video view
190 | addCloseView();
191 |
192 | return true;
193 | case MotionEvent.ACTION_UP:
194 |
195 | if ((Math.abs(initialTouchX - event.getRawX()) < 5) && (Math.abs(initialTouchY - event.getRawY()) < 5)) {
196 |
197 |
198 | if (isMediaPrepared) {
199 | if (isPlaying) {
200 |
201 | Toast.makeText(VideoChatHeadService.this, "Pause Video", Toast.LENGTH_SHORT).show();
202 |
203 | isPlaying = false;
204 | imgPlayPause.setVisibility(View.VISIBLE);
205 | // Utils.setBackground(VideoChatHeadService.this, imgPlayPause, R.drawable.ic_pause);
206 |
207 | imgPlayPause.setImageResource(R.drawable.ic_pause);
208 | mediaPlayer.pause();
209 |
210 | } else {
211 |
212 | isPlaying = true;
213 |
214 | Toast.makeText(VideoChatHeadService.this, "Play Video", Toast.LENGTH_SHORT).show();
215 |
216 | imgPlayPause.setImageResource(R.drawable.ic_play);
217 |
218 | new Handler().postDelayed(new Runnable() {
219 | @Override
220 | public void run() {
221 | imgPlayPause.setVisibility(View.GONE);
222 | mediaPlayer.start();
223 |
224 | }
225 | }, 1000);
226 |
227 |
228 | }
229 | }
230 | }
231 |
232 | int centerOfScreenByX = windowWidth / 2;
233 |
234 | // remove video view when the it is in the close view area
235 | if ((mVideoViewParams.y > windowHeight - closeView.getHeight() - chatHeadsView.getHeight()) &&
236 | ((mVideoViewParams.x > centerOfScreenByX - ivCloseView.getWidth() - chatHeadsView.getWidth() / 2) && (mVideoViewParams.x < centerOfScreenByX + ivCloseView.getWidth() / 2))) {
237 | mVibrator.vibrate(100);
238 |
239 |
240 | if (isMediaPrepared) {
241 | mediaPlayer.stop();
242 |
243 | }
244 |
245 | stopSelf();
246 | }
247 |
248 |
249 | // always remove close view ImageView when video view is dropped
250 | mWindowManager.removeView(closeView);
251 | ivCloseView = null;
252 |
253 | return true;
254 | case MotionEvent.ACTION_MOVE:
255 | // move videoview ImageView
256 |
257 | mVideoViewParams.x = initialX + (int) (initialTouchX - event.getRawX());
258 | mVideoViewParams.y = initialY + (int) (event.getRawY() - initialTouchY);
259 | mWindowManager.updateViewLayout(chatHeadsView, mVideoViewParams);
260 | return true;
261 | }
262 | return false;
263 | }
264 | });
265 | }
266 |
267 | private void addCloseView() {
268 |
269 | liClose = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
270 |
271 | // add close view ImageView centered on the bottom of the screen
272 | mCloseViewParams = new WindowManager.LayoutParams(
273 | WindowManager.LayoutParams.WRAP_CONTENT,
274 | WindowManager.LayoutParams.WRAP_CONTENT,
275 | WindowManager.LayoutParams.TYPE_PHONE,
276 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
277 | PixelFormat.TRANSLUCENT);
278 |
279 | mCloseViewParams.gravity = Gravity.BOTTOM | Gravity.CENTER;
280 | mCloseViewParams.height = 400;
281 | mCloseViewParams.width = WindowManager.LayoutParams.MATCH_PARENT;
282 |
283 | closeView = liClose.inflate(R.layout.view_layout_close, null);
284 |
285 | ivCloseView = (ImageView) closeView.findViewById(R.id.img_close);
286 |
287 | mCloseViewParams.x = 0;
288 | mCloseViewParams.y = 0;
289 |
290 | mWindowManager.addView(closeView, mCloseViewParams);
291 | }
292 |
293 | @Nullable
294 | @Override
295 | public IBinder onBind(Intent intent) {
296 | return null;
297 | }
298 |
299 | @Override
300 | public void onDestroy() {
301 |
302 | // remove views on destroy!
303 | if (chatHeadsView != null) {
304 | mWindowManager.removeView(chatHeadsView);
305 | mVideoSurfaceView[0] = null;
306 |
307 | imgPlayPause = null;
308 | }
309 |
310 | if (ivCloseView != null) {
311 | mWindowManager.removeView(closeView);
312 | ivCloseView = null;
313 | }
314 |
315 | if (isMediaPrepared) {
316 | mediaPlayer.stop();
317 | }
318 |
319 | super.onDestroy();
320 |
321 | }
322 |
323 | @Override
324 | public void surfaceCreated(SurfaceHolder holder) {
325 | }
326 |
327 | @Override
328 | public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
329 |
330 | }
331 |
332 | @Override
333 | public void surfaceDestroyed(SurfaceHolder holder) {
334 |
335 |
336 | }
337 |
338 |
339 | }
340 |
--------------------------------------------------------------------------------
/app/src/main/java/com/takeoffandroid/videochatheads/utils/Constants.java:
--------------------------------------------------------------------------------
1 | package com.takeoffandroid.videochatheads.utils;
2 |
3 | /**
4 | * Created by chandrasekar on 14/11/16.
5 | */
6 |
7 | public class Constants {
8 |
9 | public static final int VIDEO_VIEW_CIRCLE_SIZE = 200;
10 |
11 | public static final int VIDEO_VIEW_CORNER_RADIUS = 102;
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/takeoffandroid/videochatheads/utils/Utils.java:
--------------------------------------------------------------------------------
1 | package com.takeoffandroid.videochatheads.utils;
2 |
3 | import android.content.Context;
4 | import android.graphics.drawable.Drawable;
5 | import android.os.Build;
6 | import android.util.DisplayMetrics;
7 | import android.util.Log;
8 | import android.util.TypedValue;
9 | import android.widget.ImageView;
10 |
11 | import com.takeoffandroid.videochatheads.R;
12 |
13 | /**
14 | * Created by chandrasekar on 28/10/16.
15 | */
16 |
17 | public class Utils {
18 |
19 |
20 | public static void setBackground(Context context, ImageView imageView, int drawableID) {
21 | if (Build.VERSION.SDK_INT >= 21) {
22 | imageView.setBackground(context.getResources().getDrawable(drawableID,null));
23 | }else if(Build.VERSION.SDK_INT >= 16){
24 | imageView.setBackground(context.getResources().getDrawable(drawableID));
25 | }else {
26 | imageView.setBackgroundDrawable(context.getResources().getDrawable(drawableID));
27 | }
28 |
29 | }
30 |
31 | public static float dipToPixels(Context context, float dipValue) {
32 | DisplayMetrics metrics = context.getResources().getDisplayMetrics();
33 | return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, dipValue, metrics);
34 |
35 |
36 |
37 | }
38 |
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/main/java/com/takeoffandroid/videochatheads/views/GLRoundedGeometry.java:
--------------------------------------------------------------------------------
1 | package com.takeoffandroid.videochatheads.views;
2 |
3 | import android.graphics.Point;
4 | import android.graphics.RectF;
5 | import android.support.annotation.NonNull;
6 |
7 |
8 | public class GLRoundedGeometry {
9 |
10 | // The key points of the geometry
11 | private float[] mLeftTop = new float[2];
12 | private float[] mLeftBottom = new float[2];
13 | private float[] mTopLeft = new float[2];
14 | private float[] mTopRight = new float[2];
15 | private float[] mRightTop = new float[2];
16 | private float[] mRightBottom = new float[2];
17 | private float[] mBottomLeft = new float[2];
18 | private float[] mBottomRight = new float[2];
19 |
20 | private float[] mInnerTopLeft = new float[2];
21 | private float[] mInnerTopRight = new float[2];
22 | private float[] mInnerBottomRight = new float[2];
23 | private float[] mInnerBottomLeft = new float[2];
24 |
25 | private float[] mTopLeftRadius = new float[2];
26 | private float[] mTopRightRadius = new float[2];
27 | private float[] mBottomRightRadius = new float[2];
28 | private float[] mBottomLeftRadius = new float[2];
29 |
30 | /**
31 | * @see #generateVertexData(RectF, RectF, Point, float)
32 | */
33 | @NonNull
34 | public GeometryArrays generateVertexData(@NonNull RectF radii, @NonNull RectF viewPortGLBounds,
35 | @NonNull Point viewPortPxSize) {
36 | return generateVertexData(radii, viewPortGLBounds, viewPortPxSize, 0f);
37 | }
38 |
39 | /**
40 | * Generates a {@link GeometryArrays} object with arrays containing the resulting geometry
41 | * vertices and the corresponding triangle indexes.
42 | *
43 | * @param radii the corner radius of each corner. left is topLeft, top is topRight, right is
44 | * rightBottom and bottom is leftBottom.
45 | * @param viewPortGLBounds the bounds of the GL viewport in GL scalar units.
46 | * @param viewPortPxSize the size of the view port in pixels.
47 | * @param z the z coordinate for the z-plane geometry.
48 | * @return an object with the resulting geometry.
49 | */
50 | @NonNull
51 | public GeometryArrays generateVertexData(@NonNull RectF radii, @NonNull RectF viewPortGLBounds,
52 | @NonNull Point viewPortPxSize, float z) {
53 | final float x0 = viewPortGLBounds.left;
54 | final float x1 = viewPortGLBounds.right;
55 | final float y0 = viewPortGLBounds.bottom;
56 | final float y1 = viewPortGLBounds.top;
57 |
58 | final float leftTopRadius = radii.left;
59 | final float rightTopRadius = radii.top;
60 | final float rightBottomRadius = radii.right;
61 | final float leftBottomRadius = radii.bottom;
62 |
63 | mTopLeftRadius[0] = leftTopRadius / viewPortPxSize.x * viewPortGLBounds.width();
64 | mTopLeftRadius[1] = leftTopRadius / viewPortPxSize.y * -viewPortGLBounds.height();
65 | mTopRightRadius[0] = rightTopRadius / viewPortPxSize.x * viewPortGLBounds.width();
66 | mTopRightRadius[1] = rightTopRadius / viewPortPxSize.y * -viewPortGLBounds.height();
67 | mBottomRightRadius[0] = rightBottomRadius / viewPortPxSize.x * viewPortGLBounds.width();
68 | mBottomRightRadius[1] = rightBottomRadius / viewPortPxSize.y * -viewPortGLBounds.height();
69 | mBottomLeftRadius[0] = leftBottomRadius / viewPortPxSize.x * viewPortGLBounds.width();
70 | mBottomLeftRadius[1] = leftBottomRadius / viewPortPxSize.y * -viewPortGLBounds.height();
71 |
72 | mLeftTop[0] = x0;
73 | mLeftTop[1] = y1 - mTopLeftRadius[1];
74 | mLeftBottom[0] = x0;
75 | mLeftBottom[1] = y0 + mBottomLeftRadius[1];
76 | mTopLeft[0] = x0 + mTopLeftRadius[0];
77 | mTopLeft[1] = y1;
78 | mTopRight[0] = x1 - mTopRightRadius[0];
79 | mTopRight[1] = y1;
80 | mRightTop[0] = x1;
81 | mRightTop[1] = y1 - mTopRightRadius[1];
82 | mRightBottom[0] = x1;
83 | mRightBottom[1] = y0 + mBottomRightRadius[1];
84 | mBottomLeft[0] = x0 + mBottomLeftRadius[0];
85 | mBottomLeft[1] = y0;
86 | mBottomRight[0] = x1 - mBottomRightRadius[0];
87 | mBottomRight[1] = y0;
88 |
89 | mInnerTopLeft[0] = mTopLeft[0];
90 | mInnerTopLeft[1] = mLeftTop[1];
91 | mInnerTopRight[0] = mTopRight[0];
92 | mInnerTopRight[1] = mRightTop[1];
93 | mInnerBottomLeft[0] = mBottomLeft[0];
94 | mInnerBottomLeft[1] = mLeftBottom[1];
95 | mInnerBottomRight[0] = mBottomRight[0];
96 | mInnerBottomRight[1] = mRightBottom[1];
97 |
98 | // Each vertex has 5 floats (xyz + uv)
99 | // 5 squares (each has 4 vertices)
100 | // 4 rounded corners (each has X triangles, each triangle has 3 vertices)
101 | final int trianglesPerCorner = 6;
102 | final int floatsPerRoundedCorner = (trianglesPerCorner + 2) * 5;
103 | final int floatsPerSquare = 4 * 5;
104 | final int shortsPerTriangle = 3;
105 | final int shortsPerSquare = 2 * shortsPerTriangle;
106 | final int verticesSize = 5 * floatsPerSquare + 4 * floatsPerRoundedCorner;
107 | final int indicesSize = 5 * shortsPerSquare + 4 * trianglesPerCorner * shortsPerTriangle;
108 | final float[] vertices = new float[verticesSize];
109 | final short[] indices = new short[indicesSize];
110 | final GeometryArrays geoArrays = new GeometryArrays(vertices, indices);
111 |
112 | // Inner center rect
113 | addRect(geoArrays, new float[][]{
114 | mInnerTopLeft, mInnerTopRight, mInnerBottomLeft, mInnerBottomRight},
115 | viewPortGLBounds, z);
116 | geoArrays.verticesOffset += floatsPerSquare;
117 | geoArrays.indicesOffset += shortsPerSquare;
118 |
119 | // Left rect
120 | addRect(geoArrays, new float[][]{
121 | mLeftTop, mInnerTopLeft, mLeftBottom, mInnerBottomLeft},
122 | viewPortGLBounds, z);
123 | geoArrays.verticesOffset += floatsPerSquare;
124 | geoArrays.indicesOffset += shortsPerSquare;
125 |
126 | // Right rect
127 | addRect(geoArrays, new float[][]{
128 | mInnerTopRight, mRightTop, mInnerBottomRight, mRightBottom},
129 | viewPortGLBounds, z);
130 | geoArrays.verticesOffset += floatsPerSquare;
131 | geoArrays.indicesOffset += shortsPerSquare;
132 |
133 | // Top rect
134 | addRect(geoArrays, new float[][]{
135 | mTopLeft, mInnerTopLeft, mTopRight, mInnerTopRight},
136 | viewPortGLBounds, z);
137 | geoArrays.verticesOffset += floatsPerSquare;
138 | geoArrays.indicesOffset += shortsPerSquare;
139 |
140 | // Bottom rect
141 | addRect(geoArrays, new float[][]{
142 | mInnerBottomLeft, mBottomLeft, mInnerBottomRight, mBottomRight},
143 | viewPortGLBounds, z);
144 | geoArrays.verticesOffset += floatsPerSquare;
145 | geoArrays.indicesOffset += shortsPerSquare;
146 |
147 | // These assume uniform corners (i.e. same radius on both axis)
148 | // Top left corner
149 | addRoundedCorner(geoArrays, mInnerTopLeft, mTopLeftRadius, (float) Math.PI,
150 | (float) (Math.PI / 2.0), trianglesPerCorner, viewPortGLBounds, z);
151 | geoArrays.verticesOffset += floatsPerRoundedCorner;
152 | geoArrays.indicesOffset += trianglesPerCorner * shortsPerTriangle;
153 |
154 | // Top right corner
155 | addRoundedCorner(geoArrays, mInnerTopRight, mTopRightRadius, (float) (Math.PI / 2), 0f,
156 | trianglesPerCorner, viewPortGLBounds, z);
157 | geoArrays.verticesOffset += floatsPerRoundedCorner;
158 | geoArrays.indicesOffset += trianglesPerCorner * shortsPerTriangle;
159 |
160 | // Bottom right corner
161 | addRoundedCorner(geoArrays, mInnerBottomRight, mBottomRightRadius,
162 | (float) (Math.PI * 3.0 / 2.0), (float) Math.PI * 2, trianglesPerCorner,
163 | viewPortGLBounds, z);
164 | geoArrays.verticesOffset += floatsPerRoundedCorner;
165 | geoArrays.indicesOffset += trianglesPerCorner * shortsPerTriangle;
166 |
167 | // Bottom left corner
168 | addRoundedCorner(geoArrays, mInnerBottomLeft, mBottomLeftRadius, (float) Math.PI,
169 | (float) (Math.PI * 3.0 / 2.0), trianglesPerCorner, viewPortGLBounds, z);
170 |
171 | return new GeometryArrays(vertices, indices);
172 | }
173 |
174 | /**
175 | * Adds the vertices of a rectangle defined by 4 corner points. The array of vertices passed
176 | * in must have the required length to add the geometry points (5 floats for each vertex). Also
177 | * the coordinates of the rect corners should already be in the view port space.
178 | *
179 | * @param geoArrays an object containing the vertex and index data arrays and their current
180 | * offsets.
181 | * @param rectPoints an array of corner points defining the rectangle. index 0 is the x
182 | * coordinate and index 1 the y coordinate.
183 | * @param viewPort the bounds of the current GL viewport, this is used to calculate the texture
184 | * mapping.
185 | * @param z the z coordinate.
186 | */
187 | private void addRect(@NonNull GeometryArrays geoArrays,
188 | @NonNull float[][] rectPoints,
189 | @NonNull RectF viewPort,
190 | float z) {
191 | final float[] vertices = geoArrays.triangleVertices;
192 | final short[] indices = geoArrays.triangleIndices;
193 | final int indicesOffset = geoArrays.indicesOffset;
194 | final int verticesOffset = geoArrays.verticesOffset;
195 | int rectPointIdx = 0;
196 | for (final float[] rectPoint : rectPoints) {
197 | // 5 values [xyzuv] per vertex
198 | final int currentVertexOffset = verticesOffset + rectPointIdx * 5;
199 |
200 | // XYZ (vertex space coordinates
201 | vertices[currentVertexOffset + 0] = rectPoint[0];
202 | vertices[currentVertexOffset + 1] = rectPoint[1];
203 | vertices[currentVertexOffset + 2] = z;
204 |
205 | // UV (texture mapping)
206 | vertices[currentVertexOffset + 3] = (rectPoint[0] - viewPort.left) / viewPort.width();
207 | vertices[currentVertexOffset + 4] = (rectPoint[1] - viewPort.bottom) / -viewPort.height();
208 |
209 | rectPointIdx++;
210 | }
211 |
212 | // Index our triangles -- tell where each triangle vertex is
213 | final int initialIdx = verticesOffset / 5;
214 | indices[indicesOffset + 0] = (short) (initialIdx);
215 | indices[indicesOffset + 1] = (short) (initialIdx + 1);
216 | indices[indicesOffset + 2] = (short) (initialIdx + 2);
217 | indices[indicesOffset + 3] = (short) (initialIdx + 1);
218 | indices[indicesOffset + 4] = (short) (initialIdx + 2);
219 | indices[indicesOffset + 5] = (short) (initialIdx + 3);
220 | }
221 |
222 | /**
223 | * Adds the vertices of a number of triangles to form a rounded corner. The triangles start at
224 | * some center point and will sweep from a given initial angle up to a final one. The size of
225 | * the triangles is defined by the radius.
226 | *
227 | * The array of vertices passed in must have the required length to add the geometry points
228 | * (5 floats for each vertex). Also the coordinates of the rect corners should already be in
229 | * the view port space.
230 | *
231 | * @param geoArrays an object containing the vertex and index data arrays and their current
232 | * offsets.
233 | * @param center the center point where all triangles will start.
234 | * @param radius the desired radius in the x and y axis, in viewport dimensions.
235 | * @param rads0 the initial angle.
236 | * @param rads1 the final angle.
237 | * @param triangles the amount of triangles to create.
238 | * @param viewPort the bounds of the current GL viewport, this is used to calculate the texture
239 | * mapping.
240 | * @param z the z coordinate.
241 | */
242 | private void addRoundedCorner(@NonNull GeometryArrays geoArrays,
243 | @NonNull float[] center,
244 | float[] radius,
245 | float rads0,
246 | float rads1,
247 | int triangles,
248 | @NonNull RectF viewPort,
249 | float z) {
250 | final float[] vertices = geoArrays.triangleVertices;
251 | final short[] indices = geoArrays.triangleIndices;
252 | final int verticesOffset = geoArrays.verticesOffset;
253 | final int indicesOffset = geoArrays.indicesOffset;
254 | for (int i = 0; i < triangles; i++) {
255 | // final int currentOffset = verticesOffset + i * 15 /* each triangle is 3 * xyzuv */;
256 | final int currentOffset = verticesOffset + i * 5 + (i > 0 ? 2 * 5 : 0);
257 | final float rads = rads0 + (rads1 - rads0) * (i / (float) triangles);
258 | final float radsNext = rads0 + (rads1 - rads0) * ((i + 1) / (float) triangles);
259 | final int triangleEdge2Offset;
260 |
261 | if (i == 0) {
262 | // XYZUV - center point
263 | vertices[currentOffset + 0] = center[0];
264 | vertices[currentOffset + 1] = center[1];
265 | vertices[currentOffset + 2] = z;
266 | vertices[currentOffset + 3] =
267 | (vertices[currentOffset + 0] - viewPort.left) / viewPort.width();
268 | vertices[currentOffset + 4] =
269 | (vertices[currentOffset + 1] - viewPort.bottom) / -viewPort.height();
270 |
271 | // XYZUV - triangle edge 1
272 | vertices[currentOffset + 5] = center[0] + radius[0] * (float) Math.cos(rads);
273 | vertices[currentOffset + 6] = center[1] + radius[1] * (float) Math.sin(rads);
274 | vertices[currentOffset + 7] = z;
275 | vertices[currentOffset + 8] =
276 | (vertices[currentOffset + 5] - viewPort.left) / viewPort.width();
277 | vertices[currentOffset + 9] =
278 | (vertices[currentOffset + 6] - viewPort.bottom) / -viewPort.height();
279 |
280 | triangleEdge2Offset = 10;
281 | } else {
282 | triangleEdge2Offset = 0;
283 | }
284 |
285 | // XYZUV - triangle edge 2
286 | final int edge2Offset = currentOffset + triangleEdge2Offset;
287 | vertices[edge2Offset + 0] = center[0] + radius[0] * (float) Math.cos(radsNext);
288 | vertices[edge2Offset + 1] = center[1] + radius[1] * (float) Math.sin(radsNext);
289 | vertices[edge2Offset + 2] = z;
290 | vertices[edge2Offset + 3] =
291 | (vertices[edge2Offset + 0] - viewPort.left) / viewPort.width();
292 | vertices[edge2Offset + 4] =
293 | (vertices[edge2Offset + 1] - viewPort.bottom) / -viewPort.height();
294 |
295 | // Index our triangles -- tell where each triangle vertex is
296 | final int initialIdx = verticesOffset / 5;
297 | indices[indicesOffset + i * 3 + 0] = (short) (initialIdx);
298 | indices[indicesOffset + i * 3 + 1] = (short) (initialIdx + i + 1);
299 | indices[indicesOffset + i * 3 + 2] = (short) (initialIdx + i + 2);
300 | }
301 | }
302 |
303 | public static class GeometryArrays {
304 | public float[] triangleVertices;
305 | public short[] triangleIndices;
306 | public int verticesOffset = 0;
307 | public int indicesOffset = 0;
308 |
309 | public GeometryArrays(@NonNull float[] vertices, @NonNull short[] indices) {
310 | triangleVertices = vertices;
311 | triangleIndices = indices;
312 | }
313 | }
314 | }
315 |
--------------------------------------------------------------------------------
/app/src/main/java/com/takeoffandroid/videochatheads/views/MultiSampleEGLConfigChooser.java:
--------------------------------------------------------------------------------
1 | package com.takeoffandroid.videochatheads.views;
2 |
3 | import android.opengl.GLSurfaceView;
4 | import android.util.Log;
5 |
6 | import javax.microedition.khronos.egl.EGL10;
7 | import javax.microedition.khronos.egl.EGLConfig;
8 | import javax.microedition.khronos.egl.EGLDisplay;
9 |
10 | /**
11 | * EGL configuration Chooser slightly adapted from:
12 | *
13 | * https://code.google.com/p/gdc2011-android-opengl/source/browse/trunk/src/com/example/
14 | * gdc11/MultisampleConfigChooser.java
15 | *
16 | * This class shows how to use multisampling. To use this, call
17 | * myGLSurfaceView.setEGLConfigChooser(new MultisampleConfigChooser()); before calling
18 | * setRenderer(). Multisampling will probably slow down your app -- measure performance carefully
19 | * and decide if the vastly improved visual quality is worth the cost.
20 | */
21 | public class MultiSampleEGLConfigChooser implements GLSurfaceView.EGLConfigChooser {
22 | static private final String TAG = MultiSampleEGLConfigChooser.class.getSimpleName();
23 | @Override
24 | public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
25 | mValue = new int[1];
26 |
27 | // Try to find a normal multisample configuration first.
28 | int[] configSpec = {
29 | EGL10.EGL_RED_SIZE, 8,
30 | EGL10.EGL_GREEN_SIZE, 8,
31 | EGL10.EGL_BLUE_SIZE, 8,
32 | EGL10.EGL_ALPHA_SIZE, 8,
33 | EGL10.EGL_DEPTH_SIZE, 16,
34 | // Requires that setEGLContextClientVersion(2) is called on the view.
35 | EGL10.EGL_RENDERABLE_TYPE, 4 /* EGL_OPENGL_ES2_BIT */,
36 | EGL10.EGL_SAMPLE_BUFFERS, 1 /* true */,
37 | EGL10.EGL_SAMPLES, 4,
38 | EGL10.EGL_NONE
39 | };
40 |
41 | int numConfigs = mValue[0];
42 |
43 | if (numConfigs <= 0) {
44 | // No normal multisampling config was found. Try to create a
45 | // coverage multisampling configuration, for the nVidia Tegra2.
46 | // See the EGL_NV_coverage_sample documentation.
47 |
48 | final int EGL_COVERAGE_BUFFERS_NV = 0x30E0;
49 | final int EGL_COVERAGE_SAMPLES_NV = 0x30E1;
50 |
51 | configSpec = new int[]{
52 | EGL10.EGL_RED_SIZE, 8,
53 | EGL10.EGL_GREEN_SIZE, 8,
54 | EGL10.EGL_BLUE_SIZE, 8,
55 | EGL10.EGL_ALPHA_SIZE, 8,
56 | EGL10.EGL_DEPTH_SIZE, 16,
57 | EGL10.EGL_RENDERABLE_TYPE, 4 /* EGL_OPENGL_ES2_BIT */,
58 | EGL_COVERAGE_BUFFERS_NV, 1 /* true */,
59 | EGL_COVERAGE_SAMPLES_NV, 2, // always 5 in practice on tegra 2
60 | EGL10.EGL_NONE
61 | };
62 |
63 | numConfigs = mValue[0];
64 |
65 | if (numConfigs <= 0) {
66 | // Give up, try without multisampling.
67 | configSpec = new int[]{
68 | EGL10.EGL_RED_SIZE, 8,
69 | EGL10.EGL_GREEN_SIZE, 8,
70 | EGL10.EGL_BLUE_SIZE, 8,
71 | EGL10.EGL_ALPHA_SIZE, 8,
72 | EGL10.EGL_DEPTH_SIZE, 16,
73 | EGL10.EGL_RENDERABLE_TYPE, 4 /* EGL_OPENGL_ES2_BIT */,
74 | EGL10.EGL_NONE
75 | };
76 |
77 | if (!egl.eglChooseConfig(display, configSpec, null, 0, mValue)) {
78 | throw new IllegalArgumentException("3rd eglChooseConfig failed");
79 | }
80 | numConfigs = mValue[0];
81 |
82 | if (numConfigs <= 0) {
83 | throw new IllegalArgumentException("No configs match configSpec");
84 | }
85 | } else {
86 | mUsesCoverageAa = true;
87 | }
88 | }
89 |
90 | // Get all matching configurations.
91 | EGLConfig[] configs = new EGLConfig[numConfigs];
92 | if (!egl.eglChooseConfig(display, configSpec, configs, numConfigs, mValue)) {
93 | throw new IllegalArgumentException("data eglChooseConfig failed");
94 | }
95 |
96 | // CAUTION! eglChooseConfigs returns configs with higher bit depth
97 | // first: Even if you asked for rgb565 configurations, rgb888
98 | // configurations are considered to be "better" and returned first.
99 | // You need to explicitly filter the data returned by eglChooseConfig!
100 | //
101 | // In this case we asked for ARGB8888 so we should be good taking the
102 | // first result, but we'll still filter explicitly the one we want
103 | // just to be safe.
104 | int index = -1;
105 | for (int i = 0; i < configs.length; ++i) {
106 | if (findConfigAttrib(egl, display, configs[i], EGL10.EGL_ALPHA_SIZE, 0) == 8) {
107 | index = i;
108 | break;
109 | }
110 | }
111 | if (index == -1) {
112 | Log.w(TAG, "Did not find sane config, using first");
113 | }
114 | EGLConfig config = configs.length > 0 ? configs[index] : null;
115 | if (config == null) {
116 | throw new IllegalArgumentException("No config chosen");
117 | }
118 | return config;
119 | }
120 |
121 | private int findConfigAttrib(EGL10 egl, EGLDisplay display,
122 | EGLConfig config, int attribute, int defaultValue) {
123 | if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
124 | return mValue[0];
125 | }
126 | return defaultValue;
127 | }
128 |
129 | public boolean usesCoverageAa() {
130 | return mUsesCoverageAa;
131 | }
132 |
133 | private int[] mValue;
134 | private boolean mUsesCoverageAa;
135 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/takeoffandroid/videochatheads/views/VideoSurfaceView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.takeoffandroid.videochatheads.views;
18 |
19 | import android.content.Context;
20 | import android.graphics.PixelFormat;
21 | import android.graphics.Point;
22 | import android.graphics.RectF;
23 | import android.graphics.SurfaceTexture;
24 | import android.media.MediaPlayer;
25 | import android.opengl.GLES20;
26 | import android.opengl.GLSurfaceView;
27 | import android.opengl.Matrix;
28 | import android.support.annotation.NonNull;
29 | import android.support.annotation.Nullable;
30 | import android.util.AttributeSet;
31 | import android.util.Log;
32 | import android.view.Surface;
33 |
34 | import java.io.IOException;
35 | import java.nio.ByteBuffer;
36 | import java.nio.ByteOrder;
37 | import java.nio.FloatBuffer;
38 | import java.nio.ShortBuffer;
39 |
40 | import javax.microedition.khronos.egl.EGLConfig;
41 | import javax.microedition.khronos.opengles.GL10;
42 |
43 | /**
44 | * This class has been adapted from
45 | *
46 | * https://code.google.com/p/android-source-browsing/source/browse/tests/tests/media/src/
47 | * android/media/cts/VideoSurfaceView.java?repo=platform--cts
48 | *
49 | * {@link GLSurfaceView} subclass that displays video on the screen with the ability
50 | * to round its corners.
51 | *
52 | * It creates a GL texture and from that generates a {@link SurfaceTexture} and an
53 | * associated {@link Surface} which it binds to a given
54 | * {@link MediaPlayer}. The {@link MediaPlayer} will draw the decoded
55 | * video frames directly on to the GL texture which afterwards is rendered on the screen mapped to
56 | * a given (rounded corner) geometry.
57 | *
58 | * To set adjust the rounded corners use {@link #setCornerRadius(float, float, float, float)}.
59 | *
60 | */
61 | public class VideoSurfaceView extends GLSurfaceView {
62 | private static final String TAG = "VideoSurfaceView";
63 | private static final boolean USE_MULTI_SAMPLING = true;
64 |
65 | VideoRenderer mRenderer;
66 | MediaPlayer mMediaPlayer = null;
67 | MultiSampleEGLConfigChooser mMultiSamplingConfigChooser;
68 |
69 | public VideoSurfaceView(Context context) {
70 | super(context);
71 | init(new VideoRenderer(this));
72 | }
73 |
74 | public VideoSurfaceView(Context context, AttributeSet attrs) {
75 | super(context, attrs);
76 | init(new VideoRenderer(this));
77 | }
78 |
79 | VideoSurfaceView(Context context, @NonNull VideoRenderer videoRender) {
80 | super(context);
81 | init(videoRender);
82 | }
83 |
84 | private void init(@NonNull VideoRenderer videoRender) {
85 | setEGLContextClientVersion(2);
86 |
87 | setupEGLConfig(true, USE_MULTI_SAMPLING);
88 | if (USE_MULTI_SAMPLING && mMultiSamplingConfigChooser != null) {
89 | videoRender.setUsesCoverageAa(mMultiSamplingConfigChooser.usesCoverageAa());
90 | }
91 | mRenderer = videoRender;
92 | setRenderer(mRenderer);
93 | setRenderMode(RENDERMODE_WHEN_DIRTY);
94 | }
95 |
96 | /**
97 | * Make sure the {@link android.view.SurfaceHolder} pixel format matches your EGL configuration.
98 | *
99 | * @param translucent true if the view should show views below if parts of the view area are
100 | * transparent. Has performance implications.
101 | * @param multisampling true if the GL Surface should perform multi-sampling. This avoids hard
102 | * edges on the geometry. Has performance implications.
103 | */
104 | private void setupEGLConfig(boolean translucent, boolean multisampling) {
105 | if (translucent) {
106 | setZOrderOnTop(true);
107 | if (multisampling) {
108 | mMultiSamplingConfigChooser = new MultiSampleEGLConfigChooser();
109 | setEGLConfigChooser(mMultiSamplingConfigChooser);
110 | } else {
111 | setEGLConfigChooser(8, 8, 8, 8, 16, 0);
112 | }
113 | this.getHolder().setFormat(PixelFormat.RGBA_8888);
114 | } else {
115 | if (multisampling) {
116 | mMultiSamplingConfigChooser = new MultiSampleEGLConfigChooser();
117 | setEGLConfigChooser(mMultiSamplingConfigChooser);
118 | } else {
119 | setEGLConfigChooser(5, 6, 5, 0, 16, 0);
120 | }
121 | this.getHolder().setFormat(PixelFormat.RGB_565);
122 | }
123 | }
124 |
125 | public void setCornerRadius(float radius) {
126 | setCornerRadius(radius, radius, radius, radius);
127 | }
128 |
129 | public void setCornerRadius(float topLeft, float topRight, float bottomRight,
130 | float bottomLeft) {
131 | mRenderer.setCornerRadius(topLeft, topRight, bottomRight, bottomLeft);
132 | }
133 |
134 | // TODO
135 | public void setVideoAspectRatio(float aspectRatio) {
136 | mRenderer.setVideoAspectRatio(aspectRatio);
137 | }
138 |
139 | @Override
140 | public void onResume() {
141 | queueEvent(new Runnable(){
142 | public void run() {
143 | mRenderer.setMediaPlayer(mMediaPlayer);
144 | }});
145 |
146 | super.onResume();
147 | }
148 |
149 | public void setMediaPlayer(@Nullable MediaPlayer mediaPlayer) {
150 | mMediaPlayer = mediaPlayer;
151 | if (mRenderer != null) {
152 | mRenderer.setMediaPlayer(mediaPlayer);
153 | }
154 | }
155 |
156 | private static class VideoRenderer
157 | implements Renderer, SurfaceTexture.OnFrameAvailableListener {
158 | private static String TAG = "VideoRender";
159 |
160 | private static final int FLOAT_SIZE_BYTES = 4;
161 | private static final int SHORT_SIZE_BYTES = 2;
162 | private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
163 | private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
164 | private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
165 |
166 | private final boolean USE_DRAW_ELEMENTS = true;
167 |
168 | private final String mVertexShader =
169 | "uniform mat4 uMVPMatrix;\n" +
170 | "uniform mat4 uSTMatrix;\n" +
171 | "attribute vec4 aPosition;\n" +
172 | "attribute vec4 aTextureCoord;\n" +
173 | "varying vec2 vTextureCoord;\n" +
174 | "void main() {\n" +
175 | " gl_Position = uMVPMatrix * aPosition;\n" +
176 | " vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" +
177 | "}\n";
178 |
179 | private final String mFragmentShader =
180 | "#extension GL_OES_EGL_image_external : require\n" +
181 | "precision mediump float;\n" +
182 | "varying vec2 vTextureCoord;\n" +
183 | "uniform samplerExternalOES sTexture;\n" +
184 | "void main() {\n" +
185 | " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
186 | "}\n";
187 |
188 | private float[] mMVPMatrix = new float[16];
189 | private float[] mSTMatrix = new float[16];
190 |
191 | private int mProgram;
192 | private int mTextureID;
193 | private int muMVPMatrixHandle;
194 | private int muSTMatrixHandle;
195 | private int maPositionHandle;
196 | private int maTextureHandle;
197 |
198 | private static int GL_TEXTURE_EXTERNAL_OES = 0x8D65;
199 |
200 | private final GLSurfaceView mGLSurfaceView;
201 | private MediaPlayer mMediaPlayer;
202 | private SurfaceTexture mSurfaceTexture;
203 | private boolean mUpdateSurface = false;
204 |
205 | private float[] mTriangleVerticesData;
206 | private short[] mTriangleIndicesData;
207 | private FloatBuffer mTriangleVertices;
208 | private ShortBuffer mTriangleIndices;
209 | private RectF mRoundRadius = new RectF();
210 | private GLRoundedGeometry mRoundedGeometry;
211 | private final Point mViewPortSize = new Point();
212 | private final RectF mViewPortGLBounds;
213 | private boolean mUsesCoverageAa = false;
214 |
215 | public VideoRenderer(@NonNull GLSurfaceView view) {
216 | this(view, new GLRoundedGeometry(), new RectF(-1, 1, 1, -1));
217 | }
218 |
219 | public VideoRenderer(@NonNull GLSurfaceView view,
220 | @NonNull GLRoundedGeometry roundedGeometry,
221 | @NonNull RectF viewPortGLBounds) {
222 | mGLSurfaceView = view;
223 | mRoundedGeometry = roundedGeometry;
224 | mViewPortGLBounds = viewPortGLBounds;
225 | mViewPortSize.set(1, 1); // init this with a non-zero size
226 |
227 | Matrix.setIdentityM(mSTMatrix, 0);
228 | }
229 |
230 | public void setUsesCoverageAa(boolean usesCoverageAa) {
231 | mUsesCoverageAa = usesCoverageAa;
232 | }
233 |
234 | public void setCornerRadius(float topLeft, float topRight, float bottomRight,
235 | float bottomLeft) {
236 | mRoundRadius.left = topLeft;
237 | mRoundRadius.top = topRight;
238 | mRoundRadius.right = bottomRight;
239 | mRoundRadius.bottom = bottomLeft;
240 | if (mViewPortSize.x > 1) {
241 | updateVertexData();
242 | }
243 | }
244 |
245 | private void updateVertexData() {
246 | final GLRoundedGeometry.GeometryArrays arrays =
247 | mRoundedGeometry.generateVertexData(
248 | mRoundRadius,
249 | mViewPortGLBounds,
250 | mViewPortSize);
251 | mTriangleVerticesData = arrays.triangleVertices;
252 | mTriangleIndicesData = arrays.triangleIndices;
253 | if (mTriangleVertices != null) {
254 | mTriangleVertices.clear();
255 | } else {
256 | mTriangleVertices = ByteBuffer.allocateDirect(
257 | mTriangleVerticesData.length * FLOAT_SIZE_BYTES)
258 | .order(ByteOrder.nativeOrder()).asFloatBuffer();
259 | }
260 | if (mTriangleIndices != null) {
261 | mTriangleIndices.clear();
262 | } else {
263 | mTriangleIndices = ByteBuffer.allocateDirect(
264 | mTriangleIndicesData.length * SHORT_SIZE_BYTES)
265 | .order(ByteOrder.nativeOrder()).asShortBuffer();
266 | }
267 | mTriangleVertices.put(mTriangleVerticesData).position(0);
268 | mTriangleIndices.put(mTriangleIndicesData).position(0);
269 | }
270 |
271 | public void setMediaPlayer(MediaPlayer player) {
272 | mMediaPlayer = player;
273 | if (mSurfaceTexture != null) {
274 | Surface surface = new Surface(mSurfaceTexture);
275 | mMediaPlayer.setSurface(surface);
276 | surface.release();
277 |
278 | try {
279 | mMediaPlayer.prepare();
280 | } catch (IOException t) {
281 | Log.e(TAG, "media player prepare failed");
282 | }
283 | }
284 | }
285 |
286 | public void onDrawFrame(GL10 glUnused) {
287 | synchronized(this) {
288 | if (mUpdateSurface) {
289 | mSurfaceTexture.updateTexImage();
290 | mSurfaceTexture.getTransformMatrix(mSTMatrix);
291 | mUpdateSurface = false;
292 | }
293 | }
294 |
295 | GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
296 | int clearMask = GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT;
297 | if (mUsesCoverageAa) { // Tegra weirdness
298 | final int GL_COVERAGE_BUFFER_BIT_NV = 0x8000;
299 | clearMask |= GL_COVERAGE_BUFFER_BIT_NV;
300 | }
301 | GLES20.glClear(clearMask);
302 |
303 | GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
304 | GLES20.GL_CLAMP_TO_EDGE);
305 | GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
306 | GLES20.GL_CLAMP_TO_EDGE);
307 |
308 | GLES20.glUseProgram(mProgram);
309 | checkGlError("glUseProgram");
310 |
311 | GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
312 | GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextureID);
313 |
314 | mTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
315 | GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false,
316 | TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
317 | checkGlError("glVertexAttribPointer maPosition");
318 | GLES20.glEnableVertexAttribArray(maPositionHandle);
319 | checkGlError("glEnableVertexAttribArray maPositionHandle");
320 |
321 | mTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
322 | GLES20.glVertexAttribPointer(maTextureHandle, 3, GLES20.GL_FLOAT, false,
323 | TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
324 | checkGlError("glVertexAttribPointer maTextureHandle");
325 | GLES20.glEnableVertexAttribArray(maTextureHandle);
326 | checkGlError("glEnableVertexAttribArray maTextureHandle");
327 |
328 | Matrix.setIdentityM(mMVPMatrix, 0);
329 | Matrix.scaleM(mMVPMatrix, 0, 1f, 1f, 1f);
330 | GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
331 | GLES20.glUniformMatrix4fv(muSTMatrixHandle, 1, false, mSTMatrix, 0);
332 |
333 | // Alternatively we can use
334 | //
335 | // GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, mTriangleVerticesData.length / 5);
336 | //
337 | // but with the current geometry setup it ends up drawing a lot of 'degenerate'
338 | // triangles which represents more work for our shaders, especially the fragment one.
339 | GLES20.glDrawElements(GLES20.GL_TRIANGLES, mTriangleIndicesData.length,
340 | GL10.GL_UNSIGNED_SHORT, mTriangleIndices);
341 |
342 | checkGlError("glDrawElements");
343 | GLES20.glFinish();
344 | }
345 |
346 | public void onSurfaceChanged(GL10 glUnused, int width, int height) {
347 | GLES20.glViewport(0, 0, width, height);
348 | mViewPortSize.set(width, height);
349 | updateVertexData();
350 | }
351 |
352 | public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {
353 | mProgram = createProgram(mVertexShader, mFragmentShader);
354 | if (mProgram == 0) {
355 | return;
356 | }
357 | maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
358 | checkGlError("glGetAttribLocation aPosition");
359 | if (maPositionHandle == -1) {
360 | throw new RuntimeException("Could not get attrib location for aPosition");
361 | }
362 | maTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord");
363 | checkGlError("glGetAttribLocation aTextureCoord");
364 | if (maTextureHandle == -1) {
365 | throw new RuntimeException("Could not get attrib location for aTextureCoord");
366 | }
367 |
368 | muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
369 | checkGlError("glGetUniformLocation uMVPMatrix");
370 | if (muMVPMatrixHandle == -1) {
371 | throw new RuntimeException("Could not get attrib location for uMVPMatrix");
372 | }
373 |
374 | muSTMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uSTMatrix");
375 | checkGlError("glGetUniformLocation uSTMatrix");
376 | if (muSTMatrixHandle == -1) {
377 | throw new RuntimeException("Could not get attrib location for uSTMatrix");
378 | }
379 |
380 | int[] textures = new int[1];
381 | GLES20.glGenTextures(1, textures, 0);
382 |
383 | mTextureID = textures[0];
384 | GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextureID);
385 | checkGlError("glBindTexture mTextureID");
386 |
387 | GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER,
388 | GLES20.GL_LINEAR);
389 | GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER,
390 | GLES20.GL_LINEAR);
391 |
392 | /*
393 | * Create the SurfaceTexture that will feed this textureID,
394 | * and pass it to the MediaPlayer
395 | */
396 | mSurfaceTexture = new SurfaceTexture(mTextureID);
397 | mSurfaceTexture.setOnFrameAvailableListener(this);
398 |
399 | if (mMediaPlayer != null) {
400 | Surface surface = new Surface(mSurfaceTexture);
401 | mMediaPlayer.setSurface(surface);
402 | surface.release();
403 | try {
404 | mMediaPlayer.prepare();
405 | } catch (IOException t) {
406 | Log.e(TAG, "media player prepare failed");
407 | }
408 | }
409 |
410 | synchronized(this) {
411 | mUpdateSurface = false;
412 | }
413 | }
414 |
415 | synchronized public void onFrameAvailable(SurfaceTexture surface) {
416 | mUpdateSurface = true;
417 | mGLSurfaceView.requestRender();
418 | }
419 |
420 | private int loadShader(int shaderType, String source) {
421 | int shader = GLES20.glCreateShader(shaderType);
422 | if (shader != 0) {
423 | GLES20.glShaderSource(shader, source);
424 | GLES20.glCompileShader(shader);
425 | int[] compiled = new int[1];
426 | GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
427 | if (compiled[0] == 0) {
428 | Log.e(TAG, "Could not compile shader " + shaderType + ":");
429 | Log.e(TAG, GLES20.glGetShaderInfoLog(shader));
430 | GLES20.glDeleteShader(shader);
431 | shader = 0;
432 | }
433 | }
434 | return shader;
435 | }
436 |
437 | private int createProgram(String vertexSource, String fragmentSource) {
438 | int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
439 | if (vertexShader == 0) {
440 | return 0;
441 | }
442 | int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
443 | if (pixelShader == 0) {
444 | return 0;
445 | }
446 |
447 | int program = GLES20.glCreateProgram();
448 | if (program != 0) {
449 | GLES20.glAttachShader(program, vertexShader);
450 | checkGlError("glAttachShader");
451 | GLES20.glAttachShader(program, pixelShader);
452 | checkGlError("glAttachShader");
453 | GLES20.glLinkProgram(program);
454 | int[] linkStatus = new int[1];
455 | GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
456 | if (linkStatus[0] != GLES20.GL_TRUE) {
457 | Log.e(TAG, "Could not link program: ");
458 | Log.e(TAG, GLES20.glGetProgramInfoLog(program));
459 | GLES20.glDeleteProgram(program);
460 | program = 0;
461 | }
462 | }
463 | return program;
464 | }
465 |
466 | private void checkGlError(String op) {
467 | int error;
468 | while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
469 | Log.e(TAG, op + ": glError " + error);
470 | throw new RuntimeException(op + ": glError " + error);
471 | }
472 | }
473 |
474 | public void setVideoAspectRatio(float aspectRatio) {
475 | // TODO
476 | }
477 | }
478 |
479 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/background_shade.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ckdevrel/VideoChatHeads/fdb4a4310e20aa8ba1bc569967dac81698f07783/app/src/main/res/drawable/background_shade.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/drawable_circle_white.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
10 |
11 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ckdevrel/VideoChatHeads/fdb4a4310e20aa8ba1bc569967dac81698f07783/app/src/main/res/drawable/ic_close.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_pause.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_play.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
15 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/view_layout_chat_heads.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
10 |
15 |
16 |
17 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/view_layout_close.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_main.xml:
--------------------------------------------------------------------------------
1 |
5 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ckdevrel/VideoChatHeads/fdb4a4310e20aa8ba1bc569967dac81698f07783/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ckdevrel/VideoChatHeads/fdb4a4310e20aa8ba1bc569967dac81698f07783/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ckdevrel/VideoChatHeads/fdb4a4310e20aa8ba1bc569967dac81698f07783/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ckdevrel/VideoChatHeads/fdb4a4310e20aa8ba1bc569967dac81698f07783/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ckdevrel/VideoChatHeads/fdb4a4310e20aa8ba1bc569967dac81698f07783/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/raw/sample_video.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ckdevrel/VideoChatHeads/fdb4a4310e20aa8ba1bc569967dac81698f07783/app/src/main/res/raw/sample_video.mp4
--------------------------------------------------------------------------------
/app/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 | >
2 |
3 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 | 39dip
6 | 233dp
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Facebook-VideoChatHeads
3 | Settings
4 | Press play button and throw the paper into recycle bin.
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:1.5.0'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
21 | task clean(type: Delete) {
22 | delete rootProject.buildDir
23 | }
24 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ckdevrel/VideoChatHeads/fdb4a4310e20aa8ba1bc569967dac81698f07783/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Oct 21 11:34:03 PDT 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------