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 cz.cvut.fel.dce.barcodescanner.share;
19 import android.app.Activity;
20 import android.content.ContentResolver;
21 import android.content.Intent;
22 import android.database.Cursor;
23 import android.net.Uri;
24 import android.os.Bundle;
25 import android.provider.BaseColumns;
26 import android.provider.Browser;
27 import android.provider.ContactsContract;
28 import android.util.Log;
29 import android.view.KeyEvent;
30 import android.view.View;
31 import android.widget.TextView;
33 import com.google.zxing.BarcodeFormat;
34 import cz.cvut.fel.dce.barcodescanner.Contents;
35 import cz.cvut.fel.dce.barcodescanner.Intents;
36 import cz.cvut.fel.dce.barcodescanner.R;
37 import cz.cvut.fel.dce.barcodescanner.clipboard.ClipboardInterface;
40 * Barcode Scanner can share data like contacts and bookmarks by displaying a QR Code on screen,
41 * such that another user can scan the barcode with their phone.
43 * @author dswitkin@google.com (Daniel Switkin)
45 public final class ShareActivity extends Activity {
47 private static final String TAG = ShareActivity.class.getSimpleName();
49 private static final int PICK_BOOKMARK = 0;
50 private static final int PICK_CONTACT = 1;
51 private static final int PICK_APP = 2;
53 private View clipboardButton;
55 private final View.OnClickListener contactListener = new View.OnClickListener() {
57 public void onClick(View v) {
58 Intent intent = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI);
59 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
60 startActivityForResult(intent, PICK_CONTACT);
64 private final View.OnClickListener bookmarkListener = new View.OnClickListener() {
66 public void onClick(View v) {
67 Intent intent = new Intent(Intent.ACTION_PICK);
68 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
69 intent.setClassName(ShareActivity.this, BookmarkPickerActivity.class.getName());
70 startActivityForResult(intent, PICK_BOOKMARK);
74 private final View.OnClickListener appListener = new View.OnClickListener() {
76 public void onClick(View v) {
77 Intent intent = new Intent(Intent.ACTION_PICK);
78 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
79 intent.setClassName(ShareActivity.this, AppPickerActivity.class.getName());
80 startActivityForResult(intent, PICK_APP);
84 private final View.OnClickListener clipboardListener = new View.OnClickListener() {
86 public void onClick(View v) {
87 // Should always be true, because we grey out the clipboard button in onResume() if it's empty
88 CharSequence text = ClipboardInterface.getText(ShareActivity.this);
90 launchSearch(text.toString());
95 private final View.OnKeyListener textListener = new View.OnKeyListener() {
97 public boolean onKey(View view, int keyCode, KeyEvent event) {
98 if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_DOWN) {
99 String text = ((TextView) view).getText().toString();
100 if (text != null && !text.isEmpty()) {
109 private void launchSearch(String text) {
110 Intent intent = new Intent(Intents.Encode.ACTION);
111 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
112 intent.putExtra(Intents.Encode.TYPE, Contents.Type.TEXT);
113 intent.putExtra(Intents.Encode.DATA, text);
114 intent.putExtra(Intents.Encode.FORMAT, BarcodeFormat.QR_CODE.toString());
115 startActivity(intent);
119 public void onCreate(Bundle icicle) {
120 super.onCreate(icicle);
121 setContentView(R.layout.share);
123 findViewById(R.id.share_contact_button).setOnClickListener(contactListener);
124 findViewById(R.id.share_bookmark_button).setOnClickListener(bookmarkListener);
125 findViewById(R.id.share_app_button).setOnClickListener(appListener);
126 clipboardButton = findViewById(R.id.share_clipboard_button);
127 clipboardButton.setOnClickListener(clipboardListener);
128 findViewById(R.id.share_text_view).setOnKeyListener(textListener);
132 protected void onResume() {
134 clipboardButton.setEnabled(ClipboardInterface.hasText(this));
138 public void onActivityResult(int requestCode, int resultCode, Intent intent) {
139 if (resultCode == RESULT_OK) {
140 switch (requestCode) {
143 showTextAsBarcode(intent.getStringExtra(Browser.BookmarkColumns.URL));
146 // Data field is content://contacts/people/984
147 showContactAsBarcode(intent.getData());
153 private void showTextAsBarcode(String text) {
154 Log.i(TAG, "Showing text as barcode: " + text);
156 return; // Show error?
158 Intent intent = new Intent(Intents.Encode.ACTION);
159 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
160 intent.putExtra(Intents.Encode.TYPE, Contents.Type.TEXT);
161 intent.putExtra(Intents.Encode.DATA, text);
162 intent.putExtra(Intents.Encode.FORMAT, BarcodeFormat.QR_CODE.toString());
163 startActivity(intent);
167 * Takes a contact Uri and does the necessary database lookups to retrieve that person's info,
168 * then sends an Encode intent to render it as a QR Code.
170 * @param contactUri A Uri of the form content://contacts/people/17
172 private void showContactAsBarcode(Uri contactUri) {
173 Log.i(TAG, "Showing contact URI as barcode: " + contactUri);
174 if (contactUri == null) {
175 return; // Show error?
177 ContentResolver resolver = getContentResolver();
181 // We're seeing about six reports a week of this exception although I don't understand why.
182 cursor = resolver.query(contactUri, null, null, null, null);
183 } catch (IllegalArgumentException ignored) {
186 if (cursor == null) {
194 if (!cursor.moveToFirst()) {
198 id = cursor.getString(cursor.getColumnIndex(BaseColumns._ID));
199 name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
200 hasPhone = cursor.getInt(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)) > 0;
207 // Don't require a name to be present, this contact might be just a phone number.
208 Bundle bundle = new Bundle();
209 if (name != null && !name.isEmpty()) {
210 bundle.putString(ContactsContract.Intents.Insert.NAME, massageContactData(name));
214 Cursor phonesCursor = resolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
216 ContactsContract.CommonDataKinds.Phone.CONTACT_ID + '=' + id,
219 if (phonesCursor != null) {
222 int phonesNumberColumn = phonesCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
223 int phoneTypeColumn = phonesCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE);
224 while (phonesCursor.moveToNext() && foundPhone < Contents.PHONE_KEYS.length) {
225 String number = phonesCursor.getString(phonesNumberColumn);
226 if (number != null && !number.isEmpty()) {
227 bundle.putString(Contents.PHONE_KEYS[foundPhone], massageContactData(number));
229 int type = phonesCursor.getInt(phoneTypeColumn);
230 bundle.putInt(Contents.PHONE_TYPE_KEYS[foundPhone], type);
234 phonesCursor.close();
239 Cursor methodsCursor = resolver.query(ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_URI,
241 ContactsContract.CommonDataKinds.StructuredPostal.CONTACT_ID + '=' + id,
244 if (methodsCursor != null) {
246 if (methodsCursor.moveToNext()) {
247 String data = methodsCursor.getString(
248 methodsCursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS));
249 if (data != null && !data.isEmpty()) {
250 bundle.putString(ContactsContract.Intents.Insert.POSTAL, massageContactData(data));
254 methodsCursor.close();
258 Cursor emailCursor = resolver.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI,
260 ContactsContract.CommonDataKinds.Email.CONTACT_ID + '=' + id,
263 if (emailCursor != null) {
266 int emailColumn = emailCursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA);
267 while (emailCursor.moveToNext() && foundEmail < Contents.EMAIL_KEYS.length) {
268 String email = emailCursor.getString(emailColumn);
269 if (email != null && !email.isEmpty()) {
270 bundle.putString(Contents.EMAIL_KEYS[foundEmail], massageContactData(email));
279 Intent intent = new Intent(Intents.Encode.ACTION);
280 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
281 intent.putExtra(Intents.Encode.TYPE, Contents.Type.CONTACT);
282 intent.putExtra(Intents.Encode.DATA, bundle);
283 intent.putExtra(Intents.Encode.FORMAT, BarcodeFormat.QR_CODE.toString());
285 Log.i(TAG, "Sending bundle for encoding: " + bundle);
286 startActivity(intent);
289 private static String massageContactData(String data) {
290 // For now -- make sure we don't put newlines in shared contact data. It messes up
291 // any known encoding of contact data. Replace with space.
292 if (data.indexOf('\n') >= 0) {
293 data = data.replace("\n", " ");
295 if (data.indexOf('\r') >= 0) {
296 data = data.replace("\r", " ");