diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 486980fcd..143c3e3ea 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -249,7 +249,7 @@ - diff --git a/src/main/java/com/b44t/messenger/DcContext.java b/src/main/java/com/b44t/messenger/DcContext.java index b02b4dc22..003f62dd3 100644 --- a/src/main/java/com/b44t/messenger/DcContext.java +++ b/src/main/java/com/b44t/messenger/DcContext.java @@ -56,6 +56,7 @@ public class DcContext { public final static int DC_QR_BACKUP = 251; public final static int DC_QR_BACKUP2 = 252; public final static int DC_QR_WEBRTC = 260; + public final static int DC_QR_PROXY = 271; public final static int DC_QR_ADDR = 320; public final static int DC_QR_TEXT = 330; public final static int DC_QR_URL = 332; diff --git a/src/main/java/org/thoughtcrime/securesms/ConversationListActivity.java b/src/main/java/org/thoughtcrime/securesms/ConversationListActivity.java index 10844d728..c7ac6dc1a 100644 --- a/src/main/java/org/thoughtcrime/securesms/ConversationListActivity.java +++ b/src/main/java/org/thoughtcrime/securesms/ConversationListActivity.java @@ -20,7 +20,7 @@ import static org.thoughtcrime.securesms.ConversationActivity.CHAT_ID_EXTRA; import static org.thoughtcrime.securesms.ConversationActivity.STARTING_POSITION_EXTRA; import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_ADDRESS; import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_SERVER_FLAGS; -import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_SOCKS5_ENABLED; +import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_PROXY_URL; import static org.thoughtcrime.securesms.util.RelayUtil.acquireRelayMessageContent; import static org.thoughtcrime.securesms.util.RelayUtil.getDirectSharingChatId; import static org.thoughtcrime.securesms.util.RelayUtil.getSharedTitle; @@ -64,6 +64,7 @@ import org.thoughtcrime.securesms.connect.DcHelper; import org.thoughtcrime.securesms.connect.DirectShareUtil; import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.permissions.Permissions; +import org.thoughtcrime.securesms.proxy.ProxySettingsActivity; import org.thoughtcrime.securesms.qr.QrActivity; import org.thoughtcrime.securesms.qr.QrCodeHandler; import org.thoughtcrime.securesms.recipients.Recipient; @@ -361,7 +362,7 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit if (!isRelayingMessageContent(this)) { inflater.inflate(R.menu.text_secure_normal, menu); menu.findItem(R.id.menu_global_map).setVisible(Prefs.isLocationStreamingEnabled(this)); - menu.findItem(R.id.menu_proxy_settings).setVisible(DcHelper.getInt(this, CONFIG_SOCKS5_ENABLED) == 1); + menu.findItem(R.id.menu_proxy_settings).setVisible(!TextUtils.isEmpty(DcHelper.get(this, CONFIG_PROXY_URL))); } super.onPrepareOptionsMenu(menu); diff --git a/src/main/java/org/thoughtcrime/securesms/InstantOnboardingActivity.java b/src/main/java/org/thoughtcrime/securesms/InstantOnboardingActivity.java index 7eee049ff..3adaf56e7 100644 --- a/src/main/java/org/thoughtcrime/securesms/InstantOnboardingActivity.java +++ b/src/main/java/org/thoughtcrime/securesms/InstantOnboardingActivity.java @@ -48,6 +48,7 @@ import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.permissions.Permissions; import org.thoughtcrime.securesms.profiles.AvatarHelper; import org.thoughtcrime.securesms.profiles.ProfileMediaConstraints; +import org.thoughtcrime.securesms.proxy.ProxySettingsActivity; import org.thoughtcrime.securesms.qr.RegistrationQrActivity; import org.thoughtcrime.securesms.scribbles.ScribbleActivity; import org.thoughtcrime.securesms.util.Prefs; diff --git a/src/main/java/org/thoughtcrime/securesms/ProxySettingsActivity.java b/src/main/java/org/thoughtcrime/securesms/ProxySettingsActivity.java deleted file mode 100644 index d88f66073..000000000 --- a/src/main/java/org/thoughtcrime/securesms/ProxySettingsActivity.java +++ /dev/null @@ -1,160 +0,0 @@ -package org.thoughtcrime.securesms; - -import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_SOCKS5_ENABLED; -import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_SOCKS5_HOST; -import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_SOCKS5_PASSWORD; -import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_SOCKS5_PORT; -import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_SOCKS5_USER; - -import android.os.Bundle; -import android.text.TextUtils; -import android.util.Patterns; -import android.view.MenuItem; -import android.view.View; - -import androidx.annotation.IdRes; -import androidx.appcompat.app.ActionBar; -import androidx.appcompat.widget.SwitchCompat; -import androidx.constraintlayout.widget.Group; - -import com.b44t.messenger.DcContext; -import com.google.android.material.textfield.TextInputEditText; - -import org.thoughtcrime.securesms.connect.DcHelper; -import org.thoughtcrime.securesms.util.DynamicTheme; - -public class ProxySettingsActivity extends BaseActionBarActivity { - - private enum VerificationType { - SERVER, - PORT, - } - - private final DynamicTheme dynamicTheme = new DynamicTheme(); - - private SwitchCompat proxySwitch; - private Group proxyGroup; - - @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); - } - - proxyGroup = findViewById(R.id.proxy_group); - - proxySwitch = findViewById(R.id.proxy_switch); - proxySwitch.setOnCheckedChangeListener((buttonView, isChecked) -> { - proxyGroup.setVisibility(isChecked? View.VISIBLE : View.GONE); - }); - proxySwitch.setChecked(DcHelper.getInt(this, CONFIG_SOCKS5_ENABLED) == 1); - - TextInputEditText proxyHostInput = findViewById(R.id.proxy_host_text); - proxyHostInput.setOnFocusChangeListener((view, focused) -> focusListener(view, focused, VerificationType.SERVER)); - proxyHostInput.setText(DcHelper.get(this, CONFIG_SOCKS5_HOST)); - - TextInputEditText proxyPortInput = findViewById(R.id.proxy_port_text); - proxyPortInput.setOnFocusChangeListener((view, focused) -> focusListener(view, focused, VerificationType.PORT)); - proxyPortInput.setText(DcHelper.get(this, CONFIG_SOCKS5_PORT)); - - TextInputEditText proxyUserInput = findViewById(R.id.proxy_user_text); - proxyUserInput.setText(DcHelper.get(this, CONFIG_SOCKS5_USER)); - - TextInputEditText proxyPasswordInput = findViewById(R.id.proxy_password_text); - proxyPasswordInput.setText(DcHelper.get(this, CONFIG_SOCKS5_PASSWORD)); - } - - @Override - public void onResume() { - super.onResume(); - dynamicTheme.onResume(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 onDestroy() { - super.onDestroy(); - saveConfig(); - } - - @Override - public void onPause() { - super.onPause(); - saveConfig(); - } - - private void focusListener(View view, boolean focused, VerificationType type) { - - if (!focused) { - TextInputEditText inputEditText = (TextInputEditText) view; - switch (type) { - case SERVER: - verifyServer(inputEditText); - break; - case PORT: - verifyPort(inputEditText); - break; - } - } - } - - private void verifyServer(TextInputEditText view) { - String server = view.getText().toString(); - if (!TextUtils.isEmpty(server) && !Patterns.DOMAIN_NAME.matcher(server).matches() - && !Patterns.IP_ADDRESS.matcher(server).matches() - && !Patterns.WEB_URL.matcher(server).matches() - && !"localhost".equals(server)) { - view.setError(getString(R.string.login_error_server)); - } - } - - private void verifyPort(TextInputEditText view) { - String portString = view.getText().toString(); - if (!portString.isEmpty()) { - String error = getString(R.string.login_error_port); - try { - int port = Integer.valueOf(portString); - if (port < 1 || port > 65535) { - view.setError(error); - } - } catch (NumberFormatException exception) { - view.setError(error); - } - } - } - - private void saveConfig() { - DcContext dcContext = DcHelper.getContext(this); - dcContext.setConfigInt(CONFIG_SOCKS5_ENABLED, proxySwitch.isChecked()? 1 : 0); - setConfig(R.id.proxy_host_text, CONFIG_SOCKS5_HOST, true); - setConfig(R.id.proxy_port_text, CONFIG_SOCKS5_PORT, true); - setConfig(R.id.proxy_user_text, CONFIG_SOCKS5_USER, true); - setConfig(R.id.proxy_password_text, CONFIG_SOCKS5_PASSWORD, false); - dcContext.stopIo(); - dcContext.startIo(); - } - - private void setConfig(@IdRes int viewId, String configTarget, boolean doTrim) { - TextInputEditText view = findViewById(viewId); - String value = view.getText().toString(); - if(doTrim) { - value = value.trim(); - } - DcHelper.getContext(this).setConfig(configTarget, value.isEmpty()? null : value); - } - -} diff --git a/src/main/java/org/thoughtcrime/securesms/RegistrationActivity.java b/src/main/java/org/thoughtcrime/securesms/RegistrationActivity.java index 8a5eb6f9e..f52eafffb 100644 --- a/src/main/java/org/thoughtcrime/securesms/RegistrationActivity.java +++ b/src/main/java/org/thoughtcrime/securesms/RegistrationActivity.java @@ -12,11 +12,7 @@ import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_SEND_SECURITY; import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_SEND_SERVER; import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_SEND_USER; import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_SERVER_FLAGS; -import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_SOCKS5_ENABLED; -import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_SOCKS5_HOST; -import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_SOCKS5_PASSWORD; -import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_SOCKS5_PORT; -import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_SOCKS5_USER; +import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_PROXY_ENABLED; import static org.thoughtcrime.securesms.connect.DcHelper.getContext; import static org.thoughtcrime.securesms.service.IPCAddAccountsService.ACCOUNT_DATA; @@ -59,6 +55,7 @@ import com.google.android.material.textfield.TextInputEditText; import org.thoughtcrime.securesms.connect.DcEventCenter; import org.thoughtcrime.securesms.connect.DcHelper; import org.thoughtcrime.securesms.permissions.Permissions; +import org.thoughtcrime.securesms.proxy.ProxySettingsActivity; import org.thoughtcrime.securesms.util.DynamicTheme; import org.thoughtcrime.securesms.util.IntentUtils; import org.thoughtcrime.securesms.util.Util; @@ -162,7 +159,7 @@ public class RegistrationActivity extends BaseActionBarActivity implements DcEve String strVal; int intVal; - intVal = DcHelper.getInt(this, CONFIG_SOCKS5_ENABLED); + intVal = DcHelper.getInt(this, CONFIG_PROXY_ENABLED); proxySwitch.setChecked(intVal == 1); expandAdvanced = expandAdvanced || intVal == 1; @@ -260,7 +257,7 @@ public class RegistrationActivity extends BaseActionBarActivity implements DcEve public void onResume() { super.onResume(); dynamicTheme.onResume(this); - proxySwitch.setChecked(DcHelper.getInt(this, CONFIG_SOCKS5_ENABLED) == 1); + proxySwitch.setChecked(DcHelper.getInt(this, CONFIG_PROXY_ENABLED) == 1); } private void showLog() { diff --git a/src/main/java/org/thoughtcrime/securesms/connect/DcHelper.java b/src/main/java/org/thoughtcrime/securesms/connect/DcHelper.java index b3317dd7a..a6d50e324 100644 --- a/src/main/java/org/thoughtcrime/securesms/connect/DcHelper.java +++ b/src/main/java/org/thoughtcrime/securesms/connect/DcHelper.java @@ -72,11 +72,8 @@ public class DcHelper { public static final String CONFIG_SHOW_EMAILS = "show_emails"; public static final String CONFIG_MEDIA_QUALITY = "media_quality"; public static final String CONFIG_WEBRTC_INSTANCE = "webrtc_instance"; - public static final String CONFIG_SOCKS5_ENABLED = "socks5_enabled"; - public static final String CONFIG_SOCKS5_HOST = "socks5_host"; - public static final String CONFIG_SOCKS5_PORT = "socks5_port"; - public static final String CONFIG_SOCKS5_USER = "socks5_user"; - public static final String CONFIG_SOCKS5_PASSWORD = "socks5_password"; + public static final String CONFIG_PROXY_ENABLED = "proxy_enabled"; + public static final String CONFIG_PROXY_URL = "proxy_url"; public static final String CONFIG_VERIFIED_ONE_ON_ONE_CHATS = "verified_one_on_one_chats"; public static final String CONFIG_WEBXDC_REALTIME_ENABLED = "webxdc_realtime_enabled"; diff --git a/src/main/java/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragment.java b/src/main/java/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragment.java index fbcf11397..f5f095e4a 100644 --- a/src/main/java/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragment.java +++ b/src/main/java/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragment.java @@ -37,7 +37,7 @@ import com.b44t.messenger.rpc.RpcException; import org.thoughtcrime.securesms.ApplicationPreferencesActivity; import org.thoughtcrime.securesms.ConversationActivity; import org.thoughtcrime.securesms.LogViewActivity; -import org.thoughtcrime.securesms.ProxySettingsActivity; +import org.thoughtcrime.securesms.proxy.ProxySettingsActivity; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.RegistrationActivity; import org.thoughtcrime.securesms.connect.DcEventCenter; diff --git a/src/main/java/org/thoughtcrime/securesms/proxy/ProxyListAdapter.java b/src/main/java/org/thoughtcrime/securesms/proxy/ProxyListAdapter.java new file mode 100644 index 000000000..01fe5c832 --- /dev/null +++ b/src/main/java/org/thoughtcrime/securesms/proxy/ProxyListAdapter.java @@ -0,0 +1,172 @@ +package org.thoughtcrime.securesms.proxy; + +import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_PROXY_ENABLED; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.TextView; + +import com.b44t.messenger.DcContext; +import com.b44t.messenger.DcLot; + +import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.connect.DcHelper; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +public class ProxyListAdapter extends BaseAdapter { + private enum ProxyState { + CONNECTED, + CONNECTING, + NOT_CONNECTED, + } + + @NonNull private final Context context; + @NonNull private final DcContext dcContext; + @NonNull private final List proxies = new LinkedList<>(); + @Nullable private ItemClickListener itemClickListener; + @Nullable private ProxyState proxyState; + @Nullable private String selectedProxy; + + public ProxyListAdapter(@NonNull Context context) + { + this.context = context; + this.dcContext = DcHelper.getContext(context); + } + + @Override + public int getCount() { + return proxies.size(); + } + + @Override + public Object getItem(int position) { + return proxies.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(final int position, View v, final ViewGroup parent) { + if (v == null) { + v = LayoutInflater.from(context).inflate(R.layout.proxy_list_item, parent, false); + } + + TextView host = v.findViewById(R.id.host); + TextView protocol = v.findViewById(R.id.protocol); + ImageView checkmark = v.findViewById(R.id.checkmark); + TextView status = v.findViewById(R.id.status); + + final String proxyUrl = (String)getItem(position); + final DcLot qrParsed = dcContext.checkQr(proxyUrl); + if (qrParsed.getState() == DcContext.DC_QR_PROXY) { + host.setText(qrParsed.getText1()); + protocol.setText(proxyUrl.split(":", 2)[0]); + } else { + host.setText(proxyUrl); + protocol.setText(R.string.unknown); + } + if (proxyUrl.equals(selectedProxy)) { + checkmark.setVisibility(View.VISIBLE); + status.setVisibility(View.VISIBLE); + status.setText(getConnectivityString()); + } else { + checkmark.setVisibility(View.GONE); + status.setVisibility(View.GONE); + } + + v.setOnClickListener(view -> { + if (itemClickListener != null) { + itemClickListener.onItemClick(proxyUrl); + } + }); + v.findViewById(R.id.share).setOnClickListener(view -> { + if (itemClickListener != null) { + itemClickListener.onItemShare(proxyUrl); + } + }); + v.findViewById(R.id.delete).setOnClickListener(view -> { + if (itemClickListener != null) { + itemClickListener.onItemDelete(proxyUrl); + } + }); + + return v; + } + + public void changeData(String newProxies) { + proxies.clear(); + if (!TextUtils.isEmpty(newProxies)) { + Collections.addAll(proxies, newProxies.split("\n")); + } + selectedProxy = proxies.isEmpty()? null : proxies.get(0); + proxyState = null; // to force notifyDataSetChanged() in refreshConnectivity() + refreshConnectivity(); + } + + public void setSelectedProxy(String proxyUrl) { + selectedProxy = proxyUrl; + notifyDataSetChanged(); + } + + private String getConnectivityString() { + if (proxyState == ProxyState.CONNECTED) { + return context.getString(R.string.connectivity_connected); + } + if (proxyState == ProxyState.CONNECTING) { + return context.getString(R.string.connectivity_connecting); + } + return context.getString(R.string.connectivity_not_connected); + } + + public void refreshConnectivity() { + if (DcHelper.getInt(context, CONFIG_PROXY_ENABLED) != 1) { + if (proxyState != ProxyState.NOT_CONNECTED) { + proxyState = ProxyState.NOT_CONNECTED; + notifyDataSetChanged(); + } + return; + } + + int connectivity = dcContext.getConnectivity(); + if (connectivity >= DcContext.DC_CONNECTIVITY_WORKING) { + if (proxyState != ProxyState.CONNECTED) { + proxyState = ProxyState.CONNECTED; + notifyDataSetChanged(); + } + } else if (connectivity >= DcContext.DC_CONNECTIVITY_CONNECTING) { + if (proxyState != ProxyState.CONNECTING) { + proxyState = ProxyState.CONNECTING; + notifyDataSetChanged(); + } + } else if (proxyState != ProxyState.NOT_CONNECTED) { + proxyState = ProxyState.NOT_CONNECTED; + notifyDataSetChanged(); + } + } + + public void setItemClickListener(@Nullable ItemClickListener listener) { + itemClickListener = listener; + } + + public interface ItemClickListener { + void onItemClick(String proxyUrl); + void onItemShare(String proxyUrl); + void onItemDelete(String proxyUrl); + } + +} diff --git a/src/main/java/org/thoughtcrime/securesms/proxy/ProxySettingsActivity.java b/src/main/java/org/thoughtcrime/securesms/proxy/ProxySettingsActivity.java new file mode 100644 index 000000000..3dcb023e3 --- /dev/null +++ b/src/main/java/org/thoughtcrime/securesms/proxy/ProxySettingsActivity.java @@ -0,0 +1,189 @@ +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.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 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); + } + + 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"); + 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); + } + + @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)) { + 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) { + 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))); + } + + @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 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); + 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); + 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 restartIO() { + DcContext dcContext = DcHelper.getContext(this); + dcContext.stopIo(); + dcContext.startIo(); + } + + @Override + public void handleEvent(@NonNull DcEvent event) { + if (event.getId() == DcContext.DC_EVENT_CONNECTIVITY_CHANGED) { + adapter.refreshConnectivity(); + } + } + +} diff --git a/src/main/res/layout/proxy_list_footer.xml b/src/main/res/layout/proxy_list_footer.xml new file mode 100644 index 000000000..159ec0eb4 --- /dev/null +++ b/src/main/res/layout/proxy_list_footer.xml @@ -0,0 +1,24 @@ + + + + + + diff --git a/src/main/res/layout/proxy_list_header.xml b/src/main/res/layout/proxy_list_header.xml new file mode 100644 index 000000000..1ec7175bb --- /dev/null +++ b/src/main/res/layout/proxy_list_header.xml @@ -0,0 +1,13 @@ + + diff --git a/src/main/res/layout/proxy_list_item.xml b/src/main/res/layout/proxy_list_item.xml new file mode 100644 index 000000000..fc6328587 --- /dev/null +++ b/src/main/res/layout/proxy_list_item.xml @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/res/layout/proxy_settings_activity.xml b/src/main/res/layout/proxy_settings_activity.xml index d3660855e..4570f22f4 100644 --- a/src/main/res/layout/proxy_settings_activity.xml +++ b/src/main/res/layout/proxy_settings_activity.xml @@ -1,128 +1,23 @@ - + android:layout_height="wrap_content" + android:orientation="vertical" + tools:context=".proxy.ProxySettingsActivity"> - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 07ac2131b..c31bd6c1f 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -608,13 +608,22 @@ Proxy Use Proxy - + Add Proxy + Supported proxy types: HTTP(S), SOCKS5 and Shadowsocks. + Enter proxy URL here + Invalid or unsupported proxy URL + Connections + Delete Proxy + Are you sure you want to delete \"%1$s\"? + + SOCKS5 Use SOCKS5 SOCKS5 Host SOCKS5 Port SOCKS5 User SOCKS5 Password + Continue with simplified setup? The entered e-mail address supports a simplified setup (OAuth 2.0).\n\nIn the next step, please allow Delta Chat to act as your Chat over E-mail app.\n\nDelta Chat does not collect user data, everything stays on your device. Certificate Checks