From 171a2a81f2b40cfcf2b7a724eaa88108674959dd Mon Sep 17 00:00:00 2001 From: Marcel Dopita Date: Fri, 20 Dec 2024 22:55:23 +0100 Subject: [PATCH] Replace ICU4J with chardet4j #76 --- README.md | 2 +- app/build.gradle | 4 +- .../java/com/brouken/player/UtilsFeature.java | 70 ------------------- .../java/com/brouken/player/UtilsFeature.java | 11 --- .../com/brouken/player/PlayerActivity.java | 2 +- .../com/brouken/player/SubtitleFetcher.java | 2 +- .../main/java/com/brouken/player/Utils.java | 65 ++++++++++++++++- 7 files changed, 69 insertions(+), 87 deletions(-) diff --git a/README.md b/README.md index 9e7e56bb..e42060bf 100644 --- a/README.md +++ b/README.md @@ -148,7 +148,7 @@ Just pause and resume playback once again. ### Why is the APK so big? -The APK available here contains native libraries for all supported architectures (`armeabi-v7a`/`armeabi-v7a-neon`/`arm64-v8a`/`x86`/`x86_64`), which is what takes the most space. Although Just Player relies mostly on device decoders, it packs _FFmpeg_ for some advanced features (video chapters and frame rate detection). The second largest dependency is [ICU4J](https://github.com/moneytoo/Player/issues/76) - 10 MB only for charset detection of subtitle files. 🤷 +The APK available here contains native libraries for all supported architectures (`armeabi-v7a`/`armeabi-v7a-neon`/`arm64-v8a`/`x86`/`x86_64`), which is what takes the most space. Although Just Player relies mostly on device decoders, it packs _FFmpeg_ for some advanced features (video chapters and frame rate detection). Please note that installs and updates made through Google Play are significantly smaller thanks to Android App Bundles and delta updates. diff --git a/app/build.gradle b/app/build.gradle index 2e3259b2..81ba0fcc 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -12,7 +12,7 @@ android { applicationId "com.brouken.player" minSdkVersion 21 targetSdkVersion 34 - versionCode 181 + versionCode 182 versionName "0.${versionCode}" archivesBaseName = "Just.Player.v${versionName}" if (abiFilter) { @@ -117,11 +117,11 @@ dependencies { implementation 'com.google.android.material:material:1.12.0' implementation 'androidx.coordinatorlayout:coordinatorlayout:1.2.0' implementation "androidx.core:core:$androidxCoreVersion" - fullImplementation 'com.ibm.icu:icu4j:76.1' fullImplementation 'com.arthenica:ffmpeg-kit-https:6.0-2.LTS' implementation 'androidx.appcompat:appcompat:1.7.0' implementation 'androidx.preference:preference:1.2.1' implementation 'com.squareup.okhttp3:okhttp:4.12.0' + implementation 'com.sigpwned:chardet4j:75.1.2' implementation project(path: ':doubletapplayerview') implementation project(path: ':android-file-chooser') implementation fileTree(dir: "libs", include: ["lib-*.aar"]) diff --git a/app/src/full/java/com/brouken/player/UtilsFeature.java b/app/src/full/java/com/brouken/player/UtilsFeature.java index 0fd63351..75cf6563 100644 --- a/app/src/full/java/com/brouken/player/UtilsFeature.java +++ b/app/src/full/java/com/brouken/player/UtilsFeature.java @@ -2,7 +2,6 @@ import android.app.Activity; import android.content.ContentResolver; -import android.content.Context; import android.net.Uri; import android.os.Build; @@ -15,80 +14,11 @@ import com.arthenica.ffmpegkit.MediaInformation; import com.arthenica.ffmpegkit.MediaInformationSession; import com.arthenica.ffmpegkit.StreamInformation; -import com.ibm.icu.text.CharsetDetector; -import com.ibm.icu.text.CharsetMatch; -import java.io.BufferedInputStream; -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; import java.util.List; public class UtilsFeature { - public static Uri convertToUTF(PlayerActivity activity, Uri subtitleUri) { - try { - String scheme = subtitleUri.getScheme(); - if (scheme != null && scheme.toLowerCase().startsWith("http")) { - List urls = new ArrayList<>(); - urls.add(subtitleUri); - SubtitleFetcher subtitleFetcher = new SubtitleFetcher(activity, urls); - subtitleFetcher.start(); - return null; - } else { - InputStream inputStream = activity.getContentResolver().openInputStream(subtitleUri); - return convertInputStreamToUTF(activity, subtitleUri, inputStream); - } - } catch (Exception e) { - e.printStackTrace(); - } - return subtitleUri; - } - - public static Uri convertInputStreamToUTF(Context context, Uri subtitleUri, InputStream inputStream) { - try { - final CharsetDetector detector = new CharsetDetector(); - final BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream); - detector.setText(bufferedInputStream); - final CharsetMatch charsetMatch = detector.detect(); - - if (!StandardCharsets.UTF_8.displayName().equals(charsetMatch.getName())) { - String filename = subtitleUri.getPath(); - filename = filename.substring(filename.lastIndexOf("/") + 1); - final File file = new File(context.getCacheDir(), filename); - final BufferedReader bufferedReader = new BufferedReader(charsetMatch.getReader()); - final BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file)); - char[] buffer = new char[512]; - int num; - int pass = 0; - boolean success = true; - while ((num = bufferedReader.read(buffer)) != -1) { - bufferedWriter.write(buffer, 0, num); - pass++; - if (pass * 512 > 2_000_000) { - success = false; - break; - } - } - bufferedWriter.close(); - bufferedReader.close(); - if (success) { - subtitleUri = Uri.fromFile(file); - } else { - subtitleUri = null; - } - } - } catch (IOException e) { - e.printStackTrace(); - } - return subtitleUri; - } - private static MediaInformation getMediaInformation(final Activity activity, final Uri uri) { String path; if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) { diff --git a/app/src/lite/java/com/brouken/player/UtilsFeature.java b/app/src/lite/java/com/brouken/player/UtilsFeature.java index bfcc69f8..1fa4f3f3 100644 --- a/app/src/lite/java/com/brouken/player/UtilsFeature.java +++ b/app/src/lite/java/com/brouken/player/UtilsFeature.java @@ -1,22 +1,11 @@ package com.brouken.player; -import android.content.Context; import android.net.Uri; import androidx.media3.ui.PlayerControlView; -import java.io.InputStream; - public class UtilsFeature { - public static Uri convertToUTF(PlayerActivity activity, Uri subtitleUri) { - return subtitleUri; - } - - public static Uri convertInputStreamToUTF(Context context, Uri subtitleUri, InputStream inputStream) { - return subtitleUri; - } - public static boolean switchFrameRate(final PlayerActivity activity, final Uri uri, final boolean play) { return false; } diff --git a/app/src/main/java/com/brouken/player/PlayerActivity.java b/app/src/main/java/com/brouken/player/PlayerActivity.java index c57ac635..b5209496 100644 --- a/app/src/main/java/com/brouken/player/PlayerActivity.java +++ b/app/src/main/java/com/brouken/player/PlayerActivity.java @@ -1154,7 +1154,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { private void handleSubtitles(Uri uri) { // Convert subtitles to UTF-8 if necessary SubtitleUtils.clearCache(this); - uri = UtilsFeature.convertToUTF(this, uri); + uri = Utils.convertToUTF(this, uri); mPrefs.updateSubtitle(uri); } diff --git a/app/src/main/java/com/brouken/player/SubtitleFetcher.java b/app/src/main/java/com/brouken/player/SubtitleFetcher.java index 0f9f175a..7a33ea5c 100644 --- a/app/src/main/java/com/brouken/player/SubtitleFetcher.java +++ b/app/src/main/java/com/brouken/player/SubtitleFetcher.java @@ -108,7 +108,7 @@ public void onResponse(@NonNull Call call, @NonNull Response response) throws IO } InputStream inputStream = responseBody.byteStream(); - Uri convertedSubtitleUri = UtilsFeature.convertInputStreamToUTF(activity, subtitleUri, inputStream); + Uri convertedSubtitleUri = Utils.convertInputStreamToUTF(activity, subtitleUri, inputStream); if (convertedSubtitleUri == null) { return; diff --git a/app/src/main/java/com/brouken/player/Utils.java b/app/src/main/java/com/brouken/player/Utils.java index 41b967fa..8a02f5c4 100644 --- a/app/src/main/java/com/brouken/player/Utils.java +++ b/app/src/main/java/com/brouken/player/Utils.java @@ -46,11 +46,19 @@ import androidx.media3.common.MimeTypes; import com.obsez.android.lib.filechooser.ChooserDialog; +import com.sigpwned.chardet4j.Chardet; +import com.sigpwned.chardet4j.io.DecodedInputStreamReader; +import java.io.BufferedReader; +import java.io.BufferedWriter; import java.io.File; +import java.io.FileWriter; +import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Constructor; import java.lang.reflect.Method; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -603,7 +611,7 @@ public void onChoosePath(String path, File pathFile) { } else { // Convert subtitles to UTF-8 if necessary SubtitleUtils.clearCache(activity); - uri = UtilsFeature.convertToUTF(activity, uri); + uri = Utils.convertToUTF(activity, uri); activity.mPrefs.updateSubtitle(uri); } @@ -625,6 +633,61 @@ public void onCancel(DialogInterface dialog) { return true; } + public static Uri convertToUTF(PlayerActivity activity, Uri subtitleUri) { + try { + String scheme = subtitleUri.getScheme(); + if (scheme != null && scheme.toLowerCase().startsWith("http")) { + List urls = new ArrayList<>(); + urls.add(subtitleUri); + SubtitleFetcher subtitleFetcher = new SubtitleFetcher(activity, urls); + subtitleFetcher.start(); + return null; + } else { + InputStream inputStream = activity.getContentResolver().openInputStream(subtitleUri); + return convertInputStreamToUTF(activity, subtitleUri, inputStream); + } + } catch (Exception e) { + e.printStackTrace(); + } + return subtitleUri; + } + + public static Uri convertInputStreamToUTF(Context context, Uri subtitleUri, InputStream inputStream) { + try { + DecodedInputStreamReader decodedInputStreamReader = Chardet.decode(inputStream, StandardCharsets.UTF_8); + Charset charset = decodedInputStreamReader.charset(); + if (!StandardCharsets.UTF_8.equals(charset)) { + String filename = subtitleUri.getPath(); + filename = filename.substring(filename.lastIndexOf("/") + 1); + final File file = new File(context.getCacheDir(), filename); + final BufferedReader bufferedReader = new BufferedReader(decodedInputStreamReader); + final BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file)); + char[] buffer = new char[512]; + int num; + int pass = 0; + boolean success = true; + while ((num = bufferedReader.read(buffer)) != -1) { + bufferedWriter.write(buffer, 0, num); + pass++; + if (pass * 512 > 2_000_000) { + success = false; + break; + } + } + bufferedWriter.close(); + bufferedReader.close(); + if (success) { + subtitleUri = Uri.fromFile(file); + } else { + subtitleUri = null; + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return subtitleUri; + } + public static boolean isPiPSupported(Context context) { PackageManager packageManager = context.getPackageManager(); if (BuildConfig.FLAVOR_distribution.equals("amazon") && packageManager.hasSystemFeature(FEATURE_FIRE_TV)) {