Following is the code inside my SplashActivity.kt. Whenever there is an update available, mostly it doesn't show at all, but I think that is not because of any mistake in my code but as I learned it takes some time to show the user about the new update. My concern now is that when it shows the in-app update screen, it goes off and the app continues to the next screen and it doesn't ask the user to update the app to continue using the app. Have I made a mistake?
#Suppress("DEPRECATION")
class SplashActivity : AppCompatActivity() {
private lateinit var binding: ActivitySplashBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivitySplashBinding.inflate(layoutInflater)
setContentView(binding.root)
callInAppUpdate()
window.setFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN
)
Handler().postDelayed(
{
val currentUserID = FirestoreClass().getCurrentUserID()
if (currentUserID.isNotEmpty()) {
startActivity(Intent(this#SplashActivity, HomeActivity::class.java))
} else {
startActivity(
Intent(
this#SplashActivity,
LoginActivity::class.java
)
)
}
finish()
},
2500
)
}
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (data == null) return
if (requestCode == updateRequestCode) {
Toast.makeText(this, "Downloading", Toast.LENGTH_SHORT).show()
if (resultCode != RESULT_OK) {
Log.d("TAG", "update flow failed $resultCode")
}
}
}
private val updateRequestCode = 1612
private fun callInAppUpdate() {
val appUpdateManager = AppUpdateManagerFactory.create(this)
val appUpdateInfoTask = appUpdateManager.appUpdateInfo
appUpdateInfoTask.addOnSuccessListener { appUpdateInfo ->
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
&& appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)
) {
appUpdateManager.startUpdateFlowForResult(
appUpdateInfo, AppUpdateType.IMMEDIATE, this, updateRequestCode
)
}
}
}
}
EDIT:
I moved the postDelayed to the addOnSuccessListener, the fun callInAppUpdate() now look like the following.
private fun callInAppUpdate() {
val appUpdateManager = AppUpdateManagerFactory.create(this)
val appUpdateInfoTask = appUpdateManager.appUpdateInfo
appUpdateInfoTask.addOnSuccessListener { appUpdateInfo ->
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
&& appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)
) {
appUpdateManager.startUpdateFlowForResult(
appUpdateInfo, AppUpdateType.IMMEDIATE, this, updateRequestCode
)
}else{
Handler().postDelayed(
{
val currentUserID = FirestoreClass().getCurrentUserID()
if (currentUserID.isNotEmpty()) {
startActivity(Intent(this#SplashActivity, DashboardActivity::class.java))
} else {
startActivity(
Intent(
this#SplashActivity,
LoginWithPhoneNumberActivity::class.java
)
)
}
finish()
},
2500
)
}
}
}
You could achieve your behaviour like below:
class SplashActivity : AppCompatActivity() {
private val updateRequestCode = 1612
private lateinit var binding: ActivitySplashBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivitySplashBinding.inflate(layoutInflater)
setContentView(binding.root)
callInAppUpdate()
window.setFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN
)
}
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (data == null) return
if (requestCode == updateRequestCode) {
Toast.makeText(this, "Downloading", Toast.LENGTH_SHORT).show()
if (resultCode != RESULT_OK) {
Log.d("TAG", "update flow failed $resultCode")
navigate()
}
}
}
private fun navigate(){
Handler().postDelayed(
{
val currentUserID = FirestoreClass().getCurrentUserID()
if (currentUserID.isNotEmpty()) {
startActivity(Intent(this#SplashActivity, DashboardActivity::class.java))
} else {
startActivity(
Intent(
this#SplashActivity,
LoginWithPhoneNumberActivity::class.java
)
)
}
finish()
},
2500
)
}
private fun callInAppUpdate() {
val appUpdateManager = AppUpdateManagerFactory.create(this)
val appUpdateInfoTask = appUpdateManager.appUpdateInfo
appUpdateInfoTask.addOnSuccessListener { appUpdateInfo ->
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
&& appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) {
appUpdateManager.startUpdateFlowForResult(
appUpdateInfo, AppUpdateType.IMMEDIATE, this, updateRequestCode
)
}
else {
navigate()
}
}
}
}
You have navigation logic inside postDelayed. You are explicitly navigating to new activity after 2500 ms. So the code is doing exactly what you wrote.
If you want to navigate after checking for the update then you probably should move navigation logic to addOnSuccessListener although it does seems like a hack too. Either way i wouldn't do this check in the splash screen but in some more "static" screen that does not have navigation on timer set
Related
I am new to android development and trying to teach myself. Currently I am trying to add a functionality in my app where I click a button and the gallery opens up for a user to select an image to be used later and then a dialog box is supposed to show up after the gallery closes. However, after closing the gallery nothing happens and it seems that the onActivityResult() code does not run for some reason. I get no error in logcat and Log.d statments do not show up. Code below. Thanks!
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Log.d("TEST1", "FUN EXECUTED")
val fab = view.findViewById<FloatingActionButton>(R.id.fababc)
fab.setOnClickListener {
showCreateTodoList()
Log.d("TEST2", "RECIEVING INPUT")
}
}
private fun showCreateTodoList() {
openGallery()
}
private fun openGallery() {
val gallery = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.INTERNAL_CONTENT_URI)
requireActivity().startActivityForResult(gallery, p)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if(resultCode == AppCompatActivity.RESULT_OK && requestCode == p){
Log.d("TESTING", "THIS CODE RUNS")
imageURI = data!!.data
activity?.let {
val dialogTitle = getString(R.string.newScp)
val positiveButtonTitle = getString(R.string.create)
val myDialog = AlertDialog.Builder(it)
val todoTitleEditText = EditText(it)
todoTitleEditText.inputType =
InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_CAP_WORDS
myDialog.setTitle(dialogTitle)
myDialog.setView(todoTitleEditText)
myDialog.setPositiveButton(positiveButtonTitle) { dialog, _ ->
uri = imageURI.toString()
val list = viewData(todoTitleEditText.text.toString(),uri,R.drawable.image,"SCP Name: ", "SCP Class", "Date Discovered: ", "Kill Count:" )
addToList(list)
dialog.dismiss()
ListItemCLickedu(list)
}
myDialog.create().show()
}
}
else{ Log.d("TESTING2", "THIS CODE DOES NOT RUN!")
}
}
Based on your code,i try this and works fine:
private lateinit var imageURI: Uri
companion object {
const val REQUEST_MEDIA = 101
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.d("TEST1", "FUN EXECUTED")
button.setOnClickListener {
showCreateTodoList()
Log.d("TEST2", "RECIEVING INPUT")
}
}
private fun showCreateTodoList() {
openGallery()
}
private fun openGallery() {
val gallery = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.INTERNAL_CONTENT_URI)
startActivityForResult(gallery, REQUEST_MEDIA)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == RESULT_OK && requestCode == REQUEST_MEDIA) {
Log.d("TESTING", "THIS CODE RUNS")
imageURI = data?.data!!
// Use the Builder class for convenient dialog construction
val builder = AlertDialog.Builder(this)
builder.setMessage("Dummy Dialog Title")
.setPositiveButton("Ok",
DialogInterface.OnClickListener { dialog, id ->
// what you want to do on positive click
dialog.dismiss()
})
.setNegativeButton("Cancel",
DialogInterface.OnClickListener { dialog, id ->
// User cancelled the dialog
dialog.dismiss()
})
// Create the AlertDialog object and return it
builder.create().show()
}
}
Please check if you add permissions in your manifest file and also if you are in fragment check if activity in activity?.let is null. If so, try something getBaseActivity()?.let or applicationContext?.let and in there display your dialog.
On my app, users can save some notes with image. But, users can not save their notes without image. Thus image is compulsory to save their data. But ı want to change it. I want to allow them to save their note without image. How can ı do this ? This is my saveclcikbutton codes;
class TakeNotesActivity : AppCompatActivity() {
var selectedPicture: Uri? = null
var selectedBitmap: Bitmap? = null
private lateinit var db: FirebaseFirestore
private lateinit var auth: FirebaseAuth
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_take_notes)
auth = FirebaseAuth.getInstance()
db = FirebaseFirestore.getInstance()
}
fun putPictureClick(view: View) {
val popupMenu = PopupMenu(this, view)
val inflater = popupMenu.menuInflater
inflater.inflate(R.menu.secondmenu, popupMenu.menu)
popupMenu.show()
popupMenu.setOnMenuItemClickListener {
if (it.itemId == R.id.galleryImage) {
if (ContextCompat.checkSelfPermission(
this,
android.Manifest.permission.READ_EXTERNAL_STORAGE
) != PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(
this,
arrayOf(android.Manifest.permission.READ_EXTERNAL_STORAGE),
1
)
} else {
val intentToGallery =
Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
startActivityForResult(intentToGallery, 2)
}
}
if (it.itemId == R.id.capturePhoto) {
if (ContextCompat.checkSelfPermission(
this,
android.Manifest.permission.CAMERA
) != PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(
this,
arrayOf(android.Manifest.permission.CAMERA),
10
)
} else {
openCamera()
}
}
if (it.itemId == R.id.cancel) {
Toast.makeText(applicationContext, "Canceled", Toast.LENGTH_LONG).show()
}
true
}
}
fun openCamera() {
val values = ContentValues()
values.put(MediaStore.Images.Media.TITLE, "New Picture")
values.put(MediaStore.Images.Media.DESCRIPTION, "From the Camera")
selectedPicture =
contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
// Camera Intent
val cameraIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, selectedPicture)
startActivityForResult(cameraIntent, 20)
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
if (requestCode == 1) {
if (grantResults.size > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
val intentToGallery =
Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
startActivityForResult(intentToGallery, 2)
}
}
if (requestCode == 10) {
if (grantResults.size > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
openCamera()
} else {
Toast.makeText(this, "Permission Denied", Toast.LENGTH_LONG).show()
}
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == 2 && resultCode == Activity.RESULT_OK && data != null) {
selectedPicture = data.data
try {
if (selectedPicture != null) {
if (Build.VERSION.SDK_INT >= 28) {
val source =
ImageDecoder.createSource(this.contentResolver, selectedPicture!!)
selectedBitmap = ImageDecoder.decodeBitmap(source)
imageButton.setImageBitmap(selectedBitmap)
} else {
selectedBitmap =
MediaStore.Images.Media.getBitmap(this.contentResolver, selectedPicture)
imageButton.setImageBitmap(selectedBitmap)
}
}
} catch (e: Exception) {
}
}
//called when image was captured from camera intent
if (requestCode == 20 && resultCode == Activity.RESULT_OK && data != null) {
imageButton.setImageURI(selectedPicture)
}
super.onActivityResult(requestCode, resultCode, data)
}
fun saveClick(view: View) {
//UUID -> Image Name
val uuid = UUID.randomUUID()
val imageName = "$uuid.jpg"
val storage = FirebaseStorage.getInstance()
val reference = storage.reference
val imagesReference = reference.child("images").child(imageName)
if (selectedPicture != null) {
imagesReference.putFile(selectedPicture!!).addOnSuccessListener { taskSnapshot ->
// take the picture link to save the database
val uploadedPictureReference =
FirebaseStorage.getInstance().reference.child("images").child(imageName)
uploadedPictureReference.downloadUrl.addOnSuccessListener { uri ->
val downloadUrl = uri.toString()
println(downloadUrl)
val noteMap = hashMapOf<String, Any>()
noteMap.put("downloadUrl", downloadUrl)
noteMap.put("userEmail", auth.currentUser!!.email.toString())
noteMap.put("noteTitle", titleText.text.toString())
noteMap.put("yourNote", noteText.text.toString())
noteMap.put("date", Timestamp.now())
db.collection("Notes").add(noteMap).addOnCompleteListener { task ->
if (task.isComplete && task.isSuccessful) {
finish()
}
}.addOnFailureListener { exception ->
Toast.makeText(
applicationContext,
exception.localizedMessage?.toString(),
Toast.LENGTH_LONG
).show()
}
}
}
}
}
}
I also try to add on saveClick fun the codes below but did not work. What should I do ?
fun saveClick(view: View) {
//UUID -> Image Name
val uuid = UUID.randomUUID()
val imageName = "$uuid.jpg"
val storage = FirebaseStorage.getInstance()
val reference = storage.reference
val imagesReference = reference.child("images").child(imageName)
if (selectedPicture != null) {
imagesReference.putFile(selectedPicture!!).addOnSuccessListener { taskSnapshot ->
// take the picture link to save the database
val uploadedPictureReference =
FirebaseStorage.getInstance().reference.child("images").child(imageName)
uploadedPictureReference.downloadUrl.addOnSuccessListener { uri ->
val downloadUrl = uri.toString()
println(downloadUrl)
val noteMap = hashMapOf<String, Any>()
noteMap.put("downloadUrl", downloadUrl)
noteMap.put("userEmail", auth.currentUser!!.email.toString())
noteMap.put("noteTitle", titleText.text.toString())
noteMap.put("yourNote", noteText.text.toString())
noteMap.put("date", Timestamp.now())
db.collection("Notes").add(noteMap).addOnCompleteListener { task ->
if (task.isComplete && task.isSuccessful) {
finish()
}
}.addOnFailureListener { exception ->
Toast.makeText(
applicationContext,
exception.localizedMessage?.toString(),
Toast.LENGTH_LONG
).show()
}
}
}
}else {
val noteMap = hashMapOf<String, Any>()
noteMap.put("userEmail", auth.currentUser!!.email.toString())
noteMap.put("noteTitle", titleText.text.toString())
noteMap.put("yourNote", noteText.text.toString())
noteMap.put("date", Timestamp.now())
db.collection("Notes").add(noteMap).addOnCompleteListener { task ->
if (task.isComplete && task.isSuccessful) {
finish()
}
}
}
}
}
So all your logic is basically wrapped up in that putFile success callback, which requires an image to be successfully stored and retrieved (?) before anything will be added to the database.
You need to break that logic up, so you can only run the parts you want to - like just running the database update part if you don't have an image, or running that part later once your image is successfully handled.
So really, you need a "store in database" function to handle the final writing - call that directly if you don't have an image, call it in the success callbacks if you do. I'm just going to pseudocode this but:
saveData(noteMap: Map) {
add note to DB
}
saveClick() {
create noteMap with basic, non-image details (email, title etc)
if image is null, call saveData(noteMap)
else do the image stuff:
onSuccess:
add the downloadUrl to noteMap
call saveData(noteMap)
}
I see you've made an edit with an else branch which creates the map sans url and writes it - you're basically almost there, just make it a function instead, and pass the map in!
I've got a question about results/callback from activities to fragments.
Until now I have a fragment which calls a camera activity to scan QR Codes. So I start the activity from the fragment with startActivityForResult. If a QR Code is successfully scanned I get a callback Intent which is handled in onActivityResult.
This works perfectly.
Now I want to handle multiple scanns. In detail that means, that every successfully scan should call the onActivityResult function without closing the activity. The problem which I got at this point is, that onActivityResult is only called if I call finish() in the camera activity.
So my question is, how can I call onActivityResult multiple times with or without calling finish() but without closing the activity? Or is there another way to handle callbacks from activities to fragments?
This is my fragment code:
class ScanFragment : Fragment() {
private val CHECKIN_CODE = 0
private val CHECKOUT_CODE = 1
companion object {
fun newInstance(): LeadScanFragment = LeadScanFragment()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_lead_scan, container, false)
view.checkin.setOnClickListener {view ->
val intent = Intent(activity, CodeScannerActivity::class.java)
startActivityForResult(intent, CHECKIN_CODE)
}
view.checkout.setOnClickListener {view ->
val intent = Intent(activity, CodeScannerActivity::class.java)
startActivityForResult(intent, CHECKOUT_CODE)
}
return view
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == CHECKIN_CODE) {
if (resultCode == Activity.RESULT_OK) {
val returnString = data!!.getStringExtra("hash")
Log.d("scaned in", returnString)
}
}
if (requestCode == CHECKOUT_CODE) {
if (resultCode == Activity.RESULT_OK) {
val returnString = data!!.getStringExtra("hash")
Log.d("scaned out", returnString)
}
}
}
}
And this is the camera activity code:
class CodeScannerActivity : AppCompatActivity() {
private val requestCodeCameraPermission = 1001
private lateinit var cameraSource: CameraSource
private lateinit var detector: BarcodeDetector
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_code_scanner)
if (ContextCompat.checkSelfPermission(this#CodeScannerActivity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
askForCameraPermission()
} else {
setup()
}
}
private fun setup() {
detector = BarcodeDetector.Builder(this#CodeScannerActivity).build()
cameraSource = CameraSource.Builder(this#CodeScannerActivity, detector).setAutoFocusEnabled(true).build()
cameraSurfaceView.holder.addCallback(surfaceCallback)
detector.setProcessor(processor)
}
private fun askForCameraPermission() {
ActivityCompat.requestPermissions(this#CodeScannerActivity, arrayOf(Manifest.permission.CAMERA), requestCodeCameraPermission)
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if(requestCode == requestCodeCameraPermission && grantResults.isNotEmpty()) {
if(grantResults[0] == PackageManager.PERMISSION_GRANTED) {
setup()
} else {
Toast.makeText(applicationContext, "Permission denied!", Toast.LENGTH_SHORT).show()
}
}
}
private val surfaceCallback = object : SurfaceHolder.Callback {
override fun surfaceCreated(surfaceHolder: SurfaceHolder?) {
try {
cameraSource.start(surfaceHolder)
} catch (exception: Exception) {
Toast.makeText(applicationContext, "Something went wrong", Toast.LENGTH_SHORT).show()
}
}
override fun surfaceChanged(p0: SurfaceHolder?, p1: Int, p2: Int, p3: Int) {
}
override fun surfaceDestroyed(p0: SurfaceHolder?) {
cameraSource.stop()
}
}
private val processor = object : Detector.Processor<Barcode> {
override fun release() {
}
override fun receiveDetections(detections: Detector.Detections<Barcode>?) {
val intent = Intent()
if(detections != null && detections.detectedItems.isNotEmpty()) {
val qrCodes: SparseArray<Barcode> = detections.detectedItems
val code = qrCodes.valueAt(0)
intent.putExtra("hash", code.displayValue)
setResult(Activity.RESULT_OK, intent)
finish()
} else {
setResult(Activity.RESULT_CANCELED, intent)
finish()
}
}
}
}
receiveDetections inside the processor in the lower area of the camera activity code is where the callback Intent is send back to onActivityResult.
You could have the scanner Activity send a local broadcast Intent to forward "results" to the calling Fragment. The Fragment (or its hosting Activity) should set a listener to listen for the broadcast "results". In this way you could perform multiple scans and send each result back to the underlying Activity.
I am not getting permission result callback on Activity Result api as mentioned in the doc.
Getting a result from an activity
I am using implementation "androidx.activity:activity-ktx:1.2.0-alpha02"
Here is the api code :
/**
* An {#link ActivityResultContract} to {#link Activity#requestPermissions request a permission}
*/
public static class RequestPermission extends ActivityResultContract<String, Boolean> {
#NonNull
#Override
public Intent createIntent(#NonNull String input) {
return new Intent(ACTION_REQUEST_PERMISSIONS)
.putExtra(EXTRA_PERMISSIONS, new String[] { input });
}
#NonNull
#Override
public Boolean parseResult(int resultCode, #Nullable Intent intent) {
if (resultCode != Activity.RESULT_OK) return false;
if (intent == null) return false;
int[] grantResults = intent.getIntArrayExtra(EXTRA_PERMISSION_GRANT_RESULTS);
if (grantResults == null) return false;
return grantResults[0] == PackageManager.PERMISSION_GRANTED;
}
}
Here is my implementation :
private val permission = prepareCall(object : ActivityResultContracts.RequestPermission() {
override fun createIntent(input: String): Intent {
if (shouldShowRequestPermissionRationale(input)) {
Toast.makeText(
this#MainActivity,
"shouldShowRequestPermissionRationale - true",
Toast.LENGTH_SHORT
).show()
// case 2
} else {
Toast.makeText(
this#MainActivity,
"shouldShowRequestPermissionRationale - false",
Toast.LENGTH_SHORT
).show()
// case 1 and 3
}
return super.createIntent(input)
}
override fun parseResult(resultCode: Int, intent: Intent?): Boolean {
val t = super.parseResult(resultCode, intent)
Toast.makeText(this#MainActivity, "parseResult $t", Toast.LENGTH_SHORT).show()
return t
}
}) {
Log.d("permission granted?", "$it")
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button.setOnClickListener {
permission(android.Manifest.permission.CAMERA)
}
}
As per doc, I should be getting callback inside ActivityResultCallback but after backtracking I figure in my case, it even does not call ParseResult function. Though I am still receiving callback inside onRequestPermissionsResult function.
So, am I doing something wrong ?
Please refer Getting a result from an activity to understand this api.
Update
I was able to get the callback after extending my activity with ComponentActivity but again, if the permission is already given then app wont receive callback, is there any way to directly handle here? I don't wanna check self permission.
Sample demo code - https://github.com/tasneembohra/ActivityResultApiDemo
Here is my implementation, hope it helps:
val cameraPermission = prepareCall(ActivityResultContracts.RequestPermission()){
if(it){
Log.e("TAG", "permnission granted")
}else{
Log.e("TAG", "No permnission")
}
}
cameraPermission(Manifest.permission.CAMERA)
It enters Permission Granted condition, if you granted permission earlier and just open app, or grant it now.
P.S. My Activity extends AppCompatActivity
Well I ran in a lot of circles trying to solve the same problem, but my solution was to simply remove the override onActivityResult from my activity!
You can easily handle your permissions callback by creating a support class extending DefaultLifecycleObserver
class PermissionHandler(
private val context: Context,
private val registry: ActivityResultRegistry
) : DefaultLifecycleObserver {
private val permissionsKey = "permissionsKey"
private lateinit var requestPermissions: ActivityResultLauncher<Array<String>>
private var onAllowed: (() -> Unit)? = null
private var onDenied: (() -> Unit)? = null
private val readExternalStorageRequest = arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.CAMERA
)
override fun onCreate(owner: LifecycleOwner) {
requestPermissions = registry.register(permissionsKey, owner, ActivityResultContracts.RequestMultiplePermissions()) { result ->
Log.d("PERMISSIONS", "RESULT: $result")
if (result.values.isNullOrEmpty() || result.values.contains(false)) {
Log.d("PERMISSIONS", "DENIED")
onDenied?.invoke()
}
else {
Log.d("PERMISSIONS", "ALLOWED")
onAllowed?.invoke()
}
}
}
fun checkForPermissions(permissions: Array<String> = readExternalStorageRequest, onAllowed: (() -> Unit)?, onDenied: (() -> Unit)?) {
this.onAllowed = onAllowed
this.onDenied = onDenied
val remainingPermissions = mutableListOf<String>()
remainingPermissions.clear()
for (permission in permissions) {
if (ContextCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
remainingPermissions.add(permission)
}
}
Log.d("PERMISSIONS", "REMAINING: $remainingPermissions")
if (remainingPermissions.isNotEmpty()) {
requestPermissions.launch(remainingPermissions.toTypedArray())
}
else {
onAllowed?.invoke()
}
}
}
then use it in your activity or fragment
private lateinit var permissionHandler: PermissionHandler
override fun onCreate(savedInstanceState: Bundle?) {
permissionHandler = PermissionHandler(this, activityResultRegistry)
lifecycle.addObserver(permissionHandler)
}
fun foo() {
permissionHandler.checkForPermissions(
onAllowed = {
//on allowed stuffs...
},
onDenied = {
//on denied stuffs...
}
)
}
for fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
permissionHandler = PermissionHandler(requireContext(), requireActivity().activityResultRegistry)
lifecycle.addObserver(permissionHandler)
}
I am currently trying to work with Google Fit API. I have been mainly by following Google's documentation.
Below is the code that I have which seems to have a problem
The problem I have is that it shows me all step count. I want to return my app today step count. this code return me 550530 steps. I want to show today's step count.
class MainActivity : AppCompatActivity(), OnDataPointListener,
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {
private var authInProgress = false
private var mApiClient: GoogleApiClient? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (savedInstanceState != null) {
authInProgress = savedInstanceState.getBoolean(AUTH_PENDING)
}
mApiClient = GoogleApiClient.Builder(this)
.addApi(Fitness.SENSORS_API)
.addScope(Scope(Scopes.FITNESS_ACTIVITY_READ_WRITE))
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build()
}
override fun onStart() {
super.onStart()
mApiClient!!.connect()
}
override fun onStop() {
super.onStop()
Fitness.SensorsApi.remove(mApiClient, this)
.setResultCallback { status ->
if (status.isSuccess) {
mApiClient!!.disconnect()
}
}
}
private fun registerFitnessDataListener(dataSource: DataSource, dataType: DataType) {
val request = SensorRequest.Builder()
.setDataSource(dataSource)
.setDataType(dataType)
.setSamplingRate(3, TimeUnit.SECONDS)
.build()
Fitness.SensorsApi.add(mApiClient, request, this).setResultCallback { status ->
if (status.isSuccess) {
Log.e("GoogleFit", "SensorApi successfully added")
} else {
Log.e(
"GoogleFit",
"adding status: " + status.statusMessage
)
}
}
}
override fun onConnected(bundle: Bundle?) {
val dataSourceRequest = DataSourcesRequest.Builder()
.setDataTypes(DataType.TYPE_STEP_COUNT_CUMULATIVE)
.setDataSourceTypes(DataSource.TYPE_RAW)
.build()
val dataSourcesResultCallback =
ResultCallback<DataSourcesResult> { dataSourcesResult ->
for (dataSource in dataSourcesResult.dataSources) {
if (DataType.TYPE_STEP_COUNT_CUMULATIVE == dataSource.dataType) {
registerFitnessDataListener(
dataSource,
DataType.TYPE_STEP_COUNT_CUMULATIVE
)
}
}
}
Fitness.SensorsApi.findDataSources(
mApiClient,
dataSourceRequest
)
.setResultCallback(dataSourcesResultCallback)
}
override fun onConnectionFailed(connectionResult: ConnectionResult) {
if (!authInProgress) {
try {
authInProgress = true
connectionResult.startResolutionForResult(
this,
REQUEST_OAUTH
)
} catch (e: IntentSender.SendIntentException) {
Log.e("GoogleFit", "sendingIntentException " + e.message)
}
} else {
Log.e("GoogleFit", "authInProgress")
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
//++++++++
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_OAUTH) {
authInProgress = false
if (resultCode == Activity.RESULT_OK) {
if (!mApiClient!!.isConnecting && !mApiClient!!.isConnected) {
mApiClient!!.connect()
}
} else if (resultCode == Activity.RESULT_CANCELED) {
Log.e("GoogleFit", "RESULT_CANCELED")
}
} else {
Log.e("GoogleFit", "requestCode NOT request_oauth")
}
}
override fun onConnectionSuspended(i: Int) {}
override fun onDataPoint(dataPoint: DataPoint) {
for (field in dataPoint.dataType.fields) {
val value = dataPoint.getValue(field)
runOnUiThread {
Toast.makeText(
applicationContext,
"Field: " + field.name + " Value: " + value,
Toast.LENGTH_SHORT
).show()
}
}
}
companion object {
private const val REQUEST_OAUTH = 1
private const val AUTH_PENDING = "auth_state_pending"
}
}
The code is returning 550530 steps because you are using DataType.TYPE_STEP_COUNT_CUMULATIVE, CUMULATIVE will return the step count data as a sum since the start of the count or you can say from the beginning.
You can use DataType.TYPE_STEP_COUNT_DELTA it will give you each data point not the sum of all.
You can check more in the documentation from DataType
Happy Coding!!