/* * Copyright (c) 2012-2016 Arne Schwabe * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt */ package de.blinkt.openvpn.fragments; import android.app.AlertDialog; import android.content.Context; import androidx.recyclerview.widget.RecyclerView; import android.text.Editable; import android.text.TextWatcher; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.*; import java.util.Arrays; import de.blinkt.openvpn.R; import de.blinkt.openvpn.VpnProfile; import de.blinkt.openvpn.core.Connection; public class ConnectionsAdapter extends RecyclerView.Adapter { private static final int TYPE_NORMAL = 0; private static final int TYPE_FOOTER = TYPE_NORMAL + 1; private final Context mContext; private final VpnProfile mProfile; private final Settings_Connections mConnectionFragment; private Connection[] mConnections; ConnectionsAdapter(Context c, Settings_Connections connections_fragments, VpnProfile vpnProfile) { mContext = c; mConnections = vpnProfile.mConnections; mProfile = vpnProfile; mConnectionFragment = connections_fragments; } @Override public ConnectionsAdapter.ConnectionsHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { LayoutInflater li = LayoutInflater.from(mContext); View card; if (viewType == TYPE_NORMAL) { card = li.inflate(R.layout.server_card, viewGroup, false); } else { // TYPE_FOOTER card = li.inflate(R.layout.server_footer, viewGroup, false); } return new ConnectionsHolder(card, this, viewType); } @Override public void onBindViewHolder(final ConnectionsAdapter.ConnectionsHolder cH, int position) { if (position == mConnections.length) { // Footer return; } final Connection connection = mConnections[position]; cH.mConnection = null; cH.mPortNumberView.setText(connection.mServerPort); cH.mServerNameView.setText(connection.mServerName); cH.mPortNumberView.setText(connection.mServerPort); cH.mRemoteSwitch.setChecked(connection.mEnabled); cH.mProxyNameView.setText(connection.mProxyName); cH.mProxyPortNumberView.setText(connection.mProxyPort); cH.mConnectText.setText(String.valueOf(connection.getTimeout())); cH.mConnectSlider.setProgress(connection.getTimeout()); cH.mProtoGroup.check(connection.mUseUdp ? R.id.udp_proto : R.id.tcp_proto); switch (connection.mProxyType) { case NONE: cH.mProxyGroup.check(R.id.proxy_none); break; case HTTP: cH.mProxyGroup.check(R.id.proxy_http); break; case SOCKS5: cH.mProxyGroup.check(R.id.proxy_socks); break; case ORBOT: cH.mProxyGroup.check(R.id.proxy_orbot); break; } cH.mProxyAuthCb.setChecked(connection.mUseProxyAuth); cH.mProxyAuthUser.setText(connection.mProxyAuthUser); cH.mProxyAuthPassword.setText(connection.mProxyAuthPassword); cH.mCustomOptionsLayout.setVisibility(connection.mUseCustomConfig ? View.VISIBLE : View.GONE); cH.mCustomOptionText.setText(connection.mCustomConfiguration); cH.mCustomOptionCB.setChecked(connection.mUseCustomConfig); cH.mConnection = connection; setVisibilityProxyServer(cH, connection); } private void setVisibilityProxyServer(ConnectionsHolder cH, Connection connection) { int visible = (connection.mProxyType == Connection.ProxyType.HTTP || connection.mProxyType == Connection.ProxyType.SOCKS5) ? View.VISIBLE : View.GONE; int authVisible = (connection.mProxyType == Connection.ProxyType.HTTP) ? View.VISIBLE : View.GONE; cH.mProxyNameView.setVisibility(visible); cH.mProxyPortNumberView.setVisibility(visible); cH.mProxyPortNameView.setVisibility(visible); cH.mProxyNameLabel.setVisibility(visible); cH.mProxyAuthLayout.setVisibility(authVisible); } private void removeRemote(int idx) { Connection[] mConnections2 = Arrays.copyOf(mConnections, mConnections.length - 1); for (int i = idx + 1; i < mConnections.length; i++) { mConnections2[i - 1] = mConnections[i]; } mConnections = mConnections2; } @Override public int getItemCount() { return mConnections.length + 1; //for footer } @Override public int getItemViewType(int position) { if (position == mConnections.length) return TYPE_FOOTER; else return TYPE_NORMAL; } void addRemote() { mConnections = Arrays.copyOf(mConnections, mConnections.length + 1); mConnections[mConnections.length - 1] = new Connection(); notifyItemInserted(mConnections.length - 1); displayWarningIfNoneEnabled(); } void displayWarningIfNoneEnabled() { int showWarning = View.VISIBLE; for (Connection conn : mConnections) { if (conn.mEnabled) showWarning = View.GONE; } mConnectionFragment.setWarningVisible(showWarning); } void saveProfile() { mProfile.mConnections = mConnections; } static abstract class OnTextChangedWatcher implements 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) { } } class ConnectionsHolder extends RecyclerView.ViewHolder { private final EditText mServerNameView; private final EditText mPortNumberView; private final Switch mRemoteSwitch; private final RadioGroup mProtoGroup; private final EditText mCustomOptionText; private final CheckBox mCustomOptionCB; private final View mCustomOptionsLayout; private final ImageButton mDeleteButton; private final EditText mConnectText; private final SeekBar mConnectSlider; private final ConnectionsAdapter mConnectionsAdapter; private final RadioGroup mProxyGroup; private final EditText mProxyNameView; private final EditText mProxyPortNumberView; private final View mProxyPortNameView; private final View mProxyNameLabel; private final View mProxyAuthLayout; private final EditText mProxyAuthUser; private final EditText mProxyAuthPassword; private final CheckBox mProxyAuthCb; protected Connection mConnection; // Set to null on update ConnectionsHolder(View card, ConnectionsAdapter connectionsAdapter, int viewType) { super(card); mServerNameView = card.findViewById(R.id.servername); mPortNumberView = card.findViewById(R.id.portnumber); mRemoteSwitch = card.findViewById(R.id.remoteSwitch); mCustomOptionCB = card.findViewById(R.id.use_customoptions); mCustomOptionText = card.findViewById(R.id.customoptions); mProtoGroup = card.findViewById(R.id.udptcpradiogroup); mCustomOptionsLayout = card.findViewById(R.id.custom_options_layout); mDeleteButton = card.findViewById(R.id.remove_connection); mConnectSlider = card.findViewById(R.id.connect_silder); mConnectText = card.findViewById(R.id.connect_timeout); mProxyGroup = card.findViewById(R.id.proxyradiogroup); mProxyNameView = card.findViewById(R.id.proxyname); mProxyPortNumberView = card.findViewById(R.id.proxyport); mProxyPortNameView = card.findViewById(R.id.proxyport_label); mProxyNameLabel = card.findViewById(R.id.proxyserver_label); mProxyAuthLayout = card.findViewById(R.id.proxyauthlayout); mProxyAuthCb = card.findViewById(R.id.enable_proxy_auth); mProxyAuthUser = card.findViewById(R.id.proxyuser); mProxyAuthPassword = card.findViewById(R.id.proxypassword); mConnectionsAdapter = connectionsAdapter; if (viewType == TYPE_NORMAL) addListeners(); } void addListeners() { mRemoteSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> { if (mConnection != null) { mConnection.mEnabled = isChecked; mConnectionsAdapter.displayWarningIfNoneEnabled(); } }); mProtoGroup.setOnCheckedChangeListener((group, checkedId) -> { if (mConnection != null) { if (checkedId == R.id.udp_proto) mConnection.mUseUdp = true; else if (checkedId == R.id.tcp_proto) mConnection.mUseUdp = false; } }); mProxyGroup.setOnCheckedChangeListener((group, checkedId) -> { if (mConnection != null) { switch (checkedId) { case R.id.proxy_none: mConnection.mProxyType = Connection.ProxyType.NONE; break; case R.id.proxy_http: mConnection.mProxyType = Connection.ProxyType.HTTP; break; case R.id.proxy_socks: mConnection.mProxyType = Connection.ProxyType.SOCKS5; break; case R.id.proxy_orbot: mConnection.mProxyType = Connection.ProxyType.ORBOT; break; } setVisibilityProxyServer(this, mConnection); } }); mProxyAuthCb.setOnCheckedChangeListener((group, isChecked) -> { if (mConnection != null) { mConnection.mUseProxyAuth = isChecked; setVisibilityProxyServer(this, mConnection); } }); mCustomOptionText.addTextChangedListener(new OnTextChangedWatcher() { @Override public void afterTextChanged(Editable s) { if (mConnection != null) mConnection.mCustomConfiguration = s.toString(); } }); mCustomOptionCB.setOnCheckedChangeListener((buttonView, isChecked) -> { if (mConnection != null) { mConnection.mUseCustomConfig = isChecked; mCustomOptionsLayout.setVisibility(mConnection.mUseCustomConfig ? View.VISIBLE : View.GONE); } }); mServerNameView.addTextChangedListener(new OnTextChangedWatcher() { @Override public void afterTextChanged(Editable s) { if (mConnection != null) { mConnection.mServerName = s.toString(); } } }); mPortNumberView.addTextChangedListener(new OnTextChangedWatcher() { @Override public void afterTextChanged(Editable s) { if (mConnection != null) { mConnection.mServerPort = s.toString(); } } }); mProxyNameView.addTextChangedListener(new OnTextChangedWatcher() { @Override public void afterTextChanged(Editable s) { if (mConnection != null) { mConnection.mProxyName = s.toString(); } } }); mProxyPortNumberView.addTextChangedListener(new OnTextChangedWatcher() { @Override public void afterTextChanged(Editable s) { if (mConnection != null) { mConnection.mProxyPort = s.toString(); } } }); mProxyAuthPassword.addTextChangedListener(new OnTextChangedWatcher() { @Override public void afterTextChanged(Editable s) { if (mConnection != null) { mConnection.mProxyAuthPassword = s.toString(); } } }); mProxyAuthUser.addTextChangedListener(new OnTextChangedWatcher() { @Override public void afterTextChanged(Editable s) { if (mConnection != null) { mConnection.mProxyAuthUser = s.toString(); } } }); mCustomOptionText.addTextChangedListener(new OnTextChangedWatcher() { @Override public void afterTextChanged(Editable s) { if (mConnection != null) { mConnection.mCustomConfiguration = s.toString(); } } }); mConnectSlider.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if (fromUser && mConnection != null) { mConnectText.setText(String.valueOf(progress)); mConnection.mConnectTimeout = progress; } } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { } }); mConnectText.addTextChangedListener(new OnTextChangedWatcher() { @Override public void afterTextChanged(Editable s) { if (mConnection != null) { try { int t = Integer.valueOf(String.valueOf(s)); mConnectSlider.setProgress(t); mConnection.mConnectTimeout = t; } catch (Exception ignored) { } } } }); mDeleteButton.setOnClickListener( v -> { AlertDialog.Builder ab = new AlertDialog.Builder(mContext); ab.setTitle(R.string.query_delete_remote); ab.setPositiveButton(R.string.keep, null); ab.setNegativeButton(R.string.delete, (dialog, which) -> { removeRemote(getAdapterPosition()); notifyItemRemoved(getAdapterPosition()); }); ab.create().show(); } ); } } }