summaryrefslogtreecommitdiffstats
path: root/quickReader/src/org/lineageos/quickreader
diff options
context:
space:
mode:
authorJoey Rizzoli <joey@lineageos.org>2017-12-15 17:13:21 +0100
committerBruno Martins <bgcngm@gmail.com>2018-11-20 12:32:17 +0000
commitfe30f91881b9b7d76a9c404bafcbe19f240e0a61 (patch)
tree941fc58fd38a5ec42373b708ff98cc9269823913 /quickReader/src/org/lineageos/quickreader
parentbec37cd5493c672007ef88f03d77e02d2a708176 (diff)
downloadandroid_packages_apps_Snap-fe30f91881b9b7d76a9c404bafcbe19f240e0a61.tar.gz
android_packages_apps_Snap-fe30f91881b9b7d76a9c404bafcbe19f240e0a61.tar.bz2
android_packages_apps_Snap-fe30f91881b9b7d76a9c404bafcbe19f240e0a61.zip
QuickReader: initial commit
libs from me.dm7.barcodescanner:zxing:1.9.0 maven cache Change-Id: Ib97b7e54d8e28cd6f637e452eb41045052cae3dc Signed-off-by: Joey Rizzoli <joey@lineageos.org>
Diffstat (limited to 'quickReader/src/org/lineageos/quickreader')
-rw-r--r--quickReader/src/org/lineageos/quickreader/ScannerActivity.java321
-rw-r--r--quickReader/src/org/lineageos/quickreader/ScannerIntentHelper.java238
2 files changed, 559 insertions, 0 deletions
diff --git a/quickReader/src/org/lineageos/quickreader/ScannerActivity.java b/quickReader/src/org/lineageos/quickreader/ScannerActivity.java
new file mode 100644
index 000000000..fb583a24a
--- /dev/null
+++ b/quickReader/src/org/lineageos/quickreader/ScannerActivity.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2017 The LineageOS 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.
+ */
+package org.lineageos.quickreader;
+
+import android.Manifest;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Patterns;
+import android.webkit.URLUtil;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import com.google.zxing.Result;
+
+import java.util.concurrent.ExecutionException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.android.camera.CameraActivity;
+import me.dm7.barcodescanner.zxing.ZXingScannerView;
+
+import org.codeaurora.snapcam.R;
+
+public class ScannerActivity extends Activity implements ZXingScannerView.ResultHandler {
+ private static final String TAG = "QuickReader";
+ private static final int REQUEST_CAMERA = 391;
+ public static final Pattern ACCEPTED_URI_SCHEMA = Pattern.compile(
+ "(?i)" + // switch on case insensitive matching
+ "(" + // begin group for schema
+ "(?:http|https|file|chrome)://" +
+ "|(?:inline|data|about|javascript):" +
+ ")" +
+ "(.*)"
+ );
+
+ private static ScannerIntentHelper sHelper;
+
+ private AnalyzeTask task;
+
+ private ZXingScannerView mScanView;
+ private FrameLayout mIdentifyLayout;
+ private ImageView mIdentifyIcon;
+ private ImageView mFlashIcon;
+
+ private boolean allowReading = true;
+
+ @Override
+ protected void onCreate(Bundle savedInstance) {
+ super.onCreate(savedInstance);
+
+ setContentView(R.layout.activity_scanner);
+
+ mScanView = (ZXingScannerView) findViewById(R.id.scanner_view);
+ mIdentifyLayout = (FrameLayout) findViewById(R.id.identify_layout);
+ mIdentifyIcon = (ImageView) findViewById(R.id.identify_icon);
+ mFlashIcon = (ImageView) findViewById(R.id.action_flash);
+ ImageView closeIcon = (ImageView) findViewById(R.id.action_close);
+
+ mIdentifyLayout.setOnClickListener(v -> sHelper.run(this));
+ mFlashIcon.setOnClickListener(v -> toggleFlash());
+ closeIcon.setOnClickListener(v -> finish());
+
+ if (!hasCameraPermission()) {
+ requestPermission();
+ }
+
+ sHelper = ScannerIntentHelper.getInstance();
+ task = new AnalyzeTask();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ mScanView.setResultHandler(this);
+ mScanView.startCamera();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+
+ try {
+ if (mScanView.getFlash()) {
+ mScanView.setFlash(false);
+ mFlashIcon.setImageResource(R.drawable.ic_flash_off);
+ }
+ } catch (Exception ignored) {
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+
+ mScanView.stopCamera();
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, String[] permissions,
+ int[] grantResults) {
+ if (requestCode != REQUEST_CAMERA || permissions.length == 0 || grantResults.length == 0) {
+ return;
+ }
+
+ if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
+ if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {
+ showRationaleErrorDialog();
+ } else {
+ showFatalErrorDialog();
+ }
+ }
+ }
+
+ @Override
+ public void handleResult(Result rawResult) {
+ mScanView.resumeCameraPreview(this);
+
+ if (!allowReading) {
+ return;
+ }
+
+ if (task.getStatus() == AsyncTask.Status.RUNNING) {
+ return;
+ }
+
+ allowReading = false;
+ task = new AnalyzeTask();
+ task.execute(rawResult.getText());
+
+ try {
+ int result = task.get();
+ postAnalyze(result);
+ allowReading = true;
+ } catch (InterruptedException | ExecutionException e) {
+ Log.e(TAG, e.getMessage());
+ }
+ }
+
+ private boolean hasCameraPermission() {
+ return checkSelfPermission(Manifest.permission.CAMERA) ==
+ PackageManager.PERMISSION_GRANTED;
+ }
+
+ private void requestPermission() {
+ requestPermissions(new String[] { Manifest.permission.CAMERA }, REQUEST_CAMERA);
+ }
+
+ private void openSettings() {
+ Uri uri = Uri.fromParts("package", getPackageName(), null);
+ Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, uri);
+ startActivity(intent);
+ }
+
+ private void showRationaleErrorDialog() {
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.quick_reader_permission_error_title)
+ .setMessage(R.string.quick_reader_permission_rationale_message)
+ .setPositiveButton(R.string.quick_reader_permission_rationale_positive,
+ (dialog, i) -> requestPermission())
+ .setNegativeButton(R.string.quick_reader_action_dismiss,
+ (dialog, i) -> finish())
+ .show();
+ }
+
+ private void showFatalErrorDialog() {
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.quick_reader_permission_error_title)
+ .setMessage(R.string.quick_reader_permission_fatal_message)
+ .setPositiveButton(R.string.quick_reader_permission_fatal_positive,
+ (dialog, i) -> openSettings())
+ .setNegativeButton(R.string.quick_reader_action_dismiss,
+ (dialog, i) -> finish())
+ .show();
+ }
+
+ private void postAnalyze(int result) {
+ if (result == 0 || !sHelper.isValid()) {
+ return;
+ }
+
+ showScanResult(result);
+ }
+
+ private void showScanResult(int imageResource) {
+ mIdentifyIcon.setImageResource(imageResource);
+
+ if (mIdentifyLayout.getScaleX() == 0) {
+ // First shown, animate
+ mIdentifyLayout.animate()
+ .scaleX(1)
+ .scaleY(1)
+ .start();
+ }
+ }
+
+ private void toggleFlash() {
+ boolean isEnabled = mScanView.getFlash();
+ mScanView.setFlash(!isEnabled);
+ mFlashIcon.setImageResource(isEnabled ? R.drawable.ic_flash_off : R.drawable.ic_flash_on);
+ }
+
+ private static class AnalyzeTask extends AsyncTask<String, Void, Integer> {
+ private static final String ID_SMS = "smsto:";
+ private static final String ID_EMAIL = "mailto:";
+ private static final String ID_PHONE = "tel:";
+ private static final String ID_LOCATION = "geo:";
+ private static final String ID_VCARD = "BEGIN:VCARD";
+ private static final String ID_VEVENT = "BEGIN:VEVENT";
+ private static final String ID_MECARD = "MECARD:";
+ private static final String ID_MEBKM_URL = "MEBKM:TITLE:";
+
+ @Override
+ protected Integer doInBackground(String... params) {
+ if (params.length == 0) {
+ return null;
+ }
+
+ String content = params[0];
+ int icon;
+
+ if (TextUtils.isEmpty(content)) {
+ return null;
+ }
+
+ sHelper.reset();
+
+ if (content.startsWith(ID_SMS)) {
+ icon = R.drawable.ic_sms;
+ sHelper.setSMSIntent(content);
+ } else if (content.startsWith(ID_EMAIL)) {
+ icon = R.drawable.ic_email;
+ } else if (content.startsWith(ID_PHONE)) {
+ icon = R.drawable.ic_phone;
+ sHelper.setUriIntent(content);
+ } else if (content.startsWith(ID_LOCATION)) {
+ sHelper.setUriIntent(content);
+ icon = R.drawable.ic_location;
+ } else if (content.startsWith(ID_VEVENT)) {
+ sHelper.setCalendarIntent(content);
+ icon = R.drawable.ic_event;
+ } else if (content.startsWith(ID_MECARD)) {
+ sHelper.setContactIntent(content.replace(ID_MECARD, ""), false);
+ icon = R.drawable.ic_contact;
+ } else if (content.startsWith(ID_VCARD)) {
+ sHelper.setContactIntent(content, true);
+ icon = R.drawable.ic_contact;
+ } else if (content.startsWith(ID_MEBKM_URL)) {
+ sHelper.setMeBkmUrl(content);
+ icon = R.drawable.ic_http;
+ } else {
+ String properContent = smartUrlFilter(content);
+
+ if (TextUtils.isEmpty(properContent)) {
+ // Fallback to plain text
+ sHelper.setText(content);
+ return R.drawable.ic_text;
+ }
+
+ sHelper.setUriIntent(properContent);
+ icon = R.drawable.ic_http;
+ }
+
+ return icon;
+ }
+
+ /**
+ * Attempts to determine whether user input is a URL or search
+ * terms. Anything with a space is passed to search if canBeSearch is true.
+ *
+ * Converts to lowercase any mistakenly uppercased schema (i.e.,
+ * "Http://" converts to "http://"
+ *
+ * @return Original or modified URL
+ *
+ */
+ private String smartUrlFilter(String url) {
+ String inUrl = url.trim();
+ boolean hasSpace = inUrl.indexOf(' ') != -1;
+
+ Matcher matcher = ACCEPTED_URI_SCHEMA.matcher(inUrl);
+ if (matcher.matches()) {
+ // force scheme to lowercase
+ String scheme = matcher.group(1);
+ String lcScheme = scheme.toLowerCase();
+ if (!lcScheme.equals(scheme)) {
+ inUrl = lcScheme + matcher.group(2);
+ }
+ if (hasSpace && Patterns.WEB_URL.matcher(inUrl).matches()) {
+ inUrl = inUrl.replace(" ", "%20");
+ }
+ return inUrl;
+ }
+ if (!hasSpace && Patterns.WEB_URL.matcher(inUrl).matches()) {
+ return URLUtil.guessUrl(inUrl);
+ }
+ return null;
+ }
+ }
+} \ No newline at end of file
diff --git a/quickReader/src/org/lineageos/quickreader/ScannerIntentHelper.java b/quickReader/src/org/lineageos/quickreader/ScannerIntentHelper.java
new file mode 100644
index 000000000..c8fa500c1
--- /dev/null
+++ b/quickReader/src/org/lineageos/quickreader/ScannerIntentHelper.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2017 The LineageOS 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.
+ */
+package org.lineageos.quickreader;
+
+import android.app.AlertDialog;
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.provider.CalendarContract;
+import android.provider.ContactsContract;
+import android.text.TextUtils;
+import android.widget.Toast;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+import org.codeaurora.snapcam.R;
+
+class ScannerIntentHelper {
+ private static final String EVENT_SUMMARY = "SUMMARY:";
+ private static final String EVENT_DESCRIPTION = "DESCRIPTION:";
+ private static final String EVENT_START = "DTSTART:";
+ private static final String EVENT_END = "DTEND:";
+ private static final String EVENT_LOCATION = "LOCATION:";
+ private static final String EVENT_DATE_FORMAT = "yyyyMMdd";
+
+ private static final String SMS_URI = "smsto:";
+ private static final String SMS_BODY = "sms_body";
+
+ private static final String CONTACT_NAME = "N:";
+ private static final String CONTACT_ORG = "ORG:";
+ private static final String CONTACT_TITLE = "TITLE:";
+ private static final String CONTACT_TEL = "TEL:";
+ private static final String CONTACT_EMAIL = "EMAIL:";
+ private static final String CONTACT_NOTE = "NOTE:";
+
+ private static final String MEBKM_URL = "URL:";
+
+ private static ScannerIntentHelper INSTANCE;
+
+ private Intent mIntent;
+
+ private String mScannedText;
+
+ private ScannerIntentHelper() {
+ }
+
+ static ScannerIntentHelper getInstance() {
+ if (INSTANCE == null) {
+ INSTANCE = new ScannerIntentHelper();
+ }
+
+ return INSTANCE;
+ }
+
+ void run(Context context) {
+ if (mIntent == null) {
+ showScannedText(context);
+ } else {
+ runIntent(context);
+ }
+ }
+
+ void reset() {
+ mIntent = null;
+ mScannedText = null;
+ }
+
+ boolean isValid() {
+ return mIntent != null || !TextUtils.isEmpty(mScannedText);
+ }
+
+ void setUriIntent(String uri) {
+ mIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri));
+ }
+
+ void setCalendarIntent(String text) {
+ String[] data = text.split("\n");
+ String summary = "";
+ String description = "";
+ String location = "";
+ Date start = new Date();
+ Date end = new Date();
+
+ for (String item : data) {
+ if (item.startsWith(EVENT_SUMMARY)) {
+ summary = item.replace(EVENT_SUMMARY, "");
+ } else if (item.startsWith(EVENT_DESCRIPTION)) {
+ summary = item.replace(EVENT_DESCRIPTION, "");
+ } else if (item.startsWith(EVENT_LOCATION)) {
+ location = item.replace(EVENT_LOCATION, "");
+ } else if (item.startsWith(EVENT_START)) {
+ start = parseDate(item.replace(EVENT_START, ""));
+ } else if (item.startsWith(EVENT_END)) {
+ start = parseDate(item.replace(EVENT_END, ""));
+ }
+ }
+
+ mIntent = new Intent(Intent.ACTION_INSERT, CalendarContract.Events.CONTENT_URI);
+ mIntent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, start)
+ .putExtra(CalendarContract.EXTRA_EVENT_END_TIME, end)
+ .putExtra(CalendarContract.Events.TITLE, summary)
+ .putExtra(CalendarContract.Events.DESCRIPTION, description)
+ .putExtra(CalendarContract.Events.EVENT_LOCATION, location);
+ }
+
+ void setSMSIntent(String text) {
+ String[] data = text.split(":");
+
+ mIntent = new Intent(Intent.ACTION_SENDTO, Uri.parse(SMS_URI + data[1]));
+ if (data.length > 2) {
+ mIntent.putExtra(SMS_BODY, data[2]);
+ }
+ }
+
+ void setContactIntent(String text, boolean isVCard) {
+ String[] data = text.split(isVCard ? "\n" : ";");
+ String name = "";
+ String org = "";
+ String title = "";
+ String telephone = "";
+ String email = "";
+ String notes = "";
+
+
+ for (String item : data) {
+ if (item.startsWith(CONTACT_NAME)) {
+ name = item.replace(CONTACT_NAME, "");
+ } else if (item.startsWith(CONTACT_ORG)) {
+ org = item.replace(CONTACT_ORG, "");
+ } else if (item.startsWith(CONTACT_TITLE)) {
+ title = item.replace(CONTACT_TITLE, "");
+ } else if (item.startsWith(CONTACT_TEL)) {
+ telephone = item.replace(CONTACT_TEL, "");
+ } else if (item.startsWith(CONTACT_EMAIL)) {
+ email = item.replace(CONTACT_EMAIL, "");
+ } else if (item.startsWith(CONTACT_NOTE)) {
+ notes = item.replace(CONTACT_NOTE, "");
+ }
+ }
+
+ mIntent = new Intent(ContactsContract.Intents.Insert.ACTION);
+ mIntent.setType(ContactsContract.RawContacts.CONTENT_TYPE)
+ .putExtra(ContactsContract.Intents.Insert.NAME, name)
+ .putExtra(ContactsContract.Intents.Insert.COMPANY, org)
+ .putExtra(ContactsContract.Intents.Insert.JOB_TITLE, title)
+ .putExtra(ContactsContract.Intents.Insert.PHONE, telephone)
+ .putExtra(ContactsContract.Intents.Insert.PHONE_TYPE,
+ ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE)
+ .putExtra(ContactsContract.Intents.Insert.EMAIL, email)
+ .putExtra(ContactsContract.Intents.Insert.EMAIL_TYPE,
+ ContactsContract.CommonDataKinds.Email.TYPE_WORK)
+ .putExtra(ContactsContract.Intents.Insert.NOTES, notes);
+ }
+
+ void setMeBkmUrl(String text) {
+ String[] data = text.split(":");
+
+ for (String item : data) {
+ if (!item.startsWith(MEBKM_URL)) {
+ continue;
+ }
+
+ setUriIntent(item.replace(MEBKM_URL, "").replace("\\", "").replace(";", ""));
+ }
+ }
+
+ void setText(String text) {
+ mScannedText = text;
+ }
+
+ private Date parseDate(String item) {
+ SimpleDateFormat format = new SimpleDateFormat(EVENT_DATE_FORMAT, Locale.getDefault());
+ String formattedDate = item.split("T")[0];
+
+ try {
+ return format.parse(formattedDate);
+ } catch (ParseException e) {
+ return new Date();
+ }
+ }
+
+ private void runIntent(Context context) {
+ if (mIntent.resolveActivity(context.getPackageManager()) == null) {
+ Toast.makeText(context, context.getString(R.string.quick_reader_no_activity_found),
+ Toast.LENGTH_LONG).show();
+ } else {
+ context.startActivity(mIntent);
+ }
+ }
+
+ private void showScannedText(Context context) {
+ new AlertDialog.Builder(context)
+ .setTitle(R.string.quick_reader_scanned_text_title)
+ .setMessage(mScannedText)
+ .setNeutralButton(R.string.quick_reader_action_dismiss, null)
+ .setPositiveButton(R.string.quick_reader_scanned_text_positive,
+ (dialog, i) -> copyScanned(context))
+ .setNegativeButton(R.string.quick_reader_scanned_text_negative,
+ (dialog, i) -> shareScanned(context))
+ .show();
+ }
+
+ private void copyScanned(Context context) {
+ ClipboardManager manager = (ClipboardManager)
+ context.getSystemService(Context.CLIPBOARD_SERVICE);
+ if (manager != null) {
+ manager.setPrimaryClip(ClipData.newPlainText("", mScannedText));
+ Toast.makeText(context, context.getString(R.string.quick_reader_copied_message),
+ Toast.LENGTH_LONG).show();
+ }
+ }
+
+ private void shareScanned(Context context) {
+ Intent intent = new Intent(Intent.ACTION_SEND);
+ intent.setType("text/plain");
+ intent.putExtra(Intent.EXTRA_TEXT, mScannedText);
+ context.startActivity(Intent.createChooser(intent,
+ context.getString(R.string.quick_reader_share_title)));
+ }
+} \ No newline at end of file