Next step in converting util_ methods that required blocking: blank tile
assignment. Now post a query and add a method that the client code can
call when the user's decided. Include enough state (col, row, and
playerNum) so that it's probably pretty safe.
Probably breaks curses build, but for gtk and Android
turn move and trade confirmation into a two-step process, making
board_commitTurn() non-interactive when called with a second
parameter. The old blocking util methods now return void and it's up to
the client code to interact (on the main thread) then re-call
board_commitTurn() if appropriate.
First attempt to stop blocking the jni thread: instead of returning a
password from a util_ method, have it include enough state that the UI
can return, put up a dialog, and then pass that state and the password
back and have them matched up again. I think this will work for the
remaining blocking Alerts too.
Includes a hack in DBAlert that's required I think because
GameConfigDelegate is launched "for result". onCreateDialog() always
fails after a rotation the first time because GameConfigDelegate isn't
there to have its makeDialog() dispatched to. So it puts up a dummy
alert and then post()s code that is successful in calling makeDialog()
to get an alert from GameConfigDelegate that can replace the
dummy. Nothing shows on the screen on simulator anyway.
The major problem remaining is that blocking alerts in BoardDelegate are
recreated after rotation but the thread that was blocking has been freed
so nothing can be done after the new alert returns. E.g. blank tile
picker will be posted again, user will pick a tile, but the common
code's not in a state to do anything with that choice (which cannot even
be "returned.") Options are to find a way to make the JNIThread survive
the configuration change without unblocking or to rewrite all the common
code to not expect return values from util_ methods.
This commit is not well tested, and diffs don't allow a thorough check
of the conversion of each DlgID type.
New way of adding onDismiss listener to DialogFragment-based alerts
required API change to DBAlert and the makeDialog() method. Fixes
customizing default player name.
Had left out dispatch of "negative" buttons populated from an
ActionPair. Tested for offer to remove redundant new game buttons at
bottom of games list.
For GamesListDelegate only so far, replace calling <activity>.show(int)
to launch an Alert with something producing and showing a
DialogFragment. Replaces passing and saving state inside the
DelegateBase subclass with saving it as part of the fragment's bundle,
and it looks as if a single class will work for nearly all of the alerts
managed by DelegateBase.{onCreateDialog,prepareDialog}(), which will
eventually go away. The beauty is that the implementations of
onCreateDialog and onPrepareDialog remain, but as the body of a new
makeDialog() that's called by the fragment's onCreateView. Less code
changes, but now it's all called every time an alert's created.
Since associated DlgDelegate instance doesn't survive a rotation, needed
to pipe results through a DlgClickNotify implementation on XWActivity
that then dispatches in the DualpaneDelegate case to all visible
fragments. I hope this turns out to work for all DlgClickNotify
implementations as I switch them over. We'll see.
Add script that when copied to device and run from 'adb shell' captures
all app and OS logs to file. Next: add code to XWApp.java to copy it
from assets/ and run it on launch.
On 2.3 anyway there's a java.lang.VerifyError trying to load that
class. So wrap the loading in a new class that catches the error and
sets a boolean so subsequent calls don't need it. There are other calls
that could load the class, but they may not be reachable. To be tested.
Required to fix alerts going away now that orientations are handled by
the OS (which in turn is the preferred way to do things now since not
ALL config changes CAN be handled by the OS.) With this change some
alerts will probably cause crashes during a rotation. Those all need to
be fixed.
Add listener interface to DBUtils and hook StudyListDelegate into it so
that if a word is added while the list is being displayed the new word
shows up immediately.
wordlist browser selection and downloaded info about downloadable
wordlists didn't survive rotation. They do now, the latter as a huge
serialized array. To make selection work I save the keySet() of a
mapping of selected names to the views that represent them. Now the
presence of a key, even if the value is (temporarily) null, signals that
something's selected.
Old code that didn't rotate properly was meant to associate view IDs
with fragments so I could iterate over them, e.g. to dispatch
intents. To replace that the common superclass of all fragments now
keeps a set of active ones and provides a method that uses that to find
the fragment that owns a view. So I can iterate over fragments based on
the dualcontainer-contained views as before.
Remove the generated FrameLayout that was breaking restoration after a
config change. Set layout_weight=1 for all fragment root views so they
get half the screen in landscape mode. Remove some code. Problems
remain, first among them that notification intents aren't dispatched correctly.
A group that's closed stays closed even if a game's added to it. The
change flushed out a bug where the groups cache wasn't being invalided
on a game move, so fix that too.
Name file and set internal constant sent to server to use same git rev
string so that the script will correctly identify whether the version I
have is the latest it has available. Fixes server offering to replace
with what I already have.
replace symlink silliness ant required with gradle commands to sign
debug builds with the checked-in (local file) debug keystore. Make
it possible for an environment variable to override in case somebody
wants to use his own.
Looks like there are battery drainage issues with the current half-done
implementation, so add option to turn it off. Only takes effect on
restart, and only matters on dbg variant now since it's compile-time
disabled on the main variant.
Also add discovery machine state to ConnState printout, stop running the
machine over and over (likely the battery problem), but add ability to
restart it triggered by the ConnState's "reconnect" button.
Also start persisting the most recently seen set of peers. If service
discovery and not connecting is the problem having these available to
try to connect to on startup might help.
Build different jni code (into different obj and libs directories) for
the different release/debug and xw4/xw4dbg combinations. This works when
only one build is done, but when doing two some later task tries to put
both multiple same-named libraries into the .apk and so fails. I still
think it's worth adding this to keep from using the wrong binary, but
that needs to be fixed.
cleanNDK just nukes all the directories. Easier than invoking build-ndk
clean...
Merging of AndroidManifest.xml files meant the dbg flavor was getting
its C2D_MESSAGE permission and the main flavor's, which on recent OS
versions meant it couldn't be installed. Use substitution from gradle to
fix.
GamesList menu was in a bad state after a game closed because hadn't
been rebuilt. It's simplest to invalidate it surgically, when the
BoardDelegate notifies that the board's closed, though doing in in
onWindowFocusChanged() might catch more edge cases. We'll see. I want a
low-risk impact right now.
but not on emulator running same OS version (or close). And in spite of
an exception being thrown the work being done, hiding a preference,
still succeeds. Whatever.
Fixes case where user has received invitations but not allowed SMS then
decides to do so. It's a hack giving Perms23 class knowledge of how to
send moves etc. Instead I should be letting interested parties register
for perms-granted events, but that can happen later, and is less
valueable while SMS is the only "dangerous" permission and the only one
that blocks message sends.
JSONObject(<string>, null) clears out any existing entry but doesn't add
a new one. So store missing names as "" instead. Not sure how this
worked when I first tested it....
My old asking for permission to turn on/off SMS is confusing and adds no
value when the OS separates out permissions and is confusing. So get rid
of it on Marshmallow and later, always returning true from the old API
and hiding the preference in that case.
The write-red-on-it thing doesn't work for the notify.png file used in
notifications (I think because I can't find a color Android doesn't
strip out.) So use a rotation transform instead. Users will never see
this anyway.
Move connection options READ_PHONE_STATE check from ConnViaViewLayout to
GameConfigDelegate and give it a rationale. Preferences stuff can't do
permissions because it can't override onRequestPermissionsResult (base
activity is of wrong class). Should fix that at some point, either by
moving to preference fragments or with a dummy activity to go over the
top.
Explicit "ask again" and "skip" buttons in alert showing rationale;
check/ask for permission before sending invitation via SMS; warn each
time SMS-enabled game is opened without permission but allow it to stay
open and if necessary send an invitation.
add "close game" button to warning about lack of comms, and do that on
dismiss too. Nothing good can come from having the thing stay open.
Eventually the "close" should turn into "edit" and launch GameConfig on
the game with the comms selector up, but that's hard, as currently the
launch only happens from GamesList and is via
startActivityForResult. Might be easiest to close and send an intent to
GamesList to cause it to launch GameConfig that way. Not for this release.
An edge case, but: doing "new from" on a game without any connection
types crashed because of an assertion in comms that assumed
addr_setType() was being called on zero-initialized flags, which
shouldn't have been a requirement. Pulled that as well as java code that
added RELAY-type connectivity to any game that had none. If a game has
none, leave it that way.
replace dlgButtonClicked() with separate methods for positive and
negative buttons and alert dismissal. I was making too many mistakes
because the old method was getting called twice (e.g. for negative and
then dismissal) and I hadn't needed to differentiate until adding that
new Action. There should be no behavior change with this, but it's
pervasive and replaces some spaghetti.
Let OS ask for STORAGE when user chooses Downloads dict storage option,
but do nothing if it's not granted. Existing code asks again when the
actual move is to done.
If an game's open that uses a wordlist kept in Downloads and permission
is revoked, close cleanly. On re-opening the user will be prompted by
existing code to download again. (Note: not prompted to turn STORAGE
permission back on. It would't be easy to detect that case, and I'm not
spending time helping people who are perverse.)
Add new static method that takes a request, rationale and Action, offers
the rationale if required and then, if user hasn't cancelled, asks for
permission. Eventually the Action is issued with either POSITIVE or
NEGATIVE. Use this to ask for STORAGE when downloading upgrades and
moving wordlists to the downloads area.
You need PHONE permission to start and play an SMS game but not to have
started it in response to an invitation. But to rematch you need it. So
ask, including offering a rationale that differs depending on whether
there's a way for the game to communicate once SMS is removed.
They were hacks for debugging SMS early on, probably infrequently
seen. Instead post messages. BUT: they aren't being received because of
how listeners get cleared in onPause(). Needs to be fixed, but there's
no harm in having them dangle for now.
Sometimes when a new game is created the board winds up with the toolbar
drawn over the tray. That's because when it was laid out the game wasn't
running yet and so none of the toolbar buttons was enabled, giving the
toolbar a height of 0. So now the first time the updated toolbar reports
that at least one button is enabled we force the board to lay itself out
again.
Little hack to dismiss a game when the invitations dialog was cancelled
broke invitations altogether. Revert that change, and simply check on
getting focus whether all players are present; if they're not, put up
the "invite or wait" dialog. This is a big change, but in 10 minutes of
testing I can't get the wrong thing to happen.
Remove strings that no longer exist in English, add missing "other"
quantities in Japanese (taking a guess from context that it's ok to use
generic counters), and in a couple of languages fix typos in format
specifiers that would have caused crashes.
It wasn't even checking all languages. Now it does, lets you specify
which ones if a subset's desired, and differentiates between sets of
format specifiers mismatching in a way that'll cause a
crash (e.g. expecting int in one and string in another) and just having
some missing.
Let's make it easier to add to the set of available BT devices from the
place where the user sees the need. TODO: update the descriptive text,
but not this release since it'll break existing localizations.
Channel wasn't a thing back then. Catch the ClassNotFound exception and
set the same boolean as when permissions aren't available (in manifest),
which means nothing else tries to run.
Add cancelled notification to invite choices dialog. Look for that in
board delegate and close the game. Makes the inviting experience more
consistent, the goal being that you never look at a game that's missing
players without some intervening dialog preventing you from trying to
play.
Use an array of actual objects rather than indices, and add an equals()
method, so checkbox status can survive changes in the set of available
items and even be reproduced when the objects backing them change (as
happens after a new BT scan finishes.)
Control whether wifi direct is enabled from build.xml, a file that's
already different for the two variants. The point here is to do a
release with permissions changes but without having to fix everything
wrong with wifi direct.
If user hasn't granted READ_CONTACTS permission, use the pairing of
numbers/names that's come from Contacts via the SMS invitation dialog.
Sometimes it'll have names for numbers that arrived via invitations. Do
some cleanup while at it to prevent stored BT mac addresses from being
looked up as phone numbers.
On launching SMS invite dialog, ask for READ_CONTACTS permission. Where
it's needed, though, is for the user-invisible process of translating
phone numbers to names (not launching the Contacts app via an
Intent). This just seems like the best place to ask since the user's
thinking about contacts and phone numbers.
InviteDelegate superclass now tracks whether something is checked,
though it's not doing as well as subclasses did before, e.g. when SMS
would uncheck everything but a newly added contact.
Add post-open test for SMS and then ask for SMS permission, giving
rationale if the OS says I should. Meant adding a new interface for
rationales and wiring that in. It's a bit spaghetti and I may think of a
cleaner way later.
When opening a game that sends via SMS, check if it has permission and
offer to remove the config if it doesn't, removing a similar check from
pre-opening code. (Here it'll catch e.g. games opened from the
GameConfig process.) Sends/receives without SMS permission are already
caught by existing catch{} blocks, so there's no crash.
Moving stuff into superclass and trying in general to simplify
things. BT and SMS invitation work, but the option to have more than one
player per device isn't available. RelayID invitation is a mess, but was
before I think. WiDir invitation crashes in assert because I can't
debug it on this branch. Lots of work and cleanup still required.
rewrite perms23 class to use Builder pattern, making it easier to pass
more than one required permission. Use that (with hard-coded set for
now) to check and ask for permissions a game needs to communicate. Offer
to remove the comms methods the user doesn't want to permit (but that's
not implemented yet.)
First, needed to remove some state caching since though a non-SMS
phone will never become one my ability to ask changes over time. Then
changed population of communication options to first ask for the
permission needed in order to find out what's supported, then to find
out what's supported via methods that will return false without
permission. Can't think of a better way right now....
Add a compile-time option to send commands in json as strings rather
than ordinals (ints) for easier reading of logs. And log/keep track of
when discovery's turned off or on in case it's useful for making all
this work.
In handling resend for a newly available transport, if the lock can't be
obtained see if that's because the game's currently open and if so post
to its thread.
Each time we get a socket to a peer, try sending all pending wifi direct
messages. It's a hack, but trying to send isn't that expensive and the
experience is much improved.
When contacting opponents, iterate over opponent addresses rather than
information cached in the summary. Start implementing for BT, but that
needs more work. Change enum names in WiDirService state machine. P2p
opponent notification untested because apparently an open hotspot, which
you can't prevent devices from connecting to, breaks P2P connectivity.
Rewrite to use new XWPacket class that wraps JSONObject but could later
support a binary format, and reply with new message when packet arrives
for a game that does not exist. Code already present turns that into an
invitation to delete the game.
fix race condition in accept thread; only call wifimgr.connect() on
guest side. Connectivity now works reliably if the guest starts, or is
restarted, when the group owner is running. If owner is restarted the
guest seems never to connect (without restart.)
Two crashes reported on the Google Play store in the 112 version have at
least easy work-arounds: catching exceptions and doing nothing. In both
cases that should be harmless, so assuming the causes are rare it's a
good move.
Make it a trivial state machine that implements Runnable and
ActionListener so it can be scheduled and notified easily. Removes a
bunch of code. Connecting still happens unreliably, but it should be
easier to tweak things now.
Add UI, minor thanks to recent refactoring, to allow invitations via
wifi direct. Uses a mapping of all currently known device mac addresses
to names, with only the latter shown to users. Works well, though
something I changed seems to have causes devices to start losing track
of their connections to each other.
Simple fix to the problem that a group owner may not be in the role of
game host: if I'm a guest and don't have a socket for the mac address to
which I need to send, slap src and dest addresses into the message and
send it to the group owner. That device can then forward it.
Found a recipe for service discovery that at least in the bit of testing
I've done is much more reliable. Devices connect almost
immediately. Basically everything chains through onSuccess() handlers.
URL to recipe is in code.
Examples kill it before calling connect(), so do the same. Seems to make
connection more reliable. (Pending: handle case where more than one
two devices are in the group!!!)
Made WifiDirectService into an actual service in order to better process
incoming packets. Now works for game messages from comms, and posts
notifications when app's in background. The latter requires using the
app context from XWApp since unlike the other transports this one
doesn't involve the OS invoking us with a Context.
Got to the point where I have an open socket for the packet I want to
send! Lots of changes to how discovery works seem to have improved
reliability though I'm still unimpressed. Much to learn.
Grab and store the local device's mac address. Add p2p as a type of
address, represented by the mac address of the recipient. Include the
local device's address in invitations sent when specified by user. Now
the WifiDirectService class is being passed a packet and the address of
the recipient; it will next need to set up sockets with every device it
encounters and map them to their mac addresses so that it can do a send.
Starting with the release of Nougat there have been cases where the
board would not correctly redraw. It's been most evident when using the
hit feature many times in a row, with something going wrong every fifth
time or so (but not that consistently.) It's as if modifications of the
bitmap backing BoardCanvas were being done asynchronously and not
necessarily all completed when I blit the canvas to the screen via
canvas.drawBitmap(). (As evidence of this I confirmed that a tap on a
tile in the tray after a bad draw would cause the screen to correct
itself even though the only additional rendering was to the tray. So by
the time that second drawBitmap() call happened the bitmap data was
correct: all draws that hadn't completed earlier had done so by now.)
The fix is to call Bitmap.createBitmap(bitmap) and to use the copy as
the source of the canvas.drawBitmap() blit operation. I suspect that
createBitmap() waits for any pending draws into its source to complete
before making the copy. Regardless, if this hasn't fixed the problem
it's made it so rare that I'm not seeing it, and since I'm only doing
the copy on Nougat there's little risk in the change. And I can't detect
any problems coming from the considerable additional memory being used
and immediately marked available for gc.
Starting with the release of Nougat there have been cases where the
board would not correctly redraw. It's been most evident when using the
hit feature many times in a row, with something going wrong every fifth
time or so (but not that consistently.) It's as if modifications of the
bitmap backing BoardCanvas were being done asynchronously and not
necessarily all completed when I blit the canvas to the screen via
canvas.drawBitmap(). (As evidence of this I confirmed that a tap on a
tile in the tray after a bad draw would cause the screen to correct
itself even though the only additional rendering was to the tray. So by
the time that second drawBitmap() call happened the bitmap data was
correct: all draws that hadn't completed earlier had done so by now.)
The fix is to call Bitmap.createBitmap(bitmap) and to use the copy as
the source of the canvas.drawBitmap() blit operation. I suspect that
createBitmap() waits for any pending draws into its source to complete
before making the copy. Regardless, if this hasn't fixed the problem
it's made it so rare that I'm not seeing it, and since I'm only doing
the copy on Nougat there's little risk in the change. And I can't detect
any problems coming from the considerable additional memory being used
and immediately marked available for gc.
commented-out logging of drawCell, with flags; debug-build checks that
static rects passed to java draw code aren't being used by multiple
threads at once.
Current code is hitting the relay every 60 seconds, at least on
non-google-play installs like f-droid. Switch to using a backoff timer
that maxes out at once per hour. Eventually may want to not run the
timer at all when there aren't any unfinished relay games present.
Now sends and receives a single packet more often than not. Disabled for
non-DEBUG devices, and new permissions that are required are commented
out so I don't accidentally ship before the sdk-23 permissions stuff
makes additions less scary.
Disabled at compile time, and missing permissions required, but if
enabled is able to discover other devices running same app. Next: try to
open a socket.
Meant using NotificationCompat, dropping FloatMath, and changing a bunch
of build config stuff. Not done for gradle builds yet. Currently crashes
on a new install until you go into Apps/Crosswords/Permissions and turn
on the four "dangerous" ones it's using. Now the work is to check for
and request missing permissions on demand.
Crash occurs when creating new game: because there's no turn set yet the
jboolean has no set value (whatever was on the stack). Apparently the
java runtime accepts only 1 and 0 for jbooleans.
Crash occurs when creating new game: because there's no turn set yet the
jboolean has no set value (whatever was on the stack). Apparently the
java runtime accepts only 1 and 0 for jbooleans.
This completes moving to using traditional logging. At least to having
each file provide a TAG. Class rather than TAG is passed in, and format
strings are preferred to contatenation, but now adb can work with the
output.
I think this was related to changing the order in which save operations
happen and that it's no longer valid to insist that there already be a
rowid at this point, but could be wrong....
Fixes problem with list view always being a move behind, since it's
saving not summarizing that was triggering the refresh, but summarizing
that added the data from which refresh/list view drew.
Idea is to have the games list stay in sync, but in fact it stays one
move behind, at least in a typical standalone human-vs-robot game. So
this is incomplete.
There are some screen dimensions, especially with dual-pane mode, where
the board is just bit narrower than the screen. Rather than have narrow
white borders, allow the cells to take up the slack. The API takes an
upper bound on the ratio of width to height so things shouldn't get too
odd looking.
Running into a case where views haven't been added to the layout yet but
I'm searching there for their associated fragments. Fix to NPE stops a
crash, but intents still aren't being handled. I need to try to handle
them later, but am unsure when to start trying.
Mistaken option to gtk_box_pack_start() meant the scrollbar got
allocated space along with the board when the container expands, not
what you want with a scrollbar.
Too-clever double loop had an no-exit path. Now it's simpler: find a
fragment that can handle the intent, pop all fragments to its right
until it becomes top-most (there may be none), and then give it the
intent. Use of popBackStackImmediate() is required so old fragments are
gone before intent handling wants to create new ones.
Since display is now by whose turn it is and how recently there's been a
move in the game, the order can change on every save. So in the listener
for saves, redraw the group the saved game belongs to. The best way to
do this without flicker seems to be to mimic closing then re-expanding
the group, so that's what I'm doing.
Getting an NPE iterating over fragment/children of dualpane view
because, I think, committing them's been postponed. So add the iteration
as a Runnable to the same postponement mechanism. This may be wrong:
needs to be run for a while to see if it fixes the NPEs I'm seeing (on
opening an intent in the morning after the device has been idle all
night.)
Attempt to fix crashes happening when the occasional fragment
transaction happens before the OS is ready. Keep a boolean indicating
whether we're in that state, and when it's not set create a Runnable to
be run next time it is. Temporary Toast lets me know when it's working.
Attempt to fix crashes happening when the occasional fragment
transaction happens before the OS is ready. Keep a boolean indicating
whether we're in that state, and when it's not set create a Runnable to
be run next time it is. Temporary Toast lets me know when it's working.
Problem: brain-dead Android code (on 5.1.0 anyway) caches the
SectionIndexer separately from the ListAdapter that implements it, so
when you change the ListAdapter it keeps using the old one for its
SectionIndexer. Solution is to replace the ListView in the layout with a
FrameLayout into which a ListView is inserted at runtime on init and
whenever the data changes.