content: package com.adafruit.bluefruit.le.connect.app;
  
  import android.app.AlertDialog;
  import android.bluetooth.BluetoothGattCharacteristic;
  import android.content.Context;
  import android.content.Intent;
  import android.content.res.Configuration;
  import android.os.Bundle;
  import android.os.Handler;
  import android.support.v4.app.Fragment;
  import android.support.v4.app.FragmentManager;
  import android.support.v4.app.FragmentPagerAdapter;
  import android.support.v4.view.ViewPager;
  import android.util.Log;
  import android.view.Menu;
  import android.view.MenuItem;
  import android.view.ViewGroup;
  import android.view.inputmethod.InputMethodManager;
  import android.widget.Toast;
  
  import com.adafruit.bluefruit.le.connect.R;
  import com.adafruit.bluefruit.le.connect.app.settings.ConnectedSettingsActivity;
  import com.adafruit.bluefruit.le.connect.ble.BleManager;
  import com.adafruit.bluefruit.le.connect.ui.tabs.SlidingTabLayout;
  
  import java.nio.charset.Charset;
  
  public class BeaconActivity extends UartInterfaceActivity implements IBeaconFragment.OnFragmentInteractionListener, URIBeaconFragment.OnFragmentInteractionListener {
      // Log
      private final static String TAG = BeaconActivity.class.getSimpleName();
  
      // Constants
      private final static int kOperation_BeaconNoOperation = -1;
      private final static int kOperation_BeaconDisable = 0;
      private final static int kOperation_iBeaconEnable = 1;
      private final static int kOperation_UriBeaconEnable = 2;
      private final static int kOperation_BeaconTestMode = 3;
  
      private final static int kTab_iBeacon = 0;
      private final static int kTab_uriBeacon = 1;
  
      private final static int kSendTimeout = 1000;   // in milliseconds
  
      // Activity request codes (used for onActivityResult)
      private static final int kActivityRequestCode_ConnectedSettingsActivity = 0;
  
      // Data
      private BeaconPagerAdapter mAdapterViewPager;
      private int mCurrentTab;
      private int mCurrentOperation;
      private AlertDialog mDialog;
      private Handler mCommandTimeoutHandler;
  
      private DataFragment mRetainedDataFragment;
  
      private Runnable commandTimeoutRunnable = new Runnable() {
          @Override
          public void run() {
              // Alert
              AlertDialog.Builder builder = new AlertDialog.Builder(BeaconActivity.this);
              builder.setTitle(android.R.string.dialog_alert_title).setMessage(R.string.beacon_aterror).setPositiveButton(android.R.string.ok, null);
  
              if (mDialog != null) {
                  mDialog.dismiss();
              }
  
              mDialog = builder.create();
              mDialog.show();
  
              mCommandTimeoutHandler = null;
          }
      };
  
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_beacon);
  
          // Ble
          mBleManager = BleManager.getInstance(this);
          restoreRetainedDataFragment();
  
          // Get params
          Intent intent = getIntent();
          int rssi = intent.getIntExtra("rssi", 0);
  
          // UI
          ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
          mAdapterViewPager = new BeaconPagerAdapter(getSupportFragmentManager(), getApplicationContext(), rssi);
          viewPager.setAdapter(mAdapterViewPager);
  
          SlidingTabLayout slidingTabLayout = (SlidingTabLayout) findViewById(R.id.sliding_tabs);
          slidingTabLayout.setDistributeEvenly(true);
          slidingTabLayout.setViewPager(viewPager);
  
          // Attach the page change listener inside the activity
          slidingTabLayout.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
  
              @Override
              public void onPageSelected(int position) {
                  mCurrentTab = position;
                  dismissKeyboard();
              }
  
              @Override
              public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
  
              }
  
              @Override
              public void onPageScrollStateChanged(int state) {
              }
          });
  
          // Start services
          onServicesDiscovered();
      }
  
      @Override
      public void onResume() {
          super.onResume();
  
          // Setup listeners
          mBleManager.setBleListener(this);
      }
  
      @Override
      public void onDestroy() {
          if (mDialog != null) {
              mDialog.dismiss();
              mDialog = null;
          }
  
          // Retain data
          saveRetainedDataFragment();
  
          // Cancel the timer if was set
          if (mCommandTimeoutHandler != null) {
              mCommandTimeoutHandler.removeCallbacksAndMessages(null);
              mCommandTimeoutHandler = null;
          }
  
          super.onDestroy();
      }
  
      @Override
      public void onBackPressed() {
          boolean result = false;
  
          if (mCurrentTab == kTab_iBeacon) {
              // if pressed back check if we need to dimiss the custom keyboard used on iBeacon Activity
              Fragment currentFragment = mAdapterViewPager.getCurrentFragment();
              result = ((IBeaconFragment) currentFragment).onBackPressed();
          }
  
          if (!result) {
              super.onBackPressed();
          }
      }
  
  
      @Override
      public void onConfigurationChanged(Configuration newConfig) {
          super.onConfigurationChanged(newConfig);
  
          dismissKeyboard();
      }
  
  
      @Override
      public boolean onCreateOptionsMenu(Menu menu) {
          // Inflate the menu; this adds items to the action bar if it is present.
          getMenuInflater().inflate(R.menu.menu_beacon, menu);
          return true;
      }
  
      @Override
      public boolean onOptionsItemSelected(MenuItem item) {
          // Handle action bar item clicks here. The action bar will
          // automatically handle clicks on the Home/Up button, so long
          // as you specify a parent activity in AndroidManifest.xml.
          int id = item.getItemId();
  
          //noinspection SimplifiableIfStatement
          if (id == R.id.action_help) {
              startHelp();
              return true;
          } else if (id == R.id.action_connected_settings) {
              startConnectedSettings();
              return true;
          } else if (id == R.id.action_refreshcache) {
              if (mBleManager != null) {
                  mBleManager.refreshDeviceCache();
              }
          }
  
  
          return super.onOptionsItemSelected(item);
      }
  
      private void startConnectedSettings() {
          // Launch connected settings activity
          Intent intent = new Intent(this, ConnectedSettingsActivity.class);
          startActivityForResult(intent, kActivityRequestCode_ConnectedSettingsActivity);
      }
  
      private void startHelp() {
          // Launch app help activity
          Intent intent = new Intent(this, CommonHelpActivity.class);
          intent.putExtra("title", getString(R.string.beacon_help_title));
          intent.putExtra("help", "beacon_help.html");
          startActivity(intent);
      }
  
      @Override
      protected void onActivityResult(final int requestCode, final int resultCode, final Intent intent) {
          if (resultCode == RESULT_OK && requestCode == kActivityRequestCode_ConnectedSettingsActivity) {
              finish();
          }
      }
  
      private void testATParser() {
          mCurrentOperation = kOperation_BeaconTestMode;
          String uartCommand = "AT\r\n";
          Log.d(TAG, "send command: " + uartCommand);
  
          mCommandTimeoutHandler = new Handler();
          mCommandTimeoutHandler.postDelayed(new Runnable() {
              @Override
              public void run() {
                  Log.d(TAG, "device not responding to commands. lets change the mode and try again...");
                  String uartCommand = "+++\r\nAT\r\n";
                  Log.d(TAG, "send command: " + uartCommand);
                  mCommandTimeoutHandler = new Handler();
                  mCommandTimeoutHandler.postDelayed(commandTimeoutRunnable, kSendTimeout);
                  sendData(uartCommand);
  
              }
          }, kSendTimeout);
  
          sendData(uartCommand);
      }
  
      private void dismissKeyboard() {
  
          try {
              InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
  
              if (imm != null && imm.isAcceptingText()) { // verify if the soft keyboard is open
                  try {
                      imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
                  } catch (NullPointerException ignored) {
                  }
              }
          } catch (Exception ignored) {
          }
      }
  
      // region BleManagerListener
      /*
      @Override
      public void onConnected() {
  
      }
  
      @Override
      public void onConnecting() {
  
      }
  */
      @Override
      public void onDisconnected() {
          super.onDisconnected();
          Log.d(TAG, "Disconnected. Back to previous activity");
          setResult(-1);      // Unexpected Disconnect
          finish();
      }
  
      @Override
      public void onServicesDiscovered() {
          super.onServicesDiscovered();
          enableRxNotifications();
  
          // Test AT parser
          testATParser();
      }
  
      @Override
      public void onDataAvailable(BluetoothGattCharacteristic characteristic) {
          super.onDataAvailable(characteristic);
          // UART RX
          if (characteristic.getService().getUuid().toString().equalsIgnoreCase(UUID_SERVICE)) {
              if (characteristic.getUuid().toString().equalsIgnoreCase(UUID_RX)) {
  
                  // Cancel the timer if was set
                  if (mCommandTimeoutHandler != null) {
                      mCommandTimeoutHandler.removeCallbacksAndMessages(null);
                      mCommandTimeoutHandler = null;
                  }
  
                  final String data = new String(characteristic.getValue(), Charset.forName("UTF-8")).trim();
                  Log.d(TAG, "received: " + data);
  
                  String message = null;
                  if (data.equalsIgnoreCase("OK")) {      // All good!
  
                      switch (mCurrentOperation) {
                          case kOperation_BeaconDisable:
                              break;
                          case kOperation_iBeaconEnable:
                              onBeaconEnabled();
                              break;
                          case kOperation_UriBeaconEnable:
                              onBeaconEnabled();
                              break;
                          default:
                              break;
                      }
                  } else  // Error received
                  {
                      mCurrentOperation = kOperation_BeaconNoOperation;
                      message = data;
                  }
  
                  if (message != null) {
                      final String finalMessage = message;
                      // Update UI
                      runOnUiThread(new Runnable() {
                          @Override
                          public void run() {
                              if (mDialog != null) {
                                  mDialog.dismiss();
                              }
  
                              // Alert
                              AlertDialog.Builder builder = new AlertDialog.Builder(BeaconActivity.this);
                              builder.setMessage(finalMessage).setPositiveButton(android.R.string.ok, null);
                              mDialog = builder.create();
                              mDialog.show();
  
                          }
                      });
                  }
              }
          }
      }
  
      /*
          @Override
          public void onDataAvailable(BluetoothGattDescriptor descriptor) {
  
          }
      */
      @Override
      public void onReadRemoteRssi(final int rssi) {
          super.onReadRemoteRssi(rssi);
          if (mCurrentTab == kTab_iBeacon) {
              final Fragment currentFragment = mAdapterViewPager.getCurrentFragment();
              runOnUiThread(new Runnable() {
                  @Override
                  public void run() {
                      ((IBeaconFragment) currentFragment).setRssi(rssi);
                  }
              });
          }
      }
      // endregion
  
      private void onBeaconEnabled() {
          // Reset device
          String uartCommand = "ATZ\r\n";
          Log.d(TAG, "send command: " + uartCommand);
          sendData(uartCommand);
  
          runOnUiThread(new Runnable() {
              @Override
              public void run() {
                  Toast.makeText(BeaconActivity.this, R.string.beacon_beacon_enabled, Toast.LENGTH_LONG).show();
                  finish();
              }
          });
      }
  
      public static class BeaconPagerAdapter extends FragmentPagerAdapter {
          private static int kNumItems = 2;
  
          private Context mContext;
          private int mRssi;
          private Fragment mCurrentFragment;
  
          BeaconPagerAdapter(FragmentManager fragmentManager, Context context, int rssi) {
              super(fragmentManager);
  
              mContext = context;
              mRssi = rssi;
          }
  
          Fragment getCurrentFragment() {
              return mCurrentFragment;
          }
  
          @Override
          public void setPrimaryItem(ViewGroup container, int position, Object object) {
              if (getCurrentFragment() != object) {
                  mCurrentFragment = ((Fragment) object);
              }
              super.setPrimaryItem(container, position, object);
          }
  
          // Returns total number of pages
          @Override
          public int getCount() {
              return kNumItems;
          }
  
          // Returns the fragment to display for that page
          @Override
          public Fragment getItem(int position) {
              switch (position) {
                  case 0: // Fragment # 0
                      return IBeaconFragment.newInstance(mRssi);
                  case 1: // Fragment # 1
                      return URIBeaconFragment.newInstance();
                  default:
                      return null;
              }
          }
  
          // Returns the page title for the top indicator
          @Override
          public CharSequence getPageTitle(int position) {
              return mContext.getResources().getStringArray(R.array.beacon_page_titles)[position];
          }
      }
  
      @Override
      public void onEnable(String vendor, String uuid, String major, String minor, String rssi) {
          mCurrentOperation = kOperation_iBeaconEnable;
  
          // iBeacon Enable
          String uartCommand = String.format("AT+BLEBEACON=%s,%s,%s,%s,%s\r\n", vendor, uuid, major, minor, rssi);
          Log.d(TAG, "send command: " + uartCommand);
          mCommandTimeoutHandler = new Handler();
          mCommandTimeoutHandler.postDelayed(commandTimeoutRunnable, kSendTimeout);
          sendData(uartCommand);
  
          // Alert
          if (mDialog != null) {
              mDialog.dismiss();
          }
          AlertDialog.Builder builder = new AlertDialog.Builder(BeaconActivity.this);
          builder.setMessage(R.string.beacon_beacon_enabling);//.setPositiveButton(android.R.string.ok, null);
          mDialog = builder.create();
          mDialog.show();
      }
  
      @Override
      public void onEnable(String encodedUri) {
          mCurrentOperation = kOperation_UriBeaconEnable;
  
          // URIBeacon enableNotification
          String uartCommand = String.format("AT+BLEURIBEACON=%s\r\n", encodedUri);
          Log.d(TAG, "send command: " + uartCommand);
          mCommandTimeoutHandler = new Handler();
          mCommandTimeoutHandler.postDelayed(commandTimeoutRunnable, kSendTimeout);
          sendData(uartCommand);
  
          // Alert
          if (mDialog != null) {
              mDialog.dismiss();
          }
          AlertDialog.Builder builder = new AlertDialog.Builder(BeaconActivity.this);
          builder.setMessage(R.string.beacon_beacon_enabling);//.setPositiveButton(android.R.string.ok, null);
          mDialog = builder.create();
          mDialog.show();
  
      }
  
      @Override
      public void onDisable() {
          mCurrentOperation = kOperation_BeaconDisable;
  
          // Disable
          String uartCommand = "+++\r\nAT+FACTORYRESET\r\n+++\r\n";
          Log.d(TAG, "send command: " + uartCommand);
          sendData(uartCommand);
      }
  
      // region DataFragment
      public static class DataFragment extends android.app.Fragment {
          private Handler mCommandTimeoutHandler;
          private int mCurrentTab;
          private int mCurrentOperation = kOperation_BeaconNoOperation;
  
          @Override
          public void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setRetainInstance(true);
          }
      }
  
      private void restoreRetainedDataFragment() {
          // find the retained fragment
          android.app.FragmentManager fm = getFragmentManager();
          mRetainedDataFragment = (DataFragment) fm.findFragmentByTag(TAG);
  
          if (mRetainedDataFragment == null) {
              // Create
              mRetainedDataFragment = new DataFragment();
              fm.beginTransaction().add(mRetainedDataFragment, TAG).commit();
  
  
          } else {
              // Restore status
              mCommandTimeoutHandler = mRetainedDataFragment.mCommandTimeoutHandler;
              mCurrentTab = mRetainedDataFragment.mCurrentTab;
              mCurrentOperation = mRetainedDataFragment.mCurrentOperation;
  
          }
      }
  
      private void saveRetainedDataFragment() {
          mRetainedDataFragment.mCommandTimeoutHandler = mCommandTimeoutHandler;
          mRetainedDataFragment.mCurrentTab = mCurrentTab;
          mRetainedDataFragment.mCurrentOperation = mCurrentOperation;
      }
      // endregion
  }
  