Play store reports crashes but stack crawl doesn't tell me where
from. So try catching the exception and on debug builds logging what
dialog is responsible.
When those became an advanced feature I added a warning for upgrading
players who'd miss them. That was long ago, and the warning was only
supposed to last a release or two.
If a Makefile defines a dirty word list then a new python script is
invoked to filter for and remove those words as the dict is being
built. So far I have for English only, which makes sense because only
English wordlists are built-in on Android and Google's rating system
cares only about what's built in.
showDialog() goes away except for PrefsActivity which can't do
fragments. Move stuff required by PrefsDelegate into it from
DelegateBase since no other subclasses does old-style Dialogs
any more. Remove a bunch of stuff from DlgDelegate, e.g. saving
state, that no longer gets used.
Was getting crash with "Can not perform this action after
onSaveInstanceState". This lets the back stack get back into shape so it
can put up another alert.
Add to DldID the ability to specify whether dialog fragments bearing
that id go on the back stack. Set INVITE alerts (only) to use that
mechanism. Having them on the back stack seems to be required by their
buttons being hacked to not dismiss them, which in turn is required
because I want them to stick around underneath other alerts their
buttons launch.
Get rid of explicit enable-dualpane boolean pref, instead relying on the
three way default/force-phone/force-tablet pref. Offer to change that
rather than the former in BoardDelegate when run on upgrade. Move the
prefs setting from Debug to Appearance since it's "real" now.
Recent recommendations are not to attempt to handle it myself, so don't
and we'll see how it goes. And unify notions of tablet and enabling
dual-pane: a device that isn't dual-pane should not be using the new
single-activity stuff at all.
Had to disable use of the back-stack for DialogFragments, though that
means I can't prevent duplicates from stacking up (esp. in the
pathological robot-vs-robot case, but also just when I rotate the device
while a "rematch" alert is up.) The problem seems to be in dismiss
actions being handled too late. One easy-to-duplicate case is the
tile-picker. When it's enabled and you commit a turn I post first the
confirmation and then, in response to a "yes", the picker. But the
picker gets added to the stack moments before the confirmation is
removed, and it's the nature of stack removal that everything above
what's being removed gets pulled too. So you never see the
picker. Simply post()ing a runnable to put the picker up later fixes
this one case, but a similar trick doesn't work elsewhere, so I'm
punting until I have time to root-cause the problem.
Each alert gets a unique name. Add to backstack, and before doing so
look for another with the same name and dismiss it. This works most of
the time, especially to prevent them from piling up with orientation
changes. But in a robot-vs-robot game, and occasionally in a game with
three robots and one not, SCORE alerts stack up. It's the removal that's
failing, not the test for a duplicate, so perhaps I need to somehow wait
for it to succeed before adding the new alert.
DELETE_DICT Action is expected to flow through child, at least until I
fix to disable deleting a dict while it's open in the dict browser. It
may be confusing but with the assert gone does no harm.
Make the default be that it's on, and move the new-feature notice from
app launch to board open, the point where most will see the feature for
the first time. Remove the notify-and-exit thing, which is harder to
make work from the board, instead just posting an alert, if user chooses
to disable, that the setting change takes effect after a restart.
Needs to show different text and buttons, so now that it isn't getting
dismissed and rebuilt every time a button's pressed force that process
manually the time it's required.
When game's resumed (brought from background) and Wait alert is up
onStart() is called before getDialog() has a non-null value to
return. onResume() is the place to modify the dialog.
Rewrote Wait/Invite alert to use new functionality that allows it to set
its AlertDialog buttons to not exit when tapped. Turns out things are
much simpler when I don't have to hook into all the places my dialog
might need to be put up again.
Override onCancel() in order to finish when the alert's closed via the
back button. Count the number of alerts and when it drops to 0 (when all
the alerts launched from the invite/wait dialog are gone) put the
invite/wait up again.
I can't figure out how to prevent rotation from duplicating the wait
alert, and don't have the ability to have makeDialog() return null, so
when that happens -- when a second is being created -- dismiss the first
first. And clean up some callbacks made unnecessary by onDismiss().
Try to track in local saved state enough, including a count of
sub-alerts the main Invite alert has put up, whether when something's
dismissed the alert should go back up or the game should be
closed (which is the right thing to do when the back button's pressed.)
Hack: setting a variable to null in onSaveInstanceState() then testing
it in an onDismiss() call that happens as part of rotation avoids an
exception when rotation the phone.
Move most state that needs to be saved in Bundle into a single
Serializable class so adding new is easier. Add to that the current
selected group so we don't crash when nothing's been set. (I think in
the move to DialogFragments I lost the code that would have disabled the
Move button. That's back.)
Move call to end of derived class' method. This may very well be the
solution to a bunch of random crashes logging
"java.lang.IllegalStateException: Can not perform this action after
onSaveInstanceState". Chief suspect is the change in XWActivity.java,
but I'm making it everywhere for consistency's sake: it's a good habit
to get into.
New classes implement custom alert and its view, where most of the logic
for putting up one button per tile, hiding and showing buttons based on
what's left, etc. lives. Rough, but works well until rotated, when gets
redrawn without spaces for the buttons that could come back.
The crazy hack to get around Android's lack of modal dialogs is at long
last gone. Remove semaphore and code that uses it. Will leave the
background thread in place, at least for now, as removing it would be a
huge change.
Got a crash opening games where tile pick was on and first player was a
robot. Cause: non-robot player's tiles were being assigned before the
robot's, and the move stack didn't like the out-of-order assignments.
Now we assign tiles in order as before, but pause each time we find a
non-robot that needs to pick its own.
Make face-up tile picker util method return void and add mechanism for
passing in selected tiles asynchronously, as has been done recently with
the rest of the once-blocking util callbacks. Works perfectly in the gtk
case. Likely crashes in curses (if picking face-up option is on.) In
java all the callbacks are there but rather than put up a UI we pretend
the user says "pick 'em for me" each time. Putting up a UI is next.
Rewrite the overly-complex invite dialog that's posted when a game opens
and is missing players so that it doesn't crash after an orientation
change. It's still possible to dismiss it yet have the game stay open.
The goal is that a game in that state always has a dialog on top of it
so you don't get confused about e.g. why you don't have tiles, but
there's more work to be done there.
Also added a common superclass for all my DialogFragments. It may be
useful e.g. for having each DelegateBase instance able to track what
dialogs it currently has up, e.g. to have a policy about how many of the
same class can be live. I'm thinking here of how to prevent a
robot-vs-robot game from ending with a stack of 25 move reports. (The
change here is that the old activity.showDialog(int) only allowed one at
a time, and some of my logic takes that for granted.)
There were multiple places launching it, which in a DialogFragment
world (in the absence of other per-dlg-id controls) means stacked
multiple alerts. And dismissDialog() didn't work. So use a boolean to
prevent more than one being posted and keep a reference to the
DialogFragment behind it in order to be able to call dismiss() on
it. That's the only place I was calling dismissDialog() (other
than the tile-picking stuff which has to be rewritten anyway) so this
little hack is probably ok.
This alert still gets messed up with the screen config changes. Fixing
that is next.
Back in August I "fixed" timers running after the board was cleared but
didn't realize that util contexts were shared by snapshot
boards. Clearing those timers when the board's destroyed was stopping
timers for a visible board as well. So I added a boolean indicating
whether to clear timers. Ref counting or similar would be better, but a
lot of work given the concept isn't really in the common/ code at
all (outside of dicts...)
I can't get preference fragments working in my world (mostly because
nested PreferenceScreens don't open when clicked on and the workarounds
for that are too complex) so next best thing is to fix alerts to work on
top of prefs activity. That involves creating a new transparent activity
that subclasses XWActivity and has a DelegateBase subclass. Launching an
alert is then just a matter of passing DlgState into the new activity
via an Intent, at which point everything Just Works. Button clicks are
returned via an intent as well.
Continue conversion of alerts that required blocking the JNI thread. Now
board_commitTurn() takes a second boolean indicating whether phonies
found have been approved by user. Common code informs user, and if he
approves client code calls board_commitTurn() again. In case where
turn's lost there's no call to make back, but there's the undesirable
change that if a robot moves next its move will be reported on top of
the turn-lost alert. Ideally new alerts would appear under, not on top
of, those that have not yet been dismissed.
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.