My main activity kicks off a secondary activity (using startActivityForResult) that invites the user to input details of one of four players at a bridge table.
When the user has chosen a player and clicked OK, the following things happen (triggered by code in onActivityResult) 1. The player details (passed back in the Intent returned by the secondary activity) are written to a text file. 2. An AsyncTask is started to send that file to a server PC using SMB. 3. While the AsyncTask is still running, the same secondary activity is invoked again to get the next player's details. This sequence is repeated until all four players' details have been sent to the server. On my tablet, this works perfectly every time but one of my beta testers has a bizarre problem. He can usually enter the first player's name successfully, sometimes the second and occasionally the third but eventually one of the names screens appears for a short time later only to disappear again without the user clicking my OK button. Note that the player-names screen allows the user to type a partial name and uses a ListView to display all players in a club membership list that contain the letters typed. I don't know if that's significant. What I am seeing in my trace file is that the onCreate method of the select player activity is called. The OKClicked method is never called. In fact, I have trace points before every finish() call in the select player activity and none of them is called. After the onCreate call, the next thing I see in my trace file, about 30 seconds later, is onActivityResult reporting a ResultCode of zero and the returned Intent pointer of null. In other words, the select player activity has spontaneously closed without the user clicking OK or it going through any of my code designed to close the activity tidily. This never happens on my tablet, which is running Android 4.4.2. It always happens on my beta tester's tablet, which is running 4.4. After reading reports of a similar Android problem, I changed all the activities' settings in my manifest (including the main activity) to launchMode singleTop but this made no difference. Any suggestions? This is particularly awkward because I cannot reproduce the problem on my kit. I am having to do all my debugging remotely by adding trace points and asking my beta tester to repeat the experiment. I have uploaded the code for the select player activity in case it's of interest. Kind regards Keith -- You received this message because you are subscribed to the Google Groups "Android Developers" group. To unsubscribe from this group and stop receiving emails from it, send an email to android-developers+unsubscr...@googlegroups.com. To post to this group, send email to android-developers@googlegroups.com. Visit this group at https://groups.google.com/group/android-developers. To view this discussion on the web visit https://groups.google.com/d/msgid/android-developers/6502018f-b8b0-4dbb-a3a8-b132458cca3d%40googlegroups.com. For more options, visit https://groups.google.com/d/optout.
package uk.org.writerman.scoresheet; import android.content.Intent; import android.os.SystemClock; import android.support.v4.content.res.ResourcesCompat; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.text.Editable; import android.text.TextWatcher; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import java.util.ArrayList; import java.util.HashMap; public class SelectPlayer extends AppCompatActivity { private cClubMembers MembershipList = new cClubMembers(); private cClubMembers EBUList = new cClubMembers(); private cClubMembers MatchingPlayers = new cClubMembers(); private cClubMemberDetails ChosenPlayer; private String TableName; private boolean UseExternalFilestore; private boolean HaveNamesFile; private boolean HaveEBUList; private String SeatName; private Button OKButton; private ImageButton SearchButton; private ListView lvMatches; private long LastAbortClick; private boolean m_InternalChange; private boolean VisitorOnDisplay; @Override protected void onCreate(Bundle savedInstanceState) { // SelectPlayer. Parameters are Table identity string, Seat (as string) UseExternalFilestore and HaveNames // If it's a repeat, may also have ParamPlayerID, ParamPlayerName and ParamPlayerEBUNo // Return values are ParamPlayerID, ParamPlayerName and ParamPlayerEBUNo // We use the prompt above the input box (instructions when input is empty) to show // no matches, number of matches or full details of exact match. Colour code the background super.onCreate(savedInstanceState); setContentView(R.layout.activity_select_player); // STAW Statics.log(getApplicationContext(), "Start SelectPlayer activity"); m_InternalChange = false; VisitorOnDisplay = false; LastAbortClick = SystemClock.elapsedRealtime() - 4000; // To ensure first click doesn't abort TableName = getIntent().getStringExtra(GLOBALS.ParamChosenTable); SeatName = getIntent().getStringExtra(GLOBALS.ParamCompassPole); UseExternalFilestore = getIntent().getBooleanExtra(GLOBALS.ParamUseExternalFilestore, true); HaveNamesFile = getIntent().getBooleanExtra(GLOBALS.ParamHaveNames, false); HaveEBUList = getIntent().getBooleanExtra(GLOBALS.ParamHaveEBUList, false); final TextView tvCaption = (TextView) findViewById(R.id.spTableCaption); tvCaption.setText(getString(R.string.Table) + " " + TableName + " " + SeatName); OKButton = (Button)findViewById(R.id.bOK); lvMatches = (ListView)findViewById(R.id.spMatchList); if (HaveNamesFile) { try { MembershipList.ProcessNamesFile(getApplicationContext(), UseExternalFilestore, GLOBALS.NamesFileName); } catch (Exception ex) { Toast.makeText(getApplicationContext(), R.string.CannotParseNames, Toast.LENGTH_LONG).show(); } } if (HaveEBUList) { try { EBUList.ProcessNamesFile(getApplicationContext(), UseExternalFilestore, GLOBALS.EBUNamesFile); EBUList.isEBUList = true; } catch (Exception ex) { Toast.makeText(getApplicationContext(), R.string.CannotParseEBUNames, Toast.LENGTH_LONG).show(); } } // Monitor for selection from list lvMatches.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { if (position < MatchingPlayers.size()) { // Copy matching player info to name box String HisName = ChoosePlayer(MatchingPlayers.get(position)); // As amn internal change, copy the name to the input box TextView tvInput = (TextView) findViewById(R.id.spUserInput); m_InternalChange = true; tvInput.setText(HisName); m_InternalChange = false; } } }); // Monitor for changes to name box final TextView tvUserInput = (TextView) findViewById(R.id.spUserInput); tvUserInput.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { } @Override public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { } @Override public void afterTextChanged(Editable editable) { if (m_InternalChange) return; LookForMatches(); } }); // Have we been here before? String MemberName = getIntent().getStringExtra(GLOBALS.ParamPlayerName); if (MemberName != null) { int MemberNo = getIntent().getIntExtra(GLOBALS.ParamPlayerID, -1); int EBUNo = getIntent().getIntExtra(GLOBALS.ParamPlayerEBUNo, -1); cClubMemberDetails LastChoice = new cClubMemberDetails(MemberNo, MemberName.trim(), cClubMemberDetails.EBUNoToString(EBUNo)); if (!LastChoice.getMemberName().isEmpty()) ChoosePlayer(LastChoice); } } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.abortmenu, menu); return super.onCreateOptionsMenu(menu); } /** * Event Handling for Individual menu item selected * Identify single menu item by it's id * */ @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.closeapp: if ((SystemClock.elapsedRealtime() - LastAbortClick) <= GLOBALS.AbortTime) { Statics.log(getApplicationContext(), "SelectPlayer: User selected ahort"); setResult(GLOBALS.RetValCloseApp); finish(); } else { Toast.makeText(getApplicationContext(), getString(R.string.AbortPrompt), Toast.LENGTH_LONG).show(); LastAbortClick = SystemClock.elapsedRealtime(); } return true; default: return super.onOptionsItemSelected(item); } } private void ClearMatchList() { // Also disables the OK button // Now populate the array list with one empty entry ArrayList<HashMap<String, String>> nList = new ArrayList<HashMap<String, String>>(); HashMap<String, String> nLine = new HashMap<String, String>(); nLine.put(namesListAdapter.NameColumn, ""); nList.add(nLine); namesListAdapter adapter = new namesListAdapter(getApplicationContext(), this, nList); lvMatches.setAdapter(adapter); // String[] MatchList = new String[1]; // MatchList[0] = ""; // ArrayAdapter<String> lvAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, MatchList); // // lvMatches.setAdapter(lvAdapter); Statics.enableButton(OKButton, false); } private void hideVisPanel() { setVisPanel(""); } private void setVisPanel(String Text) { TextView visPrompt = (TextView) findViewById(R.id.ifVisPrompt); LinearLayout visPanel = (LinearLayout) findViewById(R.id.ifVisPanel); visPrompt.setText(Text); if (Text.isEmpty()) { visPrompt.setVisibility(View.GONE); visPanel.setVisibility(View.GONE); } else { visPrompt.setVisibility(View.VISIBLE); visPanel.setVisibility(View.VISIBLE); } } private void LookForMatches() { // If single digit, look only for club number MatchingPlayers.clear(); final TextView tvUserInput = (TextView) findViewById(R.id.spUserInput); String NewInput = tvUserInput.getText().toString().trim(); final TextView tvPrompt = (TextView)findViewById(R.id.spNamePrompt); boolean AllNumeric = Statics.isEntirelyNumeric(NewInput); VisitorOnDisplay = false; if (NewInput.isEmpty() || (!AllNumeric && NewInput.length()<3)) { // Clear the prompt. User instructions will appear as hint if the box is empty tvPrompt.setBackgroundColor(GLOBALS.PromptColour); tvPrompt.setText(""); ClearMatchList(); hideVisPanel(); return; } // If it's entirely numeric, check for club number // If it's text, search by name cClubMemberDetails Match; int MatchType = GLOBALS.mtNoMatch; if (AllNumeric) { int NumberInput = Integer.parseInt(NewInput); // Check for club number or EBU number, first in members list then in EBU list if (HaveNamesFile) { // Search names file by club number try { Match = MembershipList.findByNumber(NumberInput); // If we find a match on number, don't search any further MatchingPlayers.add(Match); MatchType = GLOBALS.mtPerfectMatch; } catch (Exception ex) { } } // Not found in club members list. How about EBU list? if (MatchingPlayers.size() < 1 && HaveEBUList) { try { Match = EBUList.findByNumber(NumberInput); // If we find a match on number, don't search any further MatchingPlayers.add(Match); MatchType = GLOBALS.mtPerfectMatch; } catch (Exception ex) { } } } else { // Not numeric. Search the list(s)for name matches. There could be several partials if (HaveNamesFile) { MatchType = MatchingPlayers.findMatchesIn(MembershipList, NewInput); } if (MatchType == GLOBALS.mtNoMatch && HaveEBUList) MatchType = MatchingPlayers.findMatchesIn(EBUList, NewInput); } switch (MatchType){ case GLOBALS.mtPerfectMatch: { ChoosePlayer(Match = MatchingPlayers.get(0)); } break; case GLOBALS.mtAmbiguousMatch: { // Populate our ListView with possible matches String[] MatchList = MatchingPlayers.toStringArray(); int nMatches = MatchingPlayers.size(); ArrayList<HashMap<String, String>> nList = new ArrayList<HashMap<String, String>>(); for (int n=0; n<nMatches; ++n) { HashMap<String, String> nLine = new HashMap<String, String>(); nLine.put(namesListAdapter.NameColumn, MatchList[n]); nList.add(nLine); } namesListAdapter adapter = new namesListAdapter(getApplicationContext(), this, nList); lvMatches.setAdapter(adapter); // ArrayAdapter<String> lvAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, MatchList); // String Prompt = (nMatches == 1) ? getString(R.string.OnePlayerMatch) : Integer.toString(nMatches) + " " + getString(R.string.MultiplePlayerMatches); tvPrompt.setText(Prompt); tvPrompt.setBackgroundColor(GLOBALS.ExactMatchColour); // lvMatches.setAdapter(lvAdapter); Statics.enableButton(OKButton, false); } break; default: { // No matches tvPrompt.setText(getString(R.string.NoPlayerMatches)); tvPrompt.setBackgroundColor(GLOBALS.NoMatchColour); ClearMatchList(); // It could be an explicitly typed name. Note that ClearMatchList() will // have disabled the OK button if (!NewInput.isEmpty() && !AllNumeric) { ChosenPlayer = new cClubMemberDetails(NewInput); Statics.enableButton(OKButton, true); tvPrompt.setText(getString(R.string.NoPlayerMatches)); String Prompt=getString(R.string.If) + " \"" + NewInput + "\" " + getString(R.string.isVisitor); setVisPanel(Prompt); VisitorOnDisplay = true; } } } } private String ChoosePlayer(cClubMemberDetails TheOne) { ChosenPlayer = TheOne; String EBUNo = ChosenPlayer.getEBUNumber().trim(); String EBUString; String DetailsString; if (EBUNo.isEmpty()) EBUString = ""; else EBUString = " (" + EBUNo + ")"; if (ChosenPlayer.getClubNumber() > 0) { // We have found a matching club member DetailsString = Integer.toString(ChosenPlayer.getClubNumber()) + ": " + ChosenPlayer.getMemberName() + EBUString; } else { // Matching non-member DetailsString = ChosenPlayer.getMemberName()+ EBUString; } final TextView tvPrompt = (TextView)findViewById(R.id.spNamePrompt); Statics.setTextViewHTML(getApplicationContext(), tvPrompt, DetailsString); // tvPrompt.setText(DetailsString); tvPrompt.setBackgroundColor(GLOBALS.ExactMatchColour); VisitorOnDisplay = false; Statics.enableButton(OKButton, true); return ChosenPlayer.getMemberName(); } public void ClearClicked(View callingView) { final TextView tvInput = (TextView)findViewById(R.id.spUserInput); tvInput.setText(""); ClearMatchList(); } public void PromptClicked(View callingView) { // if (VisitorOnDisplay) OKClicked(callingView); } public void OKClicked(View callingView) { if (ChosenPlayer != null) { Statics.log(getApplicationContext(), "OK clicked. Returning player details " + ChosenPlayer.toString()); Intent results = new Intent(); results.putExtra(GLOBALS.ParamPlayerID, ChosenPlayer.getClubNumber()); results.putExtra(GLOBALS.ParamPlayerName, ChosenPlayer.getMemberName()); results.putExtra(GLOBALS.ParamPlayerEBUNo, ChosenPlayer.getEBUNumber()); setResult(GLOBALS.RetValNormal, results); // STAW Statics.log(getApplicationContext(), "Finish"); finish(); } } @Override public void onBackPressed() { setResult(GLOBALS.RetValGoBack); // STAW Statics.log(getApplicationContext(), "Back was pressed"); finish(); } }