codec2_talkie/codec2talkie/src/main/java/com/radio/codec2talkie/MainActivity.java

846 wiersze
34 KiB
Java

package com.radio.codec2talkie;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.view.MenuCompat;
import androidx.preference.PreferenceManager;
import android.Manifest;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Typeface;
import android.hardware.usb.UsbManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.SystemClock;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.radio.codec2talkie.app.AppMessage;
import com.radio.codec2talkie.app.AppService;
import com.radio.codec2talkie.app.AppWorker;
import com.radio.codec2talkie.connect.BleConnectActivity;
import com.radio.codec2talkie.connect.BluetoothConnectActivity;
import com.radio.codec2talkie.connect.BluetoothSocketHandler;
import com.radio.codec2talkie.connect.TcpIpConnectActivity;
import com.radio.codec2talkie.maps.MapActivity;
import com.radio.codec2talkie.settings.SettingsWrapper;
import com.radio.codec2talkie.storage.log.LogItemActivity;
import com.radio.codec2talkie.protocol.ProtocolFactory;
import com.radio.codec2talkie.recorder.RecorderActivity;
import com.radio.codec2talkie.settings.PreferenceKeys;
import com.radio.codec2talkie.settings.SettingsActivity;
import com.radio.codec2talkie.storage.message.group.MessageGroupActivity;
import com.radio.codec2talkie.tools.AudioTools;
import com.radio.codec2talkie.tools.RadioTools;
import com.radio.codec2talkie.transport.TransportFactory;
import com.radio.codec2talkie.connect.UsbConnectActivity;
import com.radio.codec2talkie.connect.UsbPortHandler;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
public class MainActivity extends AppCompatActivity implements ServiceConnection {
private static final String TAG = MainActivity.class.getSimpleName();
private final static int REQUEST_PERMISSIONS = 1;
// S9 level at -93 dBm as per VHF Managers Handbook
private final static int S_METER_S0_VALUE_DB = -147;
// from S0 up to S9+40
private final static int S_METER_RANGE_DB = 100;
private final static long BACK_EXIT_MS_DELAY = 2000;
private final String[] _requiredPermissions = new String[] {
Manifest.permission.BLUETOOTH,
Manifest.permission.BLUETOOTH_ADMIN,
Manifest.permission.RECORD_AUDIO,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
private AppService _appService;
private SharedPreferences _sharedPreferences;
private boolean _isTestMode;
private boolean _isBleEnabled;
private TextView _textConnInfo;
private TextView _textStatus;
private TextView _textTelemetry;
private TextView _textCodecMode;
private TextView _textRssi;
private ProgressBar _progressAudioLevel;
private ProgressBar _progressRssi;
private ImageButton _btnPtt;
private Menu _menu;
public static boolean isPaused = false;
private boolean _isConnecting = false;
private boolean _isAppExit = false;
private boolean _isAppRestart = false;
private boolean _isRigCtlUsbConnected = false;
private long _backPressedTimestamp;
@SuppressLint("ClickableViewAccessibility")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "onCreate()");
// title
String appName = getResources().getString(R.string.app_name);
setTitle(appName + " v" + BuildConfig.VERSION_NAME);
_sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
setContentView(R.layout.activity_main);
_textConnInfo = findViewById(R.id.textBtName);
_textStatus = findViewById(R.id.textStatus);
_textTelemetry = findViewById(R.id.textTelemetry);
_textRssi = findViewById(R.id.textRssi);
// UV bar
int barMaxValue = AppWorker.getAudioMaxLevel() - AppWorker.getAudioMinLevel();
_progressAudioLevel = findViewById(R.id.progressAudioLevel);
_progressAudioLevel.setMax(barMaxValue);
_progressAudioLevel.getProgressDrawable().setColorFilter(
new PorterDuffColorFilter(AudioTools.colorFromAudioLevel(AppWorker.getAudioMinLevel()), PorterDuff.Mode.SRC_IN));
// S-meter
_progressRssi = findViewById(R.id.progressRssi);
_progressRssi.setMax(S_METER_RANGE_DB);
_progressRssi.getProgressDrawable().setColorFilter(
new PorterDuffColorFilter(Color.GRAY, PorterDuff.Mode.SRC_IN));
// PTT button
_btnPtt = findViewById(R.id.btnPtt);
_btnPtt.setEnabled(false);
_btnPtt.setOnTouchListener(onBtnPttTouchListener);
_textCodecMode = findViewById(R.id.codecMode);
// BT/USB disconnects
registerReceiver(onBluetoothDisconnected, new IntentFilter(BluetoothDevice.ACTION_ACL_DISCONNECTED));
registerReceiver(onUsbDetached, new IntentFilter(UsbManager.ACTION_USB_DEVICE_DETACHED));
_isTestMode = SettingsWrapper.isLoopbackTransport(_sharedPreferences);
_isBleEnabled = SettingsWrapper.isBleTransport(_sharedPreferences);
// show/hide S-meter
FrameLayout frameRssi = findViewById(R.id.frameRssi);
if (SettingsWrapper.isFreeDvSoundModemModulation(_sharedPreferences)) {
frameRssi.setVisibility(View.VISIBLE);
} else if (SettingsWrapper.isKissExtensionEnabled(_sharedPreferences) && !SettingsWrapper.isSoundModemEnabled(_sharedPreferences)) {
int sLevelId = RadioTools.getMinimumDecodeSLevelLabel(_sharedPreferences, S_METER_S0_VALUE_DB);
TextView sLevel = findViewById(sLevelId);
if (sLevel != null) {
sLevel.setTypeface(null, Typeface.BOLD_ITALIC);
}
frameRssi.setVisibility(View.VISIBLE);
} else {
frameRssi.setVisibility(View.GONE);
}
// screen always on / auto turn screen on / run above app lock
if (_sharedPreferences.getBoolean(PreferenceKeys.APP_KEEP_SCREEN_ON, false)) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
if (_sharedPreferences.getBoolean(PreferenceKeys.APP_TURN_SCREEN_ON, false)) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
}
if (_sharedPreferences.getBoolean(PreferenceKeys.APP_NO_LOCK, false)) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
}
_isAppRestart = false;
_isAppExit = false;
startTransportConnection();
}
@Override
protected void onStart() {
super.onStart();
Log.i(TAG, "onStart()");
}
@Override
protected void onStop() {
super.onStop();
Log.i(TAG, "onStop()");
}
@Override
protected void onRestart() {
super.onRestart();
Log.i(TAG, "onRestart()");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.i(TAG, "onDestroy()");
unregisterReceiver(onBluetoothDisconnected);
unregisterReceiver(onUsbDetached);
}
@Override
protected void onPause() {
super.onPause();
Log.i(TAG, "onPause()");
isPaused = true;
}
@Override
protected void onResume() {
super.onResume();
Log.i(TAG, "onResume()");
isPaused = false;
if (!AppService.isRunning && !_isConnecting) {
_btnPtt.setEnabled(false);
startTransportConnection();
}
}
private void exitApplication() {
Log.i(TAG, "exitApplication()");
_isAppExit = true;
if (_appService != null) {
_appService.stopRunning();
}
}
private void restartApplication() {
Log.i(TAG, "restartApplication()");
_isAppRestart = true;
if (_appService != null) {
_appService.stopRunning();
}
}
private void startTransportConnection() {
Log.i(TAG, "startTransportConnection()");
if (AppService.isRunning) {
startAppService(AppService.transportType);
} else if (requestPermissions()) {
switch (SettingsWrapper.getCurrentTransportType(_sharedPreferences)) {
case LOOPBACK:
_textConnInfo.setText(R.string.main_status_loopback_test);
startAppService(TransportFactory.TransportType.LOOPBACK);
break;
case SOUND_MODEM:
_textConnInfo.setText(R.string.main_status_sound_modem);
// start sound modem without rig cat usb connection
if (SettingsWrapper.isSoundModemRigDisabled(_sharedPreferences))
startAppService(TransportFactory.TransportType.SOUND_MODEM);
// otherwise try to connect with usb for cat ptt
else
startUsbConnectActivity();
break;
case TCP_IP:
startTcpIpConnectActivity();
break;
case USB:
startUsbConnectActivity();
break;
case BLUETOOTH:
case BLE:
startBluetoothConnectActivity();
break;
}
}
}
private final ActivityResultLauncher<Intent> _usbActivityLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
Intent data = result.getData();
assert data != null;
int resultCode = result.getResultCode();
if (SettingsWrapper.isSoundModemEnabled(_sharedPreferences)) {
_isRigCtlUsbConnected = resultCode == RESULT_OK;
startAppService(TransportFactory.TransportType.SOUND_MODEM);
} else if (resultCode == RESULT_CANCELED) {
_textConnInfo.setText(R.string.main_status_loopback_test);
startAppService(TransportFactory.TransportType.LOOPBACK);
} else if (resultCode == RESULT_OK) {
_textConnInfo.setText(data.getStringExtra("name"));
startAppService(TransportFactory.TransportType.USB);
}
}
);
protected void startUsbConnectActivity() {
_isConnecting = true;
_isRigCtlUsbConnected = false;
_usbActivityLauncher.launch(new Intent(this, UsbConnectActivity.class));
}
private final ActivityResultLauncher<Intent> _bluetoothActivityLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
Intent data = result.getData();
assert data != null;
int resultCode = result.getResultCode();
if (resultCode == RESULT_CANCELED) {
// fall back to loopback if bluetooth failed
_textConnInfo.setText(R.string.main_status_loopback_test);
startAppService(TransportFactory.TransportType.LOOPBACK);
} else if (resultCode == RESULT_OK) {
_textConnInfo.setText(data.getStringExtra("name"));
if (_isBleEnabled) {
startAppService(TransportFactory.TransportType.BLE);
} else {
startAppService(TransportFactory.TransportType.BLUETOOTH);
}
}
}
);
protected void startBluetoothConnectActivity() {
_isConnecting = true;
Intent bluetoothConnectIntent;
if (_isBleEnabled) {
bluetoothConnectIntent = new Intent(this, BleConnectActivity.class);
} else {
bluetoothConnectIntent = new Intent(this, BluetoothConnectActivity.class);
}
_bluetoothActivityLauncher.launch(bluetoothConnectIntent);
}
private final ActivityResultLauncher<Intent> _tcpipActivityLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
Intent data = result.getData();
assert data != null;
int resultCode = result.getResultCode();
if (resultCode == RESULT_CANCELED) {
// fall back to loopback if tcp/ip failed
_textConnInfo.setText(R.string.main_status_loopback_test);
startAppService(TransportFactory.TransportType.LOOPBACK);
} else if (resultCode == RESULT_OK) {
_textConnInfo.setText(data.getStringExtra("name"));
startAppService(TransportFactory.TransportType.TCP_IP);
}
}
);
protected void startTcpIpConnectActivity() {
_isConnecting = true;
_tcpipActivityLauncher.launch(new Intent(this, TcpIpConnectActivity.class));
}
private final ActivityResultLauncher<Intent> _recorderActivityLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(), result -> { });
protected void startRecorderActivity() {
_recorderActivityLauncher.launch(new Intent(this, RecorderActivity.class));
}
private final ActivityResultLauncher<Intent> _logViewActivityLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(), result -> { });
protected void startLogViewActivity() {
_logViewActivityLauncher.launch(new Intent(this, LogItemActivity.class));
}
protected void startMapViewActivity() {
_logViewActivityLauncher.launch(new Intent(this, MapActivity.class));
}
private final ActivityResultLauncher<Intent> _settingsActivityLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(), result -> restartApplication());
protected void startSettingsActivity() {
_settingsActivityLauncher.launch(new Intent(this, SettingsActivity.class));
}
private final ActivityResultLauncher<Intent> _messagesActivityLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(), result -> {});
protected void startMessagesActivity() {
_messagesActivityLauncher.launch(new Intent(this, MessageGroupActivity.class));
}
protected boolean requestPermissions() {
List<String> permissionsToRequest = new LinkedList<>();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
String permission = Manifest.permission.FOREGROUND_SERVICE;
if (ContextCompat.checkSelfPermission(MainActivity.this, permission) == PackageManager.PERMISSION_DENIED) {
permissionsToRequest.add(permission);
}
}
for (String permission : _requiredPermissions) {
if (ContextCompat.checkSelfPermission(MainActivity.this, permission) == PackageManager.PERMISSION_DENIED) {
permissionsToRequest.add(permission);
}
}
if (permissionsToRequest.size() > 0) {
ActivityCompat.requestPermissions(
MainActivity.this,
permissionsToRequest.toArray(new String[0]),
REQUEST_PERMISSIONS);
return false;
}
return true;
}
private void bindAppService() {
if (!bindService(new Intent(this, AppService.class), this, Context.BIND_AUTO_CREATE)) {
Log.e(TAG, "Service does not exists or no access");
}
}
private void unbindAppService() {
unbindService(this);
}
private void startAppService(TransportFactory.TransportType transportType) {
Log.i(TAG, "Starting app service processing: " + transportType.toString());
ProtocolFactory.ProtocolType protocolType = ProtocolFactory.getBaseProtocolType(getApplicationContext());
_btnPtt.setEnabled(protocolType != ProtocolFactory.ProtocolType.KISS_PARROT);
updateStatusText(protocolType);
Intent serviceIntent = new Intent(this, AppService.class);
serviceIntent.putExtra("transportType", transportType);
serviceIntent.putExtra("callback", new Messenger(onAppServiceStateChanged));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
startForegroundService(serviceIntent);
else
startService(serviceIntent);
bindAppService();
}
private void stopAppService() {
stopService(new Intent(this, AppService.class));
}
private void updateStatusText(ProtocolFactory.ProtocolType protocolType) {
// protocol
String status = "";
// recording
boolean recordingEnabled = _sharedPreferences.getBoolean(PreferenceKeys.CODEC2_RECORDING_ENABLED, false);
if (recordingEnabled) {
status += getString(R.string.recorder_status_label);
}
// scrambling
boolean scramblingEnabled = _sharedPreferences.getBoolean(PreferenceKeys.KISS_SCRAMBLING_ENABLED, false);
if (scramblingEnabled) {
status += getString(R.string.kiss_scrambler_label);
}
// rig CAT control
String rigName = _sharedPreferences.getString(PreferenceKeys.PORTS_SOUND_MODEM_RIG, "Disabled");
if (!rigName.equals("Disabled") && _isRigCtlUsbConnected) {
status += getString(R.string.ports_sound_modem_rig_label);
}
// aprs
boolean aprsEnabled = SettingsWrapper.isAprsEnabled(_sharedPreferences);
if (aprsEnabled) {
status += getString(R.string.aprs_label);
// VoAX25
boolean voax25Enabled = SettingsWrapper.isVoax25Enabled(_sharedPreferences);
if (voax25Enabled) {
status += getString(R.string.voax25_label);
}
// Lora aprs text packets
boolean textPacketsEnabled = SettingsWrapper.isTextPacketsEnabled(_sharedPreferences);
if (textPacketsEnabled) {
status += getString(R.string.text_packets_label);
}
// Digirepeater
boolean isDigirepeaterEnabled = _sharedPreferences.getBoolean(PreferenceKeys.AX25_DIGIREPEATER_ENABLED, false);
if (isDigirepeaterEnabled) {
status += getString(R.string.digirepeater_label);
}
// APRSIS
boolean aprsisEnabled = SettingsWrapper.isAprsIsEnabled(_sharedPreferences);
if (aprsisEnabled) {
status += getString(R.string.aprsis_label);
}
}
if (_appService != null) {
boolean isTracking = _appService.isTracking();
if (isTracking) {
status += getString(R.string.tracking_label);
}
}
status = status.length() == 0 ? protocolType.toString() : protocolType.toString() + " " + status;
String statusLine = AudioTools.getSpeedStatusText(_sharedPreferences, getResources()) + ", " + status;
_textCodecMode.setText(statusLine);
}
private final BroadcastReceiver onBluetoothDisconnected = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (_appService != null && BluetoothSocketHandler.getSocket() != null && !_isTestMode) {
Toast.makeText(MainActivity.this, R.string.bt_disconnected, Toast.LENGTH_LONG).show();
_appService.stopRunning();
}
}
};
private final BroadcastReceiver onUsbDetached = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (_appService != null && UsbPortHandler.getPort() != null && !_isTestMode) {
Toast.makeText(MainActivity.this, R.string.usb_detached, Toast.LENGTH_LONG).show();
_appService.stopRunning();
}
}
};
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
boolean isAprsEnabled = SettingsWrapper.isAprsEnabled(_sharedPreferences);
menu.setGroupVisible(R.id.group_aprs, isAprsEnabled);
return true;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuCompat.setGroupDividerEnabled(menu, true);
getMenuInflater().inflate(R.menu.main_menu, menu);
_menu = menu;
updateMenuItemsAndStatusText();
return true;
}
private void updateMenuItemsAndStatusText() {
if (AppService.isRunning & _menu != null && _appService != null) {
MenuItem item = _menu.findItem(R.id.start_tracking);
if (_appService.isTracking()) {
item.setTitle(R.string.menu_stop_tracking);
} else {
item.setTitle(R.string.menu_start_tracking);
}
updateStatusText(ProtocolFactory.getBaseProtocolType(getApplicationContext()));
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
int itemId = item.getItemId();
if (itemId == R.id.preferences) {
if (_appService != null && _appService.isTracking())
_appService.stopTracking();
startSettingsActivity();
return true;
}
if (itemId == R.id.recorder) {
startRecorderActivity();
return true;
}
if (itemId == R.id.reconnect) {
if (_appService != null) {
_appService.stopRunning();
}
return true;
}
else if (itemId == R.id.exit) {
exitApplication();
return true;
}
else if (itemId == R.id.send_position) {
_appService.sendPosition();
return true;
}
else if (itemId == R.id.start_tracking) {
if (_appService.isTracking()) {
_appService.stopTracking();
item.setTitle(R.string.menu_start_tracking);
} else {
_appService.startTracking();
item.setTitle(R.string.menu_stop_tracking);
}
return true;
}
else if (itemId == R.id.messages) {
startMessagesActivity();
return true;
}
else if (itemId == R.id.aprs_log) {
startLogViewActivity();
return true;
}
else if (itemId == R.id.aprs_map) {
startMapViewActivity();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
// headset hardware ptt cannot be used for long press
case KeyEvent.KEYCODE_HEADSETHOOK:
if (_btnPtt.isPressed()) {
_btnPtt.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(),
SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, 0, 0, 0));
} else {
_btnPtt.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(),
SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, 0, 0, 0));
}
return true;
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_UP:
if (_sharedPreferences.getBoolean(PreferenceKeys.APP_VOLUME_PTT, false)) {
_btnPtt.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(),
SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, 0, 0, 0));
return true;
}
break;
case KeyEvent.KEYCODE_TV_DATA_SERVICE:
_btnPtt.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(),
SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, 0, 0, 0));
return true;
}
return super.onKeyDown(keyCode, event);
}
@Override
public void onBackPressed() {
if (_backPressedTimestamp + BACK_EXIT_MS_DELAY > System.currentTimeMillis()) {
//super.onBackPressed();
exitApplication();
} else {
Toast.makeText(getBaseContext(), "Press back again to exit", Toast.LENGTH_SHORT).show();
}
_backPressedTimestamp = System.currentTimeMillis();
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_UP:
if (_sharedPreferences.getBoolean(PreferenceKeys.APP_VOLUME_PTT, false)) {
_btnPtt.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(),
SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, 0, 0, 0));
return true;
}
break;
case KeyEvent.KEYCODE_TV_DATA_SERVICE:
_btnPtt.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(),
SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, 0, 0, 0));
return true;
}
return super.onKeyUp(keyCode, event);
}
private final View.OnTouchListener onBtnPttTouchListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (_appService != null)
_appService.startTransmit();
break;
case MotionEvent.ACTION_UP:
v.performClick();
if (_appService != null)
_appService.startReceive();
break;
}
return false;
}
};
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_PERMISSIONS) {
boolean allGranted = true;
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
allGranted = false;
break;
}
}
if (allGranted) {
Toast.makeText(MainActivity.this, R.string.permissions_granted, Toast.LENGTH_SHORT).show();
startUsbConnectActivity();
} else {
Toast.makeText(MainActivity.this, R.string.permissions_denied, Toast.LENGTH_SHORT).show();
exitApplication();
}
}
}
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
Log.i(TAG, "Connected to app service");
_appService = ((AppService.AppServiceBinder)service).getService();
if (AppService.isRunning) {
_textConnInfo.setText(_appService.getTransportName());
updateMenuItemsAndStatusText();
}
}
@Override
public void onServiceDisconnected(ComponentName className) {
Log.i(TAG, "Disconnected from app service");
_appService = null;
}
private final Handler onAppServiceStateChanged = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (AppMessage.values()[msg.what]) {
case EV_CONNECTED:
Log.i(TAG, "EV_CONNECTED");
updateMenuItemsAndStatusText();
_isConnecting = false;
_btnPtt.setEnabled(true);
Toast.makeText(getBaseContext(), R.string.processor_connected, Toast.LENGTH_SHORT).show();
break;
case EV_DISCONNECTED:
Log.i(TAG, "EV_DISCONNECTED");
updateMenuItemsAndStatusText();
_btnPtt.setImageResource(R.drawable.btn_ptt_stop);
_btnPtt.setEnabled(false);
// app restart, stop app service and restart ourselves
if (_isAppRestart) {
Log.i(TAG, "App restart");
unbindAppService();
stopAppService();
finish();
startActivity(getIntent());
// app exit, stop app service and finish
} else if (_isAppExit) {
Log.i(TAG, "App exit");
unbindAppService();
stopAppService();
finish();
// otherwise just reconnect if app is not on pause
} else if (!isPaused) {
Log.i(TAG, "App restart transport");
Toast.makeText(getBaseContext(), R.string.processor_disconnected, Toast.LENGTH_SHORT).show();
startTransportConnection();
}
break;
case EV_LISTENING:
_btnPtt.setImageResource(R.drawable.btn_ptt_touch);
_textStatus.setText("");
break;
case EV_TRANSMITTED_VOICE:
if (msg.obj != null) {
_textStatus.setText((String) msg.obj);
}
_btnPtt.setImageResource(R.drawable.btn_ptt_mic);
break;
case EV_RECEIVING:
_btnPtt.setImageResource(R.drawable.btn_ptt_listen);
break;
case EV_TEXT_MESSAGE_RECEIVED:
case EV_DATA_RECEIVED:
case EV_POSITION_RECEIVED:
if (msg.obj != null) {
String note = (String)msg.obj;
_textStatus.setText(note.split(":")[0]);
}
_btnPtt.setImageResource(R.drawable.btn_ptt_letter);
break;
case EV_VOICE_RECEIVED:
if (msg.obj != null) {
_textStatus.setText((String) msg.obj);
}
//_btnPtt.setText(R.string.main_status_voice_received);
_btnPtt.setImageResource(R.drawable.btn_ptt_listen);
break;
case EV_RX_RADIO_LEVEL:
if (msg.arg1 == 0 && msg.arg2 == 0) {
_textRssi.setText("");
_progressRssi.getProgressDrawable().setColorFilter(new PorterDuffColorFilter(Color.GRAY, PorterDuff.Mode.SRC_IN));
_progressRssi.setProgress(0);
} else if (msg.arg1 == 0) {
double snr = (double)msg.arg2 / 100.0;
_textRssi.setText(String.format(Locale.getDefault(), "%2.2f", (double)msg.arg2 / 100.0));
_progressRssi.getProgressDrawable().setColorFilter(new PorterDuffColorFilter(Color.GREEN, PorterDuff.Mode.SRC_IN));
_progressRssi.setProgress((int) ((-110.0 + snr) - S_METER_S0_VALUE_DB));
} else {
_textRssi.setText(String.format(Locale.getDefault(), "%3d dBm, %2.2f", msg.arg1, (double)msg.arg2 / 100.0));
_progressRssi.getProgressDrawable().setColorFilter(new PorterDuffColorFilter(Color.GREEN, PorterDuff.Mode.SRC_IN));
_progressRssi.setProgress(msg.arg1 - S_METER_S0_VALUE_DB);
}
break;
case EV_TELEMETRY:
if (msg.arg1 > 0) {
// NOTE, reuse status indicator for voltage
_textTelemetry.setText(String.format(Locale.getDefault(), "%2.2fV", (double)msg.arg1 / 100.0));
}
break;
// same progress bar is reused for rx and tx levels
case EV_RX_LEVEL:
case EV_TX_LEVEL:
_progressAudioLevel.getProgressDrawable().setColorFilter(new PorterDuffColorFilter(AudioTools.colorFromAudioLevel(msg.arg1), PorterDuff.Mode.SRC_IN));
_progressAudioLevel.setProgress(msg.arg1 - AppWorker.getAudioMinLevel());
break;
case EV_RX_ERROR:
_btnPtt.setImageResource(R.drawable.btn_ptt_err_rx);
break;
case EV_TX_ERROR:
_btnPtt.setImageResource(R.drawable.btn_ptt_err_tx);
break;
case EV_STARTED_TRACKING:
case EV_STOPPED_TRACKING:
updateStatusText(ProtocolFactory.getBaseProtocolType(getApplicationContext()));
break;
}
}
};
}