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() { render() {
const channel = this.state.addingChannel ? 'new' : this.props.channel; const channel = this.state.addingChannel ? 'new' : this.props.channel;
const { fetchingChannels, channels = [] } = this.props; const { fetchingChannels, channels = [] } = this.props;
console.log(channels);
const pickerItems = [{ name: Constants.ITEM_ANONYMOUS }, { name: Constants.ITEM_CREATE_A_CHANNEL }].concat( const pickerItems = [{ name: Constants.ITEM_ANONYMOUS }, { name: Constants.ITEM_CREATE_A_CHANNEL }].concat(
channels channels
); );

View file

@ -34,17 +34,25 @@ class PublishPage extends React.PureComponent {
camera = null; camera = null;
state = { state = {
// gallery videos
videos: null,
// camera
cameraType: RNCamera.Constants.Type.back, cameraType: RNCamera.Constants.Type.back,
videoRecordingMode: false, videoRecordingMode: false,
recordingVideo: false, recordingVideo: false,
showCameraOverlay: false, showCameraOverlay: false,
// paths and media
uploadsPath: null,
thumbnailPath: null, thumbnailPath: null,
videos: null,
currentMedia: null, currentMedia: null,
currentThumbnailUri: null,
updatingThumbnailUri: false,
currentPhase: Constants.PHASE_SELECTOR, currentPhase: Constants.PHASE_SELECTOR,
advancedMode: false,
// publish // publish
advancedMode: false,
anonymous: true, anonymous: true,
channelName: CLAIM_VALUES.CHANNEL_ANONYMOUS, channelName: CLAIM_VALUES.CHANNEL_ANONYMOUS,
priceSet: false, priceSet: false,
@ -145,6 +153,7 @@ class PublishPage extends React.PureComponent {
pushDrawerStack(); pushDrawerStack();
setPlayerVisible(); setPlayerVisible();
NativeModules.Gallery.getThumbnailPath().then(thumbnailPath => { NativeModules.Gallery.getThumbnailPath().then(thumbnailPath => {
if (thumbnailPath != null) { if (thumbnailPath != null) {
this.setState({ thumbnailPath }); this.setState({ thumbnailPath });
@ -184,6 +193,7 @@ class PublishPage extends React.PureComponent {
showSelector() { showSelector() {
this.setState({ this.setState({
currentMedia: null, currentMedia: null,
currentThumbnailUri: null,
currentPhase: Constants.PHASE_SELECTOR, currentPhase: Constants.PHASE_SELECTOR,
// publish // publish
@ -218,6 +228,10 @@ class PublishPage extends React.PureComponent {
this.setState({ showCameraOverlay: false, videoRecordingMode: false }); this.setState({ showCameraOverlay: false, videoRecordingMode: false });
} }
getFilePathFromUri = (uri) => {
return uri.substring('file://'.length);
}
handleCameraActionPressed = () => { handleCameraActionPressed = () => {
// check if it's video or photo mode // check if it's video or photo mode
if (this.state.videoRecordingMode) { if (this.state.videoRecordingMode) {
@ -231,13 +245,15 @@ class PublishPage extends React.PureComponent {
this.setState({ recordingVideo: false }); this.setState({ recordingVideo: false });
const currentMedia = { const currentMedia = {
id: -1, id: -1,
filePath: data.uri, filePath: this.getFilePathFromUri(data.uri),
name: generateCombination(2, ' ', true), name: generateCombination(2, ' ', true),
type: 'video/mp4', // always MP4 type: 'video/mp4', // always MP4
duration: 0 duration: 0
}; };
this.setCurrentMedia(currentMedia); this.setCurrentMedia(currentMedia);
this.setState({ this.setState({
currentThumbnailUri: null,
updatingThumbnailUri: false,
currentPhase: Constants.PHASE_DETAILS, currentPhase: Constants.PHASE_DETAILS,
showCameraOverlay: false, showCameraOverlay: false,
videoRecordingMode: false, videoRecordingMode: false,
@ -250,13 +266,19 @@ class PublishPage extends React.PureComponent {
this.camera.takePictureAsync(options).then(data => { this.camera.takePictureAsync(options).then(data => {
const currentMedia = { const currentMedia = {
id: -1, id: -1,
filePath: data.uri, filePath: this.getFilePathFromUri(data.uri),
name: generateCombination(2, ' ', true), name: generateCombination(2, ' ', true),
type: 'image/jpg', // always JPEG type: 'image/jpg', // always JPEG
duration: 0 duration: 0
}; };
this.setCurrentMedia(currentMedia); 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 = () => { handlePublishAgainPressed = () => {
this.showSelector(); this.showSelector();
}; };
@ -311,18 +339,35 @@ class PublishPage extends React.PureComponent {
this.setState({ channelName: channel }); this.setState({ channelName: channel });
}; };
getThumbnailUriForMedia = (media) => { updateThumbnailUriForMedia = (media) => {
const { thumbnailPath } = this.state; if (this.state.updatingThumbnailUri) {
if (media.type) { return;
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;
}
} }
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 => { handleTitleChange = title => {
@ -406,16 +451,18 @@ class PublishPage extends React.PureComponent {
</View> </View>
); );
} else if (Constants.PHASE_DETAILS === this.state.currentPhase && this.state.currentMedia) { } else if (Constants.PHASE_DETAILS === this.state.currentPhase && this.state.currentMedia) {
const { currentMedia } = this.state; const { currentMedia, currentThumbnailUri } = this.state;
const thumbnailUri = this.getThumbnailUriForMedia(currentMedia); if (!currentThumbnailUri) {
this.updateThumbnailUriForMedia(currentMedia);
}
content = ( content = (
<ScrollView style={publishStyle.publishDetails}> <ScrollView style={publishStyle.publishDetails}>
{thumbnailUri && thumbnailUri.trim().length > 0 && {currentThumbnailUri && currentThumbnailUri.trim().length > 0 &&
<View style={publishStyle.mainThumbnailContainer}> <View style={publishStyle.mainThumbnailContainer}>
<FastImage <FastImage
style={publishStyle.mainThumbnail} style={publishStyle.mainThumbnail}
resizeMode={FastImage.resizeMode.contain} resizeMode={FastImage.resizeMode.contain}
source={{ uri: thumbnailUri }} source={{ uri: currentThumbnailUri }}
/> />
</View>} </View>}
@ -589,7 +636,8 @@ class PublishPage extends React.PureComponent {
buttonNegative: 'Cancel', 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}> <View style={publishStyle.controlsRow}>
<TouchableOpacity onPress={this.handleCloseCameraPressed}> <TouchableOpacity onPress={this.handleCloseCameraPressed}>
<Icon name="arrow-left" size={28} color={Colors.White} /> <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.database.Cursor;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.media.ThumbnailUtils;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.provider.MediaStore; 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.WritableArray;
import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableMap;
import io.lbry.browser.Utils;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
@ -59,6 +63,119 @@ public class GalleryModule extends ReactContextBaseJavaModule {
promise.resolve(null); 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() { private List<GalleryItem> loadVideos() {
String[] projection = { String[] projection = {
MediaStore.MediaColumns._ID, MediaStore.MediaColumns._ID,