mirror of
https://codeberg.org/tom79/Fedilab.git
synced 2024-12-23 01:00:04 +02:00
Long press links
This commit is contained in:
parent
a8afc99401
commit
27ef0003d7
7 changed files with 475 additions and 5 deletions
|
@ -27,13 +27,18 @@ import androidx.lifecycle.ViewModelProvider;
|
||||||
import androidx.lifecycle.ViewModelStoreOwner;
|
import androidx.lifecycle.ViewModelStoreOwner;
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import app.fedilab.android.BaseMainActivity;
|
import app.fedilab.android.BaseMainActivity;
|
||||||
import app.fedilab.android.R;
|
import app.fedilab.android.R;
|
||||||
import app.fedilab.android.activities.ComposeActivity;
|
import app.fedilab.android.activities.ComposeActivity;
|
||||||
|
import app.fedilab.android.activities.MainActivity;
|
||||||
import app.fedilab.android.client.entities.Account;
|
import app.fedilab.android.client.entities.Account;
|
||||||
|
import app.fedilab.android.client.mastodon.MastodonSearchService;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Results;
|
||||||
import app.fedilab.android.client.mastodon.entities.Status;
|
import app.fedilab.android.client.mastodon.entities.Status;
|
||||||
import app.fedilab.android.exception.DBException;
|
import app.fedilab.android.exception.DBException;
|
||||||
import app.fedilab.android.ui.drawer.AccountsSearchAdapter;
|
import app.fedilab.android.ui.drawer.AccountsSearchAdapter;
|
||||||
|
@ -41,6 +46,11 @@ import app.fedilab.android.viewmodel.mastodon.AccountsVM;
|
||||||
import app.fedilab.android.viewmodel.mastodon.SearchVM;
|
import app.fedilab.android.viewmodel.mastodon.SearchVM;
|
||||||
import app.fedilab.android.viewmodel.mastodon.StatusesVM;
|
import app.fedilab.android.viewmodel.mastodon.StatusesVM;
|
||||||
import es.dmoral.toasty.Toasty;
|
import es.dmoral.toasty.Toasty;
|
||||||
|
import okhttp3.OkHttpClient;
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.Response;
|
||||||
|
import retrofit2.Retrofit;
|
||||||
|
import retrofit2.converter.gson.GsonConverterFactory;
|
||||||
|
|
||||||
public class CrossActionHelper {
|
public class CrossActionHelper {
|
||||||
|
|
||||||
|
@ -235,6 +245,122 @@ public class CrossActionHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static MastodonSearchService init(Context context, @NonNull String instance) {
|
||||||
|
final OkHttpClient okHttpClient = new OkHttpClient.Builder()
|
||||||
|
.readTimeout(60, TimeUnit.SECONDS)
|
||||||
|
.connectTimeout(60, TimeUnit.SECONDS)
|
||||||
|
.proxy(Helper.getProxy(context))
|
||||||
|
.build();
|
||||||
|
Retrofit retrofit = new Retrofit.Builder()
|
||||||
|
.baseUrl("https://" + instance + "/api/v2/")
|
||||||
|
.addConverterFactory(GsonConverterFactory.create())
|
||||||
|
.client(okHttpClient)
|
||||||
|
.build();
|
||||||
|
return retrofit.create(MastodonSearchService.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch and federate the remote status
|
||||||
|
*/
|
||||||
|
public static void fetchRemoteStatus(@NonNull Context context, @NonNull Account ownerAccount, Status targetedStatus, Callback callback) {
|
||||||
|
MastodonSearchService mastodonSearchService = init(context, MainActivity.currentInstance);
|
||||||
|
new Thread(() -> {
|
||||||
|
Call<Results> resultsCall = mastodonSearchService.search(ownerAccount.token, targetedStatus.url, null, "statuses", false, true, false, 0, null, null, 1);
|
||||||
|
Results results = null;
|
||||||
|
if (resultsCall != null) {
|
||||||
|
try {
|
||||||
|
Response<Results> resultsResponse = resultsCall.execute();
|
||||||
|
if (resultsResponse.isSuccessful()) {
|
||||||
|
results = resultsResponse.body();
|
||||||
|
if (results != null) {
|
||||||
|
if (results.statuses == null) {
|
||||||
|
results.statuses = new ArrayList<>();
|
||||||
|
} else {
|
||||||
|
results.statuses = SpannableHelper.convertStatus(context, results.statuses);
|
||||||
|
}
|
||||||
|
if (results.accounts == null) {
|
||||||
|
results.accounts = new ArrayList<>();
|
||||||
|
}
|
||||||
|
if (results.hashtags == null) {
|
||||||
|
results.hashtags = new ArrayList<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||||
|
Results finalResults = results;
|
||||||
|
Runnable myRunnable = () -> {
|
||||||
|
if (finalResults != null && finalResults.statuses != null && finalResults.statuses.size() > 0) {
|
||||||
|
callback.federatedStatus(finalResults.statuses.get(0));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
mainHandler.post(myRunnable);
|
||||||
|
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch and federate the remote status
|
||||||
|
*/
|
||||||
|
public static void fetchRemoteAccount(@NonNull Context context, @NonNull Account ownerAccount, app.fedilab.android.client.mastodon.entities.Account targetedAccount, Callback callback) {
|
||||||
|
|
||||||
|
|
||||||
|
MastodonSearchService mastodonSearchService = init(context, MainActivity.currentInstance);
|
||||||
|
String search;
|
||||||
|
if (targetedAccount.acct.contains("@")) { //Not from same instance
|
||||||
|
search = targetedAccount.acct;
|
||||||
|
} else {
|
||||||
|
search = targetedAccount.acct + "@" + BaseMainActivity.currentInstance;
|
||||||
|
}
|
||||||
|
new Thread(() -> {
|
||||||
|
Call<Results> resultsCall = mastodonSearchService.search(ownerAccount.token, search, null, "accounts", false, true, false, 0, null, null, 1);
|
||||||
|
Results results = null;
|
||||||
|
if (resultsCall != null) {
|
||||||
|
try {
|
||||||
|
Response<Results> resultsResponse = resultsCall.execute();
|
||||||
|
if (resultsResponse.isSuccessful()) {
|
||||||
|
results = resultsResponse.body();
|
||||||
|
if (results != null) {
|
||||||
|
if (results.statuses == null) {
|
||||||
|
results.statuses = new ArrayList<>();
|
||||||
|
} else {
|
||||||
|
results.statuses = SpannableHelper.convertStatus(context, results.statuses);
|
||||||
|
}
|
||||||
|
if (results.accounts == null) {
|
||||||
|
results.accounts = new ArrayList<>();
|
||||||
|
}
|
||||||
|
if (results.hashtags == null) {
|
||||||
|
results.hashtags = new ArrayList<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||||
|
Results finalResults = results;
|
||||||
|
Runnable myRunnable = () -> {
|
||||||
|
if (finalResults != null && finalResults.accounts != null && finalResults.accounts.size() > 0) {
|
||||||
|
callback.federatedAccount(finalResults.accounts.get(0));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
mainHandler.post(myRunnable);
|
||||||
|
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Callback {
|
||||||
|
void federatedStatus(Status status);
|
||||||
|
|
||||||
|
void federatedAccount(app.fedilab.android.client.mastodon.entities.Account account);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public enum TypeOfCrossAction {
|
public enum TypeOfCrossAction {
|
||||||
FOLLOW_ACTION,
|
FOLLOW_ACTION,
|
||||||
UNFOLLOW_ACTION,
|
UNFOLLOW_ACTION,
|
||||||
|
|
|
@ -270,7 +270,10 @@ public class Helper {
|
||||||
public static final Pattern mediumPattern = Pattern.compile("([\\w@-]*)?\\.?medium.com/@?([/\\w-]+)");
|
public static final Pattern mediumPattern = Pattern.compile("([\\w@-]*)?\\.?medium.com/@?([/\\w-]+)");
|
||||||
public static final Pattern wikipediaPattern = Pattern.compile("([\\w_-]+)\\.wikipedia.org/(((?!([\"'<])).)*)");
|
public static final Pattern wikipediaPattern = Pattern.compile("([\\w_-]+)\\.wikipedia.org/(((?!([\"'<])).)*)");
|
||||||
public static final Pattern codePattern = Pattern.compile("code=([\\w-]+)");
|
public static final Pattern codePattern = Pattern.compile("code=([\\w-]+)");
|
||||||
|
public static final Pattern urlPattern = Pattern.compile(
|
||||||
|
"(?i)\\b((?:[a-z][\\w-]+:(?:/{1,3}|[a-z0-9%])|www\\d{0,3}[.]|[a-z0-9.\\-]+[.][a-z]{2,10}/)(?:[^\\s()<>]+|\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\))+(?:\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\)|[^\\s`!()\\[\\]{};:'\".,<>?«»“”‘’]))",
|
||||||
|
|
||||||
|
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
|
||||||
/*
|
/*
|
||||||
* List from ClearUrls
|
* List from ClearUrls
|
||||||
* https://gitlab.com/KevinRoebert/ClearUrls/blob/master/data/data.min.json#L106
|
* https://gitlab.com/KevinRoebert/ClearUrls/blob/master/data/data.min.json#L106
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
package app.fedilab.android.helper;
|
||||||
|
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.text.Layout;
|
||||||
|
import android.text.Selection;
|
||||||
|
import android.text.Spannable;
|
||||||
|
import android.text.method.LinkMovementMethod;
|
||||||
|
import android.text.method.MovementMethod;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
|
||||||
|
//https://stackoverflow.com/a/20435892
|
||||||
|
public class LongClickLinkMovementMethod extends LinkMovementMethod {
|
||||||
|
|
||||||
|
private static LongClickLinkMovementMethod sInstance;
|
||||||
|
private Handler mLongClickHandler;
|
||||||
|
private boolean mIsLongPressed = false;
|
||||||
|
|
||||||
|
public static MovementMethod getInstance() {
|
||||||
|
if (sInstance == null) {
|
||||||
|
sInstance = new LongClickLinkMovementMethod();
|
||||||
|
sInstance.mLongClickHandler = new Handler();
|
||||||
|
}
|
||||||
|
|
||||||
|
return sInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onTouchEvent(final TextView widget, Spannable buffer,
|
||||||
|
MotionEvent event) {
|
||||||
|
int action = event.getAction();
|
||||||
|
if (action == MotionEvent.ACTION_CANCEL) {
|
||||||
|
if (mLongClickHandler != null) {
|
||||||
|
mLongClickHandler.removeCallbacksAndMessages(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action == MotionEvent.ACTION_UP ||
|
||||||
|
action == MotionEvent.ACTION_DOWN) {
|
||||||
|
int x = (int) event.getX();
|
||||||
|
int y = (int) event.getY();
|
||||||
|
|
||||||
|
x -= widget.getTotalPaddingLeft();
|
||||||
|
y -= widget.getTotalPaddingTop();
|
||||||
|
|
||||||
|
x += widget.getScrollX();
|
||||||
|
y += widget.getScrollY();
|
||||||
|
|
||||||
|
Layout layout = widget.getLayout();
|
||||||
|
int line = layout.getLineForVertical(y);
|
||||||
|
int off = layout.getOffsetForHorizontal(line, x);
|
||||||
|
|
||||||
|
final LongClickableSpan[] link = buffer.getSpans(off, off, LongClickableSpan.class);
|
||||||
|
|
||||||
|
if (link.length != 0) {
|
||||||
|
if (action == MotionEvent.ACTION_UP) {
|
||||||
|
if (mLongClickHandler != null) {
|
||||||
|
mLongClickHandler.removeCallbacksAndMessages(null);
|
||||||
|
}
|
||||||
|
if (!mIsLongPressed) {
|
||||||
|
link[0].onClick(widget);
|
||||||
|
}
|
||||||
|
mIsLongPressed = false;
|
||||||
|
} else {
|
||||||
|
Selection.setSelection(buffer,
|
||||||
|
buffer.getSpanStart(link[0]),
|
||||||
|
buffer.getSpanEnd(link[0]));
|
||||||
|
int LONG_CLICK_TIME = 1000;
|
||||||
|
mLongClickHandler.postDelayed(() -> {
|
||||||
|
link[0].onLongClick(widget);
|
||||||
|
mIsLongPressed = true;
|
||||||
|
widget.invalidate();
|
||||||
|
}, LONG_CLICK_TIME);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.onTouchEvent(widget, buffer, event);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package app.fedilab.android.helper;
|
||||||
|
|
||||||
|
import android.text.style.ClickableSpan;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
public abstract class LongClickableSpan extends ClickableSpan {
|
||||||
|
|
||||||
|
abstract public void onLongClick(View view);
|
||||||
|
|
||||||
|
}
|
|
@ -15,15 +15,21 @@ package app.fedilab.android.helper;
|
||||||
* see <http://www.gnu.org/licenses>. */
|
* see <http://www.gnu.org/licenses>. */
|
||||||
|
|
||||||
|
|
||||||
|
import static app.fedilab.android.helper.Helper.USER_AGENT;
|
||||||
import static app.fedilab.android.helper.Helper.convertDpToPixel;
|
import static app.fedilab.android.helper.Helper.convertDpToPixel;
|
||||||
|
import static app.fedilab.android.helper.Helper.urlPattern;
|
||||||
import static app.fedilab.android.helper.ThemeHelper.linkColor;
|
import static app.fedilab.android.helper.ThemeHelper.linkColor;
|
||||||
|
|
||||||
|
import android.content.ClipData;
|
||||||
|
import android.content.ClipboardManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.text.Spannable;
|
import android.text.Spannable;
|
||||||
import android.text.SpannableString;
|
import android.text.SpannableString;
|
||||||
|
@ -34,9 +40,12 @@ import android.text.style.ClickableSpan;
|
||||||
import android.text.style.ImageSpan;
|
import android.text.style.ImageSpan;
|
||||||
import android.text.style.URLSpan;
|
import android.text.style.URLSpan;
|
||||||
import android.util.Patterns;
|
import android.util.Patterns;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
import com.bumptech.glide.Glide;
|
import com.bumptech.glide.Glide;
|
||||||
|
@ -47,15 +56,23 @@ import com.github.penfeizhou.animation.gif.GifDrawable;
|
||||||
import com.github.penfeizhou.animation.gif.decode.GifParser;
|
import com.github.penfeizhou.animation.gif.decode.GifParser;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import javax.net.ssl.HttpsURLConnection;
|
||||||
|
|
||||||
import app.fedilab.android.R;
|
import app.fedilab.android.R;
|
||||||
|
import app.fedilab.android.activities.ContextActivity;
|
||||||
import app.fedilab.android.activities.HashTagActivity;
|
import app.fedilab.android.activities.HashTagActivity;
|
||||||
|
import app.fedilab.android.activities.MainActivity;
|
||||||
import app.fedilab.android.activities.ProfileActivity;
|
import app.fedilab.android.activities.ProfileActivity;
|
||||||
import app.fedilab.android.client.mastodon.entities.Account;
|
import app.fedilab.android.client.mastodon.entities.Account;
|
||||||
import app.fedilab.android.client.mastodon.entities.Attachment;
|
import app.fedilab.android.client.mastodon.entities.Attachment;
|
||||||
|
@ -64,6 +81,8 @@ import app.fedilab.android.client.mastodon.entities.Field;
|
||||||
import app.fedilab.android.client.mastodon.entities.Mention;
|
import app.fedilab.android.client.mastodon.entities.Mention;
|
||||||
import app.fedilab.android.client.mastodon.entities.Poll;
|
import app.fedilab.android.client.mastodon.entities.Poll;
|
||||||
import app.fedilab.android.client.mastodon.entities.Status;
|
import app.fedilab.android.client.mastodon.entities.Status;
|
||||||
|
import app.fedilab.android.databinding.PopupLinksBinding;
|
||||||
|
import es.dmoral.toasty.Toasty;
|
||||||
|
|
||||||
public class SpannableHelper {
|
public class SpannableHelper {
|
||||||
|
|
||||||
|
@ -188,11 +207,172 @@ public class SpannableHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matchStart >= 0 && matchEnd <= content.length() && matchEnd >= matchStart) {
|
if (matchStart >= 0 && matchEnd <= content.length() && matchEnd >= matchStart) {
|
||||||
content.setSpan(new ClickableSpan() {
|
content.setSpan(new LongClickableSpan() {
|
||||||
|
@Override
|
||||||
|
public void onLongClick(View view) {
|
||||||
|
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(view.getContext(), Helper.dialogStyle());
|
||||||
|
PopupLinksBinding popupLinksBinding = PopupLinksBinding.inflate(LayoutInflater.from(context));
|
||||||
|
dialogBuilder.setView(popupLinksBinding.getRoot());
|
||||||
|
AlertDialog alertDialog = dialogBuilder.create();
|
||||||
|
alertDialog.show();
|
||||||
|
|
||||||
|
popupLinksBinding.displayFullLink.setOnClickListener(v -> {
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(context, Helper.dialogStyle());
|
||||||
|
builder.setMessage(url);
|
||||||
|
builder.setTitle(context.getString(R.string.display_full_link));
|
||||||
|
builder.setPositiveButton(R.string.close, (dialog, which) -> dialog.dismiss())
|
||||||
|
.show();
|
||||||
|
alertDialog.dismiss();
|
||||||
|
});
|
||||||
|
popupLinksBinding.shareLink.setOnClickListener(v -> {
|
||||||
|
Intent sendIntent = new Intent(Intent.ACTION_SEND);
|
||||||
|
sendIntent.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.shared_via));
|
||||||
|
sendIntent.putExtra(Intent.EXTRA_TEXT, url);
|
||||||
|
sendIntent.setType("text/plain");
|
||||||
|
context.startActivity(Intent.createChooser(sendIntent, context.getString(R.string.share_with)));
|
||||||
|
alertDialog.dismiss();
|
||||||
|
});
|
||||||
|
|
||||||
|
popupLinksBinding.openOtherApp.setOnClickListener(v -> {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
|
intent.setData(Uri.parse(url));
|
||||||
|
try {
|
||||||
|
context.startActivity(intent);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Toasty.error(context, context.getString(R.string.toast_error), Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
alertDialog.dismiss();
|
||||||
|
});
|
||||||
|
|
||||||
|
popupLinksBinding.copyLink.setOnClickListener(v -> {
|
||||||
|
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||||
|
ClipData clip = ClipData.newPlainText(Helper.CLIP_BOARD, url);
|
||||||
|
if (clipboard != null) {
|
||||||
|
clipboard.setPrimaryClip(clip);
|
||||||
|
Toasty.info(context, context.getString(R.string.clipboard_url), Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
alertDialog.dismiss();
|
||||||
|
});
|
||||||
|
|
||||||
|
popupLinksBinding.checkRedirect.setOnClickListener(v -> {
|
||||||
|
try {
|
||||||
|
|
||||||
|
URL finalUrlCheck = new URL(url);
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
String redirect = null;
|
||||||
|
HttpsURLConnection httpsURLConnection = (HttpsURLConnection) finalUrlCheck.openConnection();
|
||||||
|
httpsURLConnection.setConnectTimeout(10 * 1000);
|
||||||
|
httpsURLConnection.setRequestProperty("http.keepAlive", "false");
|
||||||
|
httpsURLConnection.setRequestProperty("User-Agent", USER_AGENT);
|
||||||
|
httpsURLConnection.setRequestMethod("HEAD");
|
||||||
|
if (httpsURLConnection.getResponseCode() == 301 || httpsURLConnection.getResponseCode() == 302) {
|
||||||
|
Map<String, List<String>> map = httpsURLConnection.getHeaderFields();
|
||||||
|
for (Map.Entry<String, List<String>> entry : map.entrySet()) {
|
||||||
|
if (entry.toString().toLowerCase().startsWith("location")) {
|
||||||
|
Matcher matcher = urlPattern.matcher(entry.toString());
|
||||||
|
if (matcher.find()) {
|
||||||
|
redirect = matcher.group(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
httpsURLConnection.getInputStream().close();
|
||||||
|
if (redirect != null && redirect.compareTo(url) != 0) {
|
||||||
|
URL redirectURL = new URL(redirect);
|
||||||
|
String host = redirectURL.getHost();
|
||||||
|
String protocol = redirectURL.getProtocol();
|
||||||
|
if (protocol == null || host == null) {
|
||||||
|
redirect = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Handler mainHandler = new Handler(context.getMainLooper());
|
||||||
|
String finalRedirect = redirect;
|
||||||
|
Runnable myRunnable = () -> {
|
||||||
|
AlertDialog.Builder builder1 = new AlertDialog.Builder(context, Helper.dialogStyle());
|
||||||
|
if (finalRedirect != null) {
|
||||||
|
builder1.setMessage(context.getString(R.string.redirect_detected, url, finalRedirect));
|
||||||
|
builder1.setNegativeButton(R.string.copy_link, (dialog, which) -> {
|
||||||
|
ClipboardManager clipboard1 = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||||
|
ClipData clip1 = ClipData.newPlainText(Helper.CLIP_BOARD, finalRedirect);
|
||||||
|
if (clipboard1 != null) {
|
||||||
|
clipboard1.setPrimaryClip(clip1);
|
||||||
|
Toasty.info(context, context.getString(R.string.clipboard_url), Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
dialog.dismiss();
|
||||||
|
});
|
||||||
|
builder1.setNeutralButton(R.string.share_link, (dialog, which) -> {
|
||||||
|
Intent sendIntent1 = new Intent(Intent.ACTION_SEND);
|
||||||
|
sendIntent1.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.shared_via));
|
||||||
|
sendIntent1.putExtra(Intent.EXTRA_TEXT, url);
|
||||||
|
sendIntent1.setType("text/plain");
|
||||||
|
context.startActivity(Intent.createChooser(sendIntent1, context.getString(R.string.share_with)));
|
||||||
|
dialog.dismiss();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
builder1.setMessage(R.string.no_redirect);
|
||||||
|
}
|
||||||
|
builder1.setTitle(context.getString(R.string.check_redirect));
|
||||||
|
builder1.setPositiveButton(R.string.close, (dialog, which) -> dialog.dismiss())
|
||||||
|
.show();
|
||||||
|
|
||||||
|
};
|
||||||
|
mainHandler.post(myRunnable);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
}).start();
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
alertDialog.dismiss();
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(@NonNull View textView) {
|
public void onClick(@NonNull View textView) {
|
||||||
textView.setTag(CLICKABLE_SPAN);
|
textView.setTag(CLICKABLE_SPAN);
|
||||||
Helper.openBrowser(context, newURL);
|
Pattern link = Pattern.compile("https?://([\\da-z.-]+\\.[a-z.]{2,10})/(@[\\w._-]*[0-9]*)(/[0-9]+)?$");
|
||||||
|
Matcher matcherLink = link.matcher(url);
|
||||||
|
if (matcherLink.find() && !url.contains("medium.com")) {
|
||||||
|
if (matcherLink.group(3) != null && Objects.requireNonNull(matcherLink.group(3)).length() > 0) { //It's a toot
|
||||||
|
CrossActionHelper.fetchRemoteStatus(context, MainActivity.accountWeakReference.get(), status, new CrossActionHelper.Callback() {
|
||||||
|
@Override
|
||||||
|
public void federatedStatus(Status status) {
|
||||||
|
Intent intent = new Intent(context, ContextActivity.class);
|
||||||
|
intent.putExtra(Helper.ARG_STATUS, status);
|
||||||
|
context.startActivity(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void federatedAccount(Account account) {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {//It's an account
|
||||||
|
CrossActionHelper.fetchRemoteAccount(context, MainActivity.accountWeakReference.get(), status.account, new CrossActionHelper.Callback() {
|
||||||
|
@Override
|
||||||
|
public void federatedStatus(Status status) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void federatedAccount(Account account) {
|
||||||
|
Intent intent = new Intent(context, ProfileActivity.class);
|
||||||
|
Bundle b = new Bundle();
|
||||||
|
b.putSerializable(Helper.ARG_ACCOUNT, account);
|
||||||
|
intent.putExtras(b);
|
||||||
|
context.startActivity(intent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Helper.openBrowser(context, newURL);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -42,7 +42,6 @@ import android.text.Layout;
|
||||||
import android.text.SpannableString;
|
import android.text.SpannableString;
|
||||||
import android.text.Spanned;
|
import android.text.Spanned;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.method.LinkMovementMethod;
|
|
||||||
import android.text.style.ForegroundColorSpan;
|
import android.text.style.ForegroundColorSpan;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.ContextThemeWrapper;
|
import android.view.ContextThemeWrapper;
|
||||||
|
@ -116,6 +115,7 @@ import app.fedilab.android.databinding.LayoutMediaBinding;
|
||||||
import app.fedilab.android.databinding.LayoutPollItemBinding;
|
import app.fedilab.android.databinding.LayoutPollItemBinding;
|
||||||
import app.fedilab.android.helper.CrossActionHelper;
|
import app.fedilab.android.helper.CrossActionHelper;
|
||||||
import app.fedilab.android.helper.Helper;
|
import app.fedilab.android.helper.Helper;
|
||||||
|
import app.fedilab.android.helper.LongClickLinkMovementMethod;
|
||||||
import app.fedilab.android.helper.MastodonHelper;
|
import app.fedilab.android.helper.MastodonHelper;
|
||||||
import app.fedilab.android.helper.SpannableHelper;
|
import app.fedilab.android.helper.SpannableHelper;
|
||||||
import app.fedilab.android.helper.ThemeHelper;
|
import app.fedilab.android.helper.ThemeHelper;
|
||||||
|
@ -940,8 +940,8 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
||||||
holder.binding.mediaContainer.setVisibility(View.GONE);
|
holder.binding.mediaContainer.setVisibility(View.GONE);
|
||||||
holder.binding.attachmentsListContainer.setVisibility(View.GONE);
|
holder.binding.attachmentsListContainer.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
holder.binding.statusContent.setMovementMethod(LinkMovementMethod.getInstance());
|
holder.binding.statusContent.setMovementMethod(LongClickLinkMovementMethod.getInstance());
|
||||||
|
//holder.binding.statusContent.setMovementMethod(LinkMovementMethod.getInstance());
|
||||||
holder.binding.reblogInfo.setOnClickListener(v -> {
|
holder.binding.reblogInfo.setOnClickListener(v -> {
|
||||||
if (remote) {
|
if (remote) {
|
||||||
Toasty.info(context, context.getString(R.string.retrieve_remote_status), Toasty.LENGTH_SHORT).show();
|
Toasty.info(context, context.getString(R.string.retrieve_remote_status), Toasty.LENGTH_SHORT).show();
|
||||||
|
|
69
app/src/main/res/layout/popup_links.xml
Normal file
69
app/src/main/res/layout/popup_links.xml
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="@dimen/fab_margin"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="@dimen/fab_margin">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/display_full_link"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:layout_marginBottom="10dp"
|
||||||
|
android:drawableEnd="@drawable/ic_baseline_navigate_next_24"
|
||||||
|
android:text="@string/display_full_link"
|
||||||
|
android:textColor="@color/cyanea_accent_reference"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:drawableTint="@color/cyanea_accent_reference" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/share_link"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:layout_marginBottom="10dp"
|
||||||
|
android:drawableEnd="@drawable/ic_baseline_navigate_next_24"
|
||||||
|
android:text="@string/share_link"
|
||||||
|
android:textColor="@color/cyanea_accent_reference"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:drawableTint="@color/cyanea_accent_reference" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/open_other_app"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:layout_marginBottom="10dp"
|
||||||
|
android:drawableEnd="@drawable/ic_baseline_navigate_next_24"
|
||||||
|
android:text="@string/open_other_app"
|
||||||
|
android:textColor="@color/cyanea_accent_reference"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:drawableTint="@color/cyanea_accent_reference" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/copy_link"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:layout_marginBottom="10dp"
|
||||||
|
android:drawableEnd="@drawable/ic_baseline_navigate_next_24"
|
||||||
|
android:text="@string/copy_link"
|
||||||
|
android:textColor="@color/cyanea_accent_reference"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:drawableTint="@color/cyanea_accent_reference" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/check_redirect"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:layout_marginBottom="10dp"
|
||||||
|
android:drawableEnd="@drawable/ic_baseline_navigate_next_24"
|
||||||
|
android:text="@string/check_redirect"
|
||||||
|
android:textColor="@color/cyanea_accent_reference"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:drawableTint="@color/cyanea_accent_reference" />
|
||||||
|
</LinearLayout>
|
Loading…
Reference in a new issue