Mark M. | has entered the room |
Mark M. | turned on guest access |
Dec 1 | 8:25 AM |
Kai H. | has entered the room |
Mark M. |
hello, Kai!
|
Kai H. |
Hello
|
Kai H. |
*bing*
|
Mark M. |
how can I help you today?
|
Mark M. |
I'm more of a DuckDuckGo person, myself :-)
|
Kai H. |
By answering 1-10 questions
|
Kai H. |
hail google
|
Kai H. |
Kotlin: Are the following notations always interchangeable? fun get() {...} and fun get() = ...
|
Mark M. |
you can always write fun get() = "whatever" as fun get() { return "whatever" }
|
Mark M. |
er, sorry, fun get(): String { return "whatever" }
|
Kai H. |
Ok
|
Mark M. |
the reverse is not necessarily easy, if there are 2+ statements in the {...} part
|
Kai H. |
And vice versa?
|
Kai H. |
The .. = .. notation can only take one statement, righ?
|
Mark M. |
right
|
Dec 1 | 8:30 AM |
Mark M. |
now, with scope functions like let() and apply(), a single Kotlin statement can get you farther than can a single Java statement
|
Mark M. |
but, eventually, you hit a limit
|
Mark M. |
and torturing your code to get it to use = syntax may result in stuff that is more difficult to read
|
Kai H. |
I see where this is going ;-)
|
Mark M. |
in the end, we usually want readable code
|
Mark M. |
= syntax can help eliminate some syntactic overhead, but if you wind up with a mess on the right-hand side just to get it all shoved into a single statement, you might not have improved readability
|
Kai H. |
Ok
|
Kai H. |
Could you recommend a project with good Kotlin code to read (other than yours ;-) )?
|
Dec 1 | 8:35 AM |
Kai H. |
"Read this if you want to learn what a well written, well architectured Kotlin app looks like".
|
Mark M. |
not really, insofar as I don't spend a lot of time reading through other project repositories -- I do that when I have a specific need to look at that project's code, not just as some sort of code critic
|
Mark M. |
on the whole, newer architecture examples from Google are probably OK, with minor variances based on personal developer style preferences
|
Mark M. |
so https://github.com/android/architecture-samples might be a place to look
|
Kai H. |
It seems to be a bit... asleep
|
Kai H. |
But thanks
|
Kai H. |
When wanting a Dialog, do I _have_ to use DialogFragment if I want my dialog to survive orientation change etc.?
|
Mark M. |
the code has been updated within the past year, which IMHO is a promising sign
|
Dec 1 | 8:40 AM |
Mark M. |
technically, a Dialog never survives a configuration change, if its hosting Activity is destroyed/recreated
|
Mark M. |
what a DialogFragment does is automatically recreate the Dialog
|
Mark M. |
if you want to manually recreate the Dialog, you are welcome to do so
|
Mark M. |
but if you want the Dialog to appear after the activity destroy/recreate cycle, *something* has to arrange to show it again, whether DialogFragment or your own code
|
Kai H. |
That answers this. As far as I remember, you seldomly use dialogs and don't create a "class hierarchy" around them, but just use them as they come from google, wherever you need them.
|
Mark M. |
DialogFragment also provides a convenient fragment-y API for defining the content that goes into the Dialog that you can elect to use
|
Mark M. |
there had been an anti-dialog push in mobile UI design for a while, but that seems to have died down somewhat
|
Kai H. |
Do you mean the things you put into a Bundle?
|
Kai H. |
By fragment-y
|
Drasko | has entered the room |
Dec 1 | 8:45 AM |
Mark M. |
no, I mean onCreateView() and onViewCreated(), for describing the UI that appears in the dialog
|
Drasko |
Hi, Mark! I had a really difficult time to find how to enter this chat. :)
|
Mark M. |
your alternative is onCreateDialog(), which allows you to define the complete Dialog from scratch, but that's more code than you need in many cases
|
Kai H. |
Ok
|
Mark M. |
Drasko: hi! sorry, was the banner not showing up?
|
Mark M. |
Kai: let me take a question from Drasko, and I'll be back with you in a bit
|
Drasko |
Well, I went to https://commonsware.com/ and from there it was really difficult to find the way.'
|
Kai H. |
Sure
|
Drasko |
But, never mind.
|
Mark M. |
Drasko: yeah, sorry, the Warescription site is the entry point for all of the Warescription services
|
Drasko |
View paste
|
Mark M. |
you will need to add a <queries> element to your manifest to be able to use the PackageManager APIs that you are using to find the launchable activities
|
Mark M. | |
Drasko |
View paste
|
Mark M. |
you use the deprecated method
|
Mark M. |
AFAIK, Google has not published an alternative mechanism
|
Mark M. |
note that ACTION_GET_CONTENT does not take a Uri
|
Drasko |
Yeah, I know that
|
Dec 1 | 8:50 AM |
Drasko |
I used it and just set type to fileManagerDonwload, fileManagerDownloadIntent.type="text/csv"
|
Drasko |
But it doesn't point me to the Downloads folder.
|
Drasko |
File manager points me to Recent.
|
Mark M. |
again, ACTION_GET_CONTENT does not take a Uri that way
|
Drasko |
Hm, how can I open Downloads folder in pre-R Androids?
|
Mark M. |
ACTION_GET_CONTENT has *never* taken a Uri
|
Mark M. |
so, if your approach works on some older devices, consider that to be a minor miracle
|
Drasko |
To be honest this is the code I inherited, so ... :)
|
Mark M. |
ah
|
Mark M. |
I am not aware of any reliable means to get a system-supplied "file picker"-style UI to open on a particular directory
|
Mark M. |
for a few more months, you could embed your own file picker UI (hand-coded or from various libraries)
|
Mark M. |
there are probably some unreliable means, such as ACTION_PICK (which *does* take a Uri)
|
Dec 1 | 8:55 AM |
Mark M. |
and there are ways that you could arrange to show the system-supplied file picker starting at some previous directory that the user had used with that file picker
|
Dec 1 | 8:55 AM |
Kai H. |
I would like that functionality too. When using SAF, it takes you to the last directory that was picked, with not means to pre-choose one.
|
Mark M. |
if you save the Uri from a previous SAF operation, you should be able to include it using EXTRA_INITIAL_URI on a future SAF operation, to use as a starting point
|
Mark M. | |
Kai H. |
But I don't have a previous SAF operation. I just want the user to pick a certain folder.
|
Mark M. |
sorry, the user gets more freedom than that
|
Kai H. |
Rather a certain collection, "Documents" to be precise. But it's not implemented.
|
Kai H. |
"collection".
|
Drasko |
I just tried previous code on some non-R emulator and it does open Downloads folder.
|
Mark M. |
there are 26,000+ Android device models out there
|
Mark M. |
do not assume that they all work like that emulator
|
Mark M. |
even when you stick within the bounds of documented behavior, we get weird stuff from time to time
|
Mark M. |
if you go outside the bounds of documented behavior, "all bets are off"
|
Mark M. |
in particular, prior to Android 5.0, ACTION_GET_CONTENT was purely an app-level Intent action -- the system did not provide anything for it
|
Mark M. |
(though it's reasonably likely that your minSdkVersion is 21 or higher by this point)
|
Dec 1 | 9:00 AM |
Mark M. |
Drasko: let me take another question from Kai, and I will return to you in a bit
|
Mark M. |
Kai: do you have another question?
|
Kai H. |
When doing "rebuld project" in Android Studio and then "run", it builds something again. Why is that? What does it build in the respective steops?
|
Mark M. |
off the top of my head, I couldn't tell you
|
Mark M. |
but the Build tool in Studio should tell you
|
Drasko |
Rebuild project cleans the project and build.
|
Jan | has entered the room |
Jan |
Hi.
|
Kai H. |
Where do I find the build tool?
|
Mark M. |
(hi, Jan -- I will be with you in a moment!)
|
Dec 1 | 9:05 AM |
Mark M. |
Kai: after a build, it is docked by default in the bottom toolstrip at the bottom of the IDE
|
Kai H. |
Oh, that, ok.
|
Mark M. |
right now, in my Studio, it is showing up next to Logcat
|
Kai H. |
:D
|
Mark M. |
that lists all the Gradle tasks that went into the last build operation
|
Mark M. |
those that lack "UP-TO-DATE" actually did something
|
Mark M. |
(OK, also excluding those with the "not" symbol instead of a checkmark, as I assume those also get skipped)
|
Mark M. |
even the "UP-TO-DATE" ones technically do a bit of something, in that need to examine files and confirm whether they are all up to date
|
Mark M. |
which is why you may see more than 1 ms times for some of those
|
Mark M. |
let me take a question from Jan, and I'll be back with you shortly
|
Mark M. |
Jan: your turn! how can I help you today?
|
Mark M. |
Jan: do you have a question?
|
Dec 1 | 9:10 AM |
Mark M. |
Jan: if you come up with a question, just let me know
|
Mark M. |
in the meantime...
|
Mark M. |
Drasko: back to you! do you have another question?
|
Jan |
View paste
|
Mark M. |
if you do not have as LoginUser, what happens?
|
Mark M. |
IOW, if you have just val contentData: LoginUser = result.data, what is the error message?
|
Jan |
I haven't tried that.
|
Mark M. |
if your result is an APIResult<LoginUser>, it should "just work" to assign result.data to a LoginUser property or variable
|
Drasko |
One more thing regarding deprecation in R.
|
Mark M. |
Jan: but, this is one of those things that I usually need to poke around in the IDE a bit, reading error messages, until I get it worked out
|
Mark M. |
Drasko: go ahead!
|
Drasko |
I receive warning message: 'getter for systemWindowInsetTop: Int' is deprecated.
|
Dec 1 | 9:15 AM |
Mark M. |
yeah, they changed the system insets APIs around -- I forget the details, as I have generally avoided those APIs in the first place :-)
|
Jan |
I tried it. Now a new warning that inferred type is Any but expected LoginUser. The result is APIResult<Any> because I use this sealed class for all returns and the return can be lots of different objects. I'm not that good with generics so thought maybe there was a better way to handle all the casting. I can suppress the warning or just not worry about it.
|
Drasko |
But I don't know how to get appropriate call on that one. This is used to set padding/margins so the views are not drawn behind status and navigation bar.
|
Drasko |
View paste
|
Mark M. |
Jan: while you might use APIResult in lots of places, any *given* APIResult should be tied to a concrete class
|
Mark M. |
Drasko: according to the docs, use getInsets() insetad of systemWindowInset*
|
Kai H. |
Drasko: Maybe this helps? https://proandroiddev.com/exploring-windowinset...
|
Drasko |
Thanks, Kai.
|
Mark M. |
Google's Chris Banes also has written quite a bit about window insets
|
Dec 1 | 9:20 AM |
Mark M. |
at this point, if anyone has further questions, just ask, and I'll field them as best I can in the time remaining!
|
Kai H. |
I get a directory from the user via SAF and then do some operations in that. Is there any way to test that?
|
Drasko |
Thanks, Mark, I'm good.
|
Mark M. |
Kai: test what, specifically?
|
Mark M. |
Drasko: OK!
|
Kai H. |
Getting a directory via SAF and then writing to and reading from it.
|
Jan |
View paste
|
Kai H. |
But one could argue that should just work and needs no testing. As with most file system functionality X)
|
Mark M. |
Jan: processResponse() may need to use Response<*> or Response<T> and APIResult<*> or APIResult<T> to retain your types
|
Dec 1 | 9:25 AM |
Mark M. |
Kai: I would isolate the logic that does the I/O from the logic that operates on the data being read or written, then test the latter
|
Jan |
What does * do? I thought T was already for all types? And if I make that change, do I need to pass in the type for T somehow?
|
Mark M. |
Jan: * says "for here, we don't care about the type" -- T says "for here, we care about the type, but the type is determined by the call site"
|
Kai H. |
The "logic" is for example get a directory and a subdirectory via DocumentFile, checking if there is write access, then writing a file and reading it again.
|
Mark M. |
Jan: if you want to use T, T will need to be visible to processResponse(), either because that is a member function of some class that defines T, or by declaring T in the function (fun <T> processResponse(response: Response<T>): APIResult<T> { ... })
|
Mark M. |
Kai: you could test that with instrumented tests, if you extract the "create the DocumentFile" logic into different implementations for the main code (uses SAF Uri) and the test code (uses a File)
|
Mark M. |
*maybe* DocumentFile survives in unit tests, perhaps with the help of Roboelectric, but I haven't tried that
|
Kai H. |
:)
|
Dec 1 | 9:30 AM |
Mark M. |
alternatively, hide all the stream API in something that has different main vs. test implementations, and test the stuff that uses the streams
|
Kai H. |
I would like to test the thing that does the streaming though, it seems :D
|
Kai H. |
Or prepares for that up unto openInputStream etc.
|
Mark M. |
then, either take advantage of DocumentFile working with Uri or File values, or you would need to cobble together something with UiAutomator that actually automates the SAF UI itself
|
Mark M. |
and that's a wrap for today's chat
|
Mark M. |
the next one is Thursday at 7:30pm US Eastern
|
Kai H. |
Thanks for your answers :D
|
Mark M. |
I am sorry if I did not get to all of your questions
|
Mark M. |
and, have a pleasant day!
|
Kai H. |
Same
|
Kai H. | has left the room |
Drasko | has left the room |
Jan | has left the room |
Mark M. | turned off guest access |