Browse Source

Add update checker back, with updated handling. Check description.

FlavorTown will check if current app was installed from F-droid and not show the update dialog if true.
You can now skip an update.
You can now force check for an update by clicking More -> Version (even if you set to skip the update).
renovate/org.robolectric-robolectric-4.x
Ammar Githam 4 years ago
parent
commit
c9d342471b
  1. 3
      app/src/main/java/awais/instagrabber/activities/MainActivity.java
  2. 51
      app/src/main/java/awais/instagrabber/fragments/settings/MorePreferencesFragment.java
  3. 2
      app/src/main/java/awais/instagrabber/utils/Constants.java
  4. 110
      app/src/main/java/awais/instagrabber/utils/FlavorTown.java
  5. 9
      app/src/main/java/awais/instagrabber/utils/SettingsHelper.java
  6. 34
      app/src/main/java/awais/instagrabber/utils/UpdateChecker.java
  7. 18
      app/src/main/res/drawable/preference_list_divider_material.xml
  8. 20
      app/src/main/res/layout/dialog_update.xml
  9. 5
      app/src/main/res/layout/item_pref_divider.xml
  10. 2
      app/src/main/res/values/strings.xml

3
app/src/main/java/awais/instagrabber/activities/MainActivity.java

@ -40,6 +40,7 @@ import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.SuggestionModel;
import awais.instagrabber.models.enums.SuggestionType;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.FlavorTown;
import awais.instagrabber.utils.Utils;
import static awais.instagrabber.utils.NavigationExtensions.setupWithNavController;
@ -91,6 +92,8 @@ public class MainActivity extends BaseLanguageActivity {
}
setupScrollingListener();
setupSuggestions();
FlavorTown.updateCheck(this);
FlavorTown.changelogCheck(this);
}
private void setupSuggestions() {

51
app/src/main/java/awais/instagrabber/fragments/settings/MorePreferencesFragment.java

@ -2,24 +2,31 @@ package awais.instagrabber.fragments.settings;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.navigation.NavDirections;
import androidx.navigation.fragment.NavHostFragment;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.R;
import awais.instagrabber.activities.Login;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.FlavorTown;
import awais.instagrabber.utils.Utils;
import static awais.instagrabber.utils.Utils.settingsHelper;
public class MorePreferencesFragment extends BasePreferencesFragment {
private static final String TAG = "MorePreferencesFragment";
private final String cookie = settingsHelper.getString(Constants.COOKIE);
@Override
@ -31,7 +38,8 @@ public class MorePreferencesFragment extends BasePreferencesFragment {
accountCategory.setIconSpaceReserved(false);
screen.addPreference(accountCategory);
final boolean isLoggedIn = !Utils.isEmpty(cookie) && Utils.getUserIdFromCookie(cookie) != null;
screen.addPreference(getPreference(isLoggedIn ? R.string.relogin : R.string.login,
screen.addPreference(getPreference(
isLoggedIn ? R.string.relogin : R.string.login,
isLoggedIn ? R.string.relogin_summary : -1,
-1,
preference -> {
@ -48,15 +56,28 @@ public class MorePreferencesFragment extends BasePreferencesFragment {
}));
}
final PreferenceCategory defaultCategory = new PreferenceCategory(requireContext());
screen.addPreference(defaultCategory);
defaultCategory.addPreference(getPreference(R.string.action_notif, R.drawable.ic_not_liked, preference -> false));
defaultCategory.addPreference(getPreference(R.string.action_settings, R.drawable.ic_outline_settings_24, preference -> {
final PreferenceCategory generalCategory = new PreferenceCategory(requireContext());
generalCategory.setTitle("General");
generalCategory.setIconSpaceReserved(false);
screen.addPreference(generalCategory);
generalCategory.addPreference(getPreference(R.string.action_notif, R.drawable.ic_not_liked, preference -> false));
generalCategory.addPreference(getPreference(R.string.action_settings, R.drawable.ic_outline_settings_24, preference -> {
final NavDirections navDirections = MorePreferencesFragmentDirections.actionMorePreferencesFragmentToSettingsPreferencesFragment();
NavHostFragment.findNavController(this).navigate(navDirections);
return true;
}));
defaultCategory.addPreference(getPreference(R.string.action_about, R.drawable.ic_outline_info_24, preference -> false));
final Preference aboutPreference = getPreference(R.string.action_about, R.drawable.ic_outline_info_24, preference -> false);
generalCategory.addPreference(aboutPreference);
final Preference divider = new Preference(requireContext());
divider.setLayoutResource(R.layout.item_pref_divider);
screen.addPreference(divider);
final Preference versionPreference = getPreference(R.string.version, BuildConfig.VERSION_NAME, -1, preference -> {
FlavorTown.updateCheck((AppCompatActivity) requireActivity(), true);
return true;
});
screen.addPreference(versionPreference);
}
@Override
@ -83,11 +104,27 @@ public class MorePreferencesFragment extends BasePreferencesFragment {
final int summary,
final int icon,
final Preference.OnPreferenceClickListener clickListener) {
String string = null;
if (summary > 0) {
try {
string = getString(summary);
} catch (Resources.NotFoundException e) {
Log.e(TAG, "Error", e);
}
}
return getPreference(title, string, icon, clickListener);
}
@NonNull
private Preference getPreference(final int title,
final String summary,
final int icon,
final Preference.OnPreferenceClickListener clickListener) {
final Preference preference = new Preference(requireContext());
if (icon <= 0) preference.setIconSpaceReserved(false);
if (icon > 0) preference.setIcon(icon);
preference.setTitle(title);
if (summary > 0) {
if (!Utils.isEmpty(summary)) {
preference.setSummary(summary);
}
preference.setOnPreferenceClickListener(clickListener);

2
app/src/main/java/awais/instagrabber/utils/Constants.java

@ -68,4 +68,6 @@ public final class Constants {
public static final String SIGNATURE_KEY = "9193488027538fd3450b83b7d05286d4ca9599a0f7eeed90d8c85925698a05dc";
public static final String BREADCRUMB_KEY = "iN4$aGr0m";
public static final int LOGIN_RESULT_CODE = 5000;
public static final String FDROID_SHA1_FINGERPRINT = "C1661EB8FD09F618307E687786D5E5056F65084D";
public static final String SKIPPED_VERSION = "skipped_version";
}

110
app/src/main/java/awais/instagrabber/utils/FlavorTown.java

@ -1,48 +1,102 @@
package awais.instagrabber.utils;
import android.annotation.SuppressLint;
import android.content.ActivityNotFoundException;
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 androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import javax.security.cert.CertificateException;
import javax.security.cert.X509Certificate;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.R;
import awais.instagrabber.databinding.DialogUpdateBinding;
import static awais.instagrabber.utils.Utils.settingsHelper;
public final class FlavorTown {
public static void updateCheck(@NonNull final Context context) {
private static final String TAG = "FlavorTown";
private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
private static AlertDialog dialog;
public static void updateCheck(@NonNull final AppCompatActivity context) {
updateCheck(context, false);
}
@SuppressLint("PackageManagerGetSignatures")
public static void updateCheck(@NonNull final AppCompatActivity context, final boolean force) {
boolean isInstalledFromFdroid = false;
final PackageInfo packageInfo;
try {
packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
for (Signature signature : packageInfo.signatures) {
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 (!version.equals(BuildConfig.VERSION_NAME) && !BuildConfig.DEBUG) {
new AlertDialog.Builder(context)
.setTitle(res.getString(R.string.update_available, version))
.setMessage(R.string.update_notice)
.setNeutralButton(R.string.cancel, null)
.setNegativeButton(R.string.action_github, (dialog, 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
}
})
.setPositiveButton(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();
if (force && version.equals(BuildConfig.VERSION_NAME)) {
Toast.makeText(context, "You're already on the latest version", Toast.LENGTH_SHORT).show();
return;
}
final String skippedVersion = settingsHelper.getString(Constants.SKIPPED_VERSION);
final boolean shouldShowDialog = force || (!version.equals(BuildConfig.VERSION_NAME) && !BuildConfig.DEBUG && !skippedVersion
.equals(version));
if (!shouldShowDialog) return;
dialog = new AlertDialog.Builder(context)
.setTitle(res.getString(R.string.update_available, version))
.setView(binding.getRoot())
.setNeutralButton(R.string.cancel, (dialog, which) -> {
if (binding.skipUpdate.isChecked()) {
settingsHelper.putString(Constants.SKIPPED_VERSION, version);
}
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);
}
@ -52,4 +106,14 @@ public final class FlavorTown {
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);
}
}

9
app/src/main/java/awais/instagrabber/utils/SettingsHelper.java

@ -1,6 +1,5 @@
package awais.instagrabber.utils;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
@ -32,6 +31,7 @@ import static awais.instagrabber.utils.Constants.MARK_AS_SEEN;
import static awais.instagrabber.utils.Constants.MUTED_VIDEOS;
import static awais.instagrabber.utils.Constants.PREV_INSTALL_VERSION;
import static awais.instagrabber.utils.Constants.SHOW_QUICK_ACCESS_DIALOG;
import static awais.instagrabber.utils.Constants.SKIPPED_VERSION;
import static awais.instagrabber.utils.Constants.STORIESIG;
public final class SettingsHelper {
@ -119,12 +119,13 @@ public final class SettingsHelper {
if (sharedPreferences != null) sharedPreferences.edit().putBoolean(key, val).apply();
}
@StringDef({APP_LANGUAGE, APP_THEME, COOKIE, FOLDER_PATH, DATE_TIME_FORMAT, DATE_TIME_SELECTION, CUSTOM_DATE_TIME_FORMAT, DEVICE_UUID})
@StringDef(
{APP_LANGUAGE, APP_THEME, COOKIE, FOLDER_PATH, DATE_TIME_FORMAT, DATE_TIME_SELECTION, CUSTOM_DATE_TIME_FORMAT, DEVICE_UUID, SKIPPED_VERSION})
public @interface StringSettings {}
@StringDef({DOWNLOAD_USER_FOLDER, BOTTOM_TOOLBAR, FOLDER_SAVE_TO, AUTOPLAY_VIDEOS, SHOW_QUICK_ACCESS_DIALOG, MUTED_VIDEOS,
AUTOLOAD_POSTS, CUSTOM_DATE_TIME_FORMAT_ENABLED, MARK_AS_SEEN, DM_MARK_AS_SEEN,
INSTADP, STORIESIG, AMOLED_THEME, CHECK_ACTIVITY, CHECK_UPDATES})
AUTOLOAD_POSTS, CUSTOM_DATE_TIME_FORMAT_ENABLED, MARK_AS_SEEN, DM_MARK_AS_SEEN,
INSTADP, STORIESIG, AMOLED_THEME, CHECK_ACTIVITY, CHECK_UPDATES})
public @interface BooleanSettings {}
@StringDef({PREV_INSTALL_VERSION})

34
app/src/main/java/awais/instagrabber/utils/UpdateChecker.java

@ -3,15 +3,15 @@ package awais.instagrabber.utils;
import android.os.AsyncTask;
import android.util.Log;
import androidx.annotation.NonNull;
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> {
public final class UpdateChecker extends AsyncTask<Void, Void, String> {
private static final String TAG = "UpdateChecker";
private final FetchListener<String> fetchListener;
private String version;
@ -19,14 +19,11 @@ public final class UpdateChecker extends AsyncTask<Void, Void, Boolean> {
this.fetchListener = fetchListener;
}
@NonNull
@Override
protected Boolean doInBackground(final Void... voids) {
protected String doInBackground(final Void... voids) {
HttpURLConnection conn = null;
try {
version = "";
HttpURLConnection conn =
(HttpURLConnection) new URL("https://github.com/austinhuang0131/instagrabber/releases/latest").openConnection();
conn = (HttpURLConnection) new URL("https://github.com/austinhuang0131/instagrabber/releases/latest").openConnection();
conn.setInstanceFollowRedirects(false);
conn.setUseCaches(false);
conn.setRequestProperty("User-Agent", Constants.A_USER_AGENT);
@ -35,20 +32,25 @@ public final class UpdateChecker extends AsyncTask<Void, Void, Boolean> {
final int responseCode = conn.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_MOVED_TEMP) {
version = conn.getHeaderField("Location").split("/v")[1];
return !version.equals(BuildConfig.VERSION_NAME);
return version;
}
conn.disconnect();
} catch (final Exception e) {
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
if (BuildConfig.DEBUG) Log.e(TAG, "", e);
} finally {
if (conn != null) {
conn.disconnect();
}
}
return false;
return null;
}
@Override
protected void onPostExecute(final Boolean result) {
if (result != null && result && fetchListener != null)
fetchListener.onResult("v"+version);
protected void onPostExecute(final String result) {
if (result == null || fetchListener == null) {
return;
}
fetchListener.onResult(version);
}
}

18
app/src/main/res/drawable/preference_list_divider_material.xml

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?><!--
Copyright (C) 2015 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#1f000000" />
<size
android:width="1dp"
android:height="1dp" />
</shape>

20
app/src/main/res/layout/dialog_update.xml

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/update_notice"
android:textAppearance="?attr/textAppearanceBody1" />
<CheckBox
android:id="@+id/skip_update"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/skip_update_checkbox" />
</LinearLayout>

5
app/src/main/res/layout/item_pref_divider.xml

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@drawable/preference_list_divider_material" />

2
app/src/main/res/values/strings.xml

@ -251,4 +251,6 @@
<string name="logout_success">Successfully logged out!</string>
<string name="dm_thread_info">Info</string>
<string name="mark_as_seen">Mark as seen</string>
<string name="skip_update_checkbox">Do not show again until next update</string>
<string name="version">Version</string>
</resources>
Loading…
Cancel
Save