package org.mian.gitnex.helpers; import android.app.Activity; import android.content.ActivityNotFoundException; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Typeface; import android.net.Uri; import android.os.Build; import android.util.Base64; import android.util.DisplayMetrics; import android.util.TypedValue; import android.view.View; import androidx.annotation.AttrRes; import androidx.annotation.ColorInt; import androidx.browser.customtabs.CustomTabColorSchemeParams; import androidx.browser.customtabs.CustomTabsIntent; import androidx.core.content.pm.PackageInfoCompat; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.StandardCharsets; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.mian.gitnex.R; import org.mian.gitnex.activities.LoginActivity; import org.mian.gitnex.activities.MainActivity; import org.mian.gitnex.core.MainApplication; import org.mian.gitnex.database.api.BaseApi; import org.mian.gitnex.database.api.UserAccountsApi; import org.mian.gitnex.database.models.UserAccount; /** * @author M M Arif */ public class AppUtil { private static final HashMap extensions = new HashMap<>(); public static Typeface typeface; static { extensions.put( new String[] {"jpg", "jpeg", "gif", "png", "ico", "tif", "tiff", "bmp"}, FileType.IMAGE); extensions.put( new String[] { "mp3", "wav", "opus", "flac", "wma", "aac", "m4a", "oga", "mpc", "ogg" }, FileType.AUDIO); extensions.put( new String[] { "mp4", "mkv", "avi", "mov", "wmv", "qt", "mts", "m2ts", "webm", "flv", "ogv", "amv", "mpg", "mpeg", "mpv", "m4v", "3gp", "wmv" }, FileType.VIDEO); extensions.put( new String[] { "doc", "docx", "ppt", "pptx", "xls", "xlsx", "xlsm", "odt", "ott", "odf", "ods", "ots", "odg", "otg", "odp", "otp", "bin", "psd", "xcf", "pdf" }, FileType.DOCUMENT); extensions.put( new String[] {"exe", "msi", "jar", "dmg", "deb", "apk"}, FileType.EXECUTABLE); extensions.put( new String[] { "txt", "md", "json", "java", "go", "php", "c", "cc", "cpp", "h", "cxx", "cyc", "m", "cs", "bash", "sh", "bsh", "cv", "python", "perl", "pm", "rb", "ruby", "javascript", "coffee", "rc", "rs", "rust", "basic", "clj", "css", "dart", "lisp", "erl", "hs", "lsp", "rkt", "ss", "llvm", "ll", "lua", "matlab", "pascal", "r", "scala", "sql", "latex", "tex", "vb", "vbs", "vhd", "tcl", "wiki.meta", "yaml", "yml", "markdown", "xml", "proto", "regex", "py", "pl", "js", "html", "htm", "volt", "ini", "htaccess", "conf", "gitignore", "gradle", "txt", "properties", "bat", "twig", "cvs", "cmake", "in", "info", "spec", "m4", "am", "dist", "pam", "hx", "ts", "kt", "kts" }, FileType.TEXT); extensions.put(new String[] {"ttf", "otf", "woff", "woff2", "ttc", "eot"}, FileType.FONT); } // AppUtil should not be instantiated. private AppUtil() {} public static void logout(Context ctx) { TinyDB tinyDB = TinyDB.getInstance(ctx); UserAccountsApi api = BaseApi.getInstance(ctx, UserAccountsApi.class); assert api != null; api.logout(tinyDB.getInt("currentActiveAccountId")); if (api.getCount() >= 1) { switchToAccount(ctx, api.loggedInUserAccounts().get(0)); if (ctx instanceof MainActivity) { ((Activity) ctx).recreate(); } else { // if it's not a MainActivity, open MainActivity instead of current one ((Activity) ctx).finish(); Intent intent = new Intent(ctx, MainActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); ctx.startActivity(intent); } } else { tinyDB.putInt("currentActiveAccountId", -2); ((Activity) ctx).finish(); Intent intent = new Intent(ctx, LoginActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); ctx.startActivity(intent); } } public static FileType getFileType(String extension) { if (extension != null && !extension.isEmpty()) { for (String[] testExtensions : extensions.keySet()) { for (String testExtension : testExtensions) { if (testExtension.equalsIgnoreCase(extension)) { return extensions.get(testExtensions); } } } } return FileType.UNKNOWN; } public static boolean hasNetworkConnection(Context context) { return NetworkStatusObserver.getInstance(context).hasNetworkConnection(); } public static void copyProgress( InputStream inputStream, OutputStream outputStream, long totalSize, ProgressListener progressListener) throws IOException { byte[] buffer = new byte[4096]; int read; long totalSteps = (long) Math.ceil((double) totalSize / buffer.length); long stepsPerPercent = (long) Math.floor((double) totalSteps / 100); short percent = 0; long stepCount = 0; progressListener.onActionStarted(); while ((read = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, read); stepCount++; if (stepCount == stepsPerPercent) { percent++; if (percent <= 100) { progressListener.onProgressChanged(percent); } stepCount = 0; } } if (percent < 100) { progressListener.onProgressChanged((short) 100); } progressListener.onActionFinished(); } public static int getAppBuildNo(Context context) { try { PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); return (int) PackageInfoCompat.getLongVersionCode(packageInfo); } catch (PackageManager.NameNotFoundException e) { throw new RuntimeException("Could not get package name: " + e); } } public static String getAppVersion(Context context) { try { PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); return packageInfo.versionName; } catch (PackageManager.NameNotFoundException e) { throw new RuntimeException("Could not get package name: " + e); } } public static boolean isPro(Context context) { return context.getPackageName().equals("org.mian.gitnex.pro"); } public static Boolean checkStringsWithAlphaNumeric(String str) { // [a-zA-Z0-9] return str.matches("^\\w+$"); } public static Boolean checkStrings(String str) { // [a-zA-Z0-9-_. ] return str.matches("^[\\w .-]+$"); } public static Boolean checkLabel(String str) { // [a-zA-Z0-9-_. /:] return str.matches("^[\\w .-/:]+$"); } public static Boolean checkStringsWithAlphaNumericDashDotUnderscore( String str) { // [a-zA-Z0-9-_] return str.matches("^[\\w.-]+$"); } public static Boolean checkStringsWithDash(String str) { // [a-zA-Z0-9-_. ] return str.matches("^[\\w-]+$"); } public static Boolean checkIntegers(String str) { return str.matches("\\d+"); } public static void setAppLocale(Resources resource, String locCode) { String[] multiCodeLang = locCode.split("-"); String countryCode; if (locCode.contains("-")) { locCode = multiCodeLang[0]; countryCode = multiCodeLang[1]; } else { countryCode = ""; } DisplayMetrics dm = resource.getDisplayMetrics(); Configuration config = resource.getConfiguration(); config.setLocale(new Locale(locCode.toLowerCase(), countryCode)); resource.updateConfiguration(config, dm); } public static String getTimestampFromDate(Context context, Date date) { String[] locale_ = AppDatabaseSettings.getSettingsValue(context, AppDatabaseSettings.APP_LOCALE_KEY) .split("\\|"); Locale locale = new Locale(locale_[1]); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX", locale).format(date); } else { return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", locale).format(date); } } @ColorInt public static int getColorFromAttribute(Context context, @AttrRes int resid) { TypedValue typedValue = new TypedValue(); context.getTheme().resolveAttribute(resid, typedValue, true); return typedValue.data; } public static String customDateFormat(String customDate) { String[] parts = customDate.split("-"); final String year = parts[0]; final String month = parts[1]; final String day = parts[2]; String sMonth; if (Integer.parseInt(month) < 10) { sMonth = "0" + month; } else { sMonth = month; } String sDay; if (Integer.parseInt(day) < 10) { sDay = "0" + day; } else { sDay = day; } return year + "-" + sMonth + "-" + sDay; } public static String customDateCombine(String customDate) { final Calendar c = Calendar.getInstance(); int mHour = c.get(Calendar.HOUR_OF_DAY); int mMinute = c.get(Calendar.MINUTE); int mSeconds = c.get(Calendar.SECOND); String sMin; if ((mMinute) < 10) { sMin = "0" + mMinute; } else { sMin = String.valueOf(mMinute); } String sSec; if ((mSeconds) < 10) { sSec = "0" + mSeconds; } else { sSec = String.valueOf(mSeconds); } return (customDate + "T" + mHour + ":" + sMin + ":" + sSec + "Z"); } public static String encodeBase64(String str) { String base64Str = str; if (!str.isEmpty()) { byte[] data = str.getBytes(StandardCharsets.UTF_8); base64Str = Base64.encodeToString(data, Base64.DEFAULT); } return base64Str; } public static String decodeBase64(String str) { String base64Str = str; if (!str.isEmpty()) { byte[] data = Base64.decode(base64Str, Base64.DEFAULT); base64Str = new String(data, StandardCharsets.UTF_8); } return base64Str; } public static String getLastCharactersOfWord(String str, int count) { return str.substring(str.length() - count); } public static void setMultiVisibility(int visibility, View... views) { for (View view : views) { view.setVisibility(visibility); } } public static int getPixelsFromDensity(Context context, int dp) { return (int) (context.getResources().getDisplayMetrics().density * dp); } public static int getPixelsFromScaledDensity(Context context, int sp) { return (int) (context.getResources().getDisplayMetrics().scaledDensity * sp); } public static long getLineCount(String s) { if (s.isEmpty()) { return 0; } long lines = 1; // we start counting at 1 because there is always at least one line Pattern pattern = Pattern.compile("(\r\n|\n)"); Matcher matcher = pattern.matcher(s); while (matcher.find()) lines++; return lines; } public static void copyToClipboard(Context ctx, CharSequence data, String message) { ClipboardManager clipboard = (ClipboardManager) Objects.requireNonNull(ctx).getSystemService(Context.CLIPBOARD_SERVICE); assert clipboard != null; ClipData clip = ClipData.newPlainText(data, data); clipboard.setPrimaryClip(clip); Toasty.info(ctx, message); } public static boolean switchToAccount(Context context, UserAccount userAccount) { return ((MainApplication) context.getApplicationContext()) .switchToAccount(userAccount, false); } public static void switchToAccount(Context context, UserAccount userAccount, boolean tmp) { ((MainApplication) context.getApplicationContext()).switchToAccount(userAccount, tmp); } public static void sharingIntent(Context ctx, String url) { Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND); sharingIntent.setType("text/plain"); sharingIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, url); sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, url); ctx.startActivity(Intent.createChooser(sharingIntent, url)); } private static Intent wrapBrowserIntent(Context context, Intent intent) { final PackageManager pm = context.getPackageManager(); final List activities = pm.queryIntentActivities( new Intent(intent) .setData( Objects.requireNonNull(intent.getData()) .buildUpon() .authority("example.com") .scheme("https") .build()), PackageManager.MATCH_ALL); final ArrayList chooserIntents = new ArrayList<>(); final String ourPackageName = context.getPackageName(); Collections.sort(activities, new ResolveInfo.DisplayNameComparator(pm)); for (ResolveInfo resInfo : activities) { ActivityInfo info = resInfo.activityInfo; if (!info.enabled || !info.exported) { continue; } if (info.packageName.equals(ourPackageName)) { continue; } Intent targetIntent = new Intent(intent); targetIntent.setPackage(info.packageName); targetIntent.setDataAndType(intent.getData(), intent.getType()); chooserIntents.add(targetIntent); } if (chooserIntents.isEmpty()) { return null; } final Intent lastIntent = chooserIntents.remove(chooserIntents.size() - 1); if (chooserIntents.isEmpty()) { return lastIntent; } Intent chooserIntent = Intent.createChooser(lastIntent, null); String extraName = Intent.EXTRA_ALTERNATE_INTENTS; chooserIntent.putExtra(extraName, chooserIntents.toArray(new Intent[0])); return chooserIntent; } public static void openUrlInBrowser(Context context, String url) { Intent i; if (Boolean.parseBoolean( AppDatabaseSettings.getSettingsValue( context, AppDatabaseSettings.APP_CUSTOM_BROWSER_KEY))) { i = new CustomTabsIntent.Builder() .setDefaultColorSchemeParams( new CustomTabColorSchemeParams.Builder() .setToolbarColor( getColorFromAttribute( context, R.attr.primaryBackgroundColor)) .setNavigationBarColor( getColorFromAttribute( context, R.attr.primaryBackgroundColor)) .setSecondaryToolbarColor(R.attr.primaryTextColor) .build()) .build() .intent; i.setData(Uri.parse(url)); } else { i = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); i.addCategory(Intent.CATEGORY_BROWSABLE); } try { Intent browserIntent = wrapBrowserIntent(context, i); if (browserIntent == null) { Toasty.error(context, context.getString(R.string.genericError)); } context.startActivity(browserIntent); } catch (ActivityNotFoundException e) { Toasty.error(context, context.getString(R.string.browserOpenFailed)); } catch (Exception e) { Toasty.error(context, context.getString(R.string.genericError)); } } public static Uri getUriFromGitUrl(String url) { Uri uri = Uri.parse(url); String host = uri.getHost(); if (host != null && !host.contains(":")) { return uri; } // must be a Git SSH URL now (old rcp standard) return Uri.parse(getUriFromSSHUrl(url)); } public static String getUriFromSSHUrl(String url) { String[] urlParts = url.split("://"); if (urlParts.length > 1) { url = urlParts[1]; } return "https://" + url.replace(":", "/"); } public static Uri changeScheme(Uri origin, String scheme) { String raw = origin.toString(); int schemeIndex = raw.indexOf("://"); if (schemeIndex >= 0) { raw = raw.substring(schemeIndex); } return Uri.parse(scheme + raw); } public static Typeface getTypeface(Context context) { if (typeface == null) { switch (Integer.parseInt( AppDatabaseSettings.getSettingsValue( context, AppDatabaseSettings.APP_FONT_KEY))) { case 0: typeface = Typeface.createFromAsset(context.getAssets(), "fonts/roboto.ttf"); break; case 2: typeface = Typeface.createFromAsset( context.getAssets(), "fonts/sourcecodeproregular.ttf"); break; case 3: typeface = Typeface.DEFAULT; break; default: typeface = Typeface.createFromAsset( context.getAssets(), "fonts/manroperegular.ttf"); break; } } return typeface; } /** Pretty number format Example, 1200 = 1.2k */ public static String numberFormatter(Number number) { char[] suffix = {' ', 'k', 'M', 'B', 'T'}; long numValue = number.longValue(); int value = (int) Math.floor(Math.log10(numValue)); int base = value / 3; if (value >= 3 && base < suffix.length) { return new DecimalFormat("#0.0").format(numValue / Math.pow(10, base * 3)) + suffix[base]; } if (base >= suffix.length) { return new DecimalFormat("#0").format(numValue / Math.pow(10, base * 2)) + suffix[4]; } else { return new DecimalFormat("#,##0").format(numValue); } } /* * check for ghost/restricted users/profiles */ public static Boolean checkGhostUsers(String str) { ArrayList restrictedUsers = new ArrayList<>(); restrictedUsers.add("Ghost"); return restrictedUsers.contains(str); } public int getResponseStatusCode(String u) throws Exception { URL url = new URL(u); HttpURLConnection http = (HttpURLConnection) url.openConnection(); return (http.getResponseCode()); } public enum FileType { IMAGE, AUDIO, VIDEO, DOCUMENT, TEXT, EXECUTABLE, FONT, UNKNOWN } public interface ProgressListener { default void onActionStarted() {} default void onActionFinished() {} void onProgressChanged(short progress); } public static boolean isNightModeThemeDynamic(Context context) { int nightModeFlags = context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; return nightModeFlags == Configuration.UI_MODE_NIGHT_YES; } public static int dynamicColorResource(Context context) { int resource = 0; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { resource = context.getResources().getColor(android.R.color.system_accent1_900, null); } return resource; } }