diff --git a/app/build.gradle b/app/build.gradle index cd02eece..64c5f1cf 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,8 +1,8 @@ plugins { alias(libs.plugins.androidApplication) + alias(libs.plugins.ossLicenses) alias(libs.plugins.googleServices) alias(libs.plugins.firebaseCrashlytics) - alias(libs.plugins.aboutLibraries) alias(libs.plugins.hiltAndroid) } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8dd3e47f..f52666f4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -63,7 +63,11 @@ android:theme="@style/AppTheme" /> + diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/help/HelpActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/help/HelpActivity.java index 327e14a1..8b60c4cf 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/help/HelpActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/help/HelpActivity.java @@ -1,7 +1,6 @@ package com.d4rk.androidtutorials.java.ui.screens.help; import android.content.ActivityNotFoundException; -import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Bundle; @@ -23,7 +22,6 @@ import com.d4rk.androidtutorials.java.utils.OpenSourceLicensesUtils; import com.google.android.material.snackbar.Snackbar; import com.google.android.play.core.review.ReviewInfo; -import com.mikepenz.aboutlibraries.LibsBuilder; import dagger.hilt.android.AndroidEntryPoint; @@ -82,37 +80,13 @@ public boolean onOptionsItemSelected(@NonNull MenuItem item) { openLink("https://mihaicristiancondrea.github.io/profile/#privacy-policy-end-user-software"); return true; } else if (itemId == R.id.oss) { - OpenSourceLicensesUtils.loadHtmlData(this, (changelogHtml, eulaHtml) -> openLicensesScreen(this, eulaHtml, changelogHtml)); + OpenSourceLicensesUtils.openLicensesScreen(this); return true; } else { return super.onOptionsItemSelected(item); } } - private void openLicensesScreen(Context context, String eulaHtmlString, String changelogHtmlString) { - new LibsBuilder() - .withActivityTitle(context.getString(R.string.open_source_licenses)) - .withEdgeToEdge(true) - .withShowLoadingProgress(true) - .withSearchEnabled(true) - .withAboutIconShown(true) - .withAboutAppName(context.getString(R.string.app_name)) - .withVersionShown(true) - .withAboutVersionString(BuildConfig.VERSION_NAME + " (" + BuildConfig.VERSION_CODE + ")") - .withLicenseShown(true) - .withAboutVersionShown(true) - .withAboutSpecial1(context.getString(R.string.eula_title)) - .withAboutSpecial1Description( - eulaHtmlString != null ? eulaHtmlString : context.getString(R.string.loading_eula) - ) - .withAboutSpecial2(context.getString(R.string.changelog)) - .withAboutSpecial2Description( - changelogHtmlString != null ? changelogHtmlString : context.getString(R.string.loading_changelog) - ) - .withAboutDescription(context.getString(R.string.app_short_description)) - .start(context); - } - private void showVersionInfoDialog() { DialogVersionInfoBinding binding = DialogVersionInfoBinding.inflate(getLayoutInflater()); AlertDialog.Builder builder = new AlertDialog.Builder(this); @@ -131,7 +105,7 @@ private void openGooglePlayListing() { final String appPackageName = getPackageName(); try { startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + appPackageName))); - } catch (android.content.ActivityNotFoundException anfe) { + } catch (ActivityNotFoundException anfe) { startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=" + appPackageName))); } } diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/settings/SettingsFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/settings/SettingsFragment.java index e54b38ab..859147ad 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/settings/SettingsFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/settings/SettingsFragment.java @@ -16,11 +16,9 @@ import androidx.preference.PreferenceFragmentCompat; import androidx.preference.SwitchPreferenceCompat; -import com.d4rk.androidtutorials.java.BuildConfig; import com.d4rk.androidtutorials.java.R; import com.d4rk.androidtutorials.java.ui.components.dialogs.RequireRestartDialog; import com.d4rk.androidtutorials.java.utils.OpenSourceLicensesUtils; -import com.mikepenz.aboutlibraries.LibsBuilder; public class SettingsFragment extends PreferenceFragmentCompat { @Override @@ -57,7 +55,7 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { Preference ossPreference = findPreference(getString(R.string.key_open_source_licenses)); if (ossPreference != null) { ossPreference.setOnPreferenceClickListener(preference -> { - OpenSourceLicensesUtils.loadHtmlData(requireContext(), (changelogHtml, eulaHtml) -> openLicensesScreen(requireContext(), eulaHtml, changelogHtml)); + OpenSourceLicensesUtils.openLicensesScreen(requireContext()); return true; }); } @@ -98,28 +96,4 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { }); } } - - private void openLicensesScreen(Context context, String eulaHtmlString, String changelogHtmlString) { - new LibsBuilder() - .withActivityTitle(context.getString(R.string.open_source_licenses)) - .withEdgeToEdge(true) - .withShowLoadingProgress(true) - .withSearchEnabled(true) - .withAboutIconShown(true) - .withAboutAppName(context.getString(R.string.app_name)) - .withVersionShown(true) - .withAboutVersionString(BuildConfig.VERSION_NAME + " (" + BuildConfig.VERSION_CODE + ")") - .withLicenseShown(true) - .withAboutVersionShown(true) - .withAboutSpecial1(context.getString(R.string.eula_title)) - .withAboutSpecial1Description( - eulaHtmlString != null ? eulaHtmlString : context.getString(R.string.loading_eula) - ) - .withAboutSpecial2(context.getString(R.string.changelog)) - .withAboutSpecial2Description( - changelogHtmlString != null ? changelogHtmlString : context.getString(R.string.loading_changelog) - ) - .withAboutDescription(context.getString(R.string.app_short_description)) - .start(context); - } } \ No newline at end of file diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/utils/OpenSourceLicensesUtils.java b/app/src/main/java/com/d4rk/androidtutorials/java/utils/OpenSourceLicensesUtils.java index 3dc9d3c1..be290945 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/utils/OpenSourceLicensesUtils.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/utils/OpenSourceLicensesUtils.java @@ -1,123 +1,32 @@ package com.d4rk.androidtutorials.java.utils; +import android.app.Activity; import android.content.Context; -import android.os.Handler; -import android.os.Looper; -import android.util.Log; +import android.content.Intent; -import com.d4rk.androidtutorials.java.R; - -import org.commonmark.node.Node; -import org.commonmark.parser.Parser; -import org.commonmark.renderer.html.HtmlRenderer; - -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.Objects; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class OpenSourceLicensesUtils { - private static final String TAG = "OpenSourceLicensesUtils"; - private static final ExecutorService executor = Executors.newSingleThreadExecutor(); - private static final Handler mainHandler = new Handler(Looper.getMainLooper()); - - public static void loadHtmlData(final Context context, final HtmlDataCallback callback) { - executor.execute(() -> { - String packageName = context.getPackageName(); - String currentVersion = getAppVersion(context); - String changelogUrl = "https://raw.githubusercontent.com/MihaiCristianCondrea/" + packageName + "/refs/heads/main/CHANGELOG.md"; - String eulaUrl = "https://raw.githubusercontent.com/MihaiCristianCondrea/" + packageName + "/refs/heads/main/EULA.md"; - - String changelogMarkdown = loadMarkdown(context, changelogUrl, R.string.error_loading_changelog); - String extractedChangelog = extractLatestVersionChangelog(changelogMarkdown, currentVersion); - String changelogHtml = markdownToHtml(extractedChangelog); - - String eulaMarkdown = loadMarkdown(context, eulaUrl, R.string.error_loading_eula); - String eulaHtml = markdownToHtml(eulaMarkdown); +import androidx.annotation.Nullable; - mainHandler.post(() -> callback.onHtmlDataLoaded(changelogHtml, eulaHtml)); - }); - } +import com.d4rk.androidtutorials.java.R; +import com.google.android.gms.oss.licenses.OssLicensesMenuActivity; - private static String loadMarkdown(Context context, String urlString, int errorStringId) { - HttpURLConnection connection = null; - BufferedReader reader = null; - try { - URL url = new URL(urlString); - connection = (HttpURLConnection) url.openConnection(); - connection.setRequestMethod("GET"); - connection.setConnectTimeout(10000); - connection.setReadTimeout(10000); +public final class OpenSourceLicensesUtils { - int responseCode = connection.getResponseCode(); - if (responseCode == HttpURLConnection.HTTP_OK) { - reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); - StringBuilder content = new StringBuilder(); - String line; - while ((line = reader.readLine()) != null) { - content.append(line).append("\n"); - } - return content.toString(); - } else { - Log.e(TAG, "Failed to load URL: " + urlString + " with response code: " + responseCode); - return context.getString(errorStringId); - } - } catch (Exception e) { - Log.e(TAG, "Error loading markdown from URL: " + urlString, e); - return context.getString(errorStringId); - } finally { - if (reader != null) { - try { - reader.close(); - } catch (Exception e) { - Log.e(TAG, "Error closing reader", e); - } - } - if (connection != null) { - connection.disconnect(); - } - } + private OpenSourceLicensesUtils() { + // Utility class } - private static String extractLatestVersionChangelog(String markdown, String currentVersion) { - // Define the regex pattern to match the latest version section - String regexPattern = "(?m)^#\\s+Version\\s+" + Pattern.quote(currentVersion) + ":\\s*(.*?)^(#\\s+Version\\s+|$)"; - Pattern pattern = Pattern.compile(regexPattern, Pattern.DOTALL | Pattern.MULTILINE); - Matcher matcher = pattern.matcher(markdown); - - if (matcher.find()) { - // Group 1 contains the changelog for the current version - return Objects.requireNonNull(matcher.group(1)).trim(); - } else { - Log.e(TAG, "No changelog available for version " + currentVersion); - return "No changelog available for version " + currentVersion; + public static void openLicensesScreen(@Nullable Context context) { + if (context == null) { + return; } - } - private static String markdownToHtml(String markdown) { - Parser parser = Parser.builder().build(); - HtmlRenderer renderer = HtmlRenderer.builder().build(); - Node document = parser.parse(markdown); - return renderer.render(document); - } + OssLicensesMenuActivity.setActivityTitle(context.getString(R.string.open_source_licenses)); - private static String getAppVersion(Context context) { - try { - return context.getPackageManager() - .getPackageInfo(context.getPackageName(), 0) - .versionName; - } catch (Exception e) { - Log.e(TAG, "Error getting app version", e); - return "1.0.0"; // Fallback version + Intent intent = new Intent(context, OssLicensesMenuActivity.class); + if (!(context instanceof Activity)) { + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); } - } - public interface HtmlDataCallback { - void onHtmlDataLoaded(String changelogHtml, String eulaHtml); + context.startActivity(intent); } } \ No newline at end of file diff --git a/build.gradle b/build.gradle index 7363c4db..fd67164d 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,19 @@ +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + // The oss-licenses Gradle plugin still relies on XmlSlurper from groovy-xml when + // running on recent Gradle versions, so we provide it explicitly on the classpath. + classpath 'org.codehaus.groovy:groovy-xml:3.0.19' + } +} + plugins { alias(libs.plugins.androidApplication) apply false alias(libs.plugins.googleServices) apply false alias(libs.plugins.firebaseCrashlytics) apply false - alias(libs.plugins.aboutLibraries) apply true + alias(libs.plugins.ossLicenses) apply false alias(libs.plugins.hiltAndroid) apply false } diff --git a/docs/core/core-module.md b/docs/core/core-module.md index aa1db163..3e4819bc 100644 --- a/docs/core/core-module.md +++ b/docs/core/core-module.md @@ -11,7 +11,7 @@ Houses use case classes and other business logic that operate on repositories. Contains Activities, Fragments and ViewModels such as `MainViewModel`. ### utils -Provides helpers like `OpenSourceLicensesUtils`, `ReviewHelper` and `EdgeToEdgeDelegate`. +Provides helpers like `OpenSourceLicensesUtils` for launching the open source license screen, `ReviewHelper` and `EdgeToEdgeDelegate`. ### di Contains Hilt modules and qualifiers for dependency injection. diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 91b612e3..7740a22d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,11 +2,11 @@ agp = "8.13.0" firebaseCrashlyticsPlugin = "3.0.6" googleServices = "4.4.3" +ossLicensesPlugin = "0.10.6" appcompat = "1.7.1" appUpdate = "2.1.0" billing = "8.0.0" constraintlayout = "2.2.1" -aboutlibraries = "11.3.0" coreSplashscreen = "1.0.1" core = "4.6.2" coreTesting = "2.2.0" @@ -27,6 +27,7 @@ coreKtx = "1.17.0" material = "1.14.0-alpha04" multidex = "2.0.1" playServicesAds = "24.6.0" +playServicesOssLicenses = "17.3.0" codeview = "1.3.9" hilt = "2.57.1" room = "2.8.0" @@ -63,6 +64,7 @@ firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "fir firebase-crashlytics = { module = "com.google.firebase:firebase-crashlytics" } google-firebase-perf = { module = "com.google.firebase:firebase-perf" } play-services-ads = { module = "com.google.android.gms:play-services-ads", version.ref = "playServicesAds" } +play-services-oss-licenses = { module = "com.google.android.gms:play-services-oss-licenses", version.ref = "playServicesOssLicenses" } # Networking (Retrofit) retrofit2 = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" } @@ -73,7 +75,6 @@ hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hilt" hilt-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hilt" } # Utility -aboutlibraries = { module = "com.mikepenz:aboutlibraries", version.ref = "aboutlibraries" } app-update = { module = "com.google.android.play:app-update", version.ref = "appUpdate" } billing = { module = "com.android.billingclient:billing", version.ref = "billing" } core = { module = "io.noties.markwon:core", version.ref = "core" } @@ -94,14 +95,15 @@ mockito-inline = { module = "org.mockito:mockito-inline", version.ref = "mockito androidApplication = { id = "com.android.application", version.ref = "agp" } googleServices = { id = "com.google.gms.google-services", version.ref = "googleServices" } firebaseCrashlytics = { id = "com.google.firebase.crashlytics", version.ref = "firebaseCrashlyticsPlugin" } -aboutLibraries = { id = "com.mikepenz.aboutlibraries.plugin", version.ref = "aboutlibraries" } hiltAndroid = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } +ossLicenses = { id = "com.google.android.gms.oss-licenses-plugin", version.ref = "ossLicensesPlugin" } [bundles] # Google Play services and related UI components google-core = [ "material", "play-services-ads", + "play-services-oss-licenses", "review", "app-update", "billing", @@ -138,7 +140,6 @@ androidx-lifecycle = [ # Shared UI tooling and visuals ui-toolkit = [ - "aboutlibraries", "core", "lottie", "library", diff --git a/settings.gradle b/settings.gradle index e1bd2e89..77d4c0a7 100644 --- a/settings.gradle +++ b/settings.gradle @@ -8,7 +8,7 @@ pluginManagement { resolutionStrategy { eachPlugin { if (requested.id.id == 'com.google.android.gms.oss-licenses-plugin') { - useModule("com.google.android.gms:oss-licenses-plugin:${requested.version}") + useModule('com.google.android.gms:oss-licenses-plugin:0.10.6') } } }