Lots of updates:
[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 com.google.zxing.BarcodeFormat;
20 import com.google.zxing.MultiFormatWriter;
21 import com.google.zxing.WriterException;
22 import com.google.zxing.common.ByteMatrix;
23
24 import android.app.Activity;
25 import android.content.Intent;
26 import android.graphics.Bitmap;
27 import android.os.Bundle;
28 import android.os.Handler;
29 import android.os.Message;
30 import android.provider.Contacts;
31 import android.telephony.PhoneNumberUtils;
32 import android.util.Log;
33
34 /**
35  * This class does the work of decoding the user's request and extracting all the data
36  * to be encoded in a QR Code.
37  *
38  * @author dswitkin@google.com (Daniel Switkin)
39  */
40 public final class QRCodeEncoder {
41   private final Activity activity;
42   private String contents;
43   private String displayContents;
44   private String title;
45   private BarcodeFormat format;
46
47   public QRCodeEncoder(Activity activity, Intent intent) {
48     this.activity = activity;
49     if (!encodeContents(intent)) {
50       throw new IllegalArgumentException("No valid data to encode.");
51     }
52   }
53
54   public void requestBarcode(Handler handler, int pixelResolution) {
55     Thread encodeThread = new EncodeThread(contents, handler, pixelResolution,
56         format);
57     encodeThread.start();
58   }
59
60   public String getContents() {
61     return contents;
62   }
63
64   public String getDisplayContents() {
65     return displayContents;
66   }
67
68   public String getTitle() {
69     return title;
70   }
71   
72   public String getFormat() {
73     return format.toString();
74   }
75
76   // It would be nice if the string encoding lived in the core ZXing library,
77   // but we use platform specific code like PhoneNumberUtils, so it can't.
78   private boolean encodeContents(Intent intent) {
79     if (intent == null) {
80       return false;
81     }
82     
83     // default to QR_CODE if no format given
84     String format = intent.getStringExtra(Intents.Encode.FORMAT);
85     if (format == null || format.length() == 0 || 
86         format.equals(Contents.Format.QR_CODE)) {
87       String type = intent.getStringExtra(Intents.Encode.TYPE);
88       if (type == null || type.length() == 0) {
89         return false;
90       }
91       this.format = BarcodeFormat.QR_CODE;
92       encodeQRCodeContents(intent, type);
93     } else {
94       String data = intent.getStringExtra(Intents.Encode.DATA);
95       if (data != null && data.length() != 0) {
96         contents = data;
97         displayContents = data;
98         title = activity.getString(R.string.contents_text);
99         if (format.equals(Contents.Format.CODE_128))
100           this.format = BarcodeFormat.CODE_128;
101         else if (format.equals(Contents.Format.CODE_39))
102           this.format = BarcodeFormat.CODE_39;
103         else if (format.equals(Contents.Format.EAN_8))
104           this.format = BarcodeFormat.EAN_8;
105         else if (format.equals(Contents.Format.EAN_13))
106           this.format = BarcodeFormat.EAN_13;
107         else if (format.equals(Contents.Format.UPC_A))
108           this.format = BarcodeFormat.UPC_A;
109         else if (format.equals(Contents.Format.UPC_E))
110           this.format = BarcodeFormat.UPC_E;
111       }
112     }
113     return contents != null && contents.length() > 0;
114   }
115
116   private void encodeQRCodeContents(Intent intent, String type) {
117     if (type.equals(Contents.Type.TEXT)) {
118       String data = intent.getStringExtra(Intents.Encode.DATA);
119       if (data != null && data.length() > 0) {
120         contents = data;
121         displayContents = data;
122         title = activity.getString(R.string.contents_text);
123       }
124     } else if (type.equals(Contents.Type.EMAIL)) {
125       String data = intent.getStringExtra(Intents.Encode.DATA);
126       if (data != null && data.length() > 0) {
127         contents = "mailto:" + data;
128         displayContents = data;
129         title = activity.getString(R.string.contents_email);
130       }
131     } else if (type.equals(Contents.Type.PHONE)) {
132       String data = intent.getStringExtra(Intents.Encode.DATA);
133       if (data != null && data.length() > 0) {
134         contents = "tel:" + data;
135         displayContents = PhoneNumberUtils.formatNumber(data);
136         title = activity.getString(R.string.contents_phone);
137       }
138     } else if (type.equals(Contents.Type.SMS)) {
139       String data = intent.getStringExtra(Intents.Encode.DATA);
140       if (data != null && data.length() > 0) {
141         contents = "sms:" + data;
142         displayContents = PhoneNumberUtils.formatNumber(data);
143         title = activity.getString(R.string.contents_sms);
144       }
145     } else if (type.equals(Contents.Type.CONTACT)) {
146       Bundle bundle = intent.getBundleExtra(Intents.Encode.DATA);
147       if (bundle != null) {
148         StringBuilder newContents = new StringBuilder();
149         StringBuilder newDisplayContents = new StringBuilder();
150         newContents.append("MECARD:");
151         String name = bundle.getString(Contacts.Intents.Insert.NAME);
152         if (name != null && name.length() > 0) {
153           newContents.append("N:").append(name).append(';');
154           newDisplayContents.append(name);
155         }
156         String address = bundle.getString(Contacts.Intents.Insert.POSTAL);
157         if (address != null && address.length() > 0) {
158           newContents.append("ADR:").append(address).append(';');
159           newDisplayContents.append('\n').append(address);
160         }
161         for (int x = 0; x < Contents.PHONE_KEYS.length; x++) {
162           String phone = bundle.getString(Contents.PHONE_KEYS[x]);
163           if (phone != null && phone.length() > 0) {
164             newContents.append("TEL:").append(phone).append(';');
165             newDisplayContents.append('\n').append(PhoneNumberUtils.formatNumber(phone));
166           }
167         }
168         for (int x = 0; x < Contents.EMAIL_KEYS.length; x++) {
169           String email = bundle.getString(Contents.EMAIL_KEYS[x]);
170           if (email != null && email.length() > 0) {
171             newContents.append("EMAIL:").append(email).append(';');
172             newDisplayContents.append('\n').append(email);
173           }
174         }
175         // Make sure we've encoded at least one field.
176         if (newDisplayContents.length() > 0) {
177           newContents.append(';');
178           contents = newContents.toString();
179           displayContents = newDisplayContents.toString();
180           title = activity.getString(R.string.contents_contact);
181         } else {
182           contents = null;
183           displayContents = null;
184         }
185       }
186     } else if (type.equals(Contents.Type.LOCATION)) {
187       Bundle bundle = intent.getBundleExtra(Intents.Encode.DATA);
188       if (bundle != null) {
189         // These must use Bundle.getFloat(), not getDouble(), it's part of the API.
190         float latitude = bundle.getFloat("LAT", Float.MAX_VALUE);
191         float longitude = bundle.getFloat("LONG", Float.MAX_VALUE);
192         if (latitude != Float.MAX_VALUE && longitude != Float.MAX_VALUE) {
193           contents = "geo:" + latitude + ',' + longitude;
194           displayContents = latitude + "," + longitude;
195           title = activity.getString(R.string.contents_location);
196         }
197       }
198     }
199   }
200
201   private static final class EncodeThread extends Thread {
202     private static final String TAG = "EncodeThread";
203
204     private final String contents;
205     private final Handler handler;
206     private final int pixelResolution;
207     private final BarcodeFormat format;
208
209     EncodeThread(String contents, Handler handler, int pixelResolution,
210         BarcodeFormat format) {
211       this.contents = contents;
212       this.handler = handler;
213       this.pixelResolution = pixelResolution;
214       this.format = format;
215     }
216
217     @Override
218     public void run() {
219       try {
220         ByteMatrix result = new MultiFormatWriter().encode(contents, format,
221             pixelResolution, pixelResolution);
222         int width = result.getWidth();
223         int height = result.getHeight();
224         byte[][] array = result.getArray();
225         int[] pixels = new int[width * height];
226         for (int y = 0; y < height; y++) {
227           for (int x = 0; x < width; x++) {
228             int grey = array[y][x] & 0xff;
229             // pixels[y * width + x] = (0xff << 24) | (grey << 16) | (grey << 8) | grey;
230             pixels[y * width + x] = 0xff000000 | (0x00010101 * grey);
231           }
232         }
233
234         Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
235         bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
236         Message message = Message.obtain(handler, R.id.encode_succeeded);
237         message.obj = bitmap;
238         message.sendToTarget();
239       } catch (WriterException e) {
240         Log.e(TAG, e.toString());
241         Message message = Message.obtain(handler, R.id.encode_failed);
242         message.sendToTarget();
243       } catch (IllegalArgumentException e) {
244         Log.e(TAG, e.toString());
245         Message message = Message.obtain(handler, R.id.encode_failed);
246         message.sendToTarget();
247       }
248     }
249   }
250 }