Office Hours — Today, June 16

Thursday, June 11

Jun 16
3:50 PM
Mark M.
has entered the room
Mark M.
turned on guest access
3:55 PM
Randall M.
has entered the room
Mark M.
hello, Randall!
how can I help you today?
Randall M.
Hi, Mark!
This is a bit out there, but I really do have a good reason for wanting to do this...
Mark M.
I always get worried when people preface questions like that. :-)
Randall M.
I'm working on a medical application, that communicates via Bluetooth to a cardiac monitor that a patient wears for a 30 day procedure.
heh
The client would like to lock down the device as much as possible, so the patient cannot exit the application or otherwise muck about on the device.
We've already set the "homescreen" app to be this application, but it's will possible to access settings (via swiping down from the top of the screen) and reset this.
4:00 PM
Randall M.
My next plan is to try to detect when the app is moved to the background, and force it back, using some tips from <http://www.andreas-schrade.de/2015/02/16/androi...;, but I'm not having much success.
Mark M.
anything outside of Android 5.0's kiosk mode is not going to be reliable
MyWay
has entered the room
Mark M.
are you supplying the Android hardware?
MyWay
hi :)
Mark M.
(BTW, hello, MyWay -- I will be with you shortly!)
CodeApprentice
has entered the room
Randall M.
I've been told "do the best you can". And the target OS is 4.4.2, I think due to the devices they have (Moto Droid RAZR).
Mark M.
(BTW, hello, CodeApprentice -- I will be with you after Randall and MyWay!)
CodeApprentice
Hello
Mark M.
Randall: to be blunt, I wouldn't touch this project with a 10' pole
CodeApprentice
no hurry. Still trying to formulate an intelligent question.
Randall M.
Yeah, it's not the patient's device, they are providing the device. So we can configure it as necessary, but locking it down so the patient cannot change it back is the challenge.
Mark M.
I'd then be looking at the custom ROM direction
Randall M.
Too late, I'm trying to deliver a GM this week so they can start their FDA testing.
Mark M.
"a GM"?
Randall M.
This feature is not FDA required, but their patients have a history of mucking about with the device.
"golden master", "release candidate"...
Mark M.
ah
Randall M.
I've not worked much with 5.0 yet...is that pretty reliably locked down?
Mark M.
it's more that kiosk mode is actually part of the OS
as opposed to, um, hacks to try to fake it
Randall M.
That is likely a future direction, but because of hardware on hand, we can't go there yet.
4:05 PM
Randall M.
At this point, they're prety OK with hacks, even if not 100% reliable.
Mark M.
that being said, I don't really know what to tell you
I avoid getting into this sort of area
I recognize that your use case for this sort of stuff is legitimate
Randall M.
The app is able to recover from crashes and resume the procedure, but the problem I've encountered with the hack in the aforementioned link is, when it tries to restart the Activity, it starts it over from scratch, losing all state data that's important.
OK, I understand. I've been working on this a couple days now, and decided to leave no stone unturned. :)
Mark M.
I really apologize -- I try not to exclude topics, but this is one
Randall M.
I'm OK telling them that all they want cannot be done, but I want to make sure I perform my due diligence first.
Mark M.
let me take questions from the others, and if you can come up with another take on this that doesn't frighten me, go ahead :-)
I'll be back with you shortly
Randall M.
Thanks.
Mark M.
MyWay: your turn! do you have a question?
MyWay
View paste
I'm still working on my chat app. I'm showing a limited number of recent messages inside a ListView (say 30 messages, using a LinkedList).
I'm using android:stackFromBottom="true" to pop messages from the bottom and android:transcriptMode="normal" to avoid the auto-forced-scroll to the bottom when a user puts a new message and the user is reading old messages. Anyway I'd like to avoid that when user scroll up a little to re-read some old message gets messages "moving up". I'd like to "pause" the messages being added when the user is reading old messages, how can I do it? Then I'd have to show again new messages when the user goes back to the bottom of the listview.
4:10 PM
Mark M.
hmmm, I thought that's what transcriptMode normal did
regardless, you'd need to detect that you are not showing the bottom chat entries
probably an OnScrollListener and getLastVisiblePosition() on the ListView would handle that
your chat is going to be using a background thread for incoming messages, so somewhere you're passing those over to the main application thread
that logic would check the state and, if you're not at the bottom, queue up the messages
when the OnScrollListener says "hey, we're at the bottom!", you'd add all the queued messages
personally, I am somewhat skeptical that this is going to give you a good UX though
MyWay
yes, so I stop passing them and when I'm at bottom, I load them
Mark M.
and I haven't tried this, so my recipe may have flaws
but it's where I'd start if I had the need for trying something like this
MyWay
the bad thing is that you're reading old messages, but you can't, because new messages are coming up
or you want to tap on a message and you can't, for the same reason
so I had the idea to stop sending new messages
maybe I can place something at the bottom saying that there are new messages for the UX
4:15 PM
Mark M.
or at least do that if the number of queued messages exceeds some threshold
dumping dozens of new lines in at one shot without warning may be confusing, but adding one or two might not be bad
let me take questions from the others, and I will swing back to you in a bit
CodeApprentice: your turn! do you have a question?
MyWay
yes, thank you
CodeApprentice
Have you had a chance to look at Espresso since the last time we talked?
Mark M.
nope
got swamped by a macademia nut cookie
CodeApprentice
well, that's a good reason ;-)
Mark M.
it's now in the "I really need to get to this, but I have no idea when my schedule will support it" bucket
CodeApprentice
I'm there as well. I want to look at using it for my existing app.
But I have other bugs and features that are a bit more pressing.
Mark M.
I forget how far along Chiu-Ki Chan is with her Espresso book
ah, OK, she's not as far along as I had been thinking
CodeApprentice
I'll have to look into that.
Mark M.
FWIW, she has a bunch of samples at https://github.com/chiuki/espresso-samples
CodeApprentice
Rather than typing out a question, can I give an SO link?
Mark M.
sure
4:20 PM
CodeApprentice
4:20 PM
CodeApprentice
That's my most pressing issue right now.
Basically it boils down to: how do I force all other threads to wait for a signal from another thread that they can continue?
Mark M.
hmmm...
the problem is that your LoaderManager stuff is production code, not test code, right?
CodeApprentice
I'm a multithreading noob, so I'm having a hard time wrapping my head around this.
yes, LoaderManager is production
Mark M.
so you really don't want to be adding in crap to it that is testing-specific if possible
CodeApprentice
right
Mark M.
CodeApprentice
and to be specific. I'm using the default LoaderManager and CursorLoader implementations and provide a LoaderCallbacks implementation
Mark M.
yeah, to be honest, I haven't a clue off the top of my head
this is one of the reasons I don't fuss with GUI testing, doing my tests more at the model/controller level
I'd test to confirm the Cursor, or maybe even the CursorLoader, comes back with what I want
CodeApprentice
I try to do that as well.
4:25 PM
Mark M.
toss a bounty on your SO question and hope somebody gives it some love, I guess
I'm sure there's a recipe for it
CodeApprentice
I'll consider that...although, I'm getting close to 20k, so I want to be sparing with bounties.
Mark M.
let me take questions from the others, and I'll be back with you in a bit
CodeApprentice
kk
Mark M.
Randall: back to you! do you have another question?
Randall M.
Almost...can I defer, and you come back to me?
Mark M.
sure
let me know when you're ready so I can put you back into the queue
MyWay: back to you! do you have another question?
Randall M.
OK, ready when you are...
Mark M.
Randall: go ahead
4:30 PM
Randall M.
OK, so I'm trying to "restore" an Activity when a Service I've created (running every 2 seconds) detects that my app is no longer in the foreground. The same code I have shows starting the activity with a FLAG_ACTIVITY_NEW_TASK flag, which looks like it compete recreates the activity, rather than simply restarting it.
is FLAG_ACTIVITY_BROUGHT_TO_FRONT what I want instead?
Or maybe FLAG_ACTIVITY_CLEAR_TOP?
Mark M.
well, I don't think you have a choice when calling startActivity() from a service
I think you have to use FLAG_ACTIVITY_NEW_TASK
Randall M.
And does this work for activities that are not part of my app?
Oh. Hmmm...
Bother.
I think that kills that idea then.
Mark M.
usually, if you are starting an activity from something other than an activity, you need FLAG_ACTIVITY_NEW_TASK
is this service in the same process as the activity?
Randall M.
Yes.
Mark M.
then don't have the service call startActivity(0
Randall M.
Is it possible to store the current Activity in an ivar, and manually call start() (or resume()?) on it, from the service?
Mark M.
er, startActivity()
no, in part because there is no start() or resume()
and you don't want to put a reference to an Activity in a static data member (my interpretation of "ivar")
due to memory leaks
you could use an in-process event bus to let the UI layer know "hey, we're in the background -- do something"
LocalBroadcastManager, Square's Otto, and greenrobot's EventBus are the three big event buses
4:35 PM
Randall M.
"then don't have the service call startActivity()"..."hey, we're in the background -- do something"...Yeah, what's that "something"?
Mark M.
if your activity still exists, it can call startActivity() with flags that are more apropos to your use case
if the service determines that nobody handled the event, then you know that your activity is flat-out gone (e.g., unhandled exception), and you should be restarting it from scratch with startActivity() and FLAG_ACTIVITY_NEW_TASK anyway
Randall M.
Ah, cool....OK, I'll give that a shot.
Mark M.
let me take questions from the others, and I'll be back with you in a bit
MyWay: do you have another question?
Randall M.
Sure thing.
MyWay
maybe I can stop the messages as we were saying, add a info message/button to go back to the bottom and add to the top something like "load older messages" like some app is doing, to avoid users losing new messages and showing them too much, what do you think Mark? And if yes, how can I do that thing "load older messages"?
Mark M.
well, for the "load older messages", I'd be tempted to say "pull to refresh", but your direction is inverted from the norm there
usually you pull from the top to add things to the top
beyond that, have a widget that you hide and show, or use a snackbar
or use a crouton
(snackbar is in the Android Design Support Library, crouton is an open source library)
4:40 PM
MyWay
yes
Mark M.
or other similar sorts of alert-y things: http://android-arsenal.com/tag/104
MyWay
to load older messages I'd have to put it at the top
thank you for your help, I think it can work this way
Mark M.
OK
let me give the others another shot, and I'll return to you in a bit
CodeApprentice: do you have another question?
CodeApprentice: if you come up with another question, let me know
Randall: back to you! do you have another question?
Randall M.
Yeah, so...
CodeApprentice
Getting some info together for you and trying to formulate my question.
still thinking about the synchronization issue
Randall M.
I tried
View paste
      Intent i = new Intent(context, MainSetupActivity.class);
      i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
      context.startActivity(i);
