/* * Copyright (C) 2008 ZXing authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.zxing.client.android.share; import android.provider.ContactsContract; import com.google.zxing.BarcodeFormat; import com.google.zxing.client.android.Contents; import com.google.zxing.client.android.Intents; import com.google.zxing.client.android.R; import android.app.Activity; import android.content.ContentResolver; import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.provider.BaseColumns; import android.provider.Browser; import android.text.ClipboardManager; import android.util.Log; import android.view.KeyEvent; import android.view.View; import android.widget.Button; import android.widget.TextView; import com.google.zxing.FakeR; /** * Barcode Scanner can share data like contacts and bookmarks by displaying a QR Code on screen, * such that another user can scan the barcode with their phone. * * @author dswitkin@google.com (Daniel Switkin) */ public final class ShareActivity extends Activity { private static final String TAG = ShareActivity.class.getSimpleName(); private static final int PICK_BOOKMARK = 0; private static final int PICK_CONTACT = 1; private static final int PICK_APP = 2; private Button clipboardButton; private static FakeR fakeR; private final Button.OnClickListener contactListener = new Button.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); startActivityForResult(intent, PICK_CONTACT); } }; private final Button.OnClickListener bookmarkListener = new Button.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(Intent.ACTION_PICK); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); intent.setClassName(ShareActivity.this, BookmarkPickerActivity.class.getName()); startActivityForResult(intent, PICK_BOOKMARK); } }; private final Button.OnClickListener appListener = new Button.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(Intent.ACTION_PICK); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); intent.setClassName(ShareActivity.this, AppPickerActivity.class.getName()); startActivityForResult(intent, PICK_APP); } }; private final Button.OnClickListener clipboardListener = new Button.OnClickListener() { @Override public void onClick(View v) { ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); // Should always be true, because we grey out the clipboard button in onResume() if it's empty if (clipboard.hasText()) { launchSearch(clipboard.getText().toString()); } } }; private final View.OnKeyListener textListener = new View.OnKeyListener() { @Override public boolean onKey(View view, int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_DOWN) { String text = ((TextView) view).getText().toString(); if (text != null && text.length() > 0) { launchSearch(text); } return true; } return false; } }; private void launchSearch(String text) { Intent intent = new Intent(Intents.Encode.ACTION); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); intent.putExtra(Intents.Encode.TYPE, Contents.Type.TEXT); intent.putExtra(Intents.Encode.DATA, text); intent.putExtra(Intents.Encode.FORMAT, BarcodeFormat.QR_CODE.toString()); startActivity(intent); } @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); fakeR = new FakeR(this); setContentView(fakeR.getId("layout", "share")); findViewById(fakeR.getId("id", "share_contact_button")).setOnClickListener(contactListener); findViewById(fakeR.getId("id", "share_bookmark_button")).setOnClickListener(bookmarkListener); findViewById(fakeR.getId("id", "share_app_button")).setOnClickListener(appListener); clipboardButton = (Button) findViewById(fakeR.getId("id", "share_clipboard_button")); clipboardButton.setOnClickListener(clipboardListener); findViewById(fakeR.getId("id", "share_text_view")).setOnKeyListener(textListener); } @Override protected void onResume() { super.onResume(); ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); clipboardButton.setEnabled(clipboard.hasText()); } @Override public void onActivityResult(int requestCode, int resultCode, Intent intent) { if (resultCode == RESULT_OK) { switch (requestCode) { case PICK_BOOKMARK: case PICK_APP: showTextAsBarcode(intent.getStringExtra(Browser.BookmarkColumns.URL)); break; case PICK_CONTACT: // Data field is content://contacts/people/984 showContactAsBarcode(intent.getData()); break; } } } private void showTextAsBarcode(String text) { Log.i(TAG, "Showing text as barcode: " + text); if (text == null) { return; // Show error? } Intent intent = new Intent(Intents.Encode.ACTION); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); intent.putExtra(Intents.Encode.TYPE, Contents.Type.TEXT); intent.putExtra(Intents.Encode.DATA, text); intent.putExtra(Intents.Encode.FORMAT, BarcodeFormat.QR_CODE.toString()); startActivity(intent); } /** * Takes a contact Uri and does the necessary database lookups to retrieve that person's info, * then sends an Encode intent to render it as a QR Code. * * @param contactUri A Uri of the form content://contacts/people/17 */ private void showContactAsBarcode(Uri contactUri) { Log.i(TAG, "Showing contact URI as barcode: " + contactUri); if (contactUri == null) { return; // Show error? } ContentResolver resolver = getContentResolver(); Bundle bundle = new Bundle(); Cursor cursor; try { // We're seeing about six reports a week of this exception although I don't understand why. cursor = resolver.query(contactUri, null, null, null, null); } catch (IllegalArgumentException e) { return; } if (cursor == null) { return; } String id; String name; boolean hasPhone; try { if (!cursor.moveToFirst()) { return; } id = cursor.getString(cursor.getColumnIndex(BaseColumns._ID)); name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)); hasPhone = cursor.getInt(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)) > 0; } finally { cursor.close(); } // Don't require a name to be present, this contact might be just a phone number. if (name != null && name.length() > 0) { bundle.putString(ContactsContract.Intents.Insert.NAME, massageContactData(name)); } if (hasPhone) { Cursor phonesCursor = resolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + '=' + id, null, null); if (phonesCursor != null) { try { int foundPhone = 0; int phonesNumberColumn = phonesCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER); while (phonesCursor.moveToNext() && foundPhone < Contents.PHONE_KEYS.length) { String number = phonesCursor.getString(phonesNumberColumn); if (number != null && number.length() > 0) { bundle.putString(Contents.PHONE_KEYS[foundPhone], massageContactData(number)); } foundPhone++; } } finally { phonesCursor.close(); } } } Cursor methodsCursor = resolver.query(ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_URI, null, ContactsContract.CommonDataKinds.StructuredPostal.CONTACT_ID + '=' + id, null, null); if (methodsCursor != null) { try { if (methodsCursor.moveToNext()) { String data = methodsCursor.getString( methodsCursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS)); if (data != null && data.length() > 0) { bundle.putString(ContactsContract.Intents.Insert.POSTAL, massageContactData(data)); } } } finally { methodsCursor.close(); } } Cursor emailCursor = resolver.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI, null, ContactsContract.CommonDataKinds.Email.CONTACT_ID + '=' + id, null, null); if (emailCursor != null) { try { int foundEmail = 0; int emailColumn = emailCursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA); while (emailCursor.moveToNext() && foundEmail < Contents.EMAIL_KEYS.length) { String email = emailCursor.getString(emailColumn); if (email != null && email.length() > 0) { bundle.putString(Contents.EMAIL_KEYS[foundEmail], massageContactData(email)); } foundEmail++; } } finally { emailCursor.close(); } } Intent intent = new Intent(Intents.Encode.ACTION); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); intent.putExtra(Intents.Encode.TYPE, Contents.Type.CONTACT); intent.putExtra(Intents.Encode.DATA, bundle); intent.putExtra(Intents.Encode.FORMAT, BarcodeFormat.QR_CODE.toString()); Log.i(TAG, "Sending bundle for encoding: " + bundle); startActivity(intent); } private static String massageContactData(String data) { // For now -- make sure we don't put newlines in shared contact data. It messes up // any known encoding of contact data. Replace with space. if (data.indexOf('\n') >= 0) { data = data.replace("\n", " "); } if (data.indexOf('\r') >= 0) { data = data.replace("\r", " "); } return data; } }