Skip to content

Commit 641ee7d

Browse files
committed
add share video
1 parent 5327557 commit 641ee7d

File tree

6 files changed

+169
-94
lines changed

6 files changed

+169
-94
lines changed

.idea/misc.xml

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/src/main/AndroidManifest.xml

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
33
package="com.aquaapps.readtorecite">
44

5-
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
6-
<uses-permission android:name="android.permission.CAMERA"/>
7-
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
5+
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
6+
android:maxSdkVersion="28"/>
7+
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
8+
<uses-permission android:name="android.permission.CAMERA" />
9+
<uses-permission android:name="android.permission.RECORD_AUDIO" />
810

911
<application
1012
android:allowBackup="true"
@@ -24,6 +26,16 @@
2426
<category android:name="android.intent.category.LAUNCHER" />
2527
</intent-filter>
2628
</activity>
29+
30+
<provider
31+
android:name="androidx.core.content.FileProvider"
32+
android:authorities="${applicationId}.fileProvider"
33+
android:exported="false"
34+
android:grantUriPermissions="true">
35+
<meta-data
36+
android:name="android.support.FILE_PROVIDER_PATHS"
37+
android:resource="@xml/file_paths" />
38+
</provider>
2739
</application>
2840

2941
</manifest>
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package com.aquaapps.readtorecite;
2+
3+
import android.Manifest;
4+
import android.content.ContentValues;
5+
import android.content.pm.PackageManager;
6+
import android.net.Uri;
7+
import android.os.Build;
8+
import android.provider.MediaStore;
9+
10+
import androidx.appcompat.app.AppCompatActivity;
11+
import androidx.camera.core.CameraSelector;
12+
import androidx.camera.core.Preview;
13+
import androidx.camera.lifecycle.ProcessCameraProvider;
14+
import androidx.camera.video.FallbackStrategy;
15+
import androidx.camera.video.MediaStoreOutputOptions;
16+
import androidx.camera.video.Quality;
17+
import androidx.camera.video.QualitySelector;
18+
import androidx.camera.video.Recorder;
19+
import androidx.camera.video.Recording;
20+
import androidx.camera.video.VideoCapture;
21+
import androidx.camera.video.VideoRecordEvent;
22+
import androidx.camera.view.PreviewView;
23+
import androidx.core.app.ActivityCompat;
24+
import androidx.core.content.ContextCompat;
25+
import androidx.core.util.Consumer;
26+
27+
import java.text.SimpleDateFormat;
28+
import java.util.Date;
29+
import java.util.Locale;
30+
import java.util.concurrent.ExecutionException;
31+
32+
33+
public class CameraController {
34+
public PreviewView previewView;
35+
public ProcessCameraProvider cameraProvider;
36+
public Recorder recorder;
37+
public Recording recording = null;
38+
public VideoCapture<Recorder> videoCapture;
39+
public AppCompatActivity activity;
40+
41+
public CameraController(AppCompatActivity activity) {
42+
this.activity = activity;
43+
try {
44+
cameraProvider = ProcessCameraProvider.getInstance(activity).get();
45+
Preview preview = new Preview.Builder().build();
46+
previewView = activity.findViewById(R.id.previewView);
47+
CameraSelector cameraSelector = new CameraSelector.Builder()
48+
.requireLensFacing(CameraSelector.LENS_FACING_FRONT)
49+
.build();
50+
preview.setSurfaceProvider(previewView.getSurfaceProvider());
51+
52+
QualitySelector qs = QualitySelector.fromOrderedList(
53+
QualitySelector.getSupportedQualities(
54+
cameraSelector.filter(cameraProvider.getAvailableCameraInfos()).get(0)),
55+
FallbackStrategy.higherQualityOrLowerThan(Quality.SD));
56+
recorder = new Recorder.Builder()
57+
.setQualitySelector(qs)
58+
.build();
59+
videoCapture = VideoCapture.withOutput(recorder);
60+
cameraProvider.bindToLifecycle(activity, cameraSelector, videoCapture, preview);
61+
62+
} catch (ExecutionException | InterruptedException e) {
63+
e.printStackTrace();
64+
}
65+
}
66+
67+
public boolean isRecording() {
68+
return recording != null;
69+
}
70+
71+
public void startRecord(Consumer<VideoRecordEvent> videoRecordListener) {
72+
ContentValues values = new ContentValues();
73+
values.put(MediaStore.MediaColumns.DISPLAY_NAME,
74+
"ReadToRecite-" + new SimpleDateFormat("yyyyMMdd-HHmmss", Locale.ENGLISH).format(new Date())+".3gp");
75+
Uri videoCollection;
76+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
77+
videoCollection = MediaStore.Video.Media
78+
.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
79+
} else {
80+
videoCollection = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
81+
}
82+
MediaStoreOutputOptions options = new MediaStoreOutputOptions.Builder(
83+
activity.getContentResolver(), videoCollection)
84+
.setContentValues(values)
85+
.build();
86+
87+
if (ActivityCompat.checkSelfPermission(activity, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
88+
ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.RECORD_AUDIO}, 114514);
89+
}
90+
91+
recording = videoCapture.getOutput()
92+
.prepareRecording(activity, options)
93+
.withAudioEnabled()
94+
.start(ContextCompat.getMainExecutor(activity), videoRecordListener);
95+
}
96+
97+
public void stopRecord() {
98+
recording.stop();
99+
recording = null;
100+
}
101+
102+
}
Lines changed: 44 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,26 @@
11
package com.aquaapps.readtorecite;
22

