Separate out latest version check logic per flavor

This commit is contained in:
Ammar Githam 2021-03-28 22:56:29 +09:00
parent c9f46a1986
commit 1089a39375
8 changed files with 184 additions and 144 deletions

View File

@ -0,0 +1,64 @@
package awais.instagrabber.utils;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import org.json.JSONObject;
import java.net.HttpURLConnection;
import java.net.URL;
public class UpdateChecker {
private static final Object LOCK = new Object();
private static final String TAG = UpdateChecker.class.getSimpleName();
private static UpdateChecker instance;
public static UpdateChecker getInstance() {
if (instance == null) {
synchronized (LOCK) {
if (instance == null) {
instance = new UpdateChecker();
}
}
}
return instance;
}
/**
* Needs to be called asynchronously
*
* @return the latest version from f-droid
*/
@Nullable
public String getLatestVersion() {
HttpURLConnection conn = null;
try {
conn = (HttpURLConnection) new URL("https://f-droid.org/api/v1/packages/me.austinhuang.instagrabber").openConnection();
conn.setUseCaches(false);
conn.setRequestProperty("User-Agent", "https://Barinsta.AustinHuang.me / mailto:Barinsta@AustinHuang.me");
conn.connect();
final int responseCode = conn.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
final JSONObject data = new JSONObject(NetworkUtils.readFromConnection(conn));
return "v" + data.getJSONArray("packages").getJSONObject(0).getString("versionName");
// if (BuildConfig.VERSION_CODE < data.getInt("suggestedVersionCode")) {
// }
}
} catch (final Exception e) {
Log.e(TAG, "", e);
} finally {
if (conn != null) {
conn.disconnect();
}
}
return null;
}
public void onDownload(@NonNull final AppCompatActivity context) {
Utils.openURL(context, "https://f-droid.org/packages/me.austinhuang.instagrabber/");
}
}

View File

@ -0,0 +1,61 @@
package awais.instagrabber.utils;
import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.net.HttpURLConnection;
import java.net.URL;
public class UpdateChecker {
private static final Object LOCK = new Object();
private static final String TAG = UpdateChecker.class.getSimpleName();
private static UpdateChecker instance;
public static UpdateChecker getInstance() {
if (instance == null) {
synchronized (LOCK) {
if (instance == null) {
instance = new UpdateChecker();
}
}
}
return instance;
}
/**
* Needs to be called asynchronously
*
* @return the latest version from Github
*/
@Nullable
public String getLatestVersion() {
HttpURLConnection conn = null;
try {
conn = (HttpURLConnection) new URL("https://github.com/austinhuang0131/barinsta/releases/latest").openConnection();
conn.setInstanceFollowRedirects(false);
conn.setUseCaches(false);
conn.setRequestProperty("User-Agent", "https://Barinsta.AustinHuang.me / mailto:Barinsta@AustinHuang.me");
conn.connect();
final int responseCode = conn.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_MOVED_TEMP) {
return "v" + conn.getHeaderField("Location").split("/v")[1];
// return !version.equals(BuildConfig.VERSION_NAME);
}
} catch (final Exception e) {
Log.e(TAG, "", e);
} finally {
if (conn != null) {
conn.disconnect();
}
}
return null;
}
public void onDownload(@NonNull final Context context) {
Utils.openURL(context, "https://github.com/austinhuang0131/instagrabber/releases/latest");
}
}

View File

@ -193,7 +193,9 @@ public class MorePreferencesFragment extends BasePreferencesFragment {
BuildConfig.VERSION_NAME + " (" + BuildConfig.VERSION_CODE + ")", BuildConfig.VERSION_NAME + " (" + BuildConfig.VERSION_CODE + ")",
-1, -1,
preference -> { preference -> {
FlavorTown.updateCheck((AppCompatActivity) requireActivity(), true); final AppCompatActivity activity = (AppCompatActivity) getActivity();
if (activity == null) return true;
FlavorTown.updateCheck(activity, true);
return true; return true;
})); }));
screen.addPreference(getDivider(context)); screen.addPreference(getDivider(context));

View File

