Noticed we should just use BarcodeFormat constants in the client for simplicity
[zxing.git] / android / src / com / google / zxing / client / android / share / ShareActivity.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.share;
18
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.Browser;
26 import android.provider.Contacts;
27 import android.provider.BaseColumns;
28 import android.text.ClipboardManager;
29 import android.view.View;
30 import android.widget.Button;
31 import com.google.zxing.BarcodeFormat;
32 import com.google.zxing.client.android.Intents;
33 import com.google.zxing.client.android.Contents;
34 import com.google.zxing.client.android.R;
35
36 /**
37  * Barcode Scanner can share data like contacts and bookmarks by displaying a QR Code on screen,
38  * such that another user can scan the barcode with their phone.
39  *
40  * @author dswitkin@google.com (Daniel Switkin)
41  */
42 public final class ShareActivity extends Activity {
43
44   private static final int PICK_BOOKMARK = 0;
45   private static final int PICK_CONTACT = 1;
46   private static final int PICK_APP = 2;
47
48   //private static final int METHODS_ID_COLUMN = 0;
49   private static final int METHODS_KIND_COLUMN = 1;
50   private static final int METHODS_DATA_COLUMN = 2;
51
52   private static final String[] METHODS_PROJECTION = {
53       BaseColumns._ID, // 0
54       Contacts.ContactMethodsColumns.KIND, // 1
55       Contacts.ContactMethodsColumns.DATA, // 2
56   };
57
58   private static final int PHONES_NUMBER_COLUMN = 1;
59
60   private static final String[] PHONES_PROJECTION = {
61       BaseColumns._ID, // 0
62       Contacts.PhonesColumns.NUMBER // 1
63   };
64
65   private Button clipboardButton;
66
67   private final Button.OnClickListener contactListener = new Button.OnClickListener() {
68     public void onClick(View v) {
69       Intent intent = new Intent(Intent.ACTION_PICK, Contacts.People.CONTENT_URI);
70       intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
71       startActivityForResult(intent, PICK_CONTACT);
72     }
73   };
74
75   private final Button.OnClickListener bookmarkListener = new Button.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, BookmarkPickerActivity.class.getName());
80       startActivityForResult(intent, PICK_BOOKMARK);
81     }
82   };
83
84   private final Button.OnClickListener appListener = new Button.OnClickListener() {
85     public void onClick(View v) {
86       Intent intent = new Intent(Intent.ACTION_PICK);
87       intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
88       intent.setClassName(ShareActivity.this, AppPickerActivity.class.getName());
89       startActivityForResult(intent, PICK_APP);
90     }
91   };
92
93   private final Button.OnClickListener clipboardListener = new Button.OnClickListener() {
94     public void onClick(View v) {
95       ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
96       // Should always be true, because we grey out the clipboard button in onResume() if it's empty
97       if (clipboard.hasText()) {
98         Intent intent = new Intent(Intents.Encode.ACTION);
99         intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
100         intent.putExtra(Intents.Encode.TYPE, Contents.Type.TEXT);
101         intent.putExtra(Intents.Encode.DATA, clipboard.getText().toString());
102         intent.putExtra(Intents.Encode.FORMAT, BarcodeFormat.QR_CODE.toString());
103         startActivity(intent);
104       }
105     }
106   };
107
108   @Override
109   public void onCreate(Bundle icicle) {
110     super.onCreate(icicle);
111     setContentView(R.layout.share);
112
113     findViewById(R.id.contact_button).setOnClickListener(contactListener);
114     findViewById(R.id.bookmark_button).setOnClickListener(bookmarkListener);
115     findViewById(R.id.app_button).setOnClickListener(appListener);
116     clipboardButton = (Button) findViewById(R.id.clipboard_button);
117     clipboardButton.setOnClickListener(clipboardListener);
118   }
119
120   @Override
121   protected void onResume() {
122     super.onResume();
123
124     ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
125     if (clipboard.hasText()) {
126       clipboardButton.setEnabled(true);
127       clipboardButton.setText(R.string.button_share_clipboard);
128     } else {
129       clipboardButton.setEnabled(false);
130       clipboardButton.setText(R.string.button_clipboard_empty);
131     }
132   }
133
134   @Override
135   public void onActivityResult(int requestCode, int resultCode, Intent intent) {
136     if (resultCode == RESULT_OK) {
137       switch (requestCode) {
138         case PICK_BOOKMARK:
139         case PICK_APP:
140           showTextAsBarcode(intent.getStringExtra(Browser.BookmarkColumns.URL));
141           break;
142         case PICK_CONTACT:
143           // Data field is content://contacts/people/984
144           showContactAsBarcode(intent.getData());
145           break;
146       }
147     }
148   }
149
150   private void showTextAsBarcode(String text) {
151     Intent intent = new Intent(Intents.Encode.ACTION);
152     intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
153     intent.putExtra(Intents.Encode.TYPE, Contents.Type.TEXT);
154     intent.putExtra(Intents.Encode.DATA, text);
155     intent.putExtra(Intents.Encode.FORMAT, BarcodeFormat.QR_CODE.toString());
156     startActivity(intent);
157   }
158
159   /**
160    * Takes a contact Uri and does the necessary database lookups to retrieve that person's info,
161    * then sends an Encode intent to render it as a QR Code.
162    *
163    * @param contactUri A Uri of the form content://contacts/people/17
164    */
165   private void showContactAsBarcode(Uri contactUri) {
166     ContentResolver resolver = getContentResolver();
167     Cursor contactCursor = resolver.query(contactUri, null, null, null, null);
168     Bundle bundle = new Bundle();
169     if (contactCursor != null && contactCursor.moveToFirst()) {
170       int nameColumn = contactCursor.getColumnIndex(Contacts.PeopleColumns.NAME);
171       String name = contactCursor.getString(nameColumn);
172
173       // Don't require a name to be present, this contact might be just a phone number.
174       if (name != null && name.length() > 0) {
175         bundle.putString(Contacts.Intents.Insert.NAME, massageContactData(name));
176       }
177       contactCursor.close();
178
179       Uri phonesUri = Uri.withAppendedPath(contactUri, Contacts.People.Phones.CONTENT_DIRECTORY);
180       Cursor phonesCursor = resolver.query(phonesUri, PHONES_PROJECTION, null, null, null);
181       if (phonesCursor != null) {
182         int foundPhone = 0;
183         while (phonesCursor.moveToNext()) {
184           String number = phonesCursor.getString(PHONES_NUMBER_COLUMN);
185           if (foundPhone < Contents.PHONE_KEYS.length) {
186             bundle.putString(Contents.PHONE_KEYS[foundPhone], massageContactData(number));
187             foundPhone++;
188           }
189         }
190         phonesCursor.close();
191       }
192
193       Uri methodsUri = Uri.withAppendedPath(contactUri,
194           Contacts.People.ContactMethods.CONTENT_DIRECTORY);
195       Cursor methodsCursor = resolver.query(methodsUri, METHODS_PROJECTION, null, null, null);
196       if (methodsCursor != null) {
197         int foundEmail = 0;
198         boolean foundPostal = false;
199         while (methodsCursor.moveToNext()) {
200           int kind = methodsCursor.getInt(METHODS_KIND_COLUMN);
201           String data = methodsCursor.getString(METHODS_DATA_COLUMN);
202           switch (kind) {
203             case Contacts.KIND_EMAIL:
204               if (foundEmail < Contents.EMAIL_KEYS.length) {
205                 bundle.putString(Contents.EMAIL_KEYS[foundEmail], massageContactData(data));
206                 foundEmail++;
207               }
208               break;
209             case Contacts.KIND_POSTAL:
210               if (!foundPostal) {
211                 bundle.putString(Contacts.Intents.Insert.POSTAL, massageContactData(data));
212                 foundPostal = true;
213               }
214               break;
215           }
216         }
217         methodsCursor.close();
218       }
219
220       Intent intent = new Intent(Intents.Encode.ACTION);
221       intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);      
222       intent.putExtra(Intents.Encode.TYPE, Contents.Type.CONTACT);
223       intent.putExtra(Intents.Encode.DATA, bundle);
224       intent.putExtra(Intents.Encode.FORMAT, BarcodeFormat.QR_CODE.toString());
225
226       startActivity(intent);
227     }
228   }
229
230   private static String massageContactData(String data) {
231     // For now -- make sure we don't put newlines in shared contact data. It messes up
232     // any known encoding of contact data. Replace with space.
233     if (data.indexOf('\n') >= 0) {
234       data = data.replace("\n", " ");
235     }
236     if (data.indexOf('\r') >= 0) {
237       data = data.replace("\r", " ");
238     }
239     return data;
240   }
241 }