The STK menu activity cannot treat subsequent user input

The boolean field 'mAcceptUsersInput' is mistakenly left in some
use-cases after it is changed to false. That causes an issue that user
cannot do anything on the STK main menu.

The boolean field is changed to false when user clicks one of the menu
items on the STK main menu. It is changed back to true in onResume()
when screen transition happens. However, we cannot say that screen
transition always happens after selecting the item on the main menu.

That is why we have to introduce one more trigger to reset the value of
'mAcceptUsersInput' when the current session is ended.

Bug: 31288411
Change-Id: I15ac01269a474e1dbc04ad250762066dfdb70f1e
diff --git a/Android.mk b/Android.mk
index 79a84e3..a9e2d1d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -6,6 +6,7 @@
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_JAVA_LIBRARIES := telephony-common
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 LOCAL_PACKAGE_NAME := Stk
diff --git a/src/com/android/stk/StkAppService.java b/src/com/android/stk/StkAppService.java
index ea30d80..96d596e 100644
--- a/src/com/android/stk/StkAppService.java
+++ b/src/com/android/stk/StkAppService.java
@@ -45,6 +45,7 @@
 import android.os.SystemProperties;
 import android.os.Vibrator;
 import android.provider.Settings;
+import android.support.v4.content.LocalBroadcastManager;
 import android.telephony.CarrierConfigManager;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
@@ -249,6 +250,8 @@
 
     private static final String LOG_TAG = new Object(){}.getClass().getEnclosingClass().getName();
 
+    static final String SESSION_ENDED = "session_ended";
+
     // Inner class used for queuing telephony messages (proactive commands,
     // session end) while the service is busy processing a previous message.
     private class DelayedCmd {
@@ -438,6 +441,14 @@
         return false;
     }
 
+    boolean isMainMenuAvailable(int slotId) {
+        if (slotId >= 0 && slotId < mSimCount) {
+            // The main menu can handle the next user operation if the previous session finished.
+            return (mStkContext[slotId].lastSelectedItem == null) ? true : false;
+        }
+        return false;
+    }
+
     /*
      * Package api used by StkMenuActivity to get its Menu parameter.
      */
@@ -836,6 +847,12 @@
         if (StkMenuActivity.STATE_SECONDARY == mStkContext[slotId].mMenuState) {
             launchMenuActivity(null, slotId);
         }
+
+        // Send a local broadcast as a notice that this service handled the session end event.
+        Intent intent = new Intent(SESSION_ENDED);
+        intent.putExtra(SLOT_ID, slotId);
+        LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
+
         if (mStkContext[slotId].mCmdsQ.size() != 0) {
             callDelayedMsg(slotId);
         } else {
diff --git a/src/com/android/stk/StkMenuActivity.java b/src/com/android/stk/StkMenuActivity.java
old mode 100755
new mode 100644
index 04e5ed0..8dab6fd
--- a/src/com/android/stk/StkMenuActivity.java
+++ b/src/com/android/stk/StkMenuActivity.java
@@ -19,11 +19,14 @@
 import android.app.ListActivity;
 import android.app.ActionBar;
 import android.app.Activity;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
+import android.support.v4.content.LocalBroadcastManager;
 import android.telephony.SubscriptionManager;
 import android.view.ContextMenu;
 import android.view.ContextMenu.ContextMenuInfo;
@@ -85,7 +88,6 @@
             switch(msg.what) {
             case MSG_ID_TIMEOUT:
                 CatLog.d(LOG_TAG, "MSG_ID_TIMEOUT mState: " + mState);
-                mAcceptUsersInput = false;
                 if (mState == STATE_SECONDARY) {
                     appService.getStkContext(mSlotId).setPendingActivityInstance(mInstance);
                 }
@@ -113,7 +115,6 @@
         mTitleIconView = (ImageView) findViewById(R.id.title_icon);
         mProgressView = (ProgressBar) findViewById(R.id.progress_bar);
         mContext = getBaseContext();
-        mAcceptUsersInput = true;
         getListView().setOnCreateContextMenuListener(this);
 
         // appService can be null if this activity is automatically recreated by the system
@@ -124,6 +125,8 @@
             return;
         }
 
+        LocalBroadcastManager.getInstance(this).registerReceiver(mLocalBroadcastReceiver,
+                new IntentFilter(StkAppService.SESSION_ENDED));
         initFromIntent(getIntent());
         if (!SubscriptionManager.isValidSlotIndex(mSlotId)) {
             finish();
@@ -153,10 +156,6 @@
         }
         cancelTimeOut();
         sendResponse(StkAppService.RES_ID_MENU_SELECTION, item.id, false);
-        mAcceptUsersInput = false;
-        mProgressView.setVisibility(View.VISIBLE);
-        mProgressView.setIndeterminate(true);
-
         invalidateOptionsMenu();
     }
 
