This commit is contained in:
世界 2020-04-27 19:53:35 +08:00
parent 28b2955041
commit 3f1c93dc81
No known key found for this signature in database
GPG Key ID: CD109927C34A63C4
11 changed files with 24 additions and 510 deletions

View File

@ -81,4 +81,12 @@ jobs:
- uses: actions/upload-artifact@master
with:
name: V2ray Library
path: "TMessagesProj/libs/libv2ray.aar"
path: "TMessagesProj/libs/libv2ray.aar"
- uses: actions/upload-artifact@master
with:
name: Boringssl Library
path: "TMessagesProj/jni/boringssl/build"
- uses: actions/upload-artifact@master
with:
name: Ffmpeg Library
path: "TMessagesProj/jni/ffmpeg/build"

View File

@ -150,10 +150,10 @@ void bindRequestToGuid(JNIEnv *env, jclass c, jint instanceNum, jint requestToke
return ConnectionsManager::getInstance(instanceNum).bindRequestToGuid(requestToken, guid);
}
void applyDatacenterAddress(JNIEnv *env, jclass c, jint instanceNum, jint datacenterId, jstring ipAddress, jint port,jint flag) {
void applyDatacenterAddress(JNIEnv *env, jclass c, jint instanceNum, jint datacenterId, jstring ipAddress, jint port) {
const char *valueStr = env->GetStringUTFChars(ipAddress, 0);
ConnectionsManager::getInstance(instanceNum).applyDatacenterAddress((uint32_t) datacenterId, std::string(valueStr), (uint32_t) port,(uint32_t) flag);
ConnectionsManager::getInstance(instanceNum).applyDatacenterAddress((uint32_t) datacenterId, std::string(valueStr), (uint32_t) port);
if (valueStr != 0) {
env->ReleaseStringUTFChars(ipAddress, valueStr);
@ -427,7 +427,7 @@ static JNINativeMethod ConnectionsManagerMethods[] = {
{"native_cleanUp", "(IZ)V", (void *) cleanUp},
{"native_cancelRequestsForGuid", "(II)V", (void *) cancelRequestsForGuid},
{"native_bindRequestToGuid", "(III)V", (void *) bindRequestToGuid},
{"native_applyDatacenterAddress", "(IILjava/lang/String;II)V", (void *) applyDatacenterAddress},
{"native_applyDatacenterAddress", "(IILjava/lang/String;I)V", (void *) applyDatacenterAddress},
{"native_setProxySettings", "(ILjava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", (void *) setProxySettings},
{"native_getConnectionState", "(I)I", (void *) getConnectionState},
{"native_setUserId", "(II)V", (void *) setUserId},

View File

@ -3049,16 +3049,14 @@ void ConnectionsManager::authorizedOnMovingDatacenter() {
});
}
void ConnectionsManager::applyDatacenterAddress(uint32_t datacenterId, std::string ipAddress, uint32_t port,uint32_t flag) {
void ConnectionsManager::applyDatacenterAddress(uint32_t datacenterId, std::string ipAddress, uint32_t port) {
scheduleTask([&, datacenterId, ipAddress, port] {
Datacenter *datacenter = getDatacenterWithId(datacenterId);
if (datacenter != nullptr) {
std::vector<TcpAddress> addresses;
if (ipAddress.length() != 0) {
addresses.push_back(TcpAddress(ipAddress, port, flag, ""));
}
addresses.push_back(TcpAddress(ipAddress, port, 0, ""));
datacenter->suspendConnections(true);
datacenter->replaceAddresses(addresses, flag);
datacenter->replaceAddresses(addresses, 0);
datacenter->resetAddressAndPortNum();
saveConfig();
if (datacenter->isHandshakingAny()) {

View File

@ -54,7 +54,7 @@ public:
void cleanUp(bool resetKeys);
void cancelRequestsForGuid(int32_t guid);
void bindRequestToGuid(int32_t requestToken, int32_t guid);
void applyDatacenterAddress(uint32_t datacenterId, std::string ipAddress, uint32_t port,uint32_t flag);
void applyDatacenterAddress(uint32_t datacenterId, std::string ipAddress, uint32_t port);
void setDelegate(ConnectiosManagerDelegate *connectiosManagerDelegate);
ConnectionState getConnectionState();
void setUserId(int32_t userId);

View File

@ -434,19 +434,6 @@ void Handshake::processHandshakeResponse(TLObject *message, int64_t messageId) {
"PGHKSMeRFvp3IWcmdJqXahxLCUS1Eh6MAQIDAQAB\n"
"-----END RSA PUBLIC KEY-----");
serverPublicKeysFingerprints.push_back(0x5a181b2235057d98LL);
serverPublicKeys.push_back("-----BEGIN RSA PUBLIC KEY-----\n"
"MIIBCgKCAQEAvKLEOWTzt9Hn3/9Kdp/RdHcEhzmd8xXeLSpHIIzaXTLJDw8BhJy1\n"
"jR/iqeG8Je5yrtVabqMSkA6ltIpgylH///FojMsX1BHu4EPYOXQgB0qOi6kr08iX\n"
"ZIH9/iOPQOWDsL+Lt8gDG0xBy+sPe/2ZHdzKMjX6O9B4sOsxjFrk5qDoWDrioJor\n"
"AJ7eFAfPpOBf2w73ohXudSrJE0lbQ8pCWNpMY8cB9i8r+WBitcvouLDAvmtnTX7a\n"
"khoDzmKgpJBYliAY4qA73v7u5UIepE8QgV0jCOhxJCPubP8dg+/PlLLVKyxU5Cdi\n"
"QtZj2EMy4s9xlNKzX8XezE0MHEa6bQpnFwIDAQAB\n"
"-----END RSA PUBLIC KEY-----");
serverPublicKeysFingerprints.push_back(0xa9e071c1771060cdLL);
}
size_t count2 = serverPublicKeysFingerprints.size();

View File

@ -175,7 +175,7 @@ public class GcmPushListenerService extends FirebaseMessagingService {
}
String ip = parts[0];
int port = Integer.parseInt(parts[1]);
ConnectionsManager.getInstance(currentAccount).applyDatacenterAddress(dc, ip, port, 0);
ConnectionsManager.getInstance(currentAccount).applyDatacenterAddress(dc, ip, port);
ConnectionsManager.getInstance(currentAccount).resumeNetworkMaybe();
countDownLatch.countDown();
return;

View File

@ -55,7 +55,6 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import tw.nekomimi.nekogram.DataCenter;
import tw.nekomimi.nekogram.ExternalGcm;
import tw.nekomimi.nekogram.InternalFilters;
import tw.nekomimi.nekogram.NekoConfig;
@ -8127,9 +8126,7 @@ public class MessagesController extends BaseController implements NotificationCe
getMessagesStorage().cleanup(false);
cleanup();
getContactsController().deleteUnknownAppAccounts();
if (MessagesController.getMainSettings(currentAccount).getBoolean("custom_dc",false)) {
DataCenter.applyOfficalDataCanter(currentAccount);
} else if (ConnectionsManager.native_isTestBackend(currentAccount) != 0) {
if (ConnectionsManager.native_isTestBackend(currentAccount) != 0) {
ConnectionsManager.native_switchBackend(currentAccount);
}
}

View File

@ -155,26 +155,16 @@ public class ConnectionsManager extends BaseController {
public ConnectionsManager(int instance) {
super(instance);
connectionState = native_getConnectionState(currentAccount);
init();
}
public void init() {
String deviceModel;
String systemLangCode;
String langCode;
String appVersion;
String systemVersion;
File config = ApplicationLoader.getFilesDirFixed();
if (currentAccount != 0) {
config = new File(config, "account" + currentAccount);
if (instance != 0) {
config = new File(config, "account" + instance);
config.mkdirs();
}
File tgnetFile = new File(config,"tgnet.dat");
File tgnetFileNew = new File(config,"tgnet.dat.new");
if (tgnetFileNew.isFile()) {
tgnetFile.delete();
tgnetFileNew.renameTo(tgnetFile);
}
String configPath = config.toString();
boolean enablePushConnection = isPushConnectionEnabled();
try {
@ -212,15 +202,7 @@ public class ConnectionsManager extends BaseController {
int timezoneOffset = (TimeZone.getDefault().getRawOffset() + TimeZone.getDefault().getDSTSavings()) / 1000;
int layer = MessagesController.getMainSettings(currentAccount).getInt("layer",TLRPC.LAYER);
if (layer != TLRPC.LAYER) {
FileLog.d("use custom layer " + layer);
}
init(BuildVars.BUILD_VERSION, layer, BuildConfig.APP_ID, deviceModel, systemVersion, appVersion, langCode, systemLangCode, configPath, FileLog.getNetworkLogPath(), pushString, fingerprint, timezoneOffset, getUserConfig().getClientUserId(), enablePushConnection);
init(BuildVars.BUILD_VERSION, TLRPC.LAYER, BuildConfig.APP_ID, deviceModel, systemVersion, appVersion, langCode, systemLangCode, configPath, FileLog.getNetworkLogPath(), pushString, fingerprint, timezoneOffset, getUserConfig().getClientUserId(), enablePushConnection);
}
public boolean isPushConnectionEnabled() {
@ -328,8 +310,8 @@ public class ConnectionsManager extends BaseController {
native_bindRequestToGuid(currentAccount, requestToken, guid);
}
public void applyDatacenterAddress(int datacenterId, String ipAddress, int port, int flag) {
native_applyDatacenterAddress(currentAccount, datacenterId, ipAddress, port, flag);
public void applyDatacenterAddress(int datacenterId, String ipAddress, int port) {
native_applyDatacenterAddress(currentAccount, datacenterId, ipAddress, port);
}
public int getConnectionState() {
@ -538,9 +520,6 @@ public class ConnectionsManager extends BaseController {
public static void onRequestNewServerIpAndPort(final int second, final int currentAccount) {
Utilities.stageQueue.postRunnable(() -> {
if (MessagesController.getMainSettings(currentAccount).getBoolean("custom_dc", false)) {
return;
}
if (currentTask != null || second == 0 && Math.abs(lastDnsRequestTime - System.currentTimeMillis()) < 10000 || !ApplicationLoader.isNetworkOnline()) {
if (BuildVars.LOGS_ENABLED) {
@ -670,7 +649,7 @@ public class ConnectionsManager extends BaseController {
public static native void native_bindRequestToGuid(int currentAccount, int requestToken, int guid);
public static native void native_applyDatacenterAddress(int currentAccount, int datacenterId, String ipAddress, int port, int flag);
public static native void native_applyDatacenterAddress(int currentAccount, int datacenterId, String ipAddress, int port);
public static native int native_getConnectionState(int currentAccount);

View File

@ -67,8 +67,6 @@ import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import com.v2ray.ang.util.Utils;
import org.telegram.PhoneFormat.PhoneFormat;
import org.telegram.messenger.AccountInstance;
import org.telegram.messenger.AndroidUtilities;
@ -127,7 +125,6 @@ import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import kotlin.Unit;
import tw.nekomimi.nekogram.BottomBuilder;
import tw.nekomimi.nekogram.DataCenter;
import tw.nekomimi.nekogram.EditTextAutoFill;
import tw.nekomimi.nekogram.NekoXConfig;
@ -270,7 +267,6 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No
private int menu_other = 5;
private int menu_custom_api = 6;
private int menu_custom_dc = 7;
@Override
public View createView(Context context) {
@ -530,258 +526,6 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No
builder.show();
} else if (id == menu_custom_dc) {
AtomicInteger targetDc = new AtomicInteger(-1);
BottomBuilder builder = new BottomBuilder(getParentActivity());
EditText[] inputs = new EditText[4];
builder.addTitle("Custom Backend",
true,
"~");
int dcType;
if (MessagesController.getMainSettings(currentAccount).getBoolean("custom_dc", false)) {
dcType = 2;
} else if (ConnectionsManager.native_isTestBackend(currentAccount) != 0) {
dcType = 1;
} else {
dcType = 0;
}
builder.addRadioItem("OFFICAL", dcType == 0, (cell) -> {
targetDc.set(0);
builder.doRadioCheck(cell);
for (EditText input : inputs) input.setVisibility(View.GONE);
return Unit.INSTANCE;
});
builder.addRadioItem("TEST DC", dcType == 1, (cell) -> {
targetDc.set(1);
builder.doRadioCheck(cell);
for (EditText input : inputs) input.setVisibility(View.GONE);
return Unit.INSTANCE;
});
builder.addRadioItem(LocaleController.getString("CustomApiInput", R.string.CustomApiInput), dcType == 2, (cell) -> {
targetDc.set(2);
builder.doRadioCheck(cell);
for (EditText input : inputs) input.setVisibility(View.VISIBLE);
return Unit.INSTANCE;
});
inputs[0] = builder.addEditText("Ipv4 Address");
inputs[0].setFilters(new InputFilter[]{new InputFilter.LengthFilter(15)});
if (StrUtil.isNotBlank(NekoXConfig.customDcIpv4)) {
inputs[0].setText(NekoXConfig.customDcIpv4);
}
inputs[0].addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (StrUtil.isBlank(s) || Utils.isIpv4Address(s.toString())) {
inputs[0].setError(null);
NekoXConfig.customDcIpv4 = s.toString();
} else {
inputs[0].setError("Invalid Ipv4 Address");
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
inputs[1] = builder.addEditText("Ipv6 Address");
if (StrUtil.isNotBlank(NekoXConfig.customDcIpv6)) {
inputs[1].setText(NekoXConfig.customDcIpv6);
}
inputs[1].addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (StrUtil.isBlank(s) || Utils.isIpv6Address(s.toString())) {
inputs[1].setError(null);
NekoXConfig.customDcIpv6 = s.toString();
} else {
inputs[1].setError("Invalid Ipv6 Address");
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
inputs[2] = builder.addEditText("Port");
inputs[2].setInputType(InputType.TYPE_CLASS_NUMBER);
if (NekoXConfig.customDcPort != 0) {
inputs[2].setText(NekoXConfig.customDcPort + "");
}
inputs[2].setFilters(new InputFilter[]{new InputFilter.LengthFilter(5)});
inputs[2].addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (StrUtil.isBlank(s.toString())) {
NekoXConfig.customDcPort = 0;
} else {
NekoXConfig.customDcPort = NumberUtil.parseInt(s.toString());
if (NekoXConfig.customDcPort <= 0 || NekoXConfig.customDcPort > 65535) {
NekoXConfig.customDcPort = 0;
inputs[2].setError("Invalid Port");
} else {
inputs[2].setError(null);
}
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
inputs[3] = builder.addEditText("Layer");
inputs[3].setInputType(InputType.TYPE_CLASS_NUMBER);
if (NekoXConfig.customDcLayer != 0) {
inputs[3].setText(NekoXConfig.customDcLayer + "");
}
inputs[3].setFilters(new InputFilter[]{new InputFilter.LengthFilter(3)});
inputs[3].addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (StrUtil.isBlank(s.toString())) {
NekoXConfig.customDcLayer = 0;
} else {
NekoXConfig.customDcLayer = NumberUtil.parseInt(s.toString());
if (NekoXConfig.customDcLayer > TLRPC.LAYER || NekoXConfig.customDcLayer < 85) {
NekoXConfig.customDcLayer = 0;
inputs[2].setError("Layer not supported");
} else {
inputs[2].setError(null);
}
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
if (dcType < 2) {
for (EditText input : inputs) input.setVisibility(View.GONE);
}
builder.addCancelButton();
builder.addButton(LocaleController.getString("Set", R.string.Set), (it) -> {
int target = targetDc.get();
if (target >= 2) {
if (inputs[0].getError() != null) {
inputs[0].requestFocus();
AndroidUtilities.showKeyboard(inputs[0]);
return Unit.INSTANCE;
} else if (inputs[1].getError() != null) {
inputs[1].requestFocus();
AndroidUtilities.showKeyboard(inputs[1]);
return Unit.INSTANCE;
} else if (StrUtil.isBlank(inputs[2].getText().toString())) {
inputs[2].setError("Port required");
inputs[2].requestFocus();
AndroidUtilities.showKeyboard(inputs[2]);
return Unit.INSTANCE;
} else if (StrUtil.isBlank(inputs[3].getText().toString())) {
inputs[3].setError("Layer required");
inputs[3].requestFocus();
AndroidUtilities.showKeyboard(inputs[3]);
return Unit.INSTANCE;
} else if (StrUtil.isBlank(NekoXConfig.customDcIpv4) && StrUtil.isBlank(NekoXConfig.customDcIpv6)) {
inputs[0].requestFocus();
AndroidUtilities.showKeyboard(inputs[0]);
return Unit.INSTANCE;
}
}
if (target == dcType) {
// do nothing
} else if (target == 0) {
DataCenter.applyOfficalDataCanter(currentAccount);
} else if (target == 1) {
DataCenter.applyTestDataCenter(currentAccount);
} else {
DataCenter.applyCustomDataCenter(currentAccount,NekoXConfig.customDcIpv4,NekoXConfig.customDcIpv6,NekoXConfig.customDcPort,NekoXConfig.customDcLayer);
NekoXConfig.saveCustomDc();
}
builder.dismiss();
return Unit.INSTANCE;
});
builder.show();
}
}
@ -810,12 +554,6 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No
otherItem.addSubItem(menu_custom_api, R.drawable.baseline_vpn_key_24, LocaleController.getString("CustomApi", R.string.CustomApi));
if(NekoXConfig.developerMode) {
otherItem.addSubItem(menu_custom_dc, R.drawable.baseline_sync_24, "Custom Backend");
}
actionBar.setAllowOverlayTitle(true);
doneItem = menu.addItemWithWidth(done_button, R.drawable.ic_done, AndroidUtilities.dp(56));
doneProgressView = new ContextProgressView(context, 1);

View File

@ -1,178 +0,0 @@
package tw.nekomimi.nekogram
import com.v2ray.ang.util.Utils
import org.telegram.messenger.ApplicationLoader
import org.telegram.messenger.MessagesController
import org.telegram.tgnet.AbstractSerializedData
import org.telegram.tgnet.ConnectionsManager
import org.telegram.tgnet.SerializedData
import tw.nekomimi.nekogram.utils.AlertUtil
import tw.nekomimi.nekogram.utils.receive
import java.io.File
object DataCenter {
val ConnectionsManager.tgnetFile by receive<ConnectionsManager, File> {
var config = ApplicationLoader.getFilesDirFixed()
if (currentAccount != 0) {
config = File(config, "account$currentAccount")
config.mkdirs()
}
return@receive File(config,"tgnet.dat")
}
val ConnectionsManager.tgnetFileNew by receive<ConnectionsManager, File> { File(tgnetFile.parentFile!!, "${tgnetFile.name}.new") }
@JvmStatic
fun applyOfficalDataCanter(account: Int) {
MessagesController.getMainSettings(account).edit().remove("layer").remove("custom_dc").apply()
ConnectionsManager.getInstance(account).tgnetFileNew.delete()
if (ConnectionsManager.native_isTestBackend(account) != 0) {
ConnectionsManager.getInstance(account).switchBackend()
} else {
applyDataCanter(account, 1, "149.154.175.50", 0)
applyDataCanter(account, 1, "2001:b28:f23d:f001:0000:0000:0000:000a", 1)
applyDataCanter(account, 2, "149.154.167.51", 0)
applyDataCanter(account, 2, "2001:67c:4e8:f002:0000:0000:0000:000a", 1)
applyDataCanter(account, 3, "149.154.175.100", 0)
applyDataCanter(account, 3, "2001:b28:f23d:f003:0000:0000:0000:000a", 1)
applyDataCanter(account, 4, "149.154.167.91", 0)
applyDataCanter(account, 4, "2001:67c:4e8:f004:0000:0000:0000:000a", 1)
applyDataCanter(account, 5, "149.154.171.5", 0)
applyDataCanter(account, 5, "2001:67c:4e8:f005:0000:0000:0000:000a", 1)
}
ConnectionsManager.native_cleanUp(account, true)
}
@JvmStatic
fun applyTestDataCenter(account: Int) {
MessagesController.getMainSettings(account).edit().remove("layer").remove("custom_dc").apply()
if (ConnectionsManager.native_isTestBackend(account) == 0) {
ConnectionsManager.getInstance(account).switchBackend()
}
}
@JvmStatic
fun applyCustomDataCenter(account: Int, ipv4Address: String = "", ipv6Address: String = "", port: Int, layer: Int) {
MessagesController.getMainSettings(account).edit().putInt("layer", layer).putBoolean("custom_dc", true).apply()
if (ConnectionsManager.native_isTestBackend(account) != 0) {
ConnectionsManager.getInstance(account).switchBackend()
}
ConnectionsManager.getInstance(account).apply {
tgnetFile.delete()
tgnetFileNew.delete()
}
val buffer = SerializedData()
val time = (System.currentTimeMillis() / 1000).toInt()
buffer.writeInt32(5) // configVersion
buffer.writeBool(false) // testBackend
buffer.writeBool(true) // clientBlocked
buffer.writeString("en") // lastInitSystemLangcode
buffer.writeBool(true) // currentDatacenter != nullptr
buffer.writeInt32(2) // currentDatacenterId
buffer.writeInt32(0) // timeDifference
buffer.writeInt32(time) // lastDcUpdateTime
buffer.writeInt64(0L) // pushSessionId
buffer.writeBool(false) // registeredForInternalPush
buffer.writeInt32(time) // getCurrentTime()
buffer.writeInt32(0) // sessions.size
buffer.writeInt32(5) // datacenters.size
repeat(5) {
buffer.writeDataCenter(it + 1, ipv4Address, ipv6Address, port)
}
ConnectionsManager.getInstance(account).tgnetFileNew.apply {
createNewFile()
writeBytes(buffer.toByteArray())
}
AlertUtil.showToast("Restart required.")
}
private fun AbstractSerializedData.writeDataCenter(id: Int, ipv4Address: String, ipv6Address: String, port: Int) {
writeInt32(13) // configVersion
writeInt32(id) // datacenterId
writeInt32(13) // lastInitVersion
writeInt32(13) //lastInitMediaVersion
writeDataCenterAddress(ipv4Address, port)
writeDataCenterAddress(ipv6Address, port)
writeDataCenterAddress("", port)
writeDataCenterAddress("", port)
writeBool(false) // isCdnDatacenter
writeInt32(0) // authKeyPerm
writeInt64(0L) // authKeyPermId
writeInt32(0) // authKeyTemp
writeInt64(0L) // authKeyTempId
writeInt32(0) // authKeyMediaTemp
writeInt64(0L) // authKeyMediaTempId
writeInt32(0) // authorized
writeInt32(0) // serverSalts.size
writeInt32(0) // mediaServerSalts.size
}
private fun AbstractSerializedData.writeDataCenterAddress(address: String, port: Int) {
writeInt32(if (address.isBlank()) 0 else 1) // array->size()
if (address.isNotBlank()) {
writeString(address) // address
writeInt32(port) // port
writeInt32(if (Utils.isIpv6Address(address)) 1 else 0) // flags
writeString("") // secret
}
}
private fun applyDataCanter(account: Int, dataCenter: Int, address: String, flag: Int, port: Int = 443) {
ConnectionsManager.getInstance(account).applyDatacenterAddress(dataCenter, address, port, flag)
}
}

View File

@ -85,20 +85,5 @@ public class NekoXConfig {
}
public static String customDcIpv4 = preferences.getString("custom_dc_v4", "");
public static String customDcIpv6 = preferences.getString("custom_dc_v6", "");
public static int customDcPort = preferences.getInt("custom_dc_port", 12345);
public static int customDcLayer = preferences.getInt("custom_dc_layer", 0);
public static void saveCustomDc() {
preferences.edit()
.putString("custom_dc_v4", customDcIpv4)
.putString("custom_dc_v6", customDcIpv6)
.putInt("custom_dc_port",customDcPort)
.putInt("custom_dc_layer",customDcLayer)
.apply();
}
}