2135 lines
97 KiB
Java
2135 lines
97 KiB
Java
package app.fedilab.android.mastodon.helper;
|
|
/* Copyright 2021 Thomas Schneider
|
|
*
|
|
* This file is a part of Fedilab
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
|
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
|
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
|
* Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
|
|
* see <http://www.gnu.org/licenses>. */
|
|
|
|
import static android.content.Context.DOWNLOAD_SERVICE;
|
|
import static app.fedilab.android.BaseMainActivity.currentAccount;
|
|
import static app.fedilab.android.mastodon.activities.BaseActivity.currentThemeId;
|
|
import static app.fedilab.android.mastodon.helper.LogoHelper.getNotificationIcon;
|
|
import static app.fedilab.android.mastodon.helper.ThemeHelper.fetchAccentColor;
|
|
|
|
import android.annotation.SuppressLint;
|
|
import android.app.Activity;
|
|
import android.app.DownloadManager;
|
|
import android.app.Notification;
|
|
import android.app.NotificationChannel;
|
|
import android.app.NotificationManager;
|
|
import android.app.PendingIntent;
|
|
import android.content.ComponentName;
|
|
import android.content.ContentResolver;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.SharedPreferences;
|
|
import android.content.pm.PackageManager;
|
|
import android.content.pm.ResolveInfo;
|
|
import android.content.res.Configuration;
|
|
import android.content.res.Resources;
|
|
import android.database.Cursor;
|
|
import android.graphics.Bitmap;
|
|
import android.graphics.BitmapFactory;
|
|
import android.graphics.Color;
|
|
import android.graphics.Paint;
|
|
import android.graphics.PorterDuff;
|
|
import android.graphics.PorterDuffColorFilter;
|
|
import android.graphics.drawable.BitmapDrawable;
|
|
import android.graphics.drawable.Drawable;
|
|
import android.media.RingtoneManager;
|
|
import android.net.ConnectivityManager;
|
|
import android.net.NetworkInfo;
|
|
import android.net.Uri;
|
|
import android.os.Build;
|
|
import android.os.Bundle;
|
|
import android.os.CountDownTimer;
|
|
import android.os.Environment;
|
|
import android.os.Handler;
|
|
import android.os.Looper;
|
|
import android.os.Parcelable;
|
|
import android.provider.MediaStore;
|
|
import android.provider.OpenableColumns;
|
|
import android.text.TextUtils;
|
|
import android.util.DisplayMetrics;
|
|
import android.util.TypedValue;
|
|
import android.view.Menu;
|
|
import android.view.MenuItem;
|
|
import android.view.View;
|
|
import android.view.WindowManager;
|
|
import android.view.inputmethod.InputMethodManager;
|
|
import android.webkit.MimeTypeMap;
|
|
import android.webkit.URLUtil;
|
|
import android.widget.ImageView;
|
|
import android.widget.TextView;
|
|
import android.widget.Toast;
|
|
|
|
import androidx.annotation.IdRes;
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.Nullable;
|
|
import androidx.appcompat.app.AlertDialog;
|
|
import androidx.browser.customtabs.CustomTabColorSchemeParams;
|
|
import androidx.browser.customtabs.CustomTabsIntent;
|
|
import androidx.core.app.NotificationCompat;
|
|
import androidx.core.app.NotificationManagerCompat;
|
|
import androidx.core.content.ContextCompat;
|
|
import androidx.core.graphics.drawable.DrawableCompat;
|
|
import androidx.fragment.app.Fragment;
|
|
import androidx.fragment.app.FragmentManager;
|
|
import androidx.fragment.app.FragmentTransaction;
|
|
import androidx.lifecycle.LifecycleOwner;
|
|
import androidx.lifecycle.ViewModelProvider;
|
|
import androidx.lifecycle.ViewModelStoreOwner;
|
|
import androidx.preference.PreferenceManager;
|
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
|
|
|
import com.avatarfirst.avatargenlib.AvatarGenerator;
|
|
import com.bumptech.glide.Glide;
|
|
import com.bumptech.glide.RequestBuilder;
|
|
import com.bumptech.glide.load.resource.bitmap.CenterCrop;
|
|
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
|
|
import com.bumptech.glide.load.resource.gif.GifDrawable;
|
|
import com.bumptech.glide.request.RequestOptions;
|
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
|
import com.google.gson.Gson;
|
|
import com.google.gson.GsonBuilder;
|
|
import com.google.gson.TypeAdapter;
|
|
import com.google.gson.reflect.TypeToken;
|
|
import com.google.gson.stream.JsonReader;
|
|
import com.google.gson.stream.JsonToken;
|
|
import com.google.gson.stream.JsonWriter;
|
|
|
|
import java.io.ByteArrayOutputStream;
|
|
import java.io.File;
|
|
import java.io.FileOutputStream;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.OutputStream;
|
|
import java.net.Authenticator;
|
|
import java.net.HttpURLConnection;
|
|
import java.net.InetAddress;
|
|
import java.net.InetSocketAddress;
|
|
import java.net.PasswordAuthentication;
|
|
import java.net.Proxy;
|
|
import java.net.URL;
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.text.DateFormat;
|
|
import java.text.ParseException;
|
|
import java.text.SimpleDateFormat;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.Collections;
|
|
import java.util.Date;
|
|
import java.util.HashSet;
|
|
import java.util.LinkedHashMap;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
import java.util.Map;
|
|
import java.util.Objects;
|
|
import java.util.Random;
|
|
import java.util.Set;
|
|
import java.util.TimeZone;
|
|
import java.util.UUID;
|
|
import java.util.concurrent.TimeUnit;
|
|
import java.util.regex.Matcher;
|
|
import java.util.regex.Pattern;
|
|
|
|
import app.fedilab.android.BaseMainActivity;
|
|
import app.fedilab.android.BuildConfig;
|
|
import app.fedilab.android.R;
|
|
import app.fedilab.android.activities.LoginActivity;
|
|
import app.fedilab.android.activities.MainActivity;
|
|
import app.fedilab.android.databinding.PopupReleaseNotesBinding;
|
|
import app.fedilab.android.mastodon.activities.ComposeActivity;
|
|
import app.fedilab.android.mastodon.activities.ProfileActivity;
|
|
import app.fedilab.android.mastodon.broadcastreceiver.ToastMessage;
|
|
import app.fedilab.android.mastodon.client.entities.api.Attachment;
|
|
import app.fedilab.android.mastodon.client.entities.api.Status;
|
|
import app.fedilab.android.mastodon.client.entities.app.Account;
|
|
import app.fedilab.android.mastodon.client.entities.app.BaseAccount;
|
|
import app.fedilab.android.mastodon.client.entities.app.CachedBundle;
|
|
import app.fedilab.android.mastodon.client.entities.app.ReleaseNote;
|
|
import app.fedilab.android.mastodon.client.entities.app.Timeline;
|
|
import app.fedilab.android.mastodon.exception.DBException;
|
|
import app.fedilab.android.mastodon.interfaces.OnDownloadInterface;
|
|
import app.fedilab.android.mastodon.ui.drawer.ReleaseNoteAdapter;
|
|
import app.fedilab.android.mastodon.viewmodel.mastodon.AccountsVM;
|
|
import app.fedilab.android.mastodon.viewmodel.mastodon.OauthVM;
|
|
import app.fedilab.android.mastodon.watermark.androidwm.WatermarkBuilder;
|
|
import app.fedilab.android.mastodon.watermark.androidwm.bean.WatermarkText;
|
|
import es.dmoral.toasty.Toasty;
|
|
import okhttp3.MediaType;
|
|
import okhttp3.MultipartBody;
|
|
import okhttp3.OkHttpClient;
|
|
import okhttp3.Request;
|
|
import okhttp3.RequestBody;
|
|
|
|
public class Helper {
|
|
|
|
public static final String TAG = "fedilab_app";
|
|
public static final String CLIP_BOARD = "CLIP_BOARD";
|
|
|
|
public static final String INSTANCE_SOCIAL_KEY = "jGj9gW3z9ptyIpB8CMGhAlTlslcemMV6AgoiImfw3vPP98birAJTHOWiu5ZWfCkLvcaLsFZw9e3Pb7TIwkbIyrj3z6S7r2oE6uy6EFHvls3YtapP8QKNZ980p9RfzTb4";
|
|
public static final String WEBSITE_VALUE = "https://fedilab.app";
|
|
|
|
public static final String RECEIVE_TOAST_MESSAGE = "RECEIVE_TOAST_MESSAGE";
|
|
public static final String RECEIVE_TOAST_TYPE = "RECEIVE_TOAST_TYPE";
|
|
public static final String RECEIVE_TOAST_CONTENT = "RECEIVE_TOAST_CONTENT";
|
|
public static final String RECEIVE_TOAST_TYPE_ERROR = "RECEIVE_TOAST_TYPE_ERROR";
|
|
public static final String RECEIVE_TOAST_TYPE_INFO = "RECEIVE_TOAST_TYPE_INFO";
|
|
public static final String RECEIVE_TOAST_TYPE_SUCCESS = "RECEIVE_TOAST_TYPE_SUCCESS";
|
|
public static final String RECEIVE_TOAST_TYPE_WARNING = "RECEIVE_TOAST_TYPE_WARNING";
|
|
|
|
//Intent
|
|
public static final String INTENT_ACTION = "intent_action";
|
|
|
|
public static final String BROADCAST_DATA = "BROADCAST_DATA";
|
|
public static final String RECEIVE_REDRAW_TOPBAR = "RECEIVE_REDRAW_TOPBAR";
|
|
public static final String RECEIVE_REDRAW_BOTTOM = "RECEIVE_REDRAW_BOTTOM";
|
|
|
|
public static final String RECEIVE_STATUS_ACTION = "RECEIVE_STATUS_ACTION";
|
|
|
|
public static final String RECEIVE_REFRESH_NOTIFICATIONS_ACTION = "RECEIVE_REFRESH_NOTIFICATIONS_ACTION";
|
|
public static final String RECEIVE_ERROR_MESSAGE = "RECEIVE_ERROR_MESSAGE";
|
|
|
|
public static final String RECEIVE_RECREATE_ACTIVITY = "RECEIVE_RECREATE_ACTIVITY";
|
|
public static final String RECEIVE_RECREATE_PEERTUBE_ACTIVITY = "RECEIVE_RECREATE_PEERTUBE_ACTIVITY";
|
|
|
|
public static final String RECEIVE_NEW_MESSAGE = "RECEIVE_NEW_MESSAGE";
|
|
public static final String RECEIVE_COMPOSE_ERROR_MESSAGE = "RECEIVE_COMPOSE_ERROR_MESSAGE";
|
|
public static final String RECEIVE_MASTODON_LIST = "RECEIVE_MASTODON_LIST";
|
|
public static final String RECEIVE_REDRAW_PROFILE = "RECEIVE_REDRAW_PROFILE";
|
|
|
|
public static final String ARG_TIMELINE_TYPE = "ARG_TIMELINE_TYPE";
|
|
|
|
public static final String ARG_INTENT_ID = "ARG_INTENT_ID";
|
|
public static final String ARG_PEERTUBE_NAV_REMOTE = "ARG_PEERTUBE_NAV_REMOTE";
|
|
|
|
public static final String ARG_REMOTE_INSTANCE_STRING = "ARG_REMOTE_INSTANCE_STRING";
|
|
|
|
public static final String ARG_NOTIFICATION_TYPE = "ARG_NOTIFICATION_TYPE";
|
|
public static final String ARG_EXCLUDED_NOTIFICATION_TYPE = "ARG_EXCLUDED_NOTIFICATION_TYPE";
|
|
public static final String ARG_STATUS = "ARG_STATUS";
|
|
public static final String ARG_FOCUSED_STATUS_URI = "ARG_FOCUSED_STATUS_URI";
|
|
public static final String ARG_TIMELINE_REFRESH_ALL = "ARG_TIMELINE_REFRESH_ALL";
|
|
public static final String ARG_REFRESH_NOTFICATION = "ARG_REFRESH_NOTFICATION";
|
|
public static final String ARG_STATUS_DELETED = "ARG_STATUS_DELETED";
|
|
public static final String ARG_STATUS_UPDATED = "ARG_STATUS_UPDATED";
|
|
|
|
public static final String ARG_STATUS_POSTED = "ARG_STATUS_POSTED";
|
|
public static final String ARG_STATUS_ACTION = "ARG_STATUS_ACTION";
|
|
public static final String ARG_DELETE_ALL_FOR_ACCOUNT_ID = "ARG_DELETE_ALL_FOR_ACCOUNT_ID";
|
|
public static final String ARG_STATUS_ACCOUNT_ID_DELETED = "ARG_STATUS_ACCOUNT_ID_DELETED";
|
|
|
|
public static final String ARG_STATUS_DRAFT = "ARG_STATUS_DRAFT";
|
|
public static final String ARG_EDIT_STATUS_ID = "ARG_EDIT_STATUS_ID";
|
|
|
|
public static final String ARG_STATUS_SCHEDULED = "ARG_STATUS_SCHEDULED";
|
|
public static final String ARG_SLUG_OF_FIRST_FRAGMENT = "ARG_SLUG_OF_FIRST_FRAGMENT";
|
|
|
|
public static final String ARG_STATUS_DRAFT_ID = "ARG_STATUS_DRAFT_ID";
|
|
public static final String ARG_STATUS_REPLY = "ARG_STATUS_REPLY";
|
|
public static final String ARG_MENTION_BOOSTER = "ARG_MENTION_BOOSTER";
|
|
public static final String ARG_QUOTED_MESSAGE = "ARG_QUOTED_MESSAGE";
|
|
public static final String ARG_STATUS_REPLY_ID = "ARG_STATUS_REPLY_ID";
|
|
public static final String ARG_ACCOUNT = "ARG_ACCOUNT";
|
|
public static final String ARG_ACCOUNT_ID = "ARG_ACCOUNT_ID";
|
|
public static final String ARG_CACHED_ACCOUNT_ID = "ARG_CACHED_ACCOUNT_ID";
|
|
public static final String ARG_CACHED_STATUS_ID = "ARG_CACHED_STATUS_ID";
|
|
public static final String ARG_ADMIN_DOMAINBLOCK = "ARG_ADMIN_DOMAINBLOCK";
|
|
public static final String ARG_ADMIN_DOMAINBLOCK_DELETE = "ARG_ADMIN_DOMAINBLOCK_DELETE";
|
|
public static final String FEDILAB_MUTED_HASHTAGS = "Fedilab muted hashtags";
|
|
public static final String ARG_REPORT = "ARG_REPORT";
|
|
public static final String ARG_ACCOUNT_MENTION = "ARG_ACCOUNT_MENTION";
|
|
public static final String ARG_MINIFIED = "ARG_MINIFIED";
|
|
public static final String ARG_STATUS_REPORT = "ARG_STATUS_REPORT";
|
|
public static final String ARG_STATUS_MENTION = "ARG_STATUS_MENTION";
|
|
public static final String ARG_SHARE_URL_MEDIA = "ARG_SHARE_URL_MEDIA";
|
|
public static final String ARG_SHARE_URL = "ARG_SHARE_URL";
|
|
public static final String ARG_SHARE_TITLE = "ARG_SHARE_TITLE";
|
|
public static final String ARG_SHARE_SUBJECT = "ARG_SHARE_SUBJECT";
|
|
public static final String ARG_SHARE_DESCRIPTION = "ARG_SHARE_DESCRIPTION";
|
|
public static final String ARG_SHARE_CONTENT = "ARG_SHARE_CONTENT";
|
|
public static final String ARG_FOLLOW_TYPE = "ARG_FOLLOW_TYPE";
|
|
public static final String ARG_TYPE_OF_INFO = "ARG_TYPE_OF_INFO";
|
|
public static final String ARG_TOKEN = "ARG_TOKEN";
|
|
public static final String ARG_INSTANCE = "ARG_INSTANCE";
|
|
public static final String ARG_REMOTE_INSTANCE = "ARG_REMOTE_INSTANCE";
|
|
public static final String ARG_STATUS_ID = "ARG_STATUS_ID";
|
|
public static final String ARG_WORK_ID = "ARG_WORK_ID";
|
|
public static final String ARG_LIST_ID = "ARG_LIST_ID";
|
|
public static final String ARG_LEMMY_POST_ID = "ARG_LEMMY_POST_ID";
|
|
|
|
public static final String ARG_SEARCH_KEYWORD = "ARG_SEARCH_KEYWORD";
|
|
public static final String ARG_DIRECTORY_ORDER = "ARG_DIRECTORY_ORDER";
|
|
public static final String ARG_DIRECTORY_LOCAL = "ARG_DIRECTORY_LOCAL";
|
|
public static final String ARG_SEARCH_TYPE = "ARG_SEARCH_TYPE";
|
|
public static final String ARG_SEARCH_KEYWORD_CACHE = "ARG_SEARCH_KEYWORD_CACHE";
|
|
public static final String ARG_VIEW_MODEL_KEY = "ARG_VIEW_MODEL_KEY";
|
|
public static final String ARG_TAG_TIMELINE = "ARG_TAG_TIMELINE";
|
|
public static final String ARG_BUBBLE_TIMELINE = "ARG_BUBBLE_TIMELINE";
|
|
public static final String ARG_MEDIA_POSITION = "ARG_MEDIA_POSITION";
|
|
public static final String ARG_MEDIA_ATTACHMENT = "ARG_MEDIA_ATTACHMENT";
|
|
public static final String ARG_MEDIA_ATTACHMENTS = "ARG_MEDIA_ATTACHMENTS";
|
|
public static final String ARG_SHOW_REPLIES = "ARG_SHOW_REPLIES";
|
|
public static final String ARG_SHOW_REBLOGS = "ARG_SHOW_REBLOGS";
|
|
public static final String ARG_INITIALIZE_VIEW = "ARG_INITIALIZE_VIEW";
|
|
public static final String ARG_SHOW_PINNED = "ARG_SHOW_PINNED";
|
|
public static final String ARG_SHOW_MEDIA_ONY = "ARG_SHOW_MEDIA_ONY";
|
|
public static final String ARG_MENTION = "ARG_MENTION";
|
|
public static final String ARG_CHECK_REMOTELY = "ARG_CHECK_REMOTELY";
|
|
public static final String ARG_USER_ID = "ARG_USER_ID";
|
|
public static final String ARG_MEDIA_ARRAY = "ARG_MEDIA_ARRAY";
|
|
public static final String ARG_MEDIA_ARRAY_PROFILE = "ARG_MEDIA_ARRAY_PROFILE";
|
|
public static final String ARG_VISIBILITY = "ARG_VISIBILITY";
|
|
public static final String ARG_SCHEDULED_DATE = "ARG_SCHEDULED_DATE";
|
|
|
|
public static final String WORKER_REFRESH_NOTIFICATION = "WORKER_REFRESH_NOTIFICATION";
|
|
public static final String WORKER_REFRESH_HOME = "WORKER_REFRESH_HOME";
|
|
public static final String WORKER_SCHEDULED_STATUSES = "WORKER_SCHEDULED_STATUSES";
|
|
public static final String WORKER_SCHEDULED_REBLOGS = "WORKER_SCHEDULED_REBLOGS";
|
|
|
|
public static final String VALUE_TRENDS = "VALUE_TRENDS";
|
|
|
|
//public static final String USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; rv:60.0) Gecko/20100101 Firefox/60.0";
|
|
public static final String REDIRECT_CONTENT_WEB = "fedilab://backtofedilab";
|
|
public static final String REDIRECT_CONTENT = "urn:ietf:wg:oauth:2.0:oob";
|
|
public static final String APP_OAUTH_SCOPES = "read write";
|
|
public static final String OAUTH_SCOPES = "read write follow push";
|
|
public static final String OAUTH_SCOPES_ADMIN = "read write follow push admin:read admin:write";
|
|
public static final int DEFAULT_VIDEO_CACHE_MB = 100;
|
|
public static final int LED_COLOUR = 0;
|
|
|
|
|
|
public static final String SCHEDULE_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ssZ";
|
|
|
|
|
|
public static final String PREF_USER_TOKEN = "PREF_USER_TOKEN";
|
|
public static final String PREF_USER_ID = "PREF_USER_ID";
|
|
public static final String PREF_USER_INSTANCE = "PREF_USER_INSTANCE";
|
|
public static final String PREF_USER_INSTANCE_PEERTUBE_BROWSING = "PREF_USER_INSTANCE_PEERTUBE_BROWSING";
|
|
|
|
|
|
public static final String PREF_USER_SOFTWARE = "PREF_USER_SOFTWARE";
|
|
public static final String PREF_IS_MODERATOR = "PREF_IS_MODERATOR";
|
|
public static final String PREF_IS_ADMINISTRATOR = "PREF_IS_ADMINISTRATOR";
|
|
public static final String PREF_MESSAGE_URL = "PREF_MESSAGE_URL";
|
|
|
|
|
|
public static final String SET_SECURITY_PROVIDER = "SET_SECURITY_PROVIDER";
|
|
|
|
public static final int NOTIFICATION_INTENT = 1;
|
|
public static final int OPEN_NOTIFICATION = 2;
|
|
public static final int OPEN_WITH_ANOTHER_ACCOUNT = 3;
|
|
public static final String INTENT_TARGETED_ACCOUNT = "INTENT_TARGETED_ACCOUNT";
|
|
public static final String INTENT_TARGETED_STATUS = "INTENT_TARGETED_STATUS";
|
|
public static final String INTENT_SEND_MODIFIED_IMAGE = "INTENT_SEND_MODIFIED_IMAGE";
|
|
public static final String INTENT_COMPOSE_ERROR_MESSAGE = "INTENT_COMPOSE_ERROR_MESSAGE";
|
|
public static final String TEMP_MEDIA_DIRECTORY = "TEMP_MEDIA_DIRECTORY";
|
|
public static final String TEMP_EXPORT_DATA = "TEMP_EXPORT_DATA";
|
|
|
|
public static final int EXTERNAL_STORAGE_REQUEST_CODE = 84;
|
|
public static final int EXTERNAL_STORAGE_REQUEST_CODE_MEDIA_SAVE = 85;
|
|
public static final int EXTERNAL_STORAGE_REQUEST_CODE_MEDIA_SHARE = 86;
|
|
//Some regex
|
|
/*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);*/
|
|
|
|
public static final Pattern hashtagPattern = Pattern.compile("(#[\\w_A-zÀ-ÿ]+)");
|
|
public static final Pattern groupPattern = Pattern.compile("(![\\w_]+)");
|
|
public static final Pattern mentionPattern = Pattern.compile("(@[\\w_.-]?[\\w]+)");
|
|
public static final Pattern mentionLongPattern = Pattern.compile("(@[\\w_.-]+@[a-zA-Z0-9][a-zA-Z0-9.-]{1,61}[a-zA-Z0-9](?:\\.[a-zA-Z]{2,})+)");
|
|
|
|
|
|
public static final Pattern mentionPatternALL = Pattern.compile("(@[\\w_.-]+@[a-zA-Z0-9][a-zA-Z0-9.-]{1,61}[a-zA-Z0-9](?:\\.[a-zA-Z]{2,})+)|(@[\\w_.-]?[\\w]+)");
|
|
public static final Pattern mathsPattern = Pattern.compile("\\\\\\(|\\\\\\[");
|
|
public static final Pattern mathsComposePattern = Pattern.compile("\\\\\\(.*\\\\\\)|\\\\\\[.*\\\\\\]");
|
|
public static final Pattern twitterPattern = Pattern.compile("((@[\\w]+)@twitter\\.com)");
|
|
public static final Pattern youtubePattern = Pattern.compile("(www\\.|m\\.)?(youtube\\.com|youtu\\.be|youtube-nocookie\\.com)/(((?!([\"'<])).)*)");
|
|
public static final Pattern nitterPattern = Pattern.compile("(mobile\\.|www\\.)?twitter\\.com([\\w/-]+)");
|
|
public static final Pattern bibliogramPattern = Pattern.compile("(m\\.|www\\.)?instagram.com(/p/[\\w-/]+)");
|
|
public static final Pattern libredditPattern = Pattern.compile("(www\\.|m\\.)?(reddit\\.com|preview\\.redd\\.it|i\\.redd\\.it|redd\\.it)/(((?!([\"'<])).)*)");
|
|
public static final Pattern ouichesPattern = Pattern.compile("https?://ouich\\.es/tag/(\\w+)");
|
|
|
|
public static final Pattern geminiPattern = Pattern.compile("(gemini://.*)\\b");
|
|
public static final Pattern xmppPattern = Pattern.compile("xmpp:[-a-zA-Z0-9+$&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]");
|
|
public static final Pattern peertubePattern = Pattern.compile("(https?://([\\da-z.-]+\\.[a-z.]{2,10}))/videos/watch/(\\w{8}-\\w{4}-\\w{4}-\\w{4}-\\w{12})$");
|
|
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 codePattern = Pattern.compile("code=([\\w-]+)");
|
|
public static final Pattern nitterIDPattern = Pattern.compile("/status/(\\d+)");
|
|
public static final Pattern emailPattern = Pattern.compile("(\\s+[\\w_.-]+@[a-zA-Z0-9][a-zA-Z0-9.-]{1,61}[a-zA-Z0-9](?:\\.[a-zA-Z]{2,})+)");
|
|
public static final Pattern statusIdInUrl = Pattern.compile("statuses/(\\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);
|
|
|
|
public static final Pattern urlPatternSimple = Pattern.compile(
|
|
"https?:\\/\\/(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b([-a-zA-Z0-9()@:%_\\+.~#?&//=]*)",
|
|
|
|
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);*/
|
|
|
|
public static final Pattern aLink = Pattern.compile("<a((?!href).)*href=\"([^\"]*)\"[^>]*(((?!</a).)*)</a>");
|
|
public static final Pattern imgPattern = Pattern.compile("<img [^>]*src=\"([^\"]+)\"[^>]*>");
|
|
|
|
// --- Static Map of patterns used in spannable status content
|
|
public static final Map<PatternType, Pattern> patternHashMap;
|
|
public static final int NOTIFICATION_MEDIA = 451;
|
|
public static final int NOTIFICATION_USER_NOTIF = 411;
|
|
public static final int NOTIFICATION_THEMING = 412;
|
|
/*
|
|
* List from ClearUrls
|
|
* https://gitlab.com/KevinRoebert/ClearUrls/blob/master/data/data.min.json#L106
|
|
*/
|
|
private static final String[] UTM_PARAMS = {
|
|
"utm_\\w+",
|
|
"ga_source",
|
|
"ga_medium",
|
|
"ga_term",
|
|
"ga_content",
|
|
"ga_campaign",
|
|
"ga_place",
|
|
"yclid",
|
|
"_openstat",
|
|
"fb_action_ids",
|
|
"fb_action_types",
|
|
"fb_source",
|
|
"fb_ref",
|
|
"fbclid",
|
|
"action_object_map",
|
|
"action_type_map",
|
|
"action_ref_map",
|
|
"gs_l",
|
|
"mkt_tok",
|
|
"hmb_campaign",
|
|
"hmb_medium",
|
|
"hmb_source",
|
|
"[\\?|&]ref[\\_]?"
|
|
|
|
};
|
|
public static int counter = 1;
|
|
private static int notificationId = 1;
|
|
//Allow to store in shared preference first visible fragment when the app starts
|
|
private static String slugOfFirstFragment;
|
|
|
|
static {
|
|
LinkedHashMap<PatternType, Pattern> aMap = new LinkedHashMap<>();
|
|
aMap.put(PatternType.MENTION_LONG, mentionLongPattern);
|
|
aMap.put(PatternType.MENTION, mentionPattern);
|
|
aMap.put(PatternType.TAG, hashtagPattern);
|
|
aMap.put(PatternType.GROUP, groupPattern);
|
|
patternHashMap = Collections.unmodifiableMap(aMap);
|
|
}
|
|
|
|
|
|
/**
|
|
* Manage downloads with URLs
|
|
*
|
|
* @param context Context
|
|
* @param url String download url
|
|
*/
|
|
public static void manageDownloads(final Context context, final String url) {
|
|
final AlertDialog.Builder builder = new MaterialAlertDialogBuilder(context);
|
|
final DownloadManager.Request request;
|
|
try {
|
|
request = new DownloadManager.Request(Uri.parse(url.trim()));
|
|
} catch (Exception e) {
|
|
Toasty.error(context, context.getString(R.string.toast_error), Toast.LENGTH_LONG).show();
|
|
return;
|
|
}
|
|
final String fileName = URLUtil.guessFileName(url, null, null);
|
|
builder.setMessage(context.getResources().getString(R.string.download_file, fileName));
|
|
builder.setCancelable(false)
|
|
.setPositiveButton(context.getString(R.string.yes), (dialog, id) -> {
|
|
request.allowScanningByMediaScanner();
|
|
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);
|
|
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
|
|
DownloadManager dm = (DownloadManager) context.getSystemService(DOWNLOAD_SERVICE);
|
|
assert dm != null;
|
|
dm.enqueue(request);
|
|
dialog.dismiss();
|
|
})
|
|
.setNegativeButton(context.getString(R.string.cancel), (dialog, id) -> dialog.cancel());
|
|
AlertDialog alert = builder.create();
|
|
if (alert.getWindow() != null)
|
|
alert.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
|
|
alert.show();
|
|
}
|
|
|
|
public static void colorizeIconMenu(Menu menu, int toolbarIconsColor) {
|
|
final PorterDuffColorFilter colorFilter
|
|
= new PorterDuffColorFilter(toolbarIconsColor, PorterDuff.Mode.MULTIPLY);
|
|
for (int i = 0; i < menu.size(); i++) {
|
|
MenuItem v = menu.getItem(i);
|
|
v.getIcon().setColorFilter(colorFilter);
|
|
}
|
|
}
|
|
|
|
|
|
/***
|
|
* Check if the user is connected to Internet
|
|
* @return boolean
|
|
*/
|
|
public static BaseMainActivity.status isConnectedToInternet(Context context, String instance) {
|
|
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
|
if (cm == null)
|
|
return BaseMainActivity.status.CONNECTED;
|
|
NetworkInfo ni = cm.getActiveNetworkInfo();
|
|
if (ni != null && ni.isConnected()) {
|
|
try {
|
|
InetAddress ipAddr = InetAddress.getByName(instance);
|
|
return (!ipAddr.toString().equals("")) ? BaseMainActivity.status.CONNECTED : BaseMainActivity.status.DISCONNECTED;
|
|
} catch (Exception e) {
|
|
try {
|
|
InetAddress ipAddr = InetAddress.getByName("mastodon.social");
|
|
return (!ipAddr.toString().equals("")) ? BaseMainActivity.status.CONNECTED : BaseMainActivity.status.DISCONNECTED;
|
|
} catch (Exception ex) {
|
|
return BaseMainActivity.status.DISCONNECTED;
|
|
}
|
|
}
|
|
} else {
|
|
return BaseMainActivity.status.DISCONNECTED;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns boolean depending if the user is authenticated
|
|
*
|
|
* @param context Context
|
|
* @return boolean
|
|
*/
|
|
public static boolean isLoggedIn(Context context) {
|
|
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
|
String prefKeyOauthTokenT = sharedpreferences.getString(PREF_USER_TOKEN, null);
|
|
return (prefKeyOauthTokenT != null);
|
|
}
|
|
|
|
/***
|
|
* Returns a String depending of the date
|
|
* @param context Context
|
|
* @param date Date
|
|
* @return String
|
|
*/
|
|
public static String dateDiff(Context context, Date date) {
|
|
if (date == null) {
|
|
date = new Date();
|
|
}
|
|
Date now = new Date();
|
|
long diff = now.getTime() - date.getTime();
|
|
long seconds = diff / 1000;
|
|
long minutes = seconds / 60;
|
|
long hours = minutes / 60;
|
|
long days = hours / 24;
|
|
long months = days / 30;
|
|
long years = days / 365;
|
|
|
|
if (years > 0) {
|
|
return DateFormat.getDateInstance(DateFormat.SHORT, Locale.getDefault()).format(date);
|
|
} else if (months > 0 || days > 7) {
|
|
//Removes the year depending of the locale from DateFormat.SHORT format
|
|
SimpleDateFormat df = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.SHORT, Locale.getDefault());
|
|
df.applyPattern(df.toPattern().replaceAll("[^\\p{Alpha}]*y+[^\\p{Alpha}]*", ""));
|
|
return df.format(date);
|
|
} else if (days > 0)
|
|
return context.getString(R.string.date_day, days);
|
|
else if (hours > 0)
|
|
return context.getResources().getString(R.string.date_hours, (int) hours);
|
|
else if (minutes > 0)
|
|
return context.getResources().getString(R.string.date_minutes, (int) minutes);
|
|
else {
|
|
if (seconds < 0)
|
|
seconds = 0;
|
|
return context.getResources().getString(R.string.date_seconds, (int) seconds);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convert a date in String -> format yyyy-MM-dd HH:mm:ss
|
|
*
|
|
* @param date Date
|
|
* @return String
|
|
*/
|
|
public static String dateToString(Date date) {
|
|
if (date == null)
|
|
return null;
|
|
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
|
|
return dateFormat.format(date);
|
|
}
|
|
|
|
/**
|
|
* Convert a date in String
|
|
*
|
|
* @param date Date
|
|
* @return String
|
|
*/
|
|
public static String longDateToString(Date date) {
|
|
if (date == null) {
|
|
date = new Date();
|
|
}
|
|
DateFormat df = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT, Locale.getDefault());
|
|
return df.format(date);
|
|
}
|
|
|
|
/**
|
|
* Convert a date in String
|
|
*
|
|
* @param date Date
|
|
* @return String
|
|
*/
|
|
public static String mediumDateToString(Date date) {
|
|
if (date == null) {
|
|
date = new Date();
|
|
}
|
|
DateFormat df = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.getDefault());
|
|
return df.format(date);
|
|
}
|
|
|
|
/**
|
|
* Convert a date in String -> format yyyy-MM-dd HH:mm:ss
|
|
*
|
|
* @param date Date
|
|
* @return String
|
|
*/
|
|
public static String shortDateToString(Date date) {
|
|
if (date == null) {
|
|
date = new Date();
|
|
}
|
|
SimpleDateFormat df = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.SHORT, Locale.getDefault());
|
|
return df.format(date);
|
|
}
|
|
|
|
/**
|
|
* Convert String date from db to Date Object
|
|
*
|
|
* @param stringDate date to convert
|
|
* @return Date
|
|
*/
|
|
public static Date stringToDate(Context context, String stringDate) {
|
|
if (stringDate == null)
|
|
return null;
|
|
Locale userLocale;
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
userLocale = context.getResources().getConfiguration().getLocales().get(0);
|
|
} else {
|
|
userLocale = context.getResources().getConfiguration().locale;
|
|
}
|
|
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", userLocale);
|
|
Date date = null;
|
|
try {
|
|
date = dateFormat.parse(stringDate);
|
|
} catch (java.text.ParseException ignored) {
|
|
|
|
}
|
|
return date;
|
|
}
|
|
|
|
/**
|
|
* Convert String date from db to Date Object
|
|
*
|
|
* @param stringDate date to convert
|
|
* @return Date
|
|
*/
|
|
public static Date stringToDateWithFormat(Context context, String stringDate, String format) {
|
|
if (stringDate == null)
|
|
return null;
|
|
Locale userLocale;
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
userLocale = context.getResources().getConfiguration().getLocales().get(0);
|
|
} else {
|
|
userLocale = context.getResources().getConfiguration().locale;
|
|
}
|
|
SimpleDateFormat dateFormat = new SimpleDateFormat(format, userLocale);
|
|
Date date = null;
|
|
try {
|
|
date = dateFormat.parse(stringDate);
|
|
} catch (java.text.ParseException ignored) {
|
|
|
|
}
|
|
return date;
|
|
}
|
|
|
|
/**
|
|
* Converts dp to pixel
|
|
*
|
|
* @param dp float - the value in dp to convert
|
|
* @param context Context
|
|
* @return float - the converted value in pixel
|
|
*/
|
|
public static float convertDpToPixel(float dp, Context context) {
|
|
Resources resources = context.getResources();
|
|
DisplayMetrics metrics = resources.getDisplayMetrics();
|
|
return dp * ((float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT);
|
|
}
|
|
|
|
/**
|
|
* Manage URLs to open (built-in or external app)
|
|
*
|
|
* @param context Context
|
|
* @param url String url to open
|
|
*/
|
|
public static void openBrowser(Context context, String url) {
|
|
if (url == null) {
|
|
return;
|
|
}
|
|
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
|
boolean customTab = sharedpreferences.getBoolean(context.getString(R.string.SET_CUSTOM_TABS), true);
|
|
if (customTab) {
|
|
CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
|
|
int colorInt = ThemeHelper.getAttColor(context, R.attr.statusBar);
|
|
CustomTabColorSchemeParams defaultColors = new CustomTabColorSchemeParams.Builder()
|
|
.setToolbarColor(colorInt)
|
|
.build();
|
|
builder.setDefaultColorSchemeParams(defaultColors);
|
|
CustomTabsIntent customTabsIntent = builder.build();
|
|
try {
|
|
customTabsIntent.launchUrl(context, Uri.parse(url).normalizeScheme());
|
|
} catch (Exception e) {
|
|
Toasty.error(context, context.getString(R.string.toast_error), Toast.LENGTH_LONG).show();
|
|
}
|
|
|
|
} else {
|
|
Intent intent = new Intent(Intent.ACTION_VIEW);
|
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
if (!url.toLowerCase().startsWith("http://") && !url.toLowerCase().startsWith("https://") && !url.toLowerCase().startsWith("gemini://")) {
|
|
url = "http://" + url;
|
|
}
|
|
intent.setData(Uri.parse(url).normalizeScheme());
|
|
try {
|
|
context.startActivity(intent);
|
|
} catch (Exception e) {
|
|
Toasty.error(context, context.getString(R.string.toast_error), Toast.LENGTH_LONG).show();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Transform URLs to privacy frontend
|
|
*
|
|
* @param context Context
|
|
* @param url String
|
|
*/
|
|
public static String transformURL(Context context, String url) {
|
|
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
|
Matcher matcher;
|
|
boolean nitter = Helper.getSharedValue(context, context.getString(R.string.SET_NITTER));
|
|
if (nitter) {
|
|
matcher = Helper.nitterPattern.matcher(url);
|
|
if (matcher.find()) {
|
|
final String nitter_directory = matcher.group(2);
|
|
String nitterHost = sharedpreferences.getString(context.getString(R.string.SET_NITTER_HOST), context.getString(R.string.DEFAULT_NITTER_HOST)).toLowerCase();
|
|
if (nitterHost.trim().isEmpty()) {
|
|
nitterHost = context.getString(R.string.DEFAULT_NITTER_HOST);
|
|
}
|
|
return "https://" + nitterHost + nitter_directory;
|
|
}
|
|
}
|
|
|
|
boolean bibliogram = Helper.getSharedValue(context, context.getString(R.string.SET_BIBLIOGRAM));
|
|
|
|
if (bibliogram) {
|
|
matcher = Helper.bibliogramPattern.matcher(url);
|
|
if (matcher.find()) {
|
|
final String bibliogram_directory = matcher.group(2);
|
|
String bibliogramHost = sharedpreferences.getString(context.getString(R.string.SET_BIBLIOGRAM_HOST), context.getString(R.string.DEFAULT_BIBLIOGRAM_HOST)).toLowerCase();
|
|
if (bibliogramHost.trim().isEmpty()) {
|
|
bibliogramHost = context.getString(R.string.DEFAULT_BIBLIOGRAM_HOST);
|
|
}
|
|
return "https://" + bibliogramHost + bibliogram_directory;
|
|
}
|
|
}
|
|
|
|
boolean libreddit = Helper.getSharedValue(context, context.getString(R.string.SET_LIBREDDIT));
|
|
if (libreddit) {
|
|
matcher = Helper.libredditPattern.matcher(url);
|
|
if (matcher.find()) {
|
|
final String libreddit_directory = matcher.group(3);
|
|
String libreddit_host = sharedpreferences.getString(context.getString(R.string.SET_LIBREDDIT_HOST), context.getString(R.string.DEFAULT_LIBREDDIT_HOST)).toLowerCase();
|
|
if (libreddit_host.trim().isEmpty()) {
|
|
libreddit_host = context.getString(R.string.DEFAULT_LIBREDDIT_HOST);
|
|
}
|
|
return "https://" + libreddit_host + "/" + libreddit_directory;
|
|
}
|
|
}
|
|
|
|
boolean invidious = Helper.getSharedValue(context, context.getString(R.string.SET_INVIDIOUS));
|
|
if (invidious) {
|
|
matcher = Helper.youtubePattern.matcher(url);
|
|
if (matcher.find()) {
|
|
final String youtubeId = matcher.group(3);
|
|
String invidiousHost = sharedpreferences.getString(context.getString(R.string.SET_INVIDIOUS_HOST), context.getString(R.string.DEFAULT_INVIDIOUS_HOST)).toLowerCase();
|
|
if (invidiousHost.trim().isEmpty()) {
|
|
invidiousHost = context.getString(R.string.DEFAULT_INVIDIOUS_HOST);
|
|
}
|
|
if (matcher.group(2) != null && Objects.equals(matcher.group(2), "youtu.be")) {
|
|
return "https://" + invidiousHost + "/watch?v=" + youtubeId + "&local=true";
|
|
} else {
|
|
return "https://" + invidiousHost + "/" + youtubeId + "&local=true";
|
|
}
|
|
}
|
|
}
|
|
|
|
boolean medium = Helper.getSharedValue(context, context.getString(R.string.REPLACE_MEDIUM));
|
|
if (medium) {
|
|
matcher = Helper.mediumPattern.matcher(url);
|
|
if (matcher.find()) {
|
|
String path = matcher.group(2);
|
|
String user = matcher.group(1);
|
|
if (user != null && user.length() > 0 & !user.equals("www")) {
|
|
path = user + "/" + path;
|
|
}
|
|
String mediumReplaceHost = sharedpreferences.getString(context.getString(R.string.REPLACE_MEDIUM_HOST), context.getString(R.string.DEFAULT_REPLACE_MEDIUM_HOST)).toLowerCase();
|
|
if (mediumReplaceHost.trim().isEmpty()) {
|
|
mediumReplaceHost = context.getString(R.string.DEFAULT_REPLACE_MEDIUM_HOST);
|
|
}
|
|
return "https://" + mediumReplaceHost + "/" + path;
|
|
}
|
|
}
|
|
|
|
boolean wikipedia = Helper.getSharedValue(context, context.getString(R.string.REPLACE_WIKIPEDIA));
|
|
if (wikipedia) {
|
|
matcher = Helper.wikipediaPattern.matcher(url);
|
|
if (matcher.find()) {
|
|
String subdomain = matcher.group(1);
|
|
String path = matcher.group(2);
|
|
String wikipediaReplaceHost = sharedpreferences.getString(context.getString(R.string.REPLACE_WIKIPEDIA_HOST), context.getString(R.string.DEFAULT_REPLACE_WIKIPEDIA_HOST)).toLowerCase();
|
|
String lang = "";
|
|
if (path != null && subdomain != null && !subdomain.equals("www")) {
|
|
lang = (path.contains("?")) ? TextUtils.htmlEncode("&") : "?";
|
|
lang = lang + "lang=" + subdomain;
|
|
}
|
|
if (wikipediaReplaceHost.trim().isEmpty()) {
|
|
wikipediaReplaceHost = context.getString(R.string.DEFAULT_REPLACE_WIKIPEDIA_HOST);
|
|
}
|
|
return "https://" + wikipediaReplaceHost + "/" + path + lang;
|
|
}
|
|
}
|
|
boolean filterUTM = Helper.getSharedValue(context, context.getString(R.string.SET_FILTER_UTM));
|
|
if (filterUTM) {
|
|
return remove_tracking_param(context, url);
|
|
}
|
|
return url;
|
|
}
|
|
|
|
/**
|
|
* Remove tracking parameters
|
|
*
|
|
* @param context - Context
|
|
* @param original_content - String original URL
|
|
* @return cleaned URL
|
|
*/
|
|
private static String remove_tracking_param(Context context, String original_content) {
|
|
if (original_content == null)
|
|
return original_content;
|
|
String cleaned_content = original_content;
|
|
for (String utm : UTM_PARAMS) {
|
|
cleaned_content = cleaned_content.replaceAll("&" + utm + "=[0-9a-zA-Z._-]*", "");
|
|
cleaned_content = cleaned_content.replaceAll("&" + utm + "=[0-9a-zA-Z._-]*", "");
|
|
cleaned_content = cleaned_content.replaceAll("\\?" + utm + "=[0-9a-zA-Z._-]*", "?");
|
|
}
|
|
if (cleaned_content.endsWith("?")) {
|
|
cleaned_content = cleaned_content.substring(0, cleaned_content.length() - 1);
|
|
}
|
|
return cleaned_content;
|
|
}
|
|
|
|
@SuppressLint("DefaultLocale")
|
|
public static String withSuffix(long count) {
|
|
if (count < 1000) return String.valueOf(count);
|
|
int exp = (int) (Math.log(count) / Math.log(1000));
|
|
Locale locale = null;
|
|
try {
|
|
locale = Locale.getDefault();
|
|
} catch (Exception ignored) {
|
|
}
|
|
if (locale != null)
|
|
return String.format(locale, "%.1f %c",
|
|
count / Math.pow(1000, exp),
|
|
"kMGTPE".charAt(exp - 1));
|
|
else
|
|
return String.format("%.1f %c",
|
|
count / Math.pow(1000, exp),
|
|
"kMGTPE".charAt(exp - 1));
|
|
}
|
|
|
|
/**
|
|
* Send a toast message to main activity
|
|
*
|
|
* @param context Context
|
|
* @param type String - type of the toast (error, warning, info, success)
|
|
* @param content String - message of the toast
|
|
*/
|
|
public static void sendToastMessage(Context context, String type, String content) {
|
|
Intent intentBC = new Intent(context, ToastMessage.class);
|
|
Bundle args = new Bundle();
|
|
args.putString(RECEIVE_TOAST_TYPE, type);
|
|
args.putString(RECEIVE_TOAST_CONTENT, content);
|
|
intentBC.setAction(Helper.RECEIVE_TOAST_MESSAGE);
|
|
new CachedBundle(context).insertBundle(args, currentAccount, bundleId -> {
|
|
Bundle bundle = new Bundle();
|
|
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
|
|
intentBC.putExtras(bundle);
|
|
intentBC.setPackage(BuildConfig.APPLICATION_ID);
|
|
context.sendBroadcast(intentBC);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @param fragmentManager Fragment Manager
|
|
* @param containerViewId Id of the fragment container
|
|
* @param fragment Fragment to be added
|
|
* @param args Arguments to pass to the new fragment. null for none
|
|
* @param tag Tag to pass to the fragment
|
|
* @param backStackName An optional name to use when adding to back stack, or null.
|
|
*/
|
|
public static Fragment addFragment(@NonNull FragmentManager fragmentManager,
|
|
@IdRes int containerViewId,
|
|
@NonNull Fragment fragment,
|
|
@Nullable Bundle args,
|
|
@Nullable String tag,
|
|
@Nullable String backStackName) {
|
|
FragmentTransaction ft = fragmentManager.beginTransaction();
|
|
ft.setCustomAnimations(R.anim.enter, R.anim.exit, R.anim.pop_enter, R.anim.pop_exit);
|
|
Fragment _fragment = fragmentManager.findFragmentByTag(tag);
|
|
if (_fragment != null && _fragment.isAdded()) {
|
|
ft.show(_fragment).commit();
|
|
fragment = _fragment;
|
|
} else {
|
|
if (args != null) fragment.setArguments(args);
|
|
ft = fragmentManager.beginTransaction();
|
|
ft.add(containerViewId, fragment, tag);
|
|
if (backStackName != null) ft.addToBackStack(backStackName);
|
|
if (!fragmentManager.isDestroyed()) {
|
|
ft.commit();
|
|
}
|
|
}
|
|
fragmentManager.executePendingTransactions();
|
|
return fragment;
|
|
}
|
|
|
|
|
|
/**
|
|
* Load a media into a view
|
|
*
|
|
* @param view ImageView - the view where the image will be loaded
|
|
* @param url - String
|
|
*/
|
|
public static void loadImage(ImageView view, String url) {
|
|
Context context = view.getContext();
|
|
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
|
boolean disableGif = sharedpreferences.getBoolean(context.getString(R.string.SET_DISABLE_GIF), false);
|
|
if (disableGif || (!url.endsWith(".gif"))) {
|
|
Glide.with(view.getContext())
|
|
.asDrawable()
|
|
.load(url)
|
|
.thumbnail(0.1f)
|
|
.apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10)))
|
|
.into(view);
|
|
} else {
|
|
Glide.with(view.getContext())
|
|
.asGif()
|
|
.load(url)
|
|
.thumbnail(0.1f)
|
|
.apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10)))
|
|
.into(view);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Log out the authenticated user by removing its token
|
|
*
|
|
* @param activity Activity
|
|
* @throws DBException Exception
|
|
*/
|
|
public static void removeAccount(Activity activity) throws DBException {
|
|
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(activity);
|
|
//Current user
|
|
String userId = sharedpreferences.getString(PREF_USER_ID, null);
|
|
String instance = sharedpreferences.getString(PREF_USER_INSTANCE, null);
|
|
Account accountDB = new Account(activity);
|
|
|
|
OauthVM oauthVM = new ViewModelProvider((ViewModelStoreOwner) activity).get(OauthVM.class);
|
|
|
|
if (currentAccount != null) {
|
|
//Revoke the token
|
|
oauthVM.revokeToken(currentAccount.instance, currentAccount.token, currentAccount.client_id, currentAccount.client_secret);
|
|
//Log out the current user
|
|
accountDB.removeUser(currentAccount);
|
|
}
|
|
BaseAccount newAccount = accountDB.getLastUsedAccount();
|
|
SharedPreferences.Editor editor = sharedpreferences.edit();
|
|
if (newAccount == null) {
|
|
editor.putString(PREF_USER_TOKEN, null);
|
|
editor.putString(PREF_USER_INSTANCE, null);
|
|
editor.putString(PREF_USER_SOFTWARE, null);
|
|
editor.putString(PREF_USER_ID, null);
|
|
editor.commit();
|
|
Intent loginActivity = new Intent(activity, LoginActivity.class);
|
|
activity.startActivity(loginActivity);
|
|
activity.finish();
|
|
} else {
|
|
currentAccount = newAccount;
|
|
editor.putString(PREF_USER_TOKEN, newAccount.token);
|
|
editor.putString(PREF_USER_SOFTWARE, newAccount.software);
|
|
editor.putString(PREF_USER_INSTANCE, newAccount.instance);
|
|
editor.putString(PREF_USER_ID, newAccount.user_id);
|
|
BaseMainActivity.currentUserID = newAccount.user_id;
|
|
BaseMainActivity.currentToken = newAccount.token;
|
|
BaseMainActivity.currentInstance = newAccount.instance;
|
|
editor.commit();
|
|
Intent changeAccount = new Intent(activity, MainActivity.class);
|
|
changeAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
|
activity.startActivity(changeAccount);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Check if the app is not finishing
|
|
*
|
|
* @param context - Context
|
|
* @return boolean - context is valid and image can be loaded
|
|
*/
|
|
public static boolean isValidContextForGlide(final Context context) {
|
|
if (context == null) {
|
|
return false;
|
|
}
|
|
if (context instanceof Activity activity) {
|
|
return !activity.isDestroyed() && !activity.isFinishing();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Get filename from uri
|
|
*
|
|
* @param context Context
|
|
* @param uri Uri
|
|
* @return String
|
|
*/
|
|
public static String getFileName(Context context, Uri uri) {
|
|
ContentResolver resolver = context.getContentResolver();
|
|
Cursor returnCursor = null;
|
|
try {
|
|
returnCursor =
|
|
resolver.query(uri, null, null, null, null);
|
|
} catch (Exception ignored) {
|
|
}
|
|
if (returnCursor != null) {
|
|
try {
|
|
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
|
|
returnCursor.moveToFirst();
|
|
String name = returnCursor.getString(nameIndex);
|
|
returnCursor.close();
|
|
Random r = new Random();
|
|
int suf = r.nextInt(9999 - 1000) + 1000;
|
|
return suf + name;
|
|
} catch (Exception e) {
|
|
Random r = new Random();
|
|
int suf = r.nextInt(9999 - 1000) + 1000;
|
|
ContentResolver cr = context.getContentResolver();
|
|
String mime = cr.getType(uri);
|
|
if (mime != null && mime.split("/").length > 1)
|
|
return "__" + suf + "." + mime.split("/")[1];
|
|
else
|
|
return "__" + suf + ".jpg";
|
|
}
|
|
} else {
|
|
Random r = new Random();
|
|
int suf = r.nextInt(9999 - 1000) + 1000;
|
|
ContentResolver cr = context.getContentResolver();
|
|
String mime = cr.getType(uri);
|
|
if (mime != null && mime.split("/").length > 1)
|
|
return "__" + suf + "." + mime.split("/")[1];
|
|
else
|
|
return "__" + suf + ".jpg";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return shared value
|
|
*
|
|
* @param context Context
|
|
* @param type String
|
|
* @return boolean
|
|
*/
|
|
public static boolean getSharedValue(Context context, String type) {
|
|
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
|
if (type.compareTo(context.getString(R.string.SET_INVIDIOUS)) == 0) {
|
|
return sharedpreferences.getBoolean(type, false);
|
|
} else if (type.compareTo(context.getString(R.string.SET_BIBLIOGRAM)) == 0) {
|
|
return sharedpreferences.getBoolean(type, false);
|
|
} else if (type.compareTo(context.getString(R.string.SET_NITTER)) == 0) {
|
|
return sharedpreferences.getBoolean(type, false);
|
|
} else if (type.compareTo(context.getString(R.string.REPLACE_MEDIUM)) == 0) {
|
|
return sharedpreferences.getBoolean(type, false);
|
|
} else if (type.compareTo(context.getString(R.string.REPLACE_WIKIPEDIA)) == 0) {
|
|
return sharedpreferences.getBoolean(type, false);
|
|
}
|
|
return sharedpreferences.getBoolean(type, false);
|
|
}
|
|
|
|
/**
|
|
* Get size from uri
|
|
*
|
|
* @param context Context
|
|
* @param uri Uri - uri to check
|
|
* @return long - file size
|
|
*/
|
|
public static long getRealSizeFromUri(Context context, Uri uri) {
|
|
Cursor cursor = null;
|
|
try {
|
|
String[] proj = {MediaStore.Audio.Media.SIZE};
|
|
cursor = context.getContentResolver().query(uri, proj, null, null, null);
|
|
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.SIZE);
|
|
cursor.moveToFirst();
|
|
return Long.parseLong(cursor.getString(column_index));
|
|
} catch (Exception e) {
|
|
return 0;
|
|
} finally {
|
|
if (cursor != null) {
|
|
cursor.close();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Load a profile picture for the account
|
|
*
|
|
* @param view ImageView - the view where the image will be loaded
|
|
* @param account - {@link Account}
|
|
*/
|
|
public static void loadPP(Activity activity, ImageView view, BaseAccount account) {
|
|
loadPP(activity, view, account, false);
|
|
}
|
|
|
|
/**
|
|
* Load a profile picture for the account
|
|
*
|
|
* @param view ImageView - the view where the image will be loaded
|
|
* @param account - {@link Account}
|
|
*/
|
|
public static void loadPP(Activity activity, ImageView view, BaseAccount account, boolean crop) {
|
|
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(activity);
|
|
boolean disableGif = sharedpreferences.getBoolean(activity.getString(R.string.SET_DISABLE_GIF), false);
|
|
String targetedUrl = "";
|
|
if (account.mastodon_account != null) {
|
|
targetedUrl = disableGif ? account.mastodon_account.avatar_static : account.mastodon_account.avatar;
|
|
} else if (account.peertube_account != null) {
|
|
if (account.peertube_account.getAvatar() != null) {
|
|
targetedUrl = account.peertube_account.getAvatar().getPath();
|
|
if (targetedUrl != null && targetedUrl.startsWith("/")) {
|
|
targetedUrl = "https://" + account.instance + account.peertube_account.getAvatar().getPath();
|
|
}
|
|
} else {
|
|
BitmapDrawable avatar = new AvatarGenerator.AvatarBuilder(activity)
|
|
.setLabel(account.peertube_account.getAcct())
|
|
.setAvatarSize(120)
|
|
.setTextSize(30)
|
|
.toSquare()
|
|
.setBackgroundColor(fetchAccentColor(activity))
|
|
.build();
|
|
if (Helper.isValidContextForGlide(activity)) {
|
|
Glide.with(activity)
|
|
.asDrawable()
|
|
.load(avatar)
|
|
.apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10)))
|
|
.into(view);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (targetedUrl != null && Helper.isValidContextForGlide(activity)) {
|
|
if (disableGif || (!targetedUrl.endsWith(".gif"))) {
|
|
try {
|
|
RequestBuilder<Drawable> requestBuilder = Glide.with(activity)
|
|
.asDrawable()
|
|
.load(targetedUrl)
|
|
.thumbnail(0.1f);
|
|
if (crop) {
|
|
requestBuilder = requestBuilder.apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10)));
|
|
}
|
|
requestBuilder.apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10))).into(view);
|
|
} catch (Exception ignored) {
|
|
}
|
|
} else {
|
|
RequestBuilder<GifDrawable> requestBuilder = Glide.with(activity)
|
|
.asGif()
|
|
.load(targetedUrl)
|
|
.thumbnail(0.1f);
|
|
if (crop) {
|
|
requestBuilder = requestBuilder.apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10)));
|
|
}
|
|
requestBuilder.apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10))).into(view);
|
|
}
|
|
} else if (Helper.isValidContextForGlide(activity)) {
|
|
Glide.with(activity)
|
|
.asDrawable()
|
|
.load(R.drawable.ic_person)
|
|
.thumbnail(0.1f)
|
|
.apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10)))
|
|
.into(view);
|
|
}
|
|
}
|
|
|
|
public static Proxy getProxy(Context context) {
|
|
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
|
String hostVal = sharedpreferences.getString(context.getString(R.string.SET_PROXY_HOST), "127.0.0.1");
|
|
int portVal = sharedpreferences.getInt(context.getString(R.string.SET_PROXY_PORT), 8118);
|
|
final String login = sharedpreferences.getString(context.getString(R.string.SET_PROXY_LOGIN), null);
|
|
final String pwd = sharedpreferences.getString(context.getString(R.string.SET_PROXY_PASSWORD), null);
|
|
final int type = sharedpreferences.getInt(context.getString(R.string.SET_PROXY_TYPE), 0);
|
|
boolean enable_proxy = sharedpreferences.getBoolean(context.getString(R.string.SET_PROXY_ENABLED), false);
|
|
if (!enable_proxy) {
|
|
return null;
|
|
}
|
|
Proxy proxy = new Proxy(type == 0 ? Proxy.Type.HTTP : Proxy.Type.SOCKS,
|
|
InetSocketAddress.createUnresolved(hostVal, portVal));
|
|
Authenticator.setDefault(new Authenticator() {
|
|
@Override
|
|
protected PasswordAuthentication getPasswordAuthentication() {
|
|
if (getRequestingHost().equalsIgnoreCase(hostVal)) {
|
|
if (portVal == getRequestingPort()) {
|
|
return new PasswordAuthentication(login, pwd.toCharArray());
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
});
|
|
return proxy;
|
|
}
|
|
|
|
/***
|
|
* Convert Uri to byte[]
|
|
* @param context Context
|
|
* @param uri Uri
|
|
* @return byte[]
|
|
*/
|
|
public static byte[] uriToByteArray(Context context, Uri uri) {
|
|
byte[] buffer = null;
|
|
try {
|
|
InputStream iStream = context.getContentResolver().openInputStream(uri);
|
|
ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();
|
|
int bufferSize = 1024;
|
|
buffer = new byte[bufferSize];
|
|
int len;
|
|
while ((len = iStream.read(buffer)) != -1) {
|
|
byteBuffer.write(buffer, 0, len);
|
|
}
|
|
return byteBuffer.toByteArray();
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
return buffer;
|
|
}
|
|
|
|
/**
|
|
* Creates MultipartBody.Part from Uri
|
|
*
|
|
* @return MultipartBody.Part for the given Uri
|
|
*/
|
|
public static MultipartBody.Part getMultipartBody(@NonNull String paramName, @NonNull Attachment attachment) {
|
|
RequestBody requestFile = RequestBody.create(MediaType.parse(attachment.mimeType), new File(attachment.local_path));
|
|
return MultipartBody.Part.createFormData(paramName, attachment.filename, requestFile);
|
|
}
|
|
|
|
/**
|
|
* Creates MultipartBody.Part from Uri
|
|
*
|
|
* @return MultipartBody.Part for the given Uri
|
|
*/
|
|
public static MultipartBody.Part getMultipartBodyWithWM(Context context, String waterMark, @NonNull String paramName, @NonNull Attachment attachment) {
|
|
File files = new File(attachment.local_path);
|
|
float textSize = 15;
|
|
Paint paint = new Paint();
|
|
float width = paint.measureText(waterMark, 0, waterMark.length());
|
|
try {
|
|
|
|
BitmapFactory.Options options = new BitmapFactory.Options();
|
|
Bitmap backgroundBitmap = BitmapFactory.decodeFile(files.getAbsolutePath(), options);
|
|
int w = backgroundBitmap.getWidth();
|
|
int h = backgroundBitmap.getHeight();
|
|
float valx = (float) 1.0 - ((Helper.convertDpToPixel(width, context) + 10)) / (float) w;
|
|
if (valx < 0)
|
|
valx = 0;
|
|
float valy = (h - Helper.convertDpToPixel(textSize, context) - 0) / (float) h;
|
|
WatermarkText watermarkText = new WatermarkText(waterMark)
|
|
.setPositionX(valx)
|
|
.setPositionY(valy)
|
|
.setTextColor(Color.WHITE)
|
|
.setTextShadow(0.1f, 1, 1, Color.LTGRAY)
|
|
.setTextAlpha(200)
|
|
.setRotation(0)
|
|
.setTextSize(textSize);
|
|
|
|
Bitmap bitmap = WatermarkBuilder
|
|
.create(context, backgroundBitmap)
|
|
.loadWatermarkText(watermarkText)
|
|
.getWatermark()
|
|
.getOutputImage();
|
|
|
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
|
bitmap.compress(Bitmap.CompressFormat.PNG, 0, bos);
|
|
byte[] bitmapdata = bos.toByteArray();
|
|
RequestBody requestFile = RequestBody.create(MediaType.parse(attachment.mimeType), bitmapdata);
|
|
return MultipartBody.Part.createFormData(paramName, attachment.filename, requestFile);
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
RequestBody requestFile = RequestBody.create(MediaType.parse(attachment.mimeType), new File(attachment.local_path));
|
|
return MultipartBody.Part.createFormData(paramName, attachment.filename, requestFile);
|
|
}
|
|
|
|
public static MultipartBody.Part getMultipartBody(Context context, @NonNull String paramName, @NonNull Uri uri) {
|
|
byte[] imageBytes = uriToByteArray(context, uri);
|
|
ContentResolver cR = context.getApplicationContext().getContentResolver();
|
|
String mimeType = cR.getType(uri);
|
|
RequestBody requestFile = RequestBody.create(MediaType.parse(mimeType), imageBytes);
|
|
return MultipartBody.Part.createFormData(paramName, Helper.getFileName(context, uri), requestFile);
|
|
}
|
|
|
|
public static void createAttachmentFromUri(Context context, List<Uri> uris, OnAttachmentCopied callBack) {
|
|
new Thread(() -> {
|
|
List<Attachment> attachments = new ArrayList<>();
|
|
for (Uri uri : uris) {
|
|
Attachment attachment = new Attachment();
|
|
attachment.filename = Helper.getFileName(context, uri);
|
|
attachment.size = Helper.getRealSizeFromUri(context, uri);
|
|
ContentResolver cR = context.getApplicationContext().getContentResolver();
|
|
attachment.mimeType = cR.getType(uri);
|
|
|
|
MimeTypeMap mime = MimeTypeMap.getSingleton();
|
|
String extension = mime.getExtensionFromMimeType(cR.getType(uri));
|
|
if (uri.toString().endsWith("fedilab_recorded_audio.ogg")) {
|
|
extension = "ogg";
|
|
attachment.mimeType = "audio/ogg";
|
|
}
|
|
SimpleDateFormat formatter = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_" + counter, Locale.getDefault());
|
|
counter++;
|
|
Date now = new Date();
|
|
attachment.filename = formatter.format(now) + "." + extension;
|
|
Set<String> imageType = new HashSet<>(Arrays.asList("image/png", "image/jpeg", "image/jpg"));
|
|
if (imageType.contains(attachment.mimeType)) {
|
|
final File certCacheDir = new File(context.getCacheDir(), TEMP_MEDIA_DIRECTORY);
|
|
boolean isCertCacheDirExists = certCacheDir.exists();
|
|
if (!isCertCacheDirExists) {
|
|
certCacheDir.mkdirs();
|
|
}
|
|
String filePath = certCacheDir.getAbsolutePath() + "/" + attachment.filename;
|
|
MediaHelper.ResizedImageRequestBody(context, uri, new File(filePath));
|
|
attachment.local_path = filePath;
|
|
} else {
|
|
InputStream selectedFileInputStream;
|
|
try {
|
|
selectedFileInputStream = context.getContentResolver().openInputStream(uri);
|
|
if (selectedFileInputStream != null) {
|
|
final File certCacheDir = new File(context.getCacheDir(), TEMP_MEDIA_DIRECTORY);
|
|
boolean isCertCacheDirExists = certCacheDir.exists();
|
|
if (!isCertCacheDirExists) {
|
|
isCertCacheDirExists = certCacheDir.mkdirs();
|
|
}
|
|
if (isCertCacheDirExists) {
|
|
String filePath = certCacheDir.getAbsolutePath() + "/" + attachment.filename;
|
|
attachment.local_path = filePath;
|
|
OutputStream selectedFileOutPutStream = new FileOutputStream(filePath);
|
|
byte[] buffer = new byte[1024];
|
|
int length;
|
|
while ((length = selectedFileInputStream.read(buffer)) > 0) {
|
|
selectedFileOutPutStream.write(buffer, 0, length);
|
|
}
|
|
selectedFileOutPutStream.flush();
|
|
selectedFileOutPutStream.close();
|
|
}
|
|
selectedFileInputStream.close();
|
|
}
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
attachments.add(attachment);
|
|
}
|
|
Handler mainHandler = new Handler(Looper.getMainLooper());
|
|
Runnable myRunnable = () -> callBack.onAttachmentCopied(attachments);
|
|
mainHandler.post(myRunnable);
|
|
}).start();
|
|
}
|
|
|
|
|
|
public static void createFileFromUri(Context context, Uri uri, OnFileCopied callBack) {
|
|
new Thread(() -> {
|
|
InputStream selectedFileInputStream;
|
|
File file = null;
|
|
try {
|
|
String uriFullPath = uri.getPath();
|
|
String[] uriFullPathStr = uriFullPath.split(":");
|
|
String fullPath = uriFullPath;
|
|
if (uriFullPathStr.length > 1) {
|
|
fullPath = uriFullPathStr[1];
|
|
}
|
|
final String fileName = Helper.dateFileToString(context, new Date()) + ".zip";
|
|
selectedFileInputStream = context.getContentResolver().openInputStream(uri);
|
|
if (selectedFileInputStream != null) {
|
|
final File certCacheDir = new File(context.getCacheDir(), TEMP_EXPORT_DATA);
|
|
boolean isCertCacheDirExists = certCacheDir.exists();
|
|
if (!isCertCacheDirExists) {
|
|
isCertCacheDirExists = certCacheDir.mkdirs();
|
|
}
|
|
if (isCertCacheDirExists) {
|
|
String filePath = certCacheDir.getAbsolutePath() + "/" + fileName;
|
|
file = new File(filePath);
|
|
OutputStream selectedFileOutPutStream = new FileOutputStream(filePath);
|
|
byte[] buffer = new byte[1024];
|
|
int length;
|
|
while ((length = selectedFileInputStream.read(buffer)) > 0) {
|
|
selectedFileOutPutStream.write(buffer, 0, length);
|
|
}
|
|
selectedFileOutPutStream.flush();
|
|
selectedFileOutPutStream.close();
|
|
}
|
|
selectedFileInputStream.close();
|
|
}
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
Handler mainHandler = new Handler(Looper.getMainLooper());
|
|
File finalFile = file;
|
|
Runnable myRunnable = () -> callBack.onFileCopied(finalFile);
|
|
mainHandler.post(myRunnable);
|
|
}).start();
|
|
}
|
|
|
|
public static void createAttachmentFromPAth(String path, OnAttachmentCopied callBack) {
|
|
new Thread(() -> {
|
|
List<Attachment> attachmentList = new ArrayList<>();
|
|
Attachment attachment = new Attachment();
|
|
attachment.mimeType = "image/*";
|
|
String extension = "jpg";
|
|
SimpleDateFormat formatter = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_" + counter, Locale.getDefault());
|
|
attachment.local_path = path;
|
|
Date now = new Date();
|
|
attachment.filename = formatter.format(now) + "." + extension;
|
|
Handler mainHandler = new Handler(Looper.getMainLooper());
|
|
attachmentList.add(attachment);
|
|
Runnable myRunnable = () -> callBack.onAttachmentCopied(attachmentList);
|
|
mainHandler.post(myRunnable);
|
|
}).start();
|
|
}
|
|
|
|
/**
|
|
* change color of a drawable
|
|
*
|
|
* @param imageView int the ImageView
|
|
* @param hexaColor example 0xffff00
|
|
*/
|
|
public static void changeDrawableColor(Context context, ImageView imageView, int hexaColor) {
|
|
if (imageView == null)
|
|
return;
|
|
int color;
|
|
try {
|
|
color = context.getResources().getColor(hexaColor);
|
|
} catch (Resources.NotFoundException e) {
|
|
color = hexaColor;
|
|
}
|
|
imageView.setColorFilter(color);
|
|
}
|
|
|
|
/**
|
|
* change color of a drawable
|
|
*
|
|
* @param drawable int the drawable
|
|
* @param hexaColor example 0xffff00
|
|
*/
|
|
public static Drawable changeDrawableColor(Context context, int drawable, int hexaColor) {
|
|
Drawable mDrawable = ContextCompat.getDrawable(context, drawable);
|
|
int color;
|
|
try {
|
|
color = Color.parseColor(context.getString(hexaColor));
|
|
} catch (Resources.NotFoundException e) {
|
|
try {
|
|
TypedValue typedValue = new TypedValue();
|
|
Resources.Theme theme = context.getTheme();
|
|
theme.resolveAttribute(hexaColor, typedValue, true);
|
|
color = typedValue.data;
|
|
} catch (Resources.NotFoundException ed) {
|
|
color = hexaColor;
|
|
}
|
|
}
|
|
assert mDrawable != null;
|
|
mDrawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
|
|
DrawableCompat.setTint(mDrawable, color);
|
|
return mDrawable;
|
|
}
|
|
|
|
/**
|
|
* Convert a date in String -> format yyyy-MM-dd HH:mm:ss
|
|
*
|
|
* @param context Context
|
|
* @param date Date
|
|
* @return String
|
|
*/
|
|
public static String dateFileToString(Context context, Date date) {
|
|
Locale userLocale;
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
userLocale = context.getResources().getConfiguration().getLocales().get(0);
|
|
} else {
|
|
userLocale = context.getResources().getConfiguration().locale;
|
|
}
|
|
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", userLocale);
|
|
return dateFormat.format(date);
|
|
}
|
|
|
|
/**
|
|
* Change locale
|
|
*
|
|
* @param activity - Activity
|
|
*/
|
|
public static void setLocale(Activity activity) {
|
|
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(activity);
|
|
String defaultLocaleString = sharedpreferences.getString(activity.getString(R.string.SET_DEFAULT_LOCALE_NEW), null);
|
|
if (defaultLocaleString != null) {
|
|
Locale locale;
|
|
if (defaultLocaleString.equals("zh-CN")) {
|
|
locale = Locale.SIMPLIFIED_CHINESE;
|
|
} else if (defaultLocaleString.equals("zh-TW")) {
|
|
locale = Locale.TRADITIONAL_CHINESE;
|
|
} else {
|
|
locale = new Locale(defaultLocaleString);
|
|
}
|
|
Locale.setDefault(locale);
|
|
Resources resources = activity.getResources();
|
|
Configuration config = resources.getConfiguration();
|
|
config.setLocale(locale);
|
|
resources.updateConfiguration(config, resources.getDisplayMetrics());
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send broadcast to recreate Mainactivity
|
|
*
|
|
* @param activity - Activity
|
|
*/
|
|
public static void recreateMainActivity(Activity activity) {
|
|
Bundle args = new Bundle();
|
|
args.putBoolean(Helper.RECEIVE_RECREATE_ACTIVITY, true);
|
|
Intent intentBD = new Intent(Helper.BROADCAST_DATA);
|
|
new CachedBundle(activity).insertBundle(args, currentAccount, bundleId -> {
|
|
Bundle bundle = new Bundle();
|
|
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
|
|
intentBD.putExtras(bundle);
|
|
intentBD.setPackage(BuildConfig.APPLICATION_ID);
|
|
activity.sendBroadcast(intentBD);
|
|
});
|
|
}
|
|
|
|
public static void showKeyboard(Context context, View view) {
|
|
view.requestFocus();
|
|
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
|
|
imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
|
|
}
|
|
|
|
/**
|
|
* Sends notification with intent
|
|
*
|
|
* @param context Context
|
|
* @param intent Intent associated to the notifcation
|
|
* @param icon Bitmap profile picture
|
|
* @param title String title of the notification
|
|
* @param message String message for the notification
|
|
*/
|
|
@SuppressLint("UnspecifiedImmutableFlag")
|
|
public static void notify_user(Context context, BaseAccount account, Intent intent, Bitmap icon, NotifType notifType, String title, String message) {
|
|
final SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
|
// prepare intent which is triggered if the user click on the notification
|
|
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
|
|
int requestCode = (int) System.currentTimeMillis();
|
|
PendingIntent pIntent;
|
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
|
|
pIntent = PendingIntent.getActivity(context, requestCode, intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT);
|
|
} else {
|
|
pIntent = PendingIntent.getActivity(context, requestCode, intent, PendingIntent.FLAG_ONE_SHOT);
|
|
}
|
|
intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
|
RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
|
|
// build notification
|
|
String channelId;
|
|
String channelTitle;
|
|
|
|
switch (notifType) {
|
|
case FAV -> {
|
|
channelId = "channel_favourite";
|
|
channelTitle = context.getString(R.string.channel_notif_fav);
|
|
}
|
|
case FOLLLOW -> {
|
|
channelId = "channel_follow";
|
|
channelTitle = context.getString(R.string.channel_notif_follow);
|
|
}
|
|
case MENTION -> {
|
|
channelId = "channel_mention";
|
|
channelTitle = context.getString(R.string.channel_notif_mention);
|
|
}
|
|
case POLL -> {
|
|
channelId = "channel_poll";
|
|
channelTitle = context.getString(R.string.channel_notif_poll);
|
|
}
|
|
case BACKUP -> {
|
|
channelId = "channel_backup";
|
|
channelTitle = context.getString(R.string.channel_notif_backup);
|
|
}
|
|
case STORE -> {
|
|
channelId = "channel_media";
|
|
channelTitle = context.getString(R.string.channel_notif_media);
|
|
}
|
|
case TOOT -> {
|
|
channelId = "channel_status";
|
|
channelTitle = context.getString(R.string.channel_notif_status);
|
|
}
|
|
case UPDATE -> {
|
|
channelId = "channel_update";
|
|
channelTitle = context.getString(R.string.channel_notif_update);
|
|
}
|
|
case SIGN_UP -> {
|
|
channelId = "channel_signup";
|
|
channelTitle = context.getString(R.string.channel_notif_signup);
|
|
}
|
|
case REPORT -> {
|
|
channelId = "channel_report";
|
|
channelTitle = context.getString(R.string.channel_notif_report);
|
|
}
|
|
default -> {
|
|
channelId = "channel_boost";
|
|
channelTitle = context.getString(R.string.channel_notif_boost);
|
|
}
|
|
}
|
|
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, channelId)
|
|
.setSmallIcon(getNotificationIcon(context)).setTicker(message);
|
|
/* if (notifType == NotifType.MENTION) {
|
|
if (message.length() > 500) {
|
|
message = message.substring(0, 499) + "…";
|
|
}
|
|
}*/
|
|
notificationBuilder.setGroup(account.mastodon_account != null ? account.mastodon_account.username + "@" + account.instance : "@" + account.instance)
|
|
.setContentIntent(pIntent)
|
|
.setContentText(message);
|
|
int ledColour = Color.BLUE;
|
|
int prefColor;
|
|
prefColor = Integer.parseInt(sharedpreferences.getString(context.getString(R.string.SET_LED_COLOUR_VAL_N), String.valueOf(LED_COLOUR)));
|
|
switch (prefColor) {
|
|
case 1 -> // CYAN
|
|
ledColour = Color.CYAN;
|
|
case 2 -> // MAGENTA
|
|
ledColour = Color.MAGENTA;
|
|
case 3 -> // GREEN
|
|
ledColour = Color.GREEN;
|
|
case 4 -> // RED
|
|
ledColour = Color.RED;
|
|
case 5 -> // YELLOW
|
|
ledColour = Color.YELLOW;
|
|
case 6 -> // WHITE
|
|
ledColour = Color.WHITE;
|
|
}
|
|
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
NotificationChannel channel;
|
|
NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
|
if (sharedpreferences.getBoolean(context.getString(R.string.SET_NOTIF_SILENT), false)) {
|
|
channel = new NotificationChannel(channelId, channelTitle, NotificationManager.IMPORTANCE_LOW);
|
|
channel.setSound(null, null);
|
|
channel.setVibrationPattern(new long[]{500, 500, 500});
|
|
channel.enableVibration(true);
|
|
channel.setLightColor(ledColour);
|
|
} else {
|
|
channel = new NotificationChannel(channelId, channelTitle, NotificationManager.IMPORTANCE_DEFAULT);
|
|
/*String soundUri = sharedpreferences.getString(context.getString(R.string.SET_NOTIF_SOUND), ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + context.getPackageName() + "/" + R.raw.boop);
|
|
AudioAttributes audioAttributes = new AudioAttributes.Builder()
|
|
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
|
|
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
|
|
.build();
|
|
channel.setSound(Uri.parse(soundUri), audioAttributes);*/
|
|
}
|
|
assert mNotificationManager != null;
|
|
mNotificationManager.createNotificationChannel(channel);
|
|
} else {
|
|
if (sharedpreferences.getBoolean(context.getString(R.string.SET_NOTIF_SILENT), false)) {
|
|
notificationBuilder.setVibrate(new long[]{500, 500, 500});
|
|
} else {
|
|
String soundUri = sharedpreferences.getString(context.getString(R.string.SET_NOTIF_SOUND), ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + context.getPackageName() + "/" + R.raw.boop);
|
|
notificationBuilder.setSound(Uri.parse(soundUri));
|
|
}
|
|
notificationBuilder.setLights(ledColour, 500, 1000);
|
|
}
|
|
notificationBuilder.setContentTitle(title);
|
|
notificationBuilder.setLargeIcon(icon);
|
|
notificationBuilder.setSubText(String.format("@%s@%s", account.mastodon_account.username, account.instance));
|
|
|
|
Notification summaryNotification = new NotificationCompat.Builder(context, channelId)
|
|
.setContentTitle(title)
|
|
.setContentText(channelTitle)
|
|
.setContentIntent(pIntent)
|
|
.setLargeIcon(icon)
|
|
.setSmallIcon(getNotificationIcon(context))
|
|
.setStyle(new NotificationCompat.BigTextStyle().bigText(message))
|
|
.setGroup(account.mastodon_account != null ? account.mastodon_account.username + "@" + account.instance : "@" + account.instance)
|
|
.setGroupSummary(true)
|
|
.build();
|
|
|
|
notificationManager.notify(notificationId++, notificationBuilder.build());
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
|
|
notificationManager.notify(0, summaryNotification);
|
|
}
|
|
}
|
|
|
|
|
|
public static String dateDiffFull(Date dateToot) {
|
|
SimpleDateFormat df = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.MEDIUM, Locale.getDefault());
|
|
try {
|
|
return df.format(dateToot);
|
|
} catch (Exception e) {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
|
|
public static String dateDiffFullShort(Date dateToot) {
|
|
SimpleDateFormat df = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.getDefault());
|
|
try {
|
|
return df.format(dateToot);
|
|
} catch (Exception e) {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Makes the tvDate TextView field clickable, and displays the absolute date & time of a toot
|
|
* for 5 seconds.
|
|
*
|
|
* @param context Context
|
|
* @param tvDate TextView
|
|
* @param date Date
|
|
*/
|
|
|
|
public static void absoluteDateTimeReveal(final Context context, final TextView tvDate, final Date date, final Date dateEdit) {
|
|
tvDate.setOnClickListener(v -> {
|
|
|
|
if (dateEdit == null) {
|
|
tvDate.setText(dateDiffFull(date));
|
|
} else {
|
|
String dateEditText = context.getString(R.string.full_date_edited, dateDiffFull(date), dateDiffFull(dateEdit));
|
|
tvDate.setText(dateEditText);
|
|
}
|
|
|
|
new CountDownTimer((5 * 1000), 1000) {
|
|
|
|
public void onTick(long millisUntilFinished) {
|
|
}
|
|
|
|
public void onFinish() {
|
|
tvDate.setText(dateDiff(context, date));
|
|
}
|
|
}.start();
|
|
});
|
|
}
|
|
|
|
public static String generateString() {
|
|
String uuid = UUID.randomUUID().toString();
|
|
return "@fedilab_fetch_more_" + uuid;
|
|
}
|
|
|
|
public static String generateIdString() {
|
|
String uuid = UUID.randomUUID().toString();
|
|
return "@fedilab_compose_" + uuid;
|
|
}
|
|
|
|
public static Gson getDateBuilder() {
|
|
SimpleDateFormat[] formats = new SimpleDateFormat[]{
|
|
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.getDefault()),
|
|
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault()),
|
|
};
|
|
return new GsonBuilder()
|
|
.registerTypeAdapter(Date.class, new TypeAdapter<Date>() {
|
|
|
|
@Override
|
|
public void write(JsonWriter out, Date value) {
|
|
}
|
|
|
|
@Override
|
|
public Date read(JsonReader reader) throws IOException {
|
|
if (reader.peek() == JsonToken.NULL) {
|
|
reader.nextNull();
|
|
return null;
|
|
}
|
|
String dateAsString = reader.nextString();
|
|
for (SimpleDateFormat format : formats) {
|
|
try {
|
|
format.setTimeZone(TimeZone.getTimeZone("UTC"));
|
|
return format.parse(dateAsString);
|
|
} catch (ParseException ignored) {
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
})
|
|
.create();
|
|
}
|
|
|
|
/***
|
|
* Download method which works for http and https connections
|
|
* @param downloadUrl String download url
|
|
* @param listener OnDownloadInterface, listener which manages progress
|
|
*/
|
|
public static void download(Context context, final String downloadUrl, final OnDownloadInterface listener) {
|
|
new Thread(() -> {
|
|
URL url;
|
|
int CHUNK_SIZE = 4096;
|
|
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
|
boolean enable_proxy = sharedpreferences.getBoolean(context.getString(R.string.SET_PROXY_ENABLED), false);
|
|
Proxy proxy = null;
|
|
if (enable_proxy) {
|
|
proxy = getProxy(context);
|
|
}
|
|
try {
|
|
url = new URL(downloadUrl);
|
|
HttpURLConnection httpURLConnection;
|
|
if (proxy != null)
|
|
httpURLConnection = (HttpURLConnection) url.openConnection(proxy);
|
|
else
|
|
httpURLConnection = (HttpURLConnection) url.openConnection();
|
|
// httpURLConnection.setRequestProperty("User-Agent", USER_AGENT);
|
|
int responseCode = httpURLConnection.getResponseCode();
|
|
|
|
// always check HTTP response code first
|
|
if (responseCode == HttpURLConnection.HTTP_OK) {
|
|
String fileName = "";
|
|
String disposition = httpURLConnection.getHeaderField("Content-Disposition");
|
|
|
|
if (disposition != null) {
|
|
// extracts file name from header field
|
|
int index = disposition.indexOf("filename=");
|
|
if (index > 0) {
|
|
fileName = disposition.substring(index + 10,
|
|
disposition.length() - 1);
|
|
}
|
|
} else {
|
|
// extracts file name from URL
|
|
fileName = downloadUrl.substring(downloadUrl.lastIndexOf("/") + 1);
|
|
}
|
|
fileName = FileNameCleaner.cleanFileName(fileName);
|
|
// opens input stream from the HTTP connection
|
|
InputStream inputStream = httpURLConnection.getInputStream();
|
|
final File saveDir = new File(context.getCacheDir(), TEMP_MEDIA_DIRECTORY);
|
|
boolean isCertCacheDirExists = saveDir.exists();
|
|
if (!isCertCacheDirExists) {
|
|
saveDir.mkdirs();
|
|
}
|
|
final String saveFilePath = saveDir + File.separator + fileName;
|
|
// opens an output stream to save into file
|
|
FileOutputStream outputStream = new FileOutputStream(saveFilePath);
|
|
|
|
int bytesRead;
|
|
byte[] buffer = new byte[CHUNK_SIZE];
|
|
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
|
outputStream.write(buffer, 0, bytesRead);
|
|
}
|
|
outputStream.close();
|
|
inputStream.close();
|
|
((ComposeActivity) context).runOnUiThread(() -> listener.onDownloaded(saveFilePath, downloadUrl, null));
|
|
} else {
|
|
((ComposeActivity) context).runOnUiThread(() -> listener.onDownloaded(null, downloadUrl, new Error()));
|
|
|
|
}
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
((ComposeActivity) context).runOnUiThread(() -> listener.onDownloaded(null, downloadUrl, new Error()));
|
|
}
|
|
|
|
}).start();
|
|
}
|
|
|
|
public static <T, E> T getKeyByValue(Map<T, E> map, E value) {
|
|
for (Map.Entry<T, E> entry : map.entrySet()) {
|
|
if (Objects.equals(value, entry.getValue())) {
|
|
return entry.getKey();
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public static void displayReleaseNotesIfNeeded(Activity activity, boolean forced) {
|
|
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(activity);
|
|
int lastReleaseNoteRead = sharedpreferences.getInt(activity.getString(R.string.SET_POPUP_RELEASE_NOTES), 0);
|
|
int versionCode = BuildConfig.VERSION_CODE;
|
|
boolean disabled = sharedpreferences.getBoolean(activity.getString(R.string.SET_DISABLE_RELEASE_NOTES_ALERT), false);
|
|
if (disabled && !forced) {
|
|
return;
|
|
}
|
|
if (lastReleaseNoteRead != versionCode || forced) {
|
|
try {
|
|
InputStream is = activity.getAssets().open("release_notes/notes.json");
|
|
int size;
|
|
size = is.available();
|
|
byte[] buffer = new byte[size];
|
|
is.read(buffer);
|
|
is.close();
|
|
String json = new String(buffer, StandardCharsets.UTF_8);
|
|
Gson gson = new Gson();
|
|
AlertDialog.Builder dialogBuilderOptin = new MaterialAlertDialogBuilder(activity);
|
|
PopupReleaseNotesBinding binding = PopupReleaseNotesBinding.inflate(activity.getLayoutInflater());
|
|
dialogBuilderOptin.setView(binding.getRoot());
|
|
try {
|
|
List<ReleaseNote.Note> releaseNotes = gson.fromJson(json, new TypeToken<List<ReleaseNote.Note>>() {
|
|
}.getType());
|
|
if (releaseNotes != null && releaseNotes.size() > 0) {
|
|
ReleaseNoteAdapter adapter = new ReleaseNoteAdapter(releaseNotes);
|
|
binding.releasenotes.setAdapter(adapter);
|
|
binding.releasenotes.setLayoutManager(new LinearLayoutManager(activity));
|
|
}
|
|
} catch (Exception ignored) {
|
|
}
|
|
if (BuildConfig.DONATIONS) {
|
|
binding.aboutSupport.setVisibility(View.VISIBLE);
|
|
binding.aboutSupportPaypal.setVisibility(View.VISIBLE);
|
|
} else {
|
|
binding.aboutSupport.setVisibility(View.GONE);
|
|
binding.aboutSupportPaypal.setVisibility(View.GONE);
|
|
}
|
|
binding.accountFollow.setIconResource(R.drawable.ic_baseline_person_add_24);
|
|
binding.aboutSupport.setOnClickListener(v -> {
|
|
Intent intentLiberapay = new Intent(Intent.ACTION_VIEW);
|
|
intentLiberapay.setData(Uri.parse("https://liberapay.com/tom79"));
|
|
try {
|
|
activity.startActivity(intentLiberapay);
|
|
} catch (Exception e) {
|
|
Helper.openBrowser(activity, "https://liberapay.com/tom79");
|
|
}
|
|
});
|
|
binding.aboutSupportPaypal.setOnClickListener(v -> Helper.openBrowser(activity, "https://www.paypal.me/Mastalab"));
|
|
CrossActionHelper.fetchRemoteAccount(activity, "@apps@toot.fedilab.app", new CrossActionHelper.Callback() {
|
|
@Override
|
|
public void federatedStatus(Status status) {
|
|
|
|
}
|
|
|
|
@Override
|
|
public void federatedAccount(app.fedilab.android.mastodon.client.entities.api.Account account) {
|
|
if (account != null && account.username.equalsIgnoreCase("apps")) {
|
|
|
|
MastodonHelper.loadPPMastodon(binding.accountPp, account);
|
|
binding.accountDn.setText(account.display_name);
|
|
binding.accountUn.setText(account.acct);
|
|
binding.accountPp.setOnClickListener(v -> {
|
|
Intent intent = new Intent(activity, ProfileActivity.class);
|
|
Bundle args = new Bundle();
|
|
args.putSerializable(Helper.ARG_ACCOUNT, account);
|
|
new CachedBundle(activity).insertBundle(args, currentAccount, bundleId -> {
|
|
Bundle bundle = new Bundle();
|
|
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
|
|
intent.putExtras(bundle);
|
|
activity.startActivity(intent);
|
|
});
|
|
});
|
|
|
|
AccountsVM accountsVM = new ViewModelProvider((ViewModelStoreOwner) activity).get(AccountsVM.class);
|
|
List<String> ids = new ArrayList<>();
|
|
ids.add(account.id);
|
|
accountsVM.getRelationships(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, ids)
|
|
.observe((LifecycleOwner) activity, relationShips -> {
|
|
if (relationShips != null && relationShips.size() > 0) {
|
|
if (!relationShips.get(0).following) {
|
|
binding.acccountContainer.setVisibility(View.VISIBLE);
|
|
binding.accountFollow.setVisibility(View.VISIBLE);
|
|
binding.accountFollow.setOnClickListener(v -> accountsVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, true, false, null)
|
|
.observe((LifecycleOwner) activity, relationShip -> binding.accountFollow.setVisibility(View.GONE)));
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
});
|
|
dialogBuilderOptin.setPositiveButton(R.string.close, (dialog, id) -> dialog.dismiss());
|
|
try {
|
|
Handler handler = new Handler();
|
|
handler.postDelayed(() -> {
|
|
if (!activity.isFinishing()) {
|
|
dialogBuilderOptin.show();
|
|
}
|
|
}, 1000);
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
|
|
SharedPreferences.Editor editor = sharedpreferences.edit();
|
|
editor.putInt(activity.getString(R.string.SET_POPUP_RELEASE_NOTES), versionCode);
|
|
editor.apply();
|
|
}
|
|
}
|
|
|
|
public static String getSlugOfFirstFragment(Context context, String userId, String instance) {
|
|
if (slugOfFirstFragment != null) {
|
|
return slugOfFirstFragment;
|
|
}
|
|
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
|
return sharedpreferences.getString(Helper.ARG_SLUG_OF_FIRST_FRAGMENT + userId + instance, Timeline.TimeLineEnum.HOME.getValue());
|
|
}
|
|
|
|
public static void setSlugOfFirstFragment(Context context, String slug, String userId, String instance) {
|
|
if (slug != null) {
|
|
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
|
SharedPreferences.Editor editor = sharedpreferences.edit();
|
|
slugOfFirstFragment = slug;
|
|
editor.putString(Helper.ARG_SLUG_OF_FIRST_FRAGMENT + userId + instance, slug);
|
|
editor.apply();
|
|
}
|
|
}
|
|
|
|
public static int compareTo(String value1, String value2) {
|
|
try {
|
|
long val1 = Long.parseLong(value1);
|
|
long val2 = Long.parseLong(value2);
|
|
return Long.compare(val1, val2);
|
|
} catch (Exception e) {
|
|
return value1.compareTo(value2);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Restart the app
|
|
*
|
|
* @param context
|
|
*/
|
|
public static void restart(Context context) {
|
|
Context ctx = context.getApplicationContext();
|
|
PackageManager pm = ctx.getPackageManager();
|
|
Intent intent = pm.getLaunchIntentForPackage(ctx.getPackageName());
|
|
Intent mainIntent = Intent.makeRestartActivityTask(intent.getComponent());
|
|
ctx.startActivity(mainIntent);
|
|
Runtime.getRuntime().exit(0);
|
|
}
|
|
|
|
|
|
public static void forwardToBrowser(Activity activity, Intent i) {
|
|
Intent intent = new Intent();
|
|
intent.setAction(android.content.Intent.ACTION_VIEW);
|
|
intent.setDataAndType(i.getData(), i.getType());
|
|
List<ResolveInfo> activities = activity.getPackageManager().queryIntentActivities(intent, 0);
|
|
ArrayList<Intent> targetIntents = new ArrayList<>();
|
|
String thisPackageName = activity.getPackageName();
|
|
for (ResolveInfo currentInfo : activities) {
|
|
String packageName = currentInfo.activityInfo.packageName;
|
|
if (!thisPackageName.equals(packageName)) {
|
|
Intent targetIntent = new Intent(android.content.Intent.ACTION_VIEW);
|
|
targetIntent.setDataAndType(intent.getData(), intent.getType());
|
|
targetIntent.setPackage(intent.getPackage());
|
|
targetIntent.setComponent(new ComponentName(packageName, currentInfo.activityInfo.name));
|
|
targetIntents.add(targetIntent);
|
|
}
|
|
}
|
|
if (targetIntents.size() > 0) {
|
|
Intent chooserIntent = Intent.createChooser(targetIntents.remove(0), activity.getString(R.string.open_with));
|
|
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetIntents.toArray(new Parcelable[]{}));
|
|
activity.startActivity(chooserIntent);
|
|
}
|
|
}
|
|
|
|
|
|
public static int dialogStyle() {
|
|
if (R.style.AppThemeBar == currentThemeId || R.style.AppTheme == currentThemeId) {
|
|
return R.style.AppThemeAlertDialog;
|
|
} else if (R.style.SolarizedAppThemeBar == currentThemeId || R.style.SolarizedAppTheme == currentThemeId) {
|
|
return R.style.SolarizedAlertDialog;
|
|
} else if (R.style.BlackAppThemeBar == currentThemeId || R.style.BlackAppTheme == currentThemeId) {
|
|
return R.style.BlackAlertDialog;
|
|
} else if (R.style.DraculaAppThemeBar == currentThemeId || R.style.DraculaAppTheme == currentThemeId) {
|
|
return R.style.DraculaAlertDialog;
|
|
}
|
|
return R.style.AppTheme;
|
|
}
|
|
|
|
public static void addMutedAccount(app.fedilab.android.mastodon.client.entities.api.Account target) {
|
|
if (MainActivity.filteredAccounts == null) {
|
|
MainActivity.filteredAccounts = new ArrayList<>();
|
|
}
|
|
if (!MainActivity.filteredAccounts.contains(target)) {
|
|
MainActivity.filteredAccounts.add(target);
|
|
}
|
|
}
|
|
|
|
public static void removeMutedAccount(app.fedilab.android.mastodon.client.entities.api.Account target) {
|
|
if (MainActivity.filteredAccounts != null) {
|
|
MainActivity.filteredAccounts.remove(target);
|
|
}
|
|
}
|
|
|
|
public static boolean isNumeric(String str) {
|
|
try {
|
|
Double.parseDouble(str);
|
|
return true;
|
|
} catch (NumberFormatException e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public static OkHttpClient myOkHttpClient(Context context) {
|
|
return new OkHttpClient.Builder()
|
|
.addInterceptor(chain -> {
|
|
Request originalRequest = chain.request();
|
|
Request requestWithUserAgent = originalRequest.newBuilder()
|
|
.header("User-Agent", context.getString(R.string.app_name) + "/" + BuildConfig.VERSION_NAME + "/" + BuildConfig.VERSION_CODE)
|
|
.build();
|
|
return chain.proceed(requestWithUserAgent);
|
|
})
|
|
.readTimeout(60, TimeUnit.SECONDS)
|
|
.connectTimeout(60, TimeUnit.SECONDS)
|
|
.callTimeout(60, TimeUnit.SECONDS)
|
|
.proxy(Helper.getProxy(context))
|
|
.build();
|
|
}
|
|
|
|
//Enum that described actions to replace inside a toot content
|
|
public enum PatternType {
|
|
MENTION,
|
|
MENTION_LONG,
|
|
TAG,
|
|
GROUP
|
|
}
|
|
|
|
public enum NotifType {
|
|
FOLLLOW,
|
|
MENTION,
|
|
BOOST,
|
|
FAV,
|
|
POLL,
|
|
UPDATE,
|
|
SIGN_UP,
|
|
REPORT,
|
|
STATUS,
|
|
BACKUP,
|
|
STORE,
|
|
TOOT
|
|
}
|
|
|
|
public interface OnAttachmentCopied {
|
|
void onAttachmentCopied(List<Attachment> attachments);
|
|
}
|
|
|
|
public interface OnFileCopied {
|
|
void onFileCopied(File file);
|
|
}
|
|
}
|