And got
Exception: "Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?"
Mark M.
right
Randall M.
So, do I understand that correctly, that even though in the same task, I can't do that?
Mark M.
that's because context is not an instance of Activity
4:45 PM
Randall M.
Or am I not in the same task?
4:45 PM
Mark M.
a Context that is not an Activity has no task
tasks are purely an activity thing
Randall M.
Ah, so hence your suggestion for a broadcast, received by a context that is an Activity, and *that* activity restarts the other one?
Mark M.
right
though don't use a system broadcast; use an in-process event bus
Randall M.
OK, I think I get it now.
Mark M.
so LocalBroadcastManager would be fine, if you're into broadcasts and IntentFilter and such
Randall M.
Is there some flag I can set to tell the system to try to hold onto a given Activity for as long as possible?
CodeApprentice
let me know when you're ready for my question, Mark
Randall M.
(to try to prevent it from being destroyed before my 2-second service execution)?
Mark M.
Randall: not really
the only reason it would be destroyed is if you finished it, the user pressed BACK, or you crashed with an unhandled exception
Android does not randomly destroy activities, though it does terminate *processes* due to low memory conditions
Randall M.
Ah, OK then. I'll go down this path, and see what happens.
You might see my Thursday evening. :)
Mark M.
OK
CodeApprentice: OK, back to you
CodeApprentice
First, the relevant classes in my github:
View paste
Example test: https://github.com/codeguru42/bbct/blob/issue/56-loaders/gradle/android/src/androidTest/java/bbct/android/common/activity/test/BaseballCardListWithoutDataTest.java

