Added barcode decoding

pull/214/head
M66B 2 years ago
parent 25a165002c
commit d2445c70af

@ -54,3 +54,4 @@ FairEmail uses parts or all of:
* [Brave Lists](https://github.com/brave/adblock-lists). [Mozilla Public License Version 2.0](https://github.com/brave/adblock-lists/blob/master/LICENSE). * [Brave Lists](https://github.com/brave/adblock-lists). [Mozilla Public License Version 2.0](https://github.com/brave/adblock-lists/blob/master/LICENSE).
* [AG Filters Registry](https://github.com/AdguardTeam/FiltersRegistry). [GNU Lesser General Public License Version 3](https://github.com/AdguardTeam/FiltersRegistry/blob/master/LICENSE). * [AG Filters Registry](https://github.com/AdguardTeam/FiltersRegistry). [GNU Lesser General Public License Version 3](https://github.com/AdguardTeam/FiltersRegistry/blob/master/LICENSE).
* [Certificate transparency for Android and JVM](https://github.com/appmattus/certificatetransparency). Copyright 2023 Appmattus Limited. [Apache License 2.0](https://github.com/appmattus/certificatetransparency/blob/main/LICENSE.md). * [Certificate transparency for Android and JVM](https://github.com/appmattus/certificatetransparency). Copyright 2023 Appmattus Limited. [Apache License 2.0](https://github.com/appmattus/certificatetransparency/blob/main/LICENSE.md).
* [ZXing](https://github.com/zxing/zxing). Copyright (C) 2014 ZXing authors. [Apache License 2.0](https://github.com/zxing/zxing/blob/master/LICENSE).

@ -572,6 +572,7 @@ dependencies {
def canary_version = "2.13" def canary_version = "2.13"
def ws_version = "2.14" def ws_version = "2.14"
def tinylog_version = "2.6.2" def tinylog_version = "2.6.2"
def zxing_version = "3.5.3"
// https://mvnrepository.com/artifact/com.android.tools/desugar_jdk_libs?repo=google // https://mvnrepository.com/artifact/com.android.tools/desugar_jdk_libs?repo=google
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:$desugar_version" coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:$desugar_version"
@ -828,4 +829,8 @@ dependencies {
// https://mvnrepository.com/artifact/org.tinylog // https://mvnrepository.com/artifact/org.tinylog
implementation "org.tinylog:tinylog-api:$tinylog_version" implementation "org.tinylog:tinylog-api:$tinylog_version"
implementation "org.tinylog:tinylog-impl:$tinylog_version" implementation "org.tinylog:tinylog-impl:$tinylog_version"
// https://github.com/zxing/zxing
// https://mvnrepository.com/artifact/com.google.zxing/core
implementation "com.google.zxing:core:$zxing_version"
} }

@ -54,3 +54,4 @@ FairEmail uses parts or all of:
* [Brave Lists](https://github.com/brave/adblock-lists). [Mozilla Public License Version 2.0](https://github.com/brave/adblock-lists/blob/master/LICENSE). * [Brave Lists](https://github.com/brave/adblock-lists). [Mozilla Public License Version 2.0](https://github.com/brave/adblock-lists/blob/master/LICENSE).
* [AG Filters Registry](https://github.com/AdguardTeam/FiltersRegistry). [GNU Lesser General Public License Version 3](https://github.com/AdguardTeam/FiltersRegistry/blob/master/LICENSE). * [AG Filters Registry](https://github.com/AdguardTeam/FiltersRegistry). [GNU Lesser General Public License Version 3](https://github.com/AdguardTeam/FiltersRegistry/blob/master/LICENSE).
* [Certificate transparency for Android and JVM](https://github.com/appmattus/certificatetransparency). Copyright 2023 Appmattus Limited. [Apache License 2.0](https://github.com/appmattus/certificatetransparency/blob/main/LICENSE.md). * [Certificate transparency for Android and JVM](https://github.com/appmattus/certificatetransparency). Copyright 2023 Appmattus Limited. [Apache License 2.0](https://github.com/appmattus/certificatetransparency/blob/main/LICENSE.md).
* [ZXing](https://github.com/zxing/zxing). Copyright (C) 2014 ZXing authors. [Apache License 2.0](https://github.com/zxing/zxing/blob/master/LICENSE).

@ -25,6 +25,7 @@ import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.graphics.pdf.PdfRenderer; import android.graphics.pdf.PdfRenderer;
@ -35,7 +36,10 @@ import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.provider.MediaStore; import android.provider.MediaStore;
import android.text.SpannableStringBuilder;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.style.StyleSpan;
import android.text.style.URLSpan;
import android.util.Pair; import android.util.Pair;
import android.util.Size; import android.util.Size;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -45,6 +49,7 @@ import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.core.text.method.LinkMovementMethodCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.lifecycle.Lifecycle; import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.LifecycleObserver;
@ -55,7 +60,16 @@ import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListUpdateCallback; import androidx.recyclerview.widget.ListUpdateCallback;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.NotFoundException;
import com.google.zxing.RGBLuminanceSource;
import com.google.zxing.Result;
import com.google.zxing.common.HybridBinarizer;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
@ -84,6 +98,7 @@ public class AdapterMedia extends RecyclerView.Adapter<AdapterMedia.ViewHolder>
ivImage = itemView.findViewById(R.id.ivImage); ivImage = itemView.findViewById(R.id.ivImage);
tvCaption = itemView.findViewById(R.id.tvCaption); tvCaption = itemView.findViewById(R.id.tvCaption);
tvProperties = itemView.findViewById(R.id.tvProperties); tvProperties = itemView.findViewById(R.id.tvProperties);
tvProperties.setMovementMethod(LinkMovementMethodCompat.getInstance());
} }
private void wire() { private void wire() {
@ -180,6 +195,7 @@ public class AdapterMedia extends RecyclerView.Adapter<AdapterMedia.ViewHolder>
} else { } else {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
boolean webp = prefs.getBoolean("webp", true); boolean webp = prefs.getBoolean("webp", true);
boolean barcode_preview = prefs.getBoolean("barcode_preview", true);
if ("image/webp".equalsIgnoreCase(type) && !webp) if ("image/webp".equalsIgnoreCase(type) && !webp)
return context.getDrawable(R.drawable.twotone_image_not_supported_24); return context.getDrawable(R.drawable.twotone_image_not_supported_24);
@ -200,6 +216,24 @@ public class AdapterMedia extends RecyclerView.Adapter<AdapterMedia.ViewHolder>
Log.w(ex); Log.w(ex);
} }
if (barcode_preview)
try (InputStream is = new FileInputStream(file)) {
Bitmap bitmap = BitmapFactory.decodeStream(is);
int width = bitmap.getWidth(), height = bitmap.getHeight();
int[] pixels = new int[width * height];
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
RGBLuminanceSource source = new RGBLuminanceSource(width, height, pixels);
BinaryBitmap bBitmap = new BinaryBitmap(new HybridBinarizer(source));
MultiFormatReader reader = new MultiFormatReader();
Result result = reader.decode(bBitmap);
args.putString("barcode", result.getText());
} catch (NotFoundException ex) {
Log.w(ex);
} catch (Throwable ex) {
Log.e(ex);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P &&
!"image/svg+xml".equals(type) && !"image/svg+xml".equals(type) &&
!"svg".equals(Helper.getExtension(file.getName()))) !"svg".equals(Helper.getExtension(file.getName())))
@ -233,47 +267,65 @@ public class AdapterMedia extends RecyclerView.Adapter<AdapterMedia.ViewHolder>
ImageHelper.animate(context, image); ImageHelper.animate(context, image);
} }
StringBuilder sb = new StringBuilder(); SpannableStringBuilder ssb = new SpannableStringBuilderEx();
int width = args.getInt("width"); int width = args.getInt("width");
int height = args.getInt("height"); int height = args.getInt("height");
if (width > 0 && height > 0) if (width > 0 && height > 0)
sb.append(width) ssb.append(Integer.toString(width))
.append("\u00d7") // × .append("\u00d7") // ×
.append(height); .append(Integer.toString(height));
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
String color = args.getString("color"); String color = args.getString("color");
if (color != null) { if (color != null) {
if (sb.length() > 0) if (ssb.length() > 0)
sb.append(' '); ssb.append(' ');
sb.append(color); ssb.append(color);
} }
String config = args.getString("config"); String config = args.getString("config");
if (config != null) { if (config != null) {
if (sb.length() > 0) if (ssb.length() > 0)
sb.append(' '); ssb.append(' ');
sb.append(config); ssb.append(config);
} }
} }
long size = args.getLong("size"); long size = args.getLong("size");
if (size > 0) { if (size > 0) {
if (sb.length() > 0) if (ssb.length() > 0)
sb.append(' '); ssb.append(' ');
sb.append(Helper.humanReadableByteCount(size)); ssb.append(Helper.humanReadableByteCount(size));
} }
int duration = args.getInt("duration"); int duration = args.getInt("duration");
if (duration > 0) { if (duration > 0) {
if (sb.length() > 0) if (ssb.length() > 0)
sb.append(' '); ssb.append(' ');
sb.append(Helper.formatDuration(duration)); ssb.append(Helper.formatDuration(duration));
}
String barcode = args.getString("barcode");
if (!TextUtils.isEmpty(barcode)) {
if (ssb.length() > 0)
ssb.append('\n');
int start = ssb.length();
ssb.append(barcode);
try {
Uri uri = UriHelper.guessScheme(Uri.parse(barcode));
if (UriHelper.isHyperLink(uri) || UriHelper.isMail(uri))
ssb.setSpan(new URLSpan(uri.toString()), start, ssb.length(), 0);
else
ssb.setSpan(new StyleSpan(Typeface.BOLD), start, ssb.length(), 0);
} catch (Throwable ex) {
Log.e(ex);
}
} }
if (sb.length() > 0) { if (ssb.length() > 0) {
tvProperties.setText(sb); tvProperties.setText(ssb);
tvProperties.setVisibility(View.VISIBLE); tvProperties.setVisibility(View.VISIBLE);
} }
} }

