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