Fragment containing ListView: https://github.com/codeguru42/bbct/blob/issue/56-loaders/gradle/android/src/main/java/bbct/android/common/activity/BaseballCardList.java

LoaderCallbacks: https://github.com/codeguru42/bbct/blob/issue/56-loaders/gradle/android/src/main/java/bbct/android/common/provider/BaseballCardLoaderCallbacks.java
hmm...didn't make links, sorry
Mark M.
that's OK
I can copy and paste with the best of 'em :-)
CodeApprentice
So one of the tests in BaseballCardListWithoutDataTest.java adds some data and then returns to the ListView to make sure the data is displayed correctly.
but the data is now being loaded with LoaderManager and CursorLoader
4:50 PM
CodeApprentice
so I need the test to wait for the loader to finish populating the listview
my idea for a solution is to add a `waitForData()` method to BaseballCardList which simply delegates the call to BaseballCardLoaderCallbacks.waitForData().
the place I am stuck is implementing this last method
Mark M.
ah, that would explain why I can't find it :-)
CodeApprentice
yah, I haven't committed anything that I've tried yet.
Mark M.
well, if you're willing to clutter your production code with test-related stuff, use a CountDownLatch or something
that's my go-to class in testing where I need to block the test thread waiting for some other async operation
CodeApprentice
I'm trying to minimize the amount of clutter in production code, but this is the best I've been able to come up with so far.
Mark M.
understood -- as I indicated earlier, I don't know how to solve this without some degree of production clutter either
CodeApprentice
so if I understand CountDownLatch correctly, I will need to create a new one each time the loading starts, correct?
Mark M.
yes, that sounds right
another more flexible approach would be to use more of a listener pattern
CodeApprentice
hmm...
Mark M.
BaseballCardLoaderCallbacks would tell listener(s) "hey, the load is done"
4:55 PM
Mark M.
your test code would hook up a listener that would countDown() the latch
and your main test method would await() the latch
now, in this case, the listener is only needed by testing, so it's technically clutter
CodeApprentice
what do you mean by "test code" and "main test method"?
Mark M.
actually, they're the same thing, sorry
let's say you have testThatMyListGetsItsCards()
which is the test method that is trying to do this whole wait-on-the-loader thing
CodeApprentice
I'm with you so far.
Mark M.
testThatMyListGetsItsCards() would somehow (and I'm fuzzy as to how) register a listener with BaseballCardsLoaderCallback, where that listener would countDown() the latch
then, testThatMyListGetsItsCards() would trigger the actual data load, then await() the latch
BaseballCardsLoaderCallback would invoke the listener's event method (e.g., yesWeHaveCards())
that listener is where the countDown() is
net: testThatMyListGetsItsCards() blocks until the load is done
problem #1: how do you get the BaseballCardsLoaderCallback to register the listener?
problem #2: is that still too soon for your test results (e.g., is the ListView updated by now)?
but I leave those as an exercise for the reader :-)
5:00 PM
CodeApprentice
I'm worried about a scenario where someone await()s but the loader is restarted. If I simply create a new CountDownLatch each time the loader restarts, then there could be a problem that the await() never returns.
Mark M.
this is test code
make sure you unregister the listener when the test completes