@ -79,7 +79,6 @@ public final class Constants {
// public static final String SIGNATURE_KEY = "9193488027538fd3450b83b7d05286d4ca9599a0f7eeed90d8c85925698a05dc"; // public static final String SIGNATURE_KEY = "9193488027538fd3450b83b7d05286d4ca9599a0f7eeed90d8c85925698a05dc";
public static final String BREADCRUMB_KEY = "iN4$aGr0m"; public static final String BREADCRUMB_KEY = "iN4$aGr0m";
public static final int LOGIN_RESULT_CODE = 5000; public static final int LOGIN_RESULT_CODE = 5000;
public static final String FDROID_SHA1_FINGERPRINT = "C1661EB8FD09F618307E687786D5E5056F65084D";
public static final String SKIPPED_VERSION = "skipped_version"; public static final String SKIPPED_VERSION = "skipped_version";
public static final String DEFAULT_TAB = "default_tab"; public static final String DEFAULT_TAB = "default_tab";
public static final String PREF_DARK_THEME = "dark_theme"; public static final String PREF_DARK_THEME = "dark_theme";

View File

@ -1,104 +1,46 @@
package awais.instagrabber.utils; package awais.instagrabber.utils;
import android.annotation.SuppressLint;
import android.content.ActivityNotFoundException;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.content.res.Resources;
import android.net.Uri;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
import javax.security.cert.CertificateException;
import javax.security.cert.X509Certificate;
import awais.instagrabber.BuildConfig; import awais.instagrabber.BuildConfig;
import awais.instagrabber.R; import awais.instagrabber.R;
import awais.instagrabber.databinding.DialogUpdateBinding;
import static awais.instagrabber.utils.Utils.settingsHelper; import static awais.instagrabber.utils.Utils.settingsHelper;
public final class FlavorTown { public final class FlavorTown {
private static final String TAG = "FlavorTown"; private static final String TAG = "FlavorTown";
private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); private static final UpdateChecker UPDATE_CHECKER = UpdateChecker.getInstance();
private static AlertDialog dialog;
private static boolean checking = false;
public static void updateCheck(@NonNull final AppCompatActivity context) { public static void updateCheck(@NonNull final AppCompatActivity context) {
updateCheck(context, false); updateCheck(context, false);
} }
@SuppressLint("PackageManagerGetSignatures") public static void updateCheck(@NonNull final AppCompatActivity context,
public static void updateCheck(@NonNull final AppCompatActivity context, final boolean force) { final boolean force) {
boolean isInstalledFromFdroid = false; if (checking) return;
final PackageInfo packageInfo; checking = true;
try { AppExecutors.getInstance().networkIO().execute(() -> {
packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES); final String version = UPDATE_CHECKER.getLatestVersion();
for (Signature signature : packageInfo.signatures) { if (version == null) return;
final X509Certificate cert = X509Certificate.getInstance(signature.toByteArray());
final String fingerprint = bytesToHex(MessageDigest.getInstance("SHA-1").digest(cert.getEncoded()));
isInstalledFromFdroid = fingerprint.equals(Constants.FDROID_SHA1_FINGERPRINT);
// Log.d(TAG, "fingerprint:" + fingerprint);
}
} catch (PackageManager.NameNotFoundException | NoSuchAlgorithmException | CertificateException e) {
Log.e(TAG, "Error", e);
}
if (isInstalledFromFdroid) return;
final DialogUpdateBinding binding = DialogUpdateBinding.inflate(context.getLayoutInflater(), null, false);
binding.skipUpdate.setOnCheckedChangeListener((buttonView, isChecked) -> {
if (dialog == null) return;
dialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(!isChecked);
});
Resources res = context.getResources();
new UpdateChecker(version -> {
if (force && version.equals(BuildConfig.VERSION_NAME)) { if (force && version.equals(BuildConfig.VERSION_NAME)) {
Toast.makeText(context, "You're already on the latest version", Toast.LENGTH_SHORT).show(); Toast.makeText(context, "You're already on the latest version", Toast.LENGTH_SHORT).show();
return; return;
} }
final String skippedVersion = settingsHelper.getString(Constants.SKIPPED_VERSION); final boolean shouldShowDialog = UpdateCheckCommon.shouldShowUpdateDialog(force, version);
final boolean shouldShowDialog = force || (!version.equals(BuildConfig.VERSION_NAME) && !BuildConfig.DEBUG && !skippedVersion
.equals(version));
if (!shouldShowDialog) return; if (!shouldShowDialog) return;
dialog = new AlertDialog.Builder(context) UpdateCheckCommon.showUpdateDialog(context, version, (dialog, which) -> {
.setTitle(res.getString(R.string.update_available, version)) UPDATE_CHECKER.onDownload(context);
.setView(binding.getRoot())
.setNeutralButton(R.string.cancel, (dialog, which) -> {
if (binding.skipUpdate.isChecked()) {
settingsHelper.putString(Constants.SKIPPED_VERSION, version);
}
dialog.dismiss(); dialog.dismiss();
}) });
.setPositiveButton(R.string.action_github, (dialog1, which) -> { });
try {
context.startActivity(new Intent(Intent.ACTION_VIEW).setData(
Uri.parse("https://github.com/austinhuang0131/instagrabber/releases/latest")));
} catch (final ActivityNotFoundException e) {
// do nothing
}
})
// if we don't show dialog for fdroid users, is the below required?
.setNegativeButton(R.string.action_fdroid, (dialog, which) -> {
try {
context.startActivity(new Intent(Intent.ACTION_VIEW).setData(
Uri.parse("https://f-droid.org/packages/me.austinhuang.instagrabber/")));
} catch (final ActivityNotFoundException e) {
// do nothing
}
})
.show();
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} }
public static void changelogCheck(@NonNull final Context context) { public static void changelogCheck(@NonNull final Context context) {
@ -121,14 +63,4 @@ public final class FlavorTown {
settingsHelper.putInteger(Constants.PREV_INSTALL_VERSION, BuildConfig.VERSION_CODE); settingsHelper.putInteger(Constants.PREV_INSTALL_VERSION, BuildConfig.VERSION_CODE);
} }
} }
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
}
return new String(hexChars);
}
} }

