package io.lbry.browser.reactmodules; import android.content.Context; import android.content.ContentResolver; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.Manifest; import android.media.ThumbnailUtils; import android.os.AsyncTask; import android.os.Bundle; import android.provider.MediaStore; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableMap; import com.facebook.react.modules.core.DeviceEventManagerModule; import io.lbry.browser.MainActivity; 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; import java.util.ArrayList; public class GalleryModule extends ReactContextBaseJavaModule { private Context context; public GalleryModule(ReactApplicationContext reactContext) { super(reactContext); this.context = reactContext; } @Override public String getName() { return "Gallery"; } @ReactMethod public void getVideos(Promise promise) { WritableArray items = Arguments.createArray(); List<GalleryItem> videos = loadVideos(); for (int i = 0; i < videos.size(); i++) { items.pushMap(videos.get(i).toMap()); } promise.resolve(items); } @ReactMethod public void getThumbnailPath(Promise promise) { if (context != null) { File cacheDir = context.getExternalCacheDir(); String thumbnailPath = String.format("%s/thumbnails", cacheDir.getAbsolutePath()); promise.resolve(thumbnailPath); return; } promise.resolve(null); } @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 = Bitmap.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, MediaStore.MediaColumns.DATA, MediaStore.MediaColumns.DISPLAY_NAME, MediaStore.MediaColumns.MIME_TYPE, MediaStore.Video.Media.DURATION }; List<String> ids = new ArrayList<String>(); List<GalleryItem> items = new ArrayList<GalleryItem>(); Cursor cursor = context.getContentResolver().query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, projection, null, null, String.format("%s DESC", MediaStore.MediaColumns.DATE_MODIFIED)); while (cursor.moveToNext()) { int idColumn = cursor.getColumnIndex(MediaStore.MediaColumns._ID); int nameColumn = cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME); int typeColumn = cursor.getColumnIndex(MediaStore.MediaColumns.MIME_TYPE); int pathColumn = cursor.getColumnIndex(MediaStore.MediaColumns.DATA); int durationColumn = cursor.getColumnIndex(MediaStore.Video.Media.DURATION); String id = cursor.getString(idColumn); GalleryItem item = new GalleryItem(); item.setId(id); item.setName(cursor.getString(nameColumn)); item.setType(cursor.getString(typeColumn)); item.setFilePath(cursor.getString(pathColumn)); items.add(item); ids.add(id); } checkThumbnails(ids); return items; } private void checkThumbnails(final List<String> ids) { (new AsyncTask<Void, Void, Void>() { protected Void doInBackground(Void... param) { if (context != null) { ContentResolver resolver = context.getContentResolver(); for (int i = 0; i < ids.size(); i++) { String id = ids.get(i); File cacheDir = context.getExternalCacheDir(); File thumbnailsDir = new File(String.format("%s/thumbnails", cacheDir.getAbsolutePath())); if (!thumbnailsDir.isDirectory()) { thumbnailsDir.mkdirs(); } String thumbnailPath = String.format("%s/%s.png", thumbnailsDir.getAbsolutePath(), id); File file = new File(thumbnailPath); if (!file.exists()) { // save the thumbnail to the path BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 1; Bitmap thumbnail = MediaStore.Video.Thumbnails.getThumbnail( resolver, Long.parseLong(id), MediaStore.Video.Thumbnails.MINI_KIND, options); if (thumbnail != null) { try (FileOutputStream os = new FileOutputStream(thumbnailPath)) { thumbnail.compress(Bitmap.CompressFormat.PNG, 80, os); } catch (IOException ex) { // skip } } } if (file.exists() && file.length() > 0 && GalleryModule.this.context != null) { WritableMap params = Arguments.createMap(); params.putString("id", id); ((ReactApplicationContext) GalleryModule.this.context).getJSModule( DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("onGalleryThumbnailChecked", params); } } } return null; } }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } private static class GalleryItem { private String id; private int duration; private String filePath; private String name; private String type; public String getId() { return id; } public void setId(String id) { this.id = id; } public int getDuration() { return duration; } public void setDuration(int duration) { this.duration = duration; } public String getFilePath() { return filePath; } public void setFilePath(String filePath) { this.filePath = filePath; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getType() { return type; } public void setType(String type) { this.type = type; } public WritableMap toMap() { WritableMap map = Arguments.createMap(); map.putString("id", id); map.putString("name", name); map.putString("filePath", filePath); map.putString("type", type); map.putInt("duration", duration); return map; } } @ReactMethod public void canUseCamera(final Promise promise) { promise.resolve(MainActivity.hasPermission(Manifest.permission.CAMERA, MainActivity.getActivity())); } }