I am getting a NullPointerException
when trying to take a photo on camera application and upload it on to the server side, but some devices it works fine, and some getting issues. i don't know what is the issues, in some devices it works once and getting crashed.
the issues are identified by using firebase CrashAnalytics
.
the fragment class.
class PhotoUploadFragment : BaseFragment() {
private lateinit var viewModel: SelectOptionViewModel
//define caseId type
private var caseId: String? = ""
//define caseNo type
private var caseNo: String? = ""
//define image clarity
private var imageClarity: String? = ""
//define image width
private var imageWidth: String? = ""
//define image height
private var imageHeight: String? = ""
//location
lateinit var mFusedLocationClient: FusedLocationProviderClient
//random id
val PERMISSION_ID = 42
//Initialize lat and long to get loc address
var longitude = 0.0
var latitude = 0.0
//Capture bitmap image
var capturedBitmap = BitmapFactory.decodeStream(null)
//Set timestamp when giving name while saving image
var addrTimeStamp = ""
//Initialize location address
var locationAddress = ""
companion object {
private var args: Bundle? = null
//pass the argument that needs to be displayed
fun newInstance(case_id: String, case_number: String): Fragment {
// fun newInstance(case_id: String, case_number: String): Fragment {
args = Bundle()
//pass the value caseId
args!!.putString(CASE_ID, case_id)
//pass the value caseNo
args!!.putString(CASE_NO, case_number)
val fragment = PhotoUploadFragment()
fragment.arguments = args
return fragment
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
//setup view model
viewModel = ViewModelProviders.of(this)[SelectOptionViewModel::class.java]
// homeViewModel = ViewModelProviders.of(this)[HomeViewModel::class.java]
val view: View = inflater.inflate(
layout.fragment_tab_photo_upload,
container,
false
)
//get caseId
caseId = arguments?.getString(CASE_ID)
//get caseNo
caseNo = arguments?.getString(CASE_NO)
if (caseNo == null || caseNo == "") {
caseNo = "Nil"
}
//get imageClarity
imageClarity = PrefsHelper().getPref<String>(Constants.IMAGE_CLARITY)
//get imageWidth
imageWidth = PrefsHelper().getPref<String>(Constants.IMAGE_WIDTH)
//get imageHeight
imageHeight = PrefsHelper().getPref<String>(Constants.IMAGE_HEIGHT)
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this.activity!!)
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
//to clear all the data inside the fragment to avoid multiple API calls
viewModel.imageUploadresponseLiveData.value = null
//to enable upload btn only after image is taken
photo_upload_button_upload.isEnabled = false
getLastLocation()
setUpListeners()
setupObservers()
}
private fun setupObservers() {
viewModel.imageUploadresponseLiveData.observe(this, androidx.lifecycle.Observer {
if (it != null) {
//photo upload success
if (it.success == 1 && it.result.success == 1) {
Toast.makeText(context, "Image uploaded successfully.", Toast.LENGTH_SHORT)
.show()
//delete file after upload
val root = Environment.getExternalStorageDirectory().toString()
val myDir = File(root + "/CV App/nomedia/images/")
myDir.deleteRecursively()
photo_upload_imageview_photo.setImageBitmap(null)
//to enable upload btn only after image is taken
photo_upload_button_upload.isEnabled = false
}
//photo upload fail
else {
Toast.makeText(
context,
"Failed to update image. Please try again.",
Toast.LENGTH_SHORT
).show()
}
}
})
//observe API call status
viewModel.imageUploadAPICallStatus.observe(this, androidx.lifecycle.Observer {
processStatus(it)
})
}
private fun setUpListeners() {
photo_upload_button_open.setOnClickListener {
//Check camera permission
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkSelfPermission(
this.activity!!,
Manifest.permission.CAMERA
) != PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(
this.activity!!,
arrayOf(Manifest.permission.CAMERA),
1
)
}
}
//Check storage permission
if (ActivityCompat.checkSelfPermission(
this.activity!!,
Manifest.permission.WRITE_EXTERNAL_STORAGE
) != PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(
this.activity!!,
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), REQUEST_PERM_WRITE_STORAGE
)
}
//if permission is allowed,open camera
else {
startCamera()
}
photo_upload_button_upload.setOnClickListener {
showDialogUpload()
}
}
}
private fun startCamera() {
Intent(MediaStore.ACTION_IMAGE_CAPTURE).also { cameraIntent ->
// Ensure that there's a camera activity to handle the intent
cameraIntent.resolveActivity(context!!.packageManager)?.also {
// Create the File where the photo should go
val photoFile: File? = try {
createImageFile()
} catch (ex: IOException) {
// Error occurred while creating the File
//...
null
}
// Continue only if the File was successfully created
photoFile?.also {
val photoURI: Uri = FileProvider.getUriForFile(
this.activity!!,
"com.xyzshopapp.fileprovider",
it
)
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
cameraIntent.putExtra("return-data", true)
this.startActivityForResult(cameraIntent, ACTION_IMAGE_CAPTURE)
}
}
}
}
/**
* Create external image file, this file will be passed to Camera for saving the captured image
*/
@Throws(IOException::class)
private fun createImageFile(): File {
// Create an image file name
val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
//Set timestamp to addrTimeStamp
addrTimeStamp = timeStamp
val root = Environment.getExternalStorageDirectory().toString()
val myDir = File("$root/CV App/nomedia/images")
myDir.mkdirs()
return File.createTempFile(
"JPG_${addrTimeStamp}_", /* prefix */
".jpg", /* suffix */
myDir /* directory */
).apply {
// Save a file: path for use with ACTION_VIEW intents
fileTemp = this
//Save the complete file path to string
imageFileName = this.toURI().path.toString()
}
}
//show thumbnail
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK ) {
when (requestCode) {
ACTION_IMAGE_CAPTURE -> {
val imageQuality = imageClarity
val byteArrayOutputStream = ByteArrayOutputStream()
//Decode file to bitmap
capturedBitmap = BitmapFactory.decodeFile(imageFileName)
val a = capturedBitmap
val destinationHeight = imageHeight
val destinationWidth = imageWidth
var imageToScale = capturedBitmap
//test
var uncompressed = capturedBitmap
if (destinationHeight != null) {
if (destinationWidth != null) {
if (destinationHeight.toInt() > 0 && destinationWidth.toInt() > 0 && imageToScale != null) {
val width = imageToScale.width
val height = imageToScale.height
//Calculate the max changing amount and decide which dimension to use
val widthRatio = destinationWidth.toFloat() / width.toFloat()
val heightRatio = destinationHeight.toFloat() / height.toFloat()
//Use the ratio that will fit the image into the desired sizes
var finalWidth = floor((width * widthRatio).toDouble()).toInt()
var finalHeight = floor((height * widthRatio).toDouble()).toInt()
if (finalWidth > destinationWidth.toInt() || finalHeight > destinationHeight.toInt()) {
finalWidth = floor((width * heightRatio).toDouble()).toInt()
finalHeight = floor((height * heightRatio).toDouble()).toInt()
}
//Scale given bitmap to fit into the desired area
imageToScale = Bitmap.createScaledBitmap(imageToScale, finalWidth, finalHeight, false)
//Created a bitmap with desired sizes
val scaledImage =
Bitmap.createBitmap(destinationWidth.toInt(), destinationHeight.toInt(), Bitmap.Config.ARGB_8888)
uncompressed = imageToScale
}
}
}
//compress by quality
if (imageQuality != null) {
uncompressed.compress(
Bitmap.CompressFormat.JPEG,
imageQuality.toInt(),
byteArrayOutputStream
)
/*try{
}catch (e:Exception)
{
println("this is exception $e")
}*/
}
val byteArray = byteArrayOutputStream.toByteArray()
//Encode bitmap to file
val compressedImageFile = FileOutputStream(imageFileName)
compressedImageFile.write(byteArray)
compressedImageFile.flush()
compressedImageFile.close()
//Set image in thumbnail
photo_upload_imageview_photo.setImageBitmap(a)
//to enable upload btn only after image is taken
photo_upload_button_upload.isEnabled = true
}
else -> {
Toast.makeText(context, "Unrecognized request code.", Toast.LENGTH_SHORT).show()
}
}
}
}
//popup box for uploading photo
fun showDialogUpload() {
val dialog = Dialog(context!!)
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
dialog.setCancelable(true)
dialog.setContentView(layout.photo_upload)
val photo_upload_submit =
dialog.findViewById(com.xyz.shopapp.R.id.image_button_submit) as Button
val spinner =
dialog.findViewById(com.xyz.shopapp.R.id.photo_upload_spinner_image_name) as Spinner
//get array of image names
val imageName = (PrefsHelper().getPref<String>("ImageName"))
//convert array to string
val gson = Gson()
val imageNameList = gson.fromJson(imageName, Array<String>::class.java).asList()
//dropdown menu
if (spinner != null) {
val adapter =
ArrayAdapter<String>(context!!, android.R.layout.simple_spinner_item, imageNameList)
// Set layout to use when the list of choices appear
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
// Set Adapter to Spinner
spinner.adapter = adapter
//get image name
var itemSpinnerSelected = ""
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
parent: AdapterView<*>,
view: View,
position: Int,
id: Long
) {
// Get the selected item
itemSpinnerSelected = "${parent.getItemAtPosition(position)}"
}
override fun onNothingSelected(parent: AdapterView<*>) {
// Another interface callback
}
}
photo_upload_submit.setOnClickListener {
if (getAddress()) {
viewModel.submitImageData(
caseId, caseNo,
imageFileName, latitude, longitude, itemSpinnerSelected, locationAddress
)
// viewModel.submitImageData(
// caseId, caseNo,
// imageFileName, latitude, longitude, itemSpinnerSelected, locationAddress
// )
// viewModel.submitImageData(caseId,caseNo,rootDir + "/CV App/nomedia/images/avc6937492103605367617.jpg",latitude,longitude,itemSpinnerSelected, locationAddress)
// dismissDialog()
// val progressBar = findViewById(com.xyz.shopapp.R.id.progresbar) as ProgressBar
// progressBar.visibility = View.INVISIBLE
}
dialog.dismiss()
}
}
dialog.show()
}
//get location
//check whether location is enabled for application
@SuppressLint("MissingPermission")
private fun getLastLocation() {
// var latitude = 0.0
if (checkPermissions()) {
//if (isLocationEnabled()) {
mFusedLocationClient.lastLocation.addOnCompleteListener(this.activity!!) { task ->
val location: Location? = task.result
if (location == null) {
requestNewLocationData()
} else {
latitude = location.latitude
longitude = location.longitude
getAddress()
}
}
/*mFusedLocationClient.lastLocation.addOnCompleteListener(this.activity!!) { task ->
var location: Location? = task.result
if (location == null) {
requestNewLocationData()
} else {
latitude = location.latitude
longitude = location.longitude
getAddress()
}
}*/
// } else {
// Toast.makeText(context, "Turn on your location.", Toast.LENGTH_LONG).show()
// val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
// startActivity(intent)
// }
} else {
//requestPermissions()
//Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION
//to get location in first try itself
val coraseLocationPermission = checkSelfPermission(
this.activity!!,
Manifest.permission.ACCESS_COARSE_LOCATION
)
val accessFineLocation = checkSelfPermission(
this.activity!!,
Manifest.permission.ACCESS_FINE_LOCATION
)
if (coraseLocationPermission != PackageManager.PERMISSION_GRANTED && accessFineLocation != PackageManager.PERMISSION_GRANTED) {
requestPermissions(
arrayOf(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
),
PERMISSION_ID
)
}
}
}
@SuppressLint("MissingPermission")
private fun requestNewLocationData() {
val mLocationRequest = LocationRequest()
mLocationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
mLocationRequest.interval = 0
mLocationRequest.fastestInterval = 0
mLocationRequest.numUpdates = 1
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this.activity!!)
mFusedLocationClient.requestLocationUpdates(
mLocationRequest, mLocationCallback,
Looper.myLooper()
)
}
private val mLocationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
val mLastLocation: Location = locationResult.lastLocation
latitude = mLastLocation.latitude
longitude = mLastLocation.longitude
}
}
// private fun isLocationEnabled(): Boolean {
// var locationManager: LocationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
// return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) || locationManager.isProviderEnabled(
// LocationManager.NETWORK_PROVIDER
// )
// }
private fun checkPermissions(): Boolean {
if (ActivityCompat.checkSelfPermission(
this.activity!!,
Manifest.permission.ACCESS_COARSE_LOCATION
) == PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(
this.activity!!,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
) {
return true
}
return false
}
//check location permission
private fun requestPermissions() {
ActivityCompat.requestPermissions(
this.activity!!
,
arrayOf(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
),
PERMISSION_ID
)
}
//request location permission
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
if (requestCode == PERMISSION_ID) {
if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
mFusedLocationClient.lastLocation.addOnCompleteListener(this.activity!!) { task ->
val location: Location? = task.result
if (location == null) {
requestNewLocationData()
} else {
latitude = location.latitude
longitude = location.longitude
// Call API
}
}
requestNewLocationData()
} else {
getLastLocation()
}
}
}
//get address from location
var addresses: List<Address> = emptyList()
private fun getAddress(): Boolean {
// showDialog()
var flag = false
locationAddress = ""
try {
val geocoder = Geocoder(context, Locale.getDefault())
addresses = geocoder.getFromLocation(latitude,
longitude, 1
) // Here 1 represent max location result to returned, by documents it recommended 1 to 5
//null checking
val address_size = addresses.size
if (address_size > 0) {
locationAddress = addresses.get(0)
.getAddressLine(0) // If any additional address line present than only, check with max available address lines by getMaxAddressLineIndex()
flag = true
}
} catch (ioException: IOException) {
val errorMessage = getString(string.service_not_available)
Log.e(ContentValues.TAG, errorMessage + " saf not avail", ioException)
}
dismissDialog()
return flag
}
private fun processStatus(resource: ResourceStatus) {
when (resource.status) {
StatusType.SUCCESS -> {
dismissDialog()
// Toast.makeText(context, "Photo Uploaded Successfully.", Toast.LENGTH_SHORT).show()
}
StatusType.EMPTY_RESPONSE -> {
dismissDialog()
}
StatusType.PROGRESSING -> {
showDialog()
}
StatusType.SWIPE_RELOADING -> {
}
StatusType.ERROR -> {
// var fail_status = "Failed to update image. Please try again."
// Toast.makeText(context, fail_status, Toast.LENGTH_SHORT).show()
dismissDialog()
}
StatusType.LOADING_MORE -> {
// CommonUtils().showSnackbar(binding.root, "Loading more..")
}
StatusType.NO_NETWORK -> {
val internet_failure = "Please check your internet connection."
Toast.makeText(context, internet_failure, Toast.LENGTH_SHORT).show()
}
StatusType.SESSION_EXPIRED -> {
// var session_expired = "Invalid credentials. Login failed"
// Toast.makeText(this, session_expired, Toast.LENGTH_SHORT).show()
}
}
}
///
}
The line which causes the error is
uncompressed.compress(Bitmap.CompressFormat.JPEG,imageQuality.toInt(),byteArrayOutputStream)
error log shown in firebase crashAnalytics console:
Caused by java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.graphics.Bitmap.compress(android.graphics.Bitmap$CompressFormat, int, java.io.OutputStream)' on a null object reference
at com.axionz.shopapp.ui.tabfragment.PhotoUploadFragment.onActivityResult(PhotoUploadFragment.kt:353)
at androidx.fragment.app.FragmentActivity.onActivityResult(FragmentActivity.java:170)
at android.app.Activity.dispatchActivityResult(Activity.java:8464)
at android.app.ActivityThread.deliverResults(ActivityThread.java:5355)
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4755)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4810)
at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:52)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:187)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:102)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2307)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:254)
at android.app.ActivityThread.main(ActivityThread.java:8243)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:612)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1006)