@@ -174,7 +173,6 @@
             case STATE_SECONDARY:
                 CatLog.d(LOG_TAG, "STATE_SECONDARY");
                 cancelTimeOut();
-                mAcceptUsersInput = false;
                 appService.getStkContext(mSlotId).setPendingActivityInstance(this);
                 sendResponse(StkAppService.RES_ID_BACKWARD);
                 return true;
@@ -221,18 +219,7 @@
         }
         displayMenu();
         startTimeOut();
-        // whenever this activity is resumed after a sub activity was invoked
-        // (Browser, In call screen) switch back to main state and enable
-        // user's input;
-        if (!mAcceptUsersInput) {
-            //Remove set mState to STATE_MAIN. This is for single instance flow.
-            mAcceptUsersInput = true;
-        }
         invalidateOptionsMenu();
-
-        // make sure the progress bar is not shown.
-        mProgressView.setIndeterminate(false);
-        mProgressView.setVisibility(View.GONE);
     }
 
     @Override
@@ -310,6 +297,8 @@
                 CatLog.d(LOG_TAG, "onDestroy: null appService.");
             }
         }
+
+        LocalBroadcastManager.getInstance(this).unregisterReceiver(mLocalBroadcastReceiver);
     }
 
     @Override
@@ -341,7 +330,6 @@
         switch (item.getItemId()) {
         case StkApp.MENU_ID_END_SESSION:
             cancelTimeOut();
-            mAcceptUsersInput = false;
             // send session end response.
             sendResponse(StkAppService.RES_ID_END_SESSION);
             cancelTimeOut();
@@ -378,7 +366,6 @@
         switch (item.getItemId()) {
             case CONTEXT_MENU_HELP:
                 cancelTimeOut();
-                mAcceptUsersInput = false;
                 int position = info.position;
                 CatLog.d(this, "Position:" + position);
                 Item stkItem = getSelectedItem(position);
@@ -407,6 +394,14 @@
         mState = savedInstanceState.getInt("STATE");
         mStkMenu = savedInstanceState.getParcelable("MENU");
         mAcceptUsersInput = savedInstanceState.getBoolean("ACCEPT_USERS_INPUT");
+        if (!mAcceptUsersInput) {
+            // Check the latest information as the saved instance state can be outdated.
+            if ((mState == STATE_MAIN) && appService.isMainMenuAvailable(mSlotId)) {
+                mAcceptUsersInput = true;
+            } else {
+                showProgressBar(true);
+            }
+        }
     }
 
     private void cancelTimeOut() {
@@ -453,6 +448,16 @@
         }
     }
 
+    private void showProgressBar(boolean show) {
+        if (show) {
+            mProgressView.setIndeterminate(true);
+            mProgressView.setVisibility(View.VISIBLE);
+        } else {
+            mProgressView.setIndeterminate(false);
+            mProgressView.setVisibility(View.GONE);
+        }
+    }
+
     private void initFromIntent(Intent intent) {
 
         if (intent != null) {
@@ -490,6 +495,13 @@
     private void sendResponse(int resId, int itemId, boolean help) {
         CatLog.d(LOG_TAG, "sendResponse resID[" + resId + "] itemId[" + itemId +
             "] help[" + help + "]");
+
+        // Disallow user operation temporarily until receiving the result of the response.
+        mAcceptUsersInput = false;
+        if (resId == StkAppService.RES_ID_MENU_SELECTION) {
+            showProgressBar(true);
+        }
+
         mIsResponseSent = true;
         Bundle args = new Bundle();
         args.putInt(StkAppService.OPCODE, StkAppService.OP_RESPONSE);
@@ -500,4 +512,17 @@
         mContext.startService(new Intent(mContext, StkAppService.class)
                 .putExtras(args));
     }
+
+    private final BroadcastReceiver mLocalBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (StkAppService.SESSION_ENDED.equals(intent.getAction())) {
+                int slotId = intent.getIntExtra(StkAppService.SLOT_ID, 0);
+                if ((mState == STATE_MAIN) && (mSlotId == slotId)) {
+                    mAcceptUsersInput = true;
+                    showProgressBar(false);
+                }
+            }
+        }
+    };
 }