2 * Copyright (C) 2008 ZXing authors
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.google.zxing.client.android;
19 import android.app.Activity;
20 import android.content.Intent;
21 import android.graphics.Bitmap;
22 import android.os.Bundle;
23 import android.os.Handler;
24 import android.os.Message;
25 import android.provider.Contacts;
26 import android.util.Log;
27 import android.telephony.PhoneNumberUtils;
28 import com.google.zxing.BarcodeFormat;
29 import com.google.zxing.MultiFormatWriter;
30 import com.google.zxing.WriterException;
31 import com.google.zxing.common.ByteMatrix;
33 public final class QRCodeEncoder {
35 private final Activity mActivity;
36 private String mContents;
37 private String mDisplayContents;
38 private String mTitle;
39 private BarcodeFormat mFormat;
41 public QRCodeEncoder(Activity activity, Intent intent) {
43 if (!encodeContents(intent)) {
44 throw new IllegalArgumentException("No valid data to encode.");
48 public void requestBarcode(Handler handler, int pixelResolution) {
49 Thread encodeThread = new EncodeThread(mContents, handler, pixelResolution,
54 public String getContents() {
58 public String getDisplayContents() {
59 return mDisplayContents;
62 public String getTitle() {
66 public String getFormat() {
67 return mFormat.toString();
70 // It would be nice if the string encoding lived in the core ZXing library,
71 // but we use platform specific code like PhoneNumberUtils, so it can't.
72 private boolean encodeContents(Intent intent) {
77 // default to QR_CODE if no format given
78 String format = intent.getStringExtra(Intents.Encode.FORMAT);
79 if (format == null || format.length() == 0 ||
80 format.equals(Contents.Format.QR_CODE)) {
81 String type = intent.getStringExtra(Intents.Encode.TYPE);
82 if (type == null || type.length() == 0) {
85 mFormat = BarcodeFormat.QR_CODE;
86 encodeQRCodeContents(intent, type);
88 String data = intent.getStringExtra(Intents.Encode.DATA);
89 if (data != null && data.length() != 0) {
91 mDisplayContents = data;
92 mTitle = mActivity.getString(R.string.contents_text);
93 if (format.equals(Contents.Format.CODE_128))
94 mFormat = BarcodeFormat.CODE_128;
95 else if (format.equals(Contents.Format.CODE_39))
96 mFormat = BarcodeFormat.CODE_39;
97 else if (format.equals(Contents.Format.EAN_8))
98 mFormat = BarcodeFormat.EAN_8;
99 else if (format.equals(Contents.Format.EAN_13))
100 mFormat = BarcodeFormat.EAN_13;
101 else if (format.equals(Contents.Format.UPC_A))
102 mFormat = BarcodeFormat.UPC_A;
103 else if (format.equals(Contents.Format.UPC_E))
104 mFormat = BarcodeFormat.UPC_E;
107 return mContents != null && mContents.length() > 0;
110 private void encodeQRCodeContents(Intent intent, String type) {
111 if (type.equals(Contents.Type.TEXT)) {
112 String data = intent.getStringExtra(Intents.Encode.DATA);
113 if (data != null && data.length() > 0) {
115 mDisplayContents = data;
116 mTitle = mActivity.getString(R.string.contents_text);
118 } else if (type.equals(Contents.Type.EMAIL)) {
119 String data = intent.getStringExtra(Intents.Encode.DATA);
120 if (data != null && data.length() > 0) {
121 mContents = "mailto:" + data;
122 mDisplayContents = data;
123 mTitle = mActivity.getString(R.string.contents_email);
125 } else if (type.equals(Contents.Type.PHONE)) {
126 String data = intent.getStringExtra(Intents.Encode.DATA);
127 if (data != null && data.length() > 0) {
128 mContents = "tel:" + data;
129 mDisplayContents = PhoneNumberUtils.formatNumber(data);
130 mTitle = mActivity.getString(R.string.contents_phone);
132 } else if (type.equals(Contents.Type.SMS)) {
133 String data = intent.getStringExtra(Intents.Encode.DATA);
134 if (data != null && data.length() > 0) {
135 mContents = "sms:" + data;
136 mDisplayContents = PhoneNumberUtils.formatNumber(data);
137 mTitle = mActivity.getString(R.string.contents_sms);
139 } else if (type.equals(Contents.Type.CONTACT)) {
140 Bundle bundle = intent.getBundleExtra(Intents.Encode.DATA);
141 if (bundle != null) {
142 StringBuilder newContents = new StringBuilder();
143 StringBuilder newDisplayContents = new StringBuilder();
144 newContents.append("MECARD:");
145 String name = bundle.getString(Contacts.Intents.Insert.NAME);
146 if (name != null && name.length() > 0) {
147 newContents.append("N:").append(name).append(';');
148 newDisplayContents.append(name);
150 String address = bundle.getString(Contacts.Intents.Insert.POSTAL);
151 if (address != null && address.length() > 0) {
152 newContents.append("ADR:").append(address).append(';');
153 newDisplayContents.append('\n').append(address);
155 for (int x = 0; x < Contents.PHONE_KEYS.length; x++) {
156 String phone = bundle.getString(Contents.PHONE_KEYS[x]);
157 if (phone != null && phone.length() > 0) {
158 newContents.append("TEL:").append(phone).append(';');
159 newDisplayContents.append('\n').append(PhoneNumberUtils.formatNumber(phone));
162 for (int x = 0; x < Contents.EMAIL_KEYS.length; x++) {
163 String email = bundle.getString(Contents.EMAIL_KEYS[x]);
164 if (email != null && email.length() > 0) {
165 newContents.append("EMAIL:").append(email).append(';');
166 newDisplayContents.append('\n').append(email);
169 // Make sure we've encoded at least one field.
170 if (newDisplayContents.length() > 0) {
171 newContents.append(';');
172 mContents = newContents.toString();
173 mDisplayContents = newDisplayContents.toString();
174 mTitle = mActivity.getString(R.string.contents_contact);
177 mDisplayContents = null;
180 } else if (type.equals(Contents.Type.LOCATION)) {
181 Bundle bundle = intent.getBundleExtra(Intents.Encode.DATA);
182 if (bundle != null) {
183 // These must use Bundle.getFloat(), not getDouble(), it's part of the API.
184 float latitude = bundle.getFloat("LAT", Float.MAX_VALUE);
185 float longitude = bundle.getFloat("LONG", Float.MAX_VALUE);
186 if (latitude != Float.MAX_VALUE && longitude != Float.MAX_VALUE) {
187 mContents = "geo:" + latitude + ',' + longitude;
188 mDisplayContents = latitude + "," + longitude;
189 mTitle = mActivity.getString(R.string.contents_location);
195 private static final class EncodeThread extends Thread {
197 private static final String TAG = "EncodeThread";
199 private final String mContents;
200 private final Handler mHandler;
201 private final int mPixelResolution;
202 private final BarcodeFormat mFormat;
204 EncodeThread(String contents, Handler handler, int pixelResolution,
205 BarcodeFormat format) {
206 mContents = contents;
208 mPixelResolution = pixelResolution;
215 ByteMatrix result = new MultiFormatWriter().encode(mContents,
216 mFormat, mPixelResolution, mPixelResolution);
217 int width = result.width();
218 int height = result.height();
219 byte[][] array = result.getArray();
220 int[] pixels = new int[width * height];
221 for (int y = 0; y < height; y++) {
222 for (int x = 0; x < width; x++) {
223 int grey = array[y][x] & 0xff;
224 // pixels[y * width + x] = (0xff << 24) | (grey << 16) | (grey << 8) | grey;
225 pixels[y * width + x] = 0xff000000 | (0x00010101 * grey);
229 Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
230 bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
231 Message message = Message.obtain(mHandler, R.id.encode_succeeded);
232 message.obj = bitmap;
233 message.sendToTarget();
234 } catch (WriterException e) {
235 Log.e(TAG, e.toString());
236 Message message = Message.obtain(mHandler, R.id.encode_failed);
237 message.sendToTarget();
238 } catch (IllegalArgumentException e) {
239 Log.e(TAG, e.toString());
240 Message message = Message.obtain(mHandler, R.id.encode_failed);
241 message.sendToTarget();