package com.firstbuild.androidapp.paragon; import android.app.Fragment; import android.app.FragmentManager; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGattService; import android.bluetooth.BluetoothManager; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.provider.MediaStore; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; import com.afollestad.materialdialogs.DialogAction; import com.afollestad.materialdialogs.GravityEnum; import com.afollestad.materialdialogs.MaterialDialog; import com.firstbuild.androidapp.FirstBuildApplication; import com.firstbuild.androidapp.ParagonValues; import com.firstbuild.androidapp.R; import com.firstbuild.androidapp.paragon.datamodel.BuiltInRecipeInfo; import com.firstbuild.androidapp.paragon.datamodel.BuiltInRecipeSettingsInfo; import com.firstbuild.androidapp.paragon.myrecipes.MyRecipesFragment; import com.firstbuild.androidapp.paragon.myrecipes.RecipeEditFragment; import com.firstbuild.androidapp.paragon.myrecipes.RecipeViewFragment; import com.firstbuild.androidapp.paragon.navigation.NavigationDrawerFragment; import com.firstbuild.androidapp.paragon.settings.SettingsActivity; import com.firstbuild.androidapp.productmanager.ParagonInfo; import com.firstbuild.androidapp.productmanager.ProductInfo; import com.firstbuild.androidapp.productmanager.ProductManager; import com.firstbuild.commonframework.blemanager.BleListener; import com.firstbuild.commonframework.blemanager.BleManager; import com.firstbuild.commonframework.blemanager.BleValues; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; public class ParagonMainActivity extends AppCompatActivity { public static final int INTERVAL_CHECKING_PARAGON_CONNECTION = 1000; static final int REQUEST_TAKE_PHOTO = 123; static final int REQUEST_ENABLE_BT = 1234; static final byte INITIAL_VALUE = 0x0f; private static final int WRITE_STATE_NONE = 0; private static final int WRITE_STATE_WRITING = 1; private static final int WRITE_STATE_WRITE_DONE = 2; private final float MIN_THICKNESS = 0.25f; private final float MAX_THICKNESS = 4.0f; private final int INTERVAL_GOODTOGO = 300; private final String PREF_KEY_FOOD_WARNING = "FoodWarning"; private final int MAX_WAITING_INIT_TIME = 30; public BuiltInRecipeInfo builtInRecipes = null; public BuiltInRecipeSettingsInfo selectedBuiltInRecipe = null; Toolbar toolbar; ProgressBar dialogGoodToGoBar; TextView dialogGoodToGoContent; private String TAG = ParagonMainActivity.class.getSimpleName(); // Bluetooth adapter handler private BluetoothAdapter bluetoothAdapter = null; private ParagonSteps currentStep = ParagonSteps.STEP_NONE; // Thread handler for checking the connection with Paragon Master. private Handler handlerCheckingConnection; // Thread for update UI. private Runnable runnable; private Handler handlerCheckingGoodToGo = null; // Thread for update UI. private Runnable runnableGoodToGo; // Navigation drawer. private NavigationDrawerFragment drawerFragment; private TextView toolbarText; private ImageView toolbarImage; private String currentPhotoPath; private MaterialDialog dialogWaiting; private MaterialDialog dialogOtaProcessing; private MaterialDialog dialogOtaAsk; private MaterialDialog disconnectDialog = null; private MaterialDialog dialogGoodToGo; private View dialogGoodToGoButton; private MaterialDialog dialogFoodWarning; private int checkingCountDown; private int writeDataState = WRITE_STATE_NONE; private BleListener bleListener = new BleListener() { @Override public void onScanDevices(HashMap<String, BluetoothDevice> bluetoothDevices) { super.onScanDevices(bluetoothDevices); Log.d(TAG, "onScanDevices IN"); } @Override public void onScanStateChanged(int status) { super.onScanStateChanged(status); Log.d(TAG, "[onScanStateChanged] status: " + status); if (status == BleValues.START_SCAN) { Log.d(TAG, "Scanning BLE devices"); } else { Log.d(TAG, "Stop scanning BLE devices"); } } @Override public void onConnectionStateChanged(final String address, final int status) { super.onConnectionStateChanged(address, status); final ProductInfo productInfo = ProductManager.getInstance().getCurrent(); if (address.equals(productInfo.address)) { Log.d(TAG, "[onConnectionStateChanged] address: " + address + ", status: " + status); new Thread(new Runnable() { @Override public void run() { runOnUiThread(new Runnable() { @Override public void run() { if (status == BluetoothProfile.STATE_CONNECTED) { if (disconnectDialog.isShowing()) { disconnectDialog.dismiss(); new MaterialDialog.Builder(ParagonMainActivity.this) .content("Bluetooth Reconnected") .positiveText("Ok") .cancelable(true).show(); } else { } } else if (status == BluetoothProfile.STATE_DISCONNECTED) { productInfo.disconnected(); disconnectDialog.show(); BleManager.getInstance().readCharacteristics(productInfo.bluetoothDevice, ParagonValues.CHARACTERISTIC_PROBE_CONNECTION_STATE); BleManager.getInstance().readCharacteristics(productInfo.bluetoothDevice, ParagonValues.CHARACTERISTIC_BATTERY_LEVEL); BleManager.getInstance().readCharacteristics(productInfo.bluetoothDevice, ParagonValues.CHARACTERISTIC_BURNER_STATUS); BleManager.getInstance().readCharacteristics(productInfo.bluetoothDevice, ParagonValues.CHARACTERISTIC_COOK_MODE); BleManager.getInstance().readCharacteristics(productInfo.bluetoothDevice, ParagonValues.CHARACTERISTIC_COOK_CONFIGURATION); BleManager.getInstance().readCharacteristics(productInfo.bluetoothDevice, ParagonValues.CHARACTERISTIC_CURRENT_COOK_STATE); } else { // do nothing } } }); } }).start(); } } @Override public void onServicesDiscovered(String address, List<BluetoothGattService> bleGattServices) { super.onServicesDiscovered(address, bleGattServices); Log.d(TAG, "[onServicesDiscovered] address: " + address); } @Override public void onCharacteristicRead(String address, String uuid, byte[] value, int status) { super.onCharacteristicRead(address, uuid, value, status); ProductInfo productInfo = ProductManager.getInstance().getCurrent(); if (address.equals(productInfo.address)) { Log.d(TAG, "[onCharacteristicRead] address: " + address + ", uuid: " + uuid); onReceivedData(uuid, value); } } @Override public void onCharacteristicWrite(String address, String uuid, final byte[] value, int status) { super.onCharacteristicWrite(address, uuid, value, status); ProductInfo productInfo = ProductManager.getInstance().getCurrent(); if (address.equals(productInfo.address)) { final int ret = (int) value[0]; Log.d(TAG, "[onCharacteristicWrite] uuid: " + uuid + ", value: " + String.format("%02x", value[0])); onWriteData(uuid, value); } } @Override public void onCharacteristicChanged(String address, String uuid, byte[] value) { super.onCharacteristicChanged(address, uuid, value); ProductInfo productInfo = ProductManager.getInstance().getCurrent(); if (address.equals(productInfo.address)) { Log.d(TAG, "[onCharacteristicChanged] address: " + address + ", uuid: " + uuid); onReceivedData(uuid, value); } } @Override public void onDescriptorWrite(String address, String uuid, byte[] value, int status) { super.onDescriptorWrite(address, uuid, value, status); ProductInfo productInfo = ProductManager.getInstance().getCurrent(); if (address.equals(productInfo.address)) { Log.d(TAG, "[onDescriptorWrite] address: " + address + ", uuid: " + uuid); } } }; public MaterialDialog getDialogOtaProcessing() { return dialogOtaProcessing; } private void onReceivedData(String uuid, byte[] value) { Log.d(TAG, "onReceivedData :" + uuid); if (value == null) { Log.d(TAG, "onReceivedData :value is null"); return; } ParagonInfo productInfo = (ParagonInfo)ProductManager.getInstance().getCurrent(); if (productInfo == null) { Log.d(TAG, "productInfo is null"); return; } switch (uuid.toUpperCase()) { case ParagonValues.CHARACTERISTIC_CURRENT_TEMPERATURE: Log.d(TAG, "CHARACTERISTIC_CURRENT_TEMPERATURE :" + String.format("%02x%02x", value[0], value[1])); new Thread(new Runnable() { @Override public void run() { runOnUiThread(new Runnable() { @Override public void run() { Fragment fragment = getFragmentManager().findFragmentById(R.id.frame_content); if (fragment instanceof SousvideStatusFragment) { ((SousvideStatusFragment) fragment).updateUiCurrentTemp(); } else if (fragment instanceof MultiStageStatusFragment) { ((MultiStageStatusFragment) fragment).updateUiCurrentTemp(); } else { //do nothing } } }); } }).start(); break; case ParagonValues.CHARACTERISTIC_ELAPSED_TIME: onElapsedTime(); break; case ParagonValues.CHARACTERISTIC_CURRENT_COOK_STATE: onCookState(); break; case ParagonValues.CHARACTERISTIC_CURRENT_COOK_STAGE: onCookStage(); break; case ParagonValues.CHARACTERISTIC_COOK_MODE: onCookMode(); break; case ParagonValues.CHARACTERISTIC_OTA_VERSION: Log.d(TAG, "CHARACTERISTIC_OTA_VERSION :" + String.format("%02x%02x%02x%02x%02x%02x", value[0], value[1], value[2], value[3], value[4], value[5])); if (OtaManager.getInstance().compareVersion(productInfo.getVersionMajor(), productInfo.getVersionMinor(), productInfo.getVersionBuild())) { if (dialogWaiting.isShowing()) { dialogWaiting.dismiss(); } new Thread(new Runnable() { @Override public void run() { runOnUiThread(new Runnable() { @Override public void run() { dialogOtaAsk.show(); } }); } }).start(); } else { Log.d(TAG, "No need to update"); onOtaVersionChecked(); } break; case ParagonValues.CHARACTERISTIC_OTA_COMMAND: Log.d(TAG, "CHARACTERISTIC_OTA_COMMAND :" + String.format("%02x", value[0])); OtaManager.getInstance().getResponse(value[0]); break; case ParagonValues.CHARACTERISTIC_ERROR_STATE: Log.d(TAG, "CHARACTERISTIC_ERROR_STATE :" + String.format("%02x", value[0])); break; case ParagonValues.CHARACTERISTIC_CURRENT_POWER_LEVEL: onPowerLevelChanged(); break; case ParagonValues.CHARACTERISTIC_COOK_CONFIGURATION: onCookConfigChanged(); break; } } private void onCookConfigChanged() { new Thread(new Runnable() { @Override public void run() { runOnUiThread(new Runnable() { @Override public void run() { Fragment fragment = getFragmentManager().findFragmentById(R.id.frame_content); if (fragment instanceof SousvideStatusFragment) { ((SousvideStatusFragment) fragment).updateCookConfig(); } else { //do nothing } } }); } }).start(); } private void onOtaVersionChecked() { new Thread(new Runnable() { @Override public void run() { runOnUiThread(new Runnable() { @Override public void run() { nextStep(ParagonSteps.STEP_CHECK_CURRENT_STATUS); } }); } }).start(); } private void onWriteData(String uuid, byte[] value) { switch (uuid.toUpperCase()) { case ParagonValues.CHARACTERISTIC_OTA_DATA: new Thread(new Runnable() { @Override public void run() { runOnUiThread(new Runnable() { @Override public void run() { dialogOtaProcessing.setMaxProgress(OtaManager.getInstance().getTransferCount()); dialogOtaProcessing.setProgress(OtaManager.getInstance().getTransferOffset()); } }); } }).start(); OtaManager.getInstance().responseWriteData(); break; case ParagonValues.CHARACTERISTIC_COOK_CONFIGURATION: break; } writeDataState = WRITE_STATE_WRITE_DONE; } private void onCookMode() { new Thread(new Runnable() { @Override public void run() { runOnUiThread(new Runnable() { @Override public void run() { Fragment fragment = getFragmentManager().findFragmentById(R.id.frame_content); if (fragment instanceof GetReadyFragment) { ((GetReadyFragment) fragment).onCookModeChanged(); } else if (fragment instanceof CompleteFragment) { ((CompleteFragment) fragment).onCookModeChanged(); } else { //do nothing } } }); } }).start(); } private void onPowerLevelChanged() { new Thread(new Runnable() { @Override public void run() { runOnUiThread(new Runnable() { @Override public void run() { Fragment fragment = getFragmentManager().findFragmentById(R.id.frame_content); if (fragment instanceof DirectStatusFragment) { ((DirectStatusFragment) fragment).updateUiPowerLevel(); } else { //do nothing. } } }); } }).start(); } private void onCookStage() { new Thread(new Runnable() { @Override public void run() { runOnUiThread(new Runnable() { @Override public void run() { Fragment fragment = getFragmentManager().findFragmentById(R.id.frame_content); if (fragment instanceof MultiStageStatusFragment) { ((MultiStageStatusFragment) fragment).updateCookStage(); } else if (fragment instanceof SousvideStatusFragment) { ((SousvideStatusFragment) fragment).updateCookStage(); } else { //do nothing. } } }); } }).start(); } private void onCookState() { new Thread(new Runnable() { @Override public void run() { runOnUiThread(new Runnable() { @Override public void run() { Fragment fragment = getFragmentManager().findFragmentById(R.id.frame_content); if (fragment instanceof SousvideStatusFragment) { ((SousvideStatusFragment) fragment).updateCookState(); } else if (fragment instanceof MultiStageStatusFragment) { ((MultiStageStatusFragment) fragment).updateCookState(); } else if (fragment instanceof DirectStatusFragment) { ((DirectStatusFragment) fragment).updateCookState(); } else if (fragment instanceof GetReadyFragment) { nextStep(ParagonSteps.STEP_COOK_STATUS); } else { // do nothing. } } }); } }).start(); } private void onElapsedTime() { new Thread(new Runnable() { @Override public void run() { runOnUiThread(new Runnable() { @Override public void run() { Fragment fragment = getFragmentManager().findFragmentById(R.id.frame_content); if (fragment instanceof SousvideStatusFragment) { ((SousvideStatusFragment) fragment).updateUiElapsedTime(); } else if (fragment instanceof MultiStageStatusFragment) { ((MultiStageStatusFragment) fragment).updateUiElapsedTime(); } else { // do nothing. } } }); } }).start(); } /** * @param step */ public void nextStep(ParagonSteps step) { Fragment fragment = null; ParagonInfo productInfo = (ParagonInfo)ProductManager.getInstance().getCurrent(); currentStep = step; switch (currentStep) { case STEP_OTA: if(productInfo.isReceivedVersion()){ if (OtaManager.getInstance().compareVersion(productInfo.getVersionMajor(), productInfo.getVersionMinor(), productInfo.getVersionBuild())) { if (dialogWaiting.isShowing()) { dialogWaiting.dismiss(); } dialogOtaAsk.show(); } else { Log.d(TAG, "No need to update"); nextStep(ParagonSteps.STEP_CHECK_CURRENT_STATUS); } } else{ BleManager.getInstance().readCharacteristics(productInfo.bluetoothDevice, ParagonValues.CHARACTERISTIC_OTA_VERSION); } return; case STEP_CHECK_CURRENT_STATUS: BleManager.getInstance().readCharacteristics(productInfo.bluetoothDevice, ParagonValues.CHARACTERISTIC_COOK_CONFIGURATION); BleManager.getInstance().setCharacteristicNotification(productInfo.bluetoothDevice, ParagonValues.CHARACTERISTIC_CURRENT_COOK_STAGE, true); BleManager.getInstance().setCharacteristicNotification(productInfo.bluetoothDevice, ParagonValues.CHARACTERISTIC_CURRENT_COOK_STATE, true); startCommunicateParagon(); return; case STEP_COOKING_MODE: loadRecipesFromAsset(); getFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE); fragment = new SelectModeFragment(); break; case STEP_SOUSVIDE_SETTINGS: fragment = new RecipeSettingsFragment(); break; case STEP_SOUSVIDE_GETREADY: BleManager.getInstance().readCharacteristics(productInfo.bluetoothDevice, ParagonValues.CHARACTERISTIC_CURRENT_TEMPERATURE); BleManager.getInstance().readCharacteristics(productInfo.bluetoothDevice, ParagonValues.CHARACTERISTIC_ELAPSED_TIME); BleManager.getInstance().setCharacteristicNotification(productInfo.bluetoothDevice, ParagonValues.CHARACTERISTIC_CURRENT_TEMPERATURE, true); BleManager.getInstance().setCharacteristicNotification(productInfo.bluetoothDevice, ParagonValues.CHARACTERISTIC_ELAPSED_TIME, true); fragment = new GetReadyFragment(); break; case STEP_COOK_STATUS: BleManager.getInstance().readCharacteristics(productInfo.bluetoothDevice, ParagonValues.CHARACTERISTIC_CURRENT_TEMPERATURE); BleManager.getInstance().readCharacteristics(productInfo.bluetoothDevice, ParagonValues.CHARACTERISTIC_ELAPSED_TIME); byte cookMode = productInfo.getErdCurrentCookMode(); if (cookMode == ParagonValues.CURRENT_COOK_MODE_DIRECT) { BleManager.getInstance().readCharacteristics(productInfo.bluetoothDevice, ParagonValues.CHARACTERISTIC_CURRENT_POWER_LEVEL); fragment = new DirectStatusFragment(); } else { fragment = new SousvideStatusFragment(); } //TODO: block this code until multi-stage. // if (recipeInfo.numStage() == 1) { // fragment = new SousvideStatusFragment(); // } // else { // fragment = new MultiStageStatusFragment(); // } break; case STEP_SOUSVIDE_COMPLETE: fragment = new CompleteFragment(); break; case STEP_QUICK_START: fragment = new QuickStartFragment(); break; case STEP_MY_RECIPES: fragment = new MyRecipesFragment(); break; case STEP_EDIT_RECIPES: fragment = new RecipeEditFragment(); setTitle("Edit"); break; case STEP_EDIT_STAGE: //TODO: block this until multi-stage enabled. // fragment = new StageEditFragment(); // int index = RecipeManager.getInstance().getCurrentStageIndex(); // // if (index == RecipeManager.INVALID_INDEX) { // setTitle("New Stage"); // } // else { // setTitle("Stage " + (index + 1)); // } break; case STEP_VIEW_RECIPE: fragment = new RecipeViewFragment(); break; case STEP_VIEW_STAGE: fragment = new StageViewFragment(); break; case STEP_ADD_RECIPE_MUTISTAGE: fragment = new RecipeEditFragment(); setTitle("Multi-Stage"); break; case STEP_ADD_RECIPE_SOUSVIDE: fragment = new SousVideEditFragment(); setTitle("Sous vide"); break; case STEP_ADD_SOUSVIDE_SETTING: fragment = new QuickStartFragment(); setTitle("Sous Vide Settings"); break; default: break; } if (fragment != null) { getFragmentManager(). beginTransaction(). replace(R.id.frame_content, fragment). addToBackStack(null). commit(); } else { } } /** * Set title. * * @param title String to be title */ protected void setTitle(String title) { if (title.equals("Paragon")) { toolbarText.setVisibility(View.GONE); toolbarImage.setVisibility(View.VISIBLE); } else { toolbarText.setVisibility(View.VISIBLE); toolbarImage.setVisibility(View.GONE); toolbarText.setText(title); } } private void checkParagonConnectionStatus() { Log.d(TAG, "checkParagonConnectionStatus"); ParagonInfo product = (ParagonInfo)ProductManager.getInstance().getCurrent(); Log.d(TAG, "checkParagonConnectionStatus buner :" + product.getErdBurnerStatus()); Log.d(TAG, "checkParagonConnectionStatus cookmode :" + product.getErdCurrentCookMode()); if (product.getErdBurnerStatus() != INITIAL_VALUE && product.getErdCurrentCookMode() != INITIAL_VALUE && product.getErdRecipeConfig() != null) { Log.d(TAG, "checkParagonConnectionStatus ..1"); dialogWaiting.dismiss(); if (product.getErdBurnerStatus() == ParagonValues.BURNER_STATE_START && product.getErdCurrentCookMode() != ParagonValues.CURRENT_COOK_MODE_OFF) { Log.d(TAG, "checkParagonConnectionStatus ..2"); nextStep(ParagonSteps.STEP_COOK_STATUS); } else { Log.d(TAG, "checkParagonConnectionStatus ..3"); nextStep(ParagonSteps.STEP_COOKING_MODE); } } else { Log.d(TAG, "checkParagonConnectionStatus ..4"); checkingCountDown--; if (checkingCountDown == 0) { if (dialogWaiting.isShowing()) { dialogWaiting.dismiss(); new MaterialDialog.Builder(ParagonMainActivity.this) .title("Not connected with Paragon") .content("Are you sure?") .positiveText("Try again") .negativeText("No") .callback(new MaterialDialog.ButtonCallback() { @Override public void onPositive(MaterialDialog dialog) { // startCommunicateParagon(); nextStep(ParagonSteps.STEP_CHECK_CURRENT_STATUS); } @Override public void onNegative(MaterialDialog dialog) { BleManager.getInstance().removeListener(bleListener); finish(); } @Override public void onNeutral(MaterialDialog dialog) { } }) .cancelable(false).show(); } } else { handlerCheckingConnection.postDelayed(runnable, INTERVAL_CHECKING_PARAGON_CONNECTION); } } } private void showOtaDialog() { dialogOtaProcessing = new MaterialDialog.Builder(this) .title(R.string.popup_ota_title) .content(R.string.popup_ota_content) .contentGravity(GravityEnum.CENTER) .progress(false, 100, true) .cancelable(false) .showListener(new DialogInterface.OnShowListener() { @Override public void onShow(DialogInterface dialogInterface) { } }).build(); dialogOtaProcessing.show(); } @Override protected void onCreate(Bundle savedInstanceState) { Log.d(TAG, "onCreate IN"); super.onCreate(savedInstanceState); setContentView(R.layout.activity_paragon_main); toolbar = (Toolbar) findViewById(R.id.app_bar); toolbar.setTitle(""); setSupportActionBar(toolbar); toolbarText = (TextView) toolbar.findViewById(R.id.toolbar_title_text); toolbarImage = (ImageView) toolbar.findViewById(R.id.toolbar_title_image); // Use this check to determine whether BLE is supported on the device. Then // you can selectively disable BLE-related features. if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { Log.d(TAG, "BLE is not supported - Stop activity!"); Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show(); BleManager.getInstance().removeListener(bleListener); finish(); } else { // Initializes Bluetooth adapter. final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); bluetoothAdapter = bluetoothManager.getAdapter(); // Checks if Bluetooth is supported on the device. if (bluetoothAdapter == null) { Log.d(TAG, "Bluetooth is not supported - Stop activity!"); Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show(); BleManager.getInstance().removeListener(bleListener); finish(); } else { // Do nothing } } OtaManager.getInstance().readImageFile(ParagonMainActivity.this); dialogWaiting = new MaterialDialog.Builder(ParagonMainActivity.this) .title("Please wait") .content("Communicating with Paragon...") .progress(true, 0) .cancelable(false).build(); dialogGoodToGo = new MaterialDialog.Builder(ParagonMainActivity.this) .title("") .customView(R.layout.include_countdown, true) .negativeText("Cancel") .cancelable(false) .callback(new MaterialDialog.ButtonCallback() { @Override public void onPositive(MaterialDialog dialog) { } @Override public void onNegative(MaterialDialog dialog) { handlerCheckingGoodToGo.removeCallbacks(runnableGoodToGo); Log.d(TAG, "dialogGoodToGo onNegative Pressed "); } @Override public void onNeutral(MaterialDialog dialog) { } }) .build(); View customView = dialogGoodToGo.getCustomView(); dialogGoodToGoBar = (ProgressBar) customView.findViewById(R.id.progressBar); dialogGoodToGoContent = (TextView) customView.findViewById(R.id.content); dialogGoodToGoButton = dialogGoodToGo.getActionButton(DialogAction.NEGATIVE); dialogOtaAsk = new MaterialDialog.Builder(ParagonMainActivity.this) .title("Update Available") .content("Are you sure you want to update Paragon now?") .positiveText("Yes") .negativeText("No") .callback(new MaterialDialog.ButtonCallback() { @Override public void onPositive(MaterialDialog dialog) { OtaManager.getInstance().startProcess(); showOtaDialog(); } @Override public void onNegative(MaterialDialog dialog) { dialogWaiting.show(); nextStep(ParagonSteps.STEP_CHECK_CURRENT_STATUS); } @Override public void onNeutral(MaterialDialog dialog) { } }) .cancelable(false).build(); handlerCheckingConnection = new Handler(); runnable = new Runnable() { @Override public void run() { checkParagonConnectionStatus(); } }; handlerCheckingGoodToGo = new Handler(); runnableGoodToGo = new Runnable() { @Override public void run() { checkGoodToGoLoop(); } }; disconnectDialog = new MaterialDialog.Builder(ParagonMainActivity.this) .title("Bluetooth Disconnected") .content("It will be reconnect when it's available again.\nIf press OK then go to My Products") .progress(true, 0) .cancelable(false) .positiveText("Ok") .callback(new MaterialDialog.ButtonCallback() { @Override public void onPositive(MaterialDialog dialog) { BleManager.getInstance().removeListener(bleListener); finish(); } @Override public void onNegative(MaterialDialog dialog) { } @Override public void onNeutral(MaterialDialog dialog) { } }) .build(); // Check bluetooth adapter. If the adapter is disabled, enable it boolean result = BleManager.getInstance().isBluetoothEnabled(); if (!result) { Log.d(TAG, "Bluetooth adapter is disabled. Enable bluetooth adapter."); Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); } else { Log.d(TAG, "Bluetooth adapter is already enabled. Start connect"); // startCommunicateParagon(); } currentStep = ParagonSteps.STEP_NONE; } @Override protected void onPause() { Log.d(TAG, "onPause"); super.onPause(); // Add ble event listener BleManager.getInstance().removeListener(bleListener); if (disconnectDialog.isShowing()) { Log.d(TAG, "Stop reconnect"); } handlerCheckingConnection.removeCallbacksAndMessages(null); } @Override protected void onResume() { Log.d(TAG, "onResume"); super.onResume(); // Add ble event listener BleManager.getInstance().addListener(bleListener); dialogWaiting.setContent("Communicating..."); dialogWaiting.show(); if(currentStep == ParagonSteps.STEP_NONE){ nextStep(ParagonSteps.STEP_OTA); } else{ nextStep(ParagonSteps.STEP_CHECK_CURRENT_STATUS); } } private void startCommunicateParagon() { Log.d(TAG, "startCommunicateParagon IN"); ProductInfo productInfo = ProductManager.getInstance().getCurrent(); // handlerCheckingConnection.postDelayed(runnable, INTERVAL_CHECKING_PARAGON_CONNECTION); // Get Initial values. // BleManager.getInstance().readCharacteristics(productInfo.bluetoothDevice, ParagonValues.CHARACTERISTIC_OTA_VERSION); // nextStep(ParagonSteps.STEP_CHECK_CURRENT_STATUS); // dialogWaiting.setContent("Communicating..."); // dialogWaiting.show(); checkingCountDown = MAX_WAITING_INIT_TIME; checkParagonConnectionStatus(); Log.d(TAG, "startCommunicateParagon OUT"); } @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_paragon_main, 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_settings) { Intent intent = new Intent(this, SettingsActivity.class); intent.putExtra("SelectedMenu", "MenuSettings"); startActivity(intent); return true; } else if (id == R.id.action_about) { Intent intent = new Intent(this, SettingsActivity.class); intent.putExtra("SelectedMenu", "MenuAbout"); startActivity(intent); return true; } else if (id == R.id.action_my_product) { BleManager.getInstance().removeListener(bleListener); finish(); return true; } else if (id == R.id.action_help) { String url = getResources().getString(R.string.url_manual); startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))); return true; } else if (id == R.id.action_feedback) { final Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND); emailIntent.setType("plain/text"); emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[]{"paragon@firstbuild.com"}); emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, "Feedback Paragon] "); emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, "Text"); startActivity(Intent.createChooser(emailIntent, "Send mail...")); } else if (id == android.R.id.home) { onBackPressed(); return true; } else { // do nothing. } return super.onOptionsItemSelected(item); } public void finishParagonMain() { // BleManager.getInstance().disconnect(); BleManager.getInstance().removeListener(bleListener); finish(); } /** * Call when OTA completed successfully. */ public void succeedOta() { if (dialogOtaProcessing.isShowing()) { dialogOtaProcessing.dismiss(); } else { // do nothing. } // Scan again. // BleManager.getInstance().disconnect(); BleManager.getInstance().removeListener(bleListener); finish(); } /** * CAll when OTA get failed. */ public void failedOta() { } /** * Take picture and store in file. */ public void dispatchTakePictureIntent() { Log.d(TAG, "dispatchTakePictureIntent IN"); Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); // Ensure that there's a camera activity to handle the intent if (takePictureIntent.resolveActivity(getPackageManager()) != null) { // Create the File where the photo should go File photoFile = null; try { photoFile = createImageFile(); } catch (IOException ex) { // Error occurred while creating the File Log.d(TAG, "dispatchTakePictureIntent error : " + ex); } // Continue only if the File was successfully created if (photoFile != null) { takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile)); startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO); } } } /** * Create image file for recipe. * * @return String dir + file name of image file. * @throws IOException */ private File createImageFile() throws IOException { // Create an image file name String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); String imageFileName = "JPEG_" + timeStamp + "_"; File storageDir = Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_PICTURES); File image = File.createTempFile( imageFileName, /* prefix */ ".jpg", /* suffix */ storageDir /* directory */ ); // Save a file: path for use with ACTION_VIEW intents currentPhotoPath = image.getAbsolutePath(); return image; } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_TAKE_PHOTO) { if (resultCode == RESULT_OK) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inPreferredConfig = Bitmap.Config.RGB_565; options.inSampleSize = 4; Bitmap imageBitmap = BitmapFactory.decodeFile(currentPhotoPath, options); Fragment fragment = getFragmentManager().findFragmentById(R.id.frame_content); if (fragment instanceof RecipeEditFragment) { ((RecipeEditFragment) fragment).setRecipeImage(imageBitmap, currentPhotoPath); } } } else if (requestCode == REQUEST_ENABLE_BT) { if (resultCode == -1) { // Success Log.d(TAG, "Bluetooth adapter is enabled. Start scanning."); // startCommunicateParagon(); nextStep(ParagonSteps.STEP_CHECK_CURRENT_STATUS); } else if (resultCode == 0) { Log.d(TAG, "Bluetooth adapter is still disabled"); } else { // Else } } else { } } @Override public void onBackPressed() { FragmentManager fm = getFragmentManager(); Fragment fragment = fm.findFragmentById(R.id.frame_content); if (fragment instanceof SelectModeFragment) { ((SelectModeFragment) fragment).onBackPressed(); } else if (fragment instanceof SousvideStatusFragment || fragment instanceof GetReadyFragment) { return; } else if (fm.getBackStackEntryCount() > 1) { fm.popBackStack(); } else { finishParagonMain(); } } @Override protected void onSaveInstanceState(Bundle outState) { } /** * Get recipe's title image from file. * * @param imageFileName File name of external storage. */ public void loadImageFromFile(String imageFileName) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inPreferredConfig = Bitmap.Config.RGB_565; options.inSampleSize = 4; Bitmap imageBitmap; try { imageBitmap = BitmapFactory.decodeFile(imageFileName, options); } catch (Exception e) { imageBitmap = null; } Fragment fragment = getFragmentManager().findFragmentById(R.id.frame_content); if (fragment instanceof RecipeViewFragment) { ((RecipeViewFragment) fragment).setRecipeImage(imageBitmap); } } /** * Read built-in receipes from asset file. */ public void loadRecipesFromAsset() { String json = null; try { InputStream is = getAssets().open("recipes/builtin2.JSON"); int size = is.available(); byte[] buffer = new byte[size]; is.read(buffer); is.close(); json = new String(buffer, "UTF-8"); } catch (IOException ex) { ex.printStackTrace(); } try { builtInRecipes = new BuiltInRecipeInfo("root"); builtInRecipes.child = new ArrayList<>(); buildRecipeLinks(builtInRecipes, new JSONObject(json)); } catch (JSONException e) { e.printStackTrace(); } } /** * Built recipe links * * @param parent parent of BuiltInRecipeInfo. * @param rootObject JSON object. */ public void buildRecipeLinks(BuiltInRecipeInfo parent, JSONObject rootObject) { try { JSONArray arrayJson = rootObject.getJSONArray("recipes"); for (int j = 0; j < arrayJson.length(); j++) { JSONObject childObj = arrayJson.getJSONObject(j); String name = childObj.getString("name"); Log.d(TAG, "name :"+name); if (childObj.has("recipes")) { BuiltInRecipeInfo foodsInfo = new BuiltInRecipeInfo(name); foodsInfo.child = new ArrayList<>(); parent.child.add(foodsInfo); foodsInfo.parent = parent; buildRecipeLinks(foodsInfo, childObj); } else { BuiltInRecipeSettingsInfo settingsInfo = new BuiltInRecipeSettingsInfo(name); settingsInfo.id = childObj.getInt("id"); settingsInfo.parent = parent; JSONArray arrayDoneness = childObj.getJSONArray("doneness"); for (int i = 0; i < arrayDoneness.length(); i++) { settingsInfo.doneness.add(arrayDoneness.getString(i)); } if (childObj.has("thickness")) { JSONArray arrayThickness = childObj.getJSONArray("thickness"); for (int i = 0; i < arrayThickness.length(); i++) { settingsInfo.thickness.add((float) arrayThickness.getDouble(i)); } } else { // do nothing. } JSONArray arrayTemp = childObj.getJSONArray("temp"); JSONArray arrayTimeMin = childObj.getJSONArray("time min"); JSONArray arrayTimeMax = childObj.getJSONArray("time max"); for (int i = 0; i < arrayTemp.length(); i++) { int temp = arrayTemp.getInt(i); float timeMin = (float) arrayTimeMin.getDouble(i); float timeMax = (float) arrayTimeMax.getDouble(i); settingsInfo.addRecipeSetting(temp, timeMin, timeMax); } parent.child.add(settingsInfo); } } } catch (JSONException e) { e.printStackTrace(); } } /** * Initialize for check good to go for cooking. */ public void checkGoodToGo() { Log.d(TAG, "checkGoodToGo"); dialogGoodToGoBar.setProgress(100); dialogGoodToGoButton.setVisibility(View.VISIBLE); checkGoodToGoLoop(); writeDataState = WRITE_STATE_NONE; } /** * This loop used for checking state if good to go to cooking state. * This call every INTERVAL_GOODTOGO time */ private void checkGoodToGoLoop() { Log.d(TAG, "checkGoodToGoLoop IN"); ParagonInfo productInfo = (ParagonInfo)ProductManager.getInstance().getCurrent(); boolean isReady = false; boolean isAllRight = false; if (productInfo.getErdBurnerStatus() == ParagonValues.BURNER_STATE_START) { dialogGoodToGo.setTitle("Press Stop on Paragon"); dialogGoodToGoContent.setText("The Paragon is currently cooking. Please press Stop on the Paragon."); isReady = false; } else if (productInfo.isProbeConnected() == false) { dialogGoodToGo.setTitle("Connect Probe"); dialogGoodToGoContent.setText("Probe is not connected. Please connect the temperature probe by holding the button on the side of the probe FOR 3 SECONDS."); isReady = false; } else if (productInfo.getErdCurrentCookMode() != ParagonValues.CURRENT_COOK_MODE_RAPID && productInfo.getErdCurrentCookMode() != ParagonValues.CURRENT_COOK_MODE_MULTISTEP) { dialogGoodToGo.setTitle("Select Rapid Precise"); dialogGoodToGoContent.setText("Please press Rapid Precise on the cooktop."); isReady = false; } else { isReady = true; } if (isReady) { // Every ERD is good to go. then now app send recipe configuration data. Log.d(TAG, "checkGoodToGoLoop isReady"); dialogGoodToGo.setTitle("Sending configuration..."); dialogGoodToGoContent.setText(""); dialogGoodToGoButton.setVisibility(View.GONE); switch (writeDataState) { case WRITE_STATE_NONE: Log.d(TAG, "checkGoodToGoLoop isReady Write None"); Fragment fragment = getFragmentManager().findFragmentById(R.id.frame_content); if (fragment instanceof QuickStartFragment) { ((QuickStartFragment) fragment).sendRecipeConfig(); } else if (fragment instanceof RecipeSettingsFragment) { ((RecipeSettingsFragment) fragment).sendRecipeConfig(); } else { } writeDataState = WRITE_STATE_WRITING; break; case WRITE_STATE_WRITING: Log.d(TAG, "checkGoodToGoLoop isReady Write Writing"); break; case WRITE_STATE_WRITE_DONE: Log.d(TAG, "checkGoodToGoLoop isReady Write Done"); isAllRight = true; break; } } int progress = dialogGoodToGoBar.getProgress(); if (isAllRight) { // Everything is all right and good to go for cooking state. if (dialogGoodToGo.isShowing()) { dialogGoodToGo.dismiss(); } Fragment fragment = getFragmentManager().findFragmentById(R.id.frame_content); if (fragment instanceof QuickStartFragment) { ((QuickStartFragment) fragment).goodToGo(); } else if (fragment instanceof RecipeSettingsFragment) { ((RecipeSettingsFragment) fragment).goodToGo(); } else { } // remove runnable handler. handlerCheckingGoodToGo.removeCallbacks(runnableGoodToGo); return; } // decrease progress bar. if (progress <= 0) { // if timer has expiered then dismiss popup. if (dialogGoodToGo.isShowing()) { dialogGoodToGo.dismiss(); } // remove runnable handler. handlerCheckingGoodToGo.removeCallbacks(runnableGoodToGo); } else { progress--; dialogGoodToGoBar.setProgress(progress); if (!dialogGoodToGo.isShowing()) { dialogGoodToGo.show(); } handlerCheckingGoodToGo.postDelayed(runnableGoodToGo, INTERVAL_GOODTOGO); } } /** * If the temperature lower then 140 F, show popup the warning of foodborne illness. * * @return if alresay dissmiss then false. */ public boolean isShowFoodWarning() { SharedPreferences settings = FirstBuildApplication.getInstance().getContext().getSharedPreferences( ProductManager.PREFS_NAME, Context.MODE_PRIVATE); boolean isShowFoodWarning = settings.getBoolean(PREF_KEY_FOOD_WARNING, false); return isShowFoodWarning; } /** * If press dismiss button on popup then save this in SharedPreference. */ public void saveShowFoodWarning() { SharedPreferences settings = FirstBuildApplication.getInstance().getContext().getSharedPreferences( ProductManager.PREFS_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor editor = settings.edit(); editor.putBoolean(PREF_KEY_FOOD_WARNING, true); editor.commit(); } /** * All step for screen transition. */ public enum ParagonSteps { STEP_NONE, STEP_OTA, STEP_CHECK_CURRENT_STATUS, STEP_COOKING_MODE, STEP_SOUSVIDE_SETTINGS, STEP_SOUSVIDE_GETREADY, STEP_COOK_STATUS, STEP_SOUSVIDE_COMPLETE, STEP_QUICK_START, STEP_MY_RECIPES, STEP_EDIT_RECIPES, STEP_EDIT_STAGE, STEP_VIEW_RECIPE, STEP_VIEW_STAGE, STEP_ADD_RECIPE_MUTISTAGE, STEP_ADD_RECIPE_SOUSVIDE, STEP_ADD_SOUSVIDE_SETTING } }