I use camera2 for capture video inside the app.
It works fine from api 26 and above, but it is now working in api 25 and 24.
the preview of video before starting video capture is ok, but after i click the record video, it crashed.
My code:
private var PERMISSIONS_REQUIRED = arrayOf(
Manifest.permission.CAMERA,
Manifest.permission.RECORD_AUDIO
)
class MainActivity : AppCompatActivity() {
private lateinit var activityMainBinding: ActivityMainBinding
private val outputDirectory: String by lazy {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
//TODO("Change save folder name")
"${Environment.DIRECTORY_DCIM}/InAppCamera/"
} else {
//TODO("Change save folder name")
"${getExternalFilesDir(Environment.DIRECTORY_DCIM)}/InAppCamera/"
}
}
private var camera: Camera? = null
private var cameraProvider: ProcessCameraProvider? = null
private var preview: Preview? = null
private var videoCapture: VideoCapture? = null
private var lensFacing = CameraSelector.DEFAULT_FRONT_CAMERA
private var isRecording = false
private val animateRecord by lazy {
ObjectAnimator.ofFloat(activityMainBinding.btnRecordVideo, View.ALPHA, 1f, 0.5f).apply {
repeatMode = ObjectAnimator.REVERSE
repeatCount = ObjectAnimator.INFINITE
doOnCancel { activityMainBinding.btnRecordVideo.alpha = 1f }
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(activityMainBinding.root)
getPermission()
activityMainBinding.btnRecordVideo.setOnClickListener {
recordVideo()
}
activityMainBinding.btnSwitchCamera.setOnClickListener { toggleCamera() }
activityMainBinding.btnRemoveVideo.setOnClickListener { removeVideo() }
}
#SuppressLint("RestrictedApi")
private fun startCamera() {
val viewFinder = activityMainBinding.viewFinder
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener({
cameraProvider = cameraProviderFuture.get()
val rotation = viewFinder.display.rotation
val localCameraProvider = cameraProvider
?: throw IllegalStateException("Camera initialization failed.")
preview = Preview.Builder()
.setTargetAspectRatio(AspectRatio.RATIO_4_3)
.setTargetRotation(rotation)
.build()
//TODO("Change size of video")
val size = Size(400, 400)
//TODO("Change setVideoFrameRate and setAudioBitRate")
val videoCaptureConfig =
VideoCapture.DEFAULT_CONFIG.config
videoCapture = VideoCapture.Builder
.fromConfig(videoCaptureConfig)
.setVideoFrameRate(10)
.setMaxResolution(size)
.build()
localCameraProvider.unbindAll()
try {
camera = localCameraProvider.bindToLifecycle(
this#MainActivity,
lensFacing,
preview,
videoCapture,
)
preview?.setSurfaceProvider(viewFinder.surfaceProvider)
} catch (e: Exception) {
Log.e(TAG, "Failed to bind use cases", e)
}
}, ContextCompat.getMainExecutor(this))
}
#SuppressLint("RestrictedApi")
private fun recordVideo() {
val localVideoCapture =
videoCapture ?: throw IllegalStateException("Camera initialization failed.")
val outputOptions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, System.currentTimeMillis())
put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4")
put(MediaStore.MediaColumns.RELATIVE_PATH, outputDirectory)
}
contentResolver.run {
val contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
VideoCapture.OutputFileOptions.Builder(this, contentUri, contentValues)
}
} else {
File(outputDirectory).mkdirs()
val file = File("$outputDirectory/${System.currentTimeMillis()}.mp4")
VideoCapture.OutputFileOptions.Builder(file)
}.build()
if (!isRecording) {
if (activityMainBinding.videoView.isPlaying) {
activityMainBinding.btnRemoveVideo.visibility = View.GONE
activityMainBinding.videoView.pause()
activityMainBinding.videoView.visibility = View.GONE
}
animateRecord.start()
if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.RECORD_AUDIO
) != PackageManager.PERMISSION_GRANTED
) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return
}
val timestamp = System.currentTimeMillis()
val contentValues = ContentValues()
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, timestamp)
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4")
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N) {
localVideoCapture.startRecording(
VideoCapture.OutputFileOptions.Builder(
contentResolver,
MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
contentValues
).build(),
mainExecutor(),
object : VideoCapture.OnVideoSavedCallback {
override fun onVideoSaved(outputFileResults: VideoCapture.OutputFileResults) {
Toast.makeText(
this#MainActivity,
"Video has been saved successfully.",
Toast.LENGTH_SHORT
).show()
activityMainBinding.btnRemoveVideo.visibility = View.VISIBLE
playVideo(outputFileResults.savedUri!!)
}
override fun onError(
videoCaptureError: Int,
message: String,
cause: Throwable?
) {
animateRecord.cancel()
Log.e("onCaptureError", message)
Toast.makeText(
this#MainActivity,
"Error saving video: $message",
Toast.LENGTH_SHORT
).show()
Toast.makeText(
this#MainActivity,
"Ino baram aks begir: ${cause!!.message}",
Toast.LENGTH_LONG
).show()
}
}
)
} else {
localVideoCapture.startRecording(
outputOptions,
mainExecutor(),
object : VideoCapture.OnVideoSavedCallback {
override fun onVideoSaved(outputFileResults: VideoCapture.OutputFileResults) {
// Create small preview
outputFileResults.savedUri
?.let { uri ->
activityMainBinding.btnRemoveVideo.visibility = View.VISIBLE
playVideo(uri)
Log.d(TAG, "Video saved in $uri")
}
}
override fun onError(
videoCaptureError: Int,
message: String,
cause: Throwable?
) {
Log.e("onCaptureError", message)
// This function is called if there is an error during recording process
animateRecord.cancel()
val msg = "Video capture failed: $message"
Toast.makeText(this#MainActivity, msg, Toast.LENGTH_SHORT).show()
Log.e(TAG, msg)
cause?.printStackTrace()
}
})
}
} else {
animateRecord.cancel()
localVideoCapture.stopRecording()
}
isRecording = !isRecording
}
private fun playVideo(uri: Uri) {
activityMainBinding.videoView.visibility = View.VISIBLE
activityMainBinding.videoView.setVideoURI(uri)
activityMainBinding.videoView.requestFocus()
activityMainBinding.videoView.start()
pauseVideo()
}
private var videoPosition = 0
private fun pauseVideo() {
activityMainBinding.videoView.setOnClickListener {
if (activityMainBinding.videoView.isPlaying) {
videoPosition = activityMainBinding.videoView.currentPosition
activityMainBinding.videoView.pause()
} else {
activityMainBinding.videoView.seekTo(videoPosition)
activityMainBinding.videoView.start()
}
}
}
private fun removeVideo() {
activityMainBinding.videoView.visibility = View.GONE
activityMainBinding.btnRemoveVideo.visibility = View.GONE
startCamera()
}
private fun toggleCamera() = activityMainBinding.btnSwitchCamera.toggleButton(
flag = lensFacing == CameraSelector.DEFAULT_FRONT_CAMERA,
rotationAngle = 180f,
firstIcon = R.drawable.ic_outline_camera_rear,
secondIcon = R.drawable.ic_outline_camera_front,
) {
lensFacing = if (it) {
CameraSelector.DEFAULT_FRONT_CAMERA
} else {
CameraSelector.DEFAULT_BACK_CAMERA
}
startCamera()
}
private fun ImageButton.toggleButton(
flag: Boolean,
rotationAngle: Float,
#DrawableRes firstIcon: Int,
#DrawableRes secondIcon: Int,
action: (Boolean) -> Unit
) {
if (flag) {
if (rotationY == 0f) rotationY = rotationAngle
animate().rotationY(0f).apply {
setListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) {
super.onAnimationEnd(animation)
action(!flag)
}
})
}.duration = 200
GlobalScope.launch(Dispatchers.Main) {
delay(100)
setImageResource(firstIcon)
}
} else {
if (rotationY == rotationAngle) rotationY = 0f
animate().rotationY(rotationAngle).apply {
setListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) {
super.onAnimationEnd(animation)
action(!flag)
}
})
}.duration = 200
GlobalScope.launch(Dispatchers.Main) {
delay(100)
setImageResource(secondIcon)
}
}
}
private fun getPermission() {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
val permissionList = PERMISSIONS_REQUIRED.toMutableList()
permissionList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE)
PERMISSIONS_REQUIRED = permissionList.toTypedArray()
}
if (!hasPermissions(this)) {
activityResultLauncher.launch(PERMISSIONS_REQUIRED)
activityMainBinding.btnRecordVideo.visibility = View.GONE
activityMainBinding.btnSwitchCamera.visibility = View.GONE
} else {
activityMainBinding.btnRecordVideo.visibility = View.VISIBLE
activityMainBinding.btnSwitchCamera.visibility = View.VISIBLE
startCamera()
}
}
private fun Context.mainExecutor(): Executor =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
mainExecutor
} else {
MainExecutor()
}
companion object {
fun hasPermissions(context: Context) = PERMISSIONS_REQUIRED.all {
ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED
}
}
private val activityResultLauncher =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions())
{ permissions ->
// Handle Permission granted/rejected
var permissionGranted = true
permissions.entries.forEach {
if (it.key in PERMISSIONS_REQUIRED && !it.value)
permissionGranted = false
}
if (!permissionGranted) {
activityMainBinding.btnRecordVideo.visibility = View.GONE
activityMainBinding.btnSwitchCamera.visibility = View.GONE
Toast.makeText(this, "Permission request denied", Toast.LENGTH_LONG).show()
} else {
activityMainBinding.btnRecordVideo.visibility = View.VISIBLE
activityMainBinding.btnSwitchCamera.visibility = View.VISIBLE
startCamera()
}
}
}
this is the crash log:
E/ACodec: OMX/mediaserver died, signalling error!
E/ACodec: signalError(omxError 0x8000100d, internalError -32)
E/ACodec: OMX/mediaserver died, signalling error!
E/ACodec: signalError(omxError 0x8000100d, internalError -32)
E/MediaCodec: Codec reported err 0xffffffe0, actionCode 0, while in state 6
E/MediaCodec: Codec reported err 0xffffffe0, actionCode 0, while in state 6
E/MediaCodec: getBufferAndFormat - not executing
I/VideoCapture: audio dequeueInputBuffer IllegalStateException null
E/AndroidRuntime: FATAL EXCEPTION: CameraX-video encoding thread
Process: ir.arinateam.inappcamera, PID: 3050
java.lang.IllegalStateException
at android.media.MediaCodec.native_dequeueOutputBuffer(Native Method)
at android.media.MediaCodec.dequeueOutputBuffer(MediaCodec.java:2570)
at androidx.camera.core.VideoCapture.videoEncode(VideoCapture.java:878)
at androidx.camera.core.VideoCapture.lambda$startRecording$4$androidx-camera-core-VideoCapture(VideoCapture.java:508)
at androidx.camera.core.VideoCapture$$ExternalSyntheticLambda6.run(D8$$SyntheticClass)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.os.HandlerThread.run(HandlerThread.java:61)
Related
I need to fetch data inside WhatsApp folders on External Storage.
As i am targeting API Level 30 i am no longer able to access WhatsApp folders on External Storage. I have implemented Storage Access Framework and got Android/media folder Uri and Document File. And using listFiles() i am able to list files but with filter() and sortedByDescending() functions it becomes very slow.
What i have tried?
Used Cursor loader with Projection and Selection Arguments but it only
worked for non hidden folders like WhatsApp Images and WhatsApp Videos
It returns empty cursor for hidden folder .Statuses
Tried replacing MediaStore.Video.Media.EXTERNAL_CONTENT_URI with MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
What is required?
List images and videos from .Statuses folder same as i am listing WhatsApp Images using Media Store in HomeActivity.java
Below is my code
In this activity i get permision to Android/media and set all WhatsApp folders URIs for status fetching and other use, but fetched WhatsApp Images with projection and selection from WhatsApp Images folder
class HomeActivity : AppCompatActivity(), InternetListener, PurchasesUpdatedListener,
CoroutineScope {
private val exceptionHandler = CoroutineExceptionHandler { context, exception ->
Toast.makeText(this, exception.message, Toast.LENGTH_LONG).show()
}
private val dataRepository: DataRepository by inject()
val tinyDB: TinyDB by inject()
val REQUEST_CODE = 12123
init {
newNativeAdSetUp = null
}
val sharedViewModel by viewModel<SharedViewModel>()
val viewModel by viewModel<HomeViewModel>()
val handler = CoroutineExceptionHandler { _, exception ->
Log.d("CoroutineException", "$exception handled !")
}
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + job + handler
private lateinit var job: Job
val sdk30PermissionListener = object : PermissionListener {
override fun onPermissionGranted() {
openDocumentTree()
}
override fun onPermissionDenied(deniedPermissions: MutableList<String>?) {
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_home)
handlePermissionsByVersion()
}
private fun handlePermissionsByVersion() {
if (SDK_INT >= Build.VERSION_CODES.R) {
if ((ContextCompat.checkSelfPermission(
this,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
== PackageManager.PERMISSION_GRANTED) && (ContextCompat.checkSelfPermission(
this,
Manifest.permission.READ_EXTERNAL_STORAGE
)
== PackageManager.PERMISSION_GRANTED)
) {
//if granted load whatsapp images and some uris setup to viewmodel
loadWhatsAppImages()
if (arePermissionsGranted()) {
if (dataRepository.mrWhatsAppImages == null || dataRepository.mrWhatsAppBusinessImages == null) {
setUpWAURIs()
}
}
} else {
TedPermission.with(this)
.setPermissionListener(sdk30PermissionListener)
.setDeniedMessage("If you reject permission,you can not use this service\n\nPlease turn on permissions at [Setting] > [Permission]")
.setPermissions(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE
)
.check()
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, #Nullable data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == RESULT_OK && requestCode == REQUEST_CODE) {
if (data != null) {
//this is the uri user has provided us
val treeUri: Uri? = data.data
if (treeUri != null) {
sharedViewModel.treeUri = treeUri
val decoded = Uri.decode(treeUri.toString())
Log.i(LOGTAG, "got uri: ${treeUri.toString()}")
// here we should do some checks on the uri, we do not want root uri
// because it will not work on Android 11, or perhaps we have some specific
// folder name that we want, etc
if (Uri.decode(treeUri.toString()).endsWith(":")) {
showWrongFolderSelection()
return
}
if (!decoded.equals(Constants.WHATSAPP_MEDIA_URI_DECODED)) {
showWrongFolderSelection()
return
}
// here we ask the content resolver to persist the permission for us
val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
contentResolver.takePersistableUriPermission(
treeUri,
takeFlags
)
val treeUriAsString = treeUri.toString()
tinyDB.putString("FOLDER_URI", treeUriAsString)
if (SDK_INT >= Build.VERSION_CODES.R) {
setupPaths()
}
}
}
}
}
private fun setupPaths() {
setUpOverlay()
fetchWhatsAppRootURIs(
this,
sharedViewModel,
dataRepository,
tinyDB
) {
fetchWhatsAppBusinessRootURIs(
this,
sharedViewModel,
dataRepository,
tinyDB
) {
tinyDB.putBoolean("WARootPathsDone", true)
removeOverlay()
}
}
}
override fun onDestroy() {
dialogHandler.removeCallbacksAndMessages(null)
super.onDestroy()
}
val loadmanagerImages = object : LoaderManager.LoaderCallbacks<Cursor> {
val whatsAppImagesArrayList = arrayListOf<File>()
override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> {
var location: File = File(
Environment.getExternalStorageDirectory()
.toString() + Constants.whatsapp_images_path
)
if (!location.exists()) {
location = File(
Environment.getExternalStorageDirectory()
.toString() + Constants.whatsapp_images_path11
)
}
if (location != null && location.exists()) {
whatsAppImagesArrayList.clear()
Timber.e("checkLoaded-onCreateLoader $id")
if (id == 0) {
var folder = location.absolutePath
val projection = arrayOf(
MediaStore.MediaColumns.DATA,
MediaStore.MediaColumns.DATE_MODIFIED
)
val selection = MediaStore.Images.Media.DATA + " like ? "
val selectionArgs: String = "%$folder%"
return CursorLoader(
this#HomeActivity,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
projection,
selection,
arrayOf(selectionArgs),
"${MediaStore.Images.Media.DATE_MODIFIED} DESC"
)
}
}
return null!!
}
override fun onLoadFinished(loader: Loader<Cursor>, cursor: Cursor?) {
Timber.e("checkLoaded-onLoadFinished")
var absolutePathOfImage: String
if (loader.id == 0) {
cursor?.let {
val columnIndexData = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA)
GlobalScope.launch(Dispatchers.Main + exceptionHandler) {
async(Dispatchers.IO + exceptionHandler) {
while (!cursor.isClosed && cursor.moveToNext() == true) {
absolutePathOfImage = cursor.getString(columnIndexData!!)
whatsAppImagesArrayList.add(File(absolutePathOfImage))
}
}.await()
LoaderManager.getInstance(this#HomeActivity).destroyLoader(0)
Timber.e("checkLoaded-Completion")
galleryViewModel.whatsAppImagesList.postValue(whatsAppImagesArrayList)
}
}
}
}
override fun onLoaderReset(loader: Loader<Cursor>) {
}
}
fun loadWhatsAppImages() {
try {
tinyDB.putBoolean("whatsAppMediaLoadCalled", true)
LoaderManager.getInstance(this).initLoader(
0,
null,
loadmanagerImages
)
} catch (e: RuntimeException) {
Log.e("exVideos ", "ex : ${e.localizedMessage}")
}
}
companion object {
const val ANDROID_DOCID = "primary:Android/media/"
const val EXTERNAL_STORAGE_PROVIDER_AUTHORITY = "com.android.externalstorage.documents"
private val androidUri = DocumentsContract.buildDocumentUri(
EXTERNAL_STORAGE_PROVIDER_AUTHORITY, ANDROID_DOCID
)
val androidTreeUri = DocumentsContract.buildTreeDocumentUri(
EXTERNAL_STORAGE_PROVIDER_AUTHORITY, ANDROID_DOCID
)
}
private fun openDocumentTree() {
val uriString = tinyDB.getString("FOLDER_URI", "")
when {
uriString == "" -> {
Log.w(LOGTAG, "uri not stored")
askPermission()
}
arePermissionsGranted() -> {
}
else -> {
Log.w(LOGTAG, "uri permission not stored")
askPermission()
}
}
}
// this will present the user with folder browser to select a folder for our data
private fun askPermission() {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, androidUri)
startActivityForResult(intent, REQUEST_CODE)
}
private fun arePermissionsGranted(): Boolean {
var uriString = tinyDB.getString("FOLDER_URI", "")
val list = contentResolver.persistedUriPermissions
for (i in list.indices) {
val persistedUriString = list[i].uri.toString()
if (persistedUriString == uriString && list[i].isWritePermission && list[i].isReadPermission) {
return true
}
}
return false
}
private fun showWrongFolderSelection() {
val layoutInflaterAndroid = LayoutInflater.from(this)
val mView = layoutInflaterAndroid.inflate(R.layout.layout_dialog_wrong_folder, null)
val builder = AlertDialog.Builder(this, R.style.ThemePageSearchDialog)
builder.setView(mView)
val alertDialog = builder.show()
alertDialog.setCancelable(false)
val btnOk = mView.findViewById(R.id.tvExit) as TextView
val tvCancel = mView.findViewById(R.id.tvCancel) as TextView
btnOk.setOnClickListener {
alertDialog.dismiss()
openDocumentTree()
}
tvCancel.setOnClickListener {
alertDialog.dismiss()
}
}
private fun setUpWAURIs() {
dataRepository.mrWhatsAppImages =
getDocumentFileFromStringURI(
this,
tinyDB.getString("mrWhatsAppImages")
)
dataRepository.mrWhatsAppVN =
getDocumentFileFromStringURI(
this,
tinyDB.getString("mrWhatsAppVN")
)
dataRepository.mrWhatsAppDocs =
getDocumentFileFromStringURI(
this,
tinyDB.getString("mrWhatsAppDocs")
)
dataRepository.mrWhatsAppVideo =
getDocumentFileFromStringURI(
this,
tinyDB.getString("mrWhatsAppVideo")
)
dataRepository.mrWhatsAppAudio =
getDocumentFileFromStringURI(
this,
tinyDB.getString("mrWhatsAppAudio")
)
dataRepository.WhatsAppStatuses =
getDocumentFileFromStringURIStatuses(
this,
tinyDB.getString("WhatsAppStatuses")
)
dataRepository.mrWhatsAppBusinessImages =
getDocumentFileFromStringURI(
this,
tinyDB.getString("mrWhatsAppBusinessImages")
)
dataRepository.mrWhatsAppBusinessVN =
getDocumentFileFromStringURI(
this,
tinyDB.getString("mrWhatsAppBusinessVN")
)
dataRepository.mrWhatsAppBusinessDocs =
getDocumentFileFromStringURI(
this,
tinyDB.getString("mrWhatsAppBusinessDocs")
)
dataRepository.mrWhatsAppBusinessVideo =
getDocumentFileFromStringURI(
this,
tinyDB.getString("mrWhatsAppBusinessVideo")
)
dataRepository.mrWhatsAppBusinessAudio =
getDocumentFileFromStringURI(
this,
tinyDB.getString("mrWhatsAppBusinessAudio")
)
dataRepository.WhatsAppBusinessStatuses =
getDocumentFileFromStringURIStatuses(
this,
tinyDB.getString("WhatsAppBusinessStatuses")
)
}
fun setUpOverlay() {
val dialogfragment = FullScreenLoadingDialog()
dialogfragment.isCancelable = false
dialogfragment.setisAdmobAd(true)
val ft: FragmentTransaction =
supportFragmentManager.beginTransaction()
ft.add(dialogfragment, "DialogFragment_FLAG")
ft.commitAllowingStateLoss()
}
fun removeOverlay() {
val fragment: Fragment? = supportFragmentManager.findFragmentByTag("DialogFragment_FLAG")
if (fragment != null && fragment is DialogFragment) {
fragment.dismissAllowingStateLoss()
}
}
fun fetchWhatsAppRootURIs(
context: Context,
sharedViewModel: SharedViewModel,
dataRepository: DataRepository,
tinyDB: TinyDB, completed: () -> Unit
) {
val selectedPackageName = Constants.WHATSAPP_PKG_NAME
val selectedRootName = Constants.WHATSAPP_ROOT_NAME
var waImages: DocumentFile? = null
var waVN: DocumentFile? = null
var waDocs: DocumentFile? = null
var waVideos: DocumentFile? = null
var waAudio: DocumentFile? = null
var waStatus: DocumentFile? = null
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && sharedViewModel.treeUri != null) {
CoroutineScope(Dispatchers.Main).launch {
async(Dispatchers.IO) {
val dir = DocumentFile.fromTreeUri(
context,
sharedViewModel.treeUri!!
)
dir?.listFiles()?.forEach {
if (it.name.equals(selectedPackageName)) {
it.listFiles().forEach {
if (it.name.equals(selectedRootName)) {
it.listFiles().forEach {
if (it.name.equals(Constants.WHATSAPP_MEDIA_FOLDER_NAME)) {
it.listFiles().forEach {
if (it.name.equals(Constants.FOLDER_NAME_WHATSAPP_IMAGES)) {
waImages = it
} else if (it.name.equals(Constants.FOLDER_NAME_WHATSAPP_VN)) {
waVN = it
} else if (it.name.equals(Constants.FOLDER_NAME_WHATSAPP_DOCUMENTS)) {
waDocs = it
} else if (it.name.equals(Constants.FOLDER_NAME_WHATSAPP_VIDEO)) {
waVideos = it
} else if (it.name.equals(Constants.FOLDER_NAME_WHATSAPP_AUDIO)) {
waAudio = it
} else if (it.name.equals(Constants.FOLDER_NAME_STATUSES)) {
waStatus = it
}
}
}
}
}
}
}
}
}.await()
Timber.e("processStatusFetch:Done")
tinyDB.putString("mrWhatsAppImages", waImages?.uri.toString())
tinyDB.putString("mrWhatsAppVN", waImages?.uri.toString())
tinyDB.putString("mrWhatsAppDocs", waImages?.uri.toString())
tinyDB.putString("mrWhatsAppVideo", waImages?.uri.toString())
tinyDB.putString("mrWhatsAppAudio", waImages?.uri.toString())
tinyDB.putString("WhatsAppStatuses", waStatus?.uri.toString())
dataRepository.mrWhatsAppImages = waImages
dataRepository.mrWhatsAppVN = waVN
dataRepository.mrWhatsAppDocs = waDocs
dataRepository.mrWhatsAppVideo = waVideos
dataRepository.mrWhatsAppAudio = waAudio
dataRepository.WhatsAppStatuses = waStatus
completed()
}
}
}
Here i am using .Statuses folder URI to list DocumentFiles and display but this way it is slow
class StatusImageFragment : Fragment(), StatusListener, CoroutineScope {
companion object {
fun newInstance() = StatusImageFragment()
}
val handler = CoroutineExceptionHandler { _, exception ->
Log.d("CoroutineException", "$exception handled !")
}
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + job + handler
private lateinit var job: Job
private var adapterSDK30 = StatusImageAdapterSDK30()
private var no_image: ImageView? = null
private var no_image_txt: TextView? = null
val tinyDB: TinyDB by inject()
val sharedViewModel by viewModel<SharedViewModel>()
private val dataRepository: DataRepository by inject()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
job = Job()
return inflater.inflate(R.layout.status_image_fragment, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
swipeRefresh(false, false)
}
public fun swipeRefresh(isReloadRequired: Boolean, isFromModeChanged: Boolean) {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (isFromModeChanged) {
status_image_recycler.visibility = View.GONE
progressbar.visibility = View.VISIBLE
no_image?.let {
it.visibility = View.GONE
}
no_image_txt?.let {
it.visibility = View.GONE
}
go_to_app?.let {
it.visibility = View.GONE
}
} else {
if (adapterSDK30.listImages == null || adapterSDK30.listImages.size == 0) {
no_image?.let {
it.visibility = View.GONE
}
no_image_txt?.let {
it.visibility = View.GONE
}
go_to_app?.let {
it.visibility = View.GONE
}
progressbar.visibility = View.VISIBLE
}
}
if (isReloadRequired) {
processStatusFetchFromChild({
sharedViewModel.statusImages.observe(viewLifecycleOwner, Observer {
val arrayList = it
adapterSDK30.listImages = arrayList
postFetchingExecutionSDK30()
})
})
} else {
sharedViewModel.statusImages.observe(viewLifecycleOwner, Observer {
val arrayList = it
adapterSDK30.listImages = arrayList
adapterSDK30.listImages = it
postFetchingExecutionSDK30()
})
}
}
} catch (ex: Exception) {
ex.printStackTrace()
}
}
private fun postFetchingExecutionSDK30() {
progressbar.visibility = View.GONE
status_image_recycler.visibility = View.VISIBLE
if (adapterSDK30!!.listImages != null && adapterSDK30!!.listImages.size > 0) {
no_image?.let {
it.visibility = View.GONE
}
no_image_txt?.let {
it.visibility = View.GONE
}
go_to_app?.let {
it.visibility = View.GONE
}
} else {
no_image?.let {
it.visibility = View.VISIBLE
}
no_image_txt?.let {
it.visibility = View.VISIBLE
}
go_to_app?.let {
it.visibility = View.VISIBLE
}
}
adapterSDK30!!.notifyDataSetChanged()
status_img_swipe.isRefreshing = false
}
override fun onDestroyView() {
job.cancel()
super.onDestroyView()
}
fun processStatusFetchFromChild(completed: () -> Unit) {
val statusSelection = tinyDB.getInt(Constants.status_accounts)
if (statusSelection == 0 || statusSelection == 1) {
if (dataRepository.WhatsAppStatuses == null) {
(activity as StatusActivity).setUpWAURIs()
}
var documentFileStatuses: DocumentFile? = dataRepository.WhatsAppStatuses
if (statusSelection == 1) {
documentFileStatuses = dataRepository.WhatsAppBusinessStatuses
}
if (documentFileStatuses != null) {
launch(Dispatchers.Main) {
val statusImages1 = arrayListOf<DocumentFile>()
async(Dispatchers.IO) {
//this takes time ; want to fetch this same as WhatsApp Gallery
statusImages1.addAll(documentFileStatuses!!.listFiles().filter {
it.mimeType.equals(Constants.MIME_TYPE_IMG_PNG) || it.mimeType.equals(
Constants.MIME_TYPE_IMG_JPG
) || it.mimeType.equals(Constants.MIME_TYPE_IMG_JPEG)
}.sortedByDescending { it.lastModified() })
}.await()
Timber.e("processStatusFetch:Done")
sharedViewModel.statusImages.postValue(statusImages1)
completed()
}
} else {
Timber.e("processStatusFetch:Done")
sharedViewModel.statusImages.postValue(arrayListOf<DocumentFile>())
completed()
}
} else {
Timber.e("processStatusFetch:Done")
sharedViewModel.statusImages.postValue(arrayListOf<DocumentFile>())
completed()
}
}
}
Please note WhatsApp folder path which i used is
val whatsapp_images_path11 = "/Android/media/“ +"com.whatsapp" +"/WhatsApp/Media/WhatsAppImages/"
How i can use MediaStore in this case so that i don't need to use sort and filter functions of list? Its not important to get java.io File only i can work with URIs as well.
What I have finally implemented, in android 10+ you need to ask the user for your specific directory access. Then you can use this functions to fetch statuses:
#RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private fun readSDKFrom30(): ArrayList<String> {
val treeUri = DocumentsContract.buildTreeDocumentUri(
EXTERNAL_STORAGE_PROVIDER_AUTHORITY,
"primary:Android/media/com.whatsapp/WhatsApp/Media/.Statuses"
)
val tree = DocumentFile.fromTreeUri(context, treeUri)!!
val pathList = ArrayList<String>()
listFolderContent(tree).forEach { uri ->
val file = createFileFromContentUri(uri)
pathList.add(file.toString())
}
return pathList
}
private fun listFolderContent(folder: DocumentFile): List<Uri> {
return if (folder.isDirectory) {
val files = folder.listFiles().toMutableList()
files.sortByDescending { it.lastModified() }
files.mapNotNull { file ->
if (file.name != null) file.uri else null
}
} else {
emptyList()
}
}
#RequiresApi(Build.VERSION_CODES.O)
private fun createFileFromContentUri(fileUri: Uri): File {
var fileName = ""
fileUri.let { returnUri ->
context.contentResolver.query(returnUri, null, null, null)
}?.use { cursor ->
val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
cursor.moveToFirst()
fileName = cursor.getString(nameIndex)
}
val iStream: InputStream =
context.contentResolver.openInputStream(fileUri)!!
val outputDir: File = context.cacheDir!!
val outputFile = File(outputDir, fileName)
copyStreamToFile(iStream, outputFile)
iStream.close()
return outputFile
}
private fun copyStreamToFile(inputStream: InputStream, outputFile: File) {
inputStream.use { input ->
val outputStream = FileOutputStream(outputFile)
outputStream.use { output ->
val buffer = ByteArray(4 * 1024) // buffer size
while (true) {
val byteCount = input.read(buffer)
if (byteCount < 0) break
output.write(buffer, 0, byteCount)
}
output.flush()
}
}
}
Using DocumentFile to handle SAF uries is slow indeed.
Better use DocumentsContract to do so.
Its about twenty times as fast as DocumentFile and about as fast as classic File class stuff.
Using MediaStore for hidden folders should be possible. You cannot create hidden folders with the mediastore. But if you managed to make them not using mediastore you should be able to list files in them using mediastore. Well if they are scanned. And if they belong to your app.
I make a mobile application using the "io fotoapparat" library and I want to create a document with a random id and in the document in the id field, add the document id, the photo we will take was updated in the firebase store and in the previously made about the same id in the receipt collection when taking a photo
link from which I took "io fotoapparat"
EDIT
add to firestore document
binding.button.setOnClickListener {
// Inflate the layout for this fragment
val biedronka = "biedronka"
val price = "20"
val img = " "
val identificator = " "
val from = biedronka
val value = price
val image = img
val idd = identificator
val db = Firebase.firestore
val data = hashMapOf(
"from" to from,
"value" to value,
"image" to image,
"id" to idd
)
db.collection("receipts")
.add(data)
.addOnSuccessListener { documentReference ->
Log.d(SCAN_DEBUG, "DocumentSnapshot written with ID: ${documentReference.id}")
db.collection("receipts")
.document(documentReference.id)
.update("id", documentReference.id)
.addOnSuccessListener {
}
}
.addOnFailureListener { e ->
Log.w(SCAN_DEBUG, "Error adding document", e)
}
}
2 add to storage and to document (doesnt work)
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CAPTURE_IMAGE && resultCode == RESULT_OK) {
val uid = profileVm.user.value?.uid!!
val imageBitmap = data?.extras?.get("data") as Bitmap
val userImage = binding.userImg
val stream = ByteArrayOutputStream()
val result = imageBitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream)
val byteArray = stream.toByteArray()
if (result) profileVm.uploadUserPhoto(byteArray, "$uid.jpg")
}
}
repository to 2
fun uploadReceiptPhoto(bytes: ByteArray) {
storage.getReference("receipts")
.child("${docId}.jpg")
.putBytes(bytes)
.addOnCompleteListener{
Log.d(REPO_DEBUG, "COMPLETE UPLOAD PHOTO")
}
.addOnSuccessListener {
getReceiptPhotoDownloadUrl(it.storage)
}
.addOnFailureListener {
Log.d(REPO_DEBUG, it.message.toString())
}
}
private fun getReceiptPhotoDownloadUrl(storage: StorageReference) {
storage.downloadUrl
.addOnSuccessListener {
updateReceiptPhoto(it.toString())
}
.addOnFailureListener {
Log.d(REPO_DEBUG, it.message.toString())
}
}
private fun updateReceiptPhoto(url: String) {
cloud.collection("receipts")
.document(docId)
.update("image", url)
.addOnSuccessListener {
Log.d(REPO_DEBUG, "UPDATE USER PHOTO")
}
.addOnFailureListener {
Log.d(REPO_DEBUG, it.message.toString())
}
}
THE REST OF THE CODE (camera code "io fotoapparat" and take pictures)
class ScanFragment : Fragment(), OnReceiptsItemAdd {
private var _binding: FragmentScanBinding? = null
private val binding get() = _binding!!
private val scanVm by viewModels<ScanViewModel>()
private val SCAN_DEBUG = "SCAN_DEBUG"
private var fotoapparat: Fotoapparat? = null
private var fotoapparatState: FotoapparatState? = null
private var cameraStatus: CameraState? = null
private var flashState: FlashState? = null
private val permissions = arrayOf(Manifest.permission.CAMERA,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE)
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
_binding = FragmentScanBinding.inflate(inflater, container, false)
createFotoapparat()
cameraStatus = CameraState.BACK
flashState = FlashState.OFF
fotoapparatState = FotoapparatState.OFF
binding.fabSwitchCamera.setOnClickListener {
switchCamera()
}
binding.fabFlash.setOnClickListener {
changeFlashState()
}
return binding.root
}
private fun createFotoapparat(){
val cameraView = binding.cameraView
fotoapparat = Fotoapparat(
context = requireContext(),
view = cameraView,
scaleType = ScaleType.CenterCrop,
lensPosition = back(),
logger = loggers(
logcat()
),
cameraErrorCallback = { error ->
println("Recorder errors: $error")
}
)
}
private fun changeFlashState() {
fotoapparat?.updateConfiguration(
CameraConfiguration(
flashMode = if(flashState == FlashState.TORCH) off() else torch()
)
)
flashState = if(flashState == FlashState.TORCH) FlashState.OFF
else FlashState.TORCH
}
private fun switchCamera() {
fotoapparat?.switchTo(
lensPosition = if (cameraStatus == CameraState.BACK) front() else back(),
cameraConfiguration = CameraConfiguration()
)
cameraStatus = if(cameraStatus == CameraState.BACK) CameraState.FRONT
else CameraState.BACK
}
private fun takePhoto() {
if (hasNoPermissions()) {
requestPermission()
}else{
fotoapparat
?.takePicture()
?.toBitmap()
}
}
override fun onStart() {
super.onStart()
if (hasNoPermissions()) {
requestPermission()
}else{
fotoapparat?.start()
fotoapparatState = FotoapparatState.ON
}
}
private fun hasNoPermissions(): Boolean{
return ContextCompat.checkSelfPermission(requireContext(),
Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(requireContext(),
Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(requireContext(),
Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED
}
private fun requestPermission(){
ActivityCompat.requestPermissions(requireActivity(), permissions,0)
}
override fun onStop() {
super.onStop()
fotoapparat?.stop()
FotoapparatState.OFF
}
}
enum class CameraState{
FRONT, BACK
}
enum class FlashState{
TORCH, OFF
}
enum class FotoapparatState{
ON, OFF
}
I had Enabled the Google Android Device Verification API.
I had added the SHA-256 onto Firebase setting and updated the GSON file.
and After adding :
Firebase.auth.firebaseAuthSettings.setAppVerificationDisabledForTesting(true)
I am getting error that SafetyNet or Captcha are not succeded (kind of error).
Can anyone tell me how can i disable the captcha check ?
Here is my code
class OTPNewActivity : AppCompatActivity(), OnKeyboardVisibilityListener, View.OnClickListener {
var TAG = "OTPNewActivity"
lateinit var binding: ActivityOtpnewBinding
val action = "android.provider.Telephony.SMS_RECEIVED"
var userEnteredCode = ""
var systemGeneratedCode = ""
var phoneNumer = ""
var phoneDigits = ""
private lateinit var auth: FirebaseAuth
private lateinit var resendToken: PhoneAuthProvider.ForceResendingToken
private var callbacks: PhoneAuthProvider.OnVerificationStateChangedCallbacks =
object : PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
override fun onVerificationCompleted(credential: PhoneAuthCredential) {
Log.d(TAG, "onVerificationCompleted: $credential")
val code = credential.smsCode
if (code != null) {
binding.otpView.setText(code)
verifyPhoneNumberWithCode(systemGeneratedCode, code!!)
}
}
override fun onVerificationFailed(e: FirebaseException) {
Log.d(TAG, "onVerificationFailed $e")
if (e is FirebaseAuthInvalidCredentialsException) {
Constants.showToast(
this#OTPNewActivity,
Constants.TOAST_TYPE_FAIL,
"Invalid request"
)
} else if (e is FirebaseTooManyRequestsException) {
Constants.showToast(
this#OTPNewActivity,
Constants.TOAST_TYPE_FAIL,
"The SMS quota for the project has been exceeded $e"
)
} else {
Constants.showToast(
this#OTPNewActivity,
Constants.TOAST_TYPE_FAIL, "Something wents wrong"
)
}
}
override fun onCodeSent(
verificationId: String,
token: PhoneAuthProvider.ForceResendingToken
) {
// The SMS verification code has been sent to the provided phone number, we
// now need to ask the user to enter the code and then construct a credential
// by combining the code with a verification ID.
Log.d(TAG, "onCodeSent: $verificationId")
systemGeneratedCode = verificationId
resendToken = token
countdownTimer()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_otpnew)
initListeners()
}
private fun initListeners() {
LocalSharedPreference.getInstance(this).isPhoneNumberVerified = false
// Firebase.auth.firebaseAuthSettings.setAppVerificationDisabledForTesting(true)
auth = Firebase.auth
setKeyboardVisibilityListener(this)
binding.btnNext.setOnClickListener(this)
binding.tvCount.setOnClickListener(this)
binding.icBack.setOnClickListener(this)
val intent = intent
intent?.let {
phoneNumer = intent.getStringExtra(Constants.PHONE_NUMBER).toString()
phoneDigits = intent.getStringExtra(Constants.SAVE_PHONE_DIGITS).toString()
binding.textView.text =
"${this.resources.getString(R.string.digit_code)} $phoneNumer"
val options = PhoneAuthOptions.newBuilder(auth)
.setPhoneNumber(phoneNumer)
.setTimeout(15L, TimeUnit.SECONDS)
.setActivity(this)
.setCallbacks(callbacks)
.build()
PhoneAuthProvider.verifyPhoneNumber(options)
}
binding.otpView.setOtpCompletionListener(OnOtpCompletionListener { otp -> // do Stuff
userEnteredCode = otp
binding.icNext.visibility = View.VISIBLE
binding.pbNext.visibility = View.GONE
verifyPhoneNumberWithCode(systemGeneratedCode, userEnteredCode)
})
}
private fun verifyPhoneNumberWithCode(verificationId: String?, code: String) {
try {
val credential = PhoneAuthProvider.getCredential(verificationId!!, code)
signInWithPhoneAuthCredential(credential);
} catch (e: Exception) {
binding.otpView.setText("")
Constants.showToast(
this#OTPNewActivity,
Constants.TOAST_TYPE_FAIL,
this#OTPNewActivity.resources.getString(R.string.wrong_Code)
)
e.printStackTrace()
}
}
private fun signInWithPhoneAuthCredential(credential: PhoneAuthCredential) {
auth.signInWithCredential(credential)
.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
// Sign in success, update UI with the signed-in user's information
Log.d(TAG, "signInWithCredential:success")
LocalSharedPreference.getInstance(this).isPhoneNumberVerified = true
if (phoneNumer.contains("+52")) {
LocalSharedPreference.getInstance(this).setSaveCountry("MX")
} else if (phoneNumer.contains("+92")) {
LocalSharedPreference.getInstance(this).setSaveCountry("PK")
} else if (phoneNumer.contains("+1")) {
LocalSharedPreference.getInstance(this).setSaveCountry("US")
}
LocalSharedPreference.getInstance(this).savePhoneNumber(phoneNumer)
LocalSharedPreference.getInstance(this).setPhoneDigits(phoneDigits)
val user = task.result?.user
val intent = Intent(this#OTPNewActivity, ProfileActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)
startActivity(intent)
finish()
} else {
// Sign in failed, display a message and update the UI
Log.w(TAG, "signInWithCredential:failure", task.exception)
if (task.exception is FirebaseAuthInvalidCredentialsException) {
Constants.showToast(
this#OTPNewActivity,
Constants.TOAST_TYPE_FAIL,
"${task.exception}"
)
}
// Update UI
}
}
}
private fun setKeyboardVisibilityListener(onKeyboardVisibilityListener: OnKeyboardVisibilityListener) {
val parentView: View = (findViewById<View>(android.R.id.content) as ViewGroup).getChildAt(0)
parentView.getViewTreeObserver()
.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
private var alreadyOpen = false
private val defaultKeyboardHeightDP = 100
private val EstimatedKeyboardDP =
defaultKeyboardHeightDP + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) 48 else 0
private val rect: Rect = Rect()
override fun onGlobalLayout() {
val estimatedKeyboardHeight = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
EstimatedKeyboardDP.toFloat(),
parentView.getResources().getDisplayMetrics()
)
.toInt()
parentView.getWindowVisibleDisplayFrame(rect)
val heightDiff: Int =
parentView.getRootView().getHeight() - (rect.bottom - rect.top)
val isShown = heightDiff >= estimatedKeyboardHeight
if (isShown == alreadyOpen) {
Log.d("Keyboard state", "Ignoring global layout change...")
return
}
alreadyOpen = isShown
onKeyboardVisibilityListener.onVisibilityChanged(isShown)
}
})
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
// Checks whether a hardware keyboard is available
if (newConfig.hardKeyboardHidden === Configuration.HARDKEYBOARDHIDDEN_NO) {
Toast.makeText(this, "keyboard visible", Toast.LENGTH_SHORT).show()
} else if (newConfig.hardKeyboardHidden === Configuration.HARDKEYBOARDHIDDEN_YES) {
Toast.makeText(this, "keyboard hidden", Toast.LENGTH_SHORT).show()
}
}
override fun onVisibilityChanged(visible: Boolean) {
if (!visible) {
val imm: InputMethodManager =
getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.showSoftInput(binding.otpView, InputMethodManager.SHOW_IMPLICIT)
}
}
override fun onResume() {
super.onResume()
binding.otpView.requestFocus()
val imm: InputMethodManager =
getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.showSoftInput(binding.otpView, InputMethodManager.SHOW_IMPLICIT)
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0)
registerReceiver(receiver, IntentFilter(action))
}
private fun countdownTimer() {
binding.pbNext.visibility = View.VISIBLE
binding.icNext.visibility = View.GONE
object : CountDownTimer(15000, 1000) {
override fun onTick(millisUntilFinished: Long) {
binding.tvCount.setText("Resend Code in : " + millisUntilFinished / 1000)
}
override fun onFinish() {
binding.tvCount.setText("I didn`t receive a code")
binding.icNext.visibility = View.VISIBLE
binding.pbNext.visibility = View.GONE
}
}.start()
}
override fun onClick(view: View) {
when (view.id) {
R.id.btn_next -> {
if (binding.otpView.text.toString().length == 6) {
LocalSharedPreference.getInstance(this#OTPNewActivity).isPhoneNumberVerified =
true
verifyPhoneNumberWithCode(systemGeneratedCode, userEnteredCode)
}
}
R.id.tv_count -> {
if (binding.tvCount.text.equals(this#OTPNewActivity.resources.getString(R.string.i_dont_received_code)))
resendVerificationCode(phoneNumer, resendToken)
}
R.id.ic_back -> {
finish()
}
}
}
private fun resendVerificationCode(
phoneNumber: String,
token: PhoneAuthProvider.ForceResendingToken?
) {
val optionsBuilder = PhoneAuthOptions.newBuilder(auth)
.setPhoneNumber(phoneNumber) // Phone number to verify
.setTimeout(15L, TimeUnit.SECONDS) // Timeout and unit
.setActivity(this) // Activity (for callback binding)
.setCallbacks(callbacks) // OnVerificationStateChangedCallbacks
if (token != null) {
optionsBuilder.setForceResendingToken(token) // callback's ForceResendingToken
}
PhoneAuthProvider.verifyPhoneNumber(optionsBuilder.build())
}
var receiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent) {
if (intent.action == "android.provider.Telephony.SMS_RECEIVED") {
val bundle = intent.extras
var msgs: Array<SmsMessage?>? = null
var msg_from: String? = ""
Log.d(TAG, "onReceive called ")
if (bundle != null) {
try {
val pdus = bundle["pdus"] as Array<Any>?
msgs = arrayOfNulls(pdus!!.size)
for (i in msgs.indices) {
msgs[i] = SmsMessage.createFromPdu(pdus[i] as ByteArray)
msg_from = msgs[i]!!.getOriginatingAddress()
val msgBody: String = msgs[i]!!.getMessageBody()
if (msgBody.contains("is your verification code for running-errands.firebaseapp.com")) {
val _1: Char = msgBody[0]
val _2: Char = msgBody[1]
val _3: Char = msgBody[2]
val _4: Char = msgBody[3]
val _5: Char = msgBody[4]
val _6: Char = msgBody[5]
val code: String =
_1.toString() + _2.toString() + _3.toString() + _4.toString() + _5.toString() + _6.toString()
// binding.otpView.text = SpannableStringBuilder(code!!)
binding.otpView.setText(code)
verifyPhoneNumberWithCode(systemGeneratedCode, code!!)
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}
}
override fun onDestroy() {
super.onDestroy()
unregisterReceiver(receiver);
}
}
I implemented Scope Storage in a sample project. Where I Save, Load, and Modify Images in Local Storage and in Scope Storage as well.
Below is my Main Activity Class where I am Saving, Loading, and Modifying images in Local and Scope Storage. The below Code was working in API level 28 or Android 10. but when I run this app in Android 11 it gets hang and gives Application Not Responding. I am unable to find any error in Logcat. Below is my code Manifest File:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.scopestorage">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/Theme.ScopeStorage">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Now Below is my Main Activity Code:
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var internalStoragePhotoAdapter: InternalStoragePhotoAdapter
private lateinit var externalStoragePhotoAdapter: SharedPhotoAdapter
private var readPermissionGranted = false
private var writePermissionGranted = false
private lateinit var permissionsLauncher: ActivityResultLauncher<Array<String>>
private lateinit var intentSenderLauncher: ActivityResultLauncher<IntentSenderRequest>
private lateinit var contentObserver: ContentObserver
private var deletedImageUri: Uri? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
internalStoragePhotoAdapter = InternalStoragePhotoAdapter {
lifecycleScope.launch {
val isDeletionSuccessful = deletePhotoFromInternalStorage(it.name)
if(isDeletionSuccessful) {
loadPhotosFromInternalStorageIntoRecyclerView()
Toast.makeText(this#MainActivity, "Photo successfully deleted", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this#MainActivity, "Failed to delete photo", Toast.LENGTH_SHORT).show()
}
}
}
externalStoragePhotoAdapter = SharedPhotoAdapter {
lifecycleScope.launch {
deletePhotoFromExternalStorage(it.contentUri)
deletedImageUri = it.contentUri
}
}
setupExternalStorageRecyclerView()
initContentObserver()
permissionsLauncher = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
readPermissionGranted = permissions[Manifest.permission.READ_EXTERNAL_STORAGE] ?: readPermissionGranted
writePermissionGranted = permissions[Manifest.permission.WRITE_EXTERNAL_STORAGE] ?: writePermissionGranted
if(readPermissionGranted) {
loadPhotosFromExternalStorageIntoRecyclerView()
} else {
Toast.makeText(this, "Can't read files without permission.", Toast.LENGTH_LONG).show()
}
}
updateOrRequestPermissions()
intentSenderLauncher = registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) {
if(it.resultCode == RESULT_OK) {
if(Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {
lifecycleScope.launch {
deletePhotoFromExternalStorage(deletedImageUri ?: return#launch)
}
}
Toast.makeText(this#MainActivity, "Photo deleted successfully", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this#MainActivity, "Photo couldn't be deleted", Toast.LENGTH_SHORT).show()
}
}
val takePhoto = registerForActivityResult(ActivityResultContracts.TakePicturePreview()) {
lifecycleScope.launch {
val isPrivate = binding.switchPrivate.isChecked
val isSavedSuccessfully = when {
isPrivate -> savePhotoToInternalStorage(UUID.randomUUID().toString(), it)
writePermissionGranted -> savePhotoToExternalStorage(UUID.randomUUID().toString(), it)
else -> false
}
if(isPrivate) {
loadPhotosFromInternalStorageIntoRecyclerView()
}
if(isSavedSuccessfully) {
Toast.makeText(this#MainActivity, "Photo saved successfully", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this#MainActivity, "Failed to save photo", Toast.LENGTH_SHORT).show()
}
}
}
binding.btnTakePhoto.setOnClickListener {
takePhoto.launch()
}
setupInternalStorageRecyclerView()
loadPhotosFromInternalStorageIntoRecyclerView()
loadPhotosFromExternalStorageIntoRecyclerView()
}
private fun initContentObserver() {
contentObserver = object : ContentObserver(null) {
override fun onChange(selfChange: Boolean) {
if(readPermissionGranted) {
loadPhotosFromExternalStorageIntoRecyclerView()
}
}
}
contentResolver.registerContentObserver(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
true,
contentObserver
)
}
private suspend fun deletePhotoFromExternalStorage(photoUri: Uri) {
withContext(Dispatchers.IO) {
try {
contentResolver.delete(photoUri, null, null)
} catch (e: SecurityException) {
val intentSender = when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> {
MediaStore.createDeleteRequest(contentResolver, listOf(photoUri)).intentSender
}
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> {
val recoverableSecurityException = e as? RecoverableSecurityException
recoverableSecurityException?.userAction?.actionIntent?.intentSender
}
else -> null
}
intentSender?.let { sender ->
intentSenderLauncher.launch(
IntentSenderRequest.Builder(sender).build()
)
}
}
}
}
private suspend fun loadPhotosFromExternalStorage(): List<SharedStoragePhoto> {
return withContext(Dispatchers.IO) {
val collection = sdk29AndUp {
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
} ?: MediaStore.Images.Media.EXTERNAL_CONTENT_URI
val projection = arrayOf(
MediaStore.Images.Media._ID,
MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.WIDTH,
MediaStore.Images.Media.HEIGHT,
)
val photos = mutableListOf<SharedStoragePhoto>()
contentResolver.query(
collection,
projection,
null,
null,
"${MediaStore.Images.Media.DISPLAY_NAME} ASC"
)?.use { cursor ->
val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID)
val displayNameColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME)
val widthColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.WIDTH)
val heightColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.HEIGHT)
while(cursor.moveToNext()) {
val id = cursor.getLong(idColumn)
val displayName = cursor.getString(displayNameColumn)
val width = cursor.getInt(widthColumn)
val height = cursor.getInt(heightColumn)
val contentUri = ContentUris.withAppendedId(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
id
)
photos.add(SharedStoragePhoto(id, displayName, width, height, contentUri))
}
photos.toList()
} ?: listOf()
}
}
private fun updateOrRequestPermissions() {
val hasReadPermission = ContextCompat.checkSelfPermission(
this,
Manifest.permission.READ_EXTERNAL_STORAGE
) == PackageManager.PERMISSION_GRANTED
val hasWritePermission = ContextCompat.checkSelfPermission(
this,
Manifest.permission.WRITE_EXTERNAL_STORAGE
) == PackageManager.PERMISSION_GRANTED
val minSdk29 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
readPermissionGranted = hasReadPermission
writePermissionGranted = hasWritePermission || minSdk29
val permissionsToRequest = mutableListOf<String>()
if(!writePermissionGranted) {
permissionsToRequest.add(Manifest.permission.WRITE_EXTERNAL_STORAGE)
}
if(!readPermissionGranted) {
permissionsToRequest.add(Manifest.permission.READ_EXTERNAL_STORAGE)
}
if(permissionsToRequest.isNotEmpty()) {
permissionsLauncher.launch(permissionsToRequest.toTypedArray())
}
}
private suspend fun savePhotoToExternalStorage(displayName: String, bmp: Bitmap): Boolean {
return withContext(Dispatchers.IO) {
val imageCollection = sdk29AndUp {
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
} ?: MediaStore.Images.Media.EXTERNAL_CONTENT_URI
val contentValues = ContentValues().apply {
put(MediaStore.Images.Media.DISPLAY_NAME, "$displayName.jpg")
put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
put(MediaStore.Images.Media.WIDTH, bmp.width)
put(MediaStore.Images.Media.HEIGHT, bmp.height)
}
try {
contentResolver.insert(imageCollection, contentValues)?.also { uri ->
contentResolver.openOutputStream(uri).use { outputStream ->
if(!bmp.compress(Bitmap.CompressFormat.JPEG, 95, outputStream)) {
throw IOException("Couldn't save bitmap")
}
}
} ?: throw IOException("Couldn't create MediaStore entry")
true
} catch(e: IOException) {
e.printStackTrace()
false
}
}
}
private fun setupInternalStorageRecyclerView() = binding.rvPrivatePhotos.apply {
adapter = internalStoragePhotoAdapter
layoutManager = StaggeredGridLayoutManager(3, RecyclerView.VERTICAL)
}
private fun setupExternalStorageRecyclerView() = binding.rvPublicPhotos.apply {
adapter = externalStoragePhotoAdapter
layoutManager = StaggeredGridLayoutManager(3, RecyclerView.VERTICAL)
}
private fun loadPhotosFromInternalStorageIntoRecyclerView() {
lifecycleScope.launch {
val photos = loadPhotosFromInternalStorage()
internalStoragePhotoAdapter.submitList(photos)
}
}
private fun loadPhotosFromExternalStorageIntoRecyclerView() {
lifecycleScope.launch {
val photos = loadPhotosFromExternalStorage()
externalStoragePhotoAdapter.submitList(photos)
}
}
private suspend fun deletePhotoFromInternalStorage(filename: String): Boolean {
return withContext(Dispatchers.IO) {
try {
deleteFile(filename)
} catch (e: Exception) {
e.printStackTrace()
false
}
}
}
private suspend fun loadPhotosFromInternalStorage(): List<InternalStoragePhoto> {
return withContext(Dispatchers.IO) {
val files = filesDir.listFiles()
files?.filter { it.canRead() && it.isFile && it.name.endsWith(".jpg") }?.map {
val bytes = it.readBytes()
val bmp = BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
InternalStoragePhoto(it.name, bmp)
} ?: listOf()
}
}
private suspend fun savePhotoToInternalStorage(filename: String, bmp: Bitmap): Boolean {
return withContext(Dispatchers.IO) {
try {
openFileOutput("$filename.jpg", MODE_PRIVATE).use { stream ->
if(!bmp.compress(Bitmap.CompressFormat.JPEG, 95, stream)) {
throw IOException("Couldn't save bitmap.")
}
}
true
} catch(e: IOException) {
e.printStackTrace()
false
}
}
}
override fun onDestroy() {
super.onDestroy()
contentResolver.unregisterContentObserver(contentObserver)
}
}
Any sort of help would be highly appreciated.
I am getting a class not found exception when trying to click an image from the camera or while fetching it from the directory. The code is in kotlin and has been given below.
This is the class which implements the functionality to capture an image or picks them from the gallery.
The methods _openCamera() and openFileSelector() are implemented.
The main motive was to capture the images and upload them in the server but the implemented methods doesn't give the proper results.
class MainActivity : AppCompatActivity() {
private var drawerResult: Drawer? = null
private var jobschedular: JobScheduler? = null
private var jobschedularCode: Int = 1
private var phoneNumber: String? = null
private var toolbar: Toolbar? = null
private var familyId: String? = null
val TAG: String? = "Activity_Name"
val REQUEST_IMAGE_CAPTURE = 1
val REQUEST_CODE_FOR_GALLERY_CAPTURE = 2
var photoFile: File? = null
var progressDialog: Dialog? = null
private var doubleBackToExitPressedOnce = false
#SuppressLint("PrivateResource")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
overridePendingTransition(R.anim.fade_in, R.anim.fade_out)
Log.d(TAG, "Inside MainActivity")
//onclick listener for open camera
onclickListenerForOpenCamera()
//starting the services here . .
val service_checkAddedtoFamily = Intent(this, checkAddedToFamily::class.java)
startService(service_checkAddedtoFamily)
val service_checkDocsToBeVerified = Intent(this, checkDocsToBeVerified::class.java)
startService(service_checkDocsToBeVerified)
/*findViewById<Button>(R.id.scan).setOnClickListener {
val i = Intent(this, Testers::class.java)
i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
i.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
startActivity(i)
overridePendingTransition(R.anim.fade_in, R.anim.fade_out)
}*/
//onclick listener for select image button
attach_onclick_listener_to_add_photos_from_gallery()
//onclick listener for select pdf files
onclickListenerForSelectPdfFile()
//get toolbar for drawer
toolbar = findViewById(R.id.toolbar_tabs)
//get phone number
val loginInfo = applicationContext.getSharedPreferences("loginInfo", Context.MODE_PRIVATE)
phoneNumber = loginInfo.getString("phoneNumber", "")
//onclick listener for upload button
//onclickListenerForUploadButton()
//onclick listener for retrieve button
onclickListenerForRetrieveButton()
//on click permanent diseases button
//onclickPermanentDiseasesButtton()
//navigation drawer
left_drawer(this, this#MainActivity, toolbar!!).createNavigationDrawer()
//verify auto upload
verifyAutoLoginInformation()
//create Sqlite database
DB_HELPER(this#MainActivity).writableDatabase
//get job schedular service
jobschedular = applicationContext.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
schedulTheJobForHealthGoals()
schedulTheJobForHealthInsurance()
setPreferencesForNutrition()
schedulTheJobForNutrition()
schedulTheJobForSyncNutritionOnline()
}
/*override fun onBackPressed() {
if (doubleBackToExitPressedOnce) {
super.onBackPressed()
return
}
this.doubleBackToExitPressedOnce = true
Toast.makeText(this, "Press back again to exit", Toast.LENGTH_SHORT).show()
Handler().postDelayed(Runnable { doubleBackToExitPressedOnce = false }, 2000)
}*/
//job schedular
fun schedulTheJobForHealthGoals() {
val builder = JobInfo.Builder(jobschedularCode, ComponentName(this#MainActivity, health_goals_services::class.java))
.setPersisted(true)
.setPeriodic(5000)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
.setRequiresCharging(false)
.setRequiresDeviceIdle(false)
val bundle = PersistableBundle()
bundle.putString("key", "value")
builder.setExtras(bundle)
val s_response = jobschedular!!.schedule(builder.build())
if (s_response <= 0) {
//something goes wrong
}
}
fun schedulTheJobForHealthInsurance() {
val builder = JobInfo.Builder(jobschedularCode, ComponentName(this#MainActivity, health_insurance_service::class.java))
.setPersisted(true)
.setPeriodic(5000)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
.setRequiresCharging(false)
.setRequiresDeviceIdle(false)
val bundle = PersistableBundle()
bundle.putString("key", "value")
builder.setExtras(bundle)
val s_response = jobschedular!!.schedule(builder.build())
if (s_response <= 0) {
//something goes wrong
}
}
fun schedulTheJobForNutrition() {
val builder = JobInfo.Builder(jobschedularCode, ComponentName(this#MainActivity, nutrition_service::class.java))
.setPersisted(true)
.setPeriodic(5000) //change to 1 hour
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
.setRequiresCharging(false)
.setRequiresDeviceIdle(false)
val bundle = PersistableBundle()
bundle.putString("key", "value")
builder.setExtras(bundle)
val s_response = jobschedular!!.schedule(builder.build())
if (s_response <= 0) {
//something goes wrong
}
}
fun setPreferencesForNutrition() {
val nutritionInfo = getSharedPreferences("nutrition", Context.MODE_PRIVATE)
val editor = nutritionInfo.edit()
editor.putString("breakFastTime_Hour", "7")
editor.putString("lunchTime_Hour", "14") //TODO: change to 13
editor.putString("DinnerTime_Hour", "20")
editor.apply()
}
fun schedulTheJobForSyncNutritionOnline() {
val builder = JobInfo.Builder(jobschedularCode, ComponentName(this#MainActivity, sync_nutrition_online::class.java))
.setPersisted(true)
.setPeriodic(5000)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
.setRequiresCharging(false)
.setRequiresDeviceIdle(false)
val bundle = PersistableBundle()
bundle.putString("key", "value")
builder.setExtras(bundle)
val s_response = jobschedular!!.schedule(builder.build())
if (s_response <= 0) {
//something goes wrong
}
}
//buttons on home screen
/*fun onclickListenerForUploadButton(){
findViewById<ImageView>(R.id.uploadButton).setOnClickListener{
openModeOfUploadActivity()
}
}*/
fun onclickListenerForRetrieveButton() {
findViewById<Button>(R.id.retrieveButton).setOnClickListener {
openHistoryActivity()
}
}
/*fun onclickPermanentDiseasesButtton(){
findViewById<Button>(R.id.permanentDiseasesButton).setOnClickListener{
openPermanentDiseases()
}
}*/
/*fun openModeOfUploadActivity(){
val intent = Intent(this,MainActivity::class.java)
startActivity(intent)
}*/
fun openHistoryActivity() {
val intent = Intent(this, history_pickFamilyMember::class.java)
startActivity(intent)
overridePendingTransition(R.anim.fade_in, R.anim.fade_out)
}
/*fun openPermanentDiseases(){
val intent = Intent(this,permanentDiseaese::class.java)
startActivity(intent)
}
*/
//verify auto login information
fun verifyAutoLoginInformation() {
val loginInfo = applicationContext.getSharedPreferences("loginInfo", Context.MODE_PRIVATE)
if (loginInfo.contains("familyOrIndividual") == true) {
//for family
if (loginInfo.getString("familyOrIndividual", "").toString() == "f") {
if (loginInfo.contains("phoneNumber") == true && loginInfo.contains("password") == true) {
val phoneNumber = loginInfo.getString("phoneNumber", "")
val password = loginInfo.getString("password", "")
individual_family_login(this#MainActivity).makeFamilyLoginApiRequest(phoneNumber, password)
} else {
left_drawer(this, this#MainActivity, toolbar!!).makeUserLogOut()
}
}
//for individual
if (loginInfo.getString("familyOrIndividual", "").toString() == "i") {
if (loginInfo.contains("phoneNumber") == true && loginInfo.contains("password") == true) {
val phoneNumber = loginInfo.getString("phoneNumber", "")
val password = loginInfo.getString("password", "")
individual_family_login(this#MainActivity).makeLoginApiRequest(phoneNumber, password)
} else {
left_drawer(this, this#MainActivity, toolbar!!).makeUserLogOut()
}
}
//for security
if (loginInfo.getString("familyOrIndividual", "").toString() != "i" && loginInfo.getString("familyOrIndividual", "").toString() != "f") {
left_drawer(this, this#MainActivity, toolbar!!).makeUserLogOut()
}
} else {
left_drawer(this, this#MainActivity, toolbar!!).makeUserLogOut()
}
}
//camera scan
fun onclickListenerForOpenCamera() {
findViewById<ImageView>(R.id.openCamera).setOnClickListener {
get_permissions_camera()
}
}
fun _openCamera() {
Log.d("Errors__", "inside _openCamera()")
try {
val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
try {
photoFile = createImageFile()
} catch (ex: Exception) {
Log.d("Errors__", "inside: " + ex.toString())
}
if (photoFile != null) {
val builder: StrictMode.VmPolicy.Builder = StrictMode.VmPolicy.Builder()
StrictMode.setVmPolicy(builder.build())
val photoURI: Uri = Uri.fromFile(photoFile!!)
Log.d("Path__", "photoURI: $photoURI")
Log.d("Path__", "photoURI.path: " + photoURI.path)
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
takePictureIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE)
}
}
} catch (e: Exception) {
Log.d("Errors__", "_openCamera" + e.toString())
}
}
fun createImageFile(): File {
Log.d("Errors__", "inside createImageFile()")
val mCurrentPhotoPath: String
val imageFileName = "camera"
val storageDir: File = getExternalFilesDir(Environment.DIRECTORY_PICTURES)
val image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
)
// Save a file: path for use with ACTION_VIEW intents
mCurrentPhotoPath = image.getAbsolutePath()
Log.d("Path__", "Image: $image")
return image
}
//file selector
fun onclickListenerForSelectPdfFile() {
findViewById<ImageView>(R.id.selectPdfFile).setOnClickListener {
get_permissions_fileExplorer()
}
}
#SuppressLint("SdCardPath")
fun openFileSelector() {
val properties = DialogProperties()
properties.selection_mode = DialogConfigs.MULTI_MODE;
properties.selection_type = DialogConfigs.FILE_SELECT;
properties.root = File(DialogConfigs.DEFAULT_DIR);
properties.error_dir = File(DialogConfigs.DEFAULT_DIR);
properties.offset = File(DialogConfigs.DEFAULT_DIR);
properties.extensions = null;
val dialog: FilePickerDialog = FilePickerDialog(this#MainActivity, properties)
dialog.setTitle("Select a File")
dialog.setDialogSelectionListener(object : DialogSelectionListener {
override fun onSelectedFilePaths(files: Array<out String>?) {
convertPdfToImages(files!!)
}
})
dialog.show()
}
fun convertPdfToImages(files: Array<out String>) {
showProcessProgress()
doAsync {
var uriList: MutableList<Uri>? = mutableListOf()
val no_of_files = files.size
var counter = 0
while (counter < no_of_files) {
var pdfFile = File(files[counter])
val decodeService = DecodeServiceBase(PdfContext())
decodeService.setContentResolver(applicationContext.getContentResolver())
decodeService.open(Uri.fromFile(pdfFile))
val pageCount: Int = decodeService.getPageCount()
var i = 0
while (i < pageCount) {
val page: PdfPage = decodeService.getPage(i) as PdfPage
val rectF = RectF(0.toFloat(), 0.toFloat(), 1.toFloat(), 1.toFloat())
// do a fit center to 1920x1080
val scaleBy = 1
val with: Int = (page.getWidth() * scaleBy)
val height: Int = (page.getHeight() * scaleBy)
val bitmap: Bitmap = page.renderBitmap(with, height, rectF)
try {
val outputFile = File(applicationContext.externalCacheDir,
System.currentTimeMillis().toString() + ".jpg")
val outputStream = FileOutputStream(outputFile)
// a bit long running
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
uriList!!.add(Uri.fromFile(outputFile))
outputStream.close()
} catch (e: IOException) {
}
i++
}
counter++
}
uiThread {
progressDialog!!.hide()
openPreview(uriList!!)
Log.d("mess", "size: " + uriList.size + " " + uriList.toString())
}
}
}
//select image
fun attach_onclick_listener_to_add_photos_from_gallery() {
findViewById<ImageView>(R.id.selectImage).setOnClickListener {
get_permissions_gallery()
}
}
fun open_selector() {
Matisse.from(this)
.choose(MimeType.allOf())
.countable(true)
.maxSelectable(200)
.restrictOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
.thumbnailScale(0.85f)
.imageEngine(PicassoEngine())
.forResult(REQUEST_CODE_FOR_GALLERY_CAPTURE)
}
//activity results
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == Activity.RESULT_OK) {
var uriOfImage = Uri.fromFile(photoFile)
Log.d("fileCapturing__", "URI Image: $uriOfImage")
//start croper
CropImage.activity(uriOfImage)
.start(this)
}
if (requestCode == REQUEST_CODE_FOR_GALLERY_CAPTURE && resultCode == Activity.RESULT_OK) {
var selected_images = Matisse.obtainResult(data)
openPreview(selected_images!!)
}
//for croper
if (requestCode == CropImage.CROP_IMAGE_ACTIVITY_REQUEST_CODE) {
val result: CropImage.ActivityResult = CropImage.getActivityResult(data)
if (resultCode == RESULT_OK) {
doAsync {
val builder: StrictMode.VmPolicy.Builder = StrictMode.VmPolicy.Builder()
StrictMode.setVmPolicy(builder.build())
var resultUri: Uri = result.getUri()
//save cropped image for persisitance
val croppedImage = createImageFile() //empty
val outputStream = FileOutputStream(croppedImage)
// a bit long running
(Picasso.with(this#MainActivity)
.load(resultUri)
.get()
).compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
Log.d("fileCapturing__", "outputStream: $outputStream")
resultUri = Uri.fromFile(croppedImage)
outputStream.close()
uiThread {
//add to mu list
var mu_list = ArrayList<Uri>(1)
mu_list.add(resultUri)
Log.d("fileCapturing__", "camera uri" + resultUri.toString())
openPreview(mu_list)
}
}
} else if (resultCode == CropImage.CROP_IMAGE_ACTIVITY_RESULT_ERROR_CODE) {
val error: Exception = result.getError()
Log.d("fileCapturing__", "Error: $error")
}
}
}
//preview
fun openPreview(list: MutableList<Uri>) {
val _object = list
val i = Intent(this, typeOfDocument::class.java)
val args = Bundle()
args.putSerializable("ARRAYLIST", _object as java.io.Serializable)
i.putExtra("BUNDLE", args)
startActivity(i)
finish()
}
//get permissions
//Camera
fun get_permissions_camera() {
if (ContextCompat.checkSelfPermission(this#MainActivity, android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
MaterialDialog.Builder(this#MainActivity)
.title("Camera permission")
.content("Camera permissions are required for opening Camera")
.negativeText("Cancel")
.onNegative(object : MaterialDialog.SingleButtonCallback {
override fun onClick(dialog: MaterialDialog, which: DialogAction) {
}
})
.positiveText("Give Permissions")
.onPositive(object : MaterialDialog.SingleButtonCallback {
override fun onClick(dialog: MaterialDialog, which: DialogAction) {
getPermissionsUsingDexter_camera(
android.Manifest.permission.CAMERA
)
}
})
.show()
} else {
_openCamera()
}
}
fun getPermissionsUsingDexter_camera(permissionString: String) {
Dexter.withActivity(this)
.withPermissions(
permissionString
).withListener(object : MultiplePermissionsListener {
override fun onPermissionRationaleShouldBeShown(permissions: MutableList<PermissionRequest>?, token: PermissionToken?) {
}
override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
if (report!!.areAllPermissionsGranted() == true) {
_openCamera()
Log.d("mess", "permission given")
} else {
Log.d("mess", "permission not granted")
}
}
})
.check()
}
//gallery
fun get_permissions_gallery() {
if (ContextCompat.checkSelfPermission(this#MainActivity, android.Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
MaterialDialog.Builder(this#MainActivity)
.title("Storage permission")
.content("Storage permissions are required for opening the Gallery")
.negativeText("Cancel")
.onNegative(object : MaterialDialog.SingleButtonCallback {
override fun onClick(dialog: MaterialDialog, which: DialogAction) {
}
})
.positiveText("Give Permissions")
.onPositive(object : MaterialDialog.SingleButtonCallback {
override fun onClick(dialog: MaterialDialog, which: DialogAction) {
getPermissionsUsingDexter_gallery(
android.Manifest.permission.READ_EXTERNAL_STORAGE
)
}
})
.show()
} else {
open_selector()
}
}
fun getPermissionsUsingDexter_gallery(permissionString: String) {
Dexter.withActivity(this)
.withPermissions(
permissionString
).withListener(object : MultiplePermissionsListener {
override fun onPermissionRationaleShouldBeShown(permissions: MutableList<PermissionRequest>?, token: PermissionToken?) {
}
override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
if (report!!.areAllPermissionsGranted() == true) {
open_selector()
Log.d("mess", "permission given")
} else {
Log.d("mess", "permission not granted")
}
}
})
.check()
}
//file exploer
fun get_permissions_fileExplorer() {
if (ContextCompat.checkSelfPermission(this#MainActivity, android.Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
MaterialDialog.Builder(this#MainActivity)
.title("Storage permission")
.content("Storage access permissions are required for opening File Explorer")
.negativeText("Cancel")
.onNegative(object : MaterialDialog.SingleButtonCallback {
override fun onClick(dialog: MaterialDialog, which: DialogAction) {
}
})
.positiveText("Give Permissions")
.onPositive(object : MaterialDialog.SingleButtonCallback {
override fun onClick(dialog: MaterialDialog, which: DialogAction) {
getPermissionsUsingDexter_fileExplores(
android.Manifest.permission.READ_EXTERNAL_STORAGE
)
}
})
.show()
} else {
openFileSelector()
}
}
fun getPermissionsUsingDexter_fileExplores(permissionString: String) {
Dexter.withActivity(this)
.withPermissions(
permissionString
).withListener(object : MultiplePermissionsListener {
override fun onPermissionRationaleShouldBeShown(permissions: MutableList<PermissionRequest>?, token: PermissionToken?) {
}
override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
if (report!!.areAllPermissionsGranted() == true) {
openFileSelector()
Log.d("mess", "permission given")
} else {
Log.d("mess", "permission not granted")
}
}
})
.check()
}
//progress bar
fun showProcessProgress() {
progressDialog = MaterialDialog.Builder(this)
.title("Please Wait")
.content("Converting Pdf to Images")
.progress(true, 0)
.show()
}
}
The error shows when I try to click an image or fetch from the library.
The provider path used is given below:
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="external_files"
path="." />
</paths>
I have tried changing the path to "/" but it didn't work. The error wasn't showing earlier but the error exists now.
Here is the snapshot of the logs.
All suggestions are accepted. Thanks in advance.