├── .gitignore ├── .idea ├── inspectionProfiles │ └── Project_Default.xml └── jarRepositories.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── braver │ │ └── tool │ │ └── filepicker │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── braver │ │ │ └── tool │ │ │ └── filepicker │ │ │ ├── AppUtils.java │ │ │ ├── CustomPreferencesManager.java │ │ │ ├── PermissionUtils.java │ │ │ ├── PickerActivity.java │ │ │ └── PreviewActivity.java │ └── res │ │ ├── drawable │ │ ├── alert_btn_bg.xml │ │ ├── ic_close.xml │ │ ├── ic_permission_audio.xml │ │ ├── ic_permission_camera.xml │ │ ├── ic_permission_storage.xml │ │ ├── ic_splash_file.png │ │ ├── ic_video_play_lib.xml │ │ ├── pic_camera.png │ │ ├── pic_doc.png │ │ ├── pic_gallery.png │ │ ├── pic_pickup.png │ │ ├── preview_btn_bg.xml │ │ ├── shape_circle_lib.xml │ │ ├── splash_bg.xml │ │ └── splash_bg_grad.xml │ │ ├── font │ │ ├── alegreya_sans_sc_bold.ttf │ │ └── courgette.ttf │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── activity_preview.xml │ │ └── permission_alert_popup.xml │ │ ├── mipmap-anydpi-v26 │ │ └── ic_app_logo.xml │ │ ├── mipmap-hdpi │ │ ├── ic_app_logo.png │ │ ├── ic_app_logo_adaptive_back.png │ │ └── ic_app_logo_adaptive_fore.png │ │ ├── mipmap-mdpi │ │ ├── ic_app_logo.png │ │ ├── ic_app_logo_adaptive_back.png │ │ └── ic_app_logo_adaptive_fore.png │ │ ├── mipmap-xhdpi │ │ ├── ic_app_logo.png │ │ ├── ic_app_logo_adaptive_back.png │ │ └── ic_app_logo_adaptive_fore.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_app_logo.png │ │ ├── ic_app_logo_adaptive_back.png │ │ └── ic_app_logo_adaptive_fore.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_app_logo.png │ │ ├── ic_app_logo_adaptive_back.png │ │ └── ic_app_logo_adaptive_fore.png │ │ ├── values │ │ ├── attrs.xml │ │ ├── colors.xml │ │ └── strings.xml │ │ └── xml │ │ └── provider_paths.xml │ └── test │ └── java │ └── com │ └── braver │ └── tool │ └── filepicker │ └── ExampleUnitTest.java ├── app_demo_pic.jpg ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── picker ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── braver │ │ └── tool │ │ └── picker │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── braver │ │ │ └── tool │ │ │ └── picker │ │ │ ├── BraveFileGetter.kt │ │ │ ├── BraveFilePath.kt │ │ │ ├── BraveFilePicker.kt │ │ │ ├── BraveFileType.kt │ │ │ ├── BraveFileUtils.kt │ │ │ ├── BraverDocPathUtils.kt │ │ │ ├── CameraActivity.java │ │ │ └── image │ │ │ └── ImageUtil.kt │ └── res │ │ ├── drawable │ │ ├── bg_record_video.xml │ │ ├── ic_automatic_flash.xml │ │ ├── ic_capture_image_icon.xml │ │ ├── ic_flash_off.xml │ │ ├── ic_flash_on.xml │ │ ├── ic_rotate_arrow_icon.xml │ │ ├── ic_solid_dot_circle_red.xml │ │ └── ic_solid_dot_circle_white.xml │ │ ├── layout │ │ └── camera_activity.xml │ │ └── values │ │ └── color.xml │ └── test │ └── java │ └── com │ └── braver │ └── tool │ └── picker │ └── ExampleUnitTest.java └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | .idea 4 | /.idea/* 5 | /local.properties 6 | /.idea/caches 7 | /.idea/libraries 8 | /.idea/modules.xml 9 | /.idea/workspace.xml 10 | /.idea/navEditor.xml 11 | /.idea/assetWizardSettings.xml 12 | .DS_Store 13 | /build 14 | /captures 15 | .externalNativeBuild 16 | .cxx 17 | local.properties 18 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android FilePicker 2 | [![](https://jitpack.io/v/braver-tool/Android11FilePicker.svg)](https://jitpack.io/#braver-tool/Android11FilePicker) 3 | 4 | # Description 5 | 6 | This app can make the file picking process easy, which allows you to select Pictures, Videos, and Documents. Also, that has Capturing Photo/Video option. 7 | For using this library, you need to migrate your project to AndroidX(If your project is not migrated to AndroidX). 8 | 9 | This works without MANAGE_EXTERNAL_STORAGE permission Since it's very useful when you publish the app on Google Plsy Strore. 10 | 11 | 12 | 13 | 14 | 15 | # Getting Started 16 | 17 | To add this library to your project, please follow below steps 18 | 19 | Add this in your root `build.gradle` file (project level gradle file): 20 | 21 | ```gradle 22 | allprojects { 23 | repositories { 24 | maven { url "https://www.jitpack.io" } 25 | } 26 | } 27 | 28 | buildscript { 29 | repositories { 30 | maven { url "https://www.jitpack.io" } 31 | } 32 | } 33 | ``` 34 | 35 | Then, Add this in your root `build.gradle` file (app level gradle file): 36 | 37 | add implementation 'implementation 'com.github.braver-tool:Android11FilePicker:1.0.0' to your build.gradle dependencies block. 38 | 39 | for example: 40 | 41 | ``` 42 | dependencies { 43 | implementation 'com.github.braver-tool:Android11FilePicker:1.0.0' 44 | } 45 | ``` 46 | 47 | # Key Features : 48 | 49 | - Kotlin language supported 50 | - Fully Handled Android's Dangerous Permissions 51 | - Compressing option for selected images included, 52 | - To Preview your selected images using 'PhotoView' 53 | - To Preview your selected videos using exoplayer 54 | - Can pick any non-media files like PDF,Doc,Txt files without using MANAGE_EXTERNAL_STORAGE permission 55 | - Supports upto Android 12 and SplashScreen API 56 | 57 | This library compatible with Android 6 and above 58 | 59 | 60 | ## Version Compatibility 61 | It depends on your targetAPI: 62 | - `targetAPI <= 28`, you are fine ;) 63 | - `targetAPI >= 29`, please enable `requestLegacyExternalStorage` on your Manifest.xml file 64 | 65 | ## How to use the included SD-card picker: 66 | 67 | ### Include a provider element 68 | 69 | Due to changes in Android 6.0 Marshmallow, bare File URIs can no 70 | longer be returned in a safe way. This change requires you to add an 71 | entry to your manifest. 72 | 73 | **NOTE: If you have an existing content provider in your app with the same authority you will have a conflict.** 74 | 75 | ```xml 76 | 81 | 85 | 86 | ``` 87 | 88 | ## Permissions Needed : 89 | 90 | * android.permission.READ_EXTERNAL_STORAGE 91 | * android.permission.CAMERA 92 | * android.permission.RECORD_AUDIO 93 | 94 | ### Include the camera activity 95 | 96 | **NOTE: The theme set in the manifest is important. 97 | 98 | ```xml 99 | 103 | 104 | ``` 105 | 106 | ### Handling the ActivityResult 107 | 108 | You can use the included utility method to parse the activity result: 109 | 110 | ```java 111 | activityResultLauncherForGallery = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> { 112 | if (result.getResultCode() == RESULT_OK && result.getData() != null) { 113 | Uri selectedMediaUri = result.getData().getData(); 114 | File imageFile = new BraveFilePicker().setActivity(PickerActivity.this).setIsCompressImage(false).setIsTrimVide(false).setFileType(BraveFileType.IMAGE).setDestinationFilePath(AppUtils.getRandomImageFileName(PickerActivity.this)).setContentUri(selectedMediaUri).getSourceFile(); 115 | // Do something with the result... 116 | } 117 | }); 118 | ``` 119 | 120 | ## Want to customize further? 121 | 122 | Sample project is here(https://github.com/braver-tool/Android11FilePicker) 123 | 124 | ## Usage 125 | 126 | * Photoview - ImageView for Android that supports zooming, by various touch gestures. 127 | https://github.com/Baseflow/PhotoView -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Created by https://github.com/braver-tool on 12/10/21, 08:30 PM 4 | * * Copyright (c) 2021 . All rights reserved. 5 | * * Last modified 23/03/22, 09:45 AM 6 | * 7 | */ 8 | 9 | apply plugin: 'com.android.application' 10 | android { 11 | compileSdkVersion 31 12 | defaultConfig { 13 | applicationId "com.braver.tool.filepicker" 14 | minSdkVersion 23 15 | targetSdkVersion 31 16 | versionCode 1 17 | versionName "1.0" 18 | 19 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 20 | } 21 | 22 | buildTypes { 23 | release { 24 | minifyEnabled false 25 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 26 | } 27 | } 28 | compileOptions { 29 | sourceCompatibility JavaVersion.VERSION_1_8 30 | targetCompatibility JavaVersion.VERSION_1_8 31 | } 32 | } 33 | 34 | dependencies { 35 | implementation 'androidx.appcompat:appcompat:1.4.1' 36 | implementation 'com.google.android.material:material:1.5.0' 37 | implementation 'androidx.constraintlayout:constraintlayout:2.1.3' 38 | implementation 'androidx.core:core-splashscreen:1.0.0-beta01' 39 | testImplementation 'junit:junit:4.13.2' 40 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 41 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 42 | implementation 'com.intuit.sdp:sdp-android:1.0.6' 43 | implementation 'com.google.android.exoplayer:exoplayer:2.17.0' 44 | implementation 'com.github.chrisbanes:PhotoView:2.3.0' 45 | //implementation project(':picker') 46 | implementation 'com.github.braver-tool:Android11FilePicker:1.0.0' 47 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/androidTest/java/com/braver/tool/filepicker/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Created by https://github.com/braver-tool on 12/10/21, 08:30 PM 4 | * * Copyright (c) 2021 . All rights reserved. 5 | * * Last modified 23/03/22, 09:45 AM 6 | * 7 | */ 8 | 9 | package com.braver.tool.filepicker; 10 | 11 | import android.content.Context; 12 | 13 | import androidx.test.platform.app.InstrumentationRegistry; 14 | import androidx.test.ext.junit.runners.AndroidJUnit4; 15 | 16 | import org.junit.Test; 17 | import org.junit.runner.RunWith; 18 | 19 | import static org.junit.Assert.*; 20 | 21 | /** 22 | * Instrumented test, which will execute on an Android device. 23 | * 24 | * @see Testing documentation 25 | */ 26 | @RunWith(AndroidJUnit4.class) 27 | public class ExampleInstrumentedTest { 28 | @Test 29 | public void useAppContext() { 30 | // Context of the app under test. 31 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 32 | assertEquals("com.braver.tool.filepicker", appContext.getPackageName()); 33 | } 34 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 25 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 37 | 40 | 41 | 46 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /app/src/main/java/com/braver/tool/filepicker/AppUtils.java: -------------------------------------------------------------------------------- 1 | /* * * * Created by https://github.com/braver-tool on 12/10/21, 08:30 PM * * Copyright (c) 2021 . All rights reserved. * * Last modified 23/03/22, 09:45 AM * */ package com.braver.tool.filepicker; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.util.Log; import androidx.core.content.FileProvider; import java.io.File; import java.util.Random; public class AppUtils { private static final boolean IS_DEBUG = false; //public static final String DOC_ALERT_MSG = "You have to allow permission to access this feature"; public static final String START_POINT = "Source file path is \n ***/***/"; public static String getRandomImageFileName(Context context) { File mediaStorageDir = context.getFilesDir(); int random = new Random().nextInt(8997); String mImageName = "Braver_Img".concat("_") + random + ".jpg"; return new File(mediaStorageDir.getPath() + "/" + mImageName).getAbsolutePath(); } /** * @param tag - Contains class name * @param msg - Log message as String * Method used to print log in console for development */ public static void printLogConsole(String tag, String msg) { if (IS_DEBUG) { Log.d("##@" + tag, msg); } } /** * This method is a string return method * Method used get file name from local file path */ public static String getFileNameFromPath(String filePath) { String fileName = ""; try { fileName = filePath.substring(filePath.lastIndexOf('/') + 1); } catch (Exception e) { AppUtils.printLogConsole("getFileNameFromPath", "Exception-------->" + e.getMessage()); } return fileName; } public static void openDocument(Context context, String filePath) { try { Uri uri = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", new File(filePath)); Intent intent = new Intent(Intent.ACTION_VIEW); if (filePath.contains(".doc") || filePath.contains(".docx")) { intent.setDataAndType(uri, "application/msword"); } else if (filePath.contains(".pdf")) { intent.setDataAndType(uri, "application/pdf"); } else if (filePath.contains(".ppt") || filePath.contains(".pptx")) { intent.setDataAndType(uri, "application/vnd.ms-powerpoint"); } else if (filePath.contains(".xls") || filePath.contains(".xlsx")) { intent.setDataAndType(uri, "application/vnd.ms-excel"); } else if (filePath.contains(".zip") || filePath.contains(".rar")) { intent.setDataAndType(uri, "application/x-wav"); } else if (filePath.contains(".rtf")) { intent.setDataAndType(uri, "application/rtf"); } else if (filePath.contains(".wav") || filePath.contains(".mp3")) { intent.setDataAndType(uri, "audio/x-wav"); } else if (filePath.contains(".gif")) { intent.setDataAndType(uri, "image/gif"); } else if (filePath.contains(".jpg") || filePath.contains(".jpeg") || filePath.contains(".png")) { intent.setDataAndType(uri, "image/jpeg"); } else if (filePath.contains(".txt")) { intent.setDataAndType(uri, "text/plain"); } else if (filePath.contains(".3gp") || filePath.contains(".mpg") || filePath.contains(".mpeg") || filePath.contains(".mpe") || filePath.contains(".mp4") || filePath.contains(".avi")) { intent.setDataAndType(uri, "video/*"); } else { intent.setDataAndType(uri, "*/*"); } intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); } catch (Exception e) { AppUtils.printLogConsole("openDocument", "Exception-------->" + e.getMessage()); } } } -------------------------------------------------------------------------------- /app/src/main/java/com/braver/tool/filepicker/CustomPreferencesManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Created by https://github.com/braver-tool on 12/10/21, 08:30 PM 4 | * * Copyright (c) 2021 . All rights reserved. 5 | * * Last modified 23/03/22, 09:45 AM 6 | * 7 | */ 8 | 9 | package com.braver.tool.filepicker; 10 | 11 | 12 | import android.content.Context; 13 | import android.content.SharedPreferences; 14 | import android.os.Build; 15 | 16 | import static com.braver.tool.filepicker.PermissionUtils.IS_PERMISSION_DIALOG_ANDROID_11; 17 | 18 | public class CustomPreferencesManager { 19 | private static final String NOTIFICATION_PREFERENCES_NAME = "braver_preferences"; 20 | public final SharedPreferences notificationPrefManager; 21 | 22 | /** 23 | * Method used to initiate Notification User Defaults 24 | * 25 | * @param context - Current Activity 26 | */ 27 | public CustomPreferencesManager(Context context) { 28 | Context storageContext; 29 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 30 | final Context deviceContext = context.createDeviceProtectedStorageContext(); 31 | if (!deviceContext.moveSharedPreferencesFrom(context, NOTIFICATION_PREFERENCES_NAME)) { 32 | AppUtils.printLogConsole("CustomPreferencesManager", "Failed to migrate shared preferences."); 33 | } 34 | storageContext = deviceContext; 35 | } else { 36 | storageContext = context; 37 | } 38 | notificationPrefManager = storageContext.getSharedPreferences(NOTIFICATION_PREFERENCES_NAME, Context.MODE_PRIVATE); 39 | } 40 | 41 | /** 42 | * Method used to get UserDefault 43 | */ 44 | public String getPermissionDialogData() { 45 | String data = ""; 46 | if (notificationPrefManager != null) { 47 | data = notificationPrefManager.getString(IS_PERMISSION_DIALOG_ANDROID_11, ""); 48 | } 49 | return data; 50 | } 51 | 52 | /** 53 | * Method used to set UserDefault 54 | * 55 | * @param permissionType - String data 56 | */ 57 | public void setPermissionDialogData(String permissionType) { 58 | if (notificationPrefManager != null) { 59 | SharedPreferences.Editor editor = notificationPrefManager.edit(); 60 | editor.putString(IS_PERMISSION_DIALOG_ANDROID_11, permissionType); 61 | editor.apply(); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /app/src/main/java/com/braver/tool/filepicker/PermissionUtils.java: -------------------------------------------------------------------------------- 1 | /* * * * Created by https://github.com/braver-tool on 12/10/21, 08:30 PM * * Copyright (c) 2021 . All rights reserved. * * Last modified 23/03/22, 09:45 AM * */ package com.braver.tool.filepicker; import android.Manifest; import android.app.Activity; import android.app.Dialog; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Build; import android.os.Environment; import android.provider.Settings; import android.view.Gravity; import android.view.Window; import android.view.WindowManager; import android.widget.ImageView; import android.widget.TextView; import androidx.core.app.ActivityCompat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Objects; /** * Class for android 6.0 and above live permission handling */ public class PermissionUtils { public static final String PERMISSION_CAMERA = "Camera"; public static final String PERMISSION_GALLERY = "Gallery"; public static final String PERMISSION_MIC = "Audio"; public static final int MANDATORY_GALLERY_PERMISSION_REQ_CODE = 3646; public static final String READ_EXTERNAL_STORAGE_PERMISSION = Manifest.permission.READ_EXTERNAL_STORAGE; public static final String IS_PERMISSION_DIALOG_ANDROID_11 = "is_permission_dialog_android_11"; /** * Method helps to add a permission into permission list */ private static void checkPermissionGrant(Context context, ArrayList permissionList, String permission) { if (ActivityCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) { permissionList.add(permission); } } /** * Method handling the permissions to getting camera and storage access * * @param context activity context * @return boolean variable */ public static boolean isFileAccessPermissionGranted(Context context) { boolean status = true; ArrayList locationPermissionList = new ArrayList<>(); checkPermissionGrant(context, locationPermissionList, Manifest.permission.CAMERA); checkPermissionGrant(context, locationPermissionList, Manifest.permission.RECORD_AUDIO); checkPermissionGrant(context, locationPermissionList, Manifest.permission.READ_EXTERNAL_STORAGE); if (locationPermissionList.size() > 0) { if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q && locationPermissionList.contains(Manifest.permission.READ_EXTERNAL_STORAGE) && Environment.isExternalStorageManager()) { locationPermissionList.remove(Manifest.permission.READ_EXTERNAL_STORAGE); } } if (locationPermissionList.size() > 0) { status = false; } return status; } /** * Method to display the dialog alert for camera and storage permission * * @param context activity context */ public static void showFileAccessPermissionRequestDialog(Context context) { ArrayList locationPermissionList = new ArrayList<>(); checkPermissionGrant(context, locationPermissionList, Manifest.permission.CAMERA); checkPermissionGrant(context, locationPermissionList, Manifest.permission.RECORD_AUDIO); checkPermissionGrant(context, locationPermissionList, Manifest.permission.READ_EXTERNAL_STORAGE); if (locationPermissionList.size() > 0) { if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q && locationPermissionList.contains(Manifest.permission.READ_EXTERNAL_STORAGE) && Environment.isExternalStorageManager()) { locationPermissionList.remove(Manifest.permission.READ_EXTERNAL_STORAGE); } } if (locationPermissionList.size() > 0) { ActivityCompat.requestPermissions((Activity) context, locationPermissionList.toArray(new String[locationPermissionList.size()]), MANDATORY_GALLERY_PERMISSION_REQ_CODE); } } /** * Method handling the permissions to isCameraAccessed * * @param context activity context * @return boolean variable */ public static boolean isCameraAccessed(Context context) { boolean status = true; ArrayList locationPermissionList = new ArrayList<>(); checkPermissionGrant(context, locationPermissionList, Manifest.permission.CAMERA); if (locationPermissionList.size() > 0) { status = false; } return status; } /** * Method handling the permissions to isAudioAccessed * * @param context activity context * @return boolean variable */ public static boolean isAudioAccessed(Context context) { boolean status = true; ArrayList locationPermissionList = new ArrayList<>(); checkPermissionGrant(context, locationPermissionList, Manifest.permission.RECORD_AUDIO); if (locationPermissionList.size() > 0) { status = false; } return status; } /** * @param context - Dialog call from * @param permissionType - Permission for ? */ public static void showAlertForAppInfoScreen(Context context, String permissionType) { if (!isValidPermissionDialog(context, permissionType)) { return; } final Dialog dialog = new Dialog(context); dialog.setContentView(R.layout.permission_alert_popup); Objects.requireNonNull(dialog.getWindow()).setBackgroundDrawableResource(android.R.color.transparent); Window window = dialog.getWindow(); WindowManager.LayoutParams wlp; if (window != null) { wlp = window.getAttributes(); wlp.gravity = Gravity.CENTER; window.setAttributes(wlp); } ImageView permissionAlertTypeImageView = dialog.findViewById(R.id.permissionAlertTypeImageView); TextView permissionAlertContentTextView = dialog.findViewById(R.id.permissionAlertContentTextView); TextView permissionAlertNotNowTextView = dialog.findViewById(R.id.permissionAlertNotNowTextView); TextView permissionAlertSettingsTextView = dialog.findViewById(R.id.permissionAlertSettingsTextView); permissionAlertContentTextView.setText(getSpecialPermissionContent(context, permissionType)); permissionAlertTypeImageView.setImageResource(getSpecialPermissionVector(permissionType)); permissionAlertNotNowTextView.setOnClickListener(v -> dialog.dismiss()); permissionAlertSettingsTextView.setOnClickListener(v -> { Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); Uri uri = Uri.fromParts("package", context.getPackageName(), null); intent.setData(uri); context.startActivity(intent); dialog.dismiss(); }); dialog.show(); } /** * @param context - context * @param permissionType - String data * @return - boolean data * Method used to return isPermissionDialog is shown for Android 11 */ private static boolean isValidPermissionDialog(Context context, String permissionType) { boolean isValid = false; CustomPreferencesManager customPreferencesManager = new CustomPreferencesManager(context); String callType = customPreferencesManager.getPermissionDialogData(); callType = callType.isEmpty() ? permissionType : callType.concat(",").concat(permissionType); customPreferencesManager.setPermissionDialogData(callType); List permissionTypeList = Arrays.asList(callType.split(",")); int count = Collections.frequency(permissionTypeList, permissionType); if (count > 1) { isValid = true; } return isValid; } /** * @param context - current activity * @param permissionType - String data * @return - */ private static String getSpecialPermissionContent(Context context, String permissionType) { String msg = ""; if (permissionType.equalsIgnoreCase(PERMISSION_CAMERA)) { msg = context.getResources().getString(R.string.camera_video_permission); } else if (permissionType.equalsIgnoreCase(PERMISSION_GALLERY)) { msg = context.getResources().getString(R.string.storage_permission); } else if (permissionType.equalsIgnoreCase(PERMISSION_MIC)) { msg = context.getResources().getString(R.string.call_permission); } return msg; } /** * @param permissionType - String data * @return - */ private static int getSpecialPermissionVector(String permissionType) { int vector; if (permissionType.equalsIgnoreCase(PERMISSION_CAMERA)) { vector = R.drawable.ic_permission_camera; return vector; } else if (permissionType.equalsIgnoreCase(PERMISSION_GALLERY)) { vector = R.drawable.ic_permission_storage; return vector; } return 0; } } -------------------------------------------------------------------------------- /app/src/main/java/com/braver/tool/filepicker/PickerActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Created by https://github.com/braver-tool on 12/10/21, 08:30 PM 4 | * * Copyright (c) 2021 . All rights reserved. 5 | * * Last modified 23/03/22, 09:45 AM 6 | * 7 | */ 8 | 9 | package com.braver.tool.filepicker; 10 | 11 | import static com.braver.tool.filepicker.AppUtils.START_POINT; 12 | import static com.braver.tool.filepicker.PermissionUtils.PERMISSION_CAMERA; 13 | import static com.braver.tool.filepicker.PermissionUtils.PERMISSION_GALLERY; 14 | import static com.braver.tool.filepicker.PermissionUtils.PERMISSION_MIC; 15 | import static com.braver.tool.filepicker.PermissionUtils.READ_EXTERNAL_STORAGE_PERMISSION; 16 | import static com.braver.tool.picker.BraveFileUtils.IMAGE_PATH; 17 | import static com.braver.tool.picker.BraveFileUtils.IS_IMAGE_FILE; 18 | import static com.braver.tool.picker.BraveFileUtils.PREVIEW_IMAGE_FILE_PATH; 19 | import static com.braver.tool.picker.BraveFileUtils.PREVIEW_VIDEO_FILE_PATH; 20 | import static com.braver.tool.picker.BraveFileUtils.VIDEO_PATH; 21 | 22 | import android.content.Intent; 23 | import android.content.pm.PackageManager; 24 | import android.graphics.Color; 25 | import android.net.Uri; 26 | import android.os.Build; 27 | import android.os.Bundle; 28 | import android.os.Handler; 29 | import android.provider.MediaStore; 30 | import android.view.View; 31 | import android.view.ViewTreeObserver; 32 | import android.view.WindowManager; 33 | import android.widget.LinearLayout; 34 | import android.widget.TextView; 35 | 36 | import androidx.activity.result.ActivityResultLauncher; 37 | import androidx.activity.result.contract.ActivityResultContracts; 38 | import androidx.annotation.NonNull; 39 | import androidx.appcompat.app.AppCompatActivity; 40 | import androidx.core.app.ActivityCompat; 41 | import androidx.core.splashscreen.SplashScreen; 42 | 43 | import com.braver.tool.picker.BraveFilePicker; 44 | import com.braver.tool.picker.BraveFileType; 45 | import com.braver.tool.picker.BraverDocPathUtils; 46 | import com.braver.tool.picker.CameraActivity; 47 | 48 | import java.io.File; 49 | 50 | public class PickerActivity extends AppCompatActivity implements View.OnClickListener { 51 | private String tempFileAbsolutePath = ""; 52 | private int selectedOption = 0; 53 | private TextView sourceFilePathTextView; 54 | private ActivityResultLauncher activityResultLauncherForGallery; 55 | private ActivityResultLauncher activityResultLauncherForCamera; 56 | private ActivityResultLauncher activityResultLauncherForDocs; 57 | private int resultOption = 0; 58 | private boolean isPreviewClicks = false; 59 | private TextView previewTextView; 60 | private boolean isNavigate = false; 61 | 62 | @Override 63 | protected void onCreate(Bundle savedInstanceState) { 64 | super.onCreate(savedInstanceState); 65 | SplashScreen.installSplashScreen(this); 66 | new Handler().postDelayed(() -> isNavigate = true, 2000); 67 | setupOnPreDrawListenerToRootView(); 68 | setContentView(R.layout.activity_main); 69 | setTheme(); 70 | initializeViews(); 71 | } 72 | 73 | @Override 74 | protected void onResume() { 75 | isPreviewClicks = false; 76 | super.onResume(); 77 | } 78 | 79 | @Override 80 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { 81 | if (requestCode == PermissionUtils.MANDATORY_GALLERY_PERMISSION_REQ_CODE) { 82 | boolean cameraAndStoragePermissionGranted = true; 83 | for (int grantResult : grantResults) { 84 | cameraAndStoragePermissionGranted &= grantResult == PackageManager.PERMISSION_GRANTED; 85 | } 86 | if (cameraAndStoragePermissionGranted) { 87 | callFileAccessIntent(); 88 | } else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q && (!PermissionUtils.isAudioAccessed(PickerActivity.this) || !ActivityCompat.shouldShowRequestPermissionRationale(this, READ_EXTERNAL_STORAGE_PERMISSION) || !PermissionUtils.isCameraAccessed(PickerActivity.this))) { 89 | if (!ActivityCompat.shouldShowRequestPermissionRationale(this, READ_EXTERNAL_STORAGE_PERMISSION)) { 90 | PermissionUtils.showAlertForAppInfoScreen(this, PERMISSION_GALLERY); 91 | } else if (!PermissionUtils.isCameraAccessed(this)) { 92 | PermissionUtils.showAlertForAppInfoScreen(this, PERMISSION_CAMERA); 93 | } else if (!PermissionUtils.isAudioAccessed(PickerActivity.this)) { 94 | PermissionUtils.showAlertForAppInfoScreen(this, PERMISSION_MIC); 95 | } 96 | } else { 97 | AppUtils.printLogConsole("onRequestPermissionsResult", "-------->Permission Denied!!"); 98 | } 99 | } 100 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 101 | } 102 | 103 | @Override 104 | public void onClick(View v) { 105 | if (v.getId() == R.id.cameraLayout) { 106 | selectedOption = 1; 107 | callFileAccessIntent(); 108 | } else if (v.getId() == R.id.galleryLayout) { 109 | selectedOption = 2; 110 | callFileAccessIntent(); 111 | } else if (v.getId() == R.id.docLayout) { 112 | selectedOption = 3; 113 | callFileAccessIntent(); 114 | } else if (v.getId() == R.id.previewButtonTextView) { 115 | if (!isPreviewClicks) { 116 | isPreviewClicks = true; 117 | if (resultOption == 1 || resultOption == 2) { 118 | navigateToPreviewScreen(); 119 | } else { 120 | AppUtils.openDocument(PickerActivity.this, tempFileAbsolutePath); 121 | } 122 | } 123 | 124 | } 125 | } 126 | 127 | /** 128 | * Declaration setupOnPreDrawListenerToRootView() 129 | * Set to navigate 130 | */ 131 | private void setupOnPreDrawListenerToRootView() { 132 | View mViewContent = findViewById(android.R.id.content); 133 | mViewContent.getViewTreeObserver().addOnPreDrawListener( 134 | new ViewTreeObserver.OnPreDrawListener() { 135 | @Override 136 | public boolean onPreDraw() { 137 | if (isNavigate) { 138 | mViewContent.getViewTreeObserver().removeOnPreDrawListener(this); 139 | return true; 140 | } else { 141 | return false; 142 | } 143 | } 144 | }); 145 | /* if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { 146 | getSplashScreen().setOnExitAnimationListener(splashScreenView -> ); 147 | }*/ 148 | } 149 | 150 | /** 151 | * Declaration setSourcePathView() 152 | * Set file path to the view and show Preview Button 153 | */ 154 | private void setSourcePathView(String path) { 155 | tempFileAbsolutePath = path; 156 | String filePath = AppUtils.getFileNameFromPath(path); 157 | sourceFilePathTextView.setText(START_POINT.concat(filePath)); 158 | previewTextView.setVisibility(View.VISIBLE); 159 | } 160 | 161 | /** 162 | * Declaration navigateToPreviewScreen() 163 | * Navigation to Preview Screen 164 | */ 165 | private void navigateToPreviewScreen() { 166 | Bundle bundle = new Bundle(); 167 | bundle.putString(resultOption == 1 ? PREVIEW_IMAGE_FILE_PATH : PREVIEW_VIDEO_FILE_PATH, tempFileAbsolutePath); 168 | Intent navigateTo = new Intent(PickerActivity.this, PreviewActivity.class); 169 | navigateTo.putExtras(bundle); 170 | startActivity(navigateTo); 171 | } 172 | 173 | 174 | /** 175 | * Declaration initializeViews() 176 | * Initialize views with the XML 177 | */ 178 | private void initializeViews() { 179 | LinearLayout cameraLayout = findViewById(R.id.cameraLayout); 180 | LinearLayout galleryLayout = findViewById(R.id.galleryLayout); 181 | LinearLayout docLayout = findViewById(R.id.docLayout); 182 | sourceFilePathTextView = findViewById(R.id.sourceFilePathTextView); 183 | previewTextView = findViewById(R.id.previewButtonTextView); 184 | cameraLayout.setOnClickListener(this); 185 | galleryLayout.setOnClickListener(this); 186 | docLayout.setOnClickListener(this); 187 | previewTextView.setOnClickListener(this); 188 | previewTextView.setVisibility(View.GONE); 189 | activityResultLauncherForGallery = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> { 190 | if (result.getResultCode() == RESULT_OK && result.getData() != null) { 191 | try { 192 | Uri selectedMediaUri = result.getData().getData(); 193 | if (selectedMediaUri.toString().contains("image") || selectedMediaUri.toString().contains("jpg") || selectedMediaUri.toString().contains("jpeg") || selectedMediaUri.toString().contains("png")) { 194 | File imageFile = new BraveFilePicker().setActivity(PickerActivity.this).setIsCompressImage(false).setIsTrimVide(false).setFileType(BraveFileType.IMAGE).setDestinationFilePath(AppUtils.getRandomImageFileName(PickerActivity.this)).setContentUri(selectedMediaUri).getSourceFile(); 195 | resultOption = 1; 196 | if (imageFile != null) { 197 | setSourcePathView(imageFile.getAbsolutePath()); 198 | } 199 | } else { 200 | //For Video 201 | resultOption = 2; 202 | setSourcePathView(String.valueOf(selectedMediaUri)); 203 | } 204 | } catch (Exception e) { 205 | AppUtils.printLogConsole("activityResultLauncherForGallery", "Exception-------->" + e.getMessage()); 206 | } 207 | } 208 | }); 209 | activityResultLauncherForCamera = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> { 210 | if (result.getResultCode() == RESULT_OK && result.getData() != null) { 211 | try { 212 | boolean isImageFile = result.getData().getBooleanExtra(IS_IMAGE_FILE, false); 213 | String filePath; 214 | if (isImageFile) { 215 | filePath = result.getData().getStringExtra(IMAGE_PATH); 216 | resultOption = 1; 217 | } else { 218 | //For Video 219 | filePath = result.getData().getStringExtra(VIDEO_PATH); 220 | resultOption = 2; 221 | } 222 | setSourcePathView(filePath); 223 | } catch (Exception e) { 224 | AppUtils.printLogConsole("activityResultLauncherForCamera", "Exception-------->" + e.getMessage()); 225 | } 226 | } 227 | }); 228 | activityResultLauncherForDocs = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> { 229 | if (result.getResultCode() == RESULT_OK && result.getData() != null) { 230 | try { 231 | //String tempFileAbsolutePath = ""; 232 | Uri selectedDocUri = result.getData().getData(); 233 | if (selectedDocUri.toString().contains("video") || selectedDocUri.toString().contains("mp4") || selectedDocUri.toString().contains("mkv") || selectedDocUri.toString().contains("mov")) { 234 | //For Video 235 | resultOption = 2; 236 | setSourcePathView(String.valueOf(selectedDocUri)); 237 | } else { 238 | tempFileAbsolutePath = BraverDocPathUtils.Companion.getSourceDocPath(PickerActivity.this, selectedDocUri); 239 | resultOption = 3; 240 | setSourcePathView(tempFileAbsolutePath); 241 | } 242 | } catch (Exception e) { 243 | AppUtils.printLogConsole("activityResultLauncherForDocs", "Exception-------->" + e.getMessage()); 244 | } 245 | } 246 | }); 247 | } 248 | 249 | /** 250 | * Declaration callFileAccessIntent() 251 | * Handling click event of the picker icons 252 | */ 253 | private void callFileAccessIntent() { 254 | if (PermissionUtils.isFileAccessPermissionGranted(this)) { 255 | if (selectedOption == 1) { 256 | openCamera(); 257 | } else if (selectedOption == 2) { 258 | openGalleryForImageAndVideo(); 259 | } else if (selectedOption == 3) { 260 | openAttachments(); 261 | } 262 | } else { 263 | PermissionUtils.showFileAccessPermissionRequestDialog(this); 264 | } 265 | } 266 | 267 | /** 268 | * Declaration openGalleryForImageAndVideo() 269 | * Method is used to open the Gallery 270 | */ 271 | private void openGalleryForImageAndVideo() { 272 | try { 273 | Intent intent = new Intent(Intent.ACTION_PICK); 274 | intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "*/*"); 275 | activityResultLauncherForGallery.launch(intent); 276 | } catch (Exception exception) { 277 | AppUtils.printLogConsole("openGalleryForImageAndVideo", "Exception-------->" + exception.getMessage()); 278 | } 279 | } 280 | 281 | /** 282 | * Declaration openCamera() 283 | * Method is used to open the Camera 284 | */ 285 | private void openCamera() { 286 | try { 287 | Intent intent = new Intent(PickerActivity.this, CameraActivity.class); 288 | activityResultLauncherForCamera.launch(intent); 289 | } catch (Exception exception) { 290 | AppUtils.printLogConsole("openCamera", "Exception-------->" + exception.getMessage()); 291 | } 292 | } 293 | 294 | /** 295 | * Handling Get doc,pdf,xlx,apk,et., from user to send throw Attachments
296 | */ 297 | private void openAttachments() { 298 | try { 299 | Intent addAttachment = new Intent(Intent.ACTION_GET_CONTENT); 300 | addAttachment.setType("*/*"); 301 | addAttachment.setAction(Intent.ACTION_GET_CONTENT); 302 | addAttachment.setAction(Intent.ACTION_OPEN_DOCUMENT); 303 | activityResultLauncherForDocs.launch(addAttachment); 304 | } catch (Exception exception) { 305 | AppUtils.printLogConsole("openAttachments", "Exception-------->" + exception.getMessage()); 306 | } 307 | } 308 | 309 | /** 310 | * Declaration setTheme() 311 | * Method used to set transparent theme for the current window 312 | */ 313 | private void setTheme() { 314 | this.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); 315 | this.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); 316 | this.getWindow().setStatusBarColor(Color.TRANSPARENT); 317 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { 318 | this.getWindow().setDecorFitsSystemWindows(false); 319 | } else { 320 | this.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); 321 | } 322 | } 323 | } -------------------------------------------------------------------------------- /app/src/main/java/com/braver/tool/filepicker/PreviewActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Created by https://github.com/braver-tool on 12/10/21, 08:30 PM 4 | * * Copyright (c) 2021 . All rights reserved. 5 | * * Last modified 23/03/22, 09:45 AM 6 | * 7 | */ 8 | 9 | package com.braver.tool.filepicker; 10 | 11 | import static com.braver.tool.picker.BraveFileUtils.PREVIEW_IMAGE_FILE_PATH; 12 | import static com.braver.tool.picker.BraveFileUtils.PREVIEW_VIDEO_FILE_PATH; 13 | 14 | import android.graphics.Bitmap; 15 | import android.graphics.BitmapFactory; 16 | import android.net.Uri; 17 | import android.os.Bundle; 18 | import android.view.View; 19 | import android.widget.ImageView; 20 | import android.widget.LinearLayout; 21 | 22 | import androidx.appcompat.app.AppCompatActivity; 23 | 24 | import com.github.chrisbanes.photoview.PhotoView; 25 | import com.google.android.exoplayer2.C; 26 | import com.google.android.exoplayer2.ExoPlayer; 27 | import com.google.android.exoplayer2.MediaItem; 28 | import com.google.android.exoplayer2.Player; 29 | import com.google.android.exoplayer2.audio.AudioAttributes; 30 | import com.google.android.exoplayer2.source.MediaSource; 31 | import com.google.android.exoplayer2.source.ProgressiveMediaSource; 32 | import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; 33 | import com.google.android.exoplayer2.ui.StyledPlayerView; 34 | import com.google.android.exoplayer2.upstream.DataSource; 35 | import com.google.android.exoplayer2.upstream.DefaultDataSource; 36 | 37 | import java.util.Objects; 38 | 39 | public class PreviewActivity extends AppCompatActivity implements View.OnClickListener { 40 | private StyledPlayerView playerView; 41 | private ExoPlayer videoPlayer; 42 | private ImageView imagePlayPause; 43 | private boolean isVideoEnded; 44 | private String imageFilePath, videoFilePath; 45 | 46 | 47 | @Override 48 | protected void onCreate(Bundle savedInstanceState) { 49 | super.onCreate(savedInstanceState); 50 | setContentView(R.layout.activity_preview); 51 | initializeViews(); 52 | } 53 | 54 | @Override 55 | protected void onPause() { 56 | super.onPause(); 57 | if (videoPlayer != null) { 58 | videoPlayer.setPlayWhenReady(false); 59 | } 60 | 61 | } 62 | 63 | @Override 64 | protected void onDestroy() { 65 | super.onDestroy(); 66 | if (videoPlayer != null) { 67 | videoPlayer.release(); 68 | } 69 | } 70 | 71 | 72 | @Override 73 | public void onClick(View view) { 74 | int id = view.getId(); 75 | if (id == R.id.videoCloseImageView) { 76 | onBackPressed(); 77 | } 78 | } 79 | 80 | private void initializeViews() { 81 | playerView = findViewById(R.id.player_view_lib); 82 | imagePlayPause = findViewById(R.id.image_play_pause); 83 | PhotoView scaleImageView = findViewById(R.id.scaleImageView); 84 | LinearLayout videoViewParentLayout = findViewById(R.id.videoViewParentLayout); 85 | ImageView videoCloseImageView = findViewById(R.id.videoCloseImageView); 86 | videoCloseImageView.setOnClickListener(this); 87 | Bundle bundle = getIntent().getExtras(); 88 | if (bundle != null) { 89 | imageFilePath = bundle.getString(PREVIEW_IMAGE_FILE_PATH, ""); 90 | videoFilePath = bundle.getString(PREVIEW_VIDEO_FILE_PATH, ""); 91 | } 92 | if (!videoFilePath.isEmpty()) { 93 | scaleImageView.setVisibility(View.GONE); 94 | videoViewParentLayout.setVisibility(View.VISIBLE); 95 | initPlayer(); 96 | setDataInView(); 97 | } else if (!imageFilePath.isEmpty()) { 98 | videoViewParentLayout.setVisibility(View.GONE); 99 | scaleImageView.setVisibility(View.VISIBLE); 100 | Bitmap photoBitmap = BitmapFactory.decodeFile(imageFilePath); 101 | if (photoBitmap != null) { 102 | scaleImageView.setImageBitmap(photoBitmap); 103 | } 104 | } 105 | } 106 | 107 | 108 | /** 109 | * SettingUp exoplayer 110 | **/ 111 | private void initPlayer() { 112 | try { 113 | videoPlayer = new ExoPlayer.Builder(this).build(); 114 | playerView.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_FIT); 115 | playerView.setPlayer(videoPlayer); 116 | AudioAttributes audioAttributes = new AudioAttributes.Builder().setUsage(C.USAGE_MEDIA).setContentType(C.CONTENT_TYPE_MOVIE).build(); 117 | videoPlayer.setAudioAttributes(audioAttributes, true); 118 | } catch (Exception e) { 119 | AppUtils.printLogConsole("initPlayer", "Exception-------->" + e.getMessage()); 120 | } 121 | } 122 | 123 | private void setDataInView() { 124 | try { 125 | imagePlayPause.setOnClickListener(v -> onVideoClicked()); 126 | Objects.requireNonNull(playerView.getVideoSurfaceView()).setOnClickListener(v -> onVideoClicked()); 127 | buildMediaSource(Uri.parse(videoFilePath)); 128 | } catch (Exception e) { 129 | AppUtils.printLogConsole("setDataInView", "Exception-------->" + e.getMessage()); 130 | } 131 | } 132 | 133 | private void onVideoClicked() { 134 | try { 135 | long lastMinValue = 0; 136 | if (isVideoEnded) { 137 | seekTo(lastMinValue); 138 | videoPlayer.setPlayWhenReady(true); 139 | return; 140 | } 141 | seekTo(lastMinValue); 142 | videoPlayer.setPlayWhenReady(!videoPlayer.getPlayWhenReady()); 143 | } catch (Exception e) { 144 | AppUtils.printLogConsole("onVideoClicked", "Exception-------->" + e.getMessage()); 145 | } 146 | } 147 | 148 | private void seekTo(long sec) { 149 | if (videoPlayer != null) 150 | videoPlayer.seekTo(sec * 1000); 151 | } 152 | 153 | private void buildMediaSource(Uri mUri) { 154 | try { 155 | DataSource.Factory dataSourceFactory = new DefaultDataSource.Factory(this); 156 | MediaSource mediaSource = new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(mUri)); 157 | videoPlayer.addMediaSource(mediaSource); 158 | videoPlayer.prepare(); 159 | videoPlayer.setPlayWhenReady(true); 160 | videoPlayer.addListener(new Player.Listener() { 161 | @Override 162 | public void onPlaybackStateChanged(int playbackState) { 163 | switch (playbackState) { 164 | case Player.STATE_ENDED: 165 | imagePlayPause.setVisibility(View.VISIBLE); 166 | isVideoEnded = true; 167 | break; 168 | case Player.STATE_READY: 169 | isVideoEnded = false; 170 | break; 171 | case Player.STATE_BUFFERING: 172 | case Player.STATE_IDLE: 173 | default: 174 | break; 175 | } 176 | } 177 | 178 | @Override 179 | public void onPlayWhenReadyChanged(boolean playWhenReady, int reason) { 180 | imagePlayPause.setVisibility(playWhenReady ? View.GONE : View.VISIBLE); 181 | 182 | } 183 | }); 184 | } catch (Exception e) { 185 | AppUtils.printLogConsole("buildMediaSource", "Exception-------->" + e.getMessage()); 186 | } 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/alert_btn_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_close.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 14 | 17 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_permission_audio.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 14 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_permission_camera.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 15 | 18 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_permission_storage.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_splash_file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/braver-tool/Android11FilePicker/59356f50d19de74db1fce02b16fdc35b335581c3/app/src/main/res/drawable/ic_splash_file.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_video_play_lib.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/pic_camera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/braver-tool/Android11FilePicker/59356f50d19de74db1fce02b16fdc35b335581c3/app/src/main/res/drawable/pic_camera.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/pic_doc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/braver-tool/Android11FilePicker/59356f50d19de74db1fce02b16fdc35b335581c3/app/src/main/res/drawable/pic_doc.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/pic_gallery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/braver-tool/Android11FilePicker/59356f50d19de74db1fce02b16fdc35b335581c3/app/src/main/res/drawable/pic_gallery.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/pic_pickup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/braver-tool/Android11FilePicker/59356f50d19de74db1fce02b16fdc35b335581c3/app/src/main/res/drawable/pic_pickup.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/preview_btn_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_circle_lib.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/splash_bg.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 13 | 14 | 18 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/splash_bg_grad.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 13 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/font/alegreya_sans_sc_bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/braver-tool/Android11FilePicker/59356f50d19de74db1fce02b16fdc35b335581c3/app/src/main/res/font/alegreya_sans_sc_bold.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/courgette.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/braver-tool/Android11FilePicker/59356f50d19de74db1fce02b16fdc35b335581c3/app/src/main/res/font/courgette.ttf -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 17 | 18 | 26 | 27 | 39 | 40 | 50 | 51 | 61 | 62 | 67 | 68 | 78 | 79 | 80 | 90 | 91 | 96 | 97 | 107 | 108 | 109 | 119 | 120 | 125 | 126 | 136 | 137 | 138 | 139 | 149 | 150 | 151 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_preview.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 16 | 17 | 21 | 22 | 30 | 31 | 35 | 36 | 37 | 38 | 43 | 44 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /app/src/main/res/layout/permission_alert_popup.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | 22 | 23 | 32 | 33 | 38 | 39 | 44 | 45 | 56 | 57 | 73 | 74 | 75 | 80 | 81 | 94 | 95 | 96 | 109 | 110 | 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_app_logo.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_app_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/braver-tool/Android11FilePicker/59356f50d19de74db1fce02b16fdc35b335581c3/app/src/main/res/mipmap-hdpi/ic_app_logo.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_app_logo_adaptive_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/braver-tool/Android11FilePicker/59356f50d19de74db1fce02b16fdc35b335581c3/app/src/main/res/mipmap-hdpi/ic_app_logo_adaptive_back.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_app_logo_adaptive_fore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/braver-tool/Android11FilePicker/59356f50d19de74db1fce02b16fdc35b335581c3/app/src/main/res/mipmap-hdpi/ic_app_logo_adaptive_fore.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_app_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/braver-tool/Android11FilePicker/59356f50d19de74db1fce02b16fdc35b335581c3/app/src/main/res/mipmap-mdpi/ic_app_logo.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_app_logo_adaptive_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/braver-tool/Android11FilePicker/59356f50d19de74db1fce02b16fdc35b335581c3/app/src/main/res/mipmap-mdpi/ic_app_logo_adaptive_back.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_app_logo_adaptive_fore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/braver-tool/Android11FilePicker/59356f50d19de74db1fce02b16fdc35b335581c3/app/src/main/res/mipmap-mdpi/ic_app_logo_adaptive_fore.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_app_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/braver-tool/Android11FilePicker/59356f50d19de74db1fce02b16fdc35b335581c3/app/src/main/res/mipmap-xhdpi/ic_app_logo.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_app_logo_adaptive_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/braver-tool/Android11FilePicker/59356f50d19de74db1fce02b16fdc35b335581c3/app/src/main/res/mipmap-xhdpi/ic_app_logo_adaptive_back.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_app_logo_adaptive_fore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/braver-tool/Android11FilePicker/59356f50d19de74db1fce02b16fdc35b335581c3/app/src/main/res/mipmap-xhdpi/ic_app_logo_adaptive_fore.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_app_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/braver-tool/Android11FilePicker/59356f50d19de74db1fce02b16fdc35b335581c3/app/src/main/res/mipmap-xxhdpi/ic_app_logo.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_app_logo_adaptive_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/braver-tool/Android11FilePicker/59356f50d19de74db1fce02b16fdc35b335581c3/app/src/main/res/mipmap-xxhdpi/ic_app_logo_adaptive_back.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_app_logo_adaptive_fore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/braver-tool/Android11FilePicker/59356f50d19de74db1fce02b16fdc35b335581c3/app/src/main/res/mipmap-xxhdpi/ic_app_logo_adaptive_fore.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_app_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/braver-tool/Android11FilePicker/59356f50d19de74db1fce02b16fdc35b335581c3/app/src/main/res/mipmap-xxxhdpi/ic_app_logo.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_app_logo_adaptive_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/braver-tool/Android11FilePicker/59356f50d19de74db1fce02b16fdc35b335581c3/app/src/main/res/mipmap-xxxhdpi/ic_app_logo_adaptive_back.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_app_logo_adaptive_fore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/braver-tool/Android11FilePicker/59356f50d19de74db1fce02b16fdc35b335581c3/app/src/main/res/mipmap-xxxhdpi/ic_app_logo_adaptive_fore.png -------------------------------------------------------------------------------- /app/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 12 | 13 | 27 | 28 | 31 | 32 | 38 | 39 | 43 | 44 | 50 | 51 | 56 | 57 | 64 | 65 | 69 | 70 | 75 | 76 | 81 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | #FFBB86FC 12 | #FF6200EE 13 | #FF3700B3 14 | #FF03DAC5 15 | #FF018786 16 | #FF000000 17 | #FFFFFFFF 18 | #1693CB 19 | #148DC2 20 | #5B9EBB 21 | #015378 22 | #373737 23 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | FilePicker 11 | images 12 | File Picker 13 | Camera 14 | Gallery 15 | Documents 16 | Click to Preview 17 | 18 | 19 | Settings 20 | To capture photos and videos, allow FilePicker access to your camera.\nTap Settings > Permissions, and turn Camera on. 21 | To send media, allow FilePicker access to your device photos, media, and files.\nTap Settings > Permissions, and "Files and media" on. 22 | To call, allow FilePicker access to your microphone.\nTap Settings > Permissions, and turn Microphone on. 23 | Not Now 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/xml/provider_paths.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 14 | 17 | 20 | 23 | 26 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/test/java/com/braver/tool/filepicker/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Created by https://github.com/braver-tool on 12/10/21, 08:30 PM 4 | * * Copyright (c) 2021 . All rights reserved. 5 | * * Last modified 23/03/22, 09:45 AM 6 | * 7 | */ 8 | 9 | package com.braver.tool.filepicker; 10 | 11 | import org.junit.Test; 12 | 13 | import static org.junit.Assert.*; 14 | 15 | /** 16 | * Example local unit test, which will execute on the development machine (host). 17 | * 18 | * @see Testing documentation 19 | */ 20 | public class ExampleUnitTest { 21 | @Test 22 | public void addition_isCorrect() { 23 | assertEquals(4, 2 + 2); 24 | } 25 | } -------------------------------------------------------------------------------- /app_demo_pic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/braver-tool/Android11FilePicker/59356f50d19de74db1fce02b16fdc35b335581c3/app_demo_pic.jpg -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Created by https://github.com/braver-tool on 12/10/21, 08:30 PM 4 | * * Copyright (c) 2021 . All rights reserved. 5 | * * Last modified 23/03/22, 09:45 AM 6 | * 7 | */ 8 | 9 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 10 | buildscript { 11 | ext.kotlin_version = '1.6.10' 12 | repositories { 13 | google() 14 | jcenter() 15 | maven { url "https://jitpack.io" } 16 | } 17 | dependencies { 18 | classpath 'com.android.tools.build:gradle:4.0.2' 19 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 20 | // NOTE: Do not place your application dependencies here; they belong 21 | // in the individual module build.gradle files 22 | } 23 | } 24 | 25 | allprojects { 26 | repositories { 27 | google() 28 | jcenter() 29 | mavenCentral() 30 | maven { url "https://jitpack.io" } 31 | } 32 | } 33 | 34 | 35 | task clean(type: Delete) { 36 | delete rootProject.buildDir 37 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # 2 | # /* 3 | # * Created by https://github.com/braver-tool on 12/10/21, 08:30 PM 4 | # * Copyright (c) 2021 . All rights reserved. 5 | # * Last modified 23/03/22, 09:45 AM 6 | # */ 7 | # 8 | 9 | # Project-wide Gradle settings. 10 | # IDE (e.g. Android Studio) users: 11 | # Gradle settings configured through the IDE *will override* 12 | # any settings specified in this file. 13 | # For more details on how to configure your build environment visit 14 | # http://www.gradle.org/docs/current/userguide/build_environment.html 15 | # Specifies the JVM arguments used for the daemon process. 16 | # The setting is particularly useful for tweaking memory settings. 17 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 18 | # When configured, Gradle will run in incubating parallel mode. 19 | # This option should only be used with decoupled projects. More details, visit 20 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 21 | # org.gradle.parallel=true 22 | # AndroidX package structure to make it clearer which packages are bundled with the 23 | # Android operating system, and which are packaged with your app"s APK 24 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 25 | android.useAndroidX=true 26 | # Automatically convert third-party libraries to use AndroidX 27 | android.enableJetifier=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/braver-tool/Android11FilePicker/59356f50d19de74db1fce02b16fdc35b335581c3/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | # 2 | # /* 3 | # * Created by https://github.com/braver-tool on 12/10/21, 08:30 PM 4 | # * Copyright (c) 2021 . All rights reserved. 5 | # * Last modified 23/03/22, 09:45 AM 6 | # */ 7 | # 8 | 9 | #Tue Oct 19 10:59:18 IST 2021 10 | distributionBase=GRADLE_USER_HOME 11 | distributionPath=wrapper/dists 12 | zipStoreBase=GRADLE_USER_HOME 13 | zipStorePath=wrapper/dists 14 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip 15 | 16 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /picker/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /picker/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Created by https://github.com/braver-tool on 12/10/21, 08:30 PM 4 | * * Copyright (c) 2021 . All rights reserved. 5 | * * Last modified 23/03/22, 09:45 AM 6 | * 7 | */ 8 | 9 | apply plugin: 'com.android.library' 10 | apply plugin: 'kotlin-android' 11 | 12 | android { 13 | compileSdkVersion 31 14 | 15 | defaultConfig { 16 | minSdkVersion 23 17 | targetSdkVersion 31 18 | versionCode 1 19 | versionName "1.0" 20 | 21 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 22 | consumerProguardFiles "consumer-rules.pro" 23 | } 24 | 25 | buildTypes { 26 | release { 27 | minifyEnabled false 28 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 29 | } 30 | } 31 | compileOptions { 32 | sourceCompatibility JavaVersion.VERSION_1_8 33 | targetCompatibility JavaVersion.VERSION_1_8 34 | } 35 | } 36 | 37 | dependencies { 38 | implementation 'androidx.appcompat:appcompat:1.4.1' 39 | implementation 'com.google.android.material:material:1.5.0' 40 | implementation 'androidx.exifinterface:exifinterface:1.3.3' 41 | testImplementation 'junit:junit:4.13.2' 42 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 43 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 44 | implementation 'com.intuit.sdp:sdp-android:1.0.6' 45 | implementation 'androidx.core:core-ktx:1.7.0' 46 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 47 | } 48 | repositories { 49 | mavenCentral() 50 | } -------------------------------------------------------------------------------- /picker/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/braver-tool/Android11FilePicker/59356f50d19de74db1fce02b16fdc35b335581c3/picker/consumer-rules.pro -------------------------------------------------------------------------------- /picker/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /picker/src/androidTest/java/com/braver/tool/picker/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Created by https://github.com/braver-tool on 12/10/21, 08:30 PM 4 | * * Copyright (c) 2021 . All rights reserved. 5 | * * Last modified 23/03/22, 09:45 AM 6 | * 7 | */ 8 | 9 | package com.braver.tool.picker; 10 | 11 | import android.content.Context; 12 | 13 | import androidx.test.platform.app.InstrumentationRegistry; 14 | import androidx.test.ext.junit.runners.AndroidJUnit4; 15 | 16 | import org.junit.Test; 17 | import org.junit.runner.RunWith; 18 | 19 | import static org.junit.Assert.*; 20 | 21 | /** 22 | * Instrumented test, which will execute on an Android device. 23 | * 24 | * @see Testing documentation 25 | */ 26 | @RunWith(AndroidJUnit4.class) 27 | public class ExampleInstrumentedTest { 28 | @Test 29 | public void useAppContext() { 30 | // Context of the app under test. 31 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 32 | assertEquals("com.braver.tool.picker.test", appContext.getPackageName()); 33 | } 34 | } -------------------------------------------------------------------------------- /picker/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /picker/src/main/java/com/braver/tool/picker/BraveFileGetter.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Created by https://github.com/braver-tool on 12/10/21, 08:30 PM 4 | * * Copyright (c) 2021 . All rights reserved. 5 | * * Last modified 23/03/22, 09:45 AM 6 | * 7 | */ 8 | package com.braver.tool.picker 9 | 10 | import android.content.Context 11 | import android.net.Uri 12 | import java.io.File 13 | 14 | object BraveFileGetter { 15 | private fun getFileType(braveFileType: BraveFileType?): String { 16 | return when (braveFileType) { 17 | BraveFileType.IMAGE -> "image" 18 | BraveFileType.VIDEO -> "video" 19 | BraveFileType.DOCS -> "docs" 20 | else -> "" 21 | } 22 | } 23 | 24 | @JvmStatic 25 | fun getSourceFileFromUri( 26 | context: Context?, 27 | isCompressImage: Boolean, 28 | isTrimVide: Boolean, 29 | braveFileType: BraveFileType?, 30 | destinationFilePath: String?, 31 | contentUri: Uri? 32 | ): File? { 33 | var file: File? = null 34 | val fileType = getFileType(braveFileType) 35 | if (fileType == "image") { 36 | //handle image 37 | val filepath = BraveFileUtils.getFilePathFromContentUri(contentUri, context) 38 | val imageBitMap = BraveFileUtils.checkIsBitMapRotated(context, true, File(filepath)) 39 | if (imageBitMap != null && destinationFilePath != null) { 40 | file = File(destinationFilePath) 41 | BraveFileUtils.saveBitmapToExternalStorage(context, imageBitMap, file) 42 | } 43 | } 44 | return file 45 | } 46 | } -------------------------------------------------------------------------------- /picker/src/main/java/com/braver/tool/picker/BraveFilePath.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Created by https://github.com/braver-tool on 12/10/21, 08:30 PM 4 | * * Copyright (c) 2021 . All rights reserved. 5 | * * Last modified 23/03/22, 09:45 AM 6 | * 7 | */ 8 | package com.braver.tool.picker 9 | 10 | import android.annotation.SuppressLint 11 | import android.content.ContentUris 12 | import android.content.Context 13 | import android.database.Cursor 14 | import android.net.Uri 15 | import android.os.Environment 16 | import android.provider.DocumentsContract 17 | import android.provider.MediaStore 18 | import android.provider.OpenableColumns 19 | import android.util.Log 20 | import androidx.core.content.ContextCompat 21 | import java.io.File 22 | import java.io.FileOutputStream 23 | import java.util.* 24 | 25 | class BraveFilePath { 26 | companion object { 27 | 28 | /** 29 | * Get a file path from a Uri. This will get the the path for Storage Access 30 | * Framework Documents, as well as the _data field for the MediaStore and 31 | * other file-based ContentProviders. 32 | * 33 | * @param context The context. 34 | * @param uri The Uri to query. 35 | * @author paulburke 36 | */ 37 | @SuppressLint("NewApi") 38 | fun getRealPathFromURI(context: Context, uri: Uri): String? { 39 | when { 40 | DocumentsContract.isDocumentUri(context, uri) -> { 41 | when { 42 | isExternalStorageDocument(uri) -> { 43 | val docId = DocumentsContract.getDocumentId(uri) 44 | val split = docId.split(":".toRegex()).toTypedArray() 45 | val type = split[0] 46 | return when { 47 | "primary".equals(type, ignoreCase = true) -> { 48 | Environment.getExternalStorageDirectory() 49 | .toString() + "/" + split[1] 50 | } 51 | isGoogleDriveUri(uri) -> { 52 | getDriveFilePath(uri, context) 53 | } 54 | else -> { 55 | BraveFileUtils.getFilePathFromContentUri(uri, context) 56 | } 57 | } 58 | } 59 | isGoogleDriveUri(uri) -> { 60 | return getDriveFilePath(uri, context) 61 | } 62 | isDownloadsDocument(uri) -> { 63 | val fileName = getFilePath(context, uri) 64 | if (fileName != null) { 65 | return Environment.getExternalStorageDirectory() 66 | .toString() + "/Download/" + fileName 67 | } 68 | var id = DocumentsContract.getDocumentId(uri) 69 | if (id.startsWith("raw:")) { 70 | id = id.replaceFirst("raw:".toRegex(), "") 71 | val file = File(id) 72 | if (file.exists()) return id 73 | } 74 | val contentUri = ContentUris.withAppendedId( 75 | Uri.parse("content://downloads/public_downloads"), 76 | id.toLong() 77 | ) 78 | return getDataColumn(context, contentUri, null, null) 79 | } 80 | isMediaDocument(uri) -> { 81 | // MediaProvider 82 | try { 83 | val docId = DocumentsContract.getDocumentId(uri) 84 | val split = docId.split(":".toRegex()).toTypedArray() 85 | val type = split[0] 86 | var contentUri: Uri? = null 87 | if ("image" == type) { 88 | contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI 89 | } 90 | val selection = "_id=?" 91 | val selectionArgs = arrayOf(split[1]) 92 | return getDataColumn(context, contentUri, selection, selectionArgs) 93 | } catch (e: Exception) { 94 | Log.d("##RealDocPathUtil", "--------->" + e.message) 95 | val fileName = getFilePath(context, uri) 96 | if (fileName != null) { 97 | var file = Environment.getExternalStorageDirectory() 98 | .toString() + "/Download/" + fileName 99 | val file1 = File(file) 100 | return if (file1.exists()) { 101 | file 102 | } else { 103 | file = copyFileToInternalStorage(context, uri, "userfiles") 104 | file 105 | } 106 | } 107 | } 108 | } 109 | else -> { 110 | return BraveFileUtils.getFilePathFromContentUri(uri, context) 111 | } 112 | } 113 | } 114 | "content".equals(uri.scheme, ignoreCase = true) -> { 115 | return if (isGooglePhotosUri(uri)) uri.lastPathSegment else getDataColumn( 116 | context, 117 | uri, 118 | null, 119 | null 120 | ) 121 | } 122 | "file".equals(uri.scheme, ignoreCase = true) -> { 123 | return uri.path 124 | } 125 | } 126 | return null 127 | } 128 | 129 | fun getFilePath(context: Context, uri: Uri?): String? { 130 | var cursor: Cursor? = null 131 | val projection = arrayOf( 132 | MediaStore.MediaColumns.DISPLAY_NAME 133 | ) 134 | try { 135 | cursor = context.contentResolver.query(uri!!, projection, null, null, null) 136 | if (cursor != null && cursor.moveToFirst()) { 137 | val index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME) 138 | return cursor.getString(index) 139 | } 140 | } finally { 141 | cursor?.close() 142 | } 143 | return null 144 | } 145 | 146 | /** 147 | * Get the value of the data column for this Uri. This is useful for 148 | * MediaStore Uris, and other file-based ContentProviders. 149 | * 150 | * @param context The context. 151 | * @param uri The Uri to query. 152 | * @param selection (Optional) Filter used in the query. 153 | * @param selectionArgs (Optional) Selection arguments used in the query. 154 | * @return The value of the _data column, which is typically a file path. 155 | */ 156 | fun getDataColumn( 157 | context: Context, 158 | uri: Uri?, 159 | selection: String?, 160 | selectionArgs: Array? 161 | ): String? { 162 | var cursor: Cursor? = null 163 | val column = "_data" 164 | val projection = arrayOf(column) 165 | try { 166 | cursor = 167 | context.contentResolver.query(uri!!, projection, selection, selectionArgs, null) 168 | if (cursor != null && cursor.moveToFirst()) { 169 | val index = cursor.getColumnIndexOrThrow(column) 170 | return cursor.getString(index) 171 | } 172 | } finally { 173 | cursor?.close() 174 | } 175 | return null 176 | } 177 | 178 | /** 179 | * @param uri The Uri to check. 180 | * @return Whether the Uri authority is ExternalStorageProvider. 181 | */ 182 | fun isExternalStorageDocument(uri: Uri): Boolean { 183 | return "com.android.externalstorage.documents" == uri.authority 184 | } 185 | 186 | /** 187 | * @param uri The Uri to check. 188 | * @return Whether the Uri authority is DownloadsProvider. 189 | */ 190 | fun isDownloadsDocument(uri: Uri): Boolean { 191 | return "com.android.providers.downloads.documents" == uri.authority 192 | } 193 | 194 | /** 195 | * @param uri The Uri to check. 196 | * @return Whether the Uri authority is MediaProvider. 197 | */ 198 | fun isMediaDocument(uri: Uri): Boolean { 199 | return "com.android.providers.media.documents" == uri.authority 200 | } 201 | 202 | /** 203 | * @param uri The Uri to check. 204 | * @return Whether the Uri authority is Google Photos. 205 | */ 206 | fun isGooglePhotosUri(uri: Uri): Boolean { 207 | return "com.google.android.apps.photos.content" == uri.authority 208 | } 209 | 210 | fun isGoogleDriveUri(uri: Uri): Boolean { 211 | return "com.google.android.apps.docs.storage" == uri.authority || "com.google.android.apps.docs.storage.legacy" == uri.authority 212 | } 213 | 214 | private fun getDriveFilePath(uri: Uri, context: Context): String { 215 | //String[] split2; 216 | val returnCursor = context.contentResolver.query(uri, null, null, null, null) 217 | /* 218 | * Get the column indexes of the data in the Cursor, 219 | * * move to the first row in the Cursor, get the data, 220 | * * and display it. 221 | * */ 222 | val nameIndex = returnCursor!!.getColumnIndex(OpenableColumns.DISPLAY_NAME) 223 | val sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE) 224 | returnCursor.moveToFirst() 225 | val name = returnCursor.getString(nameIndex) 226 | val size = java.lang.Long.toString(returnCursor.getLong(sizeIndex)) 227 | //split2 = name.split("\\."); 228 | //File file = new File(context.getCacheDir(), name); 229 | val designPath = getRandomDocFileName(context, name) 230 | try { 231 | val inputStream = context.contentResolver.openInputStream(uri) 232 | val outputStream = FileOutputStream(designPath) 233 | var read = 0 234 | val maxBufferSize = 1024 * 1024 235 | val bytesAvailable = inputStream!!.available() 236 | 237 | //int bufferSize = 1024; 238 | val bufferSize = Math.min(bytesAvailable, maxBufferSize) 239 | val buffers = ByteArray(bufferSize) 240 | while (inputStream.read(buffers).also { read = it } != -1) { 241 | outputStream.write(buffers, 0, read) 242 | } 243 | inputStream.close() 244 | outputStream.close() 245 | } catch (e: Exception) { 246 | BraveFileUtils.printLogConsole("##isGoogleDriveUri", "Exception------>" + e.message) 247 | } 248 | return designPath!!.path 249 | } 250 | 251 | /** 252 | * Declaration getExternalSdCardPath(Context context) 253 | * Description This method used save local path file and return file path name 254 | * Declared In RealDocPathUtil.java 255 | * 256 | * @param context - Contains Context 257 | */ 258 | @JvmStatic 259 | fun getExternalSdCardPath(context: Context?): String { 260 | val dirs = ContextCompat.getExternalFilesDirs( 261 | context!!, null 262 | ) 263 | val path = dirs[1] 264 | val pathSD = dirs[1].toString() 265 | val firstOpen = pathSD.indexOf("/") 266 | val secondOpen = pathSD.indexOf("/", firstOpen + 1) 267 | val thirdOpen = pathSD.indexOf("/", secondOpen + 1) 268 | return pathSD.substring(firstOpen, thirdOpen + 1) 269 | //path = new File(filename); 270 | } 271 | 272 | /** 273 | * @return - file path 274 | * method used to generate full file path using selected file name 275 | */ 276 | fun getRandomDocFileName(context: Context, fileName: String): File? { 277 | val file: File? = null 278 | val mediaStorageDir = context.filesDir 279 | if (!mediaStorageDir.exists()) { 280 | if (!mediaStorageDir.mkdirs()) { 281 | return file 282 | } 283 | } 284 | val random = Random().nextInt(6997) 285 | val mImageName = "Braver_DOC_$random$fileName" 286 | return File(mediaStorageDir.path + "/" + mImageName) 287 | } 288 | 289 | /*** 290 | * Used for Android Q+ 291 | * @param uri - ~ 292 | * @param newDirName if you want to create a directory, you can set this variable 293 | * @return - ~ 294 | */ 295 | private fun copyFileToInternalStorage( 296 | context: Context, 297 | uri: Uri, 298 | newDirName: String 299 | ): String { 300 | val returnCursor = context.contentResolver.query( 301 | uri, arrayOf( 302 | OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE 303 | ), null, null, null 304 | ) 305 | /* 306 | * Get the column indexes of the data in the Cursor, 307 | * * move to the first row in the Cursor, get the data, 308 | * * and display it. 309 | * */ 310 | val nameIndex = returnCursor!!.getColumnIndex(OpenableColumns.DISPLAY_NAME) 311 | val sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE) 312 | returnCursor.moveToFirst() 313 | val name = returnCursor.getString(nameIndex) 314 | val size = java.lang.Long.toString(returnCursor.getLong(sizeIndex)) 315 | val output: File = if (newDirName != "") { 316 | val dir = File(context.filesDir.toString() + "/" + newDirName) 317 | if (!dir.exists()) { 318 | dir.mkdir() 319 | } 320 | File(context.filesDir.toString() + "/" + newDirName + "/" + name) 321 | } else { 322 | File(context.filesDir.toString() + "/" + name) 323 | } 324 | try { 325 | val inputStream = context.contentResolver.openInputStream(uri) 326 | val outputStream = FileOutputStream(output) 327 | var read = 0 328 | val bufferSize = 1024 329 | val buffers = ByteArray(bufferSize) 330 | while (inputStream!!.read(buffers).also { read = it } != -1) { 331 | outputStream.write(buffers, 0, read) 332 | } 333 | inputStream.close() 334 | outputStream.close() 335 | } catch (e: Exception) { 336 | BraveFileUtils.printLogConsole( 337 | "##copyFileToInternalStorage", 338 | "Exception------>" + e.message 339 | ) 340 | } 341 | return output.path 342 | } 343 | } 344 | } -------------------------------------------------------------------------------- /picker/src/main/java/com/braver/tool/picker/BraveFilePicker.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Created by https://github.com/braver-tool on 12/10/21, 08:30 PM 4 | * * Copyright (c) 2021 . All rights reserved. 5 | * * Last modified 23/03/22, 09:45 AM 6 | * 7 | */ 8 | package com.braver.tool.picker 9 | 10 | import android.content.Context 11 | import android.net.Uri 12 | import com.braver.tool.picker.BraveFileGetter.getSourceFileFromUri 13 | import java.io.File 14 | import java.io.IOException 15 | import kotlin.Throws 16 | 17 | class BraveFilePicker { 18 | private var context: Context? = null 19 | private var isCompressImage = false 20 | private var isTrimVide = false 21 | 22 | //private String fileType; 23 | private var braveFileType: BraveFileType? = null 24 | private var destinationFilePath: String? = null 25 | private var contentUri: Uri? = null 26 | fun setActivity(ctx: Context?): BraveFilePicker { 27 | context = ctx 28 | return this 29 | } 30 | 31 | fun setIsCompressImage(compressImage: Boolean): BraveFilePicker { 32 | isCompressImage = compressImage 33 | return this 34 | } 35 | 36 | fun setIsTrimVide(trimVide: Boolean): BraveFilePicker { 37 | isTrimVide = trimVide 38 | return this 39 | } 40 | 41 | fun setFileType(fileType: BraveFileType?): BraveFilePicker { 42 | braveFileType = fileType 43 | return this 44 | } 45 | 46 | fun setDestinationFilePath(destinationFilePath: String?): BraveFilePicker { 47 | this.destinationFilePath = destinationFilePath 48 | return this 49 | } 50 | 51 | fun setContentUri(uri: Uri?): BraveFilePicker { 52 | contentUri = uri 53 | return this 54 | } 55 | 56 | @get:Throws(IOException::class) 57 | val sourceFile: File? 58 | get() = getSourceFileFromUri( 59 | context, 60 | isCompressImage, 61 | isTrimVide, 62 | braveFileType, 63 | destinationFilePath, 64 | contentUri 65 | ) 66 | } -------------------------------------------------------------------------------- /picker/src/main/java/com/braver/tool/picker/BraveFileType.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Created by https://github.com/braver-tool on 12/10/21, 08:30 PM 4 | * * Copyright (c) 2021 . All rights reserved. 5 | * * Last modified 23/03/22, 09:45 AM 6 | * 7 | */ 8 | package com.braver.tool.picker 9 | 10 | enum class BraveFileType { 11 | DEFAULT, IMAGE, VIDEO, DOCS 12 | } -------------------------------------------------------------------------------- /picker/src/main/java/com/braver/tool/picker/BraveFileUtils.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Created by https://github.com/braver-tool on 12/10/21, 08:30 PM 4 | * * Copyright (c) 2021 . All rights reserved. 5 | * * Last modified 23/03/22, 09:45 AM 6 | * 7 | */ 8 | package com.braver.tool.picker 9 | 10 | 11 | import android.content.ContentUris 12 | import android.content.Context 13 | import android.database.Cursor 14 | import android.graphics.Bitmap 15 | import android.graphics.BitmapFactory 16 | import android.graphics.Matrix 17 | import android.net.Uri 18 | import android.provider.DocumentsContract 19 | import android.provider.MediaStore 20 | import android.util.Log 21 | import androidx.exifinterface.media.ExifInterface 22 | import com.braver.tool.picker.BraveFilePath.Companion.getExternalSdCardPath 23 | import com.braver.tool.picker.image.ImageUtil.compressImage 24 | import java.io.File 25 | import java.io.FileInputStream 26 | import java.io.FileOutputStream 27 | import java.io.IOException 28 | import java.text.DecimalFormat 29 | import java.util.* 30 | import kotlin.math.log10 31 | import kotlin.math.pow 32 | 33 | class BraveFileUtils { 34 | companion object { 35 | const val IS_IMAGE_FILE = "is_image_file" 36 | const val IMAGE_PATH = "image_path" 37 | const val VIDEO_PATH = "video_path" 38 | private val TAG = BraveFileUtils::class.java.simpleName 39 | var IS_LOG_ENABLED = true 40 | const val PREVIEW_IMAGE_FILE_PATH = "preview_image_file_path" 41 | const val PREVIEW_VIDEO_FILE_PATH = "preview_video_file_path" 42 | 43 | /** 44 | * @param tag - Contains class name 45 | * @param msg - Log message as String 46 | * Method used to print log in console for development 47 | */ 48 | fun printLogConsole(tag: String, msg: String) { 49 | if (IS_LOG_ENABLED) { 50 | Log.d("##@@##$tag", "--------->$msg") 51 | } 52 | } 53 | 54 | @JvmStatic 55 | fun getRandomImageFileName(context: Context): File? { 56 | val file: File? = null 57 | val mediaStorageDir = context.filesDir 58 | if (!mediaStorageDir.exists()) { 59 | if (!mediaStorageDir.mkdirs()) { 60 | return file 61 | } 62 | } 63 | val random = Random().nextInt(8997) 64 | val mImageName = "Braver_IMG_$random.jpg" 65 | return File(mediaStorageDir.path + "/" + mImageName) 66 | } 67 | 68 | @JvmStatic 69 | fun getRandomRecordedVideoFileName(context: Context): File? { 70 | val file: File? = null 71 | val mediaStorageDir = context.filesDir 72 | if (!mediaStorageDir.exists()) { 73 | if (!mediaStorageDir.mkdirs()) { 74 | return file 75 | } 76 | } 77 | return File(mediaStorageDir.path) 78 | } 79 | 80 | @JvmStatic 81 | fun saveBitmapToExternalStorage(context: Context?, bitmap: Bitmap, myPath: File?) { 82 | var fileOutputStream: FileOutputStream? = null 83 | try { 84 | fileOutputStream = FileOutputStream(myPath) 85 | //String fileName = Utils.getMessageFileName(myPath.getAbsolutePath()); 86 | //fileOutputStream = context.openFileOutput(fileName, Context.MODE_PRIVATE); 87 | bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fileOutputStream) 88 | } catch (e: Exception) { 89 | printLogConsole( 90 | "$TAG##saveBitmapToExternalStorage", 91 | "Exception------>" + e.message 92 | ) 93 | } finally { 94 | try { 95 | fileOutputStream?.close() 96 | } catch (e: IOException) { 97 | printLogConsole( 98 | "$TAG##saveBitmapToExternalStorage", 99 | "Exception------>" + e.message 100 | ) 101 | } 102 | } 103 | } 104 | 105 | 106 | /** 107 | * Method used to get the real path from uri file 108 | * 109 | * @param context context 110 | * @return filepath 111 | */ 112 | fun getFilePathFromContentUri(uri: Uri?, context: Context?): String { 113 | val filePath: String 114 | var contentUri: Uri? = null 115 | var id: String 116 | try { 117 | id = DocumentsContract.getDocumentId(uri) 118 | } catch (e: IllegalArgumentException) { 119 | id = "" 120 | printLogConsole(TAG, "Uri Null") 121 | } 122 | if (id.isNotEmpty()) { 123 | val numeric = id.matches("-?\\d+(\\.\\d+)?".toRegex()) 124 | if (numeric) { 125 | contentUri = ContentUris.withAppendedId( 126 | Uri.parse("content://downloads/public_downloads"), 127 | id.toLong() 128 | ) 129 | } 130 | } else { 131 | contentUri = uri 132 | } 133 | val projection = arrayOf(MediaStore.Files.FileColumns.DATA) 134 | var cursor: Cursor? = null 135 | if (contentUri != null) { 136 | cursor = context?.contentResolver?.query(contentUri, projection, null, null, null) 137 | } 138 | if (cursor != null) { 139 | cursor.moveToFirst() 140 | val columnIndex = cursor.getColumnIndexOrThrow(projection[0]) 141 | filePath = cursor.getString(columnIndex) 142 | cursor.close() 143 | } else { 144 | filePath = uri?.path.toString() 145 | } 146 | return filePath 147 | } 148 | 149 | /** 150 | * Declaration checkIsBitMapRotated(File imageFile) 151 | * Some devices returns wrong(auto - rotated) images 152 | * Description This method used to get actual captured image from device . 153 | * Declared In Utils.java 154 | * 155 | * @param imageFile - Contains image file for the particular image path 156 | */ 157 | @JvmStatic 158 | fun checkIsBitMapRotated(context: Context?, isCompress: Boolean, imageFile: File): Bitmap? { 159 | return try { 160 | var isComp = true 161 | val compressedImageFile: File? 162 | val bytes = imageFile.length() 163 | if (bytes > 5242880 || isCompress) { // Size must below 5MB 164 | compressedImageFile = compressImage( 165 | imageFile, context?.let { getRandomImageFileName(it) }!! 166 | .absolutePath 167 | ) 168 | } else { 169 | isComp = false 170 | compressedImageFile = imageFile 171 | } 172 | printLogConsole( 173 | TAG, 174 | "Image File Size----->" + getFileSizeFromFilePath( 175 | compressedImageFile.length() 176 | ) 177 | ) 178 | var photoBitmap = BitmapFactory.decodeFile(compressedImageFile.path) 179 | val imageRotation = getImageRotation(compressedImageFile) 180 | if (imageRotation != 0) { 181 | photoBitmap = getBitmapRotatedByDegree(photoBitmap, imageRotation) 182 | } 183 | if (!isComp) { 184 | photoBitmap = Bitmap.createScaledBitmap( 185 | photoBitmap, 186 | photoBitmap.width, 187 | photoBitmap.height, 188 | true 189 | ) 190 | } 191 | photoBitmap 192 | } catch (e: IOException) { 193 | printLogConsole(TAG, "checkIsBitMapRotated()------>Exception----->" + e.message) 194 | null 195 | } 196 | } 197 | 198 | /** 199 | * Declaration getImageRotation(final File imageFile 200 | * Description This method used to set ExifInterface 201 | * Declared In Utils.java 202 | * 203 | * @param imageFile - Contains image file for the particular image path 204 | */ 205 | private fun getImageRotation(imageFile: File): Int { 206 | var exif: ExifInterface? = null 207 | var exifRotation = 0 208 | try { 209 | exif = ExifInterface(imageFile.path) 210 | exifRotation = exif.getAttributeInt( 211 | ExifInterface.TAG_ORIENTATION, 212 | ExifInterface.ORIENTATION_NORMAL 213 | ) 214 | } catch (e: IOException) { 215 | e.printStackTrace() 216 | } 217 | return if (exif == null) 0 else exifToDegrees(exifRotation) 218 | } 219 | 220 | /** 221 | * Declaration exifToDegrees(int rotation) 222 | * Description This method used to set ExifInterface with rotation degree 223 | * Declared In Utils.java 224 | * 225 | * @param rotation - Contains rotation degree for the captured image 226 | */ 227 | private fun exifToDegrees(rotation: Int): Int { 228 | if (rotation == ExifInterface.ORIENTATION_ROTATE_90) return 90 else if (rotation == ExifInterface.ORIENTATION_ROTATE_180) return 180 else if (rotation == ExifInterface.ORIENTATION_ROTATE_270) return 270 229 | return 0 230 | } 231 | 232 | /** 233 | * Declaration Bitmap getBitmapRotatedByDegree(Bitmap bitmap, int rotationDegree) 234 | * Description This method used to set bitmap height and width 235 | * Declared In Utils.java 236 | * 237 | * @param bitmap - Contains captured image bitmap 238 | * @param rotationDegree - Contains captured image rotation degree 239 | */ 240 | private fun getBitmapRotatedByDegree(bitmap: Bitmap, rotationDegree: Int): Bitmap { 241 | val matrix = Matrix() 242 | matrix.preRotate(rotationDegree.toFloat()) 243 | return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true) 244 | } 245 | 246 | /** 247 | * @param size - long data 248 | * @return - String data file size 249 | * Method used to get ile size 250 | */ 251 | private fun getFileSizeFromFilePath(size: Long): String { 252 | return if (size <= 0L) { 253 | "0" 254 | } else { 255 | val units = arrayOf("B", "KB", "MB", "GB", "TB") 256 | var var5 = size.toDouble() 257 | val var10000 = log10(var5) 258 | var5 = 1024.0 259 | val digitGroups = (var10000 / log10(var5)).toInt() 260 | val var10 = StringBuilder() 261 | val var10001 = DecimalFormat("#,##0.#") 262 | val var10002 = size.toDouble() 263 | var5 = 1024.0 264 | var10.append(var10001.format(var10002 / var5.pow(digitGroups.toDouble()))) 265 | .append(" ").append(units[digitGroups]).toString() 266 | } 267 | } 268 | } 269 | } -------------------------------------------------------------------------------- /picker/src/main/java/com/braver/tool/picker/BraverDocPathUtils.kt: -------------------------------------------------------------------------------- 1 | package com.braver.tool.picker 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Context 5 | import android.database.Cursor 6 | import android.net.Uri 7 | import android.os.Build 8 | import android.provider.DocumentsContract 9 | import android.provider.MediaStore 10 | import android.provider.OpenableColumns 11 | import android.util.Log 12 | import java.io.File 13 | import java.io.FileOutputStream 14 | 15 | 16 | class BraverDocPathUtils { 17 | companion object { 18 | @SuppressLint("Range") 19 | fun getSourceDocPath( 20 | context: Context, 21 | uridata: Uri 22 | ): String? { 23 | var displayname = "" 24 | var size = 0 25 | val contentResolver = context.contentResolver 26 | if (contentResolver.equals(null)) { 27 | return null 28 | } 29 | contentResolver.query( 30 | uridata, null, null, null, null, null 31 | )?.use { 32 | if (it.moveToFirst()) { 33 | displayname = it.getString(it.getColumnIndex(OpenableColumns.DISPLAY_NAME)) 34 | size = it.getColumnIndex(OpenableColumns.SIZE) 35 | } 36 | } 37 | //val filepath = context.applicationInfo.dataDir + File.separator + userID + "_" + System.currentTimeMillis() + displayname 38 | val filepath = 39 | context.filesDir.absolutePath + File.separator + "_" + System.currentTimeMillis() + displayname 40 | val splitName = filepath.substring(filepath.lastIndexOf('/') + 1) 41 | val file = File(filepath) 42 | try { 43 | val inputStream = contentResolver.openInputStream(uridata) ?: return null 44 | val outputStream = FileOutputStream(file) 45 | val bufferbyte = ByteArray(1024) 46 | var len: Int 47 | while (inputStream.read(bufferbyte) 48 | .also { len = it } > 0 49 | ) outputStream.write(bufferbyte, 0, len) 50 | outputStream.close() 51 | inputStream.close() 52 | } catch (e: Exception) { 53 | return "" 54 | } 55 | Log.d("##file.name", "-------->" + file.name) 56 | Log.d("##file.uridata", "-------->$uridata") 57 | Log.d("##file.splitName", "-------->$splitName") 58 | Log.d("##file.size", "-------->$size") 59 | Log.d("##file.absolutePath", "-------->" + file.absolutePath) 60 | return file.path 61 | } 62 | 63 | fun getPath(context: Context, uri: Uri): String? { 64 | val isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N 65 | 66 | if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) { 67 | if (isMediaDocument(uri)) { 68 | val docId = DocumentsContract.getDocumentId(uri) 69 | val split = docId.split(":".toRegex()).toTypedArray() 70 | val type = split[0] 71 | var contentUri: Uri? = null 72 | when (type) { 73 | "image" -> { 74 | contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI 75 | } 76 | "video" -> { 77 | contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI 78 | } 79 | "audio" -> { 80 | contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI 81 | } 82 | 83 | } 84 | val selection = "_id=?" 85 | val selectionArgs = arrayOf( 86 | split[1] 87 | ) 88 | return getDataColumn(context, contentUri, selection, selectionArgs) 89 | } 90 | } else if ("content".equals(uri.scheme, ignoreCase = true)) { 91 | return getDataColumn(context, uri, null, null) 92 | } else if ("file".equals(uri.scheme, ignoreCase = true)) { 93 | return uri.path 94 | } 95 | return null 96 | } 97 | 98 | private fun getDataColumn( 99 | context: Context, 100 | uri: Uri?, 101 | selection: String?, 102 | selectionArgs: Array? 103 | ): String? { 104 | var cursor: Cursor? = null 105 | val column = "_data" 106 | val projection = arrayOf( 107 | column 108 | ) 109 | try { 110 | 111 | cursor = context.contentResolver.query( 112 | uri!!, projection, selection, selectionArgs, 113 | null 114 | ) 115 | if (cursor != null && cursor.moveToFirst()) { 116 | val columnIndex = cursor.getColumnIndexOrThrow(column) 117 | return cursor.getString(columnIndex) 118 | } 119 | } finally { 120 | cursor?.close() 121 | } 122 | return null 123 | } 124 | 125 | private fun isMediaDocument(uri: Uri): Boolean { 126 | return "com.android.providers.media.documents" == uri.authority 127 | } 128 | 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /picker/src/main/java/com/braver/tool/picker/CameraActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Created by https://github.com/braver-tool on 12/10/21, 08:30 PM 4 | * * Copyright (c) 2021 . All rights reserved. 5 | * * Last modified 23/03/22, 09:45 AM 6 | * 7 | */ 8 | 9 | package com.braver.tool.picker; 10 | 11 | import android.annotation.SuppressLint; 12 | import android.app.ProgressDialog; 13 | import android.content.Context; 14 | import android.content.Intent; 15 | import android.content.res.Configuration; 16 | import android.graphics.Bitmap; 17 | import android.graphics.BitmapFactory; 18 | import android.graphics.Matrix; 19 | import android.hardware.Camera; 20 | import android.media.CamcorderProfile; 21 | import android.media.MediaRecorder; 22 | import android.os.AsyncTask; 23 | import android.os.Build; 24 | import android.os.Bundle; 25 | import android.os.Handler; 26 | import android.os.SystemClock; 27 | import android.util.DisplayMetrics; 28 | import android.util.Log; 29 | import android.view.MotionEvent; 30 | import android.view.Surface; 31 | import android.view.SurfaceHolder; 32 | import android.view.SurfaceView; 33 | import android.view.View; 34 | import android.view.WindowManager; 35 | import android.widget.ImageView; 36 | import android.widget.TextView; 37 | import android.widget.Toast; 38 | 39 | import androidx.appcompat.app.AppCompatActivity; 40 | 41 | import java.io.File; 42 | import java.io.IOException; 43 | import java.util.List; 44 | import java.util.Locale; 45 | import java.util.Random; 46 | 47 | import static android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE; 48 | import static android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO; 49 | 50 | @SuppressWarnings("ALL") 51 | public class CameraActivity extends AppCompatActivity implements SurfaceHolder.Callback, View.OnClickListener { 52 | private static final String PROCESSING_VIDEO = "Processing a video..."; 53 | private final String TAG = CameraActivity.class.getSimpleName(); 54 | private final Handler customHandler = new Handler(); 55 | //private OrientationEventListener myOrientationEventListener; 56 | //private final int iOrientation = 0; 57 | private int flag = 0; 58 | private int flashType = 1; 59 | private int mOrientation = 90; 60 | private SurfaceHolder surfaceHolder; 61 | private Camera camera; 62 | private Camera.PictureCallback jpegCallback; 63 | private MediaRecorder mediaRecorder; 64 | private SurfaceView imgSurface; 65 | private ImageView imgCapture; 66 | private ImageView imgFlashOnOff; 67 | private ImageView imgSwipeCamera; 68 | private TextView textCounter; 69 | private TextView hintTextView; 70 | private BackgroundTaskForSavingVideo saveVideoTask = null; 71 | private File recordedVideoFilePath = null; 72 | private File recordedImageFilePath = null; 73 | private long startTime = SystemClock.uptimeMillis(); 74 | private final Runnable updateTimerThread = new Runnable() { 75 | @SuppressLint("SetTextI18n") 76 | public void run() { 77 | long timeInMilliseconds = SystemClock.uptimeMillis() - startTime; 78 | long timeSwapBuff = 0L; 79 | long updatedTime = timeSwapBuff + timeInMilliseconds; 80 | int seconds = (int) (updatedTime / 1000); 81 | int minutes = seconds / 60; 82 | seconds = seconds % 60; 83 | textCounter.setText(String.format(Locale.getDefault(), "%02d", minutes) + ":" + String.format(Locale.getDefault(), "%02d", seconds)); 84 | customHandler.postDelayed(this, 0); 85 | 86 | } 87 | 88 | }; 89 | private BackgroundTaskForSavingImage savePicTask; 90 | private String mediaFileName = null; 91 | private Context context; 92 | private String bVideoName = ""; 93 | 94 | /** 95 | * this method used to initiate declare and initiate for Message attachment Camera and video activity 96 | * 97 | * @param savedInstanceState - this object is responsible to create the activity. 98 | */ 99 | @Override 100 | protected void onCreate(Bundle savedInstanceState) { 101 | super.onCreate(savedInstanceState); 102 | setContentView(R.layout.camera_activity); 103 | initializeViews(); 104 | } 105 | 106 | /** 107 | * Handling to mapping the xml view from{@link R.layout#activity_camera} 108 | */ 109 | private void initializeViews() { 110 | context = getApplicationContext(); 111 | getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); 112 | initControls(); 113 | initCameraActivity(); 114 | } 115 | 116 | /** 117 | * Method used to call Camera activity's lifecycle after check permission 118 | */ 119 | private void initCameraActivity() { 120 | File f = BraveFileUtils.getRandomRecordedVideoFileName(CameraActivity.this); 121 | //create a folder to get image 122 | recordedVideoFilePath = new File(f.getPath()); 123 | if (!recordedVideoFilePath.exists()) { 124 | recordedVideoFilePath.mkdirs(); 125 | } 126 | //capture image on callback 127 | captureImageCallback(); 128 | // 129 | if (camera != null) { 130 | Camera.CameraInfo info = new Camera.CameraInfo(); 131 | if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { 132 | imgFlashOnOff.setVisibility(View.GONE); 133 | } 134 | } 135 | } 136 | 137 | /** 138 | * Called when Activity content is not visible because user resume previous activity 139 | */ 140 | @Override 141 | protected void onPause() { 142 | super.onPause(); 143 | try { 144 | if (customHandler != null) customHandler.removeCallbacksAndMessages(null); 145 | releaseMediaRecorder(); 146 | } catch (Exception e) { 147 | e.printStackTrace(); 148 | } 149 | } 150 | 151 | /** 152 | * Called when back navigation 153 | */ 154 | @Override 155 | public void onBackPressed() { 156 | super.onBackPressed(); 157 | cancelSavePicTaskIfNeed(); 158 | } 159 | 160 | /** 161 | * This is called immediately after the surface is first created. 162 | * 163 | * @param arg0- The SurfaceHolder whose surface is being created 164 | */ 165 | @Override 166 | public void surfaceCreated(SurfaceHolder arg0) { 167 | try { 168 | if (flag == 0) { 169 | camera = Camera.open(0); 170 | } else { 171 | camera = Camera.open(1); 172 | } 173 | } catch (RuntimeException e) { 174 | e.printStackTrace(); 175 | return; 176 | } 177 | try { 178 | Camera.Parameters param; 179 | param = camera.getParameters(); 180 | final List modes = param.getSupportedFocusModes(); 181 | if (modes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) { 182 | param.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE); 183 | } else if (modes.contains(Camera.Parameters.FOCUS_MODE_MACRO)) { 184 | param.setFocusMode(Camera.Parameters.FOCUS_MODE_MACRO); 185 | } else if (modes.contains(Camera.Parameters.FOCUS_MODE_FIXED)) { 186 | param.setFocusMode(Camera.Parameters.FOCUS_MODE_FIXED); 187 | } else if (modes.contains(Camera.Parameters.FOCUS_MODE_INFINITY)) { 188 | param.setFocusMode(Camera.Parameters.FOCUS_MODE_INFINITY); 189 | } 190 | List sizes = param.getSupportedPreviewSizes(); 191 | //get diff to get perfect preview sizes 192 | DisplayMetrics displaymetrics = new DisplayMetrics(); 193 | getWindowManager().getDefaultDisplay().getMetrics(displaymetrics); 194 | int height = displaymetrics.heightPixels; 195 | int width = displaymetrics.widthPixels; 196 | long diff = (height * 1000 / width); 197 | long cdistance = Integer.MAX_VALUE; 198 | int idx = 0; 199 | for (int i = 0; i < sizes.size(); i++) { 200 | long value = (long) (sizes.get(i).width * 1000) / sizes.get(i).height; 201 | if (value > diff && value < cdistance) { 202 | idx = i; 203 | cdistance = value; 204 | } 205 | } 206 | Camera.Size cs = sizes.get(idx); 207 | param.setPreviewSize(cs.width, cs.height); 208 | param.setPictureSize(cs.width, cs.height); 209 | camera.setParameters(param); 210 | setCameraDisplayOrientation(0); 211 | camera.setPreviewDisplay(surfaceHolder); 212 | camera.startPreview(); 213 | if (flashType == 1) { 214 | param.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO); 215 | imgFlashOnOff.setImageResource(R.drawable.ic_automatic_flash); 216 | } else if (flashType == 2) { 217 | param.setFlashMode(Camera.Parameters.FLASH_MODE_ON); 218 | Camera.Parameters params; 219 | if (camera != null) { 220 | params = camera.getParameters(); 221 | 222 | if (params != null) { 223 | List supportedFlashModes = params.getSupportedFlashModes(); 224 | if (supportedFlashModes != null) { 225 | if (supportedFlashModes.contains(Camera.Parameters.FLASH_MODE_TORCH)) { 226 | param.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH); 227 | } else if (supportedFlashModes.contains(Camera.Parameters.FLASH_MODE_ON)) { 228 | param.setFlashMode(Camera.Parameters.FLASH_MODE_ON); 229 | } 230 | } 231 | } 232 | } 233 | imgFlashOnOff.setImageResource(R.drawable.ic_flash_on); 234 | } else if (flashType == 3) { 235 | param.setFlashMode(Camera.Parameters.FLASH_MODE_OFF); 236 | imgFlashOnOff.setImageResource(R.drawable.ic_flash_off); 237 | } 238 | } catch (Exception e) { 239 | e.printStackTrace(); 240 | } 241 | } 242 | 243 | /** 244 | * This is called immediately before a surface is being destroyed. 245 | * 246 | * @param arg0-The SurfaceHolder whose surface is being destroyed. 247 | */ 248 | @Override 249 | public void surfaceDestroyed(SurfaceHolder arg0) { 250 | try { 251 | camera.stopPreview(); 252 | camera.release(); 253 | camera = null; 254 | } catch (Exception e) { 255 | e.printStackTrace(); 256 | } 257 | } 258 | 259 | /** 260 | * This is called immediately after any structural changes (format or size) have been made to the surface. 261 | * You should at this point update the imagery in the surface. 262 | * This method is always called at least once, after surfaceCreated(SurfaceHolder). 263 | * 264 | * @param surfaceHolder -The SurfaceHolder whose surface has changed. 265 | * @param i -int: The new PixelFormat of the surface. 266 | * @param i1 -int: The new width of the surface. Value is 0 or greater 267 | * @param i2 -int:The new height of the surface. Value is 0 or greater 268 | */ 269 | @Override 270 | public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) { 271 | refreshCamera(); 272 | Camera.Parameters parameters = camera.getParameters(); 273 | parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE); 274 | camera.setParameters(parameters); 275 | camera.startPreview(); 276 | } 277 | 278 | /** 279 | * onClick is used to define the function to be invoked in the activity when the button is clicked. 280 | * 281 | * @param v - The view that was clicked. It is the view that was clicked. 282 | */ 283 | @Override 284 | public void onClick(View v) { 285 | int id = v.getId(); 286 | if (id == R.id.imgFlashOnOff) { 287 | flashToggle(); 288 | } else if (id == R.id.imgChangeCamera) { 289 | switchCameraType(); 290 | } 291 | } 292 | 293 | /** 294 | * This Method is used to initialize all views 295 | * Method call from {@link #onCreate(Bundle)} 296 | */ 297 | private void initControls() { 298 | mediaRecorder = new MediaRecorder(); 299 | imgSurface = findViewById(R.id.imgSurface); 300 | textCounter = findViewById(R.id.textCounter); 301 | imgCapture = findViewById(R.id.imgCapture); 302 | imgFlashOnOff = findViewById(R.id.imgFlashOnOff); 303 | imgSwipeCamera = findViewById(R.id.imgChangeCamera); 304 | textCounter.setVisibility(View.GONE); 305 | hintTextView = findViewById(R.id.hintTextView); 306 | surfaceHolder = imgSurface.getHolder(); 307 | surfaceHolder.addCallback(this); 308 | surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 309 | imgSwipeCamera.setOnClickListener(this); 310 | activeCameraCapture(); 311 | imgFlashOnOff.setOnClickListener(this); 312 | } 313 | 314 | /** 315 | * This method is used for handle camera capture and video recording 316 | * Method call from {@link #initControls()},{@link #savePicTask} 317 | */ 318 | @SuppressLint("ClickableViewAccessibility") 319 | private void activeCameraCapture() { 320 | if (imgCapture != null) { 321 | imgCapture.setAlpha(1.0f); 322 | imgCapture.setOnLongClickListener(v -> { 323 | hintTextView.setVisibility(View.INVISIBLE); 324 | try { 325 | if (prepareMediaRecorder()) { 326 | //myOrientationEventListener.disable(); 327 | mediaRecorder.start(); 328 | startTime = SystemClock.uptimeMillis(); 329 | customHandler.postDelayed(updateTimerThread, 0); 330 | } else { 331 | return false; 332 | } 333 | } catch (Exception e) { 334 | e.printStackTrace(); 335 | } 336 | textCounter.setVisibility(View.VISIBLE); 337 | imgSwipeCamera.setVisibility(View.GONE); 338 | imgFlashOnOff.setVisibility(View.GONE); 339 | imgCapture.setOnTouchListener((v1, event) -> { 340 | if (event.getAction() == MotionEvent.ACTION_BUTTON_PRESS) { 341 | return true; 342 | } 343 | if (event.getAction() == MotionEvent.ACTION_UP) { 344 | hintTextView.setVisibility(View.VISIBLE); 345 | cancelSaveVideoTaskIfNeed(); 346 | saveVideoTask = new BackgroundTaskForSavingVideo(); 347 | saveVideoTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); 348 | return true; 349 | } 350 | return true; 351 | }); 352 | return true; 353 | }); 354 | imgCapture.setOnClickListener(v -> captureImage()); 355 | } 356 | } 357 | 358 | /** 359 | * This method is used for handle front and back camera 360 | * Method call from {@link #onClick(View)} ()} 361 | */ 362 | private void switchCameraType() { 363 | camera.stopPreview(); 364 | camera.release(); 365 | if (flag == Camera.CameraInfo.CAMERA_FACING_BACK) { 366 | flag = Camera.CameraInfo.CAMERA_FACING_FRONT; 367 | imgFlashOnOff.setVisibility(View.GONE); 368 | } else { 369 | flag = Camera.CameraInfo.CAMERA_FACING_BACK; 370 | imgFlashOnOff.setVisibility(View.VISIBLE); 371 | } 372 | camera = Camera.open(flag); 373 | try { 374 | camera.setPreviewDisplay(surfaceHolder); 375 | } catch (IOException e) { 376 | e.printStackTrace(); 377 | } 378 | camera.startPreview(); 379 | Camera.Parameters cameraParameters = camera.getParameters(); 380 | refreshCameraPreview(cameraParameters); 381 | } 382 | 383 | /** 384 | * switch image to on and off state just to indicate flashlight status. 385 | * Method call from {@link #onClick(View)} 386 | */ 387 | private void flashToggle() { 388 | if (flashType == 1) { 389 | flashType = 2; 390 | } else if (flashType == 2) { 391 | flashType = 3; 392 | } else if (flashType == 3) { 393 | flashType = 1; 394 | } 395 | refreshCamera(); 396 | } 397 | 398 | /** 399 | * this method is used for get an image from the camera 400 | * Method call from {@link #activeCameraCapture()} 401 | */ 402 | private void captureImage() { 403 | camera.takePicture(null, null, jpegCallback); 404 | inActiveCameraCapture(); 405 | } 406 | 407 | /** 408 | * This method is used for set alpha of the click actions 409 | * Method call from {@link #captureImage()} 410 | */ 411 | private void inActiveCameraCapture() { 412 | if (imgCapture != null) { 413 | imgCapture.setAlpha(0.5f); 414 | imgCapture.setOnClickListener(null); 415 | } 416 | } 417 | 418 | /** 419 | * this method is used to get image,validate image rotation rotation and save local path as bitmap 420 | * Method call from {@link #onCreate(Bundle)} 421 | */ 422 | private void captureImageCallback() { 423 | surfaceHolder = imgSurface.getHolder(); 424 | surfaceHolder.addCallback(this); 425 | surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 426 | jpegCallback = (data, camera) -> { 427 | if (data != null) { 428 | int screenWidth = getResources().getDisplayMetrics().widthPixels; 429 | int screenHeight = getResources().getDisplayMetrics().heightPixels; 430 | Matrix mtx = new Matrix(); 431 | Bitmap bm = BitmapFactory.decodeByteArray(data, 0, data.length); 432 | int w = bm.getWidth(); 433 | int h = bm.getHeight(); 434 | if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT && flag == Camera.CameraInfo.CAMERA_FACING_BACK) { 435 | mtx.postRotate(90); 436 | // Rotating Bitmap 437 | bm = Bitmap.createBitmap(bm, 0, 0, w, h, mtx, true); 438 | } else if ((getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT && flag == Camera.CameraInfo.CAMERA_FACING_FRONT)) { 439 | mtx.postRotate(-90); 440 | mtx.postScale(-1, 1); 441 | // Rotating Bitmap 442 | bm = Bitmap.createBitmap(bm, 0, 0, w, h, mtx, true); 443 | } else {// LANDSCAPE MODE 444 | //No need to reverse width and height 445 | bm = Bitmap.createScaledBitmap(bm, screenWidth, screenHeight, true); 446 | } 447 | refreshCamera(); 448 | cancelSavePicTaskIfNeed(); 449 | savePicTask = new BackgroundTaskForSavingImage(bm); 450 | savePicTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); 451 | } 452 | }; 453 | } 454 | 455 | /** 456 | * This method is used to validate surface ,flash toggle and taken image surface refresh actions 457 | * Method call from {@link #surfaceChanged(SurfaceHolder, int, int, int)},{@link #flashToggle()},{@link #captureImageCallback()} 458 | */ 459 | public void refreshCamera() { 460 | if (surfaceHolder.getSurface() == null) { 461 | return; 462 | } 463 | try { 464 | camera.stopPreview(); 465 | Camera.Parameters param = camera.getParameters(); 466 | if (flag == 0) { 467 | if (flashType == 1) { 468 | param.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO); 469 | imgFlashOnOff.setImageResource(R.drawable.ic_automatic_flash); 470 | } else if (flashType == 2) { 471 | param.setFlashMode(Camera.Parameters.FLASH_MODE_ON); 472 | Camera.Parameters params; 473 | if (camera != null) { 474 | params = camera.getParameters(); 475 | if (params != null) { 476 | List supportedFlashModes = params.getSupportedFlashModes(); 477 | if (supportedFlashModes != null) { 478 | if (supportedFlashModes.contains(Camera.Parameters.FLASH_MODE_TORCH)) { 479 | param.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH); 480 | } else if (supportedFlashModes.contains(Camera.Parameters.FLASH_MODE_ON)) { 481 | param.setFlashMode(Camera.Parameters.FLASH_MODE_ON); 482 | } 483 | } 484 | } 485 | } 486 | imgFlashOnOff.setImageResource(R.drawable.ic_flash_on); 487 | } else if (flashType == 3) { 488 | param.setFlashMode(Camera.Parameters.FLASH_MODE_OFF); 489 | imgFlashOnOff.setImageResource(R.drawable.ic_flash_off); 490 | } 491 | } 492 | refreshCameraPreview(param); 493 | } catch (Exception e) { 494 | e.printStackTrace(); 495 | } 496 | } 497 | 498 | /** 499 | * this method is used to refresh camera preview view 500 | * 501 | * @param param - It is a camera parameter 502 | * Method call from {@link #refreshCamera()} 503 | */ 504 | private void refreshCameraPreview(Camera.Parameters param) { 505 | try { 506 | camera.setParameters(param); 507 | setCameraDisplayOrientation(0); 508 | camera.setPreviewDisplay(surfaceHolder); 509 | camera.startPreview(); 510 | 511 | } catch (Exception e) { 512 | e.printStackTrace(); 513 | } 514 | } 515 | 516 | /** 517 | * This method is used to handle camera rotation orientation 518 | * 519 | * @param cameraId - It is captured image id 520 | * Method call from {@link #surfaceCreated(SurfaceHolder)},{@link #refreshCamera()} 521 | */ 522 | public void setCameraDisplayOrientation(int cameraId) { 523 | Camera.CameraInfo info = new Camera.CameraInfo(); 524 | Camera.getCameraInfo(cameraId, info); 525 | int rotation = this.getWindowManager().getDefaultDisplay().getRotation(); 526 | int degrees = 0; 527 | switch (rotation) { 528 | case Surface.ROTATION_0: 529 | degrees = 0; 530 | break; 531 | case Surface.ROTATION_90: 532 | degrees = 90; 533 | break; 534 | case Surface.ROTATION_180: 535 | degrees = 180; 536 | break; 537 | case Surface.ROTATION_270: 538 | degrees = 270; 539 | break; 540 | } 541 | int result; 542 | if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { 543 | result = (info.orientation + degrees) % 360; 544 | result = (360 - result) % 360; // compensate the mirror 545 | } else { // back-facing 546 | result = (info.orientation - degrees + 360) % 360; 547 | } 548 | Camera.Parameters p = null; 549 | try { 550 | p = camera.getParameters(); 551 | } catch (Exception e) { 552 | Log.d(TAG, e.getMessage() != null ? e.getMessage() : ""); 553 | 554 | } 555 | camera.setDisplayOrientation(result); 556 | List supportedFocusModes = null; 557 | if (p != null) { 558 | supportedFocusModes = p.getSupportedFocusModes(); 559 | } 560 | Camera.Parameters param = camera.getParameters(); 561 | if (supportedFocusModes != null) { 562 | if (supportedFocusModes.contains(FOCUS_MODE_CONTINUOUS_PICTURE)) { 563 | param.setFocusMode(FOCUS_MODE_CONTINUOUS_PICTURE); 564 | } else if (supportedFocusModes.contains(FOCUS_MODE_CONTINUOUS_VIDEO)) { 565 | param.setFocusMode(FOCUS_MODE_CONTINUOUS_VIDEO); 566 | } 567 | } 568 | camera.setParameters(param); 569 | } 570 | 571 | /** 572 | * @param bitmap -It is a captured image 573 | * @return -Returns saved image local path 574 | */ 575 | private String saveCapturedImageToLocalPath(Bitmap bitmap) { 576 | String imagePath = ""; 577 | try { 578 | if (bitmap != null) { 579 | File tempFilePath = BraveFileUtils.getRandomImageFileName(this); 580 | BraveFileUtils.saveBitmapToExternalStorage(context, bitmap, tempFilePath); 581 | Bitmap imageBitMap = BraveFileUtils.checkIsBitMapRotated(context, true, tempFilePath); 582 | if (imageBitMap != null) { 583 | recordedImageFilePath = BraveFileUtils.getRandomImageFileName(this); 584 | BraveFileUtils.saveBitmapToExternalStorage(context, imageBitMap, recordedImageFilePath); 585 | } 586 | } 587 | } catch (Exception e) { 588 | Log.d(TAG, e.getMessage() != null ? e.getMessage() : ""); 589 | } 590 | return imagePath; 591 | } 592 | 593 | /** 594 | * This method is used to cancel the camera actions 595 | * Method call from {@link #onBackPressed()}, {@link #captureImageCallback()} 596 | */ 597 | private void cancelSavePicTaskIfNeed() { 598 | if (savePicTask != null && savePicTask.getStatus() == AsyncTask.Status.RUNNING) { 599 | savePicTask.cancel(true); 600 | } 601 | } 602 | 603 | 604 | /** 605 | * This method is used for recording video, validate orientation view and validate video size 606 | * 607 | * @return -It returns boolean value 608 | */ 609 | @SuppressLint("SimpleDateFormat") 610 | protected boolean prepareMediaRecorder() { 611 | mediaRecorder = new MediaRecorder(); 612 | camera.stopPreview(); 613 | camera.unlock(); 614 | mediaRecorder.setCamera(camera); 615 | mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); 616 | mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); 617 | if (flag == 1) { 618 | mediaRecorder.setProfile(CamcorderProfile.get(1, CamcorderProfile.QUALITY_480P)); 619 | } else { 620 | mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_480P)); 621 | } 622 | mediaRecorder.setPreviewDisplay(surfaceHolder.getSurface()); 623 | mediaRecorder.setOrientationHint(mOrientation); 624 | if (Build.MODEL.equalsIgnoreCase("Nexus 6") && flag == 1) { 625 | mediaRecorder.setOrientationHint(mOrientation); 626 | } else if (mOrientation == 90 && flag == 1) { 627 | mOrientation = 270; 628 | mediaRecorder.setOrientationHint(mOrientation); 629 | } else if (flag == 1) { 630 | mediaRecorder.setOrientationHint(mOrientation); 631 | } 632 | int random = new Random().nextInt(6997); 633 | bVideoName = "Braver_VID".concat("_") + random + ".mp4"; 634 | mediaRecorder.setOutputFile(recordedVideoFilePath.getAbsolutePath() + "/" + bVideoName); 635 | mediaRecorder.setOnInfoListener((mr, what, extra) -> { 636 | if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED) { 637 | long downTime = 0; 638 | long eventTime = 0; 639 | //float x = 0.0f; 640 | //float y = 0.0f; 641 | int metaState = 0; 642 | MotionEvent motionEvent = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, 0, 0, metaState); 643 | imgCapture.dispatchTouchEvent(motionEvent); 644 | Toast.makeText(CameraActivity.this, "You reached to Maximum(17MB) video size.", Toast.LENGTH_SHORT).show(); 645 | } 646 | }); 647 | mediaRecorder.setMaxFileSize(1000 * 17 * 1000); 648 | try { 649 | mediaRecorder.prepare(); 650 | } catch (Exception e) { 651 | releaseMediaRecorder(); 652 | Log.d(TAG, e.getMessage() != null ? e.getMessage() : ""); 653 | return false; 654 | } 655 | return true; 656 | 657 | } 658 | 659 | /** 660 | * This method is used to navigates saved video saved path in Message fragment 661 | * 662 | * @param videoPath Method call from {@link #saveVideoTask} 663 | */ 664 | public void onVideoSendDialog() { 665 | Intent intent = new Intent(); 666 | intent.putExtra(BraveFileUtils.IS_IMAGE_FILE, false); 667 | intent.putExtra(BraveFileUtils.VIDEO_PATH, recordedVideoFilePath.getAbsolutePath() + "/" + bVideoName); 668 | setResult(RESULT_OK, intent); 669 | finish(); 670 | } 671 | 672 | /** 673 | * This method is used for cancel video recording 674 | * Method call from {@link #activeCameraCapture()} ()} 675 | */ 676 | private void cancelSaveVideoTaskIfNeed() { 677 | if (saveVideoTask != null && saveVideoTask.getStatus() == AsyncTask.Status.RUNNING) { 678 | saveVideoTask.cancel(true); 679 | } 680 | } 681 | 682 | /** 683 | * This method is used for clear recorder configuration and release the recorder object 684 | * Method call from {@link #onPause()}, {@link #prepareMediaRecorder(), {@link #saveVideoTask}} 685 | */ 686 | private void releaseMediaRecorder() { 687 | if (mediaRecorder != null) { 688 | mediaRecorder.reset(); // clear recorder configuration 689 | mediaRecorder.release(); // release the recorder object 690 | mediaRecorder = new MediaRecorder(); 691 | } 692 | } 693 | 694 | //------------------SURFACE OVERRIDE METHODS END--------------------// 695 | 696 | private void sendImagePath() { 697 | Intent intent = new Intent(); 698 | intent.putExtra(BraveFileUtils.IS_IMAGE_FILE, true); 699 | intent.putExtra(BraveFileUtils.IMAGE_PATH, recordedImageFilePath.getAbsolutePath()); 700 | setResult(RESULT_OK, intent); 701 | finish(); 702 | } 703 | 704 | /** 705 | * This method is used to save picture in local path 706 | * * Method call from {@link #captureImageCallback()} 707 | */ 708 | @SuppressLint("StaticFieldLeak") 709 | private class BackgroundTaskForSavingImage extends AsyncTask { 710 | private final Bitmap data; 711 | 712 | public BackgroundTaskForSavingImage(Bitmap data) { 713 | this.data = data; 714 | } 715 | 716 | @Override 717 | protected String doInBackground(Void... params) { 718 | try { 719 | return saveCapturedImageToLocalPath(data); 720 | } catch (Exception e) { 721 | Log.d(TAG, e.getMessage() != null ? e.getMessage() : ""); 722 | return null; 723 | } 724 | } 725 | 726 | @Override 727 | protected void onPostExecute(String result) { 728 | activeCameraCapture(); 729 | sendImagePath(); 730 | } 731 | } 732 | 733 | /** 734 | * This method is used for save video in local path 735 | * Method call from {@link #activeCameraCapture()} 736 | */ 737 | 738 | @SuppressLint("StaticFieldLeak") 739 | private class BackgroundTaskForSavingVideo extends AsyncTask { 740 | ProgressDialog progressDialog = null; 741 | 742 | @SuppressLint("ClickableViewAccessibility") 743 | @Override 744 | protected void onPreExecute() { 745 | progressDialog = new ProgressDialog(CameraActivity.this); 746 | progressDialog.setMessage(PROCESSING_VIDEO); 747 | progressDialog.show(); 748 | imgCapture.setOnTouchListener(null); 749 | textCounter.setVisibility(View.GONE); 750 | imgSwipeCamera.setVisibility(View.VISIBLE); 751 | imgFlashOnOff.setVisibility(View.VISIBLE); 752 | super.onPreExecute(); 753 | 754 | } 755 | 756 | @Override 757 | protected Void doInBackground(Void... params) { 758 | try { 759 | try { 760 | customHandler.removeCallbacksAndMessages(null); 761 | mediaRecorder.stop(); 762 | releaseMediaRecorder(); 763 | } catch (Exception e) { 764 | Log.d(TAG, e.getMessage() != null ? e.getMessage() : ""); 765 | } 766 | } catch (Exception e) { 767 | Log.d(TAG, e.getMessage() != null ? e.getMessage() : ""); 768 | 769 | } 770 | return null; 771 | } 772 | 773 | @Override 774 | protected void onPostExecute(Void aVoid) { 775 | super.onPostExecute(aVoid); 776 | if (progressDialog != null) { 777 | if (progressDialog.isShowing()) { 778 | progressDialog.dismiss(); 779 | } 780 | } 781 | if (recordedVideoFilePath != null && recordedVideoFilePath.getAbsolutePath() != null) { 782 | onVideoSendDialog(); 783 | } 784 | } 785 | } 786 | } -------------------------------------------------------------------------------- /picker/src/main/java/com/braver/tool/picker/image/ImageUtil.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Created by https://github.com/braver-tool on 12/10/21, 08:30 PM 4 | * * Copyright (c) 2021 . All rights reserved. 5 | * * Last modified 23/03/22, 09:45 AM 6 | * 7 | */ 8 | package com.braver.tool.picker.image 9 | 10 | import android.graphics.Bitmap 11 | import android.graphics.BitmapFactory 12 | import android.graphics.Matrix 13 | import androidx.exifinterface.media.ExifInterface 14 | import java.io.File 15 | import java.io.FileOutputStream 16 | import java.io.IOException 17 | 18 | object ImageUtil { 19 | /** 20 | * @param imageFile - source file 21 | * @param destinationPath - design path of compressed image 22 | * @return - file path 23 | * @throws IOException - error message 24 | * Method used to compress image 25 | */ 26 | @JvmStatic 27 | @Throws(IOException::class) 28 | fun compressImage(imageFile: File, destinationPath: String): File { 29 | var fileOutputStream: FileOutputStream? = null 30 | val file = File(destinationPath).parentFile 31 | if (!file.exists()) { 32 | file.mkdirs() 33 | } 34 | try { 35 | fileOutputStream = FileOutputStream(destinationPath) 36 | // write the compressed bitmap at the destination specified by destinationPath. 37 | decodeSampledBitmapFromFile(imageFile).compress( 38 | Bitmap.CompressFormat.JPEG, 39 | 80, 40 | fileOutputStream 41 | ) 42 | } finally { 43 | if (fileOutputStream != null) { 44 | fileOutputStream.flush() 45 | fileOutputStream.close() 46 | } 47 | } 48 | return File(destinationPath) 49 | } 50 | 51 | /** 52 | * @param imageFile - source file 53 | * @return - image as bitmap 54 | * @throws IOException 55 | */ 56 | @Throws(IOException::class) 57 | fun decodeSampledBitmapFromFile(imageFile: File): Bitmap { 58 | //Bitmap bitmap = BitmapFactory.decodeFile(imageFile.getPath()); 59 | //int reqWidth = bitmap.getWidth(); 60 | //int reqHeight = bitmap.getHeight(); 61 | val reqWidth = 612 62 | val reqHeight = 816 63 | // First decode with inJustDecodeBounds=true to check dimensions 64 | val options = BitmapFactory.Options() 65 | options.inJustDecodeBounds = true 66 | BitmapFactory.decodeFile(imageFile.absolutePath, options) 67 | // Calculate inSampleSize 68 | options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight) 69 | // Decode bitmap with inSampleSize set 70 | options.inJustDecodeBounds = false 71 | var scaledBitmap = BitmapFactory.decodeFile(imageFile.absolutePath, options) 72 | //check the rotation of the image and display it properly 73 | val exif = ExifInterface(imageFile.absolutePath) 74 | val orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0) 75 | val matrix = Matrix() 76 | when (orientation) { 77 | 6 -> { 78 | matrix.postRotate(90f) 79 | } 80 | 3 -> { 81 | matrix.postRotate(180f) 82 | } 83 | 8 -> { 84 | matrix.postRotate(270f) 85 | } 86 | } 87 | scaledBitmap = Bitmap.createBitmap( 88 | scaledBitmap, 89 | 0, 90 | 0, 91 | scaledBitmap.width, 92 | scaledBitmap.height, 93 | matrix, 94 | true 95 | ) 96 | return scaledBitmap 97 | } 98 | 99 | /** 100 | * @param options - BitmapFactory 101 | * @param reqWidth - image width 102 | * @param reqHeight - image height 103 | * @return - image size 104 | */ 105 | private fun calculateInSampleSize( 106 | options: BitmapFactory.Options, 107 | reqWidth: Int, 108 | reqHeight: Int 109 | ): Int { 110 | // Raw height and width of image 111 | val height = options.outHeight 112 | val width = options.outWidth 113 | var inSampleSize = 1 114 | if (height > reqHeight || width > reqWidth) { 115 | val halfHeight = height / 2 116 | val halfWidth = width / 2 117 | // Calculate the largest inSampleSize value that is a power of 2 and keeps both 118 | // height and width larger than the requested height and width. 119 | while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) { 120 | inSampleSize *= 2 121 | } 122 | } 123 | return inSampleSize 124 | } 125 | } -------------------------------------------------------------------------------- /picker/src/main/res/drawable/bg_record_video.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 13 | 16 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /picker/src/main/res/drawable/ic_automatic_flash.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 14 | 17 | 18 | -------------------------------------------------------------------------------- /picker/src/main/res/drawable/ic_capture_image_icon.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 14 | 17 | 18 | -------------------------------------------------------------------------------- /picker/src/main/res/drawable/ic_flash_off.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 14 | 17 | 20 | 21 | -------------------------------------------------------------------------------- /picker/src/main/res/drawable/ic_flash_on.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 14 | 17 | 18 | -------------------------------------------------------------------------------- /picker/src/main/res/drawable/ic_rotate_arrow_icon.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 14 | 17 | 20 | 21 | -------------------------------------------------------------------------------- /picker/src/main/res/drawable/ic_solid_dot_circle_red.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 14 | 17 | 18 | -------------------------------------------------------------------------------- /picker/src/main/res/drawable/ic_solid_dot_circle_white.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 14 | 17 | 18 | -------------------------------------------------------------------------------- /picker/src/main/res/layout/camera_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 16 | 17 | 21 | 22 | 27 | 28 | 43 | 44 | 45 | 53 | 54 | 55 | 64 | 65 | 72 | 73 | 83 | 84 | 85 | 91 | 92 | 102 | 103 | 112 | 113 | 114 | 124 | 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /picker/src/main/res/values/color.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | #ffffff 12 | -------------------------------------------------------------------------------- /picker/src/test/java/com/braver/tool/picker/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Created by https://github.com/braver-tool on 12/10/21, 08:30 PM 4 | * * Copyright (c) 2021 . All rights reserved. 5 | * * Last modified 23/03/22, 09:45 AM 6 | * 7 | */ 8 | 9 | package com.braver.tool.picker; 10 | 11 | import org.junit.Test; 12 | 13 | import static org.junit.Assert.*; 14 | 15 | /** 16 | * Example local unit test, which will execute on the development machine (host). 17 | * 18 | * @see Testing documentation 19 | */ 20 | public class ExampleUnitTest { 21 | @Test 22 | public void addition_isCorrect() { 23 | assertEquals(4, 2 + 2); 24 | } 25 | } -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Created by https://github.com/braver-tool on 12/10/21, 08:30 PM 4 | * * Copyright (c) 2021 . All rights reserved. 5 | * * Last modified 23/03/22, 09:45 AM 6 | * 7 | */ 8 | 9 | include ':app', ':picker' 10 | --------------------------------------------------------------------------------