has been a while since I've touched Android!

This commit is contained in:
rusefillc 2022-05-24 20:04:25 -04:00
parent 829de00a21
commit 7f0babed06
7 changed files with 147 additions and 98 deletions

View File

@ -29,12 +29,12 @@ public class DfuUpload {
localDfuImageFileName = localFolder + File.separator + DFU_FILE_NAME; localDfuImageFileName = localFolder + File.separator + DFU_FILE_NAME;
} }
void fileOperation(final TextView mResultView) { void downloadFileIfNotPresent(final TextView mResultView) {
if (new File(this.localFullFile).exists()) { 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); uncompressFile(this.localFullFile, this.localFolder, this.localDfuImageFileName, mResultView);
} else { } else {
mResultView.append(this.BUNDLE_FILE + " not found!\n"); mResultView.append(BUNDLE_FILE + " not found!\n");
new Thread(new Runnable() { new Thread(new Runnable() {
@Override @Override
@ -43,30 +43,15 @@ public class DfuUpload {
ConnectionAndMeta c = new ConnectionAndMeta(BUNDLE_FILE).invoke(ConnectionAndMeta.BASE_URL_LATEST); ConnectionAndMeta c = new ConnectionAndMeta(BUNDLE_FILE).invoke(ConnectionAndMeta.BASE_URL_LATEST);
ConnectionAndMeta.downloadFile(localFullFile, c, new ConnectionAndMeta.DownloadProgressListener() { ConnectionAndMeta.downloadFile(localFullFile, c, new ConnectionAndMeta.DownloadProgressListener() {
@Override @Override
public void onPercentage(final int currentProgress) { public void onPercentage(final int currentPercentage) {
mResultView.post(new Runnable() { mResultView.post(() -> mResultView.append("Downloaded " + currentPercentage + "%\n"));
@Override
public void run() {
mResultView.append("Downloading " + currentProgress + "\n");
}
});
}
});
mResultView.post(new Runnable() {
@Override
public void run() {
mResultView.append("Downloaded! " + "\n");
} }
}); });
mResultView.post(() -> mResultView.append("Downloaded! " + "\n"));
uncompressFile(localFullFile, localFolder, localDfuImageFileName, mResultView); uncompressFile(localFullFile, localFolder, localDfuImageFileName, mResultView);
} catch (IOException | KeyManagementException | NoSuchAlgorithmException e) { } catch (IOException | KeyManagementException | NoSuchAlgorithmException e) {
mResultView.post(new Runnable() { mResultView.post(() -> mResultView.append("Error downloading " + e + "\n"));
@Override
public void run() {
mResultView.append("Error downloading " + e + "\n");
}
});
} }
} }
}).start(); }).start();

View File

@ -0,0 +1,6 @@
package com.rusefi.app;
public enum PermissionGrantedAction {
DFU,
DASHBOARD,
}

View File