3-
import android.Manifest;
4-
import android.content.ClipData;
5-
import android.content.ContentValues;
6-
import android.content.pm.PackageManager;
3+
import android.content.Intent;
4+
import android.net.Uri;
75
import android.os.Bundle;
8-
import android.provider.MediaStore;
9-
import android.util.Log;
106
import android.view.Menu;
117
import android.view.MenuItem;
128
import android.view.View;
9+
import android.widget.Toast;
1310

1411
import androidx.appcompat.app.AppCompatActivity;
1512
import androidx.appcompat.widget.Toolbar;
16-
import androidx.camera.core.Camera;
17-
import androidx.camera.core.CameraSelector;
18-
import androidx.camera.core.Preview;
19-
import androidx.camera.lifecycle.ProcessCameraProvider;
20-
import androidx.camera.video.FallbackStrategy;
21-
import androidx.camera.video.FileOutputOptions;
22-
import androidx.camera.video.MediaStoreOutputOptions;
23-
import androidx.camera.video.OutputOptions;
24-
import androidx.camera.video.Quality;
25-
import androidx.camera.video.QualitySelector;
26-
import androidx.camera.video.Recorder;
27-
import androidx.camera.video.Recording;
28-
import androidx.camera.video.VideoCapture;
13+
import androidx.camera.video.OutputResults;
2914
import androidx.camera.video.VideoRecordEvent;
30-
import androidx.camera.view.PreviewView;
31-
import androidx.core.app.ActivityCompat;
32-
import androidx.core.content.ContextCompat;
3315

3416
import com.google.android.material.floatingactionbutton.FloatingActionButton;
3517

36-
import java.text.SimpleDateFormat;
37-
import java.util.Date;
38-
import java.util.Locale;
39-
import java.util.concurrent.ExecutionException;
18+
import java.util.Arrays;
4019

