diff --git a/android/app/src/main/java/com/rusefi/app/DfuUpload.java b/android/app/src/main/java/com/rusefi/app/DfuUpload.java index 87a28bb4f0..b2e4cf5a56 100644 --- a/android/app/src/main/java/com/rusefi/app/DfuUpload.java +++ b/android/app/src/main/java/com/rusefi/app/DfuUpload.java @@ -29,12 +29,12 @@ public class DfuUpload { localDfuImageFileName = localFolder + File.separator + DFU_FILE_NAME; } - void fileOperation(final TextView mResultView) { + void downloadFileIfNotPresent(final TextView mResultView) { if (new File(this.localFullFile).exists()) { - mResultView.append(this.BUNDLE_FILE + " found!\n"); + mResultView.append(BUNDLE_FILE + " found!\n"); uncompressFile(this.localFullFile, this.localFolder, this.localDfuImageFileName, mResultView); } else { - mResultView.append(this.BUNDLE_FILE + " not found!\n"); + mResultView.append(BUNDLE_FILE + " not found!\n"); new Thread(new Runnable() { @Override @@ -43,30 +43,15 @@ public class DfuUpload { ConnectionAndMeta c = new ConnectionAndMeta(BUNDLE_FILE).invoke(ConnectionAndMeta.BASE_URL_LATEST); ConnectionAndMeta.downloadFile(localFullFile, c, new ConnectionAndMeta.DownloadProgressListener() { @Override - public void onPercentage(final int currentProgress) { - mResultView.post(new Runnable() { - @Override - public void run() { - mResultView.append("Downloading " + currentProgress + "\n"); - } - }); - } - }); - mResultView.post(new Runnable() { - @Override - public void run() { - mResultView.append("Downloaded! " + "\n"); + public void onPercentage(final int currentPercentage) { + mResultView.post(() -> mResultView.append("Downloaded " + currentPercentage + "%\n")); } }); + mResultView.post(() -> mResultView.append("Downloaded! " + "\n")); uncompressFile(localFullFile, localFolder, localDfuImageFileName, mResultView); } catch (IOException | KeyManagementException | NoSuchAlgorithmException e) { - mResultView.post(new Runnable() { - @Override - public void run() { - mResultView.append("Error downloading " + e + "\n"); - } - }); + mResultView.post(() -> mResultView.append("Error downloading " + e + "\n")); } } }).start(); diff --git a/android/app/src/main/java/com/rusefi/app/PermissionGrantedAction.java b/android/app/src/main/java/com/rusefi/app/PermissionGrantedAction.java new file mode 100644 index 0000000000..0b6212eb8c --- /dev/null +++ b/android/app/src/main/java/com/rusefi/app/PermissionGrantedAction.java @@ -0,0 +1,6 @@ +package com.rusefi.app; + +public enum PermissionGrantedAction { + DFU, + DASHBOARD, +} diff --git a/android/app/src/main/java/com/rusefi/app/rusEFI.java b/android/app/src/main/java/com/rusefi/app/rusEFI.java index 523cb43311..e5c5073eb8 100644 --- a/android/app/src/main/java/com/rusefi/app/rusEFI.java +++ b/android/app/src/main/java/com/rusefi/app/rusEFI.java @@ -60,6 +60,7 @@ import com.rusefi.proxy.NetworkConnector; import com.rusefi.proxy.NetworkConnectorContext; import com.rusefi.ui.StatusConsumer; +import java.io.IOException; import java.util.Date; public class rusEFI extends Activity { @@ -71,20 +72,20 @@ public class rusEFI extends Activity { // // protected static final int DFU_DETACH_TIMEOUT = 1000; - private static final String VERSION = "rusEFI app v0.0000008\n"; + private static final String VERSION = "rusEFI app v0.20220524\n"; /* UI elements */ private TextView mStatusView; - private TextView mResultView; + private TextView mResultView; // global dump of all messages private TextView broadcastStatus; - private EditText authToken; + private EditText authTokenUI; private TextView authStatusMessage; private TextView authStatusClickableUrl; private UsbManager usbManager; private DfuUpload dfuUpload; private SoundBroadcast soundBroadcast = new SoundBroadcast(); - + private PermissionGrantedAction onPermissionGrantedAction; @SuppressLint("SetTextI18n") @Override @@ -102,6 +103,7 @@ public class rusEFI extends Activity { findViewById(R.id.buttonDfu).setVisibility(View.GONE); broadcastStatus = findViewById(R.id.broadcastStatus); + broadcastStatus.setVisibility(View.GONE); usbManager = (UsbManager) getSystemService(Context.USB_SERVICE); @@ -110,8 +112,8 @@ public class rusEFI extends Activity { mStatusView = findViewById(R.id.text_status); mResultView = findViewById(R.id.text_result); - authToken = findViewById(R.id.authToken); - authToken.addTextChangedListener(new TextWatcher() { + authTokenUI = findViewById(R.id.authToken); + authTokenUI.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { @@ -123,7 +125,7 @@ public class rusEFI extends Activity { @Override public void afterTextChanged(Editable editable) { - String text = authToken.getText().toString(); + String text = authTokenUI.getText().toString(); if (AuthTokenUtil.isToken(text)) { SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(rusEFI.this); SharedPreferences.Editor editor = preferences.edit(); @@ -143,13 +145,14 @@ public class rusEFI extends Activity { IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION); registerReceiver(mUsbReceiver, filter); - mResultView.append(VERSION); + visibleLogAppend(VERSION); dfuUpload = new DfuUpload(this); - dfuUpload.fileOperation(mResultView); - String authToken = getAuthToken(); - this.authToken.setText(authToken); + //dfuUpload.downloadFileIfNotPresent(mResultView); + + String authToken = readPersistedAuthToken(); + authTokenUI.setText(authToken); int visibility = AuthTokenUtil.isToken(authToken) ? View.GONE : View.VISIBLE; authStatusMessage.setVisibility(visibility); authStatusClickableUrl.setVisibility(visibility); @@ -159,7 +162,7 @@ public class rusEFI extends Activity { // SoundBroadcast.checkOrRequestPermission(this); } - private String getAuthToken() { + private String readPersistedAuthToken() { SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(rusEFI.this); return preferences.getString(AuthTokenUtil.AUTH_TOKEN, ""); } @@ -185,8 +188,13 @@ public class rusEFI extends Activity { if (ACTION_USB_PERMISSION.equals(action)) { synchronized (this) { if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { - UsbDevice dfuDevice = DfuDeviceLocator.findDevice(usbManager); - doDfuUpdate(dfuDevice, rusEFI.this.mResultView); + if (onPermissionGrantedAction == PermissionGrantedAction.DFU) { + + UsbDevice dfuDevice = DfuDeviceLocator.findDevice(usbManager); + doDfuUpdate(dfuDevice); + } else if (onPermissionGrantedAction == PermissionGrantedAction.DASHBOARD) { + connectDashboard(); + } } } } @@ -195,45 +203,49 @@ public class rusEFI extends Activity { private void switchOrProgramDfu() { UsbDevice dfuDevice = DfuDeviceLocator.findDevice(usbManager); - - if (dfuDevice != null) { - dfuUpdate(dfuDevice); - } else { - mResultView.append("No DFU device\n"); + if (dfuDevice == null) { + visibleLogAppend("No DFU device\n"); switchToDfu(); // once device is in DFU mode we expect what exactly to happen? + } else if (!usbManager.hasPermission(dfuDevice)) { + requestUsbPermission(dfuDevice, PermissionGrantedAction.DFU); + } else { + doDfuUpdate(dfuDevice); } } + public void visibleLogAppend(String s) { + CharSequence current = mResultView.getText(); + mResultView.setText(s + "\n" + current); + } + @SuppressLint("SetTextI18n") private void switchToDfu() { - AndroidSerial serial = AndroidSerial.getAndroidSerial(mStatusView, mResultView, usbManager); + AndroidSerial serial = AndroidSerial.getAndroidSerial(this, mStatusView, usbManager); if (serial == null) { // error already reported to mStatusView return; } - mResultView.append("Switching to DFU\n"); + visibleLogAppend("Switching to DFU\n"); DfuHelper.sendDfuRebootCommand(serial, StatusConsumer.VOID); } - private void dfuUpdate(UsbDevice dfuDevice) { - if (usbManager.hasPermission(dfuDevice)) { - doDfuUpdate(dfuDevice, mResultView); - } else { - PendingIntent mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0); - usbManager.requestPermission(dfuDevice, mPermissionIntent); - } + public void requestUsbPermission(UsbDevice usbDevice, PermissionGrantedAction action) { + // why do we not have similar 'request serial USB permission'? + onPermissionGrantedAction = action; + PendingIntent mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0); + usbManager.requestPermission(usbDevice, mPermissionIntent); } @SuppressLint("SetTextI18n") - private void doDfuUpdate(UsbDevice dfuDevice, TextView mResultView) { + private void doDfuUpdate(UsbDevice dfuDevice) { mStatusView.setText("rusEFI: DFU detected"); DfuDeviceLocator.Result dfu = new DfuDeviceLocator().openDfu(usbManager, dfuDevice); DfuImage dfuImage = new DfuImage(); dfuImage.read(dfuUpload.localDfuImageFileName); - mResultView.append("Image size " + dfuImage.getImageSize() + "\n"); + visibleLogAppend("Image size " + dfuImage.getImageSize() + "\n"); DfuConnection connection = new AndroidDfuConnection(dfu.getConnection(), dfu.getInterfaceIndex(), dfu.getTransferSize(), dfu.getFlashRange()); @@ -244,12 +256,16 @@ public class rusEFI extends Activity { DfuLogic.uploadImage(logger, connection, dfuImage, dfu.getFlashRange()); } catch (IllegalStateException e) { - this.mResultView.append("Error " + e + "\n"); + visibleLogAppend("Error " + e + "\n"); } } + public void onConnectButton(View view) { + connectDashboard(); + } + /** - * Called when the user touches the button + * Called when the user touches a button */ public void sendMessage(View view) { if (view.getId() == R.id.buttonDfu) { @@ -259,7 +275,7 @@ public class rusEFI extends Activity { } else if (view.getId() == R.id.buttonBroadcast) { startService(new Intent(this, SerialService.class)); - AndroidSerial serial = AndroidSerial.getAndroidSerial(mStatusView, mResultView, usbManager); + AndroidSerial serial = AndroidSerial.getAndroidSerial(this, mStatusView, usbManager); if (serial == null) { // error already reported to mStatusView Snackbar mySnackbar = Snackbar.make(view, "No ECU detected", BaseTransientBottomBar.LENGTH_LONG); @@ -276,48 +292,65 @@ public class rusEFI extends Activity { })); linkManager.getConnector().connectAndReadConfiguration(new BinaryProtocol.Arguments(true), new ConnectionStateListener() { - @Override - public void onConnectionEstablished() { - mResultView.post(() -> mResultView.append(new Date() + " On connection established\n")); + @Override + public void onConnectionEstablished() { + mResultView.post(() -> visibleLogAppend(new Date() + " On connection established\n")); - NetworkConnectorContext context = new NetworkConnectorContext(); - NetworkConnector.ActivityListener oncePerSecondStatistics = new NetworkConnector.ActivityListener() { - long previousTime; + NetworkConnectorContext context = new NetworkConnectorContext(); + NetworkConnector.ActivityListener oncePerSecondStatistics = new NetworkConnector.ActivityListener() { + long previousTime; + + @Override + public void onActivity(IoStream targetEcuSocket) { + long now = System.currentTimeMillis(); + if (now - previousTime < Timeouts.SECOND) { + // only update status once per second + return; + } + previousTime = now; + broadcastStatus.post(() -> broadcastStatus.setText(targetEcuSocket.getBytesIn() + "/" + targetEcuSocket.getBytesOut())); + } + }; + NetworkConnector.NetworkConnectorResult result = new NetworkConnector().start(NetworkConnector.Implementation.Android, + readPersistedAuthToken(), context, new NetworkConnector.ReconnectListener() { + @Override + public void onReconnect() { + } + }, linkManager, oncePerSecondStatistics); + + mResultView.post(() -> visibleLogAppend(new Date() + " Broadcast: " + result + "\n")); + + } @Override - public void onActivity(IoStream targetEcuSocket) { - long now = System.currentTimeMillis(); - if (now - previousTime < Timeouts.SECOND) { - return; - } - previousTime = now; - broadcastStatus.post(() -> broadcastStatus.setText(targetEcuSocket.getBytesIn() + "/" + targetEcuSocket.getBytesOut())); + public void onConnectionFailed() { + mResultView.post(() -> visibleLogAppend("Connection failed\n")); } - }; - NetworkConnector.NetworkConnectorResult result = new NetworkConnector().start(NetworkConnector.Implementation.Android, - getAuthToken(), context, new NetworkConnector.ReconnectListener() { - @Override - public void onReconnect() { - } - }, linkManager, oncePerSecondStatistics); + }); - mResultView.post(() -> mResultView.append(new Date() + " Broadcast: " + result + "\n")); - - } - - @Override - public void onConnectionFailed() { - mResultView.post(() -> mResultView.append("Connection failed\n")); - } - }); - - Snackbar mySnackbar = Snackbar.make(view, "Broadcasting with " + getAuthToken(), BaseTransientBottomBar.LENGTH_LONG); + Snackbar mySnackbar = Snackbar.make(view, "Broadcasting with " + readPersistedAuthToken(), BaseTransientBottomBar.LENGTH_LONG); mySnackbar.show(); } } + private void connectDashboard() { + AndroidSerial serial = AndroidSerial.getAndroidSerial(this, mStatusView, usbManager); + if (serial == null) { + // error already reported to mStatusView + return; + } + try { + String signature = BinaryProtocol.getSignature(serial); + visibleLogAppend("Connected to " + signature); + + } catch (IOException e) { + e.printStackTrace(); + } + + } + @Override protected void onNewIntent(Intent intent) { } diff --git a/android/app/src/main/java/com/rusefi/app/serial/AndroidSerial.java b/android/app/src/main/java/com/rusefi/app/serial/AndroidSerial.java index 73c721f53d..8f0bdec122 100644 --- a/android/app/src/main/java/com/rusefi/app/serial/AndroidSerial.java +++ b/android/app/src/main/java/com/rusefi/app/serial/AndroidSerial.java @@ -1,6 +1,7 @@ package com.rusefi.app.serial; import android.annotation.SuppressLint; +import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbManager; import android.widget.TextView; @@ -11,6 +12,8 @@ import com.hoho.android.usbserial.driver.UsbSerialDriver; import com.hoho.android.usbserial.driver.UsbSerialPort; import com.hoho.android.usbserial.driver.UsbSerialProber; import com.opensr5.io.DataListener; +import com.rusefi.app.PermissionGrantedAction; +import com.rusefi.app.rusEFI; import com.rusefi.binaryprotocol.IncomingDataBuffer; import com.rusefi.dfu.DfuLogic; import com.rusefi.io.ByteReader; @@ -41,18 +44,35 @@ public class AndroidSerial extends AbstractIoStream { dataBuffer = createDataBuffer(""); } - @SuppressLint("SetTextI18n") - @Nullable - public static AndroidSerial getAndroidSerial(TextView mStatusView, TextView mResultView, UsbManager usbManager) { + private static UsbSerialDriver getSerialDriver(rusEFI rusEFI, TextView mStatusView, UsbManager usbManager, PermissionGrantedAction action) { List availableDrivers = findUsbSerial(usbManager); if (availableDrivers.isEmpty()) { mStatusView.setText("Serial not found"); - mResultView.append("No serial devices " + new Date() + "\n"); + rusEFI.visibleLogAppend("No serial devices " + new Date() + "\n"); return null; } mStatusView.setText("rusEFI: " + availableDrivers.size() + " device(s)"); UsbSerialDriver driver = availableDrivers.get(0); + + UsbDevice usbDevice = driver.getDevice(); + if (!usbManager.hasPermission(usbDevice)) { + mStatusView.setText("Need permission"); + rusEFI.requestUsbPermission(usbDevice, action); + return null; + } + return driver; + } + + @SuppressLint("SetTextI18n") + @Nullable + public static AndroidSerial getAndroidSerial(rusEFI rusEFI, TextView mStatusView, UsbManager usbManager) { + // todo: should support separate actions not default to dashboard! + UsbSerialDriver driver = getSerialDriver(rusEFI, mStatusView, usbManager, PermissionGrantedAction.DASHBOARD); + if (driver == null) { + // error already reported to UI or permission request was fired + return null; + } UsbDeviceConnection connection = usbManager.openDevice(driver.getDevice()); if (connection == null) { // add UsbManager.requestPermission(driver.getDevice(), ..) handling here diff --git a/android/app/src/main/res/layout/activity_usb.xml b/android/app/src/main/res/layout/activity_usb.xml index 2b634801d3..0a117a9453 100644 --- a/android/app/src/main/res/layout/activity_usb.xml +++ b/android/app/src/main/res/layout/activity_usb.xml @@ -53,6 +53,13 @@ android:onClick="sendMessage" android:text="Sound" /> +