deltachat/src/main/java/org/thoughtcrime/securesms/proxy/ProxySettingsActivity.java
2025-08-16 02:10:02 +02:00

239 lines
8.3 KiB
Java

package org.thoughtcrime.securesms.proxy;
import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_PROXY_ENABLED;
import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_PROXY_URL;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.SwitchCompat;
import com.b44t.messenger.DcContext;
import com.b44t.messenger.DcEvent;
import com.b44t.messenger.DcLot;
import com.caverock.androidsvg.SVG;
import com.caverock.androidsvg.SVGImageView;
import com.caverock.androidsvg.SVGParseException;
import org.thoughtcrime.securesms.BaseActionBarActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.connect.DcEventCenter;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.Util;
import java.util.LinkedList;
public class ProxySettingsActivity extends BaseActionBarActivity
implements ProxyListAdapter.ItemClickListener, DcEventCenter.DcEventDelegate {
private final DynamicTheme dynamicTheme = new DynamicTheme();
private SwitchCompat proxySwitch;
private ProxyListAdapter adapter;
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
dynamicTheme.onCreate(this);
setContentView(R.layout.proxy_settings_activity);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setTitle(R.string.proxy_settings);
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setElevation(0); // TODO: use custom toolbar instead
}
adapter = new ProxyListAdapter(this);
adapter.setItemClickListener(this);
proxySwitch = findViewById(R.id.proxy_switch);
proxySwitch.setChecked(DcHelper.getInt(this, CONFIG_PROXY_ENABLED) == 1);
proxySwitch.setOnClickListener(l -> {
if (proxySwitch.isChecked() && adapter.getCount() == 0) {
showAddProxyDialog();
} else {
DcHelper.set(this, CONFIG_PROXY_ENABLED, proxySwitch.isChecked()? "1" : "0");
DcHelper.getContext(this).restartIo();
}
});
ListView proxyList = findViewById(R.id.proxy_list);
proxyList.setAdapter(adapter);
proxyList.addHeaderView(View.inflate(this, R.layout.proxy_list_header, null), null, false);
View footer = View.inflate(this, R.layout.proxy_list_footer, null);
footer.setOnClickListener(l -> showAddProxyDialog());
proxyList.addFooterView(footer);
adapter.changeData(DcHelper.get(this, CONFIG_PROXY_URL));
DcHelper.getEventCenter(this).addObserver(DcContext.DC_EVENT_CONNECTIVITY_CHANGED, this);
handleOpenProxyUrl();
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
handleOpenProxyUrl();
}
@Override
public void onResume() {
super.onResume();
dynamicTheme.onResume(this);
}
@Override
public void onDestroy() {
super.onDestroy();
DcHelper.getEventCenter(this).removeObservers(this);
}
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onItemClick(String proxyUrl) {
if (DcHelper.getContext(this).setConfigFromQr(proxyUrl)) {
DcHelper.getContext(this).restartIo();
adapter.setSelectedProxy(proxyUrl);
proxySwitch.setChecked(DcHelper.getInt(this, CONFIG_PROXY_ENABLED) == 1);
} else {
Toast.makeText(this, R.string.proxy_invalid, Toast.LENGTH_LONG).show();
}
}
@Override
public void onItemShare(String proxyUrl) {
View view = View.inflate(this, R.layout.dialog_share_proxy, null);
SVGImageView qrImage = view.findViewById(R.id.qr_image);
try {
SVG svg = SVG.getFromString(DcHelper.getContext(this).createQrSvg(proxyUrl));
qrImage.setSVG(svg);
} catch (SVGParseException e) {
e.printStackTrace();
}
AlertDialog dialog = new AlertDialog.Builder(this)
.setView(view)
.setPositiveButton(android.R.string.ok, null)
.setNeutralButton(R.string.proxy_share_link, (dlg, btn) -> {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TEXT, proxyUrl);
startActivity(Intent.createChooser(intent, getString(R.string.chat_share_with_title)));
})
.show();
}
@Override
public void onItemDelete(String proxyUrl) {
String host = DcHelper.getContext(this).checkQr(proxyUrl).getText1();
AlertDialog dialog = new AlertDialog.Builder(this)
.setTitle(R.string.proxy_delete)
.setMessage(getString(R.string.proxy_delete_explain, host))
.setPositiveButton(R.string.delete, (dlg, btn) -> deleteProxy(proxyUrl))
.setNegativeButton(android.R.string.cancel, null)
.show();
Util.redPositiveButton(dialog);
}
private void deleteProxy(String proxyUrl) {
final LinkedList<String> proxies = new LinkedList<>();
for (String proxy: DcHelper.get(this, CONFIG_PROXY_URL).split("\n")) {
if (!proxy.equals(proxyUrl)) {
proxies.add(proxy);
}
}
if (proxies.isEmpty()) {
DcHelper.set(this, CONFIG_PROXY_ENABLED, "0");
proxySwitch.setChecked(false);
}
String proxyUrls = String.join("\n", proxies);
DcHelper.set(this, CONFIG_PROXY_URL, proxyUrls);
DcHelper.getContext(this).restartIo();
adapter.changeData(proxyUrls);
}
private void showAddProxyDialog() {
View view = View.inflate(this, R.layout.single_line_input, null);
EditText inputField = view.findViewById(R.id.input_field);
inputField.setHint(R.string.proxy_add_url_hint);
new AlertDialog.Builder(this)
.setTitle(R.string.proxy_add)
.setMessage(R.string.proxy_add_explain)
.setView(view)
.setPositiveButton(R.string.proxy_use_proxy, (dialog, whichButton) -> {
String newProxy = inputField.getText().toString().trim();
DcContext dcContext = DcHelper.getContext(this);
final DcLot qrParsed = dcContext.checkQr(newProxy);
if (qrParsed.getState() == DcContext.DC_QR_PROXY) {
dcContext.setConfigFromQr(newProxy);
DcHelper.getContext(this).restartIo();
adapter.changeData(DcHelper.get(this, CONFIG_PROXY_URL));
} else {
Toast.makeText(this, R.string.proxy_invalid, Toast.LENGTH_LONG).show();
}
proxySwitch.setChecked(DcHelper.getInt(this, CONFIG_PROXY_ENABLED) == 1);
})
.setNegativeButton(android.R.string.cancel, (dialog, whichButton) -> {
if (proxySwitch.isChecked() && adapter.getCount() == 0) {
// user enabled switch without having proxies yet, revert
proxySwitch.setChecked(false);
}
})
.setCancelable(false)
.show();
}
private void handleOpenProxyUrl() {
if (getIntent() != null && Intent.ACTION_VIEW.equals(getIntent().getAction())) {
Uri uri = getIntent().getData();
if (uri == null) {
return;
}
DcContext dcContext = DcHelper.getContext(this);
final DcLot qrParsed = dcContext.checkQr(uri.toString());
if (qrParsed.getState() == DcContext.DC_QR_PROXY) {
new AlertDialog.Builder(this)
.setTitle(R.string.proxy_use_proxy)
.setMessage(getString(R.string.proxy_use_proxy_confirm, qrParsed.getText1()))
.setPositiveButton(R.string.proxy_use_proxy, (dlg, btn) -> {
dcContext.setConfigFromQr(uri.toString());
dcContext.restartIo();
adapter.changeData(DcHelper.get(this, CONFIG_PROXY_URL));
proxySwitch.setChecked(DcHelper.getInt(this, CONFIG_PROXY_ENABLED) == 1);
})
.setNegativeButton(R.string.cancel, null)
.setCancelable(false)
.show();
} else {
Toast.makeText(this, R.string.proxy_invalid, Toast.LENGTH_LONG).show();
}
}
}
@Override
public void handleEvent(@NonNull DcEvent event) {
if (event.getId() == DcContext.DC_EVENT_CONNECTIVITY_CHANGED) {
adapter.refreshConnectivity();
}
}
}