mirror of https://github.com/termux/termux-app
366 lines
18 KiB
Java
366 lines
18 KiB
Java
package com.termux.shared.termux;
|
|
|
|
import android.annotation.SuppressLint;
|
|
import android.content.ComponentName;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.pm.ResolveInfo;
|
|
import android.os.Build;
|
|
|
|
import androidx.annotation.NonNull;
|
|
|
|
import com.google.common.base.Joiner;
|
|
|
|
import com.termux.shared.R;
|
|
import com.termux.shared.logger.Logger;
|
|
import com.termux.shared.markdown.MarkdownUtils;
|
|
import com.termux.shared.packages.PackageUtils;
|
|
|
|
import java.io.BufferedReader;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.InputStreamReader;
|
|
import java.text.SimpleDateFormat;
|
|
import java.util.Date;
|
|
import java.util.List;
|
|
import java.util.Properties;
|
|
import java.util.TimeZone;
|
|
import java.util.regex.Matcher;
|
|
import java.util.regex.Pattern;
|
|
|
|
public class TermuxUtils {
|
|
|
|
/**
|
|
* Get the {@link Context} for {@link TermuxConstants#TERMUX_PACKAGE_NAME} package.
|
|
*
|
|
* @param context The {@link Context} to use to get the {@link Context} of the package.
|
|
* @return Returns the {@link Context}. This will {@code null} if an exception is raised.
|
|
*/
|
|
public static Context getTermuxPackageContext(@NonNull Context context) {
|
|
return PackageUtils.getContextForPackage(context, TermuxConstants.TERMUX_PACKAGE_NAME);
|
|
}
|
|
|
|
/**
|
|
* Get the {@link Context} for {@link TermuxConstants#TERMUX_API_PACKAGE_NAME} package.
|
|
*
|
|
* @param context The {@link Context} to use to get the {@link Context} of the package.
|
|
* @return Returns the {@link Context}. This will {@code null} if an exception is raised.
|
|
*/
|
|
public static Context getTermuxAPIPackageContext(@NonNull Context context) {
|
|
return PackageUtils.getContextForPackage(context, TermuxConstants.TERMUX_API_PACKAGE_NAME);
|
|
}
|
|
|
|
/**
|
|
* Get the {@link Context} for {@link TermuxConstants#TERMUX_BOOT_PACKAGE_NAME} package.
|
|
*
|
|
* @param context The {@link Context} to use to get the {@link Context} of the package.
|
|
* @return Returns the {@link Context}. This will {@code null} if an exception is raised.
|
|
*/
|
|
public static Context getTermuxBootPackageContext(@NonNull Context context) {
|
|
return PackageUtils.getContextForPackage(context, TermuxConstants.TERMUX_BOOT_PACKAGE_NAME);
|
|
}
|
|
|
|
/**
|
|
* Get the {@link Context} for {@link TermuxConstants#TERMUX_FLOAT_PACKAGE_NAME} package.
|
|
*
|
|
* @param context The {@link Context} to use to get the {@link Context} of the package.
|
|
* @return Returns the {@link Context}. This will {@code null} if an exception is raised.
|
|
*/
|
|
public static Context getTermuxFloatPackageContext(@NonNull Context context) {
|
|
return PackageUtils.getContextForPackage(context, TermuxConstants.TERMUX_FLOAT_PACKAGE_NAME);
|
|
}
|
|
|
|
/**
|
|
* Get the {@link Context} for {@link TermuxConstants#TERMUX_STYLING_PACKAGE_NAME} package.
|
|
*
|
|
* @param context The {@link Context} to use to get the {@link Context} of the package.
|
|
* @return Returns the {@link Context}. This will {@code null} if an exception is raised.
|
|
*/
|
|
public static Context getTermuxStylingPackageContext(@NonNull Context context) {
|
|
return PackageUtils.getContextForPackage(context, TermuxConstants.TERMUX_STYLING_PACKAGE_NAME);
|
|
}
|
|
|
|
/**
|
|
* Get the {@link Context} for {@link TermuxConstants#TERMUX_TASKER_PACKAGE_NAME} package.
|
|
*
|
|
* @param context The {@link Context} to use to get the {@link Context} of the package.
|
|
* @return Returns the {@link Context}. This will {@code null} if an exception is raised.
|
|
*/
|
|
public static Context getTermuxTaskerPackageContext(@NonNull Context context) {
|
|
return PackageUtils.getContextForPackage(context, TermuxConstants.TERMUX_TASKER_PACKAGE_NAME);
|
|
}
|
|
|
|
/**
|
|
* Get the {@link Context} for {@link TermuxConstants#TERMUX_WIDGET_PACKAGE_NAME} package.
|
|
*
|
|
* @param context The {@link Context} to use to get the {@link Context} of the package.
|
|
* @return Returns the {@link Context}. This will {@code null} if an exception is raised.
|
|
*/
|
|
public static Context getTermuxWidgetPackageContext(@NonNull Context context) {
|
|
return PackageUtils.getContextForPackage(context, TermuxConstants.TERMUX_WIDGET_PACKAGE_NAME);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Send the {@link TermuxConstants#BROADCAST_TERMUX_OPENED} broadcast to notify apps that Termux
|
|
* app has been opened.
|
|
*
|
|
* @param context The Context to send the broadcast.
|
|
*/
|
|
public static void sendTermuxOpenedBroadcast(@NonNull Context context) {
|
|
if (context == null) return;
|
|
|
|
Intent broadcast = new Intent(TermuxConstants.BROADCAST_TERMUX_OPENED);
|
|
List<ResolveInfo> matches = context.getPackageManager().queryBroadcastReceivers(broadcast, 0);
|
|
|
|
// send broadcast to registered Termux receivers
|
|
// this technique is needed to work around broadcast changes that Oreo introduced
|
|
for (ResolveInfo info : matches) {
|
|
Intent explicitBroadcast = new Intent(broadcast);
|
|
ComponentName cname = new ComponentName(info.activityInfo.applicationInfo.packageName,
|
|
info.activityInfo.name);
|
|
explicitBroadcast.setComponent(cname);
|
|
context.sendBroadcast(explicitBroadcast);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get a markdown {@link String} for the app info. If the {@code context} passed is different
|
|
* from the {@link TermuxConstants#TERMUX_PACKAGE_NAME} package context, then this function
|
|
* must have been called by a different package like a plugin, so we return info for both packages
|
|
* if {@code returnTermuxPackageInfoToo} is {@code true}.
|
|
*
|
|
* @param currentPackageContext The context of current package.
|
|
* @param returnTermuxPackageInfoToo If set to {@code true}, then will return info of the
|
|
* {@link TermuxConstants#TERMUX_PACKAGE_NAME} package as well if its different from current package.
|
|
* @return Returns the markdown {@link String}.
|
|
*/
|
|
public static String getAppInfoMarkdownString(@NonNull final Context currentPackageContext, final boolean returnTermuxPackageInfoToo) {
|
|
if (currentPackageContext == null) return "null";
|
|
|
|
StringBuilder markdownString = new StringBuilder();
|
|
|
|
Context termuxPackageContext = getTermuxPackageContext(currentPackageContext);
|
|
|
|
String termuxPackageName = null;
|
|
String termuxAppName = null;
|
|
if(termuxPackageContext != null) {
|
|
termuxPackageName = PackageUtils.getPackageNameForPackage(termuxPackageContext);
|
|
termuxAppName = PackageUtils.getAppNameForPackage(termuxPackageContext);
|
|
}
|
|
|
|
String currentPackageName = PackageUtils.getPackageNameForPackage(currentPackageContext);
|
|
String currentAppName = PackageUtils.getAppNameForPackage(currentPackageContext);
|
|
|
|
boolean isTermuxPackage = (termuxPackageName != null && termuxPackageName.equals(currentPackageName));
|
|
|
|
|
|
if(returnTermuxPackageInfoToo && !isTermuxPackage)
|
|
markdownString.append("## ").append(currentAppName).append(" App Info (Current)\n");
|
|
else
|
|
markdownString.append("## ").append(currentAppName).append(" App Info\n");
|
|
markdownString.append(getAppInfoMarkdownStringInner(currentPackageContext));
|
|
|
|
if(returnTermuxPackageInfoToo && !isTermuxPackage) {
|
|
markdownString.append("\n\n## ").append(termuxAppName).append(" App Info\n");
|
|
markdownString.append(getAppInfoMarkdownStringInner(termuxPackageContext));
|
|
}
|
|
|
|
markdownString.append("\n##\n");
|
|
|
|
return markdownString.toString();
|
|
}
|
|
|
|
/**
|
|
* Get a markdown {@link String} for the app info for the package associated with the {@code context}.
|
|
*
|
|
* @param context The context for operations for the package.
|
|
* @return Returns the markdown {@link String}.
|
|
*/
|
|
public static String getAppInfoMarkdownStringInner(@NonNull final Context context) {
|
|
StringBuilder markdownString = new StringBuilder();
|
|
|
|
appendPropertyToMarkdown(markdownString,"APP_NAME", PackageUtils.getAppNameForPackage(context));
|
|
appendPropertyToMarkdown(markdownString,"PACKAGE_NAME", PackageUtils.getPackageNameForPackage(context));
|
|
appendPropertyToMarkdown(markdownString,"VERSION_NAME", PackageUtils.getVersionNameForPackage(context));
|
|
appendPropertyToMarkdown(markdownString,"VERSION_CODE", PackageUtils.getVersionCodeForPackage(context));
|
|
appendPropertyToMarkdown(markdownString,"TARGET_SDK", PackageUtils.getTargetSDKForPackage(context));
|
|
appendPropertyToMarkdown(markdownString,"IS_DEBUG_BUILD", PackageUtils.isAppForPackageADebugBuild(context));
|
|
|
|
return markdownString.toString();
|
|
}
|
|
|
|
/**
|
|
* Get a markdown {@link String} for the device info.
|
|
*
|
|
* @param context The context for operations.
|
|
* @return Returns the markdown {@link String}.
|
|
*/
|
|
public static String getDeviceInfoMarkdownString(@NonNull final Context context) {
|
|
if (context == null) return "null";
|
|
|
|
// Some properties cannot be read with {@link System#getProperty(String)} but can be read
|
|
// directly by running getprop command
|
|
Properties systemProperties = getSystemProperties();
|
|
|
|
StringBuilder markdownString = new StringBuilder();
|
|
|
|
markdownString.append("## Device Info");
|
|
|
|
markdownString.append("\n\n### Software\n");
|
|
appendPropertyToMarkdown(markdownString,"OS_VERSION", getSystemPropertyWithAndroidAPI("os.version"));
|
|
appendPropertyToMarkdown(markdownString, "SDK_INT", Build.VERSION.SDK_INT);
|
|
// If its a release version
|
|
if ("REL".equals(Build.VERSION.CODENAME))
|
|
appendPropertyToMarkdown(markdownString, "RELEASE", Build.VERSION.RELEASE);
|
|
else
|
|
appendPropertyToMarkdown(markdownString, "CODENAME", Build.VERSION.CODENAME);
|
|
appendPropertyToMarkdown(markdownString, "INCREMENTAL", Build.VERSION.INCREMENTAL);
|
|
appendPropertyToMarkdownIfSet(markdownString, "SECURITY_PATCH", systemProperties.getProperty("ro.build.version.security_patch"));
|
|
appendPropertyToMarkdownIfSet(markdownString, "IS_DEBUGGABLE", systemProperties.getProperty("ro.debuggable"));
|
|
appendPropertyToMarkdownIfSet(markdownString, "IS_EMULATOR", systemProperties.getProperty("ro.boot.qemu"));
|
|
appendPropertyToMarkdownIfSet(markdownString, "IS_TREBLE_ENABLED", systemProperties.getProperty("ro.treble.enabled"));
|
|
appendPropertyToMarkdown(markdownString, "TYPE", Build.TYPE);
|
|
appendPropertyToMarkdown(markdownString, "TAGS", Build.TAGS);
|
|
|
|
markdownString.append("\n\n### Hardware\n");
|
|
appendPropertyToMarkdown(markdownString, "MANUFACTURER", Build.MANUFACTURER);
|
|
appendPropertyToMarkdown(markdownString, "BRAND", Build.BRAND);
|
|
appendPropertyToMarkdown(markdownString, "MODEL", Build.MODEL);
|
|
appendPropertyToMarkdown(markdownString, "PRODUCT", Build.PRODUCT);
|
|
appendPropertyToMarkdown(markdownString, "DISPLAY", Build.DISPLAY);
|
|
appendPropertyToMarkdown(markdownString, "ID", Build.ID);
|
|
appendPropertyToMarkdown(markdownString, "BOARD", Build.BOARD);
|
|
appendPropertyToMarkdown(markdownString, "HARDWARE", Build.HARDWARE);
|
|
appendPropertyToMarkdown(markdownString, "DEVICE", Build.DEVICE);
|
|
appendPropertyToMarkdown(markdownString, "SUPPORTED_ABIS", Joiner.on(", ").skipNulls().join(Build.SUPPORTED_ABIS));
|
|
|
|
markdownString.append("\n##\n");
|
|
|
|
return markdownString.toString();
|
|
}
|
|
|
|
/**
|
|
* Get a markdown {@link String} for reporting an issue.
|
|
*
|
|
* @param context The context for operations.
|
|
* @return Returns the markdown {@link String}.
|
|
*/
|
|
public static String getReportIssueMarkdownString(@NonNull final Context context) {
|
|
if (context == null) return "null";
|
|
|
|
StringBuilder markdownString = new StringBuilder();
|
|
|
|
markdownString.append("## Where To Report An Issue");
|
|
|
|
markdownString.append("\n\n").append(context.getString(R.string.msg_report_issue)).append("\n");
|
|
|
|
//markdownString.append("\n\n### Email\n");
|
|
//markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_SUPPORT_EMAIL, TermuxConstants.TERMUX_SUPPORT_EMAIL_MAILTO_URL)).append(" ");
|
|
|
|
markdownString.append("\n\n### Reddit\n");
|
|
markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_REDDIT_SUBREDDIT, TermuxConstants.TERMUX_REDDIT_SUBREDDIT_URL)).append(" ");
|
|
|
|
markdownString.append("\n\n### Github Issues for Termux apps\n");
|
|
markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_APP_NAME, TermuxConstants.TERMUX_GITHUB_ISSUES_REPO_URL)).append(" ");
|
|
markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_API_APP_NAME, TermuxConstants.TERMUX_API_GITHUB_ISSUES_REPO_URL)).append(" ");
|
|
markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_BOOT_APP_NAME, TermuxConstants.TERMUX_BOOT_GITHUB_ISSUES_REPO_URL)).append(" ");
|
|
markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_FLOAT_APP_NAME, TermuxConstants.TERMUX_FLOAT_GITHUB_ISSUES_REPO_URL)).append(" ");
|
|
markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_STYLING_APP_NAME, TermuxConstants.TERMUX_STYLING_GITHUB_ISSUES_REPO_URL)).append(" ");
|
|
markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_TASKER_APP_NAME, TermuxConstants.TERMUX_TASKER_GITHUB_ISSUES_REPO_URL)).append(" ");
|
|
markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_WIDGET_APP_NAME, TermuxConstants.TERMUX_WIDGET_GITHUB_ISSUES_REPO_URL)).append(" ");
|
|
|
|
markdownString.append("\n\n### Github Issues for Termux packages\n");
|
|
markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_PACKAGES_GITHUB_REPO_NAME, TermuxConstants.TERMUX_PACKAGES_GITHUB_ISSUES_REPO_URL)).append(" ");
|
|
markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_GAME_PACKAGES_GITHUB_REPO_NAME, TermuxConstants.TERMUX_GAME_PACKAGES_GITHUB_ISSUES_REPO_URL)).append(" ");
|
|
markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_SCIENCE_PACKAGES_GITHUB_REPO_NAME, TermuxConstants.TERMUX_SCIENCE_PACKAGES_GITHUB_ISSUES_REPO_URL)).append(" ");
|
|
markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_ROOT_PACKAGES_GITHUB_REPO_NAME, TermuxConstants.TERMUX_ROOT_PACKAGES_GITHUB_ISSUES_REPO_URL)).append(" ");
|
|
markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_UNSTABLE_PACKAGES_GITHUB_REPO_NAME, TermuxConstants.TERMUX_UNSTABLE_PACKAGES_GITHUB_ISSUES_REPO_URL)).append(" ");
|
|
markdownString.append("\n").append(MarkdownUtils.getLinkMarkdownString(TermuxConstants.TERMUX_X11_PACKAGES_GITHUB_REPO_NAME, TermuxConstants.TERMUX_X11_PACKAGES_GITHUB_ISSUES_REPO_URL)).append(" ");
|
|
|
|
markdownString.append("\n##\n");
|
|
|
|
return markdownString.toString();
|
|
}
|
|
|
|
public static Properties getSystemProperties() {
|
|
Properties systemProperties = new Properties();
|
|
|
|
// getprop commands returns values in the format `[key]: [value]`
|
|
// Regex matches string starting with a literal `[`,
|
|
// followed by one or more characters that do not match a closing square bracket as the key,
|
|
// followed by a literal `]: [`,
|
|
// followed by one or more characters as the value,
|
|
// followed by string ending with literal `]`
|
|
// multiline values will be ignored
|
|
Pattern propertiesPattern = Pattern.compile("^\\[([^]]+)]: \\[(.+)]$");
|
|
|
|
try {
|
|
Process process = new ProcessBuilder()
|
|
.command("/system/bin/getprop")
|
|
.redirectErrorStream(true)
|
|
.start();
|
|
|
|
InputStream inputStream = process.getInputStream();
|
|
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
|
|
String line, key, value;
|
|
|
|
while ((line = bufferedReader.readLine()) != null) {
|
|
Matcher matcher = propertiesPattern.matcher(line);
|
|
if (matcher.matches()) {
|
|
key = matcher.group(1);
|
|
value = matcher.group(2);
|
|
if(key != null && value != null && !key.isEmpty() && !value.isEmpty())
|
|
systemProperties.put(key, value);
|
|
}
|
|
}
|
|
|
|
bufferedReader.close();
|
|
process.destroy();
|
|
|
|
} catch (IOException e) {
|
|
Logger.logStackTraceWithMessage("Failed to get run \"/system/bin/getprop\" to get system properties.", e);
|
|
}
|
|
|
|
//for (String key : systemProperties.stringPropertyNames()) {
|
|
// Logger.logVerbose(key + ": " + systemProperties.get(key));
|
|
//}
|
|
|
|
return systemProperties;
|
|
}
|
|
|
|
private static String getSystemPropertyWithAndroidAPI(@NonNull String property) {
|
|
try {
|
|
return System.getProperty(property);
|
|
} catch (Exception e) {
|
|
Logger.logVerbose("Failed to get system property \"" + property + "\":" + e.getMessage());
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private static void appendPropertyToMarkdownIfSet(StringBuilder markdownString, String label, Object value) {
|
|
if(value == null) return;
|
|
if(value instanceof String && (((String) value).isEmpty()) || "REL".equals(value)) return;
|
|
markdownString.append("\n").append(getPropertyMarkdown(label, value));
|
|
}
|
|
|
|
private static void appendPropertyToMarkdown(StringBuilder markdownString, String label, Object value) {
|
|
markdownString.append("\n").append(getPropertyMarkdown(label, value));
|
|
}
|
|
|
|
private static String getPropertyMarkdown(String label, Object value) {
|
|
return MarkdownUtils.getSingleLineMarkdownStringEntry(label, value, "-");
|
|
}
|
|
|
|
|
|
|
|
public static String getCurrentTimeStamp() {
|
|
@SuppressLint("SimpleDateFormat")
|
|
final SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
|
|
df.setTimeZone(TimeZone.getTimeZone("UTC"));
|
|
return df.format(new Date());
|
|
}
|
|
|
|
}
|