From 21106fa02c934371e6deb33f79fd2d185da322f5 Mon Sep 17 00:00:00 2001 From: Sujit Kumar <60378235+therealsujitk@users.noreply.github.com> Date: Sat, 11 Nov 2023 17:24:42 +0530 Subject: [PATCH] Display release notes in new update dialog --- app/build.gradle | 2 + .../vtopchennai/activities/LoginActivity.java | 53 ++++++++++++++ .../vtopchennai/activities/MainActivity.java | 61 +++++++--------- .../dialogs/UpdateDialogFragment.java | 57 +++++++++++++++ .../helpers/SettingsRepository.java | 36 ++++++++++ .../main/res/drawable/ic_update_available.xml | 10 +++ .../main/res/layout/layout_dialog_update.xml | 69 +++++++++++++++++++ app/src/main/res/values/strings.xml | 5 +- 8 files changed, 253 insertions(+), 40 deletions(-) create mode 100644 app/src/main/java/tk/therealsuji/vtopchennai/fragments/dialogs/UpdateDialogFragment.java create mode 100644 app/src/main/res/drawable/ic_update_available.xml create mode 100644 app/src/main/res/layout/layout_dialog_update.xml diff --git a/app/build.gradle b/app/build.gradle index bc1c30d..5c38873 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -51,6 +51,8 @@ dependencies { implementation 'commons-io:commons-io:2.6' + implementation 'io.noties.markwon:core:4.6.2' + implementation 'io.reactivex.rxjava3:rxandroid:3.0.0' debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1' diff --git a/app/src/main/java/tk/therealsuji/vtopchennai/activities/LoginActivity.java b/app/src/main/java/tk/therealsuji/vtopchennai/activities/LoginActivity.java index 263170d..da31276 100644 --- a/app/src/main/java/tk/therealsuji/vtopchennai/activities/LoginActivity.java +++ b/app/src/main/java/tk/therealsuji/vtopchennai/activities/LoginActivity.java @@ -9,15 +9,26 @@ import android.widget.EditText; import androidx.appcompat.app.AppCompatActivity; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentTransaction; import com.google.firebase.analytics.FirebaseAnalytics; +import org.json.JSONObject; + +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.core.Observer; +import io.reactivex.rxjava3.disposables.CompositeDisposable; +import io.reactivex.rxjava3.disposables.Disposable; +import tk.therealsuji.vtopchennai.BuildConfig; import tk.therealsuji.vtopchennai.R; +import tk.therealsuji.vtopchennai.fragments.dialogs.UpdateDialogFragment; import tk.therealsuji.vtopchennai.helpers.SettingsRepository; import tk.therealsuji.vtopchennai.helpers.VTOPHelper; public class LoginActivity extends AppCompatActivity { SharedPreferences encryptedSharedPreferences, sharedPreferences; + CompositeDisposable compositeDisposable = new CompositeDisposable(); VTOPHelper vtopHelper; public void signIn() { @@ -95,6 +106,42 @@ public void onComplete() { startMainActivity(); } }); + + /* + Check for updates + */ + SettingsRepository.checkForUpdates() + .subscribe(new Observer() { + @Override + public void onSubscribe(@NonNull Disposable d) { + compositeDisposable.add(d); + } + + @Override + public void onNext(@NonNull JSONObject about) { + try { + int versionCode = about.getInt("versionCode"); + String versionName = about.getString("tagName"); + String releaseNotes = about.getString("releaseNotes"); + + if (versionCode > BuildConfig.VERSION_CODE) { + FragmentManager fragmentManager = getSupportFragmentManager(); + FragmentTransaction transaction = fragmentManager.beginTransaction(); + transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); + transaction.add(android.R.id.content, new UpdateDialogFragment(versionName, releaseNotes)).addToBackStack(null).commit(); + } + } catch (Exception ignored) { + } + } + + @Override + public void onError(@NonNull Throwable e) { + } + + @Override + public void onComplete() { + } + }); } @Override @@ -122,4 +169,10 @@ protected void onStop() { super.onStop(); this.vtopHelper.unbind(); } + + @Override + protected void onDestroy() { + super.onDestroy(); + compositeDisposable.dispose(); + } } diff --git a/app/src/main/java/tk/therealsuji/vtopchennai/activities/MainActivity.java b/app/src/main/java/tk/therealsuji/vtopchennai/activities/MainActivity.java index 37cfe96..516fe6c 100644 --- a/app/src/main/java/tk/therealsuji/vtopchennai/activities/MainActivity.java +++ b/app/src/main/java/tk/therealsuji/vtopchennai/activities/MainActivity.java @@ -16,6 +16,8 @@ import androidx.core.view.WindowCompat; import androidx.documentfile.provider.DocumentFile; import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentTransaction; import com.google.android.material.badge.BadgeDrawable; import com.google.android.material.bottomnavigation.BottomNavigationView; @@ -27,10 +29,7 @@ import java.io.File; import java.io.InputStream; -import java.io.InputStreamReader; import java.io.Serializable; -import java.net.HttpURLConnection; -import java.net.URL; import java.util.ArrayList; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; @@ -46,6 +45,7 @@ import tk.therealsuji.vtopchennai.fragments.HomeFragment; import tk.therealsuji.vtopchennai.fragments.PerformanceFragment; import tk.therealsuji.vtopchennai.fragments.ProfileFragment; +import tk.therealsuji.vtopchennai.fragments.dialogs.UpdateDialogFragment; import tk.therealsuji.vtopchennai.helpers.AppDatabase; import tk.therealsuji.vtopchennai.helpers.SettingsRepository; import tk.therealsuji.vtopchennai.helpers.VTOPHelper; @@ -361,47 +361,32 @@ public void onComplete() { Check for updates */ Context context = this; - Observable.fromCallable(() -> { - try { - StringBuilder sb = new StringBuilder(); - URL url = new URL(SettingsRepository.APP_ABOUT_URL + "?v=" + BuildConfig.VERSION_NAME); - HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection(); - InputStream in = httpURLConnection.getInputStream(); - InputStreamReader reader = new InputStreamReader(in); - int data = reader.read(); - - while (data != -1) { - char current = (char) data; - sb.append(current); - data = reader.read(); - } - - String result = sb.toString(); - JSONObject about = new JSONObject(result); - - return about.getInt("versionCode"); - } catch (Exception ignored) { - return 0; - } - }) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(new Observer() { + SettingsRepository.checkForUpdates() + .subscribe(new Observer() { @Override public void onSubscribe(@NonNull Disposable d) { compositeDisposable.add(d); } @Override - public void onNext(@NonNull Integer versionCode) { - if (versionCode > BuildConfig.VERSION_CODE) { - new MaterialAlertDialogBuilder(context) - .setMessage(R.string.update_message) - .setNegativeButton(R.string.cancel, (dialogInterface, i) -> dialogInterface.dismiss()) - .setPositiveButton(R.string.update, (dialogInterface, i) -> SettingsRepository.openDownloadPage(context)) - .setTitle(R.string.update_title) - .show(); - } else if (SettingsRepository.isRefreshRequired(context)) { + public void onNext(@NonNull JSONObject about) { + try { + int versionCode = about.getInt("versionCode"); + String versionName = about.getString("tagName"); + String releaseNotes = about.getString("releaseNotes"); + + if (versionCode > BuildConfig.VERSION_CODE) { + FragmentManager fragmentManager = getSupportFragmentManager(); + FragmentTransaction transaction = fragmentManager.beginTransaction(); + transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); + transaction.add(android.R.id.content, new UpdateDialogFragment(versionName, releaseNotes)).addToBackStack(null).commit(); + + return; + } + } catch (Exception ignored) { + } + + if (SettingsRepository.isRefreshRequired(context)) { new MaterialAlertDialogBuilder(context) .setMessage(R.string.sync_message) .setNegativeButton(R.string.cancel, (dialogInterface, i) -> dialogInterface.dismiss()) diff --git a/app/src/main/java/tk/therealsuji/vtopchennai/fragments/dialogs/UpdateDialogFragment.java b/app/src/main/java/tk/therealsuji/vtopchennai/fragments/dialogs/UpdateDialogFragment.java new file mode 100644 index 0000000..8422497 --- /dev/null +++ b/app/src/main/java/tk/therealsuji/vtopchennai/fragments/dialogs/UpdateDialogFragment.java @@ -0,0 +1,57 @@ +package tk.therealsuji.vtopchennai.fragments.dialogs; + +import android.app.Dialog; +import android.os.Bundle; +import android.text.Html; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.DialogFragment; + +import io.noties.markwon.Markwon; +import tk.therealsuji.vtopchennai.R; +import tk.therealsuji.vtopchennai.helpers.SettingsRepository; + +public class UpdateDialogFragment extends DialogFragment { + String versionName, releaseNotes; + + public UpdateDialogFragment() { + // Required empty public constructor + } + + public UpdateDialogFragment(String versionName, String releaseNotes) { + this.versionName = versionName; + this.releaseNotes = releaseNotes; + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View dialogFragment = inflater.inflate(R.layout.layout_dialog_update, container, false); + + TextView description = dialogFragment.findViewById(R.id.text_view_description); + description.setText(Html.fromHtml(this.requireContext().getString(R.string.update_message, this.versionName), Html.FROM_HTML_MODE_LEGACY)); + + TextView releaseNotes = dialogFragment.findViewById(R.id.text_view_release_notes); + Markwon markwon = Markwon.create(this.requireContext()); + markwon.setMarkdown(releaseNotes, this.releaseNotes); + + dialogFragment.findViewById(R.id.button_cancel).setOnClickListener(view -> this.dismiss()); + dialogFragment.findViewById(R.id.button_update).setOnClickListener(view -> SettingsRepository.openDownloadPage(this.requireContext())); + + return dialogFragment; + } + + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + Dialog dialog = super.onCreateDialog(savedInstanceState); + dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + return dialog; + } +} diff --git a/app/src/main/java/tk/therealsuji/vtopchennai/helpers/SettingsRepository.java b/app/src/main/java/tk/therealsuji/vtopchennai/helpers/SettingsRepository.java index c6ac7a9..6534b82 100644 --- a/app/src/main/java/tk/therealsuji/vtopchennai/helpers/SettingsRepository.java +++ b/app/src/main/java/tk/therealsuji/vtopchennai/helpers/SettingsRepository.java @@ -32,6 +32,12 @@ import com.google.android.material.color.DynamicColors; import com.google.android.material.color.DynamicColorsOptions; +import org.json.JSONObject; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; import java.security.SecureRandom; import java.security.cert.X509Certificate; import java.text.ParseException; @@ -46,7 +52,11 @@ import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.schedulers.Schedulers; import okhttp3.OkHttpClient; +import tk.therealsuji.vtopchennai.BuildConfig; import tk.therealsuji.vtopchennai.R; import tk.therealsuji.vtopchennai.activities.WebViewActivity; import tk.therealsuji.vtopchennai.fragments.RecyclerViewFragment; @@ -193,6 +203,32 @@ public static boolean hasFileWritePermission(Context context) { return hasPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE); } + public static Observable checkForUpdates() { + return Observable.fromCallable(() -> { + try { + StringBuilder sb = new StringBuilder(); + URL url = new URL(SettingsRepository.APP_ABOUT_URL + "?v=" + BuildConfig.VERSION_NAME); + HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection(); + InputStream in = httpURLConnection.getInputStream(); + InputStreamReader reader = new InputStreamReader(in); + int data = reader.read(); + + while (data != -1) { + char current = (char) data; + sb.append(current); + data = reader.read(); + } + + String result = sb.toString(); + return new JSONObject(result); + } catch (Exception ignored) { + return new JSONObject(); + } + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()); + } + public static void openDownloadPage(Context context) { Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(APP_BASE_URL)); context.startActivity(browserIntent); diff --git a/app/src/main/res/drawable/ic_update_available.xml b/app/src/main/res/drawable/ic_update_available.xml new file mode 100644 index 0000000..5acaf5b --- /dev/null +++ b/app/src/main/res/drawable/ic_update_available.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout/layout_dialog_update.xml b/app/src/main/res/layout/layout_dialog_update.xml new file mode 100644 index 0000000..f1b4783 --- /dev/null +++ b/app/src/main/res/layout/layout_dialog_update.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + +