create thumbnails for camera media

This commit is contained in:
Akinwale Ariwodola 2019-06-28 11:04:40 +01:00
parent aa39e96f89
commit d821a7cc91
3 changed files with 185 additions and 21 deletions

View file

@ -170,7 +170,6 @@ export default class ChannelSelector extends React.PureComponent {
render() {
const channel = this.state.addingChannel ? 'new' : this.props.channel;
const { fetchingChannels, channels = [] } = this.props;
console.log(channels);
const pickerItems = [{ name: Constants.ITEM_ANONYMOUS }, { name: Constants.ITEM_CREATE_A_CHANNEL }].concat(
channels
);

View file

@ -34,17 +34,25 @@ class PublishPage extends React.PureComponent {
camera = null;
state = {
// gallery videos
videos: null,
// camera
cameraType: RNCamera.Constants.Type.back,
videoRecordingMode: false,
recordingVideo: false,
showCameraOverlay: false,
// paths and media
uploadsPath: null,
thumbnailPath: null,
videos: null,
currentMedia: null,
currentThumbnailUri: null,
updatingThumbnailUri: false,
currentPhase: Constants.PHASE_SELECTOR,
advancedMode: false,
// publish
advancedMode: false,
anonymous: true,
channelName: CLAIM_VALUES.CHANNEL_ANONYMOUS,
priceSet: false,
@ -145,6 +153,7 @@ class PublishPage extends React.PureComponent {
pushDrawerStack();
setPlayerVisible();
NativeModules.Gallery.getThumbnailPath().then(thumbnailPath => {
if (thumbnailPath != null) {
this.setState({ thumbnailPath });
@ -184,6 +193,7 @@ class PublishPage extends React.PureComponent {
showSelector() {
this.setState({
currentMedia: null,
currentThumbnailUri: null,
currentPhase: Constants.PHASE_SELECTOR,
// publish
@ -218,6 +228,10 @@ class PublishPage extends React.PureComponent {
this.setState({ showCameraOverlay: false, videoRecordingMode: false });
}
getFilePathFromUri = (uri) => {
return uri.substring('file://'.length);
}
handleCameraActionPressed = () => {
// check if it's video or photo mode
if (this.state.videoRecordingMode) {
@ -231,13 +245,15 @@ class PublishPage extends React.PureComponent {
this.setState({ recordingVideo: false });
const currentMedia = {
id: -1,
filePath: data.uri,
filePath: this.getFilePathFromUri(data.uri),
name: generateCombination(2, ' ', true),
type: 'video/mp4', // always MP4
duration: 0
};
this.setCurrentMedia(currentMedia);
this.setState({
currentThumbnailUri: null,
updatingThumbnailUri: false,
currentPhase: Constants.PHASE_DETAILS,
showCameraOverlay: false,
videoRecordingMode: false,
@ -250,13 +266,19 @@ class PublishPage extends React.PureComponent {
this.camera.takePictureAsync(options).then(data => {
const currentMedia = {
id: -1,
filePath: data.uri,
filePath: this.getFilePathFromUri(data.uri),
name: generateCombination(2, ' ', true),
type: 'image/jpg', // always JPEG
duration: 0
};
this.setCurrentMedia(currentMedia);
this.setState({ currentPhase: Constants.PHASE_DETAILS, showCameraOverlay: false, videoRecordingMode: false });
this.setState({
currentPhase: Constants.PHASE_DETAILS,
currentThumbnailUri: null,
updatingThumbnailUri: false,
showCameraOverlay: false,
videoRecordingMode: false
});
});
}
}
@ -283,6 +305,12 @@ class PublishPage extends React.PureComponent {
);
};
getRandomFileId = () => {
// generate a random id for a photo or recorded video between 1 and 20 (for creating thumbnails)
const id = Math.floor(Math.random() * (20 - 2)) + 1;
return '_' + id;
}
handlePublishAgainPressed = () => {
this.showSelector();
};
@ -311,18 +339,35 @@ class PublishPage extends React.PureComponent {
this.setState({ channelName: channel });
};
getThumbnailUriForMedia = (media) => {
const { thumbnailPath } = this.state;
if (media.type) {
const mediaType = media.type.substring(0, 5);
if ('video' === mediaType && media.id > -1) {
return `file://${thumbnailPath}/${media.id}.png`
} else if ('image' === mediaType) {
return media.filePath;
}
updateThumbnailUriForMedia = (media) => {
if (this.state.updatingThumbnailUri) {
return;
}
return null;
const { notify } = this.props;
const { thumbnailPath } = this.state;
this.setState({ updatingThumbnailUri: true });
if (media.type) {
const mediaType = media.type.substring(0, 5);
const tempId = this.getRandomFileId();
if ('video' === mediaType && media.id > -1) {
const uri = `file://${thumbnailPath}/${media.id}.png`;
this.setState({ currentThumbnailUri: uri, updatingThumbnailUri: false });
} else if ('image' === mediaType) {
// photo taken or file selected
NativeModules.Gallery.createImageThumbnail(tempId, media.filePath).
then(path => this.setState({ currentThumbnailUri: `file://${path}`, updatingThumbnailUri: false })).
catch(err => { notify({ message: err }); this.setState({ updatingThumbnailUri: false }); });
} else if ('video' === mediaType) {
// recorded video
NativeModules.Gallery.createVideoThumbnail(tempId, media.filePath).
then(path => this.setState({ currentThumbnailUri: `file://${path}`, updatingThumbnailUri: false })).
catch(err => { notify({ message: err }); this.setState({ updatingThumbnailUri: false }); });
}
}
}
handleTitleChange = title => {
@ -406,16 +451,18 @@ class PublishPage extends React.PureComponent {
</View>
);
} else if (Constants.PHASE_DETAILS === this.state.currentPhase && this.state.currentMedia) {
const { currentMedia } = this.state;
const thumbnailUri = this.getThumbnailUriForMedia(currentMedia);
const { currentMedia, currentThumbnailUri } = this.state;
if (!currentThumbnailUri) {
this.updateThumbnailUriForMedia(currentMedia);
}
content = (
<ScrollView style={publishStyle.publishDetails}>
{thumbnailUri && thumbnailUri.trim().length > 0 &&
{currentThumbnailUri && currentThumbnailUri.trim().length > 0 &&
<View style={publishStyle.mainThumbnailContainer}>
<FastImage
style={publishStyle.mainThumbnail}
resizeMode={FastImage.resizeMode.contain}
source={{ uri: thumbnailUri }}
source={{ uri: currentThumbnailUri }}
/>
</View>}
@ -589,7 +636,8 @@ class PublishPage extends React.PureComponent {
buttonNegative: 'Cancel',
}}
/>
<View style={[publishStyle.cameraControls, this.state.videoRecordingMode ? publishStyle.transparentControls : opaqueControls ]}>
<View style={[publishStyle.cameraControls,
this.state.videoRecordingMode ? publishStyle.transparentControls : publishStyle.opaqueControls ]}>
<View style={publishStyle.controlsRow}>
<TouchableOpacity onPress={this.handleCloseCameraPressed}>
<Icon name="arrow-left" size={28} color={Colors.White} />

View file

@ -5,6 +5,7 @@ import android.content.ContentResolver;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.ThumbnailUtils;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.MediaStore;
@ -17,7 +18,10 @@ import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import io.lbry.browser.Utils;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
@ -59,6 +63,119 @@ public class GalleryModule extends ReactContextBaseJavaModule {
promise.resolve(null);
}
/*
@ReactMethod
public void copyImage(String sourcePath, String destinationPath, Promise promise) {
try {
File source = new File(sourcePath);
File destination = new File(destinationPath);
if (source.exists()) {
FileChannel src = new FileInputStream(source).getChannel();
FileChannel dst = new FileOutputStream(destination).getChannel();
dst.transferFrom(src, 0, src.size());
src.close();
dst.close();
promise.resolve(true);
} else {
promise.reject("The source image could not be found. Please try again.");
}
} catch (Exception ex) {
promise.reject("The image could not be saved. Please try again.");
}
}*/
@ReactMethod
public void getUploadsPath(Promise promise) {
if (context != null) {
String baseFolder = Utils.getExternalStorageDir(context);
String uploadsPath = String.format("%s/LBRY/Uploads", baseFolder);
File uploadsDir = new File(uploadsPath);
if (!uploadsDir.isDirectory()) {
uploadsDir.mkdirs();
}
promise.resolve(uploadsPath);
}
promise.reject("The content could not be saved to the device. Please check your storage permissions.");
}
@ReactMethod
public void createVideoThumbnail(String targetId, String filePath, Promise promise) {
(new AsyncTask<Void, Void, String>() {
protected String doInBackground(Void... param) {
String thumbnailPath = null;
if (context != null) {
Bitmap thumbnail = ThumbnailUtils.createVideoThumbnail(filePath, MediaStore.Video.Thumbnails.MINI_KIND);
File cacheDir = context.getExternalCacheDir();
thumbnailPath = String.format("%s/thumbnails/%s.png", cacheDir.getAbsolutePath(), targetId);
File file = new File(thumbnailPath);
try (FileOutputStream os = new FileOutputStream(thumbnailPath)) {
thumbnail.compress(Bitmap.CompressFormat.PNG, 80, os);
os.close();
} catch (IOException ex) {
promise.reject("Could not create a thumbnail for the video");
return null;
}
}
return thumbnailPath;
}
public void onPostExecute(String thumbnailPath) {
if (thumbnailPath != null && thumbnailPath.trim().length() > 0) {
promise.resolve(thumbnailPath);
}
}
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
@ReactMethod
public void createImageThumbnail(String targetId, String filePath, Promise promise) {
(new AsyncTask<Void, Void, String>() {
protected String doInBackground(Void... param) {
String thumbnailPath = null;
FileOutputStream os = null;
try {
Bitmap source = BitmapFactory.decodeFile(filePath);
// MINI_KIND dimensions
Bitmap thumbnail = BitmapFactory.createScaledBitmap(source, 512, 384, false);
if (context != null) {
File cacheDir = context.getExternalCacheDir();
thumbnailPath = String.format("%s/thumbnails/%s.png", cacheDir.getAbsolutePath(), targetId);
os = new FileOutputStream(thumbnailPath);
if (thumbnail != null) {
thumbnail.compress(Bitmap.CompressFormat.PNG, 80, os);
}
os.close();
}
} catch (IOException ex) {
promise.reject("Could not create a thumbnail for the image");
return null;
} finally {
if (os != null) {
try {
os.close();
} catch (IOException ex) {
// ignoe
}
}
}
return thumbnailPath;
}
public void onPostExecute(String thumbnailPath) {
if (thumbnailPath != null && thumbnailPath.trim().length() > 0) {
promise.resolve(thumbnailPath);
}
}
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
private List<GalleryItem> loadVideos() {
String[] projection = {
MediaStore.MediaColumns._ID,