mirror of
https://github.com/jahruz67/Bible-Daily.git
synced 2026-08-08 18:14:05 +00:00
Enhance Android build process and add settings for update management
- Implement caching for Android debug keystore - Modify APK build command to include CI versioning - Add logic to prepare release assets and upload to GitHub - Update .gitignore to exclude build directories - Introduce SettingsActivity for managing auto-update preferences - Add UpdateManager for handling update checks and dialogs - Integrate update check on app startup in MainActivity
This commit is contained in:
@@ -26,18 +26,51 @@ jobs:
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
|
||||
- name: Cache Android debug keystore
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.android/debug.keystore
|
||||
key: android-debug-keystore-${{ runner.os }}
|
||||
|
||||
- name: Make Gradle wrapper executable
|
||||
run: chmod +x gradlew
|
||||
|
||||
- name: Build debug APK
|
||||
run: ./gradlew assembleDebug
|
||||
run: ./gradlew assembleDebug -PciVersionCode=${{ github.run_number }} -PciVersionName=1.0.${{ github.run_number }}
|
||||
|
||||
- name: Prepare release assets
|
||||
shell: bash
|
||||
run: |
|
||||
VERSION_CODE="${{ github.run_number }}"
|
||||
VERSION_NAME="1.0.${{ github.run_number }}"
|
||||
APK_URL="https://github.com/jahruz67/Bible-Daily/releases/download/latest/app-debug.apk"
|
||||
APK_PATH="$(find app/build/outputs/apk/debug -maxdepth 1 -name '*.apk' -print -quit)"
|
||||
|
||||
if [ -z "$APK_PATH" ]; then
|
||||
echo "No APK found in app/build/outputs/apk/debug"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cp "$APK_PATH" app-debug.apk
|
||||
cat > latest.json <<EOF
|
||||
{
|
||||
"versionCode": $VERSION_CODE,
|
||||
"versionName": "$VERSION_NAME",
|
||||
"apkUrl": "$APK_URL",
|
||||
"commit": "${GITHUB_SHA}",
|
||||
"builtAt": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
}
|
||||
EOF
|
||||
|
||||
- name: Upload APK to latest release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: latest
|
||||
name: Latest APK
|
||||
files: app/build/outputs/apk/debug/app-debug.apk
|
||||
files: |
|
||||
app-debug.apk
|
||||
latest.json
|
||||
make_latest: true
|
||||
overwrite_files: true
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
/.gradle
|
||||
/.idea
|
||||
/build
|
||||
/app/build
|
||||
|
||||
+5
-2
@@ -2,6 +2,9 @@ plugins {
|
||||
id "com.android.application"
|
||||
}
|
||||
|
||||
def ciVersionCode = providers.gradleProperty("ciVersionCode").getOrNull()
|
||||
def ciVersionName = providers.gradleProperty("ciVersionName").getOrNull()
|
||||
|
||||
android {
|
||||
namespace "com.bibliadiaria.app"
|
||||
compileSdk 35
|
||||
@@ -10,8 +13,8 @@ android {
|
||||
applicationId "com.bibliadiaria.app"
|
||||
minSdk 23
|
||||
targetSdk 35
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
versionCode ciVersionCode != null ? ciVersionCode.toInteger() : 1
|
||||
versionName ciVersionName != null ? ciVersionName : "1.0"
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/AppTheme"
|
||||
android:usesCleartextTraffic="false">
|
||||
<activity
|
||||
android:name=".SettingsActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".ExtrasActivity"
|
||||
android:exported="false" />
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.bibliadiaria.app;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
@@ -153,6 +154,18 @@ public class ExtrasActivity extends Activity {
|
||||
1f
|
||||
));
|
||||
|
||||
TextView settings = textView("Ajustes", 15, COLOR_ACCENT, Typeface.BOLD);
|
||||
settings.setGravity(Gravity.CENTER);
|
||||
settings.setPadding(dp(14), 0, dp(14), 0);
|
||||
settings.setBackground(roundedRect(Color.WHITE, dp(8), Color.rgb(219, 226, 222), dp(1)));
|
||||
settings.setOnClickListener(view -> startActivity(new Intent(this, SettingsActivity.class)));
|
||||
LinearLayout.LayoutParams settingsParams = new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
dp(40)
|
||||
);
|
||||
settingsParams.setMargins(0, 0, dp(8), 0);
|
||||
topBar.addView(settings, settingsParams);
|
||||
|
||||
TextView home = textView("Inicio", 15, COLOR_ACCENT, Typeface.BOLD);
|
||||
home.setGravity(Gravity.CENTER);
|
||||
home.setPadding(dp(14), 0, dp(14), 0);
|
||||
|
||||
@@ -91,6 +91,7 @@ public class MainActivity extends Activity {
|
||||
|
||||
setContentView(createScreen());
|
||||
loadToday();
|
||||
checkForUpdateOnStartup();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -600,6 +601,23 @@ public class MainActivity extends Activity {
|
||||
loadSelectedDate();
|
||||
}
|
||||
|
||||
private void checkForUpdateOnStartup() {
|
||||
if (!UpdateManager.isAutoUpdatesEnabled(this) || executor == null || executor.isShutdown()) {
|
||||
return;
|
||||
}
|
||||
|
||||
executor.submit(() -> {
|
||||
try {
|
||||
UpdateManager.UpdateInfo info = UpdateManager.fetchLatestUpdate(this);
|
||||
if (info.updateAvailable) {
|
||||
runOnUiThread(() -> UpdateManager.showUpdateDialog(this, info));
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
// Startup update checks stay quiet so daily readings remain the main experience.
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void loadSelectedDate() {
|
||||
loadDate(selectedDateCalendar.getTime());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,331 @@
|
||||
package com.bibliadiaria.app;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.Switch;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class SettingsActivity extends Activity {
|
||||
private static final int COLOR_BACKGROUND = Color.rgb(247, 248, 246);
|
||||
private static final int COLOR_CARD = Color.WHITE;
|
||||
private static final int COLOR_INK = Color.rgb(25, 31, 31);
|
||||
private static final int COLOR_MUTED = Color.rgb(91, 99, 98);
|
||||
private static final int COLOR_ACCENT = Color.rgb(0, 107, 90);
|
||||
private static final int COLOR_WARM = Color.rgb(217, 75, 61);
|
||||
|
||||
private ExecutorService executor;
|
||||
private TextView statusText;
|
||||
private TextView installedVersionText;
|
||||
private TextView checkButton;
|
||||
private TextView downloadButton;
|
||||
private UpdateManager.UpdateInfo latestInfo;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
getWindow().setStatusBarColor(COLOR_BACKGROUND);
|
||||
getWindow().setNavigationBarColor(COLOR_BACKGROUND);
|
||||
}
|
||||
|
||||
executor = Executors.newSingleThreadExecutor();
|
||||
setContentView(createScreen());
|
||||
updateInstalledVersionText();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (executor != null) {
|
||||
executor.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
private View createScreen() {
|
||||
FrameLayout root = new FrameLayout(this);
|
||||
root.setBackgroundColor(COLOR_BACKGROUND);
|
||||
|
||||
ScrollView scrollView = new ScrollView(this);
|
||||
scrollView.setFillViewport(true);
|
||||
scrollView.setClipToPadding(false);
|
||||
|
||||
LinearLayout content = new LinearLayout(this);
|
||||
content.setOrientation(LinearLayout.VERTICAL);
|
||||
content.setPadding(dp(20), dp(28), dp(20), dp(28));
|
||||
scrollView.addView(content, new ScrollView.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
));
|
||||
|
||||
content.addView(createTopBar());
|
||||
|
||||
TextView title = textView("Settings", 34, COLOR_INK, Typeface.BOLD);
|
||||
title.setIncludeFontPadding(false);
|
||||
LinearLayout.LayoutParams titleParams = new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
);
|
||||
titleParams.setMargins(0, dp(18), 0, dp(18));
|
||||
content.addView(title, titleParams);
|
||||
|
||||
content.addView(createAutoUpdatesCard());
|
||||
content.addView(createVersionCard());
|
||||
|
||||
checkButton = actionButton("Check now", COLOR_ACCENT, Color.WHITE);
|
||||
checkButton.setOnClickListener(view -> checkForUpdate());
|
||||
LinearLayout.LayoutParams checkParams = new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
dp(46)
|
||||
);
|
||||
checkParams.setMargins(0, dp(16), 0, 0);
|
||||
content.addView(checkButton, checkParams);
|
||||
|
||||
downloadButton = actionButton("Download latest APK", COLOR_WARM, Color.WHITE);
|
||||
downloadButton.setVisibility(View.GONE);
|
||||
downloadButton.setOnClickListener(view -> {
|
||||
String url = latestInfo == null ? UpdateManager.APK_URL : latestInfo.apkUrl;
|
||||
UpdateManager.openDownload(this, url);
|
||||
});
|
||||
LinearLayout.LayoutParams downloadParams = new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
dp(46)
|
||||
);
|
||||
downloadParams.setMargins(0, dp(10), 0, 0);
|
||||
content.addView(downloadButton, downloadParams);
|
||||
|
||||
root.addView(scrollView, new FrameLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT
|
||||
));
|
||||
|
||||
root.setOnApplyWindowInsetsListener((view, insets) -> {
|
||||
content.setPadding(
|
||||
dp(20),
|
||||
dp(28) + insets.getSystemWindowInsetTop(),
|
||||
dp(20),
|
||||
dp(28) + insets.getSystemWindowInsetBottom()
|
||||
);
|
||||
return insets;
|
||||
});
|
||||
root.requestApplyInsets();
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
private View createTopBar() {
|
||||
LinearLayout topBar = new LinearLayout(this);
|
||||
topBar.setOrientation(LinearLayout.HORIZONTAL);
|
||||
topBar.setGravity(Gravity.CENTER_VERTICAL);
|
||||
|
||||
TextView label = textView("APP", 12, COLOR_ACCENT, Typeface.BOLD);
|
||||
topBar.addView(label, new LinearLayout.LayoutParams(
|
||||
0,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
1f
|
||||
));
|
||||
|
||||
TextView back = textView("Extras", 15, COLOR_ACCENT, Typeface.BOLD);
|
||||
back.setGravity(Gravity.CENTER);
|
||||
back.setPadding(dp(14), 0, dp(14), 0);
|
||||
back.setBackground(roundedRect(Color.WHITE, dp(8), Color.rgb(219, 226, 222), dp(1)));
|
||||
back.setOnClickListener(view -> finish());
|
||||
topBar.addView(back, new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
dp(40)
|
||||
));
|
||||
|
||||
return topBar;
|
||||
}
|
||||
|
||||
private View createAutoUpdatesCard() {
|
||||
LinearLayout card = createCard();
|
||||
card.setOrientation(LinearLayout.HORIZONTAL);
|
||||
card.setGravity(Gravity.CENTER_VERTICAL);
|
||||
|
||||
LinearLayout copy = new LinearLayout(this);
|
||||
copy.setOrientation(LinearLayout.VERTICAL);
|
||||
|
||||
TextView title = textView("Auto updates", 20, COLOR_INK, Typeface.BOLD);
|
||||
title.setIncludeFontPadding(false);
|
||||
copy.addView(title);
|
||||
|
||||
TextView subtitle = textView("Check for new builds on startup", 14, COLOR_MUTED, Typeface.NORMAL);
|
||||
LinearLayout.LayoutParams subtitleParams = new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
);
|
||||
subtitleParams.setMargins(0, dp(6), 0, 0);
|
||||
copy.addView(subtitle, subtitleParams);
|
||||
|
||||
card.addView(copy, new LinearLayout.LayoutParams(
|
||||
0,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
1f
|
||||
));
|
||||
|
||||
Switch autoSwitch = new Switch(this);
|
||||
autoSwitch.setChecked(UpdateManager.isAutoUpdatesEnabled(this));
|
||||
autoSwitch.setContentDescription("Auto updates");
|
||||
autoSwitch.setOnCheckedChangeListener((button, checked) -> {
|
||||
UpdateManager.setAutoUpdatesEnabled(this, checked);
|
||||
statusText.setText(checked ? "Startup checks are on." : "Startup checks are off.");
|
||||
});
|
||||
card.addView(autoSwitch);
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
private View createVersionCard() {
|
||||
LinearLayout card = createCard();
|
||||
card.setOrientation(LinearLayout.VERTICAL);
|
||||
|
||||
TextView title = textView("Version", 20, COLOR_INK, Typeface.BOLD);
|
||||
title.setIncludeFontPadding(false);
|
||||
card.addView(title);
|
||||
|
||||
installedVersionText = textView("", 15, COLOR_MUTED, Typeface.BOLD);
|
||||
LinearLayout.LayoutParams installedParams = new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
);
|
||||
installedParams.setMargins(0, dp(10), 0, 0);
|
||||
card.addView(installedVersionText, installedParams);
|
||||
|
||||
statusText = textView("Ready to check for updates.", 15, COLOR_MUTED, Typeface.NORMAL);
|
||||
statusText.setLineSpacing(dp(3), 1.1f);
|
||||
LinearLayout.LayoutParams statusParams = new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
);
|
||||
statusParams.setMargins(0, dp(8), 0, 0);
|
||||
card.addView(statusText, statusParams);
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
private void checkForUpdate() {
|
||||
if (executor == null || executor.isShutdown()) {
|
||||
return;
|
||||
}
|
||||
|
||||
latestInfo = null;
|
||||
downloadButton.setVisibility(View.GONE);
|
||||
checkButton.setEnabled(false);
|
||||
checkButton.setAlpha(0.62f);
|
||||
statusText.setText("Checking for updates...");
|
||||
|
||||
executor.submit(() -> {
|
||||
try {
|
||||
UpdateManager.UpdateInfo info = UpdateManager.fetchLatestUpdate(this);
|
||||
runOnUiThread(() -> showUpdateResult(info));
|
||||
} catch (Exception error) {
|
||||
runOnUiThread(() -> showUpdateError(error));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showUpdateResult(UpdateManager.UpdateInfo info) {
|
||||
latestInfo = info;
|
||||
checkButton.setEnabled(true);
|
||||
checkButton.setAlpha(1f);
|
||||
|
||||
if (info.updateAvailable) {
|
||||
String version = info.latestVersionName.isEmpty()
|
||||
? String.valueOf(info.latestVersionCode)
|
||||
: info.latestVersionName + " (" + info.latestVersionCode + ")";
|
||||
statusText.setText("Update available: " + version);
|
||||
downloadButton.setVisibility(View.VISIBLE);
|
||||
return;
|
||||
}
|
||||
|
||||
statusText.setText("You are on the latest build: "
|
||||
+ info.currentVersionName + " (" + info.currentVersionCode + ")");
|
||||
}
|
||||
|
||||
private void showUpdateError(Exception error) {
|
||||
checkButton.setEnabled(true);
|
||||
checkButton.setAlpha(1f);
|
||||
statusText.setText("Could not check for updates. " + cleanMessage(error));
|
||||
}
|
||||
|
||||
private void updateInstalledVersionText() {
|
||||
try {
|
||||
installedVersionText.setText("Installed: "
|
||||
+ UpdateManager.getInstalledVersionName(this)
|
||||
+ " (" + UpdateManager.getInstalledVersionCode(this) + ")");
|
||||
} catch (Exception error) {
|
||||
installedVersionText.setText("Installed version unavailable.");
|
||||
}
|
||||
}
|
||||
|
||||
private String cleanMessage(Exception error) {
|
||||
String message = error.getMessage();
|
||||
if (message == null || message.trim().isEmpty()) {
|
||||
return "Unknown error.";
|
||||
}
|
||||
return message.trim();
|
||||
}
|
||||
|
||||
private LinearLayout createCard() {
|
||||
LinearLayout card = new LinearLayout(this);
|
||||
card.setPadding(dp(18), dp(16), dp(18), dp(16));
|
||||
card.setBackground(roundedRect(COLOR_CARD, dp(8), Color.rgb(229, 232, 230), dp(1)));
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
card.setElevation(dp(1));
|
||||
}
|
||||
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
);
|
||||
params.setMargins(0, 0, 0, dp(14));
|
||||
card.setLayoutParams(params);
|
||||
return card;
|
||||
}
|
||||
|
||||
private TextView actionButton(String text, int backgroundColor, int textColor) {
|
||||
TextView button = textView(text, 15, textColor, Typeface.BOLD);
|
||||
button.setGravity(Gravity.CENTER);
|
||||
button.setPadding(dp(14), 0, dp(14), 0);
|
||||
button.setBackground(roundedRect(backgroundColor, dp(8), Color.TRANSPARENT, 0));
|
||||
return button;
|
||||
}
|
||||
|
||||
private TextView textView(String text, int sizeSp, int color, int style) {
|
||||
TextView textView = new TextView(this);
|
||||
textView.setText(text);
|
||||
textView.setTextSize(sizeSp);
|
||||
textView.setTextColor(color);
|
||||
textView.setTypeface(Typeface.DEFAULT, style);
|
||||
return textView;
|
||||
}
|
||||
|
||||
private GradientDrawable roundedRect(int color, int radius, int strokeColor, int strokeWidth) {
|
||||
GradientDrawable drawable = new GradientDrawable();
|
||||
drawable.setShape(GradientDrawable.RECTANGLE);
|
||||
drawable.setColor(color);
|
||||
drawable.setCornerRadius(radius);
|
||||
if (strokeWidth > 0) {
|
||||
drawable.setStroke(strokeWidth, strokeColor);
|
||||
}
|
||||
return drawable;
|
||||
}
|
||||
|
||||
private int dp(int value) {
|
||||
return Math.round(value * getResources().getDisplayMetrics().density);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
package com.bibliadiaria.app;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
final class UpdateManager {
|
||||
static final String PREFS_NAME = "preferencias_lectura";
|
||||
static final String KEY_AUTO_UPDATES = "auto_updates_enabled";
|
||||
static final String LATEST_JSON_URL =
|
||||
"https://github.com/jahruz67/Bible-Daily/releases/download/latest/latest.json";
|
||||
static final String APK_URL =
|
||||
"https://github.com/jahruz67/Bible-Daily/releases/download/latest/app-debug.apk";
|
||||
|
||||
private UpdateManager() {
|
||||
}
|
||||
|
||||
static boolean isAutoUpdatesEnabled(Context context) {
|
||||
return preferences(context).getBoolean(KEY_AUTO_UPDATES, true);
|
||||
}
|
||||
|
||||
static void setAutoUpdatesEnabled(Context context, boolean enabled) {
|
||||
preferences(context).edit().putBoolean(KEY_AUTO_UPDATES, enabled).apply();
|
||||
}
|
||||
|
||||
static UpdateInfo fetchLatestUpdate(Context context) throws Exception {
|
||||
String jsonText = download(LATEST_JSON_URL, "application/json");
|
||||
JSONObject json = new JSONObject(jsonText);
|
||||
|
||||
int latestVersionCode = json.optInt("versionCode", 0);
|
||||
if (latestVersionCode <= 0) {
|
||||
throw new IOException("Update metadata did not include a valid versionCode.");
|
||||
}
|
||||
|
||||
long currentVersionCode = getInstalledVersionCode(context);
|
||||
String latestVersionName = json.optString("versionName", "");
|
||||
String apkUrl = json.optString("apkUrl", APK_URL);
|
||||
String builtAt = json.optString("builtAt", "");
|
||||
String commit = json.optString("commit", "");
|
||||
|
||||
return new UpdateInfo(
|
||||
currentVersionCode,
|
||||
getInstalledVersionName(context),
|
||||
latestVersionCode,
|
||||
latestVersionName,
|
||||
apkUrl.isEmpty() ? APK_URL : apkUrl,
|
||||
builtAt,
|
||||
commit,
|
||||
latestVersionCode > currentVersionCode
|
||||
);
|
||||
}
|
||||
|
||||
static long getInstalledVersionCode(Context context) throws PackageManager.NameNotFoundException {
|
||||
PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
return info.getLongVersionCode();
|
||||
}
|
||||
return info.versionCode;
|
||||
}
|
||||
|
||||
static String getInstalledVersionName(Context context) {
|
||||
try {
|
||||
PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
|
||||
if (info.versionName == null || info.versionName.trim().isEmpty()) {
|
||||
return "unknown";
|
||||
}
|
||||
return info.versionName;
|
||||
} catch (PackageManager.NameNotFoundException error) {
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static void showUpdateDialog(Activity activity, UpdateInfo info) {
|
||||
if (activity.isFinishing()) {
|
||||
return;
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && activity.isDestroyed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
String version = info.latestVersionName.isEmpty()
|
||||
? String.valueOf(info.latestVersionCode)
|
||||
: info.latestVersionName + " (" + info.latestVersionCode + ")";
|
||||
|
||||
new AlertDialog.Builder(activity)
|
||||
.setTitle("Update available")
|
||||
.setMessage("Version " + version + " is ready to download.")
|
||||
.setPositiveButton("Download", (dialog, which) -> openDownload(activity, info.apkUrl))
|
||||
.setNegativeButton("Later", null)
|
||||
.show();
|
||||
}
|
||||
|
||||
static void openDownload(Context context, String apkUrl) {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(apkUrl));
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
private static SharedPreferences preferences(Context context) {
|
||||
return context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
private static String download(String urlText, String acceptHeader) throws IOException {
|
||||
HttpURLConnection connection = (HttpURLConnection) new URL(urlText).openConnection();
|
||||
connection.setConnectTimeout(12000);
|
||||
connection.setReadTimeout(12000);
|
||||
connection.setInstanceFollowRedirects(true);
|
||||
connection.setRequestProperty("User-Agent", "BibliaDiaria/1.0 Android");
|
||||
connection.setRequestProperty("Accept", acceptHeader);
|
||||
|
||||
int responseCode = connection.getResponseCode();
|
||||
if (responseCode < 200 || responseCode >= 300) {
|
||||
throw new IOException("Update server responded with code " + responseCode + ".");
|
||||
}
|
||||
|
||||
try (InputStream stream = connection.getInputStream()) {
|
||||
return readStream(stream);
|
||||
} finally {
|
||||
connection.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
private static String readStream(InputStream stream) throws IOException {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
try (BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(stream, StandardCharsets.UTF_8))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
builder.append(line).append('\n');
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
static final class UpdateInfo {
|
||||
final long currentVersionCode;
|
||||
final String currentVersionName;
|
||||
final int latestVersionCode;
|
||||
final String latestVersionName;
|
||||
final String apkUrl;
|
||||
final String builtAt;
|
||||
final String commit;
|
||||
final boolean updateAvailable;
|
||||
|
||||
UpdateInfo(
|
||||
long currentVersionCode,
|
||||
String currentVersionName,
|
||||
int latestVersionCode,
|
||||
String latestVersionName,
|
||||
String apkUrl,
|
||||
String builtAt,
|
||||
String commit,
|
||||
boolean updateAvailable
|
||||
) {
|
||||
this.currentVersionCode = currentVersionCode;
|
||||
this.currentVersionName = currentVersionName;
|
||||
this.latestVersionCode = latestVersionCode;
|
||||
this.latestVersionName = latestVersionName;
|
||||
this.apkUrl = apkUrl;
|
||||
this.builtAt = builtAt;
|
||||
this.commit = commit;
|
||||
this.updateAvailable = updateAvailable;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user