@ -60,6 +60,7 @@ import com.rusefi.proxy.NetworkConnector;
import com.rusefi.proxy.NetworkConnectorContext; import com.rusefi.proxy.NetworkConnectorContext;
import com.rusefi.ui.StatusConsumer; import com.rusefi.ui.StatusConsumer;
import java.io.IOException;
import java.util.Date; import java.util.Date;
public class rusEFI extends Activity { public class rusEFI extends Activity {
@ -71,20 +72,20 @@ public class rusEFI extends Activity {
// //
// protected static final int DFU_DETACH_TIMEOUT = 1000; // 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 */ /* UI elements */
private TextView mStatusView; private TextView mStatusView;
private TextView mResultView; private TextView mResultView; // global dump of all messages
private TextView broadcastStatus; private TextView broadcastStatus;
private EditText authToken; private EditText authTokenUI;
private TextView authStatusMessage; private TextView authStatusMessage;
private TextView authStatusClickableUrl; private TextView authStatusClickableUrl;
private UsbManager usbManager; private UsbManager usbManager;
private DfuUpload dfuUpload; private DfuUpload dfuUpload;
private SoundBroadcast soundBroadcast = new SoundBroadcast(); private SoundBroadcast soundBroadcast = new SoundBroadcast();
private PermissionGrantedAction onPermissionGrantedAction;
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
@Override @Override
@ -102,6 +103,7 @@ public class rusEFI extends Activity {
findViewById(R.id.buttonDfu).setVisibility(View.GONE); findViewById(R.id.buttonDfu).setVisibility(View.GONE);
broadcastStatus = findViewById(R.id.broadcastStatus); broadcastStatus = findViewById(R.id.broadcastStatus);
broadcastStatus.setVisibility(View.GONE);
usbManager = (UsbManager) getSystemService(Context.USB_SERVICE); usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
@ -110,8 +112,8 @@ public class rusEFI extends Activity {
mStatusView = findViewById(R.id.text_status); mStatusView = findViewById(R.id.text_status);
mResultView = findViewById(R.id.text_result); mResultView = findViewById(R.id.text_result);
authToken = findViewById(R.id.authToken); authTokenUI = findViewById(R.id.authToken);
authToken.addTextChangedListener(new TextWatcher() { authTokenUI.addTextChangedListener(new TextWatcher() {
@Override @Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
@ -123,7 +125,7 @@ public class rusEFI extends Activity {
@Override @Override
public void afterTextChanged(Editable editable) { public void afterTextChanged(Editable editable) {
String text = authToken.getText().toString(); String text = authTokenUI.getText().toString();
if (AuthTokenUtil.isToken(text)) { if (AuthTokenUtil.isToken(text)) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(rusEFI.this); SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(rusEFI.this);
SharedPreferences.Editor editor = preferences.edit(); SharedPreferences.Editor editor = preferences.edit();
@ -143,13 +145,14 @@ public class rusEFI extends Activity {
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION); IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
registerReceiver(mUsbReceiver, filter); registerReceiver(mUsbReceiver, filter);
mResultView.append(VERSION); visibleLogAppend(VERSION);
dfuUpload = new DfuUpload(this); dfuUpload = new DfuUpload(this);
dfuUpload.fileOperation(mResultView); //dfuUpload.downloadFileIfNotPresent(mResultView);
String authToken = getAuthToken();
this.authToken.setText(authToken); String authToken = readPersistedAuthToken();
authTokenUI.setText(authToken);
int visibility = AuthTokenUtil.isToken(authToken) ? View.GONE : View.VISIBLE; int visibility = AuthTokenUtil.isToken(authToken) ? View.GONE : View.VISIBLE;
authStatusMessage.setVisibility(visibility); authStatusMessage.setVisibility(visibility);
authStatusClickableUrl.setVisibility(visibility); authStatusClickableUrl.setVisibility(visibility);
@ -159,7 +162,7 @@ public class rusEFI extends Activity {
// SoundBroadcast.checkOrRequestPermission(this); // SoundBroadcast.checkOrRequestPermission(this);
} }
private String getAuthToken() { private String readPersistedAuthToken() {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(rusEFI.this); SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(rusEFI.this);
return preferences.getString(AuthTokenUtil.AUTH_TOKEN, ""); return preferences.getString(AuthTokenUtil.AUTH_TOKEN, "");
} }
@ -185,8 +188,13 @@ public class rusEFI extends Activity {
if (ACTION_USB_PERMISSION.equals(action)) { if (ACTION_USB_PERMISSION.equals(action)) {
synchronized (this) { synchronized (this) {
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
UsbDevice dfuDevice = DfuDeviceLocator.findDevice(usbManager); if (onPermissionGrantedAction == PermissionGrantedAction.DFU) {
doDfuUpdate(dfuDevice, rusEFI.this.mResultView);
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() { private void switchOrProgramDfu() {
UsbDevice dfuDevice = DfuDeviceLocator.findDevice(usbManager); UsbDevice dfuDevice = DfuDeviceLocator.findDevice(usbManager);
if (dfuDevice == null) {
if (dfuDevice != null) { visibleLogAppend("No DFU device\n");
dfuUpdate(dfuDevice);
} else {
mResultView.append("No DFU device\n");
switchToDfu(); switchToDfu();
// once device is in DFU mode we expect what exactly to happen? // 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") @SuppressLint("SetTextI18n")
private void switchToDfu() { private void switchToDfu() {
AndroidSerial serial = AndroidSerial.getAndroidSerial(mStatusView, mResultView, usbManager); AndroidSerial serial = AndroidSerial.getAndroidSerial(this, mStatusView, usbManager);
if (serial == null) { if (serial == null) {
// error already reported to mStatusView // error already reported to mStatusView
return; return;
} }
mResultView.append("Switching to DFU\n"); visibleLogAppend("Switching to DFU\n");
DfuHelper.sendDfuRebootCommand(serial, StatusConsumer.VOID); DfuHelper.sendDfuRebootCommand(serial, StatusConsumer.VOID);
} }
private void dfuUpdate(UsbDevice dfuDevice) { public void requestUsbPermission(UsbDevice usbDevice, PermissionGrantedAction action) {
if (usbManager.hasPermission(dfuDevice)) { // why do we not have similar 'request serial USB permission'?
doDfuUpdate(dfuDevice, mResultView); onPermissionGrantedAction = action;
} else { PendingIntent mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
PendingIntent mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0); usbManager.requestPermission(usbDevice, mPermissionIntent);
usbManager.requestPermission(dfuDevice, mPermissionIntent);
}
} }
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
private void doDfuUpdate(UsbDevice dfuDevice, TextView mResultView) { private void doDfuUpdate(UsbDevice dfuDevice) {
mStatusView.setText("rusEFI: DFU detected"); mStatusView.setText("rusEFI: DFU detected");
DfuDeviceLocator.Result dfu = new DfuDeviceLocator().openDfu(usbManager, dfuDevice); DfuDeviceLocator.Result dfu = new DfuDeviceLocator().openDfu(usbManager, dfuDevice);
DfuImage dfuImage = new DfuImage(); DfuImage dfuImage = new DfuImage();
dfuImage.read(dfuUpload.localDfuImageFileName); 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()); 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()); DfuLogic.uploadImage(logger, connection, dfuImage, dfu.getFlashRange());
} catch (IllegalStateException e) { } 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) { public void sendMessage(View view) {
if (view.getId() == R.id.buttonDfu) { if (view.getId() == R.id.buttonDfu) {
@ -259,7 +275,7 @@ public class rusEFI extends Activity {
} else if (view.getId() == R.id.buttonBroadcast) { } else if (view.getId() == R.id.buttonBroadcast) {
startService(new Intent(this, SerialService.class)); startService(new Intent(this, SerialService.class));
AndroidSerial serial = AndroidSerial.getAndroidSerial(mStatusView, mResultView, usbManager); AndroidSerial serial = AndroidSerial.getAndroidSerial(this, mStatusView, usbManager);
if (serial == null) { if (serial == null) {
// error already reported to mStatusView // error already reported to mStatusView
Snackbar mySnackbar = Snackbar.make(view, "No ECU detected", BaseTransientBottomBar.LENGTH_LONG); 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), linkManager.getConnector().connectAndReadConfiguration(new BinaryProtocol.Arguments(true),
new ConnectionStateListener() { new ConnectionStateListener() {
@Override @Override
public void onConnectionEstablished() { public void onConnectionEstablished() {
mResultView.post(() -> mResultView.append(new Date() + " On connection established\n")); mResultView.post(() -> visibleLogAppend(new Date() + " On connection established\n"));
NetworkConnectorContext context = new NetworkConnectorContext(); NetworkConnectorContext context = new NetworkConnectorContext();
NetworkConnector.ActivityListener oncePerSecondStatistics = new NetworkConnector.ActivityListener() { NetworkConnector.ActivityListener oncePerSecondStatistics = new NetworkConnector.ActivityListener() {
long previousTime; 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 @Override
public void onActivity(IoStream targetEcuSocket) { public void onConnectionFailed() {
long now = System.currentTimeMillis(); mResultView.post(() -> visibleLogAppend("Connection failed\n"));
if (now - previousTime < Timeouts.SECOND) {
return;
}
previousTime = now;
broadcastStatus.post(() -> broadcastStatus.setText(targetEcuSocket.getBytesIn() + "/" + targetEcuSocket.getBytesOut()));
} }
}; });
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")); Snackbar mySnackbar = Snackbar.make(view, "Broadcasting with " + readPersistedAuthToken(), BaseTransientBottomBar.LENGTH_LONG);
}
@Override
public void onConnectionFailed() {
mResultView.post(() -> mResultView.append("Connection failed\n"));
}
});
Snackbar mySnackbar = Snackbar.make(view, "Broadcasting with " + getAuthToken(), BaseTransientBottomBar.LENGTH_LONG);
mySnackbar.show(); 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 @Override
protected void onNewIntent(Intent intent) { protected void onNewIntent(Intent intent) {
} }

View File

@ -1,6 +1,7 @@
package com.rusefi.app.serial; package com.rusefi.app.serial;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbManager; import android.hardware.usb.UsbManager;
import android.widget.TextView; 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.UsbSerialPort;
import com.hoho.android.usbserial.driver.UsbSerialProber; import com.hoho.android.usbserial.driver.UsbSerialProber;
import com.opensr5.io.DataListener; import com.opensr5.io.DataListener;
import com.rusefi.app.PermissionGrantedAction;
import com.rusefi.app.rusEFI;
import com.rusefi.binaryprotocol.IncomingDataBuffer; import com.rusefi.binaryprotocol.IncomingDataBuffer;
import com.rusefi.dfu.DfuLogic; import com.rusefi.dfu.DfuLogic;
import com.rusefi.io.ByteReader; import com.rusefi.io.ByteReader;
@ -41,18 +44,35 @@ public class AndroidSerial extends AbstractIoStream {
dataBuffer = createDataBuffer(""); dataBuffer = createDataBuffer("");
} }
@SuppressLint("SetTextI18n") private static UsbSerialDriver getSerialDriver(rusEFI rusEFI, TextView mStatusView, UsbManager usbManager, PermissionGrantedAction action) {
@Nullable
public static AndroidSerial getAndroidSerial(TextView mStatusView, TextView mResultView, UsbManager usbManager) {
List<UsbSerialDriver> availableDrivers = findUsbSerial(usbManager); List<UsbSerialDriver> availableDrivers = findUsbSerial(usbManager);
if (availableDrivers.isEmpty()) { if (availableDrivers.isEmpty()) {
mStatusView.setText("Serial not found"); mStatusView.setText("Serial not found");
mResultView.append("No serial devices " + new Date() + "\n"); rusEFI.visibleLogAppend("No serial devices " + new Date() + "\n");
return null; return null;
} }
mStatusView.setText("rusEFI: " + availableDrivers.size() + " device(s)"); mStatusView.setText("rusEFI: " + availableDrivers.size() + " device(s)");
UsbSerialDriver driver = availableDrivers.get(0); 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()); UsbDeviceConnection connection = usbManager.openDevice(driver.getDevice());
if (connection == null) { if (connection == null) {
// add UsbManager.requestPermission(driver.getDevice(), ..) handling here // add UsbManager.requestPermission(driver.getDevice(), ..) handling here

View File

@ -53,6 +53,13 @@
android:onClick="sendMessage" android:onClick="sendMessage"
android:text="Sound" /> android:text="Sound" />
<Button
android:id="@+id/buttonConnect"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onConnectButton"
android:text="Connect" />
<Button <Button
android:id="@+id/buttonBroadcast" android:id="@+id/buttonBroadcast"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -64,7 +71,7 @@
android:id="@+id/broadcastStatus" android:id="@+id/broadcastStatus"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="TextView" /> android:text="Hello rusEFI" />
<ScrollView <ScrollView
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -36,7 +36,7 @@ public class AutoupdateUtil {
JProgressBar jProgressBar = new JProgressBar(); JProgressBar jProgressBar = new JProgressBar();
frameHelper.getFrame().setTitle(title); frameHelper.getFrame().setTitle(title);
jProgressBar.setMaximum(ConnectionAndMeta.STEPS); jProgressBar.setMaximum(ConnectionAndMeta.CENTUM);
jProgressBarAtomicReference.set(jProgressBar); jProgressBarAtomicReference.set(jProgressBar);
frameHelper.showFrame(jProgressBar, true); frameHelper.showFrame(jProgressBar, true);
} }

View File

@ -15,7 +15,7 @@ public class ConnectionAndMeta {
public static final String BASE_URL_LATEST = "https://rusefi.com/build_server/autoupdate/"; public static final String BASE_URL_LATEST = "https://rusefi.com/build_server/autoupdate/";
private static final int BUFFER_SIZE = 32 * 1024; private static final int BUFFER_SIZE = 32 * 1024;
public static final int STEPS = 1000; public static final int CENTUM = 100;
private final String zipFileName; private final String zipFileName;
private HttpsURLConnection httpConnection; private HttpsURLConnection httpConnection;
private long completeFileSize; private long completeFileSize;
@ -42,15 +42,13 @@ public class ConnectionAndMeta {
downloadedFileSize += newDataSize; downloadedFileSize += newDataSize;
// calculate progress // calculate progress
final int currentProgress = (int) ((((double) downloadedFileSize) / ((double) completeFileSize)) * STEPS); int currentPercentage = (int) (CENTUM * downloadedFileSize / completeFileSize);
int currentPercentage = (int) (100L * downloadedFileSize / completeFileSize);
if (currentPercentage > printedPercentage + 5) { if (currentPercentage > printedPercentage + 5) {
System.out.println("Downloaded " + currentPercentage + "%"); System.out.println("Downloaded " + currentPercentage + "%");
printedPercentage = currentPercentage; printedPercentage = currentPercentage;
listener.onPercentage(currentPercentage);
} }
listener.onPercentage(currentProgress);
bout.write(data, 0, newDataSize); bout.write(data, 0, newDataSize);
} }
@ -86,7 +84,7 @@ public class ConnectionAndMeta {
} }
public interface DownloadProgressListener { public interface DownloadProgressListener {
void onPercentage(int currentProgress); void onPercentage(int currentPercentage);
} }
private static class AcceptAnyCertificateTrustManager implements X509TrustManager { private static class AcceptAnyCertificateTrustManager implements X509TrustManager {