Issue 198
[zxing.git] / android / src / com / google / zxing / client / android / QRCodeEncoder.java
1 /*
2  * Copyright (C) 2008 ZXing authors
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package com.google.zxing.client.android;
18
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;
32
33 public final class QRCodeEncoder {
34
35   private final Activity mActivity;
36   private String mContents;
37   private String mDisplayContents;
38   private String mTitle;
39   private BarcodeFormat mFormat;
40
41   public QRCodeEncoder(Activity activity, Intent intent) {
42     mActivity = activity;
43     if (!encodeContents(intent)) {
44       throw new IllegalArgumentException("No valid data to encode.");
45     }
46   }
47
48   public void requestBarcode(Handler handler, int pixelResolution) {
49     Thread encodeThread = new EncodeThread(mContents, handler, pixelResolution,
50         mFormat);
51     encodeThread.start();
52   }
53
54   public String getContents() {
55     return mContents;
56   }
57
58   public String getDisplayContents() {
59     return mDisplayContents;
60   }
61
62   public String getTitle() {
63     return mTitle;
64   }
65   
66   public String getFormat() {
67     return mFormat.toString();
68   }
69
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) {
73     if (intent == null) {
74       return false;
75     }
76     
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) {
83         return false;
84       }
85       mFormat = BarcodeFormat.QR_CODE;
86       encodeQRCodeContents(intent, type);
87     } else {
88       String data = intent.getStringExtra(Intents.Encode.DATA);
89       if (data != null && data.length() != 0) {
90         mContents = data;
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;
105       }
106     }
107     return mContents != null && mContents.length() > 0;
108   }
109
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) {
114         mContents = data;
115         mDisplayContents = data;
116         mTitle = mActivity.getString(R.string.contents_text);
117       }
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);
124       }
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);
131       }
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);
138       }
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);
149         }
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);
154         }
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));
160           }
161         }
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);
167           }
168         }
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);
175         } else {
176           mContents = null;
177           mDisplayContents = null;
178         }
179       }
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);
190         }
191       }
192     }
193   }
194
195   private static final class EncodeThread extends Thread {
196
197     private static final String TAG = "EncodeThread";
198
199     private final String mContents;
200     private final Handler mHandler;
201     private final int mPixelResolution;
202     private final BarcodeFormat mFormat;
203
204     EncodeThread(String contents, Handler handler, int pixelResolution,
205         BarcodeFormat format) {
206       mContents = contents;
207       mHandler = handler;
208       mPixelResolution = pixelResolution;
209       mFormat = format;
210     }
211
212     @Override
213     public void run() {
214       try {
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);
226           }
227         }
228
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();
242       }
243     }
244   }
245
246 }