View File

@ -0,0 +1,39 @@
package awais.instagrabber.utils;
import android.content.Context;
import android.content.DialogInterface;
import androidx.annotation.NonNull;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.R;
import static awais.instagrabber.utils.Utils.settingsHelper;
public final class UpdateCheckCommon {
public static boolean shouldShowUpdateDialog(final boolean force,
@NonNull final String version) {
final String skippedVersion = settingsHelper.getString(Constants.SKIPPED_VERSION);
return force || (!version.equals(BuildConfig.VERSION_NAME) && !BuildConfig.DEBUG && !skippedVersion
.equals(version));
}
public static void showUpdateDialog(@NonNull final Context context,
@NonNull final String version,
@NonNull final DialogInterface.OnClickListener onDownloadClickListener) {
AppExecutors.getInstance().mainThread().execute(() -> {
new MaterialAlertDialogBuilder(context)
.setTitle(context.getString(R.string.update_available, version))
.setNeutralButton(R.string.skip_update, (dialog, which) -> {
settingsHelper.putString(Constants.SKIPPED_VERSION, version);
dialog.dismiss();
})
.setPositiveButton(R.string.action_download, onDownloadClickListener)
.setNegativeButton(R.string.cancel, null)
.show();
});
}
}

View File

@ -1,58 +0,0 @@
package awais.instagrabber.utils;
import android.os.AsyncTask;
import android.util.Log;
import androidx.annotation.NonNull;
import org.json.JSONObject;
import java.net.HttpURLConnection;
import java.net.URL;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.interfaces.FetchListener;
public final class UpdateChecker extends AsyncTask<Void, Void, Boolean> {
private final FetchListener<String> fetchListener;
private String version;
public UpdateChecker(final FetchListener<String> fetchListener) {
this.fetchListener = fetchListener;
}
@NonNull
@Override
protected Boolean doInBackground(final Void... voids) {
try {
version = "";
HttpURLConnection conn =
(HttpURLConnection) new URL("https://f-droid.org/api/v1/packages/me.austinhuang.instagrabber").openConnection();
conn.setUseCaches(false);
conn.setRequestProperty("User-Agent", "https://Barinsta.AustinHuang.me / mailto:Barinsta@AustinHuang.me");
conn.connect();
final int responseCode = conn.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
final JSONObject data = new JSONObject(NetworkUtils.readFromConnection(conn));
if (BuildConfig.VERSION_CODE < data.getInt("suggestedVersionCode")) {
version = data.getJSONArray("packages").getJSONObject(0).getString("versionName");
return true;
}
}
conn.disconnect();
} catch (final Exception e) {
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
}
return false;
}
@Override
protected void onPostExecute(final Boolean result) {
if (result != null && result && fetchListener != null)
fetchListener.onResult("v"+version);
}
}

View File

@ -475,4 +475,5 @@
<string name="delete_unsuccessful">Delete unsuccessful</string> <string name="delete_unsuccessful">Delete unsuccessful</string>
<string name="crash_report_subject">Barinsta Crash Report</string> <string name="crash_report_subject">Barinsta Crash Report</string>
<string name="crash_report_title">Select an email app to send crash logs</string> <string name="crash_report_title">Select an email app to send crash logs</string>
<string name="skip_update">Skip this update</string>
</resources> </resources>