package com.google.zxing.client.android.encode;
import com.google.zxing.BarcodeFormat;
+import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
+import com.google.zxing.Result;
import com.google.zxing.WriterException;
-import com.google.zxing.client.android.Intents;
import com.google.zxing.client.android.Contents;
+import com.google.zxing.client.android.Intents;
import com.google.zxing.client.android.R;
-import com.google.zxing.common.ByteMatrix;
+import com.google.zxing.client.result.AddressBookParsedResult;
+import com.google.zxing.client.result.ParsedResult;
+import com.google.zxing.client.result.ResultParser;
+import com.google.zxing.common.BitMatrix;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
+import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
-import android.os.Message;
import android.provider.Contacts;
import android.telephony.PhoneNumberUtils;
import android.util.Log;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Hashtable;
+
/**
* This class does the work of decoding the user's request and extracting all the data
- * to be encoded in a QR Code.
+ * to be encoded in a barcode.
*
* @author dswitkin@google.com (Daniel Switkin)
*/
final class QRCodeEncoder {
+
+ private static final String TAG = QRCodeEncoder.class.getSimpleName();
+
+ private static final int WHITE = 0xFFFFFFFF;
+ private static final int BLACK = 0xFF000000;
+
private final Activity activity;
private String contents;
private String displayContents;
private String title;
private BarcodeFormat format;
- public QRCodeEncoder(Activity activity, Intent intent) {
+ QRCodeEncoder(Activity activity, Intent intent) {
this.activity = activity;
- if (!encodeContents(intent)) {
+ if (intent == null) {
throw new IllegalArgumentException("No valid data to encode.");
}
+
+ String action = intent.getAction();
+ if (action.equals(Intents.Encode.ACTION)) {
+ if (!encodeContentsFromZXingIntent(intent)) {
+ throw new IllegalArgumentException("No valid data to encode.");
+ }
+ } else if (action.equals(Intent.ACTION_SEND)) {
+ if (!encodeContentsFromShareIntent(intent)) {
+ throw new IllegalArgumentException("No valid data to encode.");
+ }
+ }
}
public void requestBarcode(Handler handler, int pixelResolution) {
public String getTitle() {
return title;
}
-
+
public String getFormat() {
return format.toString();
}
// It would be nice if the string encoding lived in the core ZXing library,
// but we use platform specific code like PhoneNumberUtils, so it can't.
- private boolean encodeContents(Intent intent) {
- if (intent == null) {
- return false;
+ private boolean encodeContentsFromZXingIntent(Intent intent) {
+ // Default to QR_CODE if no format given.
+ String formatString = intent.getStringExtra(Intents.Encode.FORMAT);
+ try {
+ format = BarcodeFormat.valueOf(formatString);
+ } catch (IllegalArgumentException iae) {
+ // Ignore it then
+ format = null;
+ formatString = null;
}
-
- // default to QR_CODE if no format given
- String format = intent.getStringExtra(Intents.Encode.FORMAT);
- if (format == null || format.length() == 0 ||
- format.equals(Contents.Format.QR_CODE)) {
+ if (format == null || BarcodeFormat.QR_CODE.equals(format)) {
String type = intent.getStringExtra(Intents.Encode.TYPE);
if (type == null || type.length() == 0) {
return false;
contents = data;
displayContents = data;
title = activity.getString(R.string.contents_text);
- if (format.equals(Contents.Format.CODE_128)) {
- this.format = BarcodeFormat.CODE_128;
- } else if (format.equals(Contents.Format.CODE_39)) {
- this.format = BarcodeFormat.CODE_39;
- } else if (format.equals(Contents.Format.EAN_8)) {
- this.format = BarcodeFormat.EAN_8;
- } else if (format.equals(Contents.Format.EAN_13)) {
- this.format = BarcodeFormat.EAN_13;
- } else if (format.equals(Contents.Format.UPC_A)) {
- this.format = BarcodeFormat.UPC_A;
- } else if (format.equals(Contents.Format.UPC_E)) {
- this.format = BarcodeFormat.UPC_E;
- }
}
}
return contents != null && contents.length() > 0;
}
+ // Handles send intents from the Contacts app, retrieving a contact as a VCARD.
+ private boolean encodeContentsFromShareIntent(Intent intent) {
+ format = BarcodeFormat.QR_CODE;
+ try {
+ Uri uri = (Uri)intent.getExtras().getParcelable(Intent.EXTRA_STREAM);
+ InputStream stream = activity.getContentResolver().openInputStream(uri);
+ int length = stream.available();
+ if (length <= 0) {
+ Log.w(TAG, "Content stream is empty");
+ return false;
+ }
+ byte[] vcard = new byte[length];
+ int bytesRead = stream.read(vcard, 0, length);
+ if (bytesRead < length) {
+ Log.w(TAG, "Unable to fully read available bytes from content stream");
+ return false;
+ }
+ String vcardString = new String(vcard, 0, bytesRead, "UTF-8");
+ Log.d(TAG, "Encoding share intent content:");
+ Log.d(TAG, vcardString);
+ Result result = new Result(vcardString, vcard, null, BarcodeFormat.QR_CODE);
+ ParsedResult parsedResult = ResultParser.parseResult(result);
+ if (!(parsedResult instanceof AddressBookParsedResult)) {
+ Log.d(TAG, "Result was not an address");
+ return false;
+ }
+ if (!encodeQRCodeContents((AddressBookParsedResult) parsedResult)) {
+ Log.d(TAG, "Unable to encode contents");
+ return false;
+ }
+ } catch (IOException e) {
+ Log.w(TAG, e);
+ return false;
+ } catch (NullPointerException e) {
+ Log.w(TAG, e);
+ // In case the uri was not found in the Intent.
+ return false;
+ }
+ return contents != null && contents.length() > 0;
+ }
+
private void encodeQRCodeContents(Intent intent, String type) {
if (type.equals(Contents.Type.TEXT)) {
String data = intent.getStringExtra(Intents.Encode.DATA);
}
}
- private static final class EncodeThread extends Thread {
- private static final String TAG = "EncodeThread";
-
- private final String contents;
- private final Handler handler;
- private final int pixelResolution;
- private final BarcodeFormat format;
+ private boolean encodeQRCodeContents(AddressBookParsedResult contact) {
+ StringBuilder newContents = new StringBuilder();
+ StringBuilder newDisplayContents = new StringBuilder();
+ newContents.append("MECARD:");
+ String[] names = contact.getNames();
+ if (names != null && names.length > 0) {
+ newContents.append("N:").append(names[0]).append(';');
+ newDisplayContents.append(names[0]);
+ }
+ String[] addresses = contact.getAddresses();
+ if (addresses != null) {
+ for (String address : addresses) {
+ if (address != null && address.length() > 0) {
+ newContents.append("ADR:").append(address).append(';');
+ newDisplayContents.append('\n').append(address);
+ }
+ }
+ }
+ String[] phoneNumbers = contact.getPhoneNumbers();
+ if (phoneNumbers != null) {
+ for (String phone : phoneNumbers) {
+ if (phone != null && phone.length() > 0) {
+ newContents.append("TEL:").append(phone).append(';');
+ newDisplayContents.append('\n').append(PhoneNumberUtils.formatNumber(phone));
+ }
+ }
+ }
+ String[] emails = contact.getEmails();
+ if (emails != null) {
+ for (String email : emails) {
+ if (email != null && email.length() > 0) {
+ newContents.append("EMAIL:").append(email).append(';');
+ newDisplayContents.append('\n').append(email);
+ }
+ }
+ }
+ String url = contact.getURL();
+ if (url != null && url.length() > 0) {
+ newContents.append("URL:").append(url).append(';');
+ newDisplayContents.append('\n').append(url);
+ }
+ // Make sure we've encoded at least one field.
+ if (newDisplayContents.length() > 0) {
+ newContents.append(';');
+ contents = newContents.toString();
+ displayContents = newDisplayContents.toString();
+ title = activity.getString(R.string.contents_contact);
+ return true;
+ } else {
+ contents = null;
+ displayContents = null;
+ return false;
+ }
+ }
- EncodeThread(String contents, Handler handler, int pixelResolution,
- BarcodeFormat format) {
- this.contents = contents;
- this.handler = handler;
- this.pixelResolution = pixelResolution;
- this.format = format;
+ static Bitmap encodeAsBitmap(String contents,
+ BarcodeFormat format,
+ int desiredWidth,
+ int desiredHeight) throws WriterException {
+ Hashtable hints = null;
+ String encoding = guessAppropriateEncoding(contents);
+ if (encoding != null) {
+ hints = new Hashtable(2);
+ hints.put(EncodeHintType.CHARACTER_SET, encoding);
+ }
+ MultiFormatWriter writer = new MultiFormatWriter();
+ BitMatrix result = writer.encode(contents, format, desiredWidth, desiredHeight, hints);
+ int width = result.getWidth();
+ int height = result.getHeight();
+ int[] pixels = new int[width * height];
+ // All are 0, or black, by default
+ for (int y = 0; y < height; y++) {
+ int offset = y * width;
+ for (int x = 0; x < width; x++) {
+ pixels[offset + x] = result.get(x, y) ? BLACK : WHITE;
+ }
}
- @Override
- public void run() {
- try {
- ByteMatrix result = new MultiFormatWriter().encode(contents, format,
- pixelResolution, pixelResolution);
- int width = result.getWidth();
- int height = result.getHeight();
- byte[][] array = result.getArray();
- int[] pixels = new int[width * height];
- for (int y = 0; y < height; y++) {
- for (int x = 0; x < width; x++) {
- int grey = array[y][x] & 0xff;
- // pixels[y * width + x] = (0xff << 24) | (grey << 16) | (grey << 8) | grey;
- pixels[y * width + x] = 0xff000000 | (0x00010101 * grey);
- }
- }
+ Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
+ return bitmap;
+ }
- Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
- Message message = Message.obtain(handler, R.id.encode_succeeded);
- message.obj = bitmap;
- message.sendToTarget();
- } catch (WriterException e) {
- Log.e(TAG, e.toString());
- Message message = Message.obtain(handler, R.id.encode_failed);
- message.sendToTarget();
- } catch (IllegalArgumentException e) {
- Log.e(TAG, e.toString());
- Message message = Message.obtain(handler, R.id.encode_failed);
- message.sendToTarget();
+ private static String guessAppropriateEncoding(CharSequence contents) {
+ // Very crude at the moment
+ for (int i = 0; i < contents.length(); i++) {
+ if (contents.charAt(i) > 0xFF) {
+ return "UTF-8";
}
}
+ return null;
}
}