diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aa724b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..30e75dc --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..958a5be --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..9325c36 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,38 @@ +apply plugin: 'com.android.application' +android { + compileSdkVersion 30 + defaultConfig { + applicationId "com.braver.tool.filepicker" + minSdkVersion 23 + targetSdkVersion 30 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.3.1' + implementation 'com.google.android.material:material:1.4.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.1' + //implementation 'androidx.core:core:1.7.0' + testImplementation 'junit:junit:4.+' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + implementation 'com.intuit.sdp:sdp-android:1.0.6' + implementation 'com.google.android.exoplayer:exoplayer:2.15.0' + implementation 'com.github.chrisbanes:PhotoView:2.3.0' + implementation project(':picker') +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/src/androidTest/java/com/braver/tool/filepicker/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/braver/tool/filepicker/ExampleInstrumentedTest.java new file mode 100644 index 0000000..ca8779b --- /dev/null +++ b/app/src/androidTest/java/com/braver/tool/filepicker/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.braver.tool.filepicker; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + assertEquals("com.braver.tool.filepicker", appContext.getPackageName()); + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..01e4952 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/braver/tool/filepicker/AppUtils.java b/app/src/main/java/com/braver/tool/filepicker/AppUtils.java new file mode 100644 index 0000000..ff294ef --- /dev/null +++ b/app/src/main/java/com/braver/tool/filepicker/AppUtils.java @@ -0,0 +1 @@ +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()); } } } \ No newline at end of file diff --git a/app/src/main/java/com/braver/tool/filepicker/CustomPreferencesManager.java b/app/src/main/java/com/braver/tool/filepicker/CustomPreferencesManager.java new file mode 100644 index 0000000..e3e2e51 --- /dev/null +++ b/app/src/main/java/com/braver/tool/filepicker/CustomPreferencesManager.java @@ -0,0 +1,56 @@ +package com.braver.tool.filepicker; + + +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Build; + +import static com.braver.tool.filepicker.PermissionUtils.IS_PERMISSION_DIALOG_ANDROID_11; + +public class CustomPreferencesManager { + private static final String NOTIFICATION_PREFERENCES_NAME = "braver_preferences"; + public final SharedPreferences notificationPrefManager; + + /** + * Method used to initiate Notification User Defaults + * + * @param context - Current Activity + */ + public CustomPreferencesManager(Context context) { + Context storageContext; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + final Context deviceContext = context.createDeviceProtectedStorageContext(); + if (!deviceContext.moveSharedPreferencesFrom(context, NOTIFICATION_PREFERENCES_NAME)) { + AppUtils.printLogConsole("CustomPreferencesManager", "Failed to migrate shared preferences."); + } + storageContext = deviceContext; + } else { + storageContext = context; + } + notificationPrefManager = storageContext.getSharedPreferences(NOTIFICATION_PREFERENCES_NAME, Context.MODE_PRIVATE); + } + + /** + * Method used to get UserDefault + */ + public String getPermissionDialogData() { + String data = ""; + if (notificationPrefManager != null) { + data = notificationPrefManager.getString(IS_PERMISSION_DIALOG_ANDROID_11, ""); + } + return data; + } + + /** + * Method used to set UserDefault + * + * @param permissionType - String data + */ + public void setPermissionDialogData(String permissionType) { + if (notificationPrefManager != null) { + SharedPreferences.Editor editor = notificationPrefManager.edit(); + editor.putString(IS_PERMISSION_DIALOG_ANDROID_11, permissionType); + editor.apply(); + } + } +} diff --git a/app/src/main/java/com/braver/tool/filepicker/LaunchActivity.java b/app/src/main/java/com/braver/tool/filepicker/LaunchActivity.java new file mode 100644 index 0000000..3ed3356 --- /dev/null +++ b/app/src/main/java/com/braver/tool/filepicker/LaunchActivity.java @@ -0,0 +1,20 @@ +package com.braver.tool.filepicker; + +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; + +import androidx.appcompat.app.AppCompatActivity; + +public class LaunchActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_launch); + new Handler().postDelayed(() -> { + startActivity(new Intent(LaunchActivity.this, PickerActivity.class)); + finish(); + }, 800); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/braver/tool/filepicker/PermissionUtils.java b/app/src/main/java/com/braver/tool/filepicker/PermissionUtils.java new file mode 100644 index 0000000..fc47916 --- /dev/null +++ b/app/src/main/java/com/braver/tool/filepicker/PermissionUtils.java @@ -0,0 +1 @@ +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.View; 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); //checkPermissionGrant(context, locationPermissionList, Manifest.permission.WRITE_EXTERNAL_STORAGE); if (locationPermissionList.size() > 0) { if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q && locationPermissionList.contains(Manifest.permission.READ_EXTERNAL_STORAGE) && locationPermissionList.contains(Manifest.permission.WRITE_EXTERNAL_STORAGE) && Environment.isExternalStorageManager()) { locationPermissionList.remove(Manifest.permission.READ_EXTERNAL_STORAGE); //locationPermissionList.remove(Manifest.permission.WRITE_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); //checkPermissionGrant(context, locationPermissionList, Manifest.permission.WRITE_EXTERNAL_STORAGE); if (locationPermissionList.size() > 0) { if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q && locationPermissionList.contains(Manifest.permission.READ_EXTERNAL_STORAGE) && locationPermissionList.contains(Manifest.permission.WRITE_EXTERNAL_STORAGE) && Environment.isExternalStorageManager()) { locationPermissionList.remove(Manifest.permission.READ_EXTERNAL_STORAGE); //locationPermissionList.remove(Manifest.permission.WRITE_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; } } \ No newline at end of file diff --git a/app/src/main/java/com/braver/tool/filepicker/PickerActivity.java b/app/src/main/java/com/braver/tool/filepicker/PickerActivity.java new file mode 100644 index 0000000..f250354 --- /dev/null +++ b/app/src/main/java/com/braver/tool/filepicker/PickerActivity.java @@ -0,0 +1,344 @@ +package com.braver.tool.filepicker; + +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.Bundle; +import android.os.Environment; +import android.provider.MediaStore; +import android.provider.Settings; +import android.view.Gravity; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.app.ActivityCompat; + +import com.braver.tool.picker.BraveFilePath; +import com.braver.tool.picker.BraveFilePicker; +import com.braver.tool.picker.BraveFileType; +import com.braver.tool.picker.BraveFileUtils; +import com.braver.tool.picker.CameraActivity; + +import java.io.File; +import java.util.Objects; + +import static com.braver.tool.filepicker.AppUtils.DOC_ALERT_MSG; +import static com.braver.tool.filepicker.AppUtils.START_POINT; +import static com.braver.tool.filepicker.PermissionUtils.PERMISSION_CAMERA; +import static com.braver.tool.filepicker.PermissionUtils.PERMISSION_GALLERY; +import static com.braver.tool.filepicker.PermissionUtils.PERMISSION_MIC; +import static com.braver.tool.filepicker.PermissionUtils.READ_EXTERNAL_STORAGE_PERMISSION; +import static com.braver.tool.picker.BraveFileUtils.IMAGE_PATH; +import static com.braver.tool.picker.BraveFileUtils.IS_IMAGE_FILE; +import static com.braver.tool.picker.BraveFileUtils.PREVIEW_IMAGE_FILE_PATH; +import static com.braver.tool.picker.BraveFileUtils.PREVIEW_VIDEO_FILE_PATH; +import static com.braver.tool.picker.BraveFileUtils.VIDEO_PATH; + +public class PickerActivity extends AppCompatActivity implements View.OnClickListener { + private String tempFileAbsolutePath = ""; + private int selectedOption = 0; + private TextView sourceFilePathTextView; + private ActivityResultLauncher activityResultLauncherForGallery; + private ActivityResultLauncher activityResultLauncherForCamera; + private ActivityResultLauncher activityResultLauncherForDocs; + private ActivityResultLauncher activityResultLauncherForDocsPermission; + private int resultOption = 0; + private boolean isPreviewClicks = false; + private TextView previewTextView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + initializeViews(); + activityResultLauncherForGallery = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> { + if (result.getResultCode() == RESULT_OK && result.getData() != null) { + try { + Uri selectedMediaUri = result.getData().getData(); + if (selectedMediaUri.toString().contains("image") || selectedMediaUri.toString().contains("jpg") || selectedMediaUri.toString().contains("jpeg") || selectedMediaUri.toString().contains("png")) { + File imageFile = new BraveFilePicker().setActivity(PickerActivity.this).setIsCompressImage(false).setIsTrimVide(false).setFileType(BraveFileType.IMAGE).setDestinationFilePath(AppUtils.getRandomImageFileName(PickerActivity.this)).setContentUri(selectedMediaUri).getSourceFile(); + resultOption = 1; + setSourcePathView(imageFile.getAbsolutePath()); + } else { + //For Video + resultOption = 2; + setSourcePathView(String.valueOf(selectedMediaUri)); + } + } catch (Exception e) { + AppUtils.printLogConsole("activityResultLauncherForGallery", "Exception-------->" + e.getMessage()); + } + } + }); + activityResultLauncherForCamera = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> { + if (result.getResultCode() == RESULT_OK && result.getData() != null) { + try { + boolean isImageFile = result.getData().getBooleanExtra(IS_IMAGE_FILE, false); + String filePath; + if (isImageFile) { + filePath = result.getData().getStringExtra(IMAGE_PATH); + resultOption = 1; + } else { + //For Video + filePath = result.getData().getStringExtra(VIDEO_PATH); + resultOption = 2; + } + setSourcePathView(filePath); + } catch (Exception e) { + AppUtils.printLogConsole("activityResultLauncherForCamera", "Exception-------->" + e.getMessage()); + } + } + }); + activityResultLauncherForDocs = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> { + if (result.getResultCode() == RESULT_OK && result.getData() != null) { + try { + //String tempFileAbsolutePath = ""; + Uri selectedDocUri = result.getData().getData(); + if (selectedDocUri.toString().contains("video") || selectedDocUri.toString().contains("mp4") || selectedDocUri.toString().contains("mkv") || selectedDocUri.toString().contains("mov")) { + //For Video + resultOption = 2; + setSourcePathView(String.valueOf(selectedDocUri)); + } else if (BraveFilePath.isGoogleDriveUri(selectedDocUri)) { + tempFileAbsolutePath = BraveFilePath.getRealPathFromURI(PickerActivity.this, selectedDocUri); + resultOption = 3; + setSourcePathView(tempFileAbsolutePath); + } else { + String filePath = BraveFilePath.getRealPathFromURI(PickerActivity.this, selectedDocUri); + tempFileAbsolutePath = BraveFileUtils.saveDocToExternalStorage(PickerActivity.this, filePath); + resultOption = 3; + setSourcePathView(tempFileAbsolutePath); + } + } catch (Exception e) { + AppUtils.printLogConsole("activityResultLauncherForDocs", "Exception-------->" + e.getMessage()); + } + } + }); + activityResultLauncherForDocsPermission = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> { + if (result.getResultCode() == RESULT_OK && result.getData() != null) { + try { + AppUtils.printLogConsole("activityResultLauncherForDocsPermission", "-------->Access Granted!"); + } catch (Exception e) { + AppUtils.printLogConsole("activityResultLauncherForDocsPermission", "Exception-------->" + e.getMessage()); + } + } + }); + } + + @Override + protected void onResume() { + isPreviewClicks = false; + super.onResume(); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + if (requestCode == PermissionUtils.MANDATORY_GALLERY_PERMISSION_REQ_CODE) { + boolean cameraAndStoragePermissionGranted = true; + for (int grantResult : grantResults) { + cameraAndStoragePermissionGranted &= grantResult == PackageManager.PERMISSION_GRANTED; + } + if (cameraAndStoragePermissionGranted) { + callFileAccessIntent(); + } 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))) { + if (!ActivityCompat.shouldShowRequestPermissionRationale(this, READ_EXTERNAL_STORAGE_PERMISSION)) { + PermissionUtils.showAlertForAppInfoScreen(this, PERMISSION_GALLERY); + } else if (!PermissionUtils.isCameraAccessed(this)) { + PermissionUtils.showAlertForAppInfoScreen(this, PERMISSION_CAMERA); + } else if (!PermissionUtils.isAudioAccessed(PickerActivity.this)) { + PermissionUtils.showAlertForAppInfoScreen(this, PERMISSION_MIC); + } + } else { + AppUtils.printLogConsole("onRequestPermissionsResult", "-------->Permission Denied!!"); + } + } + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + } + + @Override + public void onClick(View v) { + if (v.getId() == R.id.cameraLayout) { + selectedOption = 1; + callFileAccessIntent(); + } else if (v.getId() == R.id.galleryLayout) { + selectedOption = 2; + callFileAccessIntent(); + } else if (v.getId() == R.id.docLayout) { + selectedOption = 3; + callFileAccessIntent(); + } else if (v.getId() == R.id.previewButtonTextView) { + if (!isPreviewClicks) { + isPreviewClicks = true; + if (resultOption == 1 || resultOption == 2) { + navigateToPreviewScreen(); + } else { + AppUtils.openDocument(PickerActivity.this, tempFileAbsolutePath); + } + } + + } + } + + /** + * Declaration setSourcePathView() + * Set file path to the view and show Preview Button + */ + private void setSourcePathView(String path) { + tempFileAbsolutePath = path; + String filePath = AppUtils.getFileNameFromPath(path); + sourceFilePathTextView.setText(START_POINT.concat(filePath)); + previewTextView.setVisibility(View.VISIBLE); + } + + /** + * Declaration navigateToPreviewScreen() + * Navigation to Preview Screen + */ + private void navigateToPreviewScreen() { + Bundle bundle = new Bundle(); + bundle.putString(resultOption == 1 ? PREVIEW_IMAGE_FILE_PATH : PREVIEW_VIDEO_FILE_PATH, tempFileAbsolutePath); + Intent navigateTo = new Intent(PickerActivity.this, PreviewActivity.class); + navigateTo.putExtras(bundle); + startActivity(navigateTo); + } + + + /** + * Declaration initializeViews() + * Initialize views with the XML + */ + private void initializeViews() { + LinearLayout cameraLayout = findViewById(R.id.cameraLayout); + LinearLayout galleryLayout = findViewById(R.id.galleryLayout); + LinearLayout docLayout = findViewById(R.id.docLayout); + sourceFilePathTextView = findViewById(R.id.sourceFilePathTextView); + previewTextView = findViewById(R.id.previewButtonTextView); + cameraLayout.setOnClickListener(this); + galleryLayout.setOnClickListener(this); + docLayout.setOnClickListener(this); + previewTextView.setOnClickListener(this); + previewTextView.setVisibility(View.GONE); + } + + /** + * Declaration callFileAccessIntent() + * Handling click event of the picker icons + */ + private void callFileAccessIntent() { + if (PermissionUtils.isFileAccessPermissionGranted(this)) { + if (selectedOption == 1) { + openCamera(); + } else if (selectedOption == 2) { + openGalleryForImageAndVideo(); + } else if (selectedOption == 3) { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q && !Environment.isExternalStorageManager()) { + showAlertForDocSettingsScreen(PickerActivity.this); + } else { + openAttachments(); + } + } + } else { + PermissionUtils.showFileAccessPermissionRequestDialog(this); + } + } + + /** + * Declaration openGalleryForImageAndVideo() + * Method is used to open the Gallery + */ + private void openGalleryForImageAndVideo() { + try { + Intent intent = new Intent(Intent.ACTION_PICK); + intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "*/*"); + activityResultLauncherForGallery.launch(intent); + } catch (Exception exception) { + AppUtils.printLogConsole("openGalleryForImageAndVideo", "Exception-------->" + exception.getMessage()); + } + } + + /** + * Declaration openCamera() + * Method is used to open the Camera + */ + private void openCamera() { + try { + Intent intent = new Intent(PickerActivity.this, CameraActivity.class); + activityResultLauncherForCamera.launch(intent); + } catch (Exception exception) { + AppUtils.printLogConsole("openCamera", "Exception-------->" + exception.getMessage()); + } + } + + /** + * Handling Get doc,pdf,xlx,apk,et., from user to send throw Attachments
+ */ + + private void openAttachments() { + try { + Intent addAttachment = new Intent(Intent.ACTION_GET_CONTENT); + addAttachment.setType("*/*"); + addAttachment.setAction(Intent.ACTION_GET_CONTENT); + addAttachment.setAction(Intent.ACTION_OPEN_DOCUMENT); + activityResultLauncherForDocs.launch(addAttachment); + } catch (Exception exception) { + AppUtils.printLogConsole("openAttachments", "Exception-------->" + exception.getMessage()); + } + } + + /** + * @param context - Dialog call from + */ + private void showAlertForDocSettingsScreen(Context context) { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) { + 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(DOC_ALERT_MSG); + permissionAlertTypeImageView.setVisibility(View.GONE); + permissionAlertNotNowTextView.setOnClickListener(v -> dialog.dismiss()); + permissionAlertSettingsTextView.setOnClickListener(v -> { + navigateSettingsForDocAccess(); + dialog.dismiss(); + }); + dialog.show(); + } + } + + /** + * Declaration navigateSettingsForDocAccess() + * Method show alert dialog to ask access for Android 11 Doc Permission + */ + private void navigateSettingsForDocAccess() { + try { + Intent intent = new Intent(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + intent.setAction(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION); + } + Uri uri = Uri.fromParts("package", getPackageName(), null); + intent.setData(uri); + activityResultLauncherForDocsPermission.launch(intent); + } catch (Exception exception) { + AppUtils.printLogConsole("navigateSettingsForDocAccess", "Exception-------->" + exception.getMessage()); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/braver/tool/filepicker/PreviewActivity.java b/app/src/main/java/com/braver/tool/filepicker/PreviewActivity.java new file mode 100644 index 0000000..6674f4a --- /dev/null +++ b/app/src/main/java/com/braver/tool/filepicker/PreviewActivity.java @@ -0,0 +1,180 @@ +package com.braver.tool.filepicker; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.os.Bundle; +import android.view.View; +import android.widget.ImageView; +import android.widget.LinearLayout; + +import androidx.appcompat.app.AppCompatActivity; + +import com.github.chrisbanes.photoview.PhotoView; +import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.MediaItem; +import com.google.android.exoplayer2.Player; +import com.google.android.exoplayer2.SimpleExoPlayer; +import com.google.android.exoplayer2.audio.AudioAttributes; +import com.google.android.exoplayer2.source.MediaSource; +import com.google.android.exoplayer2.source.ProgressiveMediaSource; +import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; +import com.google.android.exoplayer2.ui.PlayerView; +import com.google.android.exoplayer2.upstream.DataSource; +import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; + +import java.util.Objects; + +import static com.braver.tool.picker.BraveFileUtils.PREVIEW_IMAGE_FILE_PATH; +import static com.braver.tool.picker.BraveFileUtils.PREVIEW_VIDEO_FILE_PATH; + +public class PreviewActivity extends AppCompatActivity implements View.OnClickListener { + private PlayerView playerView; + private SimpleExoPlayer videoPlayer; + private ImageView imagePlayPause; + private boolean isVideoEnded; + private String imageFilePath, videoFilePath; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_preview); + initializeViews(); + } + + @Override + protected void onPause() { + super.onPause(); + if (videoPlayer != null) { + videoPlayer.setPlayWhenReady(false); + } + + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if (videoPlayer != null) { + videoPlayer.release(); + } + } + + + @Override + public void onClick(View view) { + int id = view.getId(); + if (id == R.id.videoCloseImageView) { + onBackPressed(); + } + } + + private void initializeViews() { + playerView = findViewById(R.id.player_view_lib); + imagePlayPause = findViewById(R.id.image_play_pause); + PhotoView scaleImageView = findViewById(R.id.scaleImageView); + LinearLayout videoViewParentLayout = findViewById(R.id.videoViewParentLayout); + ImageView videoCloseImageView = findViewById(R.id.videoCloseImageView); + videoCloseImageView.setOnClickListener(this); + Bundle bundle = getIntent().getExtras(); + if (bundle != null) { + imageFilePath = bundle.getString(PREVIEW_IMAGE_FILE_PATH, ""); + videoFilePath = bundle.getString(PREVIEW_VIDEO_FILE_PATH, ""); + } + if (!videoFilePath.isEmpty()) { + scaleImageView.setVisibility(View.GONE); + videoViewParentLayout.setVisibility(View.VISIBLE); + initPlayer(); + setDataInView(); + } else if (!imageFilePath.isEmpty()) { + videoViewParentLayout.setVisibility(View.GONE); + scaleImageView.setVisibility(View.VISIBLE); + Bitmap photoBitmap = BitmapFactory.decodeFile(imageFilePath); + if (photoBitmap != null) { + scaleImageView.setImageBitmap(photoBitmap); + } + } + } + + + /** + * SettingUp exoplayer + **/ + private void initPlayer() { + try { + videoPlayer = new SimpleExoPlayer.Builder(this).build(); + playerView.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_FIT); + playerView.setPlayer(videoPlayer); + AudioAttributes audioAttributes = new AudioAttributes.Builder().setUsage(C.USAGE_MEDIA).setContentType(C.CONTENT_TYPE_MOVIE).build(); + videoPlayer.setAudioAttributes(audioAttributes, true); + } catch (Exception e) { + AppUtils.printLogConsole("initPlayer", "Exception-------->" + e.getMessage()); + } + } + + private void setDataInView() { + try { + imagePlayPause.setOnClickListener(v -> onVideoClicked()); + Objects.requireNonNull(playerView.getVideoSurfaceView()).setOnClickListener(v -> onVideoClicked()); + buildMediaSource(Uri.parse(videoFilePath)); + } catch (Exception e) { + AppUtils.printLogConsole("setDataInView", "Exception-------->" + e.getMessage()); + } + } + + private void onVideoClicked() { + try { + long lastMinValue = 0; + if (isVideoEnded) { + seekTo(lastMinValue); + videoPlayer.setPlayWhenReady(true); + return; + } + seekTo(lastMinValue); + videoPlayer.setPlayWhenReady(!videoPlayer.getPlayWhenReady()); + } catch (Exception e) { + AppUtils.printLogConsole("onVideoClicked", "Exception-------->" + e.getMessage()); + } + } + + private void seekTo(long sec) { + if (videoPlayer != null) + videoPlayer.seekTo(sec * 1000); + } + + private void buildMediaSource(Uri mUri) { + try { + DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(this, getString(R.string.app_name)); + MediaSource mediaSource = new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(mUri)); + videoPlayer.addMediaSource(mediaSource); + videoPlayer.prepare(); + videoPlayer.setPlayWhenReady(true); + videoPlayer.addListener(new Player.Listener() { + @Override + public void onPlaybackStateChanged(int playbackState) { + switch (playbackState) { + case Player.STATE_ENDED: + imagePlayPause.setVisibility(View.VISIBLE); + isVideoEnded = true; + break; + case Player.STATE_READY: + isVideoEnded = false; + break; + case Player.STATE_BUFFERING: + case Player.STATE_IDLE: + default: + break; + } + } + + @Override + public void onPlayWhenReadyChanged(boolean playWhenReady, int reason) { + imagePlayPause.setVisibility(playWhenReady ? View.GONE : View.VISIBLE); + + } + }); + } catch (Exception e) { + AppUtils.printLogConsole("buildMediaSource", "Exception-------->" + e.getMessage()); + } + } +} diff --git a/app/src/main/res/drawable/alert_btn_bg.xml b/app/src/main/res/drawable/alert_btn_bg.xml new file mode 100644 index 0000000..6212d91 --- /dev/null +++ b/app/src/main/res/drawable/alert_btn_bg.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_close.xml b/app/src/main/res/drawable/ic_close.xml new file mode 100644 index 0000000..2590d02 --- /dev/null +++ b/app/src/main/res/drawable/ic_close.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_file.xml b/app/src/main/res/drawable/ic_file.xml new file mode 100644 index 0000000..70f73fb --- /dev/null +++ b/app/src/main/res/drawable/ic_file.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_permission_audio.xml b/app/src/main/res/drawable/ic_permission_audio.xml new file mode 100644 index 0000000..a49b71b --- /dev/null +++ b/app/src/main/res/drawable/ic_permission_audio.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_permission_camera.xml b/app/src/main/res/drawable/ic_permission_camera.xml new file mode 100644 index 0000000..52704ac --- /dev/null +++ b/app/src/main/res/drawable/ic_permission_camera.xml @@ -0,0 +1,13 @@ + + + + diff --git a/app/src/main/res/drawable/ic_permission_storage.xml b/app/src/main/res/drawable/ic_permission_storage.xml new file mode 100644 index 0000000..d3362f8 --- /dev/null +++ b/app/src/main/res/drawable/ic_permission_storage.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_video_play_lib.xml b/app/src/main/res/drawable/ic_video_play_lib.xml new file mode 100644 index 0000000..3f75a23 --- /dev/null +++ b/app/src/main/res/drawable/ic_video_play_lib.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/pic_camera.png b/app/src/main/res/drawable/pic_camera.png new file mode 100644 index 0000000..7a50ee0 Binary files /dev/null and b/app/src/main/res/drawable/pic_camera.png differ diff --git a/app/src/main/res/drawable/pic_doc.png b/app/src/main/res/drawable/pic_doc.png new file mode 100644 index 0000000..2222059 Binary files /dev/null and b/app/src/main/res/drawable/pic_doc.png differ diff --git a/app/src/main/res/drawable/pic_gallery.png b/app/src/main/res/drawable/pic_gallery.png new file mode 100644 index 0000000..a436f0d Binary files /dev/null and b/app/src/main/res/drawable/pic_gallery.png differ diff --git a/app/src/main/res/drawable/pic_pickup.png b/app/src/main/res/drawable/pic_pickup.png new file mode 100644 index 0000000..0960b79 Binary files /dev/null and b/app/src/main/res/drawable/pic_pickup.png differ diff --git a/app/src/main/res/drawable/preview_btn_bg.xml b/app/src/main/res/drawable/preview_btn_bg.xml new file mode 100644 index 0000000..7ea247c --- /dev/null +++ b/app/src/main/res/drawable/preview_btn_bg.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_circle_lib.xml b/app/src/main/res/drawable/shape_circle_lib.xml new file mode 100644 index 0000000..9fac508 --- /dev/null +++ b/app/src/main/res/drawable/shape_circle_lib.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/splash_bg_grad.xml b/app/src/main/res/drawable/splash_bg_grad.xml new file mode 100644 index 0000000..2000ccf --- /dev/null +++ b/app/src/main/res/drawable/splash_bg_grad.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/font/alegreya_sans_sc_bold.ttf b/app/src/main/res/font/alegreya_sans_sc_bold.ttf new file mode 100644 index 0000000..f113fe0 Binary files /dev/null and b/app/src/main/res/font/alegreya_sans_sc_bold.ttf differ diff --git a/app/src/main/res/font/courgette.ttf b/app/src/main/res/font/courgette.ttf new file mode 100644 index 0000000..433766b Binary files /dev/null and b/app/src/main/res/font/courgette.ttf differ diff --git a/app/src/main/res/layout/activity_launch.xml b/app/src/main/res/layout/activity_launch.xml new file mode 100644 index 0000000..5a09bb0 --- /dev/null +++ b/app/src/main/res/layout/activity_launch.xml @@ -0,0 +1,19 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..34d1747 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_preview.xml b/app/src/main/res/layout/activity_preview.xml new file mode 100644 index 0000000..035619e --- /dev/null +++ b/app/src/main/res/layout/activity_preview.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/permission_alert_popup.xml b/app/src/main/res/layout/permission_alert_popup.xml new file mode 100644 index 0000000..57e7925 --- /dev/null +++ b/app/src/main/res/layout/permission_alert_popup.xml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_app_logo.xml b/app/src/main/res/mipmap-anydpi-v26/ic_app_logo.xml new file mode 100644 index 0000000..ca32248 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_app_logo.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_app_logo.png b/app/src/main/res/mipmap-hdpi/ic_app_logo.png new file mode 100644 index 0000000..431e604 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_app_logo.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_app_logo_adaptive_back.png b/app/src/main/res/mipmap-hdpi/ic_app_logo_adaptive_back.png new file mode 100644 index 0000000..1966948 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_app_logo_adaptive_back.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_app_logo_adaptive_fore.png b/app/src/main/res/mipmap-hdpi/ic_app_logo_adaptive_fore.png new file mode 100644 index 0000000..38c8a54 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_app_logo_adaptive_fore.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_app_logo.png b/app/src/main/res/mipmap-mdpi/ic_app_logo.png new file mode 100644 index 0000000..e150212 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_app_logo.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_app_logo_adaptive_back.png b/app/src/main/res/mipmap-mdpi/ic_app_logo_adaptive_back.png new file mode 100644 index 0000000..75025cf Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_app_logo_adaptive_back.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_app_logo_adaptive_fore.png b/app/src/main/res/mipmap-mdpi/ic_app_logo_adaptive_fore.png new file mode 100644 index 0000000..e33c97f Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_app_logo_adaptive_fore.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_app_logo.png b/app/src/main/res/mipmap-xhdpi/ic_app_logo.png new file mode 100644 index 0000000..b0c127c Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_app_logo.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_app_logo_adaptive_back.png b/app/src/main/res/mipmap-xhdpi/ic_app_logo_adaptive_back.png new file mode 100644 index 0000000..9784f16 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_app_logo_adaptive_back.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_app_logo_adaptive_fore.png b/app/src/main/res/mipmap-xhdpi/ic_app_logo_adaptive_fore.png new file mode 100644 index 0000000..239df3e Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_app_logo_adaptive_fore.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_app_logo.png b/app/src/main/res/mipmap-xxhdpi/ic_app_logo.png new file mode 100644 index 0000000..4a8c0e6 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_app_logo.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_app_logo_adaptive_back.png b/app/src/main/res/mipmap-xxhdpi/ic_app_logo_adaptive_back.png new file mode 100644 index 0000000..04ef206 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_app_logo_adaptive_back.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_app_logo_adaptive_fore.png b/app/src/main/res/mipmap-xxhdpi/ic_app_logo_adaptive_fore.png new file mode 100644 index 0000000..9195e98 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_app_logo_adaptive_fore.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_app_logo.png b/app/src/main/res/mipmap-xxxhdpi/ic_app_logo.png new file mode 100644 index 0000000..261ad56 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_app_logo.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_app_logo_adaptive_back.png b/app/src/main/res/mipmap-xxxhdpi/ic_app_logo_adaptive_back.png new file mode 100644 index 0000000..66a5487 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_app_logo_adaptive_back.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_app_logo_adaptive_fore.png b/app/src/main/res/mipmap-xxxhdpi/ic_app_logo_adaptive_fore.png new file mode 100644 index 0000000..90fec6f Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_app_logo_adaptive_fore.png differ diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml new file mode 100644 index 0000000..33d7d02 --- /dev/null +++ b/app/src/main/res/values/attrs.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..91bf9a9 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,15 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + #1693CB + #148DC2 + #5B9EBB + #015378 + #373737 + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..ed87ec0 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,17 @@ + + FilePicker + images + File Picker + Camera + Gallery + Documents + Click to Preview + + + Settings + To capture photos and videos, allow FilePicker access to your camera.\nTap Settings > Permissions, and turn Camera on. + To send media, allow FilePicker access to your device photos, media, and files.\nTap Settings > Permissions, and "Files and media" on. + To call, allow FilePicker access to your microphone.\nTap Settings > Permissions, and turn Microphone on. + Not Now + + diff --git a/app/src/main/res/xml/provider_paths.xml b/app/src/main/res/xml/provider_paths.xml new file mode 100644 index 0000000..6de978e --- /dev/null +++ b/app/src/main/res/xml/provider_paths.xml @@ -0,0 +1,22 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/test/java/com/braver/tool/filepicker/ExampleUnitTest.java b/app/src/test/java/com/braver/tool/filepicker/ExampleUnitTest.java new file mode 100644 index 0000000..eeef8bb --- /dev/null +++ b/app/src/test/java/com/braver/tool/filepicker/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package com.braver.tool.filepicker; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/app_demo_a.jpg b/app_demo_a.jpg new file mode 100644 index 0000000..7e2eb63 Binary files /dev/null and b/app_demo_a.jpg differ diff --git a/app_demo_b.jpg b/app_demo_b.jpg new file mode 100644 index 0000000..1fa456d Binary files /dev/null and b/app_demo_b.jpg differ diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..fef108e --- /dev/null +++ b/build.gradle @@ -0,0 +1,27 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + google() + jcenter() + maven { url "https://jitpack.io" } + } + dependencies { + classpath 'com.android.tools.build:gradle:3.5.4' + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + google() + jcenter() + mavenCentral() + maven { url "https://jitpack.io" } + } +} + + +task clean(type: Delete) { + delete rootProject.buildDir +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..52f5917 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,19 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app"s APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Automatically convert third-party libraries to use AndroidX +android.enableJetifier=true \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..e708b1c Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..d400b74 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +#Tue Oct 19 10:59:18 IST 2021 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip + diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..4f906e0 --- /dev/null +++ b/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/picker/.gitignore b/picker/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/picker/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/picker/build.gradle b/picker/build.gradle new file mode 100644 index 0000000..1aadec2 --- /dev/null +++ b/picker/build.gradle @@ -0,0 +1,36 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion 30 + + defaultConfig { + minSdkVersion 23 + targetSdkVersion 30 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles "consumer-rules.pro" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.3.1' + implementation 'com.google.android.material:material:1.4.0' + implementation 'androidx.exifinterface:exifinterface:1.3.3' + testImplementation 'junit:junit:4.+' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + implementation 'com.intuit.sdp:sdp-android:1.0.6' +} \ No newline at end of file diff --git a/picker/consumer-rules.pro b/picker/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/picker/proguard-rules.pro b/picker/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/picker/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/picker/src/androidTest/java/com/braver/tool/picker/ExampleInstrumentedTest.java b/picker/src/androidTest/java/com/braver/tool/picker/ExampleInstrumentedTest.java new file mode 100644 index 0000000..77f09cf --- /dev/null +++ b/picker/src/androidTest/java/com/braver/tool/picker/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.braver.tool.picker; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + assertEquals("com.braver.tool.picker.test", appContext.getPackageName()); + } +} \ No newline at end of file diff --git a/picker/src/main/AndroidManifest.xml b/picker/src/main/AndroidManifest.xml new file mode 100644 index 0000000..7f317c7 --- /dev/null +++ b/picker/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/picker/src/main/java/com/braver/tool/picker/BraveFileGetter.java b/picker/src/main/java/com/braver/tool/picker/BraveFileGetter.java new file mode 100644 index 0000000..aefade5 --- /dev/null +++ b/picker/src/main/java/com/braver/tool/picker/BraveFileGetter.java @@ -0,0 +1,38 @@ +package com.braver.tool.picker; + +import android.content.Context; +import android.graphics.Bitmap; +import android.net.Uri; + +import java.io.File; + +public class BraveFileGetter { + + public static String getFileType(BraveFileType braveFileType) { + switch (braveFileType) { + case IMAGE: + return "image"; + case VIDEO: + return "video"; + case DOCS: + return "docs"; + default: + return ""; + } + } + + public static File getSourceFileFromUri(Context context, boolean isCompressImage, boolean isTrimVide, BraveFileType braveFileType, String destinationFilePath, Uri contentUri) { + File file = null; + String fileType = getFileType(braveFileType); + if (fileType.equals("image")) { + //handle image + String filepath = BraveFileUtils.getFilePathFromContentUri(contentUri, context); + Bitmap imageBitMap = BraveFileUtils.checkIsBitMapRotated(context, true, new File(filepath)); + if (imageBitMap != null) { + file = new File(destinationFilePath); + BraveFileUtils.saveBitmapToExternalStorage(context, imageBitMap, file); + } + } + return file; + } +} diff --git a/picker/src/main/java/com/braver/tool/picker/BraveFilePath.java b/picker/src/main/java/com/braver/tool/picker/BraveFilePath.java new file mode 100644 index 0000000..bc9b85a --- /dev/null +++ b/picker/src/main/java/com/braver/tool/picker/BraveFilePath.java @@ -0,0 +1,314 @@ +package com.braver.tool.picker; + +import android.annotation.SuppressLint; +import android.content.ContentUris; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.os.Environment; +import android.provider.DocumentsContract; +import android.provider.MediaStore; +import android.provider.OpenableColumns; +import android.util.Log; + +import androidx.core.content.ContextCompat; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.util.Random; + +import static com.braver.tool.picker.BraveFileUtils.getFilePathFromContentUri; +import static com.braver.tool.picker.BraveFileUtils.printLogConsole; + +public class BraveFilePath { + /** + * Get a file path from a Uri. This will get the the path for Storage Access + * Framework Documents, as well as the _data field for the MediaStore and + * other file-based ContentProviders. + * + * @param context The context. + * @param uri The Uri to query. + * @author paulburke + */ + @SuppressLint("NewApi") + public static String getRealPathFromURI(final Context context, final Uri uri) { + if (DocumentsContract.isDocumentUri(context, uri)) { + if (isExternalStorageDocument(uri)) { + final String docId = DocumentsContract.getDocumentId(uri); + final String[] split = docId.split(":"); + final String type = split[0]; + if ("primary".equalsIgnoreCase(type)) { + return Environment.getExternalStorageDirectory() + "/" + split[1]; + } else if (isGoogleDriveUri(uri)) { + return getDriveFilePath(uri, context); + } else { + return getFilePathFromContentUri(uri, context); + } + } else if (isGoogleDriveUri(uri)) { + return getDriveFilePath(uri, context); + } else if (isDownloadsDocument(uri)) { + String fileName = getFilePath(context, uri); + if (fileName != null) { + return Environment.getExternalStorageDirectory().toString() + "/Download/" + fileName; + } + + String id = DocumentsContract.getDocumentId(uri); + if (id.startsWith("raw:")) { + id = id.replaceFirst("raw:", ""); + File file = new File(id); + if (file.exists()) + return id; + } + final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.parseLong(id)); + return getDataColumn(context, contentUri, null, null); + } else if (isMediaDocument(uri)) { + // MediaProvider + try { + String docId = DocumentsContract.getDocumentId(uri); + String[] split = docId.split(":"); + String type = split[0]; + Uri contentUri = null; + if ("image".equals(type)) { + contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; + } + String selection = "_id=?"; + String[] selectionArgs = new String[]{split[1]}; + return getDataColumn(context, contentUri, selection, selectionArgs); + } catch (Exception e) { + Log.d("##RealDocPathUtil", "--------->" + e.getMessage()); + String fileName = getFilePath(context, uri); + if (fileName != null) { + String file = Environment.getExternalStorageDirectory().toString() + "/Download/" + fileName; + File file1 = new File(file); + if (file1.exists()) { + return file; + } else { + file = copyFileToInternalStorage(context, uri, "userfiles"); + return file; + } + } + } + } else { + return getFilePathFromContentUri(uri, context); + } + } else if ("content".equalsIgnoreCase(uri.getScheme())) { + if (isGooglePhotosUri(uri)) return uri.getLastPathSegment(); + return getDataColumn(context, uri, null, null); + } else if ("file".equalsIgnoreCase(uri.getScheme())) { + return uri.getPath(); + } + return null; + } + + public static String getFilePath(Context context, Uri uri) { + Cursor cursor = null; + final String[] projection = { + MediaStore.MediaColumns.DISPLAY_NAME + }; + try { + cursor = context.getContentResolver().query(uri, projection, null, null, null); + if (cursor != null && cursor.moveToFirst()) { + final int index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME); + return cursor.getString(index); + } + } finally { + if (cursor != null) + cursor.close(); + } + return null; + } + + /** + * Get the value of the data column for this Uri. This is useful for + * MediaStore Uris, and other file-based ContentProviders. + * + * @param context The context. + * @param uri The Uri to query. + * @param selection (Optional) Filter used in the query. + * @param selectionArgs (Optional) Selection arguments used in the query. + * @return The value of the _data column, which is typically a file path. + */ + public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { + Cursor cursor = null; + final String column = "_data"; + final String[] projection = {column}; + try { + cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); + if (cursor != null && cursor.moveToFirst()) { + final int index = cursor.getColumnIndexOrThrow(column); + return cursor.getString(index); + } + } finally { + if (cursor != null) cursor.close(); + } + return null; + } + + /** + * @param uri The Uri to check. + * @return Whether the Uri authority is ExternalStorageProvider. + */ + public static boolean isExternalStorageDocument(Uri uri) { + return "com.android.externalstorage.documents".equals(uri.getAuthority()); + } + + /** + * @param uri The Uri to check. + * @return Whether the Uri authority is DownloadsProvider. + */ + public static boolean isDownloadsDocument(Uri uri) { + return "com.android.providers.downloads.documents".equals(uri.getAuthority()); + } + + /** + * @param uri The Uri to check. + * @return Whether the Uri authority is MediaProvider. + */ + public static boolean isMediaDocument(Uri uri) { + return "com.android.providers.media.documents".equals(uri.getAuthority()); + } + + /** + * @param uri The Uri to check. + * @return Whether the Uri authority is Google Photos. + */ + public static boolean isGooglePhotosUri(Uri uri) { + return "com.google.android.apps.photos.content".equals(uri.getAuthority()); + } + + public static boolean isGoogleDriveUri(Uri uri) { + return "com.google.android.apps.docs.storage".equals(uri.getAuthority()) || "com.google.android.apps.docs.storage.legacy".equals(uri.getAuthority()); + } + + private static String getDriveFilePath(Uri uri, Context context) { + Uri returnUri = uri; + //String[] split2; + Cursor returnCursor = context.getContentResolver().query(returnUri, null, null, null, null); + /* + * Get the column indexes of the data in the Cursor, + * * move to the first row in the Cursor, get the data, + * * and display it. + * */ + int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); + int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE); + returnCursor.moveToFirst(); + + String name = (returnCursor.getString(nameIndex)); + String size = (Long.toString(returnCursor.getLong(sizeIndex))); + //split2 = name.split("\\."); + //File file = new File(context.getCacheDir(), name); + File designPath = getRandomDocFileName(context, name); + try { + InputStream inputStream = context.getContentResolver().openInputStream(uri); + FileOutputStream outputStream = new FileOutputStream(designPath); + int read = 0; + int maxBufferSize = 1024 * 1024; + int bytesAvailable = inputStream.available(); + + //int bufferSize = 1024; + int bufferSize = Math.min(bytesAvailable, maxBufferSize); + + final byte[] buffers = new byte[bufferSize]; + while ((read = inputStream.read(buffers)) != -1) { + outputStream.write(buffers, 0, read); + } + inputStream.close(); + outputStream.close(); + } catch (Exception e) { + printLogConsole("##isGoogleDriveUri", "Exception------>" + e.getMessage()); + } + return designPath.getPath(); + } + + + /** + * Declaration getExternalSdCardPath(Context context) + * Description This method used save local path file and return file path name + * Declared In RealDocPathUtil.java + * + * @param context - Contains Context + */ + + public static String getExternalSdCardPath(Context context) { + File[] Dirs = ContextCompat.getExternalFilesDirs(context, null); + File path = (Dirs[1]); + String pathSD = Dirs[1].toString(); + int firstOpen = pathSD.indexOf("/"); + int secondOpen = pathSD.indexOf("/", firstOpen + 1); + int thirdOpen = pathSD.indexOf("/", secondOpen + 1); + return pathSD.substring(firstOpen, thirdOpen + 1); + //path = new File(filename); + } + + + /** + * @return - file path + * method used to generate full file path using selected file name + */ + public static File getRandomDocFileName(Context context, String fileName) { + File file = null; + File mediaStorageDir = context.getFilesDir(); + if (!mediaStorageDir.exists()) { + if (!mediaStorageDir.mkdirs()) { + return file; + } + } + int random = new Random().nextInt(6997); + String mImageName = "Braver_DOC".concat("_") + random + fileName; + return new File(mediaStorageDir.getPath() + "/" + mImageName); + + } + + + /*** + * Used for Android Q+ + * @param uri - ~ + * @param newDirName if you want to create a directory, you can set this variable + * @return - ~ + */ + private static String copyFileToInternalStorage(Context context, Uri uri, String newDirName) { + Uri returnUri = uri; + + Cursor returnCursor = context.getContentResolver().query(returnUri, new String[]{ + OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE + }, null, null, null); + /* + * Get the column indexes of the data in the Cursor, + * * move to the first row in the Cursor, get the data, + * * and display it. + * */ + int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); + int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE); + returnCursor.moveToFirst(); + String name = (returnCursor.getString(nameIndex)); + String size = (Long.toString(returnCursor.getLong(sizeIndex))); + File output; + if (!newDirName.equals("")) { + File dir = new File(context.getFilesDir() + "/" + newDirName); + if (!dir.exists()) { + dir.mkdir(); + } + output = new File(context.getFilesDir() + "/" + newDirName + "/" + name); + } else { + output = new File(context.getFilesDir() + "/" + name); + } + try { + InputStream inputStream = context.getContentResolver().openInputStream(uri); + FileOutputStream outputStream = new FileOutputStream(output); + int read = 0; + int bufferSize = 1024; + final byte[] buffers = new byte[bufferSize]; + while ((read = inputStream.read(buffers)) != -1) { + outputStream.write(buffers, 0, read); + } + inputStream.close(); + outputStream.close(); + } catch (Exception e) { + printLogConsole("##saveDocToExternalStorage", "Exception------>" + e.getMessage()); + } + return output.getPath(); + } + +} + diff --git a/picker/src/main/java/com/braver/tool/picker/BraveFilePicker.java b/picker/src/main/java/com/braver/tool/picker/BraveFilePicker.java new file mode 100644 index 0000000..63ad7d2 --- /dev/null +++ b/picker/src/main/java/com/braver/tool/picker/BraveFilePicker.java @@ -0,0 +1,52 @@ +package com.braver.tool.picker; + +import android.content.Context; +import android.net.Uri; + +import java.io.File; +import java.io.IOException; + +public class BraveFilePicker { + private Context context; + private boolean isCompressImage = false; + private boolean isTrimVide = false; + //private String fileType; + private BraveFileType braveFileType; + private String destinationFilePath; + private Uri contentUri; + + public BraveFilePicker setActivity(Context ctx) { + this.context = ctx; + return this; + } + + public BraveFilePicker setIsCompressImage(boolean compressImage) { + this.isCompressImage = compressImage; + return this; + } + + public BraveFilePicker setIsTrimVide(boolean trimVide) { + isTrimVide = trimVide; + return this; + } + + public BraveFilePicker setFileType(BraveFileType fileType) { + this.braveFileType = fileType; + return this; + } + + public BraveFilePicker setDestinationFilePath(String destinationFilePath) { + this.destinationFilePath = destinationFilePath; + return this; + } + + public BraveFilePicker setContentUri(Uri uri) { + this.contentUri = uri; + return this; + } + + public File getSourceFile() throws IOException { + return BraveFileGetter.getSourceFileFromUri(context, isCompressImage, isTrimVide, braveFileType, destinationFilePath, contentUri); + } + +} diff --git a/picker/src/main/java/com/braver/tool/picker/BraveFileType.java b/picker/src/main/java/com/braver/tool/picker/BraveFileType.java new file mode 100644 index 0000000..f532166 --- /dev/null +++ b/picker/src/main/java/com/braver/tool/picker/BraveFileType.java @@ -0,0 +1,5 @@ +package com.braver.tool.picker; + +public enum BraveFileType { + DEFAULT, IMAGE, VIDEO, DOCS +} diff --git a/picker/src/main/java/com/braver/tool/picker/BraveFileUtils.java b/picker/src/main/java/com/braver/tool/picker/BraveFileUtils.java new file mode 100644 index 0000000..3caed93 --- /dev/null +++ b/picker/src/main/java/com/braver/tool/picker/BraveFileUtils.java @@ -0,0 +1,291 @@ +package com.braver.tool.picker; + +import android.content.ContentUris; +import android.content.Context; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Matrix; +import android.net.Uri; +import android.provider.DocumentsContract; +import android.provider.MediaStore; +import android.util.Log; + +import androidx.exifinterface.media.ExifInterface; + +import com.braver.tool.picker.image.ImageUtil; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.channels.FileChannel; +import java.text.DecimalFormat; +import java.util.Random; + +import static com.braver.tool.picker.BraveFilePath.getExternalSdCardPath; + +public class BraveFileUtils { + public static final String IS_IMAGE_FILE = "is_image_file"; + public static final String IMAGE_PATH = "image_path"; + public static final String VIDEO_PATH = "video_path"; + private static final String TAG = BraveFileUtils.class.getSimpleName(); + public static boolean IS_LOG_ENABLED = true; + public static final String PREVIEW_IMAGE_FILE_PATH = "preview_image_file_path"; + public static final String PREVIEW_VIDEO_FILE_PATH = "preview_video_file_path"; + + /** + * @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_LOG_ENABLED) { + Log.d("##@@##" + tag, "--------->" + msg); + } + } + + public static File getRandomDocFileName(Context context, String extension) { + File file = null; + File mediaStorageDir = context.getFilesDir(); + if (!mediaStorageDir.exists()) { + if (!mediaStorageDir.mkdirs()) { + return file; + } + } + int random = new Random().nextInt(7997); + String mImageName = "Braver_DOC".concat("_") + random + "." + extension; + return new File(mediaStorageDir.getPath() + "/" + mImageName); + } + + public static File getRandomImageFileName(Context context) { + File file = null; + File mediaStorageDir = context.getFilesDir(); + if (!mediaStorageDir.exists()) { + if (!mediaStorageDir.mkdirs()) { + return file; + } + } + int random = new Random().nextInt(8997); + String mImageName = "Braver_IMG".concat("_") + random + ".jpg"; + return new File(mediaStorageDir.getPath() + "/" + mImageName); + } + + public static File getRandomRecordedVideoFileName(Context context) { + File file = null; + File mediaStorageDir = context.getFilesDir(); + if (!mediaStorageDir.exists()) { + if (!mediaStorageDir.mkdirs()) { + return file; + } + } + return new File(mediaStorageDir.getPath()); + } + + + public static void saveBitmapToExternalStorage(Context context, Bitmap bitmap, File myPath) { + FileOutputStream fileOutputStream = null; + try { + fileOutputStream = new FileOutputStream(myPath); + //String fileName = Utils.getMessageFileName(myPath.getAbsolutePath()); + //fileOutputStream = context.openFileOutput(fileName, Context.MODE_PRIVATE); + bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fileOutputStream); + } catch (Exception e) { + printLogConsole(TAG + "##saveBitmapToExternalStorage", "Exception------>" + e.getMessage()); + } finally { + try { + if (fileOutputStream != null) { + fileOutputStream.close(); + } + } catch (IOException e) { + printLogConsole(TAG + "##saveBitmapToExternalStorage", "Exception------>" + e.getMessage()); + } + } + } + + /** + * Declaration saveDocToLocalPath(Context context, String filePath) + * Description This method used save local path file and return name + * Declared In RealDocPathUtil.java + * + * @param filePath - Contains document file for the particular image path + */ + + public static String saveDocToExternalStorage(Context context, String filePath) { + try { + String splitName; + if (filePath.contains(":")) { + final String[] split = filePath.split(":"); + filePath = getExternalSdCardPath(context) + split[1]; + } + splitName = filePath.substring(filePath.lastIndexOf('/') + 1); + String extension = splitName.substring(splitName.lastIndexOf(".")); + File designPath = getRandomDocFileName(context, extension); + File sourcePath = new File(filePath); + FileChannel in = new FileInputStream(sourcePath).getChannel(); + FileChannel out = new FileOutputStream(designPath).getChannel(); + try { + in.transferTo(0, in.size(), out); + } catch (Exception e) { + printLogConsole(TAG + "##saveDocToExternalStorage", "Exception------>" + e.getMessage()); + } finally { + if (in != null) in.close(); + if (out != null) out.close(); + } + return designPath.getAbsolutePath(); + } catch (Exception ioe) { + printLogConsole(TAG + "##saveDocToExternalStorage", "Exception------>" + ioe.getMessage()); + return ""; + } + } + + /** + * Method used to get the real path from uri file + * + * @param context context + * @return filepath + */ + + public static String getFilePathFromContentUri(Uri uri, Context context) { + String filePath; + Uri contentUri = null; + String id; + try { + id = DocumentsContract.getDocumentId(uri); + } catch (IllegalArgumentException e) { + id = ""; + printLogConsole(TAG, "Uri Null"); + } + if (!id.isEmpty()) { + boolean numeric = id.matches("-?\\d+(\\.\\d+)?"); + if (numeric) { + contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.parseLong(id)); + } + } else { + contentUri = uri; + } + + String[] projection = {MediaStore.Files.FileColumns.DATA}; + Cursor cursor = null; + if (contentUri != null) { + cursor = context.getContentResolver().query(contentUri, projection, null, null, null); + } + if (cursor != null) { + cursor.moveToFirst(); + int column_index = cursor.getColumnIndexOrThrow(projection[0]); + filePath = cursor.getString(column_index); + cursor.close(); + } else { + filePath = uri.getPath(); + } + return filePath; + } + + + /** + * Declaration checkIsBitMapRotated(File imageFile) + * Some devices returns wrong(auto - rotated) images + * Description This method used to get actual captured image from device . + * Declared In Utils.java + * + * @param imageFile - Contains image file for the particular image path + */ + + public static Bitmap checkIsBitMapRotated(Context context, boolean isCompress, File imageFile) { + try { + boolean isComp = true; + File compressedImageFile; + long bytes = imageFile.length(); + if (bytes > 5242880 || isCompress) { // Size must below 5MB + compressedImageFile = ImageUtil.compressImage(imageFile, getRandomImageFileName(context).getAbsolutePath()); + } else { + isComp = false; + compressedImageFile = imageFile; + } + printLogConsole(TAG, "Image File Size----->" + getFileSizeFromFilePath(compressedImageFile.length())); + Bitmap photoBitmap = BitmapFactory.decodeFile(compressedImageFile.getPath()); + int imageRotation = getImageRotation(compressedImageFile); + if (imageRotation != 0) { + photoBitmap = getBitmapRotatedByDegree(photoBitmap, imageRotation); + } + if (!isComp) { + photoBitmap = Bitmap.createScaledBitmap(photoBitmap, photoBitmap.getWidth(), photoBitmap.getHeight(), true); + } + return photoBitmap; + } catch (IOException e) { + printLogConsole(TAG, "checkIsBitMapRotated()------>Exception----->" + e.getMessage()); + return null; + } + } + + + /** + * Declaration getImageRotation(final File imageFile + * Description This method used to set ExifInterface + * Declared In Utils.java + * + * @param imageFile - Contains image file for the particular image path + */ + private static int getImageRotation(final File imageFile) { + ExifInterface exif = null; + int exifRotation = 0; + try { + exif = new ExifInterface(imageFile.getPath()); + exifRotation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); + } catch (IOException e) { + e.printStackTrace(); + } + if (exif == null) return 0; + else return exifToDegrees(exifRotation); + } + + /** + * Declaration exifToDegrees(int rotation) + * Description This method used to set ExifInterface with rotation degree + * Declared In Utils.java + * + * @param rotation - Contains rotation degree for the captured image + */ + private static int exifToDegrees(int rotation) { + 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; + return 0; + } + + /** + * Declaration Bitmap getBitmapRotatedByDegree(Bitmap bitmap, int rotationDegree) + * Description This method used to set bitmap height and width + * Declared In Utils.java + * + * @param bitmap - Contains captured image bitmap + * @param rotationDegree - Contains captured image rotation degree + */ + private static Bitmap getBitmapRotatedByDegree(Bitmap bitmap, int rotationDegree) { + Matrix matrix = new Matrix(); + matrix.preRotate(rotationDegree); + return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); + } + + /** + * @param size - long data + * @return - String data file size + * Method used to get ile size + */ + public static String getFileSizeFromFilePath(long size) { + if (size <= 0L) { + return "0"; + } else { + String[] units = new String[]{"B", "KB", "MB", "GB", "TB"}; + double var5 = (double) size; + double var10000 = Math.log10(var5); + var5 = 1024.0D; + int digitGroups = (int) (var10000 / Math.log10(var5)); + StringBuilder var10 = new StringBuilder(); + DecimalFormat var10001 = new DecimalFormat("#,##0.#"); + double var10002 = (double) size; + var5 = 1024.0D; + return var10.append(var10001.format(var10002 / Math.pow(var5, digitGroups))).append(" ").append(units[digitGroups]).toString(); + } + } +} diff --git a/picker/src/main/java/com/braver/tool/picker/CameraActivity.java b/picker/src/main/java/com/braver/tool/picker/CameraActivity.java new file mode 100644 index 0000000..b743c85 --- /dev/null +++ b/picker/src/main/java/com/braver/tool/picker/CameraActivity.java @@ -0,0 +1,778 @@ +package com.braver.tool.picker; + +import android.annotation.SuppressLint; +import android.app.ProgressDialog; +import android.content.Context; +import android.content.Intent; +import android.content.res.Configuration; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Matrix; +import android.hardware.Camera; +import android.media.CamcorderProfile; +import android.media.MediaRecorder; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.SystemClock; +import android.util.DisplayMetrics; +import android.util.Log; +import android.view.MotionEvent; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.View; +import android.view.WindowManager; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.appcompat.app.AppCompatActivity; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.Locale; +import java.util.Random; + +import static android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE; +import static android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO; + +@SuppressWarnings("ALL") +public class CameraActivity extends AppCompatActivity implements SurfaceHolder.Callback, View.OnClickListener { + private static final String PROCESSING_VIDEO = "Processing a video..."; + private final String TAG = CameraActivity.class.getSimpleName(); + private final Handler customHandler = new Handler(); + //private OrientationEventListener myOrientationEventListener; + //private final int iOrientation = 0; + private int flag = 0; + private int flashType = 1; + private int mOrientation = 90; + private SurfaceHolder surfaceHolder; + private Camera camera; + private Camera.PictureCallback jpegCallback; + private MediaRecorder mediaRecorder; + private SurfaceView imgSurface; + private ImageView imgCapture; + private ImageView imgFlashOnOff; + private ImageView imgSwipeCamera; + private TextView textCounter; + private TextView hintTextView; + private BackgroundTaskForSavingVideo saveVideoTask = null; + private File recordedVideoFilePath = null; + private File recordedImageFilePath = null; + private long startTime = SystemClock.uptimeMillis(); + private final Runnable updateTimerThread = new Runnable() { + @SuppressLint("SetTextI18n") + public void run() { + long timeInMilliseconds = SystemClock.uptimeMillis() - startTime; + long timeSwapBuff = 0L; + long updatedTime = timeSwapBuff + timeInMilliseconds; + int seconds = (int) (updatedTime / 1000); + int minutes = seconds / 60; + seconds = seconds % 60; + textCounter.setText(String.format(Locale.getDefault(), "%02d", minutes) + ":" + String.format(Locale.getDefault(), "%02d", seconds)); + customHandler.postDelayed(this, 0); + + } + + }; + private BackgroundTaskForSavingImage savePicTask; + private String mediaFileName = null; + private Context context; + private String bVideoName = ""; + + /** + * this method used to initiate declare and initiate for Message attachment Camera and video activity + * + * @param savedInstanceState - this object is responsible to create the activity. + */ + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.camera_activity); + initializeViews(); + } + + /** + * Handling to mapping the xml view from{@link R.layout#activity_camera} + */ + private void initializeViews() { + context = getApplicationContext(); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + initControls(); + initCameraActivity(); + } + + /** + * Method used to call Camera activity's lifecycle after check permission + */ + private void initCameraActivity() { + File f = BraveFileUtils.getRandomRecordedVideoFileName(CameraActivity.this); + //create a folder to get image + recordedVideoFilePath = new File(f.getPath()); + if (!recordedVideoFilePath.exists()) { + recordedVideoFilePath.mkdirs(); + } + //capture image on callback + captureImageCallback(); + // + if (camera != null) { + Camera.CameraInfo info = new Camera.CameraInfo(); + if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { + imgFlashOnOff.setVisibility(View.GONE); + } + } + } + + /** + * Called when Activity content is not visible because user resume previous activity + */ + @Override + protected void onPause() { + super.onPause(); + try { + if (customHandler != null) customHandler.removeCallbacksAndMessages(null); + releaseMediaRecorder(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Called when back navigation + */ + @Override + public void onBackPressed() { + super.onBackPressed(); + cancelSavePicTaskIfNeed(); + } + + /** + * This is called immediately after the surface is first created. + * + * @param arg0- The SurfaceHolder whose surface is being created + */ + @Override + public void surfaceCreated(SurfaceHolder arg0) { + try { + if (flag == 0) { + camera = Camera.open(0); + } else { + camera = Camera.open(1); + } + } catch (RuntimeException e) { + e.printStackTrace(); + return; + } + try { + Camera.Parameters param; + param = camera.getParameters(); + final List modes = param.getSupportedFocusModes(); + if (modes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) { + param.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE); + } else if (modes.contains(Camera.Parameters.FOCUS_MODE_MACRO)) { + param.setFocusMode(Camera.Parameters.FOCUS_MODE_MACRO); + } else if (modes.contains(Camera.Parameters.FOCUS_MODE_FIXED)) { + param.setFocusMode(Camera.Parameters.FOCUS_MODE_FIXED); + } else if (modes.contains(Camera.Parameters.FOCUS_MODE_INFINITY)) { + param.setFocusMode(Camera.Parameters.FOCUS_MODE_INFINITY); + } + List sizes = param.getSupportedPreviewSizes(); + //get diff to get perfect preview sizes + DisplayMetrics displaymetrics = new DisplayMetrics(); + getWindowManager().getDefaultDisplay().getMetrics(displaymetrics); + int height = displaymetrics.heightPixels; + int width = displaymetrics.widthPixels; + long diff = (height * 1000 / width); + long cdistance = Integer.MAX_VALUE; + int idx = 0; + for (int i = 0; i < sizes.size(); i++) { + long value = (long) (sizes.get(i).width * 1000) / sizes.get(i).height; + if (value > diff && value < cdistance) { + idx = i; + cdistance = value; + } + } + Camera.Size cs = sizes.get(idx); + param.setPreviewSize(cs.width, cs.height); + param.setPictureSize(cs.width, cs.height); + camera.setParameters(param); + setCameraDisplayOrientation(0); + camera.setPreviewDisplay(surfaceHolder); + camera.startPreview(); + if (flashType == 1) { + param.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO); + imgFlashOnOff.setImageResource(R.drawable.ic_automatic_flash); + } else if (flashType == 2) { + param.setFlashMode(Camera.Parameters.FLASH_MODE_ON); + Camera.Parameters params; + if (camera != null) { + params = camera.getParameters(); + + if (params != null) { + List supportedFlashModes = params.getSupportedFlashModes(); + if (supportedFlashModes != null) { + if (supportedFlashModes.contains(Camera.Parameters.FLASH_MODE_TORCH)) { + param.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH); + } else if (supportedFlashModes.contains(Camera.Parameters.FLASH_MODE_ON)) { + param.setFlashMode(Camera.Parameters.FLASH_MODE_ON); + } + } + } + } + imgFlashOnOff.setImageResource(R.drawable.ic_flash_on); + } else if (flashType == 3) { + param.setFlashMode(Camera.Parameters.FLASH_MODE_OFF); + imgFlashOnOff.setImageResource(R.drawable.ic_flash_off); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * This is called immediately before a surface is being destroyed. + * + * @param arg0-The SurfaceHolder whose surface is being destroyed. + */ + @Override + public void surfaceDestroyed(SurfaceHolder arg0) { + try { + camera.stopPreview(); + camera.release(); + camera = null; + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * This is called immediately after any structural changes (format or size) have been made to the surface. + * You should at this point update the imagery in the surface. + * This method is always called at least once, after surfaceCreated(SurfaceHolder). + * + * @param surfaceHolder -The SurfaceHolder whose surface has changed. + * @param i -int: The new PixelFormat of the surface. + * @param i1 -int: The new width of the surface. Value is 0 or greater + * @param i2 -int:The new height of the surface. Value is 0 or greater + */ + @Override + public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) { + refreshCamera(); + Camera.Parameters parameters = camera.getParameters(); + parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE); + camera.setParameters(parameters); + camera.startPreview(); + } + + /** + * onClick is used to define the function to be invoked in the activity when the button is clicked. + * + * @param v - The view that was clicked. It is the view that was clicked. + */ + @Override + public void onClick(View v) { + int id = v.getId(); + if (id == R.id.imgFlashOnOff) { + flashToggle(); + } else if (id == R.id.imgChangeCamera) { + switchCameraType(); + } + } + + /** + * This Method is used to initialize all views + * Method call from {@link #onCreate(Bundle)} + */ + private void initControls() { + mediaRecorder = new MediaRecorder(); + imgSurface = findViewById(R.id.imgSurface); + textCounter = findViewById(R.id.textCounter); + imgCapture = findViewById(R.id.imgCapture); + imgFlashOnOff = findViewById(R.id.imgFlashOnOff); + imgSwipeCamera = findViewById(R.id.imgChangeCamera); + textCounter.setVisibility(View.GONE); + hintTextView = findViewById(R.id.hintTextView); + surfaceHolder = imgSurface.getHolder(); + surfaceHolder.addCallback(this); + surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); + imgSwipeCamera.setOnClickListener(this); + activeCameraCapture(); + imgFlashOnOff.setOnClickListener(this); + } + + /** + * This method is used for handle camera capture and video recording + * Method call from {@link #initControls()},{@link #savePicTask} + */ + @SuppressLint("ClickableViewAccessibility") + private void activeCameraCapture() { + if (imgCapture != null) { + imgCapture.setAlpha(1.0f); + imgCapture.setOnLongClickListener(v -> { + hintTextView.setVisibility(View.INVISIBLE); + try { + if (prepareMediaRecorder()) { + //myOrientationEventListener.disable(); + mediaRecorder.start(); + startTime = SystemClock.uptimeMillis(); + customHandler.postDelayed(updateTimerThread, 0); + } else { + return false; + } + } catch (Exception e) { + e.printStackTrace(); + } + textCounter.setVisibility(View.VISIBLE); + imgSwipeCamera.setVisibility(View.GONE); + imgFlashOnOff.setVisibility(View.GONE); + imgCapture.setOnTouchListener((v1, event) -> { + if (event.getAction() == MotionEvent.ACTION_BUTTON_PRESS) { + return true; + } + if (event.getAction() == MotionEvent.ACTION_UP) { + hintTextView.setVisibility(View.VISIBLE); + cancelSaveVideoTaskIfNeed(); + saveVideoTask = new BackgroundTaskForSavingVideo(); + saveVideoTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); + return true; + } + return true; + }); + return true; + }); + imgCapture.setOnClickListener(v -> captureImage()); + } + } + + /** + * This method is used for handle front and back camera + * Method call from {@link #onClick(View)} ()} + */ + private void switchCameraType() { + camera.stopPreview(); + camera.release(); + if (flag == Camera.CameraInfo.CAMERA_FACING_BACK) { + flag = Camera.CameraInfo.CAMERA_FACING_FRONT; + imgFlashOnOff.setVisibility(View.GONE); + } else { + flag = Camera.CameraInfo.CAMERA_FACING_BACK; + imgFlashOnOff.setVisibility(View.VISIBLE); + } + camera = Camera.open(flag); + try { + camera.setPreviewDisplay(surfaceHolder); + } catch (IOException e) { + e.printStackTrace(); + } + camera.startPreview(); + Camera.Parameters cameraParameters = camera.getParameters(); + refreshCameraPreview(cameraParameters); + } + + /** + * switch image to on and off state just to indicate flashlight status. + * Method call from {@link #onClick(View)} + */ + private void flashToggle() { + if (flashType == 1) { + flashType = 2; + } else if (flashType == 2) { + flashType = 3; + } else if (flashType == 3) { + flashType = 1; + } + refreshCamera(); + } + + /** + * this method is used for get an image from the camera + * Method call from {@link #activeCameraCapture()} + */ + private void captureImage() { + camera.takePicture(null, null, jpegCallback); + inActiveCameraCapture(); + } + + /** + * This method is used for set alpha of the click actions + * Method call from {@link #captureImage()} + */ + private void inActiveCameraCapture() { + if (imgCapture != null) { + imgCapture.setAlpha(0.5f); + imgCapture.setOnClickListener(null); + } + } + + /** + * this method is used to get image,validate image rotation rotation and save local path as bitmap + * Method call from {@link #onCreate(Bundle)} + */ + private void captureImageCallback() { + surfaceHolder = imgSurface.getHolder(); + surfaceHolder.addCallback(this); + surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); + jpegCallback = (data, camera) -> { + if (data != null) { + int screenWidth = getResources().getDisplayMetrics().widthPixels; + int screenHeight = getResources().getDisplayMetrics().heightPixels; + Matrix mtx = new Matrix(); + Bitmap bm = BitmapFactory.decodeByteArray(data, 0, data.length); + int w = bm.getWidth(); + int h = bm.getHeight(); + if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT && flag == Camera.CameraInfo.CAMERA_FACING_BACK) { + mtx.postRotate(90); + // Rotating Bitmap + bm = Bitmap.createBitmap(bm, 0, 0, w, h, mtx, true); + } else if ((getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT && flag == Camera.CameraInfo.CAMERA_FACING_FRONT)) { + mtx.postRotate(-90); + mtx.postScale(-1, 1); + // Rotating Bitmap + bm = Bitmap.createBitmap(bm, 0, 0, w, h, mtx, true); + } else {// LANDSCAPE MODE + //No need to reverse width and height + bm = Bitmap.createScaledBitmap(bm, screenWidth, screenHeight, true); + } + refreshCamera(); + cancelSavePicTaskIfNeed(); + savePicTask = new BackgroundTaskForSavingImage(bm); + savePicTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); + } + }; + } + + /** + * This method is used to validate surface ,flash toggle and taken image surface refresh actions + * Method call from {@link #surfaceChanged(SurfaceHolder, int, int, int)},{@link #flashToggle()},{@link #captureImageCallback()} + */ + public void refreshCamera() { + if (surfaceHolder.getSurface() == null) { + return; + } + try { + camera.stopPreview(); + Camera.Parameters param = camera.getParameters(); + if (flag == 0) { + if (flashType == 1) { + param.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO); + imgFlashOnOff.setImageResource(R.drawable.ic_automatic_flash); + } else if (flashType == 2) { + param.setFlashMode(Camera.Parameters.FLASH_MODE_ON); + Camera.Parameters params; + if (camera != null) { + params = camera.getParameters(); + if (params != null) { + List supportedFlashModes = params.getSupportedFlashModes(); + if (supportedFlashModes != null) { + if (supportedFlashModes.contains(Camera.Parameters.FLASH_MODE_TORCH)) { + param.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH); + } else if (supportedFlashModes.contains(Camera.Parameters.FLASH_MODE_ON)) { + param.setFlashMode(Camera.Parameters.FLASH_MODE_ON); + } + } + } + } + imgFlashOnOff.setImageResource(R.drawable.ic_flash_on); + } else if (flashType == 3) { + param.setFlashMode(Camera.Parameters.FLASH_MODE_OFF); + imgFlashOnOff.setImageResource(R.drawable.ic_flash_off); + } + } + refreshCameraPreview(param); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * this method is used to refresh camera preview view + * + * @param param - It is a camera parameter + * Method call from {@link #refreshCamera()} + */ + private void refreshCameraPreview(Camera.Parameters param) { + try { + camera.setParameters(param); + setCameraDisplayOrientation(0); + camera.setPreviewDisplay(surfaceHolder); + camera.startPreview(); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * This method is used to handle camera rotation orientation + * + * @param cameraId - It is captured image id + * Method call from {@link #surfaceCreated(SurfaceHolder)},{@link #refreshCamera()} + */ + public void setCameraDisplayOrientation(int cameraId) { + Camera.CameraInfo info = new Camera.CameraInfo(); + Camera.getCameraInfo(cameraId, info); + int rotation = this.getWindowManager().getDefaultDisplay().getRotation(); + int degrees = 0; + switch (rotation) { + case Surface.ROTATION_0: + degrees = 0; + break; + case Surface.ROTATION_90: + degrees = 90; + break; + case Surface.ROTATION_180: + degrees = 180; + break; + case Surface.ROTATION_270: + degrees = 270; + break; + } + int result; + if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { + result = (info.orientation + degrees) % 360; + result = (360 - result) % 360; // compensate the mirror + } else { // back-facing + result = (info.orientation - degrees + 360) % 360; + } + Camera.Parameters p = null; + try { + p = camera.getParameters(); + } catch (Exception e) { + Log.d(TAG, e.getMessage() != null ? e.getMessage() : ""); + + } + camera.setDisplayOrientation(result); + List supportedFocusModes = null; + if (p != null) { + supportedFocusModes = p.getSupportedFocusModes(); + } + Camera.Parameters param = camera.getParameters(); + if (supportedFocusModes != null) { + if (supportedFocusModes.contains(FOCUS_MODE_CONTINUOUS_PICTURE)) { + param.setFocusMode(FOCUS_MODE_CONTINUOUS_PICTURE); + } else if (supportedFocusModes.contains(FOCUS_MODE_CONTINUOUS_VIDEO)) { + param.setFocusMode(FOCUS_MODE_CONTINUOUS_VIDEO); + } + } + camera.setParameters(param); + } + + /** + * @param bitmap -It is a captured image + * @return -Returns saved image local path + */ + private String saveCapturedImageToLocalPath(Bitmap bitmap) { + String imagePath = ""; + try { + if (bitmap != null) { + File tempFilePath = BraveFileUtils.getRandomImageFileName(this); + BraveFileUtils.saveBitmapToExternalStorage(context, bitmap, tempFilePath); + Bitmap imageBitMap = BraveFileUtils.checkIsBitMapRotated(context, true, tempFilePath); + if (imageBitMap != null) { + recordedImageFilePath = BraveFileUtils.getRandomImageFileName(this); + BraveFileUtils.saveBitmapToExternalStorage(context, imageBitMap, recordedImageFilePath); + } + } + } catch (Exception e) { + Log.d(TAG, e.getMessage() != null ? e.getMessage() : ""); + } + return imagePath; + } + + /** + * This method is used to cancel the camera actions + * Method call from {@link #onBackPressed()}, {@link #captureImageCallback()} + */ + private void cancelSavePicTaskIfNeed() { + if (savePicTask != null && savePicTask.getStatus() == AsyncTask.Status.RUNNING) { + savePicTask.cancel(true); + } + } + + + /** + * This method is used for recording video, validate orientation view and validate video size + * + * @return -It returns boolean value + */ + @SuppressLint("SimpleDateFormat") + protected boolean prepareMediaRecorder() { + mediaRecorder = new MediaRecorder(); + camera.stopPreview(); + camera.unlock(); + mediaRecorder.setCamera(camera); + mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); + mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); + if (flag == 1) { + mediaRecorder.setProfile(CamcorderProfile.get(1, CamcorderProfile.QUALITY_480P)); + } else { + mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_480P)); + } + mediaRecorder.setPreviewDisplay(surfaceHolder.getSurface()); + mediaRecorder.setOrientationHint(mOrientation); + if (Build.MODEL.equalsIgnoreCase("Nexus 6") && flag == 1) { + mediaRecorder.setOrientationHint(mOrientation); + } else if (mOrientation == 90 && flag == 1) { + mOrientation = 270; + mediaRecorder.setOrientationHint(mOrientation); + } else if (flag == 1) { + mediaRecorder.setOrientationHint(mOrientation); + } + int random = new Random().nextInt(6997); + bVideoName = "Braver_VID".concat("_") + random + ".mp4"; + mediaRecorder.setOutputFile(recordedVideoFilePath.getAbsolutePath() + "/" + bVideoName); + mediaRecorder.setOnInfoListener((mr, what, extra) -> { + if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED) { + long downTime = 0; + long eventTime = 0; + //float x = 0.0f; + //float y = 0.0f; + int metaState = 0; + MotionEvent motionEvent = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, 0, 0, metaState); + imgCapture.dispatchTouchEvent(motionEvent); + Toast.makeText(CameraActivity.this, "You reached to Maximum(17MB) video size.", Toast.LENGTH_SHORT).show(); + } + }); + mediaRecorder.setMaxFileSize(1000 * 17 * 1000); + try { + mediaRecorder.prepare(); + } catch (Exception e) { + releaseMediaRecorder(); + Log.d(TAG, e.getMessage() != null ? e.getMessage() : ""); + return false; + } + return true; + + } + + /** + * This method is used to navigates saved video saved path in Message fragment + * + * @param videoPath Method call from {@link #saveVideoTask} + */ + public void onVideoSendDialog() { + Intent intent = new Intent(); + intent.putExtra(BraveFileUtils.IS_IMAGE_FILE, false); + intent.putExtra(BraveFileUtils.VIDEO_PATH, recordedVideoFilePath.getAbsolutePath() + "/" + bVideoName); + setResult(RESULT_OK, intent); + finish(); + } + + /** + * This method is used for cancel video recording + * Method call from {@link #activeCameraCapture()} ()} + */ + private void cancelSaveVideoTaskIfNeed() { + if (saveVideoTask != null && saveVideoTask.getStatus() == AsyncTask.Status.RUNNING) { + saveVideoTask.cancel(true); + } + } + + /** + * This method is used for clear recorder configuration and release the recorder object + * Method call from {@link #onPause()}, {@link #prepareMediaRecorder(), {@link #saveVideoTask}} + */ + private void releaseMediaRecorder() { + if (mediaRecorder != null) { + mediaRecorder.reset(); // clear recorder configuration + mediaRecorder.release(); // release the recorder object + mediaRecorder = new MediaRecorder(); + } + } + + //------------------SURFACE OVERRIDE METHODS END--------------------// + + private void sendImagePath() { + Intent intent = new Intent(); + intent.putExtra(BraveFileUtils.IS_IMAGE_FILE, true); + intent.putExtra(BraveFileUtils.IMAGE_PATH, recordedImageFilePath.getAbsolutePath()); + setResult(RESULT_OK, intent); + finish(); + } + + /** + * This method is used to save picture in local path + * * Method call from {@link #captureImageCallback()} + */ + @SuppressLint("StaticFieldLeak") + private class BackgroundTaskForSavingImage extends AsyncTask { + private final Bitmap data; + + public BackgroundTaskForSavingImage(Bitmap data) { + this.data = data; + } + + @Override + protected String doInBackground(Void... params) { + try { + return saveCapturedImageToLocalPath(data); + } catch (Exception e) { + Log.d(TAG, e.getMessage() != null ? e.getMessage() : ""); + return null; + } + } + + @Override + protected void onPostExecute(String result) { + activeCameraCapture(); + sendImagePath(); + } + } + + /** + * This method is used for save video in local path + * Method call from {@link #activeCameraCapture()} + */ + + @SuppressLint("StaticFieldLeak") + private class BackgroundTaskForSavingVideo extends AsyncTask { + ProgressDialog progressDialog = null; + + @SuppressLint("ClickableViewAccessibility") + @Override + protected void onPreExecute() { + progressDialog = new ProgressDialog(CameraActivity.this); + progressDialog.setMessage(PROCESSING_VIDEO); + progressDialog.show(); + imgCapture.setOnTouchListener(null); + textCounter.setVisibility(View.GONE); + imgSwipeCamera.setVisibility(View.VISIBLE); + imgFlashOnOff.setVisibility(View.VISIBLE); + super.onPreExecute(); + + } + + @Override + protected Void doInBackground(Void... params) { + try { + try { + customHandler.removeCallbacksAndMessages(null); + mediaRecorder.stop(); + releaseMediaRecorder(); + } catch (Exception e) { + Log.d(TAG, e.getMessage() != null ? e.getMessage() : ""); + } + } catch (Exception e) { + Log.d(TAG, e.getMessage() != null ? e.getMessage() : ""); + + } + return null; + } + + @Override + protected void onPostExecute(Void aVoid) { + super.onPostExecute(aVoid); + if (progressDialog != null) { + if (progressDialog.isShowing()) { + progressDialog.dismiss(); + } + } + if (recordedVideoFilePath != null && recordedVideoFilePath.getAbsolutePath() != null) { + onVideoSendDialog(); + } + } + } +} \ No newline at end of file diff --git a/picker/src/main/java/com/braver/tool/picker/image/ImageUtil.java b/picker/src/main/java/com/braver/tool/picker/image/ImageUtil.java new file mode 100644 index 0000000..dc38af5 --- /dev/null +++ b/picker/src/main/java/com/braver/tool/picker/image/ImageUtil.java @@ -0,0 +1,104 @@ +package com.braver.tool.picker.image; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Matrix; +import android.media.ExifInterface; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + +public class ImageUtil { + + private ImageUtil() { + + } + + /** + * @param imageFile - source file + * @param destinationPath - design path of compressed image + * @return - file path + * @throws IOException - error message + * Method used to compress image + */ + public static File compressImage(File imageFile, String destinationPath) throws IOException { + FileOutputStream fileOutputStream = null; + File file = new File(destinationPath).getParentFile(); + if (!file.exists()) { + file.mkdirs(); + } + try { + fileOutputStream = new FileOutputStream(destinationPath); + // write the compressed bitmap at the destination specified by destinationPath. + decodeSampledBitmapFromFile(imageFile).compress(Bitmap.CompressFormat.JPEG, 80, fileOutputStream); + } finally { + if (fileOutputStream != null) { + fileOutputStream.flush(); + fileOutputStream.close(); + } + } + + return new File(destinationPath); + } + + + /** + * @param imageFile - source file + * @return - image as bitmap + * @throws IOException + */ + public static Bitmap decodeSampledBitmapFromFile(File imageFile) throws IOException { + //Bitmap bitmap = BitmapFactory.decodeFile(imageFile.getPath()); + //int reqWidth = bitmap.getWidth(); + //int reqHeight = bitmap.getHeight(); + int reqWidth = 612; + int reqHeight = 816; + // First decode with inJustDecodeBounds=true to check dimensions + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeFile(imageFile.getAbsolutePath(), options); + // Calculate inSampleSize + options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); + // Decode bitmap with inSampleSize set + options.inJustDecodeBounds = false; + Bitmap scaledBitmap = BitmapFactory.decodeFile(imageFile.getAbsolutePath(), options); + //check the rotation of the image and display it properly + ExifInterface exif; + exif = new ExifInterface(imageFile.getAbsolutePath()); + int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0); + Matrix matrix = new Matrix(); + if (orientation == 6) { + matrix.postRotate(90); + } else if (orientation == 3) { + matrix.postRotate(180); + } else if (orientation == 8) { + matrix.postRotate(270); + } + scaledBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight(), matrix, true); + return scaledBitmap; + } + + /** + * @param options - BitmapFactory + * @param reqWidth - image width + * @param reqHeight - image height + * @return - image size + */ + private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { + // Raw height and width of image + final int height = options.outHeight; + final int width = options.outWidth; + int inSampleSize = 1; + if (height > reqHeight || width > reqWidth) { + final int halfHeight = height / 2; + final int halfWidth = width / 2; + // Calculate the largest inSampleSize value that is a power of 2 and keeps both + // height and width larger than the requested height and width. + while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) { + inSampleSize *= 2; + } + } + return inSampleSize; + } +} diff --git a/picker/src/main/res/drawable/bg_record_video.xml b/picker/src/main/res/drawable/bg_record_video.xml new file mode 100644 index 0000000..abaa786 --- /dev/null +++ b/picker/src/main/res/drawable/bg_record_video.xml @@ -0,0 +1,15 @@ + + + + + + + + + \ No newline at end of file diff --git a/picker/src/main/res/drawable/ic_automatic_flash.xml b/picker/src/main/res/drawable/ic_automatic_flash.xml new file mode 100644 index 0000000..fd35728 --- /dev/null +++ b/picker/src/main/res/drawable/ic_automatic_flash.xml @@ -0,0 +1,9 @@ + + + diff --git a/picker/src/main/res/drawable/ic_capture_image_icon.xml b/picker/src/main/res/drawable/ic_capture_image_icon.xml new file mode 100644 index 0000000..8e0604b --- /dev/null +++ b/picker/src/main/res/drawable/ic_capture_image_icon.xml @@ -0,0 +1,9 @@ + + + diff --git a/picker/src/main/res/drawable/ic_flash_off.xml b/picker/src/main/res/drawable/ic_flash_off.xml new file mode 100644 index 0000000..3fe34c8 --- /dev/null +++ b/picker/src/main/res/drawable/ic_flash_off.xml @@ -0,0 +1,12 @@ + + + + diff --git a/picker/src/main/res/drawable/ic_flash_on.xml b/picker/src/main/res/drawable/ic_flash_on.xml new file mode 100644 index 0000000..ceb642f --- /dev/null +++ b/picker/src/main/res/drawable/ic_flash_on.xml @@ -0,0 +1,9 @@ + + + diff --git a/picker/src/main/res/drawable/ic_rotate_arrow_icon.xml b/picker/src/main/res/drawable/ic_rotate_arrow_icon.xml new file mode 100644 index 0000000..16cca97 --- /dev/null +++ b/picker/src/main/res/drawable/ic_rotate_arrow_icon.xml @@ -0,0 +1,12 @@ + + + + diff --git a/picker/src/main/res/drawable/ic_solid_dot_circle_red.xml b/picker/src/main/res/drawable/ic_solid_dot_circle_red.xml new file mode 100644 index 0000000..d895068 --- /dev/null +++ b/picker/src/main/res/drawable/ic_solid_dot_circle_red.xml @@ -0,0 +1,9 @@ + + + diff --git a/picker/src/main/res/drawable/ic_solid_dot_circle_white.xml b/picker/src/main/res/drawable/ic_solid_dot_circle_white.xml new file mode 100644 index 0000000..31bc5ad --- /dev/null +++ b/picker/src/main/res/drawable/ic_solid_dot_circle_white.xml @@ -0,0 +1,9 @@ + + + diff --git a/picker/src/main/res/layout/camera_activity.xml b/picker/src/main/res/layout/camera_activity.xml new file mode 100644 index 0000000..a469dd7 --- /dev/null +++ b/picker/src/main/res/layout/camera_activity.xml @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/picker/src/main/res/values/color.xml b/picker/src/main/res/values/color.xml new file mode 100644 index 0000000..d6be31f --- /dev/null +++ b/picker/src/main/res/values/color.xml @@ -0,0 +1,4 @@ + + + #ffffff + \ No newline at end of file diff --git a/picker/src/test/java/com/braver/tool/picker/ExampleUnitTest.java b/picker/src/test/java/com/braver/tool/picker/ExampleUnitTest.java new file mode 100644 index 0000000..8a8634f --- /dev/null +++ b/picker/src/test/java/com/braver/tool/picker/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package com.braver.tool.picker; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..82c5358 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +include ':app', ':picker'