@ -155,7 +155,7 @@ public class FragmentOptions extends FragmentBase {
"subject_top", "subject_italic", "highlight_subject", "font_size_subject", "subject_ellipsize", "subject_top", "subject_italic", "highlight_subject", "font_size_subject", "subject_ellipsize",
"keywords_header", "labels_header", "flags", "flags_background", "preview", "preview_italic", "preview_lines", "align_header", "keywords_header", "labels_header", "flags", "flags_background", "preview", "preview_italic", "preview_lines", "align_header",
"message_zoom", "overview_mode", "addresses", "button_extra", "attachments_alt", "message_zoom", "overview_mode", "addresses", "button_extra", "attachments_alt",
"thumbnails", "pdf_preview", "video_preview", "audio_preview", "thumbnails", "pdf_preview", "video_preview", "audio_preview", "barcode_preview",
"contrast", "hyphenation", "display_font", "monospaced_pre", "contrast", "hyphenation", "display_font", "monospaced_pre",
"list_count", "bundled_fonts", "narrow_fonts", "parse_classes", "list_count", "bundled_fonts", "narrow_fonts", "parse_classes",
"background_color", "text_color", "text_size", "text_font", "text_align", "text_titles", "text_separators", "background_color", "text_color", "text_size", "text_font", "text_align", "text_titles", "text_separators",

@ -187,6 +187,7 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer
private SwitchCompat swPdfPreview; private SwitchCompat swPdfPreview;
private SwitchCompat swVideoPreview; private SwitchCompat swVideoPreview;
private SwitchCompat swAudioPreview; private SwitchCompat swAudioPreview;
private SwitchCompat swBarcodePreview;
private SwitchCompat swListCount; private SwitchCompat swListCount;
private SwitchCompat swBundledFonts; private SwitchCompat swBundledFonts;
@ -226,7 +227,7 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer
"hyphenation", "display_font", "contrast", "monospaced_pre", "hyphenation", "display_font", "contrast", "monospaced_pre",
"text_separators", "text_separators",
"collapse_quotes", "image_placeholders", "inline_images", "button_extra", "collapse_quotes", "image_placeholders", "inline_images", "button_extra",
"unzip", "attachments_alt", "thumbnails", "pdf_preview", "video_preview", "audio_preview", "unzip", "attachments_alt", "thumbnails", "pdf_preview", "video_preview", "audio_preview", "barcode_preview",
"list_count", "bundled_fonts", "narrow_fonts", "parse_classes", "list_count", "bundled_fonts", "narrow_fonts", "parse_classes",
"background_color", "text_color", "text_size", "text_font", "text_align", "text_titles", "background_color", "text_color", "text_size", "text_font", "text_align", "text_titles",
"authentication", "authentication_indicator" "authentication", "authentication_indicator"
@ -361,6 +362,7 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer
swPdfPreview = view.findViewById(R.id.swPdfPreview); swPdfPreview = view.findViewById(R.id.swPdfPreview);
swVideoPreview = view.findViewById(R.id.swVideoPreview); swVideoPreview = view.findViewById(R.id.swVideoPreview);
swAudioPreview = view.findViewById(R.id.swAudioPreview); swAudioPreview = view.findViewById(R.id.swAudioPreview);
swBarcodePreview = view.findViewById(R.id.swBarcodePreview);
swListCount = view.findViewById(R.id.swListCount); swListCount = view.findViewById(R.id.swListCount);
swBundledFonts = view.findViewById(R.id.swBundledFonts); swBundledFonts = view.findViewById(R.id.swBundledFonts);
@ -1324,6 +1326,7 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer
swPdfPreview.setEnabled(checked); swPdfPreview.setEnabled(checked);
swVideoPreview.setEnabled(checked); swVideoPreview.setEnabled(checked);
swAudioPreview.setEnabled(checked); swAudioPreview.setEnabled(checked);
swBarcodePreview.setEnabled(checked);
} }
}); });
@ -1348,6 +1351,13 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer
} }
}); });
swBarcodePreview.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
prefs.edit().putBoolean("barcode_preview", checked).apply();
}
});
swListCount.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { swListCount.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override @Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
@ -1695,6 +1705,8 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer
swVideoPreview.setEnabled(swThumbnails.isChecked()); swVideoPreview.setEnabled(swThumbnails.isChecked());
swAudioPreview.setChecked(prefs.getBoolean("audio_preview", true)); swAudioPreview.setChecked(prefs.getBoolean("audio_preview", true));
swAudioPreview.setEnabled(swThumbnails.isChecked()); swAudioPreview.setEnabled(swThumbnails.isChecked());
swBarcodePreview.setChecked(prefs.getBoolean("barcode_preview", true));
swBarcodePreview.setEnabled(swThumbnails.isChecked());
swListCount.setChecked(prefs.getBoolean("list_count", false)); swListCount.setChecked(prefs.getBoolean("list_count", false));
swBundledFonts.setChecked(prefs.getBoolean("bundled_fonts", true)); swBundledFonts.setChecked(prefs.getBoolean("bundled_fonts", true));

@ -2084,6 +2084,18 @@
app:layout_constraintTop_toBottomOf="@id/swVideoPreview" app:layout_constraintTop_toBottomOf="@id/swVideoPreview"
app:switchPadding="12dp" /> app:switchPadding="12dp" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/swBarcodePreview"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
android:text="@string/title_advanced_barcode_preview"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/swAudioPreview"
app:switchPadding="12dp" />
<androidx.constraintlayout.widget.Group <androidx.constraintlayout.widget.Group
android:id="@+id/grpUnzip" android:id="@+id/grpUnzip"
android:layout_width="0dp" android:layout_width="0dp"

@ -639,6 +639,7 @@
<string name="title_advanced_pdf_preview">Show PDF thumbnails</string> <string name="title_advanced_pdf_preview">Show PDF thumbnails</string>
<string name="title_advanced_video_preview">Show video thumbnails</string> <string name="title_advanced_video_preview">Show video thumbnails</string>
<string name="title_advanced_audio_preview">Show mini audio player</string> <string name="title_advanced_audio_preview">Show mini audio player</string>
<string name="title_advanced_barcode_preview">Show barcode content</string>
<string name="title_advanced_message_text_zoom2">Default message text zoom: %1$s %%</string> <string name="title_advanced_message_text_zoom2">Default message text zoom: %1$s %%</string>
<string name="title_advanced_editor_text_zoom">Zoom message text also in the message editor</string> <string name="title_advanced_editor_text_zoom">Zoom message text also in the message editor</string>
<string name="title_advanced_overview_mode">Zoom original messages to fit the screen</string> <string name="title_advanced_overview_mode">Zoom original messages to fit the screen</string>

Loading…
Cancel
Save