package androidnative; import org.qtproject.qt5.android.QtNative; import android.content.Intent; import android.content.Context; import android.util.Log; import android.app.Activity; import java.util.Map; import android.net.Uri; import java.util.HashMap; import android.database.Cursor; import android.content.ContentResolver ; import android.content.ContentValues ; import android.provider.MediaStore; import android.media.ThumbnailUtils ; import java.text.SimpleDateFormat; import java.io.File; import java.io.OutputStream; import java.io.FileNotFoundException ; import java.io.IOException ; import java.util.Date; import android.os.Environment; import android.content.ClipData; import java.util.List; import java.util.ArrayList; import android.graphics.Bitmap ; public class VideoPicker { // Random , generated using : $ openssl rand -hex 3 public static final int PICK_VIDEO_ACTION = 0x7c43f0; public static final int TAKE_VIDEO_ACTION = 0x4df0bc; public static final String PICK_VIDEO_MESSAGE = "androidnative.VideoPicker.pickVideo"; public static final String TAKE_VIDEO_MESSAGE = "androidnative.VideoPicker.takeVideo"; public static final String CHOSEN_MESSAGE = "androidnative.VideoPicker.chosen"; private static final String TAG = "androidnative.VideoPicker"; private static Uri mVideoUri; private static Boolean broadcast = false; static { SystemDispatcher.addListener(new SystemDispatcher.Listener() { public void onDispatched(String type , Map message) { Log.d(TAG,"onDispatched called with type :" + type ); if (type.equals(PICK_VIDEO_MESSAGE)) { Log.d(TAG,"type is PICK_VIDEO_MESSAGE" ); pickVideo(message); } else if (type.equals(TAKE_VIDEO_MESSAGE)) { Log.d(TAG,"type is TAKE_VIDEO_MESSAGE" ); takeVideo(message); } else if (type.equals(SystemDispatcher.ACTIVITY_RESULT_MESSAGE)) { Log.d(TAG,"type is ACTIVITY_RESULT_MESSAGE" ); onActivityResult(message); } } }); } static void pickVideo(Map message) { Boolean multiple = false; Activity activity = org.qtproject.qt5.android.QtNative.activity(); Intent intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Video.Media.EXTERNAL_CONTENT_URI); intent.setType("video/*"); if (message.containsKey("multiple")) { multiple = (Boolean) message.get("multiple"); } if (multiple) { intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); } // >= API 18 activity.startActivityForResult(intent, PICK_VIDEO_ACTION); } static void takeVideo(Map message) { if (message.containsKey("broadcast")) { broadcast = (Boolean) message.get("broadcast"); } Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE); Activity activity = org.qtproject.qt5.android.QtNative.activity(); if (takeVideoIntent.resolveActivity(activity.getPackageManager()) != null) { activity.startActivityForResult(takeVideoIntent, TAKE_VIDEO_ACTION ); } } static private void onActivityResult(Map message) { int resultCode = (Integer) message.get("resultCode"); if (resultCode != Activity.RESULT_OK) return; int requestCode = (Integer) message.get("requestCode"); Intent data = (Intent) message.get("data"); if (requestCode == PICK_VIDEO_ACTION) { importVideo(data); } else if (requestCode == TAKE_VIDEO_ACTION) { // Android 4.x. data will be null. // Android 6.x. data is not null Log.d(TAG,"request code is TAKE_VIDEO_ACTION:" + TAKE_VIDEO_ACTION ); mVideoUri = data.getData(); Log.d(TAG, "after TAKE_VIDEO_ACTION mVideoUri is: " + mVideoUri ); if (mVideoUri != null ) { importVideoFromContentUri(mVideoUri); } if (broadcast) broadcastToMediaScanner(mVideoUri); } } static private void importVideo(Intent data) { Uri uri = data.getData(); Log.d(TAG,"importVideo: uri:" + uri); Log.d(TAG,"importVideo: type: " + data.getType()); if (data.getClipData() != null) { importVideoFromClipData(data); } else if (uri != null ) { if (uri.getScheme().equals("file")) { importVideoFromFileUri(uri); } else { importVideoFromContentUri(uri); } } } static private void importVideoFromFileUri(Uri uri) { ArrayList list = new ArrayList(1); list.add(uri); importVideoFromFileUri(list); } static private void importVideoFromFileUri(List uris) { Map reply = new HashMap(); ArrayList list = new ArrayList(uris.size()); ArrayList thumbList = new ArrayList(uris.size()); Activity activity = org.qtproject.qt5.android.QtNative.activity(); for (int i = 0 ; i < uris.size() ; i++) { Uri uri = Uri.parse( Uri.decode(uris.get(i).toString())); String thumbPath = getVideoThumbnail(activity , uri.getPath() ); Uri thumbUri = Uri.fromParts("file", thumbPath ,""); thumbList.add(thumbUri.toString()); list.add(uris.get(i).toString()); } reply.put("videoUrls",list); reply.put("videoThumbUrls",thumbList); Log.d(TAG , "dispatching to systemdispatcher with CHOSEN_MESSAGE" ); SystemDispatcher.dispatch(CHOSEN_MESSAGE,reply); } static private void importVideoFromContentUri(Uri uri) { Activity activity = org.qtproject.qt5.android.QtNative.activity(); String[] columns = { MediaStore.Video.Media.DATA }; Cursor cursor = activity.getContentResolver().query(uri, columns, null, null, null); if (cursor == null) { Log.d(TAG,"importVideoFromContentUri: Query failed"); return; } cursor.moveToFirst(); ArrayList uris = new ArrayList(cursor.getCount()); for (int i = 0 ; i < cursor.getCount(); i++) { int columnIndex; columnIndex = cursor.getColumnIndex(columns[0]); String path = cursor.getString(columnIndex); if (path == null) { Log.d(TAG,"importVideoFromContentUri: The path of video is null. The video may not on the storage."); continue; } Uri fileUri = Uri.fromParts("file",path,""); uris.add(fileUri); Log.d(TAG,"importVideoFromContentUri: " + fileUri.toString()); } importVideoFromFileUri(uris); cursor.close(); } public static String getVideoThumbnail(Context context, String filePath ) { Log.d(TAG , "getVideoThumbnail called for filePath:" + filePath); // first get the VideoId from the path String[] columns = { MediaStore.Video.Media._ID }; ContentResolver cr = context.getContentResolver(); Cursor cursor = cr.query( android.provider.MediaStore.Video.Media.EXTERNAL_CONTENT_URI , columns, MediaStore.Video.VideoColumns.DATA + " LIKE ?", new String[] { filePath }, null); cursor.moveToFirst(); if (cursor == null) { Log.e(TAG,"query for videoId failed"); return null; } cursor.moveToFirst(); long videoId = cursor.getLong(0); Log.d(TAG , "getVideoThumbnail videoId: " + videoId); // use videoId to get the thumbnail path try { String[] projection = { MediaStore.Video.Thumbnails.DATA, }; cursor = cr.query( MediaStore.Video.Thumbnails.EXTERNAL_CONTENT_URI, projection, MediaStore.Video.Thumbnails.VIDEO_ID + "=?", new String[] { String.valueOf(videoId) }, null); if (cursor.getCount() > 0 ) { cursor.moveToFirst(); return cursor.getString(0); } Log.w(TAG, "Sorry no thumbnail found with video id: " + videoId); Log.d(TAG, "Generating new thumbnail." ); Bitmap bMap = ThumbnailUtils.createVideoThumbnail( filePath , MediaStore.Video.Thumbnails.MICRO_KIND); if (bMap != null) { Log.d(TAG, "storing new thumbnail." ); storeThumbnail(cr , bMap , videoId , MediaStore.Video.Thumbnails.MICRO_KIND); } // Query again after storing the Thumbnail in MediaStrore.Video.Thumbnails Log.d(TAG, "Querying again for thumbnail" ); cursor = cr.query( MediaStore.Video.Thumbnails.EXTERNAL_CONTENT_URI, projection, MediaStore.Video.Thumbnails.VIDEO_ID + "=?", new String[] { String.valueOf(videoId) }, null); if (cursor.getCount() > 0 ) { Log.d(TAG, "Got thumbnail in second query." ); cursor.moveToFirst(); return cursor.getString(0); } } catch (Exception e) { Log.e(TAG, Log.getStackTraceString(e)); } return null; } private static void importVideoFromClipData(Intent data) { ClipData clipData = data.getClipData(); Log.d(TAG,"Video importFromClipData"); if (clipData.getItemCount() == 0) return; ArrayList uris = new ArrayList(clipData.getItemCount()); for (int i = 0 ; i < clipData.getItemCount() ; i++ ){ Uri uri = clipData.getItemAt(i).getUri(); uris.add(resolveUri(uri)); } importVideoFromFileUri(uris); } static private Uri resolveUri(Uri uri) { Activity activity = org.qtproject.qt5.android.QtNative.activity(); String[] columns = { MediaStore.Video.Media.DATA }; Cursor cursor = activity.getContentResolver().query(uri, columns, null, null, null); if (cursor == null) { Log.d(TAG,"Query failed"); return Uri.parse(""); } cursor.moveToFirst(); int columnIndex; columnIndex = cursor.getColumnIndex(columns[0]); String path = cursor.getString(columnIndex); cursor.close(); return Uri.fromParts("file",path,""); } private static void broadcastToMediaScanner(Uri uri) { Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); mediaScanIntent.setData(uri); Activity activity = org.qtproject.qt5.android.QtNative.activity(); activity.sendBroadcast(mediaScanIntent); } private static void storeThumbnail(ContentResolver cr, Bitmap thumb, long id, int kind) { ContentValues values = new ContentValues(4); values.put(MediaStore.Video.Thumbnails.KIND,kind); values.put(MediaStore.Video.Thumbnails.VIDEO_ID,(int)id ); values.put(MediaStore.Video.Thumbnails.HEIGHT,thumb.getHeight()); values.put(MediaStore.Video.Thumbnails.WIDTH,thumb.getWidth()); Uri url = cr.insert(MediaStore.Video.Thumbnails.EXTERNAL_CONTENT_URI, values); try { OutputStream thumbOut = cr.openOutputStream(url); thumb.compress(Bitmap.CompressFormat.JPEG, 100, thumbOut); thumbOut.close(); } catch (FileNotFoundException ex) { Log.e("IMAGE_COMPRESSION_ERROR", "File not found"); ex.printStackTrace(); } catch (IOException ex) { Log.e("IMAGE_COMPRESSION_ERROR", "IO Exception"); ex.printStackTrace(); } } }