4120
public class MainActivity extends AppCompatActivity {
42-
public PreviewView previewView;
43-
public ProcessCameraProvider cameraProvider;
44-
public Recorder recorder;
45-
public Recording recording=null;
46-
public VideoCapture<Recorder> videoCapture;
21+
public CameraController cameraController;
4722

48-
public boolean isShowingPreview=true;
23+
public boolean isShowingPreview = true;
4924

5025
@Override
5126
protected void onCreate(Bundle savedInstanceState) {
@@ -55,63 +30,27 @@ protected void onCreate(Bundle savedInstanceState) {
5530
setSupportActionBar(toolbar);
5631
FloatingActionButton fab = findViewById(R.id.fab);
5732
fab.setOnClickListener(view -> {
58-
if(recording==null) {
59-
// Start record
60-
ContentValues values = new ContentValues();
61-
values.put(MediaStore.Video.Media.DISPLAY_NAME,
62-
"ReadToRecite-" + new SimpleDateFormat("yyyyMMdd-HHmmss", Locale.ENGLISH).format(new Date()) + ".mp4");
63-
MediaStoreOutputOptions options = new MediaStoreOutputOptions.Builder(
64-
this.getContentResolver(), MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
65-
.setContentValues(values)
66-
.build();
67-
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
68-
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, 114514);
69-
}
70-
71-
recording = videoCapture.getOutput().prepareRecording(this, options)
72-
.withAudioEnabled()
73-
.start(ContextCompat.getMainExecutor(this), videoRecordEvent -> {
74-
if(videoRecordEvent instanceof VideoRecordEvent.Finalize){
75-
VideoRecordEvent.Finalize finalizeEvent= (VideoRecordEvent.Finalize) videoRecordEvent;
76-
Log.e("Debug", String.valueOf(finalizeEvent.getError()));
77-
OutputOptions outputOptions=finalizeEvent.getOutputOptions();
78-
if(outputOptions instanceof FileOutputOptions)
79-
Log.e("Debug",((FileOutputOptions) outputOptions).getFile().getAbsolutePath());
80-
}
81-
});
82-
83-
fab.setImageResource(android.R.drawable.ic_menu_save);
84-
85-
} else { // Stop record
86-
recording.stop();
87-
recording=null;
33+
if (cameraController.isRecording()) {
34+
cameraController.stopRecord();
8835
fab.setImageResource(android.R.drawable.ic_media_play); // start
36+
} else {
37+
cameraController.startRecord(videoRecordEvent -> {
38+
if (videoRecordEvent instanceof VideoRecordEvent.Finalize) {
39+
VideoRecordEvent.Finalize finalizeEvent = (VideoRecordEvent.Finalize) videoRecordEvent;
40+
OutputResults outputResults = finalizeEvent.getOutputResults();
41+
int errorCode = finalizeEvent.getError();
42+
if (errorCode != VideoRecordEvent.Finalize.ERROR_NONE) {
43+
Toast.makeText(this, "错误: " + errorCode, Toast.LENGTH_LONG).show();
44+
} else {
45+
shareVideo(outputResults.getOutputUri());
46+
}
47+
}
48+
});
49+
fab.setImageResource(android.R.drawable.ic_menu_save);
8950
}
9051
});
9152
// Instance Camera
92-
try{
93-
cameraProvider=ProcessCameraProvider.getInstance(this).get();
94-
Preview preview=new Preview.Builder().build();
95-
previewView=findViewById(R.id.previewView);
96-
CameraSelector cameraSelector=new CameraSelector.Builder()
97-
.requireLensFacing(CameraSelector.LENS_FACING_FRONT)
98-
.build();
99-
preview.setSurfaceProvider(previewView.getSurfaceProvider());
100-
101-
QualitySelector qs=QualitySelector.fromOrderedList(
102-
QualitySelector.getSupportedQualities(
103-
cameraSelector.filter(cameraProvider.getAvailableCameraInfos()).get(0)),
104-
FallbackStrategy.higherQualityOrLowerThan(Quality.SD));
105-
recorder=new Recorder.Builder()
106-
.setQualitySelector(qs)
107-
.build();
108-
videoCapture=VideoCapture.withOutput(recorder);
109-
Camera camera=cameraProvider.bindToLifecycle(this,cameraSelector,videoCapture,preview);
110-
111-
}
112-
catch(ExecutionException | InterruptedException e){
113-
e.printStackTrace();
114-
}
53+
cameraController = new CameraController(this);
11554
}
11655

11756

@@ -131,17 +70,32 @@ public boolean onOptionsItemSelected(MenuItem item) {
13170

13271
//noinspection SimplifiableIfStatement
13372
if (id == R.id.action_preview) {
134-
if(isShowingPreview){
135-
previewView.setVisibility(View.INVISIBLE);
73+
if (isShowingPreview) {
74+
cameraController.previewView.setVisibility(View.INVISIBLE);
13675
item.setTitle(R.string.action_preview_enabled);
137-
} else{
138-
previewView.setVisibility(View.VISIBLE);
76+
} else {
77+
cameraController.previewView.setVisibility(View.VISIBLE);
13978
item.setTitle(R.string.action_preview_disabled);
14079
}
141-
isShowingPreview=!isShowingPreview;
80+
isShowingPreview = !isShowingPreview;
14281
return true;
14382
}
14483

14584
return super.onOptionsItemSelected(item);
14685
}
86+
87+
public void shareVideo(Uri videoUri) {
88+
Intent intent = new Intent();
89+
intent.setAction(Intent.ACTION_SEND);
90+
String[] splitResult = videoUri.getPath().split("\\.");
91+
if (splitResult[splitResult.length - 1].equals("3gp")) {
92+
intent.setType("video/3gpp");
93+
} else {
94+
intent.setType("video/mp4");
95+
}
96+
97+
intent.putExtra(Intent.EXTRA_STREAM, videoUri);
98+
startActivity(Intent.createChooser(intent, "分享..."));
99+
}
100+
147101
}

app/src/main/res/drawable/background_gradient.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
<shape xmlns:android="http://schemas.android.com/apk/res/android">
33
<gradient
44
android:startColor="@color/colorLightPink"
5-
android:endColor="@color/colorLightBlue"/>
5+
android:endColor="@color/colorLightBlue" />
66
</shape>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<paths>
3+
<external-path
4+
name="external_storage_root"
5+
path="." />
6+
</paths>

0 commit comments

Comments
 (0)