kopia lustrzana https://github.com/sh123/codec2_talkie
commit
8e716d658b
|
@ -443,6 +443,12 @@ public class MainActivity extends AppCompatActivity {
|
|||
if (voax25Enabled) {
|
||||
status += getString(R.string.voax25_label);
|
||||
}
|
||||
|
||||
// Digirepeater
|
||||
boolean isDigirepeaterEnabled = _sharedPreferences.getBoolean(PreferenceKeys.APRS_DIGIREPEATER_ENABLED, false);
|
||||
if (isDigirepeaterEnabled) {
|
||||
status += getString(R.string.digirepeater_label);
|
||||
}
|
||||
}
|
||||
|
||||
if (_appService != null) {
|
||||
|
|
|
@ -16,11 +16,12 @@ public enum AppMessage {
|
|||
EV_STARTED_TRACKING(11),
|
||||
EV_STOPPED_TRACKING(12),
|
||||
// commands
|
||||
CMD_SEND_LOCATION(13),
|
||||
CMD_SEND_LOCATION_TO_TNC(13),
|
||||
CMD_PROCESS(14),
|
||||
CMD_QUIT(15),
|
||||
CMD_START_TRACKING(16),
|
||||
CMD_STOP_TRACKING(17);
|
||||
CMD_STOP_TRACKING(17),
|
||||
CMD_SEND_SINGLE_TRACKING(18);
|
||||
|
||||
private final int _value;
|
||||
|
||||
|
|
|
@ -80,7 +80,7 @@ public class AppService extends Service {
|
|||
|
||||
public void sendPosition() {
|
||||
Message msg = Message.obtain();
|
||||
msg.what = AppMessage.CMD_SEND_LOCATION.toInt();
|
||||
msg.what = AppMessage.CMD_SEND_SINGLE_TRACKING.toInt();
|
||||
_onProcess.sendMessage(msg);
|
||||
}
|
||||
|
||||
|
@ -146,7 +146,7 @@ public class AppService extends Service {
|
|||
|
||||
String trackerName = _sharedPreferences.getString(PreferenceKeys.APRS_LOCATION_SOURCE, "manual");
|
||||
_tracker = TrackerFactory.create(trackerName);
|
||||
_tracker.initialize(getApplicationContext(), position -> { if (_appWorker != null) _appWorker.sendPosition(position); });
|
||||
_tracker.initialize(getApplicationContext(), position -> { if (_appWorker != null) _appWorker.sendPositionToTnc(position); });
|
||||
|
||||
transportType = (TransportFactory.TransportType) extras.get("transportType");
|
||||
startAppWorker(transportType);
|
||||
|
@ -206,7 +206,7 @@ public class AppService extends Service {
|
|||
case EV_LISTENING:
|
||||
hideVoiceNotification();
|
||||
break;
|
||||
case CMD_SEND_LOCATION:
|
||||
case CMD_SEND_SINGLE_TRACKING:
|
||||
_tracker.sendPosition();
|
||||
break;
|
||||
case CMD_START_TRACKING:
|
||||
|
|
|
@ -21,16 +21,13 @@ import java.util.TimerTask;
|
|||
|
||||
import com.radio.codec2talkie.R;
|
||||
import com.radio.codec2talkie.log.LogItem;
|
||||
import com.radio.codec2talkie.log.LogItemDatabase;
|
||||
import com.radio.codec2talkie.log.LogItemRepository;
|
||||
import com.radio.codec2talkie.protocol.ProtocolCallback;
|
||||
import com.radio.codec2talkie.protocol.Protocol;
|
||||
import com.radio.codec2talkie.protocol.ProtocolFactory;
|
||||
import com.radio.codec2talkie.protocol.aprs.AprsCallsign;
|
||||
import com.radio.codec2talkie.protocol.position.Position;
|
||||
import com.radio.codec2talkie.settings.PreferenceKeys;
|
||||
import com.radio.codec2talkie.tools.AudioTools;
|
||||
import com.radio.codec2talkie.tools.DebugTools;
|
||||
import com.radio.codec2talkie.transport.Transport;
|
||||
import com.radio.codec2talkie.transport.TransportFactory;
|
||||
|
||||
|
@ -40,12 +37,12 @@ public class AppWorker extends Thread {
|
|||
|
||||
private static final int AUDIO_MIN_LEVEL = -70;
|
||||
private static final int AUDIO_MAX_LEVEL = 0;
|
||||
private final int AUDIO_SAMPLE_SIZE = 8000;
|
||||
private static final int AUDIO_SAMPLE_SIZE = 8000;
|
||||
|
||||
private final int PROCESS_INTERVAL_MS = 20;
|
||||
private final int LISTEN_AFTER_MS = 1500;
|
||||
private static final int PROCESS_INTERVAL_MS = 20;
|
||||
private static final int LISTEN_AFTER_MS = 1500;
|
||||
|
||||
private boolean _needsRecording = false;
|
||||
private boolean _needTransmission = false;
|
||||
private AppMessage _currentStatus = AppMessage.EV_DISCONNECTED;
|
||||
|
||||
private final Protocol _protocol;
|
||||
|
@ -150,11 +147,11 @@ public class AppWorker extends Thread {
|
|||
}
|
||||
|
||||
public void startReceive() {
|
||||
_needsRecording = false;
|
||||
_needTransmission = false;
|
||||
}
|
||||
|
||||
public void startTransmit() {
|
||||
_needsRecording = true;
|
||||
_needTransmission = true;
|
||||
}
|
||||
|
||||
public void stopRunning() {
|
||||
|
@ -165,10 +162,10 @@ public class AppWorker extends Thread {
|
|||
_onMessageReceived.sendMessage(msg);
|
||||
}
|
||||
|
||||
public void sendPosition(Position position) {
|
||||
public void sendPositionToTnc(Position position) {
|
||||
if (_currentStatus == AppMessage.EV_DISCONNECTED) return;
|
||||
Message msg = new Message();
|
||||
msg.what = AppMessage.CMD_SEND_LOCATION.toInt();
|
||||
msg.what = AppMessage.CMD_SEND_LOCATION_TO_TNC.toInt();
|
||||
msg.obj = position;
|
||||
_onMessageReceived.sendMessage(msg);
|
||||
}
|
||||
|
@ -341,13 +338,13 @@ public class AppWorker extends Thread {
|
|||
|
||||
private void processRecordPlaybackToggle() throws IOException {
|
||||
// playback -> recording
|
||||
if (_needsRecording && _systemAudioRecorder.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {
|
||||
if (_needTransmission && _systemAudioRecorder.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {
|
||||
_systemAudioPlayer.stop();
|
||||
_systemAudioRecorder.startRecording();
|
||||
sendRxAudioLevelUpdate(null);
|
||||
}
|
||||
// recording -> playback
|
||||
if (!_needsRecording && _systemAudioRecorder.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
|
||||
if (!_needTransmission && _systemAudioRecorder.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
|
||||
_protocol.flush();
|
||||
_systemAudioRecorder.stop();
|
||||
_systemAudioPlayer.play();
|
||||
|
@ -398,7 +395,7 @@ public class AppWorker extends Thread {
|
|||
Looper.myLooper().quitSafely();
|
||||
}
|
||||
|
||||
private void onProcessorIncomingMessage(Message msg) {
|
||||
private void onWorkerIncomingMessage(Message msg) {
|
||||
switch (AppMessage.values()[msg.what]) {
|
||||
case CMD_PROCESS:
|
||||
try {
|
||||
|
@ -411,7 +408,7 @@ public class AppWorker extends Thread {
|
|||
case CMD_QUIT:
|
||||
quitProcessing();
|
||||
break;
|
||||
case CMD_SEND_LOCATION:
|
||||
case CMD_SEND_LOCATION_TO_TNC:
|
||||
try {
|
||||
_protocol.sendPosition((Position)msg.obj);
|
||||
} catch (IOException e) {
|
||||
|
@ -424,11 +421,11 @@ public class AppWorker extends Thread {
|
|||
}
|
||||
}
|
||||
|
||||
private void startProcessorMessageHandler() {
|
||||
private void startWorkerMessageHandler() {
|
||||
_onMessageReceived = new Handler(Looper.myLooper()) {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
onProcessorIncomingMessage(msg);
|
||||
onWorkerIncomingMessage(msg);
|
||||
}
|
||||
};
|
||||
_processPeriodicTimer.schedule(new TimerTask() {
|
||||
|
@ -452,7 +449,7 @@ public class AppWorker extends Thread {
|
|||
|
||||
try {
|
||||
_protocol.initialize(_transport, _context, _protocolCallback);
|
||||
startProcessorMessageHandler();
|
||||
startWorkerMessageHandler();
|
||||
Looper.loop();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
|
|
|
@ -9,7 +9,6 @@ import androidx.preference.PreferenceManager;
|
|||
import com.radio.codec2talkie.protocol.ax25.AX25Packet;
|
||||
import com.radio.codec2talkie.protocol.position.Position;
|
||||
import com.radio.codec2talkie.settings.PreferenceKeys;
|
||||
import com.radio.codec2talkie.tools.DebugTools;
|
||||
import com.radio.codec2talkie.transport.Transport;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -21,6 +20,7 @@ public class Ax25 implements Protocol {
|
|||
final Protocol _childProtocol;
|
||||
private String _digipath;
|
||||
private boolean _isVoax25Enabled;
|
||||
private boolean _isDigiRepeaterEnabled;
|
||||
|
||||
private ProtocolCallback _parentProtocolCallback;
|
||||
|
||||
|
@ -36,6 +36,7 @@ public class Ax25 implements Protocol {
|
|||
// NOTE, may need to pass through sendData/sendAudio
|
||||
_digipath = sharedPreferences.getString(PreferenceKeys.APRS_DIGIPATH, "");
|
||||
_isVoax25Enabled = sharedPreferences.getBoolean(PreferenceKeys.APRS_VOAX25_ENABLE, false);
|
||||
_isDigiRepeaterEnabled = sharedPreferences.getBoolean(PreferenceKeys.APRS_DIGIREPEATER_ENABLED, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -55,7 +56,7 @@ public class Ax25 implements Protocol {
|
|||
ax25Packet.rawData = frame;
|
||||
byte[] ax25Frame = ax25Packet.toBinary();
|
||||
if (ax25Frame == null) {
|
||||
Log.e(TAG, "Invalid source data for AX.25");
|
||||
Log.e(TAG, "Cannot convert AX.25 voice packet to binary");
|
||||
_parentProtocolCallback.onProtocolTxError();
|
||||
} else {
|
||||
_childProtocol.sendCompressedAudio(src, dst, codec2Mode, ax25Frame);
|
||||
|
@ -81,11 +82,11 @@ public class Ax25 implements Protocol {
|
|||
ax25Packet.rawData = dataPacket;
|
||||
byte[] ax25Frame = ax25Packet.toBinary();
|
||||
if (ax25Frame == null) {
|
||||
Log.e(TAG, "Invalid source data for AX.25");
|
||||
Log.e(TAG, "Cannot convert AX.25 data packet to binary");
|
||||
_parentProtocolCallback.onProtocolTxError();
|
||||
} else {
|
||||
_childProtocol.sendData(src, dst, ax25Frame);
|
||||
_parentProtocolCallback.onTransmitLog(toDebugLogLine(ax25Packet));
|
||||
_parentProtocolCallback.onTransmitLog(ax25Packet.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -106,7 +107,7 @@ public class Ax25 implements Protocol {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void onReceiveCompressedAudio(String src, String dst, int codec2Mode, byte[] audioFrames) {
|
||||
protected void onReceiveCompressedAudio(String src, String dst, int codec2Mode, byte[] audioFrames) {
|
||||
AX25Packet ax25Data = new AX25Packet();
|
||||
ax25Data.fromBinary(audioFrames);
|
||||
if (ax25Data.isValid) {
|
||||
|
@ -114,7 +115,8 @@ public class Ax25 implements Protocol {
|
|||
_parentProtocolCallback.onReceiveCompressedAudio(ax25Data.src, ax25Data.dst, ax25Data.codec2Mode, ax25Data.rawData);
|
||||
} else {
|
||||
_parentProtocolCallback.onReceiveData(ax25Data.src, ax25Data.dst, ax25Data.rawData);
|
||||
_parentProtocolCallback.onReceiveLog(toDebugLogLine(ax25Data));
|
||||
_parentProtocolCallback.onReceiveLog(ax25Data.toString());
|
||||
if (_isDigiRepeaterEnabled) digiRepeat(ax25Data);
|
||||
}
|
||||
} else {
|
||||
// fallback to raw audio if ax25 frame is invalid
|
||||
|
@ -183,11 +185,22 @@ public class Ax25 implements Protocol {
|
|||
_childProtocol.close();
|
||||
}
|
||||
|
||||
private String toDebugLogLine(AX25Packet ax25Packet) {
|
||||
String path = ax25Packet.digipath == null ? "" : ax25Packet.digipath;
|
||||
if (!path.isEmpty())
|
||||
path = "," + path;
|
||||
return String.format("%s>%s%s:%s", ax25Packet.src, ax25Packet.dst,
|
||||
path, DebugTools.bytesToDebugString(ax25Packet.rawData));
|
||||
private void digiRepeat(AX25Packet ax25Packet) {
|
||||
if (!ax25Packet.digiRepeat()) return;
|
||||
byte[] ax25Frame = ax25Packet.toBinary();
|
||||
if (ax25Frame == null) {
|
||||
Log.e(TAG, "Cannot convert AX.25 digi repeated packet to binary");
|
||||
_parentProtocolCallback.onProtocolTxError();
|
||||
} else {
|
||||
try {
|
||||
_childProtocol.sendData(ax25Packet.src, ax25Packet.dst, ax25Frame);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Cannot send AX.25 digi repeated packet");
|
||||
e.printStackTrace();
|
||||
_parentProtocolCallback.onProtocolTxError();
|
||||
return;
|
||||
}
|
||||
_parentProtocolCallback.onTransmitLog(ax25Packet.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,35 +24,35 @@ public class Kiss implements Protocol {
|
|||
|
||||
private static final String TAG = Kiss.class.getSimpleName();
|
||||
|
||||
private final int TRANSPORT_OUTPUT_BUFFER_SIZE = 1024;
|
||||
private final int TRANSPORT_INPUT_BUFFER_SIZE = 1024;
|
||||
private final int FRAME_OUTPUT_BUFFER_SIZE = 1024;
|
||||
private final int KISS_CMD_BUFFER_SIZE = 128;
|
||||
private static final int TRANSPORT_OUTPUT_BUFFER_SIZE = 1024;
|
||||
private static final int TRANSPORT_INPUT_BUFFER_SIZE = 1024;
|
||||
private static final int FRAME_OUTPUT_BUFFER_SIZE = 1024;
|
||||
private static final int KISS_CMD_BUFFER_SIZE = 128;
|
||||
|
||||
private final int KISS_RADIO_CONTROL_COMMAND_SIZE = 17;
|
||||
private static final int KISS_RADIO_CONTROL_COMMAND_SIZE = 17;
|
||||
|
||||
private final byte KISS_FEND = (byte)0xc0;
|
||||
private final byte KISS_FESC = (byte)0xdb;
|
||||
private final byte KISS_TFEND = (byte)0xdc;
|
||||
private final byte KISS_TFESC = (byte)0xdd;
|
||||
private static final byte KISS_FEND = (byte)0xc0;
|
||||
private static final byte KISS_FESC = (byte)0xdb;
|
||||
private static final byte KISS_TFEND = (byte)0xdc;
|
||||
private static final byte KISS_TFESC = (byte)0xdd;
|
||||
|
||||
// only port 0 is supported
|
||||
private final byte KISS_CMD_DATA = (byte)0x00;
|
||||
private final byte KISS_CMD_TX_DELAY = (byte)0x01;
|
||||
private final byte KISS_CMD_P = (byte)0x02;
|
||||
private final byte KISS_CMD_SLOT_TIME = (byte)0x03;
|
||||
private final byte KISS_CMD_TX_TAIL = (byte)0x04;
|
||||
private final byte KISS_CMD_SET_HARDWARE = (byte)0x06;
|
||||
private final byte KISS_CMD_SIGNAL_REPORT = (byte)0x07;
|
||||
private final byte KISS_CMD_REBOOT = (byte)0x08;
|
||||
private final byte KISS_CMD_NOCMD = (byte)0x80;
|
||||
private static final byte KISS_CMD_DATA = (byte)0x00;
|
||||
private static final byte KISS_CMD_TX_DELAY = (byte)0x01;
|
||||
private static final byte KISS_CMD_P = (byte)0x02;
|
||||
private static final byte KISS_CMD_SLOT_TIME = (byte)0x03;
|
||||
private static final byte KISS_CMD_TX_TAIL = (byte)0x04;
|
||||
private static final byte KISS_CMD_SET_HARDWARE = (byte)0x06;
|
||||
private static final byte KISS_CMD_SIGNAL_REPORT = (byte)0x07;
|
||||
private static final byte KISS_CMD_REBOOT = (byte)0x08;
|
||||
private static final byte KISS_CMD_NOCMD = (byte)0x80;
|
||||
|
||||
private final byte CSMA_PERSISTENCE = (byte)0xff;
|
||||
private final byte CSMA_SLOT_TIME = (byte)0x00;
|
||||
private final byte TX_DELAY_10MS_UNITS = (byte)(250 / 10);
|
||||
private final byte TX_TAIL_10MS_UNITS = (byte)(500 / 10);
|
||||
private static final byte CSMA_PERSISTENCE = (byte)0xff;
|
||||
private static final byte CSMA_SLOT_TIME = (byte)0x00;
|
||||
private static final byte TX_DELAY_10MS_UNITS = (byte)(250 / 10);
|
||||
private static final byte TX_TAIL_10MS_UNITS = (byte)(500 / 10);
|
||||
|
||||
private final int SIGNAL_LEVEL_EVENT_SIZE = 4;
|
||||
private static final int SIGNAL_LEVEL_EVENT_SIZE = 4;
|
||||
|
||||
private enum State {
|
||||
GET_START,
|
||||
|
|
|
@ -8,8 +8,8 @@ import java.util.TimerTask;
|
|||
|
||||
public class KissBuffered extends Kiss {
|
||||
|
||||
private final int BUFFER_SIZE = 3200 * 60 * 10; // 10 min at 3200 bps
|
||||
private final int GAP_TO_PLAY_MS = 1000;
|
||||
private static final int BUFFER_SIZE = 3200 * 60 * 10; // 10 min at 3200 bps
|
||||
private static final int GAP_TO_PLAY_MS = 1000;
|
||||
|
||||
private final ByteBuffer _buffer;
|
||||
|
||||
|
|
|
@ -9,9 +9,8 @@ import java.util.TimerTask;
|
|||
|
||||
public class KissParrot extends Kiss {
|
||||
|
||||
private final int BUFFER_SIZE = 3200 * 60 * 5; // 5 min at 3200 bps
|
||||
|
||||
private final int PLAYBACK_DELAY_MS = 1000;
|
||||
private static final int BUFFER_SIZE = 3200 * 60 * 5; // 5 min at 3200 bps
|
||||
private static final int PLAYBACK_DELAY_MS = 1000;
|
||||
|
||||
private final ByteBuffer _buffer;
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import java.util.Arrays;
|
|||
|
||||
public class Raw implements Protocol {
|
||||
|
||||
private final int RX_BUFFER_SIZE = 8192;
|
||||
private static final int RX_BUFFER_SIZE = 8192;
|
||||
|
||||
protected Transport _transport;
|
||||
protected final byte[] _rxDataBuffer;
|
||||
|
|
|
@ -23,7 +23,7 @@ public class Recorder implements Protocol {
|
|||
|
||||
private static final String TAG = MainActivity.class.getSimpleName();
|
||||
|
||||
private final int ROTATION_DELAY_MS = 5000;
|
||||
private static final int ROTATION_DELAY_MS = 5000;
|
||||
|
||||
private File _storage;
|
||||
private FileOutputStream _activeStream;
|
||||
|
|
|
@ -95,4 +95,33 @@ public class AX25Callsign {
|
|||
buffer.get(b);
|
||||
return b;
|
||||
}
|
||||
|
||||
public boolean isWide() {
|
||||
return callsign.toUpperCase().startsWith("WIDE");
|
||||
}
|
||||
|
||||
public boolean isTrace() {
|
||||
return callsign.toUpperCase().startsWith("TRACE");
|
||||
}
|
||||
|
||||
public boolean isSoftware() {
|
||||
return callsign.toUpperCase().matches("^(AP)[A-Z]{1,4}$");
|
||||
}
|
||||
|
||||
public boolean isPath() {
|
||||
return isWide();
|
||||
}
|
||||
|
||||
public boolean digiRepeat() {
|
||||
if (isPath()) {
|
||||
if (ssid > 0) {
|
||||
ssid -= 1;
|
||||
if (ssid == 0) {
|
||||
callsign += "*";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package com.radio.codec2talkie.protocol.ax25;
|
||||
|
||||
import android.util.Log;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.radio.codec2talkie.tools.DebugTools;
|
||||
|
||||
import java.nio.BufferUnderflowException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
@ -63,7 +65,7 @@ public class AX25Packet {
|
|||
byte ax25Pid = buffer.get();
|
||||
if (ax25Pid == AX25PID_AUDIO) {
|
||||
isAudio = true;
|
||||
codec2Mode = (int)buffer.get();
|
||||
codec2Mode = buffer.get();
|
||||
} else if (ax25Pid == AX25PID_NO_LAYER3) {
|
||||
isAudio = false;
|
||||
} else {
|
||||
|
@ -119,4 +121,34 @@ public class AX25Packet {
|
|||
buffer.get(b);
|
||||
return b;
|
||||
}
|
||||
|
||||
public boolean digiRepeat() {
|
||||
boolean isDigiRepeated = false;
|
||||
String[] digiPaths = digipath.split(",");
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (String digiPath : digiPaths) {
|
||||
AX25Callsign rptCallsign = new AX25Callsign();
|
||||
rptCallsign.fromString(digiPath);
|
||||
if (rptCallsign.isValid) {
|
||||
if (rptCallsign.digiRepeat()) {
|
||||
isDigiRepeated = true;
|
||||
buf.append(rptCallsign.toString());
|
||||
} else {
|
||||
buf.append(digiPath);
|
||||
}
|
||||
} else {
|
||||
buf.append(digiPath);
|
||||
}
|
||||
}
|
||||
digipath = buf.toString();
|
||||
return isDigiRepeated;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String toString() {
|
||||
String path = digipath == null ? "" : digipath;
|
||||
if (!path.isEmpty())
|
||||
path = "," + path;
|
||||
return String.format("%s>%s%s:%s", src, dst, path, DebugTools.bytesToDebugString(rawData));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,6 +71,7 @@ public final class PreferenceKeys {
|
|||
public static String APRS_PRIVACY_POSITION_AMBIGUITY = "aprs_privacy_position_ambiguity";
|
||||
public static String APRS_PRIVACY_SPEED_ENABLED = "aprs_privacy_speed_enable";
|
||||
public static String APRS_PRIVACY_ALTITUDE_ENABLED = "aprs_privacy_altitude_enable";
|
||||
public static String APRS_DIGIREPEATER_ENABLED = "aprs_digirepeater_enable";
|
||||
|
||||
public static String APRS_LOCATION_SOURCE_SMART_FAST_SPEED = "aprs_location_source_smart_fast_speed";
|
||||
public static String APRS_LOCATION_SOURCE_SMART_FAST_RATE = "aprs_location_source_smart_fast_rate";
|
||||
|
|
|
@ -260,4 +260,7 @@
|
|||
<string name="app_no_lock_summary">Run above the lock screen</string>
|
||||
<string name="app_turn_screen_on_title">Turn screen ON automatically</string>
|
||||
<string name="app_turn_screen_on_summary">Turn screen ON automatically on incoming transmissions</string>
|
||||
<string name="aprs_digirepeater_enable_title">Enable digirepeater</string>
|
||||
<string name="aprs_digirepeater_enable_summary">Incoming APRS packets will received and digirepeated</string>
|
||||
<string name="digirepeater_label">🔁</string>
|
||||
</resources>
|
|
@ -269,6 +269,14 @@
|
|||
app:dependency="aprs_enable">
|
||||
</Preference>
|
||||
|
||||
<SwitchPreference
|
||||
app:key="aprs_digirepeater_enable"
|
||||
app:title="@string/aprs_digirepeater_enable_title"
|
||||
app:summary="@string/aprs_digirepeater_enable_summary"
|
||||
app:dependency="aprs_enable"
|
||||
app:defaultValue="false">
|
||||
</SwitchPreference>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
Ładowanie…
Reference in New Issue