diff options
24 files changed, 740 insertions, 590 deletions
diff --git a/OpenPGP-Keychain/build.gradle b/OpenPGP-Keychain/build.gradle index 0fffd1fd3..566559a00 100644 --- a/OpenPGP-Keychain/build.gradle +++ b/OpenPGP-Keychain/build.gradle @@ -24,8 +24,9 @@ dependencies { compile project(':libraries:ActionBarSherlock') compile project(':libraries:HtmlTextView') compile project(':libraries:StickyListHeaders:library') - compile project(':libraries:zxing') compile project(':libraries:AndroidBootstrap') + compile project(':libraries:zxing') + compile project(':libraries:zxing-android-integration') } android { diff --git a/OpenPGP-Keychain/libs/barcodescanner-android-integration-supportv4.jar b/OpenPGP-Keychain/libs/barcodescanner-android-integration-supportv4.jar Binary files differdeleted file mode 100644 index 4a7f1a39c..000000000 --- a/OpenPGP-Keychain/libs/barcodescanner-android-integration-supportv4.jar +++ /dev/null diff --git a/OpenPGP-Keychain/project.properties b/OpenPGP-Keychain/project.properties index 76caac668..96546d84f 100644 --- a/OpenPGP-Keychain/project.properties +++ b/OpenPGP-Keychain/project.properties @@ -14,3 +14,4 @@ android.library.reference.2=../libraries/HtmlTextView android.library.reference.3=../libraries/StickyListHeaders/library android.library.reference.4=../libraries/zxing android.library.reference.5=../libraries/AndroidBootstrap +android.library.reference.6=../libraries/zxing-android-integration diff --git a/OpenPGP-Keychain/res/layout/edit_key_activity.xml b/OpenPGP-Keychain/res/layout/edit_key_activity.xml index f8597b0df..351aec512 100644 --- a/OpenPGP-Keychain/res/layout/edit_key_activity.xml +++ b/OpenPGP-Keychain/res/layout/edit_key_activity.xml @@ -45,7 +45,7 @@ <com.beardedhen.androidbootstrap.BootstrapButton android:id="@+id/edit_key_btn_change_pass_phrase" android:layout_width="match_parent" - android:layout_height="wrap_content" + android:layout_height="60dp" android:padding="4dp" android:text="@string/btn_set_passphrase" bootstrapbutton:bb_icon_left="fa-pencil" diff --git a/OpenPGP-Keychain/res/layout/edit_key_key_item.xml b/OpenPGP-Keychain/res/layout/edit_key_key_item.xml index 11bce64ee..ad6bd6779 100644 --- a/OpenPGP-Keychain/res/layout/edit_key_key_item.xml +++ b/OpenPGP-Keychain/res/layout/edit_key_key_item.xml @@ -82,7 +82,7 @@ <com.beardedhen.androidbootstrap.BootstrapButton android:id="@+id/expiry" android:layout_width="match_parent" - android:layout_height="wrap_content" + android:layout_height="60dp" android:text="@string/none" bootstrapbutton:bb_type="default" /> </TableRow> diff --git a/OpenPGP-Keychain/res/layout/import_keys_clipboard_fragment.xml b/OpenPGP-Keychain/res/layout/import_keys_clipboard_fragment.xml index 961c6523a..abde4e972 100644 --- a/OpenPGP-Keychain/res/layout/import_keys_clipboard_fragment.xml +++ b/OpenPGP-Keychain/res/layout/import_keys_clipboard_fragment.xml @@ -10,7 +10,8 @@ android:layout_width="match_parent" android:layout_height="60dp" android:layout_margin="10dp" - android:text="@string/import_from_clipboard" + android:text="@string/import_clipboard_button" + bootstrapbutton:bb_icon_left="fa-clipboard" bootstrapbutton:bb_size="default" bootstrapbutton:bb_type="default" /> diff --git a/OpenPGP-Keychain/res/layout/import_keys_nfc_fragment.xml b/OpenPGP-Keychain/res/layout/import_keys_nfc_fragment.xml index 6d70578af..203cc6ad6 100644 --- a/OpenPGP-Keychain/res/layout/import_keys_nfc_fragment.xml +++ b/OpenPGP-Keychain/res/layout/import_keys_nfc_fragment.xml @@ -1,17 +1,28 @@ <?xml version="1.0" encoding="utf-8"?> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto" android:layout_width="fill_parent" android:layout_height="wrap_content" + android:layout_margin="10dp" android:orientation="horizontal" > <com.beardedhen.androidbootstrap.BootstrapButton android:id="@+id/import_nfc_button" - android:layout_width="match_parent" + android:layout_width="wrap_content" android:layout_height="60dp" - android:layout_margin="10dp" - android:text="@string/menu_import_from_nfc" + android:layout_alignParentRight="true" + android:layout_alignParentTop="true" + android:layout_marginLeft="10dp" + android:text="@string/import_nfc_help_button" + bootstrapbutton:bb_icon_left="fa-question" bootstrapbutton:bb_size="default" bootstrapbutton:bb_type="default" /> -</LinearLayout>
\ No newline at end of file + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerVertical="true" + android:layout_toLeftOf="@+id/import_nfc_button" + android:text="@string/import_nfc_text" /> + +</RelativeLayout>
\ No newline at end of file diff --git a/OpenPGP-Keychain/res/layout/import_keys_qr_code_fragment.xml b/OpenPGP-Keychain/res/layout/import_keys_qr_code_fragment.xml index f3c09a41d..5229e7cf5 100644 --- a/OpenPGP-Keychain/res/layout/import_keys_qr_code_fragment.xml +++ b/OpenPGP-Keychain/res/layout/import_keys_qr_code_fragment.xml @@ -10,7 +10,7 @@ android:layout_width="match_parent" android:layout_height="60dp" android:layout_margin="10dp" - android:text="@string/menu_import_from_qr_code" + android:text="@string/import_qr_scan_button" bootstrapbutton:bb_icon_left="fa-barcode" bootstrapbutton:bb_size="default" bootstrapbutton:bb_type="default" /> @@ -18,13 +18,19 @@ <TextView android:id="@+id/import_qrcode_text" android:layout_width="wrap_content" - android:layout_height="wrap_content" /> + android:layout_height="wrap_content" + android:paddingLeft="10dp" + android:paddingRight="10dp" + android:visibility="gone" /> <ProgressBar android:id="@+id/import_qrcode_progress" style="?android:attr/progressBarStyleHorizontal" android:layout_width="match_parent" android:layout_height="wrap_content" - android:progress="0" /> + android:paddingLeft="10dp" + android:paddingRight="10dp" + android:progress="0" + android:visibility="gone" /> </LinearLayout>
\ No newline at end of file diff --git a/OpenPGP-Keychain/res/values/strings.xml b/OpenPGP-Keychain/res/values/strings.xml index 87f7c5a8f..ab2c638fe 100644 --- a/OpenPGP-Keychain/res/values/strings.xml +++ b/OpenPGP-Keychain/res/values/strings.xml @@ -221,6 +221,7 @@ <string name="key_sign_success">Successfully signed key</string> <string name="list_empty">This list is empty!</string> <string name="nfc_successfull">Successfully sent key with NFC Beam!</string> + <string name="key_copied_to_clipboard">Key has been copied to the clipboard!</string> <!-- errors @@ -322,9 +323,19 @@ <string name="import_import">Import selected keys</string> <string name="import_sign_and_upload">Import, Sign, and upload selected keys</string> <string name="import_from_clipboard">Import from Clipboard</string> - <string name="import_qr_code_missing">Missing QR Codes: %1$s</string> + + <plurals name="import_qr_code_missing"> + <item quantity="one">Missing QR Code with ID %1$s</item> + <item quantity="other">Missing QR Codes with IDs %1$s</item> + </plurals> + + <string name="import_qr_code_start_with_one">Please start with QR Code with ID 1</string> <string name="import_qr_code_wrong">QR Code malformed! Please try again!</string> <string name="import_qr_code_finished">QR Code scanning finished!</string> + <string name="import_qr_scan_button">Scan QR Code with \'Barcode Scanner\'</string> + <string name="import_nfc_text">To exchange keys via NFC, the device needs to be unlocked.</string> + <string name="import_nfc_help_button">Help</string> + <string name="import_clipboard_button">Get key from clipboard</string> <!-- Intent labels --> <string name="intent_decrypt_file">OpenPGP: Decrypt File</string> @@ -354,7 +365,7 @@ <!-- Share --> <string name="share_qr_code_dialog_start">Go through all QR Codes using \'Next\', and scan them one by one.</string> - <string name="share_qr_code_dialog_progress">QR Code %1$d of %2$d</string> + <string name="share_qr_code_dialog_progress">QR Code with ID %1$d of %2$d</string> <string name="share_nfc_dialog">Share with NFC</string> <!-- Key list --> @@ -366,7 +377,7 @@ <string name="key_list_empty_text1">No keys available yet…</string> <string name="key_list_empty_text2">You can start by</string> <string name="key_list_empty_text3">or</string> - <string name="key_list_empty_button_create">creating your own key pair</string> + <string name="key_list_empty_button_create">creating your own key</string> <string name="key_list_empty_button_import">importing keys.</string> <!-- Key view --> diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/ExportHelper.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/ExportHelper.java index 261e26be6..7647da794 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/ExportHelper.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/ExportHelper.java @@ -80,13 +80,7 @@ public class ExportHelper { Bundle data = message.getData(); mExportFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME); - long keyRingRowId = Long.valueOf(dataUri.getLastPathSegment()); - - // TODO? - long keyRingMasterKeyId = ProviderHelper.getSecretMasterKeyId(activity, - keyRingRowId); - - exportKeys(keyRingMasterKeyId, keyType); + exportKeys(dataUri, keyType); } } }; @@ -97,11 +91,12 @@ public class ExportHelper { DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() { public void run() { String title = null; - if (dataUri != null) { - // single key export - title = activity.getString(R.string.title_export_key); - } else { + if (dataUri == null) { + // export all keys title = activity.getString(R.string.title_export_keys); + } else { + // export only key specified at data uri + title = activity.getString(R.string.title_export_key); } String message = null; @@ -125,7 +120,7 @@ public class ExportHelper { * @param keyRingMasterKeyId * if -1 export all keys */ - public void exportKeys(long keyRingMasterKeyId, int keyType) { + public void exportKeys(Uri dataUri, int keyType) { Log.d(Constants.TAG, "exportKeys started"); // Send all information needed to service to export key in other thread @@ -139,9 +134,12 @@ public class ExportHelper { data.putString(KeychainIntentService.EXPORT_FILENAME, mExportFilename); data.putInt(KeychainIntentService.EXPORT_KEY_TYPE, keyType); - if (keyRingMasterKeyId == -1) { + if (dataUri == null) { data.putBoolean(KeychainIntentService.EXPORT_ALL, true); } else { + // TODO: put data uri into service??? + long keyRingMasterKeyId = ProviderHelper.getMasterKeyId(activity, dataUri); + data.putLong(KeychainIntentService.EXPORT_KEY_RING_MASTER_KEY_ID, keyRingMasterKeyId); } diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java index 7d8f4154f..00a648355 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -76,6 +76,8 @@ public class ImportKeysActivity extends DrawerActivity implements OnNavigationLi OnNavigationListener mOnNavigationListener; String[] mNavigationStrings; + Fragment mCurrentFragment; + BootstrapButton mImportButton; BootstrapButton mImportSignUploadButton; @@ -226,12 +228,12 @@ public class ImportKeysActivity extends DrawerActivity implements OnNavigationLi } private void loadFragment(Class<?> clss, Bundle args, String tag) { - Fragment fragment = Fragment.instantiate(this, clss.getName(), args); + mCurrentFragment = Fragment.instantiate(this, clss.getName(), args); FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); // Replace whatever is in the fragment container with this fragment // and give the fragment a tag name equal to the string at the position selected - ft.replace(R.id.import_navigation_fragment, fragment, tag); + ft.replace(R.id.import_navigation_fragment, mCurrentFragment, tag); // Apply changes ft.commit(); } @@ -298,6 +300,15 @@ public class ImportKeysActivity extends DrawerActivity implements OnNavigationLi // } // } + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + // required for qr code scanning + if (mCurrentFragment != null) { + mCurrentFragment.onActivityResult(requestCode, resultCode, data); + } + // super.onActivityResult(requestCode, resultCode, data); + } + /** * Import keys with mImportData */ diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java index f9ead3a94..bdedbec0a 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java @@ -35,7 +35,7 @@ import android.widget.TextView; import android.widget.Toast; import com.beardedhen.androidbootstrap.BootstrapButton; -import com.google.zxing.integration.android.IntentIntegratorSupportV4; +import com.google.zxing.integration.android.IntentIntegrator; import com.google.zxing.integration.android.IntentResult; public class ImportKeysQrCodeFragment extends Fragment { @@ -45,7 +45,7 @@ public class ImportKeysQrCodeFragment extends Fragment { private TextView mText; private ProgressBar mProgress; - private String[] scannedContent; + private String[] mScannedContent; /** * Creates new instance of this fragment @@ -75,7 +75,7 @@ public class ImportKeysQrCodeFragment extends Fragment { @Override public void onClick(View v) { // scan using xzing's Barcode Scanner - new IntentIntegratorSupportV4(ImportKeysQrCodeFragment.this).initiateScan(); + new IntentIntegrator(getActivity()).initiateScan(); } }); @@ -92,9 +92,9 @@ public class ImportKeysQrCodeFragment extends Fragment { @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { - case IntentIntegratorSupportV4.REQUEST_CODE: { - IntentResult scanResult = IntentIntegratorSupportV4.parseActivityResult(requestCode, - resultCode, data); + case IntentIntegrator.REQUEST_CODE: { + IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, + data); if (scanResult != null && scanResult.getFormatName() != null) { Log.d(Constants.TAG, scanResult.getContents()); @@ -117,36 +117,50 @@ public class ImportKeysQrCodeFragment extends Fragment { // first qr code -> setup if (counter == 0) { - scannedContent = new String[size]; + mScannedContent = new String[size]; mProgress.setMax(size); + mProgress.setVisibility(View.VISIBLE); + mText.setVisibility(View.VISIBLE); + } + + if (mScannedContent == null || counter > mScannedContent.length) { + Toast.makeText(getActivity(), R.string.import_qr_code_start_with_one, + Toast.LENGTH_LONG).show(); + return; } // save scanned content - scannedContent[counter] = content; + mScannedContent[counter] = content; // get missing numbers ArrayList<Integer> missing = new ArrayList<Integer>(); - for (int i = 0; i < scannedContent.length; i++) { - if (scannedContent[i] == null) { + for (int i = 0; i < mScannedContent.length; i++) { + if (mScannedContent[i] == null) { missing.add(i); } } // update progress and text - mProgress.setProgress(scannedContent.length - missing.size()); + int alreadyScanned = mScannedContent.length - missing.size(); + mProgress.setProgress(alreadyScanned); + String missingString = ""; for (int m : missing) { - if (!missingString.equals("")) + if (!missingString.equals("")) { missingString += ", "; + } missingString += String.valueOf(m + 1); } - mText.setText(getString(R.string.import_qr_code_missing, missingString)); + + String missingText = getResources().getQuantityString( + R.plurals.import_qr_code_missing, missing.size(), missingString); + mText.setText(missingText); // finished! if (missing.size() == 0) { mText.setText(R.string.import_qr_code_finished); String result = ""; - for (String in : scannedContent) { + for (String in : mScannedContent) { result += in; } mImportActivity.loadCallback(result.getBytes(), null); diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index e2f90e87c..90655ee46 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -433,6 +433,8 @@ public class ViewKeyActivity extends SherlockFragmentActivity implements CreateN new long[] { masterKeyId }); ClipboardReflection.copyToClipboard(this, keyringArmored.get(0)); + Toast.makeText(getApplicationContext(), R.string.key_copied_to_clipboard, Toast.LENGTH_LONG) + .show(); } private void shareNfc() { diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/ExpandableListFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/ExpandableListFragment.java deleted file mode 100644 index 54022342f..000000000 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/ExpandableListFragment.java +++ /dev/null @@ -1,530 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * 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 org.sufficientlysecure.keychain.ui.widget; - -import android.content.Context; -import android.os.Bundle; -import android.os.Handler; -import android.support.v4.app.Fragment; -import android.view.ContextMenu; -import android.view.ContextMenu.ContextMenuInfo; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnCreateContextMenuListener; -import android.view.ViewGroup; -import android.view.animation.AnimationUtils; -import android.widget.AdapterView; -import android.widget.ExpandableListAdapter; -import android.widget.ExpandableListView; -import android.widget.FrameLayout; -import android.widget.LinearLayout; -import android.widget.ListAdapter; -import android.widget.ProgressBar; -import android.widget.TextView; - -/** - * @author Khoa Tran - * - * @see android.support.v4.app.ListFragment - * @see android.app.ExpandableListActivity - * - * ExpandableListFragment for Android < 3.0 - * - * from - * http://stackoverflow.com/questions/6051050/expandablelistfragment-with-loadermanager-for- - * compatibility-package - * - */ -public class ExpandableListFragment extends Fragment implements OnCreateContextMenuListener, - ExpandableListView.OnChildClickListener, ExpandableListView.OnGroupCollapseListener, - ExpandableListView.OnGroupExpandListener { - - static final int INTERNAL_EMPTY_ID = 0x00ff0001; - static final int INTERNAL_PROGRESS_CONTAINER_ID = 0x00ff0002; - static final int INTERNAL_LIST_CONTAINER_ID = 0x00ff0003; - - final private Handler mHandler = new Handler(); - - final private Runnable mRequestFocus = new Runnable() { - public void run() { - mExpandableList.focusableViewAvailable(mExpandableList); - } - }; - - final private AdapterView.OnItemClickListener mOnClickListener = new AdapterView.OnItemClickListener() { - public void onItemClick(AdapterView<?> parent, View v, int position, long id) { - onListItemClick((ExpandableListView) parent, v, position, id); - } - }; - - ExpandableListAdapter mAdapter; - ExpandableListView mExpandableList; - boolean mFinishedStart = false; - View mEmptyView; - TextView mStandardEmptyView; - View mProgressContainer; - View mExpandableListContainer; - CharSequence mEmptyText; - boolean mExpandableListShown; - - public ExpandableListFragment() { - } - - /** - * Provide default implementation to return a simple list view. Subclasses can override to - * replace with their own layout. If doing so, the returned view hierarchy <em>must</em> have a - * ListView whose id is {@link android.R.id#list android.R.id.list} and can optionally have a - * sibling view id {@link android.R.id#empty android.R.id.empty} that is to be shown when the - * list is empty. - * - * <p> - * If you are overriding this method with your own custom content, consider including the - * standard layout {@link android.R.layout#list_content} in your layout file, so that you - * continue to retain all of the standard behavior of ListFragment. In particular, this is - * currently the only way to have the built-in indeterminant progress state be shown. - */ - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - final Context context = getActivity(); - - FrameLayout root = new FrameLayout(context); - - // ------------------------------------------------------------------ - - LinearLayout pframe = new LinearLayout(context); - pframe.setId(INTERNAL_PROGRESS_CONTAINER_ID); - pframe.setOrientation(LinearLayout.VERTICAL); - pframe.setVisibility(View.GONE); - pframe.setGravity(Gravity.CENTER); - - ProgressBar progress = new ProgressBar(context, null, android.R.attr.progressBarStyleLarge); - pframe.addView(progress, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT)); - - root.addView(pframe, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - // ------------------------------------------------------------------ - - FrameLayout lframe = new FrameLayout(context); - lframe.setId(INTERNAL_LIST_CONTAINER_ID); - - TextView tv = new TextView(getActivity()); - tv.setId(INTERNAL_EMPTY_ID); - tv.setGravity(Gravity.CENTER); - lframe.addView(tv, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - ExpandableListView lv = new ExpandableListView(getActivity()); - lv.setId(android.R.id.list); - lv.setDrawSelectorOnTop(false); - lframe.addView(lv, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - root.addView(lframe, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - // ------------------------------------------------------------------ - - root.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - return root; - } - - /** - * Attach to list view once the view hierarchy has been created. - */ - @Override - public void onViewCreated(View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - ensureList(); - } - - /** - * Detach from list view. - */ - @Override - public void onDestroyView() { - mHandler.removeCallbacks(mRequestFocus); - mExpandableList = null; - mExpandableListShown = false; - mEmptyView = mProgressContainer = mExpandableListContainer = null; - mStandardEmptyView = null; - super.onDestroyView(); - } - - /** - * This method will be called when an item in the list is selected. Subclasses should override. - * Subclasses can call getListView().getItemAtPosition(position) if they need to access the data - * associated with the selected item. - * - * @param l - * The ListView where the click happened - * @param v - * The view that was clicked within the ListView - * @param position - * The position of the view in the list - * @param id - * The row id of the item that was clicked - */ - public void onListItemClick(ExpandableListView l, View v, int position, long id) { - } - - /** - * Provide the cursor for the list view. - */ - public void setListAdapter(ExpandableListAdapter adapter) { - boolean hadAdapter = mAdapter != null; - mAdapter = adapter; - if (mExpandableList != null) { - mExpandableList.setAdapter(adapter); - if (!mExpandableListShown && !hadAdapter) { - // The list was hidden, and previously didn't have an - // adapter. It is now time to show it. - setListShown(true, getView().getWindowToken() != null); - } - } - } - - /** - * Set the currently selected list item to the specified position with the adapter's data - * - * @param position - */ - public void setSelection(int position) { - ensureList(); - mExpandableList.setSelection(position); - } - - /** - * Get the position of the currently selected list item. - */ - public int getSelectedItemPosition() { - ensureList(); - return mExpandableList.getSelectedItemPosition(); - } - - /** - * Get the cursor row ID of the currently selected list item. - */ - public long getSelectedItemId() { - ensureList(); - return mExpandableList.getSelectedItemId(); - } - - /** - * Get the activity's list view widget. - */ - public ExpandableListView getListView() { - ensureList(); - return mExpandableList; - } - - /** - * The default content for a ListFragment has a TextView that can be shown when the list is - * empty. If you would like to have it shown, call this method to supply the text it should use. - */ - public void setEmptyText(CharSequence text) { - ensureList(); - if (mStandardEmptyView == null) { - throw new IllegalStateException("Can't be used with a custom content view"); - } - mStandardEmptyView.setText(text); - if (mEmptyText == null) { - mExpandableList.setEmptyView(mStandardEmptyView); - } - mEmptyText = text; - } - - /** - * Control whether the list is being displayed. You can make it not displayed if you are waiting - * for the initial data to show in it. During this time an indeterminant progress indicator will - * be shown instead. - * - * <p> - * Applications do not normally need to use this themselves. The default behavior of - * ListFragment is to start with the list not being shown, only showing it once an adapter is - * given with {@link #setListAdapter(ListAdapter)}. If the list at that point had not been - * shown, when it does get shown it will be do without the user ever seeing the hidden state. - * - * @param shown - * If true, the list view is shown; if false, the progress indicator. The initial - * value is true. - */ - public void setListShown(boolean shown) { - setListShown(shown, true); - } - - /** - * Like {@link #setListShown(boolean)}, but no animation is used when transitioning from the - * previous state. - */ - public void setListShownNoAnimation(boolean shown) { - setListShown(shown, false); - } - - /** - * Control whether the list is being displayed. You can make it not displayed if you are waiting - * for the initial data to show in it. During this time an indeterminant progress indicator will - * be shown instead. - * - * @param shown - * If true, the list view is shown; if false, the progress indicator. The initial - * value is true. - * @param animate - * If true, an animation will be used to transition to the new state. - */ - private void setListShown(boolean shown, boolean animate) { - ensureList(); - if (mProgressContainer == null) { - throw new IllegalStateException("Can't be used with a custom content view"); - } - if (mExpandableListShown == shown) { - return; - } - mExpandableListShown = shown; - if (shown) { - if (animate) { - mProgressContainer.startAnimation(AnimationUtils.loadAnimation(getActivity(), - android.R.anim.fade_out)); - mExpandableListContainer.startAnimation(AnimationUtils.loadAnimation(getActivity(), - android.R.anim.fade_in)); - } else { - mProgressContainer.clearAnimation(); - mExpandableListContainer.clearAnimation(); - } - mProgressContainer.setVisibility(View.GONE); - mExpandableListContainer.setVisibility(View.VISIBLE); - } else { - if (animate) { - mProgressContainer.startAnimation(AnimationUtils.loadAnimation(getActivity(), - android.R.anim.fade_in)); - mExpandableListContainer.startAnimation(AnimationUtils.loadAnimation(getActivity(), - android.R.anim.fade_out)); - } else { - mProgressContainer.clearAnimation(); - mExpandableListContainer.clearAnimation(); - } - mProgressContainer.setVisibility(View.VISIBLE); - mExpandableListContainer.setVisibility(View.GONE); - } - } - - /** - * Get the ListAdapter associated with this activity's ListView. - */ - public ExpandableListAdapter getListAdapter() { - return mAdapter; - } - - private void ensureList() { - if (mExpandableList != null) { - return; - } - View root = getView(); - if (root == null) { - throw new IllegalStateException("Content view not yet created"); - } - if (root instanceof ExpandableListView) { - mExpandableList = (ExpandableListView) root; - } else { - mStandardEmptyView = (TextView) root.findViewById(INTERNAL_EMPTY_ID); - if (mStandardEmptyView == null) { - mEmptyView = root.findViewById(android.R.id.empty); - } else { - mStandardEmptyView.setVisibility(View.GONE); - } - mProgressContainer = root.findViewById(INTERNAL_PROGRESS_CONTAINER_ID); - mExpandableListContainer = root.findViewById(INTERNAL_LIST_CONTAINER_ID); - View rawExpandableListView = root.findViewById(android.R.id.list); - if (!(rawExpandableListView instanceof ExpandableListView)) { - if (rawExpandableListView == null) { - throw new RuntimeException( - "Your content must have a ListView whose id attribute is " - + "'android.R.id.list'"); - } - throw new RuntimeException( - "Content has view with id attribute 'android.R.id.list' " - + "that is not a ListView class"); - } - mExpandableList = (ExpandableListView) rawExpandableListView; - if (mEmptyView != null) { - mExpandableList.setEmptyView(mEmptyView); - } else if (mEmptyText != null) { - mStandardEmptyView.setText(mEmptyText); - mExpandableList.setEmptyView(mStandardEmptyView); - } - } - mExpandableListShown = true; - mExpandableList.setOnItemClickListener(mOnClickListener); - if (mAdapter != null) { - ExpandableListAdapter adapter = mAdapter; - mAdapter = null; - setListAdapter(adapter); - } else { - // We are starting without an adapter, so assume we won't - // have our data right away and start with the progress indicator. - if (mProgressContainer != null) { - setListShown(false, false); - } - } - mHandler.post(mRequestFocus); - } - - /** - * Override this to populate the context menu when an item is long pressed. menuInfo will - * contain an {@link android.widget.ExpandableListView.ExpandableListContextMenuInfo} whose - * packedPosition is a packed position that should be used with - * {@link ExpandableListView#getPackedPositionType(long)} and the other similar methods. - * <p> - * {@inheritDoc} - */ - @Override - public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { - } - - /** - * Override this for receiving callbacks when a child has been clicked. - * <p> - * {@inheritDoc} - */ - public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, - int childPosition, long id) { - return false; - } - - /** - * Override this for receiving callbacks when a group has been collapsed. - */ - public void onGroupCollapse(int groupPosition) { - } - - /** - * Override this for receiving callbacks when a group has been expanded. - */ - public void onGroupExpand(int groupPosition) { - } - - // /** - // * Ensures the expandable list view has been created before Activity restores all - // * of the view states. - // * - // *@see Activity#onRestoreInstanceState(Bundle) - // */ - // @Override - // protected void onRestoreInstanceState(Bundle state) { - // ensureList(); - // super.onRestoreInstanceState(state); - // } - - /** - * Updates the screen state (current list and other views) when the content changes. - * - * @see Activity#onContentChanged() - */ - - public void onContentChanged() { - // super.onContentChanged(); - View emptyView = getView().findViewById(android.R.id.empty); - mExpandableList = (ExpandableListView) getView().findViewById(android.R.id.list); - if (mExpandableList == null) { - throw new RuntimeException( - "Your content must have a ExpandableListView whose id attribute is " - + "'android.R.id.list'"); - } - if (emptyView != null) { - mExpandableList.setEmptyView(emptyView); - } - mExpandableList.setOnChildClickListener(this); - mExpandableList.setOnGroupExpandListener(this); - mExpandableList.setOnGroupCollapseListener(this); - - if (mFinishedStart) { - setListAdapter(mAdapter); - } - mFinishedStart = true; - } - - /** - * Get the activity's expandable list view widget. This can be used to get the selection, set - * the selection, and many other useful functions. - * - * @see ExpandableListView - */ - public ExpandableListView getExpandableListView() { - ensureList(); - return mExpandableList; - } - - /** - * Get the ExpandableListAdapter associated with this activity's ExpandableListView. - */ - public ExpandableListAdapter getExpandableListAdapter() { - return mAdapter; - } - - /** - * Gets the ID of the currently selected group or child. - * - * @return The ID of the currently selected group or child. - */ - public long getSelectedId() { - return mExpandableList.getSelectedId(); - } - - /** - * Gets the position (in packed position representation) of the currently selected group or - * child. Use {@link ExpandableListView#getPackedPositionType}, - * {@link ExpandableListView#getPackedPositionGroup}, and - * {@link ExpandableListView#getPackedPositionChild} to unpack the returned packed position. - * - * @return A packed position representation containing the currently selected group or child's - * position and type. - */ - public long getSelectedPosition() { - return mExpandableList.getSelectedPosition(); - } - - /** - * Sets the selection to the specified child. If the child is in a collapsed group, the group - * will only be expanded and child subsequently selected if shouldExpandGroup is set to true, - * otherwise the method will return false. - * - * @param groupPosition - * The position of the group that contains the child. - * @param childPosition - * The position of the child within the group. - * @param shouldExpandGroup - * Whether the child's group should be expanded if it is collapsed. - * @return Whether the selection was successfully set on the child. - */ - public boolean setSelectedChild(int groupPosition, int childPosition, boolean shouldExpandGroup) { - return mExpandableList.setSelectedChild(groupPosition, childPosition, shouldExpandGroup); - } - - /** - * Sets the selection to the specified group. - * - * @param groupPosition - * The position of the group that should be selected. - */ - public void setSelectedGroup(int groupPosition) { - mExpandableList.setSelectedGroup(groupPosition); - } -}
\ No newline at end of file diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SectionView.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SectionView.java index b33dbe4c5..1f9605fb1 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SectionView.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SectionView.java @@ -190,7 +190,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor keySizeAdapter .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); keySize.setAdapter(keySizeAdapter); - keySize.setSelection(2); // Default to 2048 for the key length + keySize.setSelection(3); // Default to 4096 for the key length dialog.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface di, int id) { @@ -26,12 +26,8 @@ I am happy about every code contribution and appreciate your effort to help us d Android Studio is currently not supported or recommended! 1. File -> Import -> Android -> Existing Android Code Into Workspace, choose "libraries/ActionBarSherlock" -2. File -> Import -> Android -> Existing Android Code Into Workspace, choose "libraries/HtmlTextView" -3. File -> Import -> Android -> Existing Android Code Into Workspace, choose "libraries/StickyListHeaders/library" -4. File -> Import -> Android -> Existing Android Code Into Workspace, choose "libraries/zxing" -5. File -> Import -> Android -> Existing Android Code Into Workspace, choose "libraries/AndroidBootstrap" -6. File -> Import -> Android -> Existing Android Code Into Workspace, choose "OpenPGP-Keychain" -7. OpenPGP-Kechain can now be build +2. Repeat step 1 with "libraries/HtmlTextView", "libraries/StickyListHeaders/library", "libraries/AndroidBootstrap", "libraries/zxing", "libraries/zxing-android-integration", "OpenPGP-Keychain" +3. Now all required source files are available in Eclipse # Keychain API @@ -109,18 +105,16 @@ TODO # Libraries -## Build Barcode Scanner Integration +## ZXing Barcode Scanner Android Integration -1. Checkout their SVN (see http://code.google.com/p/zxing/source/checkout) -2. Change android-home variable in "build.properties" in the main directory to point to your Android SDK -3. Change directory to android-integration -4. Build using ``ant build`` -5. We use "android-integration-supportv4.jar" +Classes can be found under "libraries/zxing-android-integration/". -On error see: http://code.google.com/p/zxing/issues/detail?id=1207 +1. Checkout their SVN (see http://code.google.com/p/zxing/source/checkout) +2. Copy all classes from their android-integration folder to our library folder ## ZXing +Classes can be found under "libraries/zxing/". ZXing classes were extracted from the ZXing library (http://code.google.com/p/zxing/). Only classes related to QR Code generation are utilized. @@ -131,7 +125,7 @@ Only classes related to QR Code generation are utilized. Spongy Castle is the stock Bouncy Castle libraries with a couple of small changes to make it work on Android. OpenPGP Keychain uses a forked version with some small changes to improve key import speed. These changes have been sent to Bouncy Castle, and Spongy Castle will be used again when they have filtered down. see -* Spongy Castle: http://rtyley.github.com/spongycastle/ +* Spongy Castle: https://github.com/rtyley/spongycastle-old and http://rtyley.github.com/spongycastle/ * Fork: https://github.com/ashh87/spongycastle #### Bouncy Castle resources diff --git a/libraries/zxing-android-integration/.gitignore b/libraries/zxing-android-integration/.gitignore new file mode 100644 index 000000000..71c11b159 --- /dev/null +++ b/libraries/zxing-android-integration/.gitignore @@ -0,0 +1,30 @@ +#Android specific +bin +gen +obj +libs/armeabi +lint.xml +local.properties +release.properties +ant.properties +*.class +*.apk + +#Gradle +.gradle +build +gradle.properties + +#Maven +target +pom.xml.* + +#Eclipse +.project +.classpath +.settings +.metadata + +#IntelliJ IDEA +.idea +*.iml diff --git a/libraries/zxing-android-integration/AndroidManifest.xml b/libraries/zxing-android-integration/AndroidManifest.xml new file mode 100644 index 000000000..c3a6ba464 --- /dev/null +++ b/libraries/zxing-android-integration/AndroidManifest.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.google.zxing.android"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk
+ android:minSdkVersion="8"
+ android:targetSdkVersion="19" />
+
+</manifest>
\ No newline at end of file diff --git a/libraries/zxing-android-integration/build.gradle b/libraries/zxing-android-integration/build.gradle new file mode 100644 index 000000000..21050fc98 --- /dev/null +++ b/libraries/zxing-android-integration/build.gradle @@ -0,0 +1,14 @@ +apply plugin: 'android-library' + +android { + compileSdkVersion 19 + buildToolsVersion '19.0.0' + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['src'] + res.srcDirs = ['res'] + } + } +} diff --git a/libraries/zxing-android-integration/project.properties b/libraries/zxing-android-integration/project.properties new file mode 100644 index 000000000..91d2b0246 --- /dev/null +++ b/libraries/zxing-android-integration/project.properties @@ -0,0 +1,15 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-19 +android.library=true diff --git a/libraries/zxing-android-integration/src/com/google/zxing/integration/android/IntentIntegrator.java b/libraries/zxing-android-integration/src/com/google/zxing/integration/android/IntentIntegrator.java new file mode 100644 index 000000000..4980e9e7e --- /dev/null +++ b/libraries/zxing-android-integration/src/com/google/zxing/integration/android/IntentIntegrator.java @@ -0,0 +1,434 @@ +/* + * Copyright 2009 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.integration.android; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.ActivityNotFoundException; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.net.Uri; +import android.os.Bundle; +import android.util.Log; + +/** + * <p>A utility class which helps ease integration with Barcode Scanner via {@link Intent}s. This is a simple + * way to invoke barcode scanning and receive the result, without any need to integrate, modify, or learn the + * project's source code.</p> + * + * <h2>Initiating a barcode scan</h2> + * + * <p>To integrate, create an instance of {@code IntentIntegrator} and call {@link #initiateScan()} and wait + * for the result in your app.</p> + * + * <p>It does require that the Barcode Scanner (or work-alike) application is installed. The + * {@link #initiateScan()} method will prompt the user to download the application, if needed.</p> + * + * <p>There are a few steps to using this integration. First, your {@link Activity} must implement + * the method {@link Activity#onActivityResult(int, int, Intent)} and include a line of code like this:</p> + * + * <pre>{@code + * public void onActivityResult(int requestCode, int resultCode, Intent intent) { + * IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent); + * if (scanResult != null) { + * // handle scan result + * } + * // else continue with any other code you need in the method + * ... + * } + * }</pre> + * + * <p>This is where you will handle a scan result.</p> + * + * <p>Second, just call this in response to a user action somewhere to begin the scan process:</p> + * + * <pre>{@code + * IntentIntegrator integrator = new IntentIntegrator(yourActivity); + * integrator.initiateScan(); + * }</pre> + * + * <p>Note that {@link #initiateScan()} returns an {@link AlertDialog} which is non-null if the + * user was prompted to download the application. This lets the calling app potentially manage the dialog. + * In particular, ideally, the app dismisses the dialog if it's still active in its {@link Activity#onPause()} + * method.</p> + * + * <p>You can use {@link #setTitle(String)} to customize the title of this download prompt dialog (or, use + * {@link #setTitleByID(int)} to set the title by string resource ID.) Likewise, the prompt message, and + * yes/no button labels can be changed.</p> + * + * <p>Finally, you can use {@link #addExtra(String, Object)} to add more parameters to the Intent used + * to invoke the scanner. This can be used to set additional options not directly exposed by this + * simplified API.</p> + * + * <p>By default, this will only allow applications that are known to respond to this intent correctly + * do so. The apps that are allowed to response can be set with {@link #setTargetApplications(List)}. + * For example, set to {@link #TARGET_BARCODE_SCANNER_ONLY} to only target the Barcode Scanner app itself.</p> + * + * <h2>Sharing text via barcode</h2> + * + * <p>To share text, encoded as a QR Code on-screen, similarly, see {@link #shareText(CharSequence)}.</p> + * + * <p>Some code, particularly download integration, was contributed from the Anobiit application.</p> + * + * <h2>Enabling experimental barcode formats</h2> + * + * <p>Some formats are not enabled by default even when scanning with {@link #ALL_CODE_TYPES}, such as + * PDF417. Use {@link #initiateScan(java.util.Collection)} with + * a collection containing the names of formats to scan for explicitly, like "PDF_417", to use such + * formats.</p> + * + * @author Sean Owen + * @author Fred Lin + * @author Isaac Potoczny-Jones + * @author Brad Drehmer + * @author gcstang + */ +public class IntentIntegrator { + + public static final int REQUEST_CODE = 0x0000c0de; // Only use bottom 16 bits + private static final String TAG = IntentIntegrator.class.getSimpleName(); + + public static final String DEFAULT_TITLE = "Install Barcode Scanner?"; + public static final String DEFAULT_MESSAGE = + "This application requires Barcode Scanner. Would you like to install it?"; + public static final String DEFAULT_YES = "Yes"; + public static final String DEFAULT_NO = "No"; + + private static final String BS_PACKAGE = "com.google.zxing.client.android"; + private static final String BSPLUS_PACKAGE = "com.srowen.bs.android"; + + // supported barcode formats + public static final Collection<String> PRODUCT_CODE_TYPES = list("UPC_A", "UPC_E", "EAN_8", "EAN_13", "RSS_14"); + public static final Collection<String> ONE_D_CODE_TYPES = + list("UPC_A", "UPC_E", "EAN_8", "EAN_13", "CODE_39", "CODE_93", "CODE_128", + "ITF", "RSS_14", "RSS_EXPANDED"); + public static final Collection<String> QR_CODE_TYPES = Collections.singleton("QR_CODE"); + public static final Collection<String> DATA_MATRIX_TYPES = Collections.singleton("DATA_MATRIX"); + + public static final Collection<String> ALL_CODE_TYPES = null; + + public static final List<String> TARGET_BARCODE_SCANNER_ONLY = Collections.singletonList(BS_PACKAGE); + public static final List<String> TARGET_ALL_KNOWN = list( + BSPLUS_PACKAGE, // Barcode Scanner+ + BSPLUS_PACKAGE + ".simple", // Barcode Scanner+ Simple + BS_PACKAGE // Barcode Scanner + // What else supports this intent? + ); + + private final Activity activity; + private String title; + private String message; + private String buttonYes; + private String buttonNo; + private List<String> targetApplications; + private final Map<String,Object> moreExtras; + + public IntentIntegrator(Activity activity) { + this.activity = activity; + title = DEFAULT_TITLE; + message = DEFAULT_MESSAGE; + buttonYes = DEFAULT_YES; + buttonNo = DEFAULT_NO; + targetApplications = TARGET_ALL_KNOWN; + moreExtras = new HashMap<String,Object>(3); + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public void setTitleByID(int titleID) { + title = activity.getString(titleID); + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public void setMessageByID(int messageID) { + message = activity.getString(messageID); + } + + public String getButtonYes() { + return buttonYes; + } + + public void setButtonYes(String buttonYes) { + this.buttonYes = buttonYes; + } + + public void setButtonYesByID(int buttonYesID) { + buttonYes = activity.getString(buttonYesID); + } + + public String getButtonNo() { + return buttonNo; + } + + public void setButtonNo(String buttonNo) { + this.buttonNo = buttonNo; + } + + public void setButtonNoByID(int buttonNoID) { + buttonNo = activity.getString(buttonNoID); + } + + public Collection<String> getTargetApplications() { + return targetApplications; + } + + public final void setTargetApplications(List<String> targetApplications) { + if (targetApplications.isEmpty()) { + throw new IllegalArgumentException("No target applications"); + } + this.targetApplications = targetApplications; + } + + public void setSingleTargetApplication(String targetApplication) { + this.targetApplications = Collections.singletonList(targetApplication); + } + + public Map<String,?> getMoreExtras() { + return moreExtras; + } + + public final void addExtra(String key, Object value) { + moreExtras.put(key, value); + } + + /** + * Initiates a scan for all known barcode types. + */ + public final AlertDialog initiateScan() { + return initiateScan(ALL_CODE_TYPES); + } + + /** + * Initiates a scan only for a certain set of barcode types, given as strings corresponding + * to their names in ZXing's {@code BarcodeFormat} class like "UPC_A". You can supply constants + * like {@link #PRODUCT_CODE_TYPES} for example. + * + * @return the {@link AlertDialog} that was shown to the user prompting them to download the app + * if a prompt was needed, or null otherwise + */ + public final AlertDialog initiateScan(Collection<String> desiredBarcodeFormats) { + Intent intentScan = new Intent(BS_PACKAGE + ".SCAN"); + intentScan.addCategory(Intent.CATEGORY_DEFAULT); + + // check which types of codes to scan for + if (desiredBarcodeFormats != null) { + // set the desired barcode types + StringBuilder joinedByComma = new StringBuilder(); + for (String format : desiredBarcodeFormats) { + if (joinedByComma.length() > 0) { + joinedByComma.append(','); + } + joinedByComma.append(format); + } + intentScan.putExtra("SCAN_FORMATS", joinedByComma.toString()); + } + + String targetAppPackage = findTargetAppPackage(intentScan); + if (targetAppPackage == null) { + return showDownloadDialog(); + } + intentScan.setPackage(targetAppPackage); + intentScan.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + intentScan.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); + attachMoreExtras(intentScan); + startActivityForResult(intentScan, REQUEST_CODE); + return null; + } + + /** + * Start an activity. This method is defined to allow different methods of activity starting for + * newer versions of Android and for compatibility library. + * + * @param intent Intent to start. + * @param code Request code for the activity + * @see android.app.Activity#startActivityForResult(Intent, int) + * @see android.app.Fragment#startActivityForResult(Intent, int) + */ + protected void startActivityForResult(Intent intent, int code) { + activity.startActivityForResult(intent, code); + } + + private String findTargetAppPackage(Intent intent) { + PackageManager pm = activity.getPackageManager(); + List<ResolveInfo> availableApps = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); + if (availableApps != null) { + for (String targetApp : targetApplications) { + if (contains(availableApps, targetApp)) { + return targetApp; + } + } + } + return null; + } + + private static boolean contains(Iterable<ResolveInfo> availableApps, String targetApp) { + for (ResolveInfo availableApp : availableApps) { + String packageName = availableApp.activityInfo.packageName; + if (targetApp.equals(packageName)) { + return true; + } + } + return false; + } + + private AlertDialog showDownloadDialog() { + AlertDialog.Builder downloadDialog = new AlertDialog.Builder(activity); + downloadDialog.setTitle(title); + downloadDialog.setMessage(message); + downloadDialog.setPositiveButton(buttonYes, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + String packageName; + if (targetApplications.contains(BS_PACKAGE)) { + // Prefer to suggest download of BS if it's anywhere in the list + packageName = BS_PACKAGE; + } else { + // Otherwise, first option: + packageName = targetApplications.get(0); + } + Uri uri = Uri.parse("market://details?id=" + packageName); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + try { + activity.startActivity(intent); + } catch (ActivityNotFoundException anfe) { + // Hmm, market is not installed + Log.w(TAG, "Google Play is not installed; cannot install " + packageName); + } + } + }); + downloadDialog.setNegativeButton(buttonNo, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) {} + }); + return downloadDialog.show(); + } + + + /** + * <p>Call this from your {@link Activity}'s + * {@link Activity#onActivityResult(int, int, Intent)} method.</p> + * + * @return null if the event handled here was not related to this class, or + * else an {@link IntentResult} containing the result of the scan. If the user cancelled scanning, + * the fields will be null. + */ + public static IntentResult parseActivityResult(int requestCode, int resultCode, Intent intent) { + if (requestCode == REQUEST_CODE) { + if (resultCode == Activity.RESULT_OK) { + String contents = intent.getStringExtra("SCAN_RESULT"); + String formatName = intent.getStringExtra("SCAN_RESULT_FORMAT"); + byte[] rawBytes = intent.getByteArrayExtra("SCAN_RESULT_BYTES"); + int intentOrientation = intent.getIntExtra("SCAN_RESULT_ORIENTATION", Integer.MIN_VALUE); + Integer orientation = intentOrientation == Integer.MIN_VALUE ? null : intentOrientation; + String errorCorrectionLevel = intent.getStringExtra("SCAN_RESULT_ERROR_CORRECTION_LEVEL"); + return new IntentResult(contents, + formatName, + rawBytes, + orientation, + errorCorrectionLevel); + } + return new IntentResult(); + } + return null; + } + + + /** + * Defaults to type "TEXT_TYPE". + * @see #shareText(CharSequence, CharSequence) + */ + public final AlertDialog shareText(CharSequence text) { + return shareText(text, "TEXT_TYPE"); + } + + /** + * Shares the given text by encoding it as a barcode, such that another user can + * scan the text off the screen of the device. + * + * @param text the text string to encode as a barcode + * @param type type of data to encode. See {@code com.google.zxing.client.android.Contents.Type} constants. + * @return the {@link AlertDialog} that was shown to the user prompting them to download the app + * if a prompt was needed, or null otherwise + */ + public final AlertDialog shareText(CharSequence text, CharSequence type) { + Intent intent = new Intent(); + intent.addCategory(Intent.CATEGORY_DEFAULT); + intent.setAction(BS_PACKAGE + ".ENCODE"); + intent.putExtra("ENCODE_TYPE", type); + intent.putExtra("ENCODE_DATA", text); + String targetAppPackage = findTargetAppPackage(intent); + if (targetAppPackage == null) { + return showDownloadDialog(); + } + intent.setPackage(targetAppPackage); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); + attachMoreExtras(intent); + activity.startActivity(intent); + return null; + } + + private static List<String> list(String... values) { + return Collections.unmodifiableList(Arrays.asList(values)); + } + + private void attachMoreExtras(Intent intent) { + for (Map.Entry<String,Object> entry : moreExtras.entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue(); + // Kind of hacky + if (value instanceof Integer) { + intent.putExtra(key, (Integer) value); + } else if (value instanceof Long) { + intent.putExtra(key, (Long) value); + } else if (value instanceof Boolean) { + intent.putExtra(key, (Boolean) value); + } else if (value instanceof Double) { + intent.putExtra(key, (Double) value); + } else if (value instanceof Float) { + intent.putExtra(key, (Float) value); + } else if (value instanceof Bundle) { + intent.putExtra(key, (Bundle) value); + } else { + intent.putExtra(key, value.toString()); + } + } + } + +} diff --git a/libraries/zxing-android-integration/src/com/google/zxing/integration/android/IntentResult.java b/libraries/zxing-android-integration/src/com/google/zxing/integration/android/IntentResult.java new file mode 100644 index 000000000..2469af92c --- /dev/null +++ b/libraries/zxing-android-integration/src/com/google/zxing/integration/android/IntentResult.java @@ -0,0 +1,95 @@ +/* + * Copyright 2009 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.integration.android; + +/** + * <p>Encapsulates the result of a barcode scan invoked through {@link IntentIntegrator}.</p> + * + * @author Sean Owen + */ +public final class IntentResult { + + private final String contents; + private final String formatName; + private final byte[] rawBytes; + private final Integer orientation; + private final String errorCorrectionLevel; + + IntentResult() { + this(null, null, null, null, null); + } + + IntentResult(String contents, + String formatName, + byte[] rawBytes, + Integer orientation, + String errorCorrectionLevel) { + this.contents = contents; + this.formatName = formatName; + this.rawBytes = rawBytes; + this.orientation = orientation; + this.errorCorrectionLevel = errorCorrectionLevel; + } + + /** + * @return raw content of barcode + */ + public String getContents() { + return contents; + } + + /** + * @return name of format, like "QR_CODE", "UPC_A". See {@code BarcodeFormat} for more format names. + */ + public String getFormatName() { + return formatName; + } + + /** + * @return raw bytes of the barcode content, if applicable, or null otherwise + */ + public byte[] getRawBytes() { + return rawBytes; + } + + /** + * @return rotation of the image, in degrees, which resulted in a successful scan. May be null. + */ + public Integer getOrientation() { + return orientation; + } + + /** + * @return name of the error correction level used in the barcode, if applicable + */ + public String getErrorCorrectionLevel() { + return errorCorrectionLevel; + } + + @Override + public String toString() { + StringBuilder dialogText = new StringBuilder(100); + dialogText.append("Format: ").append(formatName).append('\n'); + dialogText.append("Contents: ").append(contents).append('\n'); + int rawBytesLength = rawBytes == null ? 0 : rawBytes.length; + dialogText.append("Raw bytes: (").append(rawBytesLength).append(" bytes)\n"); + dialogText.append("Orientation: ").append(orientation).append('\n'); + dialogText.append("EC level: ").append(errorCorrectionLevel).append('\n'); + return dialogText.toString(); + } + +} diff --git a/libraries/zxing/.gitignore b/libraries/zxing/.gitignore new file mode 100644 index 000000000..71c11b159 --- /dev/null +++ b/libraries/zxing/.gitignore @@ -0,0 +1,30 @@ +#Android specific +bin +gen +obj +libs/armeabi +lint.xml +local.properties +release.properties +ant.properties +*.class +*.apk + +#Gradle +.gradle +build +gradle.properties + +#Maven +target +pom.xml.* + +#Eclipse +.project +.classpath +.settings +.metadata + +#IntelliJ IDEA +.idea +*.iml diff --git a/settings.gradle b/settings.gradle index 5602503ee..021c54832 100644 --- a/settings.gradle +++ b/settings.gradle @@ -2,5 +2,6 @@ include ':OpenPGP-Keychain' include ':libraries:ActionBarSherlock' include ':libraries:HtmlTextView' include ':libraries:StickyListHeaders:library' -include ':libraries:zxing' include ':libraries:AndroidBootstrap' +include ':libraries:zxing' +include ':libraries:zxing-android-integration' |