modernize example app
|
@ -39,7 +39,7 @@
|
|||
</value>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
|
|
|
@ -96,12 +96,12 @@ and finally:
|
|||
port.close();
|
||||
```
|
||||
|
||||
|
||||
For a simple example, see
|
||||
[UsbSerialExamples](https://github.com/mik3y/usb-serial-for-android/blob/master/usbSerialExamples)
|
||||
folder in this project.
|
||||
|
||||
For a more complete example, see separate github project
|
||||
For a more complete example with background service to stay connected while
|
||||
the app is not visible or rotating, see separate github project
|
||||
[SimpleUsbTerminal](https://github.com/kai-morich/SimpleUsbTerminal).
|
||||
|
||||
## Probing for Unrecognized Devices
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
#android.enableJetifier=true
|
||||
android.useAndroidX=true
|
|
@ -4,6 +4,11 @@ android {
|
|||
compileSdkVersion 28
|
||||
buildToolsVersion '28.0.3'
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 17
|
||||
targetSdkVersion 28
|
||||
|
@ -21,4 +26,6 @@ android {
|
|||
|
||||
dependencies {
|
||||
implementation project(':usbSerialForAndroid')
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||
implementation 'com.google.android.material:material:1.1.0'
|
||||
}
|
||||
|
|
|
@ -1,37 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.hoho.android.usbserial.examples"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0" >
|
||||
|
||||
<uses-feature android:name="android.hardware.usb.host" />
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.hoho.android.usbserial.examples">
|
||||
|
||||
<!-- mipmap/ic_launcher created with Android Studio -> New -> Image Asset using @color/colorPrimary and USB clip art -->
|
||||
<application
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:label="@string/app_name" >
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/AppTheme"
|
||||
tools:ignore="AllowBackup,GoogleAppIndexingWarning">
|
||||
<!-- for this simple app launchMode=singleTask and singleTop have same effect.
|
||||
If you would start another activity in the app, e.g. Android Settings
|
||||
then you should use singleTask, else a new MainActivity would be started
|
||||
when the settings activity is currently shown -->
|
||||
<activity
|
||||
android:name="com.hoho.android.usbserial.examples.DeviceListActivity"
|
||||
android:name=".MainActivity"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen" >
|
||||
android:launchMode="singleTask"
|
||||
android:windowSoftInputMode="stateHidden|adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
|
||||
android:resource="@xml/device_filter" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name="com.hoho.android.usbserial.examples.SerialConsoleActivity"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen" >
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
</manifest>
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
package com.hoho.android.usbserial.examples;
|
||||
|
||||
import com.hoho.android.usbserial.driver.CdcAcmSerialDriver;
|
||||
import com.hoho.android.usbserial.driver.ProbeTable;
|
||||
import com.hoho.android.usbserial.driver.UsbSerialProber;
|
||||
|
||||
/**
|
||||
* add devices here, that are not known to DefaultProber
|
||||
*
|
||||
* if the App should auto start for these devices, also
|
||||
* add IDs to app/src/main/res/xml/device_filter.xml
|
||||
*/
|
||||
class CustomProber {
|
||||
|
||||
static UsbSerialProber getCustomProber() {
|
||||
ProbeTable customTable = new ProbeTable();
|
||||
customTable.addProduct(0x16d0, 0x087e, CdcAcmSerialDriver.class); // e.g. Digispark CDC
|
||||
return new UsbSerialProber(customTable);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,233 +0,0 @@
|
|||
/* Copyright 2011-2013 Google Inc.
|
||||
* Copyright 2013 mike wakerly <opensource@hoho.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
* USA.
|
||||
*
|
||||
* Project home page: https://github.com/mik3y/usb-serial-for-android
|
||||
*/
|
||||
|
||||
package com.hoho.android.usbserial.examples;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.hardware.usb.UsbDevice;
|
||||
import android.hardware.usb.UsbManager;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import android.widget.TwoLineListItem;
|
||||
|
||||
import com.hoho.android.usbserial.driver.UsbSerialDriver;
|
||||
import com.hoho.android.usbserial.driver.UsbSerialPort;
|
||||
import com.hoho.android.usbserial.driver.UsbSerialProber;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Shows a {@link ListView} of available USB devices.
|
||||
*
|
||||
* @author mike wakerly (opensource@hoho.com)
|
||||
*/
|
||||
public class DeviceListActivity extends Activity {
|
||||
|
||||
private final String TAG = DeviceListActivity.class.getSimpleName();
|
||||
|
||||
private UsbManager mUsbManager;
|
||||
private UsbSerialPort mSerialPort;
|
||||
private ListView mListView;
|
||||
private TextView mProgressBarTitle;
|
||||
private ProgressBar mProgressBar;
|
||||
|
||||
private static final int MESSAGE_REFRESH = 101;
|
||||
private static final long REFRESH_TIMEOUT_MILLIS = 5000;
|
||||
public static final String INTENT_ACTION_GRANT_USB = BuildConfig.APPLICATION_ID + ".GRANT_USB";
|
||||
|
||||
private final Handler mHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MESSAGE_REFRESH:
|
||||
refreshDeviceList();
|
||||
mHandler.sendEmptyMessageDelayed(MESSAGE_REFRESH, REFRESH_TIMEOUT_MILLIS);
|
||||
break;
|
||||
default:
|
||||
super.handleMessage(msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
private BroadcastReceiver mUsbReceiver;
|
||||
|
||||
private List<UsbSerialPort> mEntries = new ArrayList<UsbSerialPort>();
|
||||
private ArrayAdapter<UsbSerialPort> mAdapter;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.main);
|
||||
final Context context = this;
|
||||
|
||||
mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
|
||||
mListView = findViewById(R.id.deviceList);
|
||||
mProgressBar = findViewById(R.id.progressBar);
|
||||
mProgressBarTitle = findViewById(R.id.progressBarTitle);
|
||||
|
||||
mUsbReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if(intent.getAction().equals(INTENT_ACTION_GRANT_USB)) {
|
||||
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
|
||||
showConsoleActivity(mSerialPort);
|
||||
} else {
|
||||
Toast.makeText(context, "USB permission denied", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
mAdapter = new ArrayAdapter<UsbSerialPort>(this,
|
||||
android.R.layout.simple_expandable_list_item_2, mEntries) {
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
final TwoLineListItem row;
|
||||
if (convertView == null){
|
||||
final LayoutInflater inflater =
|
||||
(LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
row = (TwoLineListItem) inflater.inflate(android.R.layout.simple_list_item_2, null);
|
||||
} else {
|
||||
row = (TwoLineListItem) convertView;
|
||||
}
|
||||
|
||||
final UsbSerialPort port = mEntries.get(position);
|
||||
final UsbSerialDriver driver = port.getDriver();
|
||||
final UsbDevice device = driver.getDevice();
|
||||
|
||||
final String title = String.format("Vendor %4X Product %4X", device.getVendorId(), device.getProductId());
|
||||
row.getText1().setText(title);
|
||||
|
||||
final String subtitle = driver.getClass().getSimpleName();
|
||||
row.getText2().setText(subtitle);
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
};
|
||||
mListView.setAdapter(mAdapter);
|
||||
|
||||
mListView.setOnItemClickListener(new ListView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
Log.d(TAG, "Pressed item " + position);
|
||||
if (position >= mEntries.size()) {
|
||||
Log.w(TAG, "Illegal position.");
|
||||
return;
|
||||
}
|
||||
|
||||
mSerialPort = mEntries.get(position);
|
||||
UsbDevice device = mSerialPort.getDriver().getDevice();
|
||||
if (!mUsbManager.hasPermission(device)) {
|
||||
PendingIntent usbPermissionIntent = PendingIntent.getBroadcast(context, 0, new Intent(INTENT_ACTION_GRANT_USB), 0);
|
||||
mUsbManager.requestPermission(device, usbPermissionIntent);
|
||||
} else {
|
||||
showConsoleActivity(mSerialPort);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
mHandler.sendEmptyMessage(MESSAGE_REFRESH);
|
||||
registerReceiver(mUsbReceiver, new IntentFilter(INTENT_ACTION_GRANT_USB));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
mHandler.removeMessages(MESSAGE_REFRESH);
|
||||
unregisterReceiver(mUsbReceiver);
|
||||
}
|
||||
|
||||
private void refreshDeviceList() {
|
||||
showProgressBar();
|
||||
|
||||
new AsyncTask<Void, Void, List<UsbSerialPort>>() {
|
||||
@Override
|
||||
protected List<UsbSerialPort> doInBackground(Void... params) {
|
||||
Log.d(TAG, "Refreshing device list ...");
|
||||
SystemClock.sleep(1000);
|
||||
|
||||
final List<UsbSerialDriver> drivers =
|
||||
UsbSerialProber.getDefaultProber().findAllDrivers(mUsbManager);
|
||||
|
||||
final List<UsbSerialPort> result = new ArrayList<UsbSerialPort>();
|
||||
for (final UsbSerialDriver driver : drivers) {
|
||||
final List<UsbSerialPort> ports = driver.getPorts();
|
||||
Log.d(TAG, String.format("+ %s: %s port%s",
|
||||
driver, Integer.valueOf(ports.size()), ports.size() == 1 ? "" : "s"));
|
||||
result.addAll(ports);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(List<UsbSerialPort> result) {
|
||||
mEntries.clear();
|
||||
mEntries.addAll(result);
|
||||
mAdapter.notifyDataSetChanged();
|
||||
mProgressBarTitle.setText(
|
||||
String.format("%s device(s) found",Integer.valueOf(mEntries.size())));
|
||||
hideProgressBar();
|
||||
Log.d(TAG, "Done refreshing, " + mEntries.size() + " entries found.");
|
||||
}
|
||||
|
||||
}.execute((Void) null);
|
||||
}
|
||||
|
||||
private void showProgressBar() {
|
||||
mProgressBar.setVisibility(View.VISIBLE);
|
||||
mProgressBarTitle.setText(R.string.refreshing);
|
||||
}
|
||||
|
||||
private void hideProgressBar() {
|
||||
mProgressBar.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
private void showConsoleActivity(UsbSerialPort port) {
|
||||
SerialConsoleActivity.show(this, port);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
package com.hoho.android.usbserial.examples;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.hardware.usb.UsbDevice;
|
||||
import android.hardware.usb.UsbManager;
|
||||
import android.os.Bundle;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.ListFragment;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.hoho.android.usbserial.driver.UsbSerialDriver;
|
||||
import com.hoho.android.usbserial.driver.UsbSerialProber;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
|
||||
public class DevicesFragment extends ListFragment {
|
||||
|
||||
class ListItem {
|
||||
UsbDevice device;
|
||||
int port;
|
||||
UsbSerialDriver driver;
|
||||
|
||||
ListItem(UsbDevice device, int port, UsbSerialDriver driver) {
|
||||
this.device = device;
|
||||
this.port = port;
|
||||
this.driver = driver;
|
||||
}
|
||||
}
|
||||
|
||||
private ArrayList<ListItem> listItems = new ArrayList<>();
|
||||
private ArrayAdapter<ListItem> listAdapter;
|
||||
private int baudRate = 19200;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setHasOptionsMenu(true);
|
||||
listAdapter = new ArrayAdapter<ListItem>(getActivity(), 0, listItems) {
|
||||
@Override
|
||||
public View getView(int position, View view, ViewGroup parent) {
|
||||
ListItem item = listItems.get(position);
|
||||
if (view == null)
|
||||
view = getActivity().getLayoutInflater().inflate(R.layout.device_list_item, parent, false);
|
||||
TextView text1 = view.findViewById(R.id.text1);
|
||||
TextView text2 = view.findViewById(R.id.text2);
|
||||
if(item.driver == null)
|
||||
text1.setText("<no driver>");
|
||||
else if(item.driver.getPorts().size() == 1)
|
||||
text1.setText(item.driver.getClass().getSimpleName().replace("SerialDriver",""));
|
||||
else
|
||||
text1.setText(item.driver.getClass().getSimpleName().replace("SerialDriver","")+", Port "+item.port);
|
||||
text2.setText(String.format(Locale.US, "Vendor %04X, Product %04X", item.device.getVendorId(), item.device.getProductId()));
|
||||
return view;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
setListAdapter(null);
|
||||
View header = getActivity().getLayoutInflater().inflate(R.layout.device_list_header, null, false);
|
||||
getListView().addHeaderView(header, null, false);
|
||||
setEmptyText("<no USB devices found>");
|
||||
((TextView) getListView().getEmptyView()).setTextSize(18);
|
||||
setListAdapter(listAdapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.menu_devices, menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
refresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
if(id == R.id.refresh) {
|
||||
refresh();
|
||||
return true;
|
||||
} else if (id ==R.id.baud_rate) {
|
||||
final String[] baudRates = getResources().getStringArray(R.array.baud_rates);
|
||||
int pos = java.util.Arrays.asList(baudRates).indexOf(String.valueOf(baudRate));
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
builder.setTitle("Baud rate");
|
||||
builder.setSingleChoiceItems(baudRates, pos, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int item) {
|
||||
baudRate = Integer.valueOf(baudRates[item]);
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
builder.create().show();
|
||||
return true;
|
||||
} else {
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
UsbManager usbManager = (UsbManager) getActivity().getSystemService(Context.USB_SERVICE);
|
||||
UsbSerialProber usbDefaultProber = UsbSerialProber.getDefaultProber();
|
||||
UsbSerialProber usbCustomProber = CustomProber.getCustomProber();
|
||||
listItems.clear();
|
||||
for(UsbDevice device : usbManager.getDeviceList().values()) {
|
||||
UsbSerialDriver driver = usbDefaultProber.probeDevice(device);
|
||||
if(driver == null) {
|
||||
driver = usbCustomProber.probeDevice(device);
|
||||
}
|
||||
if(driver != null) {
|
||||
for(int port = 0; port < driver.getPorts().size(); port++)
|
||||
listItems.add(new ListItem(device, port, driver));
|
||||
} else {
|
||||
listItems.add(new ListItem(device, 0, null));
|
||||
}
|
||||
}
|
||||
listAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onListItemClick(ListView l, View v, int position, long id) {
|
||||
ListItem item = listItems.get(position-1);
|
||||
if(item.driver == null) {
|
||||
Toast.makeText(getActivity(), "no driver", Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
Bundle args = new Bundle();
|
||||
args.putInt("device", item.device.getDeviceId());
|
||||
args.putInt("port", item.port);
|
||||
args.putInt("baud", baudRate);
|
||||
Fragment fragment = new TerminalFragment();
|
||||
fragment.setArguments(args);
|
||||
getFragmentManager().beginTransaction().replace(R.id.fragment, fragment, "terminal").addToBackStack(null).commit();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package com.hoho.android.usbserial.examples;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
|
||||
public class MainActivity extends AppCompatActivity implements FragmentManager.OnBackStackChangedListener {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
getSupportFragmentManager().addOnBackStackChangedListener(this);
|
||||
if (savedInstanceState == null)
|
||||
getSupportFragmentManager().beginTransaction().add(R.id.fragment, new DevicesFragment(), "devices").commit();
|
||||
else
|
||||
onBackStackChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackStackChanged() {
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(getSupportFragmentManager().getBackStackEntryCount()>0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSupportNavigateUp() {
|
||||
onBackPressed();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
if(intent.getAction().equals("android.hardware.usb.action.USB_DEVICE_ATTACHED")) {
|
||||
TerminalFragment terminal = (TerminalFragment)getSupportFragmentManager().findFragmentByTag("terminal");
|
||||
if (terminal != null)
|
||||
terminal.status("USB device detected");
|
||||
}
|
||||
super.onNewIntent(intent);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,230 +0,0 @@
|
|||
/* Copyright 2011-2013 Google Inc.
|
||||
* Copyright 2013 mike wakerly <opensource@hoho.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
* USA.
|
||||
*
|
||||
* Project home page: https://github.com/mik3y/usb-serial-for-android
|
||||
*/
|
||||
|
||||
package com.hoho.android.usbserial.examples;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.hardware.usb.UsbDeviceConnection;
|
||||
import android.hardware.usb.UsbManager;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.hoho.android.usbserial.driver.UsbSerialPort;
|
||||
import com.hoho.android.usbserial.util.HexDump;
|
||||
import com.hoho.android.usbserial.util.SerialInputOutputManager;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* Monitors a single {@link UsbSerialPort} instance, showing all data
|
||||
* received.
|
||||
*
|
||||
* @author mike wakerly (opensource@hoho.com)
|
||||
*/
|
||||
public class SerialConsoleActivity extends Activity {
|
||||
|
||||
private final String TAG = SerialConsoleActivity.class.getSimpleName();
|
||||
|
||||
/**
|
||||
* Driver instance, passed in statically via
|
||||
* {@link #show(Context, UsbSerialPort)}.
|
||||
*
|
||||
* <p/>
|
||||
* This is a devious hack; it'd be cleaner to re-create the driver using
|
||||
* arguments passed in with the {@link #startActivity(Intent)} intent. We
|
||||
* can get away with it because both activities will run in the same
|
||||
* process, and this is a simple demo.
|
||||
*/
|
||||
private static UsbSerialPort sPort = null;
|
||||
|
||||
private TextView mTitleTextView;
|
||||
private TextView mDumpTextView;
|
||||
private ScrollView mScrollView;
|
||||
private CheckBox chkDTR;
|
||||
private CheckBox chkRTS;
|
||||
|
||||
private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
|
||||
|
||||
private SerialInputOutputManager mSerialIoManager;
|
||||
|
||||
private final SerialInputOutputManager.Listener mListener =
|
||||
new SerialInputOutputManager.Listener() {
|
||||
|
||||
@Override
|
||||
public void onRunError(Exception e) {
|
||||
Log.d(TAG, "Runner stopped.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewData(final byte[] data) {
|
||||
SerialConsoleActivity.this.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
SerialConsoleActivity.this.updateReceivedData(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.serial_console);
|
||||
mTitleTextView = (TextView) findViewById(R.id.demoTitle);
|
||||
mDumpTextView = (TextView) findViewById(R.id.consoleText);
|
||||
mScrollView = (ScrollView) findViewById(R.id.demoScroller);
|
||||
chkDTR = (CheckBox) findViewById(R.id.checkBoxDTR);
|
||||
chkRTS = (CheckBox) findViewById(R.id.checkBoxRTS);
|
||||
|
||||
chkDTR.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
try {
|
||||
sPort.setDTR(isChecked);
|
||||
}catch (IOException x){}
|
||||
}
|
||||
});
|
||||
|
||||
chkRTS.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
try {
|
||||
sPort.setRTS(isChecked);
|
||||
}catch (IOException x){}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
stopIoManager();
|
||||
if (sPort != null) {
|
||||
try {
|
||||
sPort.close();
|
||||
} catch (IOException e) {
|
||||
// Ignore.
|
||||
}
|
||||
sPort = null;
|
||||
}
|
||||
finish();
|
||||
}
|
||||
|
||||
void showStatus(TextView theTextView, String theLabel, boolean theValue){
|
||||
String msg = theLabel + ": " + (theValue ? "enabled" : "disabled") + "\n";
|
||||
theTextView.append(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
Log.d(TAG, "Resumed, port=" + sPort);
|
||||
if (sPort == null) {
|
||||
mTitleTextView.setText("No serial device.");
|
||||
} else {
|
||||
final UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
|
||||
|
||||
UsbDeviceConnection connection = usbManager.openDevice(sPort.getDriver().getDevice());
|
||||
if (connection == null) {
|
||||
mTitleTextView.setText("Opening device failed");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
sPort.open(connection);
|
||||
sPort.setParameters(115200, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE);
|
||||
|
||||
showStatus(mDumpTextView, "CD - Carrier Detect", sPort.getCD());
|
||||
showStatus(mDumpTextView, "CTS - Clear To Send", sPort.getCTS());
|
||||
showStatus(mDumpTextView, "DSR - Data Set Ready", sPort.getDSR());
|
||||
showStatus(mDumpTextView, "DTR - Data Terminal Ready", sPort.getDTR());
|
||||
showStatus(mDumpTextView, "DSR - Data Set Ready", sPort.getDSR());
|
||||
showStatus(mDumpTextView, "RI - Ring Indicator", sPort.getRI());
|
||||
showStatus(mDumpTextView, "RTS - Request To Send", sPort.getRTS());
|
||||
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Error setting up device: " + e.getMessage(), e);
|
||||
mTitleTextView.setText("Error opening device: " + e.getMessage());
|
||||
try {
|
||||
sPort.close();
|
||||
} catch (IOException e2) {
|
||||
// Ignore.
|
||||
}
|
||||
sPort = null;
|
||||
return;
|
||||
}
|
||||
mTitleTextView.setText("Serial device: " + sPort.getClass().getSimpleName());
|
||||
}
|
||||
onDeviceStateChange();
|
||||
}
|
||||
|
||||
private void stopIoManager() {
|
||||
if (mSerialIoManager != null) {
|
||||
Log.i(TAG, "Stopping io manager ..");
|
||||
mSerialIoManager.stop();
|
||||
mSerialIoManager = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void startIoManager() {
|
||||
if (sPort != null) {
|
||||
Log.i(TAG, "Starting io manager ..");
|
||||
mSerialIoManager = new SerialInputOutputManager(sPort, mListener);
|
||||
mExecutor.submit(mSerialIoManager);
|
||||
}
|
||||
}
|
||||
|
||||
private void onDeviceStateChange() {
|
||||
stopIoManager();
|
||||
startIoManager();
|
||||
}
|
||||
|
||||
private void updateReceivedData(byte[] data) {
|
||||
final String message = "Read " + data.length + " bytes: \n"
|
||||
+ HexDump.dumpHexString(data) + "\n\n";
|
||||
mDumpTextView.append(message);
|
||||
mScrollView.smoothScrollTo(0, mDumpTextView.getBottom());
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the activity, using the supplied driver instance.
|
||||
*
|
||||
* @param context
|
||||
* @param driver
|
||||
*/
|
||||
static void show(Context context, UsbSerialPort port) {
|
||||
sPort = port;
|
||||
final Intent intent = new Intent(context, SerialConsoleActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NO_HISTORY);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,315 @@
|
|||
package com.hoho.android.usbserial.examples;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.hardware.usb.UsbDevice;
|
||||
import android.hardware.usb.UsbDeviceConnection;
|
||||
import android.hardware.usb.UsbManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.method.ScrollingMovementMethod;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import android.widget.ToggleButton;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.hoho.android.usbserial.driver.UsbSerialDriver;
|
||||
import com.hoho.android.usbserial.driver.UsbSerialPort;
|
||||
import com.hoho.android.usbserial.driver.UsbSerialProber;
|
||||
import com.hoho.android.usbserial.util.HexDump;
|
||||
import com.hoho.android.usbserial.util.SerialInputOutputManager;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class TerminalFragment extends Fragment implements SerialInputOutputManager.Listener {
|
||||
|
||||
private enum UsbPermission { Unknown, Requested, Granted, Denied };
|
||||
|
||||
private static final String INTENT_ACTION_GRANT_USB = BuildConfig.APPLICATION_ID + ".GRANT_USB";
|
||||
private static final int WRITE_WAIT_MILLIS = 2000;
|
||||
|
||||
private int deviceId, portNum, baudRate;
|
||||
|
||||
private BroadcastReceiver broadcastReceiver;
|
||||
private Handler mainLooper;
|
||||
private TextView receiveText;
|
||||
private ControlLines controlLines;
|
||||
|
||||
private SerialInputOutputManager usbIoManager;
|
||||
private UsbSerialPort usbSerialPort;
|
||||
private UsbPermission usbPermission = UsbPermission.Unknown;
|
||||
private boolean connected = false;
|
||||
|
||||
public TerminalFragment() {
|
||||
broadcastReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if(intent.getAction().equals(INTENT_ACTION_GRANT_USB)) {
|
||||
usbPermission = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)
|
||||
? UsbPermission.Granted : UsbPermission.Denied;
|
||||
connect();
|
||||
}
|
||||
}
|
||||
};
|
||||
mainLooper = new Handler(Looper.getMainLooper());
|
||||
}
|
||||
|
||||
/*
|
||||
* Lifecycle
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setHasOptionsMenu(true);
|
||||
setRetainInstance(true);
|
||||
deviceId = getArguments().getInt("device");
|
||||
portNum = getArguments().getInt("port");
|
||||
baudRate = getArguments().getInt("baud");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
getActivity().registerReceiver(broadcastReceiver, new IntentFilter(INTENT_ACTION_GRANT_USB));
|
||||
|
||||
if(usbPermission == UsbPermission.Unknown || usbPermission == UsbPermission.Granted)
|
||||
mainLooper.post(this::connect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
if(connected) {
|
||||
status("disconnected");
|
||||
disconnect();
|
||||
}
|
||||
getActivity().unregisterReceiver(broadcastReceiver);
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
/*
|
||||
* UI
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_terminal, container, false);
|
||||
receiveText = view.findViewById(R.id.receive_text); // TextView performance decreases with number of spans
|
||||
receiveText.setTextColor(getResources().getColor(R.color.colorRecieveText)); // set as default color to reduce number of spans
|
||||
receiveText.setMovementMethod(ScrollingMovementMethod.getInstance());
|
||||
TextView sendText = view.findViewById(R.id.send_text);
|
||||
View sendBtn = view.findViewById(R.id.send_btn);
|
||||
sendBtn.setOnClickListener(v -> send(sendText.getText().toString()));
|
||||
controlLines = new ControlLines(view);
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.menu_terminal, menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
if (id == R.id.clear) {
|
||||
receiveText.setText("");
|
||||
return true;
|
||||
} else {
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Serial
|
||||
*/
|
||||
@Override
|
||||
public void onNewData(byte[] data) {
|
||||
mainLooper.post(() -> {
|
||||
receive(data);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRunError(Exception e) {
|
||||
mainLooper.post(() -> {
|
||||
status("connection lost: " + e.getMessage());
|
||||
disconnect();
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Serial + UI
|
||||
*/
|
||||
private void connect() {
|
||||
UsbDevice device = null;
|
||||
UsbManager usbManager = (UsbManager) getActivity().getSystemService(Context.USB_SERVICE);
|
||||
for(UsbDevice v : usbManager.getDeviceList().values())
|
||||
if(v.getDeviceId() == deviceId)
|
||||
device = v;
|
||||
if(device == null) {
|
||||
status("connection failed: device not found");
|
||||
return;
|
||||
}
|
||||
UsbSerialDriver driver = UsbSerialProber.getDefaultProber().probeDevice(device);
|
||||
if(driver == null) {
|
||||
driver = CustomProber.getCustomProber().probeDevice(device);
|
||||
}
|
||||
if(driver == null) {
|
||||
status("connection failed: no driver for device");
|
||||
return;
|
||||
}
|
||||
if(driver.getPorts().size() < portNum) {
|
||||
status("connection failed: not enough ports at device");
|
||||
return;
|
||||
}
|
||||
usbSerialPort = driver.getPorts().get(portNum);
|
||||
UsbDeviceConnection usbConnection = usbManager.openDevice(driver.getDevice());
|
||||
if(usbConnection == null && usbPermission == UsbPermission.Unknown && !usbManager.hasPermission(driver.getDevice())) {
|
||||
usbPermission = UsbPermission.Requested;
|
||||
PendingIntent usbPermissionIntent = PendingIntent.getBroadcast(getActivity(), 0, new Intent(INTENT_ACTION_GRANT_USB), 0);
|
||||
usbManager.requestPermission(driver.getDevice(), usbPermissionIntent);
|
||||
return;
|
||||
}
|
||||
if(usbConnection == null) {
|
||||
if (!usbManager.hasPermission(driver.getDevice()))
|
||||
status("connection failed: permission denied");
|
||||
else
|
||||
status("connection failed: open failed");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
usbSerialPort.open(usbConnection);
|
||||
usbSerialPort.setParameters(baudRate, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE);
|
||||
usbIoManager = new SerialInputOutputManager(usbSerialPort, this);
|
||||
Executors.newSingleThreadExecutor().submit(usbIoManager);
|
||||
status("connected");
|
||||
connected = true;
|
||||
controlLines.start();
|
||||
} catch (Exception e) {
|
||||
status("connection failed: " + e.getMessage());
|
||||
disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
private void disconnect() {
|
||||
connected = false;
|
||||
controlLines.stop();
|
||||
if(usbIoManager != null)
|
||||
usbIoManager.stop();
|
||||
usbIoManager = null;
|
||||
try {
|
||||
usbSerialPort.close();
|
||||
} catch (IOException ignored) {}
|
||||
usbSerialPort = null;
|
||||
}
|
||||
|
||||
private void send(String str) {
|
||||
if(!connected) {
|
||||
Toast.makeText(getActivity(), "not connected", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
byte[] data = (str + '\n').getBytes();
|
||||
SpannableStringBuilder spn = new SpannableStringBuilder();
|
||||
spn.append("send " + data.length + " bytes:\n");
|
||||
spn.append(HexDump.dumpHexString(data)+"\n");
|
||||
spn.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.colorSendText)), 0, spn.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
receiveText.append(spn);
|
||||
usbSerialPort.write(data, WRITE_WAIT_MILLIS);
|
||||
} catch (Exception e) {
|
||||
onRunError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void receive(byte[] data) {
|
||||
String str = new String(data);
|
||||
SpannableStringBuilder spn = new SpannableStringBuilder();
|
||||
spn.append("receive " + data.length + " bytes:\n");
|
||||
spn.append(HexDump.dumpHexString(data)+"\n");
|
||||
receiveText.append(spn);
|
||||
}
|
||||
|
||||
void status(String str) {
|
||||
SpannableStringBuilder spn = new SpannableStringBuilder(str+'\n');
|
||||
spn.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.colorStatusText)), 0, spn.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
receiveText.append(spn);
|
||||
}
|
||||
|
||||
class ControlLines {
|
||||
private static final int refreshInterval = 200; // msec
|
||||
|
||||
private Runnable runnable;
|
||||
private ToggleButton rtsBtn, ctsBtn, dtrBtn, dsrBtn, cdBtn, riBtn;
|
||||
|
||||
ControlLines(View view) {
|
||||
runnable = this::start; // w/o explicit Runnable, a new lambda would be created on each postDelayed, which would not be found again by removeCallbacks
|
||||
|
||||
rtsBtn = view.findViewById(R.id.controlLineRts);
|
||||
ctsBtn = view.findViewById(R.id.controlLineCts);
|
||||
dtrBtn = view.findViewById(R.id.controlLineDtr);
|
||||
dsrBtn = view.findViewById(R.id.controlLineDsr);
|
||||
cdBtn = view.findViewById(R.id.controlLineCd);
|
||||
riBtn = view.findViewById(R.id.controlLineRi);
|
||||
rtsBtn.setOnClickListener(this::toggle);
|
||||
dtrBtn.setOnClickListener(this::toggle);
|
||||
}
|
||||
|
||||
private void toggle(View v) {
|
||||
ToggleButton btn = (ToggleButton) v;
|
||||
if (!connected) {
|
||||
btn.setChecked(!btn.isChecked());
|
||||
Toast.makeText(getActivity(), "not connected", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
String ctrl = "";
|
||||
try {
|
||||
if (btn.equals(rtsBtn)) { ctrl = "RTS"; usbSerialPort.setRTS(btn.isChecked()); }
|
||||
if (btn.equals(dtrBtn)) { ctrl = "DTR"; usbSerialPort.setDTR(btn.isChecked()); }
|
||||
} catch (IOException e) {
|
||||
status("set" + ctrl + " failed: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean refresh() {
|
||||
String ctrl = "";
|
||||
try {
|
||||
ctrl = "RTS"; rtsBtn.setChecked(usbSerialPort.getRTS());
|
||||
ctrl = "CTS"; ctsBtn.setChecked(usbSerialPort.getCTS());
|
||||
ctrl = "DTR"; dtrBtn.setChecked(usbSerialPort.getDTR());
|
||||
ctrl = "DSR"; dsrBtn.setChecked(usbSerialPort.getDSR());
|
||||
ctrl = "CD"; cdBtn.setChecked(usbSerialPort.getCD());
|
||||
ctrl = "RI"; riBtn.setChecked(usbSerialPort.getRI());
|
||||
} catch (IOException e) {
|
||||
status("get" + ctrl + " failed: " + e.getMessage() + " -> stopped control line refresh");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void start() {
|
||||
if (connected && refresh())
|
||||
mainLooper.postDelayed(runnable, refreshInterval);
|
||||
}
|
||||
|
||||
void stop() {
|
||||
mainLooper.removeCallbacks(runnable);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package com.hoho.android.usbserial.util;
|
||||
|
||||
import java.security.InvalidParameterException;
|
||||
|
||||
/**
|
||||
* Clone of Android's HexDump class, for use in debugging. Cosmetic changes
|
||||
* only.
|
||||
|
@ -32,17 +34,12 @@ public class HexDump {
|
|||
public static String dumpHexString(byte[] array, int offset, int length) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
byte[] line = new byte[16];
|
||||
byte[] line = new byte[8];
|
||||
int lineIndex = 0;
|
||||
|
||||
result.append("\n0x");
|
||||
result.append(toHexString(offset));
|
||||
|
||||
for (int i = offset; i < offset + length; i++) {
|
||||
if (lineIndex == 16) {
|
||||
result.append(" ");
|
||||
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (lineIndex == line.length) {
|
||||
for (int j = 0; j < line.length; j++) {
|
||||
if (line[j] > ' ' && line[j] < '~') {
|
||||
result.append(new String(line, j, 1));
|
||||
} else {
|
||||
|
@ -50,32 +47,26 @@ public class HexDump {
|
|||
}
|
||||
}
|
||||
|
||||
result.append("\n0x");
|
||||
result.append(toHexString(i));
|
||||
result.append("\n");
|
||||
lineIndex = 0;
|
||||
}
|
||||
|
||||
byte b = array[i];
|
||||
result.append(" ");
|
||||
result.append(HEX_DIGITS[(b >>> 4) & 0x0F]);
|
||||
result.append(HEX_DIGITS[b & 0x0F]);
|
||||
result.append(" ");
|
||||
|
||||
line[lineIndex++] = b;
|
||||
}
|
||||
|
||||
if (lineIndex != 16) {
|
||||
int count = (16 - lineIndex) * 3;
|
||||
count++;
|
||||
for (int i = 0; i < count; i++) {
|
||||
result.append(" ");
|
||||
}
|
||||
|
||||
for (int i = 0; i < lineIndex; i++) {
|
||||
if (line[i] > ' ' && line[i] < '~') {
|
||||
result.append(new String(line, i, 1));
|
||||
} else {
|
||||
result.append(".");
|
||||
}
|
||||
for (int i = 0; i < (line.length - lineIndex); i++) {
|
||||
result.append(" ");
|
||||
}
|
||||
for (int i = 0; i < lineIndex; i++) {
|
||||
if (line[i] > ' ' && line[i] < '~') {
|
||||
result.append(new String(line, i, 1));
|
||||
} else {
|
||||
result.append(".");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,7 +136,7 @@ public class HexDump {
|
|||
if (c >= 'a' && c <= 'f')
|
||||
return (c - 'a' + 10);
|
||||
|
||||
throw new RuntimeException("Invalid hex char '" + c + "'");
|
||||
throw new InvalidParameterException("Invalid hex char '" + c + "'");
|
||||
}
|
||||
|
||||
public static byte[] hexStringToByteArray(String hexString) {
|
||||
|
|
Before Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 2.5 KiB |
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
|
||||
</vector>
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M2.01,21L23,12 2.01,3 2,10l15,2 -15,2z"/>
|
||||
</vector>
|
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".MainActivity">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/fragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?android:attr/listDivider"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/devices"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
|
||||
</LinearLayout>
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text1"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text2"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small" />
|
||||
|
||||
</LinearLayout>
|
|
@ -0,0 +1,122 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ToggleButton
|
||||
android:id="@+id/controlLineRts"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="48sp"
|
||||
android:textOff="RTS"
|
||||
android:textOn="RTS" />
|
||||
|
||||
<ToggleButton
|
||||
android:id="@+id/controlLineCts"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="48sp"
|
||||
android:clickable="false"
|
||||
android:textColor="@android:color/secondary_text_dark"
|
||||
android:textOff="CTS"
|
||||
android:textOn="CTS" />
|
||||
|
||||
<View
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="6dp" />
|
||||
|
||||
<ToggleButton
|
||||
android:id="@+id/controlLineDtr"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="48sp"
|
||||
android:textOff="DTR"
|
||||
android:textOn="DTR" />
|
||||
|
||||
<ToggleButton
|
||||
android:id="@+id/controlLineDsr"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:clickable="false"
|
||||
android:minWidth="48sp"
|
||||
android:textColor="@android:color/secondary_text_dark"
|
||||
android:textOff="DSR"
|
||||
android:textOn="DSR" />
|
||||
|
||||
<View
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="6dp" />
|
||||
|
||||
<ToggleButton
|
||||
android:id="@+id/controlLineCd"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:clickable="false"
|
||||
android:minWidth="48sp"
|
||||
android:textColor="@android:color/secondary_text_dark"
|
||||
android:textOff="CD"
|
||||
android:textOn="CD" />
|
||||
|
||||
<ToggleButton
|
||||
android:id="@+id/controlLineRi"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="48sp"
|
||||
android:clickable="false"
|
||||
android:textColor="@android:color/secondary_text_dark"
|
||||
android:textOff="RI"
|
||||
android:textOn="RI" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:background="?android:attr/listDivider"
|
||||
android:layout_height="2dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/receive_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:freezesText="true"
|
||||
android:gravity="bottom"
|
||||
android:scrollbars="vertical"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||
android:typeface="monospace" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:background="?android:attr/listDivider"
|
||||
android:layout_height="2dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/send_text"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:inputType="text|textNoSuggestions"
|
||||
android:singleLine="true" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/send_btn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:srcCompat="@drawable/ic_send_white_24dp" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
|
||||
<TextView
|
||||
android:id="@+id/demoTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:text="@string/app_title"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/progressBarTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/demoTitle"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:text="@string/refreshing"
|
||||
android:padding="8dp"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/progressBarTitle"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:padding="8dp"
|
||||
style="@android:style/Widget.Holo.ProgressBar.Horizontal"
|
||||
android:indeterminate="true" />
|
||||
|
||||
<View
|
||||
android:id="@+id/separator"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dip"
|
||||
android:layout_below="@+id/progressBar"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:background="#eeeeee" />
|
||||
|
||||
<ListView
|
||||
android:id="@+id/deviceList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/separator" />
|
||||
|
||||
</RelativeLayout>
|
|
@ -1,54 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
<TextView
|
||||
android:id="@+id/demoTitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:text="@string/app_title"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<View
|
||||
android:id="@+id/separator"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dip"
|
||||
android:background="#eeeeee" />
|
||||
|
||||
<CheckBox
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/textBtnDTR"
|
||||
android:id="@+id/checkBoxDTR" />
|
||||
|
||||
|
||||
<CheckBox
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/textBtnRTS"
|
||||
android:id="@+id/checkBoxRTS" />
|
||||
|
||||
<View
|
||||
android:id="@+id/separator2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_below="@+id/demoTitle"
|
||||
android:layout_height="1dip"
|
||||
android:background="#eeeeee" />
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/demoScroller"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/consoleText"
|
||||
android:textIsSelectable="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:typeface="monospace" />
|
||||
</ScrollView>
|
||||
</LinearLayout>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:id="@+id/refresh"
|
||||
android:title="Refresh devices" />
|
||||
<item
|
||||
android:id="@+id/baud_rate"
|
||||
android:title="Baud rate" />
|
||||
</menu>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:id="@+id/clear"
|
||||
android:icon="@drawable/ic_delete_white_24dp"
|
||||
android:title="Clear"
|
||||
app:showAsAction="always" />
|
||||
</menu>
|
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 895 B |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 3.3 KiB |
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string-array name="baud_rates">
|
||||
<item>2400</item>
|
||||
<item>9600</item>
|
||||
<item>19200</item>
|
||||
<item>57600</item>
|
||||
<item>115200</item>
|
||||
</string-array>
|
||||
</resources>
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#949C29</color>
|
||||
<color name="colorPrimaryDark">#61671B</color>
|
||||
<color name="colorAccent">#D8E33B</color>
|
||||
|
||||
<color name="colorRecieveText">#00FF00</color>
|
||||
<color name="colorSendText">#82CAFF</color>
|
||||
<color name="colorStatusText">#FFDB58</color>
|
||||
</resources>
|
|
@ -1,10 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string name="app_title">USB Serial Example</string>
|
||||
<string name="app_name">Serial Example</string>
|
||||
<string name="refreshing">Refreshing...</string>
|
||||
<string name="textBtnRTS">RTS - Request To Send</string>
|
||||
<string name="textBtnDTR">DTR - Data Terminal Ready</string>
|
||||
|
||||
<string name="app_title">USB Serial For Android Example</string>
|
||||
<string name="app_name">USB Serial Example</string>
|
||||
<string name="devices">USB Devices</string>
|
||||
</resources>
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
<resources>
|
||||
<style name="AppTheme" parent="Theme.AppCompat.NoActionBar">
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
</style>
|
||||
</resources>
|