|
1 | 1 | package com.wmods.wppenhacer; |
2 | 2 |
|
3 | 3 | import android.app.Activity; |
| 4 | +import android.util.Log; |
4 | 5 |
|
5 | 6 | import com.wmods.wppenhacer.xposed.core.WppCore; |
6 | 7 | import com.wmods.wppenhacer.xposed.core.components.AlertDialogWpp; |
7 | 8 | import com.wmods.wppenhacer.xposed.utils.Utils; |
8 | 9 |
|
9 | 10 | import org.json.JSONObject; |
10 | 11 |
|
| 12 | +import java.text.SimpleDateFormat; |
| 13 | +import java.util.Date; |
| 14 | +import java.util.Locale; |
11 | 15 | import java.util.Objects; |
| 16 | +import java.util.concurrent.TimeUnit; |
12 | 17 |
|
13 | 18 | import de.robv.android.xposed.XposedBridge; |
14 | 19 | import okhttp3.OkHttpClient; |
15 | 20 |
|
| 21 | +import io.noties.markwon.Markwon; |
| 22 | + |
16 | 23 | public class UpdateChecker implements Runnable { |
17 | 24 |
|
| 25 | + private static final String TAG = "WAE_UpdateChecker"; |
18 | 26 | private static final String LATEST_RELEASE_API = "https://api.github.com/repos/Dev4Mod/WaEnhancer/releases/latest"; |
19 | 27 | private static final String RELEASE_TAG_PREFIX = "debug-"; |
20 | 28 | private static final String TELEGRAM_UPDATE_URL = "https://t.me/waenhancher"; |
21 | 29 |
|
| 30 | + // Singleton OkHttpClient - expensive to create, reuse across all checks |
| 31 | + private static OkHttpClient httpClient; |
| 32 | + |
22 | 33 | private final Activity mActivity; |
23 | 34 |
|
24 | 35 | public UpdateChecker(Activity activity) { |
25 | 36 | this.mActivity = activity; |
26 | 37 | } |
27 | 38 |
|
| 39 | + /** |
| 40 | + * Get or create the singleton OkHttpClient with proper timeout configuration |
| 41 | + */ |
| 42 | + private static synchronized OkHttpClient getHttpClient() { |
| 43 | + if (httpClient == null) { |
| 44 | + httpClient = new OkHttpClient.Builder() |
| 45 | + .connectTimeout(10, TimeUnit.SECONDS) |
| 46 | + .readTimeout(10, TimeUnit.SECONDS) |
| 47 | + .writeTimeout(10, TimeUnit.SECONDS) |
| 48 | + .build(); |
| 49 | + } |
| 50 | + return httpClient; |
| 51 | + } |
28 | 52 |
|
29 | 53 | @Override |
30 | 54 | public void run() { |
| 55 | + XposedBridge.log("[" + TAG + "] UpdateChecker.run() started"); |
31 | 56 | try { |
32 | | - var client = new OkHttpClient(); |
| 57 | + XposedBridge.log("[" + TAG + "] Starting update check..."); |
| 58 | + |
33 | 59 | var request = new okhttp3.Request.Builder() |
34 | 60 | .url(LATEST_RELEASE_API) |
35 | 61 | .build(); |
| 62 | + |
36 | 63 | String hash; |
37 | 64 | String changelog; |
38 | | - try (var response = client.newCall(request).execute()) { |
39 | | - if (!response.isSuccessful()) return; |
| 65 | + String publishedAt; |
| 66 | + |
| 67 | + try (var response = getHttpClient().newCall(request).execute()) { |
| 68 | + if (!response.isSuccessful()) { |
| 69 | + XposedBridge.log("[" + TAG + "] Update check failed: HTTP " + response.code()); |
| 70 | + return; |
| 71 | + } |
| 72 | + |
40 | 73 | var body = response.body(); |
| 74 | + if (body == null) { |
| 75 | + XposedBridge.log("[" + TAG + "] Update check failed: Empty response body"); |
| 76 | + return; |
| 77 | + } |
| 78 | + |
41 | 79 | var content = body.string(); |
42 | 80 | var release = new JSONObject(content); |
43 | 81 | var tagName = release.optString("tag_name", ""); |
44 | | - XposedBridge.log("[UPDATE]" +tagName); |
45 | | - if (tagName.isBlank() || !tagName.startsWith(RELEASE_TAG_PREFIX)) return; |
| 82 | + |
| 83 | + XposedBridge.log("[" + TAG + "] Latest release tag: " + tagName); |
| 84 | + |
| 85 | + if (tagName.isBlank() || !tagName.startsWith(RELEASE_TAG_PREFIX)) { |
| 86 | + XposedBridge.log("[" + TAG + "] Invalid or non-debug release tag"); |
| 87 | + return; |
| 88 | + } |
| 89 | + |
46 | 90 | hash = tagName.substring(RELEASE_TAG_PREFIX.length()).trim(); |
47 | 91 | changelog = release.optString("body", "No changelog available.").trim(); |
| 92 | + publishedAt = release.optString("published_at", ""); |
| 93 | + |
| 94 | + XposedBridge.log("[" + TAG + "] Release hash: " + hash + ", published: " + publishedAt); |
48 | 95 | } |
49 | 96 |
|
50 | | - if (hash.isBlank()) return; |
| 97 | + if (hash.isBlank()) { |
| 98 | + XposedBridge.log("[" + TAG + "] Empty hash, skipping"); |
| 99 | + return; |
| 100 | + } |
| 101 | + |
51 | 102 | var appInfo = mActivity.getPackageManager().getPackageInfo(BuildConfig.APPLICATION_ID, 0); |
52 | | - if (!appInfo.versionName.toLowerCase().contains(hash.toLowerCase().trim()) && !Objects.equals(WppCore.getPrivString("ignored_version", ""), hash)) { |
| 103 | + boolean isNewVersion = !appInfo.versionName.toLowerCase().contains(hash.toLowerCase().trim()); |
| 104 | + boolean isIgnored = Objects.equals(WppCore.getPrivString("ignored_version", ""), hash); |
| 105 | + |
| 106 | + if (isNewVersion && !isIgnored) { |
| 107 | + XposedBridge.log("[" + TAG + "] New version available, showing dialog"); |
| 108 | + |
| 109 | + final String finalHash = hash; |
| 110 | + final String finalChangelog = changelog; |
| 111 | + final String finalPublishedAt = publishedAt; |
| 112 | + |
53 | 113 | mActivity.runOnUiThread(() -> { |
54 | | - var dialog = new AlertDialogWpp(mActivity); |
55 | | - dialog.setTitle("WAE - New version available!"); |
56 | | - dialog.setMessage("Changelog:\n\n" + changelog); |
57 | | - dialog.setNegativeButton("Ignore", (dialog1, which) -> { |
58 | | - WppCore.setPrivString("ignored_version", hash); |
59 | | - dialog1.dismiss(); |
60 | | - }); |
61 | | - dialog.setPositiveButton("Update", (dialog1, which) -> { |
62 | | - Utils.openLink(mActivity, TELEGRAM_UPDATE_URL); |
63 | | - dialog1.dismiss(); |
64 | | - }); |
65 | | - dialog.show(); |
| 114 | + showUpdateDialog(finalHash, finalChangelog, finalPublishedAt); |
66 | 115 | }); |
| 116 | + } else { |
| 117 | + XposedBridge.log("[" + TAG + "] No update needed (isNew=" + isNewVersion + ", isIgnored=" + isIgnored + ")"); |
| 118 | + } |
| 119 | + } catch (java.net.SocketTimeoutException e) { |
| 120 | + XposedBridge.log("[" + TAG + "] Update check timeout: " + e.getMessage()); |
| 121 | + } catch (java.io.IOException e) { |
| 122 | + XposedBridge.log("[" + TAG + "] Network error during update check: " + e.getMessage()); |
| 123 | + } catch (Exception e) { |
| 124 | + XposedBridge.log("[" + TAG + "] Unexpected error during update check: " + e.getMessage()); |
| 125 | + XposedBridge.log(e); |
| 126 | + } |
| 127 | + } |
| 128 | + |
| 129 | + private void showUpdateDialog(String hash, String changelog, String publishedAt) { |
| 130 | + XposedBridge.log("[" + TAG + "] Attempting to show update dialog"); |
| 131 | + try { |
| 132 | + var markwon = Markwon.create(mActivity); |
| 133 | + var dialog = new AlertDialogWpp(mActivity); |
| 134 | + |
| 135 | + // Format the published date |
| 136 | + String formattedDate = formatPublishedDate(publishedAt); |
| 137 | + |
| 138 | + // Build simple message with version and date |
| 139 | + StringBuilder message = new StringBuilder(); |
| 140 | + message.append("📦 **Version:** `").append(hash).append("`\n"); |
| 141 | + if (!formattedDate.isEmpty()) { |
| 142 | + message.append("📅 **Released:** ").append(formattedDate).append("\n"); |
| 143 | + } |
| 144 | + message.append("\n### What's New\n\n").append(changelog); |
| 145 | + |
| 146 | + dialog.setTitle("🎉 New Update Available!"); |
| 147 | + dialog.setMessage(markwon.toMarkdown(message.toString())); |
| 148 | + dialog.setNegativeButton("Ignore", (dialog1, which) -> { |
| 149 | + WppCore.setPrivString("ignored_version", hash); |
| 150 | + dialog1.dismiss(); |
| 151 | + }); |
| 152 | + dialog.setPositiveButton("Update Now", (dialog1, which) -> { |
| 153 | + Utils.openLink(mActivity, TELEGRAM_UPDATE_URL); |
| 154 | + dialog1.dismiss(); |
| 155 | + }); |
| 156 | + dialog.show(); |
| 157 | + |
| 158 | + XposedBridge.log("[" + TAG + "] Update dialog shown successfully"); |
| 159 | + } catch (Exception e) { |
| 160 | + XposedBridge.log("[" + TAG + "] Error showing update dialog: " + e.getMessage()); |
| 161 | + e.printStackTrace(); |
| 162 | + } |
| 163 | + } |
| 164 | + |
| 165 | + /** |
| 166 | + * Format ISO 8601 date to human-readable format |
| 167 | + * @param isoDate ISO 8601 date string (e.g., "2024-02-14T12:34:56Z") |
| 168 | + * @return Formatted date (e.g., "Feb 14, 2024" or "February 14, 2024") |
| 169 | + */ |
| 170 | + private String formatPublishedDate(String isoDate) { |
| 171 | + if (isoDate == null || isoDate.isEmpty()) { |
| 172 | + return ""; |
| 173 | + } |
| 174 | + |
| 175 | + try { |
| 176 | + // Parse ISO 8601 date |
| 177 | + SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US); |
| 178 | + Date date = isoFormat.parse(isoDate); |
| 179 | + |
| 180 | + if (date != null) { |
| 181 | + // Format to readable date |
| 182 | + SimpleDateFormat displayFormat = new SimpleDateFormat("MMM dd, yyyy", Locale.US); |
| 183 | + return displayFormat.format(date); |
67 | 184 | } |
68 | | - } catch (Exception ignored) { |
| 185 | + } catch (Exception e) { |
| 186 | + XposedBridge.log("[" + TAG + "] Error parsing date: " + e.getMessage()); |
69 | 187 | } |
| 188 | + |
| 189 | + return ""; |
70 | 190 | } |
71 | 191 | } |
0 commit comments