diff --git a/app/build.gradle.kts b/app/build.gradle.kts index d8436a69..143dd270 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -218,6 +218,7 @@ dependencies { val appCenter = "4.4.2" debugImplementation("com.microsoft.appcenter:appcenter-crashes:${appCenter}") + debugImplementation("com.microsoft.appcenter:appcenter-analytics:${appCenter}") } configurations.all { diff --git a/app/src/debug/java/org/lsposed/manager/util/Telemetry.java b/app/src/debug/java/org/lsposed/manager/util/Telemetry.java index 40c9427b..ebb091b5 100644 --- a/app/src/debug/java/org/lsposed/manager/util/Telemetry.java +++ b/app/src/debug/java/org/lsposed/manager/util/Telemetry.java @@ -2,52 +2,66 @@ package org.lsposed.manager.util; import android.app.Application; import android.os.Handler; -import android.util.Log; import androidx.annotation.NonNull; import com.microsoft.appcenter.AppCenter; +import com.microsoft.appcenter.analytics.Analytics; import com.microsoft.appcenter.channel.AbstractChannelListener; import com.microsoft.appcenter.channel.Channel; import com.microsoft.appcenter.crashes.Crashes; +import com.microsoft.appcenter.ingestion.models.Log; import org.lsposed.manager.App; import org.lsposed.manager.BuildConfig; +import java.util.Map; + public class Telemetry { - static Channel.Listener patchDeviceListener = new AbstractChannelListener() { + private static final Channel.Listener patchDeviceListener = new AbstractChannelListener() { @Override - public void onPreparedLog(@NonNull com.microsoft.appcenter.ingestion.models.Log log, @NonNull String groupName, int flags) { - var d = log.getDevice(); - d.setAppVersion(BuildConfig.VERSION_NAME); - d.setAppBuild(String.valueOf(BuildConfig.VERSION_CODE)); - d.setAppNamespace(BuildConfig.APPLICATION_ID); - log.setDevice(d); + public void onPreparedLog(@NonNull Log log, @NonNull String groupName, int flags) { + var device = log.getDevice(); + device.setAppVersion(BuildConfig.VERSION_NAME); + device.setAppBuild(String.valueOf(BuildConfig.VERSION_CODE)); } }; - static void addPatchDeviceListener() { + private static void addPatchDeviceListener() { try { - var channel = AppCenter.class.getDeclaredField("mChannel"); - channel.setAccessible(true); - ((Channel) channel.get(AppCenter.getInstance())).addListener(patchDeviceListener); - } catch (Throwable e) { - Log.e(App.TAG, "add listener", e); + var channelField = AppCenter.class.getDeclaredField("mChannel"); + channelField.setAccessible(true); + var channel = (Channel) channelField.get(AppCenter.getInstance()); + assert channel != null; + channel.addListener(patchDeviceListener); + } catch (ReflectiveOperationException e) { + android.util.Log.e(App.TAG, "add listener", e); } } - static void patchDevice() throws Throwable { - var handle = AppCenter.class.getDeclaredField("mHandler"); - handle.setAccessible(true); - ((Handler) handle.get(AppCenter.getInstance())).post(Telemetry::addPatchDeviceListener); + private static void patchDevice() { + try { + var handlerField = AppCenter.class.getDeclaredField("mHandler"); + handlerField.setAccessible(true); + var handler = ((Handler) handlerField.get(AppCenter.getInstance())); + assert handler != null; + handler.post(Telemetry::addPatchDeviceListener); + } catch (ReflectiveOperationException e) { + android.util.Log.e(App.TAG, "patch device", e); + } } public static void start(Application app) { - AppCenter.start(app, "eb3c4175-e879-4312-a72e-b0e64bca142c", Crashes.class); - try { - patchDevice(); - } catch (Throwable e) { - Log.w(App.TAG, "patch device", e); - } + AppCenter.start(app, "eb3c4175-e879-4312-a72e-b0e64bca142c", + Analytics.class, Crashes.class); + patchDevice(); + } + + public static void trackEvent(String name, Map properties) { + Analytics.trackEvent(name, properties); + } + + public static void trackError(Throwable throwable, Map properties) { + Crashes.trackError(throwable, properties, null); } } diff --git a/app/src/main/java/org/lsposed/manager/App.java b/app/src/main/java/org/lsposed/manager/App.java index c6b071a6..0f89ee75 100644 --- a/app/src/main/java/org/lsposed/manager/App.java +++ b/app/src/main/java/org/lsposed/manager/App.java @@ -21,6 +21,7 @@ package org.lsposed.manager; import android.annotation.SuppressLint; +import android.app.ActivityManager; import android.app.Application; import android.content.BroadcastReceiver; import android.content.Context; @@ -53,6 +54,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.nio.charset.StandardCharsets; +import java.util.HashMap; import java.util.Locale; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -147,8 +149,22 @@ public class App extends Application { @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); - if (BuildConfig.DEBUG) { - Telemetry.start(this); + Telemetry.start(this); + var map = new HashMap(1); + map.put("isParasitic", String.valueOf(isParasitic())); + Telemetry.trackEvent("App start", map); + var am = getSystemService(ActivityManager.class); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + map.clear(); + var reasons = am.getHistoricalProcessExitReasons(null, 0, 1); + if (reasons.size() == 1) { + map.put("description", reasons.get(0).getDescription()); + map.put("importance", String.valueOf(reasons.get(0).getImportance())); + map.put("process", reasons.get(0).getProcessName()); + map.put("reason", String.valueOf(reasons.get(0).getReason())); + map.put("status", String.valueOf(reasons.get(0).getStatus())); + Telemetry.trackEvent("Last exit reasons", map); + } } } diff --git a/app/src/main/java/org/lsposed/manager/ui/activity/base/BaseActivity.java b/app/src/main/java/org/lsposed/manager/ui/activity/base/BaseActivity.java index 65dcf734..955df787 100644 --- a/app/src/main/java/org/lsposed/manager/ui/activity/base/BaseActivity.java +++ b/app/src/main/java/org/lsposed/manager/ui/activity/base/BaseActivity.java @@ -40,6 +40,7 @@ import org.lsposed.manager.R; import org.lsposed.manager.ui.dialog.BlurBehindDialogBuilder; import org.lsposed.manager.ui.dialog.FlashDialogBuilder; import org.lsposed.manager.util.NavUtil; +import org.lsposed.manager.util.Telemetry; import org.lsposed.manager.util.ThemeUtil; import org.lsposed.manager.util.UpdateUtil; @@ -94,6 +95,12 @@ public class BaseActivity extends MaterialActivity { setTaskDescription(new ActivityManager.TaskDescription(getTitle().toString(), icon)); } + @Override + protected void onStop() { + super.onStop(); + Telemetry.trackEvent("BaseActivity stop", null); + } + @Override public void onApplyUserThemeResource(@NonNull Resources.Theme theme, boolean isDecorView) { theme.applyStyle(ThemeUtil.getNightThemeStyleRes(this), true); diff --git a/app/src/main/java/org/lsposed/manager/ui/fragment/HomeFragment.java b/app/src/main/java/org/lsposed/manager/ui/fragment/HomeFragment.java index af8e539c..b01a3e29 100644 --- a/app/src/main/java/org/lsposed/manager/ui/fragment/HomeFragment.java +++ b/app/src/main/java/org/lsposed/manager/ui/fragment/HomeFragment.java @@ -43,9 +43,13 @@ import org.lsposed.manager.ui.dialog.BlurBehindDialogBuilder; import org.lsposed.manager.ui.dialog.FlashDialogBuilder; import org.lsposed.manager.ui.dialog.ShortcutDialog; import org.lsposed.manager.util.NavUtil; +import org.lsposed.manager.util.Telemetry; import org.lsposed.manager.util.UpdateUtil; import org.lsposed.manager.util.chrome.LinkTransformationMethod; +import java.util.Arrays; +import java.util.HashMap; + import rikka.core.util.ClipboardUtils; import rikka.material.app.LocaleDelegate; @@ -194,6 +198,12 @@ public class HomeFragment extends BaseFragment { activity.getString(R.string.info_system_abi) + "\n" + binding.systemAbi.getText(); + var map = new HashMap(); + map.put("apiVersion", binding.apiVersion.getText().toString()); + map.put("api", binding.api.getText().toString()); + map.put("frameworkVersion", binding.frameworkVersion.getText().toString()); + map.put("systemAbi", Arrays.toString(Build.SUPPORTED_ABIS)); + Telemetry.trackEvent("HomeFragment", map); binding.copyInfo.setOnClickListener(v -> { ClipboardUtils.put(activity, info); showHint(R.string.info_copied, false); diff --git a/app/src/main/java/org/lsposed/manager/ui/fragment/LogsFragment.java b/app/src/main/java/org/lsposed/manager/ui/fragment/LogsFragment.java index fbbbb611..17485979 100644 --- a/app/src/main/java/org/lsposed/manager/ui/fragment/LogsFragment.java +++ b/app/src/main/java/org/lsposed/manager/ui/fragment/LogsFragment.java @@ -83,7 +83,7 @@ public class LogsFragment extends BaseFragment { private OptionsItemSelectListener optionsItemSelectListener; private final ActivityResultLauncher saveLogsLauncher = registerForActivityResult( - new ActivityResultContracts.CreateDocument(), + new ActivityResultContracts.CreateDocument("application/zip"), uri -> { if (uri == null) return; runAsync(() -> { diff --git a/app/src/release/java/org/lsposed/manager/util/Telemetry.java b/app/src/release/java/org/lsposed/manager/util/Telemetry.java index 4d9574db..4054b646 100644 --- a/app/src/release/java/org/lsposed/manager/util/Telemetry.java +++ b/app/src/release/java/org/lsposed/manager/util/Telemetry.java @@ -2,6 +2,15 @@ package org.lsposed.manager.util; import android.app.Application; +import java.util.Map; + public class Telemetry { - public static void start(Application application) {} + public static void start(Application application) { + } + + public static void trackEvent(String name, Map properties) { + } + + public static void trackError(Throwable throwable, Map properties) { + } }