Office Hours — Today, December 15

Saturday, December 12

Mark M.
has entered the room
Mark M.
turned on guest access
Kai H.
has entered the room
Kai H.
Hello
Mark M.
hello, Kai!
how can I help you today?
Kai H.
Those messages turned up in the wrong order for me 8)
Mark M.
¯\_(ツ)_/¯
Kai H.
I have an Activty that receives certain file types, which is denoted in the AndroidManifest.
How would I start the App that this Activity is part of?
Right now the Activity gets started with some support stuff, but not the main Application, as far as I see it.
Mark M.
I guess I do not understand the question
ummmmmm... that would be extraordinarily unusual
Dec 15
8:30 AM
Mark M.
how have you determined this?
Kai H.
Guessing, I guess :D
So I am not sure about it.
Mark M.
the Application instance is created as part of forking the process
so, if you already have an existing process, starting an activity does not create an Application instance
however, that just means that the Application instance was created earlier
Kai H.
So I only need to somehow switch to it. Or switch to another Activity within that Application.
Mark M.
I don't know what either of those things mean, sorry
Kai H.
We have an Activity that with an intent filter for files with a certain suffix. And so far that Activity was opened, something was done with the file and then the activity (and the App afaik) was closed again. Now I want it to open the main Activity of the App.
Mark M.
"Now I want it to open" -- what is "it"?
Kai H.
This Activity that handles the files.
Mark M.
but, the user closed it ("and then the activity... was closed again")
Kai H.
No, we close it programmatically.
Mark M.
do you mean that you want back navigation to go to the main activity?
8:35 AM
Mark M.
um, under what circumstances do you close it programmatically? is that based on a button click or other user input?
and, isn't this just a matter of calling startActivity() to start your main activity, before you call finish()?
trocchietto_Ivano
has entered the room
Kai H.
It's an activity that just displays a progress bar and then gives up control again.
Probably.
trocchietto_Ivano
hallo world
Mark M.
(hello, Ivano -- I will be with you shortly!)
Kai H.
Hello ivano
I was under the assumption that the main application wasn't started, but I guess that was wrong.
Mark M.
Kai: assuming that you are calling finish() to get rid of this file-processing activity, just call startActivity() to start your main activity before that finish() call
the Application instance isn't really involved in anything of what you have described
Kai H.
I'll try just doing an startActivity on the Activity that is registered for launcher and main.
Mark M.
the fact that the user used a different entry point (some sort of ACTION_VIEW or ACTION_SEND Intent, I'm guessing) doesn't really change anything about your app, other than what activity gets displayed
Kai H.
OK.
Mark M.
to draw an analogy: the user linked to an article in the Web site rather than the home page -- this does not change how that article's page links back to the home page
Kai H.
Should I start the MAIN/LAUNCHER Activity or the class in <application android:name="..."?
Mark M.
startActivity() starts activities
I am unclear why you are fixating on the Application instance -- again, it is not really related to anything about your scenario
so, if your MAIN/LAUNCHER activity is MainActivity, use startActivity(this, MainActivity.class).
(give or take a semicolon at the end)
8:40 AM
Mark M.
the Application instance will already have been created (when the process was started), and startActivity() does not change that
Kai H.
Probably because it exists and I don't really know where our entry points for the App are and what I need to instantiate before.
Mark M.
the launcher activity is not special, other than because of your <intent-filter>, it appears in the launcher
Application is not an entry point -- it's more of an artifact of process creation
any exported activity, particularly those with an <intent-filter>, are entry points
and your app can have one, two, or a thousand of those
from Android's standpoint, by and large, they are all equal
a launcher activity is not particularly special -- it's just advertising itself as something a launcher might want to offer to users
now, *in your app*, the launcher activity might be special, because you might be doing some one-time initialization logic there that perhaps should reside elsewhere... but that's a matter of your code, not Android
IOW, you're overthinking this :-)
Kai H.
Ok. I'll try it :)
Thanks for the explanations.
Mark M.
let me take a question from Ivano, and I'll be back with you in a bit
Ivano: hi! how can I help you today?
trocchietto_Ivano
View paste
HI Mark, I am in the middle of a ticket and cannot understand a Kotlin behaviour, my model `Order` has an interface implemented, so I override a method. This method is called by the adapter in onBind. Well the method get called and is not null if I implement it with a Kotlin get: `    override val badge: String get() = totalOpinions.toString()`, but I do not understand why without a get() as here return null:    override val badge: String = totalOpinions.toString(). Am I missing something about the get() internals? I am fine to use get() just do not get why does not work as simple assignment with equal
8:45 AM
Mark M.
so, Order is the one with the override val badge line?
(I am just confirming, so I can phrase my response more accurately)
trocchietto_Ivano
I guess so ```
View paste (3 more lines)
```
@Parcelize
data class Order(
    override val id: String,
    @Json(name = "ordernumber") val orderNumber: String,
    @Json(name = "orderstatus_id") val orderStatus: String,
    val supplier: String,
    @Json(name = "supplier_id") override val relationId: Int,
    override val date: String,
    @Json(name = "delivery_date") val deliveryDate: String,
    @Json(name = "total_amount") val totalAmount: String,
    val applicant: String,
    @Json(name = "action_id") val actionId: String?,
    @Json(name = "total_opinions") override val totalOpinions: Int,
    val files: List<ThumbnailRepresentation>
...
Mark M.
yes, that line is part of your Order class
trocchietto_Ivano
badge is the overridden method
Mark M.
override val badge: String = totalOpinions.toString() // this will be executed when the Order instance is created
trocchietto_Ivano
that is triggered by AssessableItemInterface that implements WIthBadge that has the proprety badge: String
Mark M.
override val badge: String get() = totalOpinions.toString() // this will be executed each time something references the badge property to retrieve its value
trocchietto_Ivano
yes
Mark M.
either of those could be correct, neither is intrinsically right or wrong
it is mostly a matter of whether you are trying to capture the value of totalOpinions.toString() at the point when the Order object is created or not
trocchietto_Ivano
I see so the problem is in onActivityResult, because is only at refresh that I get the problem
but changing to get() it works
so I guess get() have to have some magic bl inside
kinda cache
Mark M.
that implies that totalOpinions changes what its toString() returns while your Order object is around, and you need badge to reflect the latest totalOpinions state
8:50 AM
trocchietto_Ivano
yes
Mark M.
for example -- and taking some guesses as to what these objects represent -- if something adds an opinion to totalOpinions, such that toString() now returns a higher count, the get() form will return that higher count, while the direct assignment will have captured the original count
trocchietto_Ivano
but when user go back the string/number is not updated, returns null, basically onBindViewHolder is calling the interface but is null without get() accessory
yes you right
anyway ain't broken do not change is fine with me in this case
so no big deal just I cannot grasp why with get I get a no null value but the updated value
the binding is made with rxjava
and the adapter calls the interface
order.setBadge(view)
so the badge method is called also when I refresh, but gives a null value
(notice order is an extension function)
Mark M.
well, I cannot really get into that, as I do not know much about your app -- all I can do is help you with the differences in those two bits of Kotlin syntax that you asked about
in Kotlin, a property maps to a Java field, plus a getter method and a setter method
with that in mind...
trocchietto_Ivano
anyway never mind mark, no big deal if you say to me that there is not major difference between get and assignment I guess is a kind of temporary delay that is caused by an assigment
instead of get that should be faster
Mark M.
override val badge: String = totalOpinions.toString() // this assigns a value to the field
trocchietto_Ivano
OK
Mark M.
override val badge: String get() = totalOpinions.toString() // this overrides the getter method
8:55 AM
trocchietto_Ivano
I see
maybe a getter has a return inside
as in Java
and that is why does not work
make sense
Mark M.
override val badge: String get() { return totalOpinions.toString() }
trocchietto_Ivano
all the Kotlin thing is confusing if used improperly
I miss the verbosity of java
thanks
Mark M.
sure!
let me take another question from Kai, and I'll return to you shortly
trocchietto_Ivano
I say good bye mark
thanks but cannot chat I am @work
Kai H.
Good luck with Kotlin :D
trocchietto_Ivano
thank you kai
have a nice day both of you
Kai H.
same
Mark M.
Kai: one quick follow-up to your original question: "We have an Activity that with an intent filter for files with a certain suffix" -- you do realize that file extension matching in <intent-filter> doesn't really work well in modern versions of Android, right?
most of our Uri values do not have extensions, and so the <intent-filter> is not going to match on them, if it is working off of file extensions
Kai H.
I actually don't.
Mark M.
most of our Uri values nowadays come from content providers, and have a content:// scheme
there is no tradition of content:// Uri values having file extensions
Kai H.
View paste
                <data
                    android:host="*"
                    android:mimeType="*/*"
                    android:pathPattern=".*\\.foobarprofile"
                    android:scheme="content" />
Mark M.
think of them as being more like https:// URLs
yeah, that has a low probability of success for any given user invocation
Kai H.
That is what we're using atm, besides similar entries with android:scheme="file"
Mark M.
right, with file:// as the scheme, the extension part is more likely to work... but file:// Uri values have been largely banned since Android 7.0
9:00 AM
Mark M.
with content:// as the scheme, you should not assume that the Uri has any particular file extension
Kai H.
I had the problem that it wouldn't work on Android 11 with the file:// scheme, so I added the same entries with the content:// scheme and it worked once again.
Wouldn't work == The app was not found as a receiver for .foobarprofile files.
Mark M.
it will depend a *lot* on the implementation of the provider
so, for example, it might not work at all on phones from certain manufacturers
Kai H.
How would the system find an app for a file with the .foobarprofile extension?
Mark M.
purely by MIME type
and, if Android (or the app with the content) does not know what .foobarprofile is, it might wind up using some default MIME type like application/octet-stream
Kai H.
The use case is that you have that file in an email app or file explorer, click it and our app get suggested, which will then deal with the file.
And only our app should be suggested.
Mark M.
and if that works more than 2% of the time on Android 7.0+, I will be surprised
Kai H.
I didn't have consistent problems with it
And have been testing on 7.1, 9, 10 and 11
Mark M.
there are hundreds of file managers, and dozens (if not hundreds) of email clients
9:05 AM
Kai H.
That is true. I have tested maybe 5 file managers.
Mark M.
for the Uri that you are getting, there are three main possibilities:
1. it has a file:// scheme, in which case the file extension is probably "for realz", and your app will match
2. it has a content:// scheme, and the content provider happens to use the file extension of an underlying file in its Uri, in which case your app will match
3. it has a content:// scheme, and the Uri does not have a file extension, in which case your app will not match
Kai H.
I think we might be talking from different ends of the app.
Mark M.
scenario #1 used to be the dominant form
Kai H.
Or I am confused.
Mark M.
my assumption is that your <intent-filter> is for an ACTION_VIEW Intent
given the description of the behavior that you are seeking
Kai H.
View paste
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
Mark M.
that's what I expected
your activity, when started, will get the Uri of the content via getIntent().getData()
in the abstract, that Uri could be of the three forms that I described... except that your activity would never get started for scenario #3
Kai H.
Ok
Mark M.
because, unless there are other <data> elements in play here, your activity will not match the Uri, because the Uri lacks a file extension
Kai H.
intent.getExtras().getString(Intent.EXTRA_STREAM) actually
Mark M.
not for ACTION_VIEW
EXTRA_STREAM should work 0% of the time for ACTION_VIEW
EXTRA_STREAM is an ACITON_SEND thing
(er, ACTION_SEND)
Kai H.
Sorry, you're right.
sudokai
has entered the room
9:10 AM
Mark M.
so, the assumption of your pathPattern <intent-filter> is that the Uri will have a file extension
trocchietto_Ivano
has left the room
Mark M.
and I'm just warning you that this is not assured, and I would expect may not even be all that probable on many devices
(BTW, hello, sudokai -- I will be with you shortly!)
Kai H.
Isn't the pathPattern used to find our application for the file extension?
Mark M.
yes, but there is no requirement for a Uri to have a file extension
suppose that the file manager decides that the Uri for this file is content://com.whatever/something/hey/this/has/n...
that is a perfectly legitimate Uri, and it might truly point to the user's desired content
Kai H.
So your point is that some file managers won't look at the extension when trying to find a the right app to open a file with?
Mark M.
correct, because file managers aren't doing that in the first place
they create an Intent and ask Android "hey, what are the options here?"
and Android is what applies the <intent-filter> algorithm
for an Intent with a Uri of content://com.whatever/something/hey/this/has/n..., Android will conclude that your app does not match
Kai H.
Huh.
Mark M.
because your <intent-filter> is looking for a particular extension, and that Uri does not have that extension
Kai H.
So I was kinda lucky it worked on my Pixel 4a and my Oneplus Nord and Huawei Tablet and Emulator=?
Mark M.
the particular file managers and email clients that you tested either are specifically using Uri values that have the right file extension, or something else is leading them to your activity other than that <intent-filter>
now, if I were writing a file manager or email client, I probably would try to use the file extension on the Uri, just to improve compatibility with apps like yours
but you shouldn't assume that, and some system-generated Uri values are not going to reliably have file extensions
9:15 AM
Mark M.
so, in addition to your <intent-filter>, make sure that your users have some other way to indicate what content they want your app to process (e.g., ACTION_OPEN_DOCUMENT)
Kai H.
So if in the future I might wonder why our app will not be in the list of apps to open .foobarprofile files with, I now know why.
Mark M.
correct -- and, from a customer service standpoint, you need to be prepared to deal with those sorts of inquiries
Kai H.
But ACION_OPEN_DOCUMENT is from the our app to the external, not the other way around.
ACTION
Mark M.
correct
Kai H.
Ok, I think I understood now.
Thank you
Mark M.
it's like Windows: supporting drag-and-drop from Explorer is nice, but usually you also have a File > Open menu
and with that, let me take a question from the very patient sudokai :-)
sudokai: hi! sorry for the delay! how can I help you?
Kai H.
Yes. Hello kai and thanks.
9:20 AM
Mark M.
sudokai: do you have a question?
sudokai
Hi
I'm working with location services
LocationManager
I request periodic updates using requestlocationupdates
But on some phones, sometimes it just doesn't receive new locations
It works for a while, then it stops
The app doesn't crash or anything
The stream of locations just stops
Have had any similar experiences Mark?
have you*
Mark M.
there are a few forms of requestLocationUpdates(), including ones that take a LocationListener or a PendingIntent parameter -- which one are you calling?
sudokai
The one with LocationListener
Mark M.
OK, then locations will stop when your process terminates
sudokai
Yeah, but that's the point
Mark M.
do you have evidence that your process is still running, yet you are not receiving updates?
sudokai
It doesn't terminate
Mark M.
OK, how are you determining that?
sudokai
Yeah, logs
9:25 AM
Mark M.
are you sure that it is one continuous process, and not that your process stopped and got restarted?
sudokai
Mmm, I subscribe to location updates again if the process restarts
Mark M.
IOW, what is keeping your process around? are you using a foreground service or something?
sudokai
Yeah, foreground service when the app goes to the background, with type set to location
I know there are a lot of manufacturer shenanigans surrounding this stuff
Mark M.
yeah, I was about to point that out
sudokai
By this stuff, I mean location access
Have you had any similar experiences
Mark M.
no, but I have not had to deal with this in a couple of years, thankfully
are you requesting ACCESS_BACKGROUND_LOCATION? I assume that you must be, if this is working at all on Android 10+
sudokai
I also suspect that on some Xiaomi devices, when the app is "minimized" then the "while-in-use" location access doesn't work, even if you use a foreground service
No I don't
I don't need to because I use a foreground service
That's the theory at least
I have the foreground notification
Mark M.
I forget the rules around ACCESS_BACKGROUND_LOCATION -- you may be right
sudokai
I have ACCESS_COURSE_LOCATION and ACCESS_FINE_LOCATION
Mark M.
the "process died, was restarted, and you requested locations" scenario is what made me think of the ACCESS_BACKGROUND_LOCATION concern
sudokai
ah, yes
Good catch
Because the screen would be off
And Android 11 at least has restrictions around that
9:30 AM
Mark M.
yeah, this whole area got doubly-murky, both from the "doing background work" changes we've been dealing with, plus now the "getting background locations" changes
sudokai
Thanks Mark, that at least is a plausible scenario
Mark M.
which is why I am grateful that I have not had to deal with this in a while :-)
sudokai
Lucky you, it's really a nightmare
Mark M.
and with that... that's a wrap for today's chat
sudokai
FusedLocationProvider is buggy as hell too
Thanks!
Kai H.
Thanks
Mark M.
next one is Thursday at 7:30pm US Eastern
have a pleasant day!
sudokai
You too
Kai H.
has left the room
sudokai
has left the room
Mark M.
turned off guest access

Saturday, December 12

 

Office Hours

People in this transcript

  • Kai H.
  • Mark Murphy
  • sudokai
  • trocchietto_Ivano