diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 2a1107f8..d85eac45 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -21,6 +21,7 @@ import com.android.build.api.variant.impl.ApplicationVariantImpl import com.android.build.api.component.analytics.AnalyticsEnabledApplicationVariant import com.android.build.gradle.internal.dsl.BuildType import java.nio.file.Paths +import java.time.Instant plugins { id("org.gradle.idea") @@ -88,6 +89,7 @@ android { "pt", "es", ) + buildConfigField("long", "BUILD_TIME", Instant.now().epochSecond.toString()) } compileOptions { diff --git a/app/src/main/java/org/lsposed/manager/App.java b/app/src/main/java/org/lsposed/manager/App.java index 78e59603..91e09c2f 100644 --- a/app/src/main/java/org/lsposed/manager/App.java +++ b/app/src/main/java/org/lsposed/manager/App.java @@ -30,6 +30,8 @@ import android.util.Log; import androidx.annotation.NonNull; import androidx.preference.PreferenceManager; +import com.google.gson.JsonParser; + import org.lsposed.hiddenapibypass.HiddenApiBypass; import org.lsposed.manager.repo.RepoLoader; import org.lsposed.manager.ui.activity.CrashReportActivity; @@ -37,13 +39,20 @@ import org.lsposed.manager.util.DoHDNS; import org.lsposed.manager.util.theme.ThemeUtil; import java.io.File; +import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneOffset; import java.util.Locale; import okhttp3.Cache; -import okhttp3.MediaType; +import okhttp3.Call; +import okhttp3.Callback; import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; import okhttp3.logging.HttpLoggingInterceptor; import rikka.material.app.DayNightDelegate; @@ -63,9 +72,6 @@ public class App extends Application { private static Cache okHttpCache; private SharedPreferences pref; - public static final MediaType JSON - = MediaType.get("application/json; charset=utf-8"); - public static App getInstance() { return instance; @@ -117,6 +123,7 @@ public class App extends Application { DayNightDelegate.setApplicationContext(this); DayNightDelegate.setDefaultNightMode(ThemeUtil.getDarkTheme()); RepoLoader.getInstance().loadRemoteData(); + loadRemoteVersion(); } @NonNull @@ -144,4 +151,51 @@ public class App extends Application { } return okHttpCache; } + + private void loadRemoteVersion() { + var request = new Request.Builder() + .url("https://api.github.com/repos/LSPosed/LSPosed/releases/latest") + .addHeader("Accept", "application/vnd.github.v3+json") + .build(); + var callback = new Callback() { + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + if (!response.isSuccessful()) return; + var body = response.body(); + if (body == null) return; + try { + var info = JsonParser.parseReader(body.charStream()).getAsJsonObject(); + var published = info.get("published_at").getAsString(); + var time = LocalDateTime.parse(published).toInstant(ZoneOffset.UTC).getEpochSecond(); + var now = Instant.now().getEpochSecond(); + pref.edit().putLong("latest_release", time).putLong("latest_check", now).apply(); + } catch (Throwable t) { + Log.e(App.TAG, t.getMessage(), t); + } + } + + @Override + public void onFailure(@NonNull Call call, @NonNull IOException e) { + Log.e(App.TAG, e.getMessage(), e); + } + }; + getOkHttpClient().newCall(request).enqueue(callback); + } + + public static boolean needUpdate() { + var now = Instant.now(); + var buildTime = Instant.ofEpochSecond(BuildConfig.BUILD_TIME); + var check = getPreferences().getLong("latest_check", 0); + if (check > 0) { + var checkTime = Instant.ofEpochSecond(check); + if (checkTime.atOffset(ZoneOffset.UTC).plusDays(30).toInstant().isBefore(now)) + return true; + var release = getPreferences().getLong("latest_release", 0); + if (release > 0) { + var releaseTime = Instant.ofEpochSecond(release); + return releaseTime.atOffset(ZoneOffset.UTC).minusDays(1).toInstant().isAfter(buildTime); + } + } + return buildTime.atOffset(ZoneOffset.UTC).plusDays(30).toInstant().isBefore(now); + } } diff --git a/app/src/main/java/org/lsposed/manager/repo/RepoLoader.java b/app/src/main/java/org/lsposed/manager/repo/RepoLoader.java index 52bbc760..5fa2d857 100644 --- a/app/src/main/java/org/lsposed/manager/repo/RepoLoader.java +++ b/app/src/main/java/org/lsposed/manager/repo/RepoLoader.java @@ -178,9 +178,11 @@ public class RepoLoader { } public interface Listener { - void repoLoaded(); + default void repoLoaded() { + } - void moduleReleasesLoaded(OnlineModule module); + default void moduleReleasesLoaded(OnlineModule module) { + } void onThrowable(Throwable t); } diff --git a/app/src/main/java/org/lsposed/manager/ui/activity/CrashReportActivity.java b/app/src/main/java/org/lsposed/manager/ui/activity/CrashReportActivity.java index 87be5fea..cb6ac237 100644 --- a/app/src/main/java/org/lsposed/manager/ui/activity/CrashReportActivity.java +++ b/app/src/main/java/org/lsposed/manager/ui/activity/CrashReportActivity.java @@ -23,7 +23,6 @@ package org.lsposed.manager.ui.activity; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Intent; -import android.content.pm.PackageInfo; import android.os.Build; import android.os.Bundle; @@ -51,7 +50,7 @@ public class CrashReportActivity extends AppCompatActivity { binding = ActivityCrashReportBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); binding.copyLogs.setOnClickListener(v -> { - ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); + var clipboard = getSystemService(ClipboardManager.class); //Are there any devices without clipboard...? if (clipboard != null) { ClipData clip = ClipData.newPlainText("LSPManagerCrashInfo", getAllErrorDetailsFromIntent(getIntent())); @@ -66,26 +65,29 @@ public class CrashReportActivity extends AppCompatActivity { Date currentDate = new Date(); DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US); - String versionName = getVersionName(); + String versionName = String.format("%s (%s)", BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE); String errorDetails = ""; errorDetails += "Build version: " + versionName + " \n"; errorDetails += "Current date: " + dateFormat.format(currentDate) + " \n"; - errorDetails += "Device: " + getDeviceModelName() + " \n \n"; + errorDetails += "Device: " + getDeviceModelName() + " \n"; + errorDetails += "Fingerprint: " + getFingerprint() + " \n \n"; errorDetails += "SDK: " + Build.VERSION.SDK_INT + " \n \n"; errorDetails += "Stack trace: \n"; errorDetails += getStackTraceFromIntent(intent); return errorDetails; } - private String getVersionName() { - try { - PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), 0); - return String.format("%s (%s)", packageInfo.versionName, packageInfo.versionCode); - } catch (Exception e) { - return "Unknown"; - } + private String getFingerprint() { + return Build.BRAND + '/' + + Build.PRODUCT + '/' + + Build.DEVICE + ':' + + Build.VERSION.RELEASE + '/' + + Build.ID + '/' + + Build.VERSION.INCREMENTAL + ':' + + Build.TYPE + '/' + + Build.TAGS; } private String getDeviceModelName() { 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 7e344b53..b40aa11d 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 @@ -32,6 +32,7 @@ import androidx.core.text.HtmlCompat; import com.bumptech.glide.Glide; import com.google.android.material.snackbar.Snackbar; +import org.lsposed.manager.App; import org.lsposed.manager.BuildConfig; import org.lsposed.manager.ConfigManager; import org.lsposed.manager.R; @@ -118,6 +119,11 @@ public class HomeFragment extends BaseFragment { cardBackgroundColor = ResourcesKt.resolveColor(activity.getTheme(), R.attr.colorWarning); binding.statusIcon.setImageResource(R.drawable.ic_warning); binding.statusSummary.setText(R.string.system_prop_incorrect_summary); + } else if (App.needUpdate()) { + binding.statusTitle.setText(R.string.need_update); + cardBackgroundColor = ResourcesKt.resolveColor(activity.getTheme(), R.attr.colorWarning); + binding.statusIcon.setImageResource(R.drawable.ic_warning); + binding.statusSummary.setText(R.string.please_update_summary); } else { binding.statusTitle.setText(R.string.activated); cardBackgroundColor = ResourcesKt.resolveColor(activity.getTheme(), R.attr.colorNormal); diff --git a/app/src/main/java/org/lsposed/manager/ui/fragment/RepoFragment.java b/app/src/main/java/org/lsposed/manager/ui/fragment/RepoFragment.java index 06bf7af3..433dd3cc 100644 --- a/app/src/main/java/org/lsposed/manager/ui/fragment/RepoFragment.java +++ b/app/src/main/java/org/lsposed/manager/ui/fragment/RepoFragment.java @@ -147,11 +147,6 @@ public class RepoFragment extends BaseFragment implements RepoLoader.Listener { }); } - @Override - public void moduleReleasesLoaded(OnlineModule module) { - - } - @Override public void onThrowable(Throwable t) { if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) { @@ -202,9 +197,7 @@ public class RepoFragment extends BaseFragment implements RepoLoader.Listener { sb.append(summary); } holder.appDescription.setText(sb); - holder.itemView.setOnClickListener(v -> { - getNavController().navigate(RepoFragmentDirections.actionRepoFragmentToRepoItemFragment(module.getName(), module.getDescription())); - }); + holder.itemView.setOnClickListener(v -> getNavController().navigate(RepoFragmentDirections.actionRepoFragmentToRepoItemFragment(module.getName(), module.getDescription()))); } @Override diff --git a/app/src/main/java/org/lsposed/manager/ui/fragment/RepoItemFragment.java b/app/src/main/java/org/lsposed/manager/ui/fragment/RepoItemFragment.java index d1e1e22b..348704c4 100644 --- a/app/src/main/java/org/lsposed/manager/ui/fragment/RepoItemFragment.java +++ b/app/src/main/java/org/lsposed/manager/ui/fragment/RepoItemFragment.java @@ -31,9 +31,7 @@ import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.webkit.RenderProcessGoneDetail; import android.webkit.WebResourceRequest; -import android.webkit.WebResourceResponse; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; @@ -195,11 +193,6 @@ public class RepoItemFragment extends BaseFragment implements RepoLoader.Listene return super.onOptionsItemSelected(item); } - @Override - public void repoLoaded() { - - } - @Override public void moduleReleasesLoaded(OnlineModule module) { this.module = module; diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 09c492e9..b8ab3e29 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -39,12 +39,14 @@ 已激活 未安装 部分激活 + 需要更新 SEPolicy 未被正确加载 警告:SEPolicy 未被正确加载,作用于系统框架的模块将不起作用。
请将此报告给 Magisk 开发者。]]>
系统框架注入失败 警告:系统注入失败。
这是极罕见的情况,可能是由 Magisk 或低质 Magisk 模块导致。
请尝试禁用除 Riru 和 LSPosed 外的 Magisk 模块,或向开发者提供完整日志。]]>
系统属性异常 警告:系统属性异常。
一些必须的系统属性被删除或被修改。
模块可能会随机失效。]]>
+ 请安装新版 LSPosed API 版本 框架版本 管理器版本 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 09510aa5..c76256e6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -39,12 +39,14 @@ Activated Not installed Partially activated + Need to update SEPolicy is not loaded properly WARNING: SEPolicy is not loaded properly, modules that hook System Framework will not work.
Please report this to Magisk developer.]]>
System Framework injection failed WARNING: System Framework inject failed.
This is rare and may be caused by Magisk or some low-quality Magisk modules.
Please try to disable Magisk modules other than Riru and LSPosed or submit full log to developers.]]>
System prop incorrect WARNING: System prop incorrect.
Some necessary system properties deleted or modified.
Modules may invalidate occasionally.]]>
+ Please install the latest version of LSPosed API version Framework version Manager version