added proxy warning when opening external links, added "remember choice" switch to confirm dialog

This commit is contained in:
nuclearfog 2023-07-30 19:20:46 +02:00
parent 588a901db5
commit d7f5d5b0a5
No known key found for this signature in database
GPG Key ID: 03488A185C476379
22 changed files with 130 additions and 77 deletions

View File

@ -11,6 +11,7 @@ import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.config.Configuration;
import org.nuclearfog.twidda.config.GlobalSettings;
import org.nuclearfog.twidda.ui.activities.StatusActivity;
import org.nuclearfog.twidda.ui.dialogs.ConfirmDialog;
import java.util.List;
import java.util.regex.Pattern;
@ -35,16 +36,17 @@ public class LinkUtils {
*
* @param activity activity used to open link
* @param url url to open
* @param redirect true to redirect to browser
*/
public static void openLink(Activity activity, String url) {
GlobalSettings settings = GlobalSettings.get(activity);
if (!url.contains("://")) // check if link contains any scheme like 'http://'
public static void openLink(final Activity activity, String url, boolean redirect) {
final GlobalSettings settings = GlobalSettings.get(activity);
if (!url.contains("://")) { // check if link contains any scheme like 'http://'
url = "https://" + url;
Uri link = Uri.parse(url);
}
// if it's a link to a Tweet, open Tweet in an activity
if ((settings.getLogin().getConfiguration() == Configuration.TWITTER1 || settings.getLogin().getConfiguration() == Configuration.TWITTER2)
if (!redirect && (settings.getLogin().getConfiguration() == Configuration.TWITTER1 || settings.getLogin().getConfiguration() == Configuration.TWITTER2)
&& TWITTER_LINK_PATTERN.matcher(url).matches()) {
Uri link = Uri.parse(url);
List<String> segments = link.getPathSegments();
Intent intent = new Intent(activity, StatusActivity.class);
intent.putExtra(StatusActivity.KEY_STATUS_ID, Long.parseLong(segments.get(2)));
@ -55,16 +57,20 @@ public class LinkUtils {
else {
// replace Twitter link with Nitter if enabled
if (settings.twitterAltSet() && (url.startsWith("https://twitter.com") || url.startsWith("https://mobile.twitter.com"))) {
url = "https://nitter.net" + link.getPath();
link = Uri.parse(url);
url = "https://nitter.net" + Uri.parse(url).getPath();
}
// open link in a browser
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(link);
try {
activity.startActivity(intent);
} catch (ActivityNotFoundException err) {
Toast.makeText(activity.getApplicationContext(), R.string.error_connection_failed, Toast.LENGTH_SHORT).show();
final Uri link = Uri.parse(url);
if (settings.isProxyWarningEnabled()) {
ConfirmDialog dialog = new ConfirmDialog(activity, new ConfirmDialog.OnConfirmListener() {
@Override
public void onConfirm(int type, boolean remember) {
settings.setProxyWarning(remember);
redirectToBrowser(activity, link);
}
});
dialog.show(ConfirmDialog.CONTINUE_BROWSER);
} else {
redirectToBrowser(activity, link);
}
}
}
@ -99,4 +105,16 @@ public class LinkUtils {
Toast.makeText(context.getApplicationContext(), R.string.error_connection_failed, Toast.LENGTH_SHORT).show();
}
}
private static void redirectToBrowser(Activity activity, Uri link) {
// open link in a browser
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(link);
try {
activity.startActivity(intent);
} catch (Exception exception) {
Toast.makeText(activity.getApplicationContext(), R.string.error_connection_failed, Toast.LENGTH_SHORT).show();
}
}
}

View File

@ -86,6 +86,7 @@ public class GlobalSettings {
private static final String PROXY_PORT = "proxy_port";
private static final String PROXY_USER = "proxy_user";
private static final String PROXY_PASS = "proxy_pass";
private static final String PROXY_WARNING = "proxy_warning";
private static final String TREND_LOC = "location";
private static final String TREND_ID = "world_id_long";
private static final String ENABLE_LIKE = "like_enable";
@ -156,6 +157,7 @@ public class GlobalSettings {
private boolean push_enabled;
private boolean isProxyEnabled;
private boolean isProxyAuthSet;
private boolean proxyWarning;
private boolean toolbarOverlap;
private boolean tweetIndicators;
private boolean filterResults;
@ -811,14 +813,17 @@ public class GlobalSettings {
this.proxyPort = proxyPort;
this.proxyUser = proxyUser;
this.proxyPass = proxyPass;
isProxyEnabled = true;
Editor edit = settings.edit();
edit.putBoolean(PROXY_SET, true);
edit.putString(PROXY_ADDR, proxyHost);
edit.putString(PROXY_PORT, proxyPort);
edit.putString(PROXY_USER, proxyUser);
edit.putString(PROXY_PASS, proxyPass);
edit.apply();
setProxyWarning(true);
notifySettingsChange();
}
@ -846,15 +851,15 @@ public class GlobalSettings {
}
/**
* set proxy server connection enabled
* enable/disable proxy warning
*
* @param enable true if proxy connection is set
* @param enable true to enable proxy warning
*/
public void setProxyEnabled(boolean enable) {
isProxyEnabled = enable;
public void setProxyWarning(boolean enable) {
proxyWarning = enable;
Editor edit = settings.edit();
edit.putBoolean(PROXY_SET, enable);
edit.putBoolean(PROXY_WARNING, enable);
edit.apply();
}
@ -946,6 +951,13 @@ public class GlobalSettings {
return isProxyAuthSet;
}
/**
* check if proxy warning should be shown when trying to open external link
*/
public boolean isProxyWarningEnabled() {
return proxyWarning;
}
/**
* Check if current user is logged in
*
@ -1077,6 +1089,7 @@ public class GlobalSettings {
localOnly = settings.getBoolean(MASTODON_LOCAL_TIMELINE, false);
hideSensitive = settings.getBoolean(HIDE_SENSITIVE, true);
floatingEnabled = settings.getBoolean(FLOATING_BUTTON, true);
proxyWarning = settings.getBoolean(PROXY_WARNING, true);
pushInstance = settings.getString(PUSH_INSTANCE, ConstantsKt.INSTANCE_DEFAULT);
proxyHost = settings.getString(PROXY_ADDR, "");
proxyPort = settings.getString(PROXY_PORT, "");

View File

@ -1,9 +1,7 @@
package org.nuclearfog.twidda.ui.activities;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
@ -37,6 +35,7 @@ import org.nuclearfog.twidda.backend.helper.ConnectionResult;
import org.nuclearfog.twidda.backend.helper.update.ConnectionUpdate;
import org.nuclearfog.twidda.backend.utils.AppStyles;
import org.nuclearfog.twidda.backend.utils.ErrorUtils;
import org.nuclearfog.twidda.backend.utils.LinkUtils;
import org.nuclearfog.twidda.config.Configuration;
import org.nuclearfog.twidda.config.GlobalSettings;
import org.nuclearfog.twidda.ui.adapter.listview.DropdownAdapter;
@ -337,12 +336,7 @@ public class LoginActivity extends AppCompatActivity implements ActivityResultCa
* open login page
*/
private void connect(String loginLink) {
Intent loginIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(loginLink));
try {
startActivity(loginIntent);
} catch (ActivityNotFoundException err) {
Toast.makeText(getApplicationContext(), R.string.error_open_link, Toast.LENGTH_SHORT).show();
}
LinkUtils.openLink(this, loginLink, true);
}
/**

View File

@ -198,7 +198,7 @@ public class MessageEditor extends MediaActivity implements OnClickListener, OnC
@Override
public void onConfirm(int type) {
public void onConfirm(int type, boolean remember) {
// retry sending message
if (type == ConfirmDialog.MESSAGE_EDITOR_ERROR) {
sendMessage();

View File

@ -1,6 +1,5 @@
package org.nuclearfog.twidda.ui.activities;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
@ -462,7 +461,7 @@ public class ProfileActivity extends AppCompatActivity implements OnClickListene
@Override
public void onLinkClick(String tag) {
LinkUtils.openLink(this, tag);
LinkUtils.openLink(this, tag, false);
}
@ -509,13 +508,7 @@ public class ProfileActivity extends AppCompatActivity implements OnClickListene
// open link added to profile
else if (v.getId() == R.id.links) {
if (!user.getProfileUrl().isEmpty()) {
String link = user.getProfileUrl();
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(link));
try {
startActivity(intent);
} catch (ActivityNotFoundException err) {
Toast.makeText(getApplicationContext(), R.string.error_connection_failed, Toast.LENGTH_SHORT).show();
}
LinkUtils.openLink(this, user.getProfileUrl(), false);
}
}
// open profile image
@ -548,7 +541,7 @@ public class ProfileActivity extends AppCompatActivity implements OnClickListene
@Override
public void onConfirm(int type) {
public void onConfirm(int type, boolean remember) {
if (user != null) {
// confirmed unfollowing user
if (type == ConfirmDialog.PROFILE_UNFOLLOW) {

View File

@ -229,7 +229,7 @@ public class ProfileEditor extends MediaActivity implements OnClickListener, Asy
@Override
public void onConfirm(int type) {
public void onConfirm(int type, boolean remember) {
// leave without settings
if (type == ConfirmDialog.PROFILE_EDITOR_LEAVE) {
finish();

View File

@ -341,7 +341,7 @@ public class SettingsActivity extends AppCompatActivity implements OnClickListen
@Override
public void onConfirm(int type) {
public void onConfirm(int type, boolean remember) {
// remove account from database
if (type == ConfirmDialog.APP_LOG_OUT) {
settings.setLogin(null, true);
@ -759,7 +759,6 @@ public class SettingsActivity extends AppCompatActivity implements OnClickListen
String proxyUserStr = proxy_user.getText().toString();
String proxyPassStr = proxy_pass.getText().toString();
settings.setProxyServer(proxyAddrStr, proxyPortStr, proxyUserStr, proxyPassStr);
settings.setProxyEnabled(true);
settings.setProxyAuthSet(enable_auth.isChecked());
}
} else {

View File

@ -1,6 +1,5 @@
package org.nuclearfog.twidda.ui.activities;
import android.content.ActivityNotFoundException;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
@ -464,11 +463,8 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
}
// get status link
else if (item.getItemId() == R.id.menu_status_browser) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(status.getUrl()));
try {
startActivity(intent);
} catch (ActivityNotFoundException err) {
Toast.makeText(getApplicationContext(), R.string.error_connection_failed, Toast.LENGTH_SHORT).show();
if (!status.getUrl().isEmpty()) {
LinkUtils.openLink(this, status.getUrl(), true);
}
return true;
}
@ -667,7 +663,7 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
@Override
public void onConfirm(int type) {
public void onConfirm(int type, boolean remember) {
if (type == ConfirmDialog.DELETE_STATUS) {
if (status != null) {
long id = status.getId();
@ -684,7 +680,7 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
@Override
public void onCardClick(Card card, int type) {
if (type == OnCardClickListener.TYPE_LINK) {
LinkUtils.openLink(this, card.getUrl());
LinkUtils.openLink(this, card.getUrl(), false);
} else if (type == OnCardClickListener.TYPE_IMAGE) {
String imageUrl = card.getImageUrl();
if (!imageUrl.isEmpty()) {
@ -739,7 +735,7 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
public void onLinkClick(String tag) {
// proceed click when there is no text blur
if (status_text.getPaint().getMaskFilter() == null) {
LinkUtils.openLink(this, tag);
LinkUtils.openLink(this, tag, false);
}
}

View File

@ -386,7 +386,7 @@ public class StatusEditor extends MediaActivity implements ActivityResultCallbac
@Override
public void onConfirm(int type) {
public void onConfirm(int type, boolean remember) {
// retry uploading status
if (type == ConfirmDialog.STATUS_EDITOR_ERROR) {
updateStatus();

View File

@ -260,7 +260,7 @@ public class UserlistActivity extends AppCompatActivity implements ActivityResul
@Override
public void onConfirm(int type) {
public void onConfirm(int type, boolean remember) {
// delete user list
if (type == ConfirmDialog.LIST_DELETE && userList != null) {
if (listLoaderAsync.isIdle()) {

View File

@ -182,7 +182,7 @@ public class UserlistEditor extends AppCompatActivity implements OnClickListener
@Override
public void onConfirm(int type) {
public void onConfirm(int type, boolean remember) {
// retry updating list
if (type == ConfirmDialog.LIST_EDITOR_ERROR) {
updateList();

View File

@ -7,6 +7,7 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.TextView;
import androidx.annotation.Nullable;
@ -143,9 +144,15 @@ public class ConfirmDialog extends Dialog implements OnClickListener {
*/
public static final int FILTER_REMOVE = 626;
/**
* show notification when opening an external link while proxy is enabled
*/
public static final int CONTINUE_BROWSER = 627;
private TextView title, message;
private TextView title, message, remember_label;
private Button confirm, cancel;
private CompoundButton remember;
private OnConfirmListener listener;
private GlobalSettings settings;
@ -172,6 +179,8 @@ public class ConfirmDialog extends Dialog implements OnClickListener {
cancel = findViewById(R.id.confirm_no);
title = findViewById(R.id.confirm_title);
message = findViewById(R.id.confirm_message);
remember = findViewById(R.id.confirm_remember);
remember_label = findViewById(R.id.confirm_remember_label);
AppStyles.setTheme(root, settings.getPopupColor());
@ -282,6 +291,13 @@ public class ConfirmDialog extends Dialog implements OnClickListener {
case FILTER_REMOVE:
messageRes = R.string.confirm_remove_filter;
break;
case CONTINUE_BROWSER:
remember_label.setVisibility(View.VISIBLE);
remember.setVisibility(View.VISIBLE);
titleRes = R.string.confirm_warning;
messageRes = R.string.confirm_proxy_bypass;
break;
}
// setup title
title.setVisibility(titleVis);
@ -345,7 +361,7 @@ public class ConfirmDialog extends Dialog implements OnClickListener {
Object tag = v.getTag();
if (tag instanceof Integer) {
int type = (int) tag;
listener.onConfirm(type);
listener.onConfirm(type, remember.isChecked());
}
dismiss();
} else if (v.getId() == R.id.confirm_no) {
@ -361,8 +377,9 @@ public class ConfirmDialog extends Dialog implements OnClickListener {
/**
* called when the positive button was clicked
*
* @param type type of dialog
* @param type type of dialog
* @param remember true if "remember choice" is checked
*/
void onConfirm(int type);
void onConfirm(int type, boolean remember);
}
}

View File

@ -126,7 +126,7 @@ public class AccountFragment extends ListFragment implements OnAccountClickListe
@Override
public void onConfirm(int type) {
public void onConfirm(int type, boolean remember) {
if (type == ConfirmDialog.REMOVE_ACCOUNT) {
load(AccountParameter.DELETE);
}

View File

@ -125,7 +125,7 @@ public class DomainFragment extends ListFragment implements OnDomainClickListene
@Override
public void onConfirm(int type) {
public void onConfirm(int type, boolean remember) {
if (type == ConfirmDialog.DOMAIN_BLOCK_REMOVE) {
DomainParam param = new DomainParam(DomainParam.MODE_UNBLOCK, DomainAdapter.NO_INDEX, DomainParam.NO_CURSOR, selectedDomain);
domainAction.execute(param, this);

View File

@ -48,7 +48,7 @@ public class FieldFragment extends ListFragment implements OnLinkClickListener {
@Override
public void onLinkClick(String url) {
LinkUtils.openLink(requireActivity(), url);
LinkUtils.openLink(requireActivity(), url, false);
}

View File

@ -95,7 +95,7 @@ public class FilterFragment extends ListFragment implements OnFilterClickListene
@Override
public void onConfirm(int type) {
public void onConfirm(int type, boolean remember) {
if (type == ConfirmDialog.FILTER_REMOVE) {
FilterActionParam param = new FilterActionParam(FilterActionParam.DELETE, selection.getId(), null);
filterAction.execute(param, filterRemoveCallback);

View File

@ -109,7 +109,7 @@ public class MessageFragment extends ListFragment implements OnMessageClickListe
@Override
public void onLinkClick(String tag) {
LinkUtils.openLink(requireActivity(), tag);
LinkUtils.openLink(requireActivity(), tag, false);
}
@ -162,7 +162,7 @@ public class MessageFragment extends ListFragment implements OnMessageClickListe
@Override
public void onConfirm(int type) {
public void onConfirm(int type, boolean remember) {
if (type == ConfirmDialog.MESSAGE_DELETE) {
MessageLoaderParam param = new MessageLoaderParam(MessageLoaderParam.DELETE, 0, selectedId, "");
messageLoader.execute(param, this);

View File

@ -170,7 +170,7 @@ public class NotificationFragment extends ListFragment implements OnNotification
@Override
public void onConfirm(int type) {
public void onConfirm(int type, boolean remember) {
if (type == ConfirmDialog.NOTIFICATION_DISMISS) {
if (select != null) {
NotificationActionParam param = new NotificationActionParam(NotificationActionParam.DISMISS, select.getId());

View File

@ -276,7 +276,7 @@ public class UserFragment extends ListFragment implements UserClickListener, OnC
@Override
public void onConfirm(int type) {
public void onConfirm(int type, boolean remember) {
// remove user from list
if (type == ConfirmDialog.LIST_REMOVE_USER) {
if (userlistManager.isIdle() && selectedUser != null) {

View File

@ -29,22 +29,36 @@
android:maxLines="@integer/confirm_message_max_ines"
android:scrollbars="vertical"
android:textSize="@dimen/confirm_message_fontsize"
app:layout_constraintBottom_toTopOf="@id/confirm_barrier"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/confirm_title" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/confirm_barrier"
<com.kyleduo.switchbutton.SwitchButton
android:id="@+id/confirm_remember"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
android:layout_marginStart="@dimen/confirm_button_margin"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/confirm_message" />
<TextView
android:id="@+id/confirm_remember_label"
android:layout_width="0dp"
android:layout_height="0dp"
app:barrierDirection="top"
app:constraint_referenced_ids="confirm_no,confirm_yes"
tools:layout_editor_absoluteY="96dp" />
android:layout_height="wrap_content"
android:visibility="gone"
android:lines="1"
android:text="@string/confirm_remember"
android:layout_marginStart="@dimen/confirm_text_margin"
android:layout_marginEnd="@dimen/confirm_text_margin"
app:layout_constraintStart_toEndOf="@id/confirm_remember"
app:layout_constraintTop_toTopOf="@id/confirm_remember"
app:layout_constraintBottom_toBottomOf="@id/confirm_remember"
app:layout_constraintEnd_toEndOf="parent" />
<Button
android:id="@+id/confirm_no"
style="@style/FeedbackButton"
android:layout_width="wrap_content"
android:layout_height="@dimen/confirm_button_height"
android:layout_margin="@dimen/confirm_button_margin"
@ -55,12 +69,13 @@
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintHorizontal_bias="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/confirm_remember"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/confirm_yes" />
app:layout_constraintEnd_toStartOf="@id/confirm_yes"
style="@style/FeedbackButton" />
<Button
android:id="@+id/confirm_yes"
style="@style/FeedbackButton"
android:layout_width="wrap_content"
android:layout_height="@dimen/confirm_button_height"
android:layout_margin="@dimen/confirm_button_margin"
@ -69,7 +84,9 @@
android:text="@android:string/ok"
android:textSize="@dimen/confirm_button_fontsize"
app:layout_constraintStart_toEndOf="@id/confirm_no"
app:layout_constraintTop_toBottomOf="@id/confirm_remember"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
app:layout_constraintEnd_toEndOf="parent"
style="@style/FeedbackButton" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -208,6 +208,9 @@
<string name="toolbar_title_filter">Status-Filter</string>
<string name="status_translate_text">übersetzen</string>
<string name="field_verified">verifiziert:\u0020</string>
<string name="confirm_remember">Entscheidung merken</string>
<string name="confirm_warning">Warnung</string>
<string name="confirm_proxy_bypass">Das Öffnen des Links umgeht die eingestellte Proxy-Verbindung. Fortsetzen?</string>
<string name="confirm_remove_account">Account aus der Liste entfernen?</string>
<string name="menu_select_account">Logins</string>
<string name="menu_add_account">Account hinzufügen</string>

View File

@ -327,6 +327,9 @@
<string name="status_translate_source">Translated by:\u0020</string>
<string name="status_translate_source_language">Language:\u0020</string>
<string name="field_verified">verified:\u0020</string>
<string name="confirm_remember">remember choice</string>
<string name="confirm_warning">Warning</string>
<string name="confirm_proxy_bypass">Opening an external link would bypass proxy connection. Proceed?</string>
<string name="confirm_remove_account">remove account from list?</string>
<string name="confirm_remove_filter">delete filter?</string>
<string name="account_user_unnamed">\'unnamed\'</string>