From ed01c37fe18f98c25b7515c459ae446efa8232ca Mon Sep 17 00:00:00 2001
From: Bahtiar `kalkin-` Gadimov
Date: Wed, 25 Dec 2013 18:14:38 +0100
Subject: Added shortifyFingerprint() to PGPHelper
---
.../src/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
index e2d89bfab..5a3d332c4 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
@@ -40,6 +40,17 @@ import android.content.Context;
public class PgpKeyHelper {
+ /**
+ * Returns the last 9 chars of a fingerprint
+ *
+ * @param fingerprint
+ * String containing short or long fingerprint
+ * @return
+ */
+ public static String shortifyFingerprint(String fingerprint) {
+ return fingerprint.substring(41);
+ }
+
public static Date getCreationDate(PGPPublicKey key) {
return key.getCreationTime();
}
--
cgit v1.2.3
From 208ea19d5d56fe374bbf26e4ab4cce13dc99656b Mon Sep 17 00:00:00 2001
From: Bahtiar `kalkin-` Gadimov
Date: Wed, 25 Dec 2013 18:15:30 +0100
Subject: Added first draft of KeyDetailsActivity
---
OpenPGP-Keychain/AndroidManifest.xml | 10 ++
OpenPGP-Keychain/res/layout/key_view.xml | 110 +++++++++++++++++++++
OpenPGP-Keychain/res/values/strings.xml | 7 +-
.../keychain/ui/KeyDetailsActivity.java | 90 +++++++++++++++++
4 files changed, 215 insertions(+), 2 deletions(-)
create mode 100644 OpenPGP-Keychain/res/layout/key_view.xml
create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyDetailsActivity.java
diff --git a/OpenPGP-Keychain/AndroidManifest.xml b/OpenPGP-Keychain/AndroidManifest.xml
index ca203c1f2..36b835266 100644
--- a/OpenPGP-Keychain/AndroidManifest.xml
+++ b/OpenPGP-Keychain/AndroidManifest.xml
@@ -123,6 +123,16 @@
android:label="@string/title_edit_key"
android:uiOptions="splitActionBarWhenNarrow"
android:windowSoftInputMode="stateHidden" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/values/strings.xml b/OpenPGP-Keychain/res/values/strings.xml
index 63f267277..9909292d3 100644
--- a/OpenPGP-Keychain/res/values/strings.xml
+++ b/OpenPGP-Keychain/res/values/strings.xml
@@ -44,6 +44,7 @@
Export to Key ServerUnknown Signature KeySign Key
+ Key DetailsHelpShare key with NFC
@@ -53,6 +54,7 @@
GeneralDefaultsAdvanced
+ Master KeySign (Clipboard)
@@ -142,6 +144,8 @@
%s key server(s)Fingerprint:Secret Key:
+ not valid
+ Secret KeyringNone
@@ -340,5 +344,4 @@
Go through all QR Codes using \'Next\', and scan them one by one.QR Code %1$d of %2$d
-
-
\ No newline at end of file
+
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyDetailsActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyDetailsActivity.java
new file mode 100644
index 000000000..652b8a89b
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyDetailsActivity.java
@@ -0,0 +1,90 @@
+package org.sufficientlysecure.keychain.ui;
+
+import java.util.Date;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.util.Log;
+
+import android.os.Bundle;
+import android.text.format.DateFormat;
+import android.widget.TextView;
+
+import com.actionbarsherlock.app.SherlockActivity;
+
+public class KeyDetailsActivity extends SherlockActivity {
+
+ private PGPPublicKey publicKey;
+ private TextView mAlgorithm;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Bundle extras = getIntent().getExtras();
+ setContentView(R.layout.key_view);
+ if (extras == null) {
+ return;
+ }
+
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setHomeButtonEnabled(true);
+
+ long key = extras.getLong("key");
+
+ KeyRings.buildPublicKeyRingsByMasterKeyIdUri(key + "");
+ String[] projection = new String[]{""};
+
+ this.publicKey = ProviderHelper.getPGPPublicKeyByKeyId(
+ getApplicationContext(), key);
+
+ TextView fingerprint = (TextView) this.findViewById(R.id.fingerprint);
+ fingerprint.setText(PgpKeyHelper.shortifyFingerprint(PgpKeyHelper.getFingerPrint(getApplicationContext(), key)));
+ String[] mainUserId = splitUserId("");
+
+ TextView expiry = (TextView) this.findViewById(R.id.expiry);
+ Date expiryDate = PgpKeyHelper.getExpiryDate(publicKey);
+ if (expiryDate == null) {
+ expiry.setText("");
+ } else {
+ expiry.setText(DateFormat.getDateFormat(getApplicationContext())
+ .format(expiryDate));
+ }
+
+ TextView creation = (TextView) this.findViewById(R.id.creation);
+ creation.setText(DateFormat.getDateFormat(getApplicationContext())
+ .format(PgpKeyHelper.getCreationDate(publicKey)));
+ mAlgorithm = (TextView) this.findViewById(R.id.algorithm);
+ mAlgorithm.setText(PgpKeyHelper.getAlgorithmInfo(publicKey));
+
+ }
+
+ private String[] splitUserId(String userId) {
+
+ String[] result = new String[]{"", "", ""};
+ Log.v("UserID", userId);
+
+ Pattern withComment = Pattern.compile("^(.*) [(](.*)[)] <(.*)>$");
+ Matcher matcher = withComment.matcher(userId);
+ if (matcher.matches()) {
+ result[0] = matcher.group(1);
+ result[1] = matcher.group(2);
+ result[2] = matcher.group(3);
+ return result;
+ }
+
+ Pattern withoutComment = Pattern.compile("^(.*) <(.*)>$");
+ matcher = withoutComment.matcher(userId);
+ if (matcher.matches()) {
+ result[0] = matcher.group(1);
+ result[1] = matcher.group(2);
+ return result;
+ }
+ return result;
+ }
+}
--
cgit v1.2.3
From e823ef6efce29fce8a8deb31b22a2d75416702c4 Mon Sep 17 00:00:00 2001
From: Bahtiar `kalkin-` Gadimov
Date: Wed, 25 Dec 2013 19:33:47 +0100
Subject: Renamed keyDetails to key_details
---
OpenPGP-Keychain/AndroidManifest.xml | 2 +-
OpenPGP-Keychain/res/values/strings.xml | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/OpenPGP-Keychain/AndroidManifest.xml b/OpenPGP-Keychain/AndroidManifest.xml
index 36b835266..e363047e6 100644
--- a/OpenPGP-Keychain/AndroidManifest.xml
+++ b/OpenPGP-Keychain/AndroidManifest.xml
@@ -126,7 +126,7 @@
Export to Key Server
Unknown Signature KeySign Key
- Key Details
+ Key DetailsHelpShare key with NFC
--
cgit v1.2.3
From f265cd4d68a4bb398467da1a8d19b6ca0b125602 Mon Sep 17 00:00:00 2001
From: Bahtiar `kalkin-` Gadimov
Date: Wed, 25 Dec 2013 19:33:53 +0100
Subject: Added context menu item KeyDetails to KeyListPublicFragment
---
.../keychain/ui/KeyListPublicFragment.java | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
index 0fdcea809..790ec5ccf 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
@@ -73,9 +73,10 @@ public class KeyListPublicFragment extends KeyListFragment implements
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
- menu.add(0, Id.menu.update, 1, R.string.menu_update_key);
- menu.add(0, Id.menu.signKey, 2, R.string.menu_sign_key);
- menu.add(0, Id.menu.exportToServer, 3, R.string.menu_export_key_to_server);
+ menu.add(0, 23, 1, R.string.title_key_details); // :TODO: Fix magic number
+ menu.add(0, Id.menu.update, 2, R.string.menu_update_key);
+ menu.add(0, Id.menu.signKey, 3, R.string.menu_sign_key);
+ menu.add(0, Id.menu.exportToServer, 4, R.string.menu_export_key_to_server);
menu.add(0, Id.menu.share, 6, R.string.menu_share);
menu.add(0, Id.menu.share_qr_code, 7, R.string.menu_share_qr_code);
menu.add(0, Id.menu.share_nfc, 8, R.string.menu_share_nfc);
@@ -112,7 +113,13 @@ public class KeyListPublicFragment extends KeyListFragment implements
startActivityForResult(queryIntent, Id.request.look_up_key_id);
return true;
-
+ case 23:
+
+ Intent detailsIntent = new Intent(mKeyListActivity, KeyDetailsActivity.class);
+ detailsIntent.putExtra("key", ProviderHelper.getPublicMasterKeyId(mKeyListActivity, keyRingRowId));
+ startActivity(detailsIntent);
+ return true;
+
case Id.menu.exportToServer:
Intent uploadIntent = new Intent(mKeyListActivity, KeyServerUploadActivity.class);
uploadIntent.setAction(KeyServerUploadActivity.ACTION_EXPORT_KEY_TO_SERVER);
--
cgit v1.2.3
From daadc30044659bc08ba3fcbca16083b0e98d353e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Tue, 31 Dec 2013 01:34:06 +0100
Subject: readme wording
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 601d4710c..0bea8d377 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@ Translations are managed at Transifex, please contribute there at https://www.tr
## Code Contributions
-Fork OpenPGP Keychain and do a pull request. I will help with occuring problems and merge your changes back into the main project.
+Fork OpenPGP Keychain and create a pull request. I will help with occuring problems and merge your changes back into the main project.
I am happy about every code contribution and appreciate your effort to help us developing OpenPGP Keychain :)
## Build with Gradle
--
cgit v1.2.3
From cdb3e04b4701079cb77fb74e57de7545431a3b37 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Tue, 31 Dec 2013 01:41:21 +0100
Subject: Add pinned listview lib
---
libraries/pinned-section-listview/.gitignore | 6 +
libraries/pinned-section-listview/README.md | 75 +++
.../example/AndroidManifest.xml | 27 ++
.../example/assets/.gitignore | 1 +
.../example/ic_launcher-web.png | Bin 0 -> 45500 bytes
.../example/libs/android-support-v4.jar | Bin 0 -> 393154 bytes
.../example/proguard-project.txt | 20 +
.../example/project.properties | 15 +
.../example/res/drawable-hdpi/ic_launcher.png | Bin 0 -> 5819 bytes
.../example/res/drawable-mdpi/ic_launcher.png | Bin 0 -> 3479 bytes
.../example/res/drawable-xhdpi/ic_launcher.png | Bin 0 -> 7534 bytes
.../example/res/drawable-xxhdpi/ic_launcher.png | Bin 0 -> 11732 bytes
.../example/res/layout/activity_main.xml | 8 +
.../example/res/menu/main.xml | 27 ++
.../example/res/values-v11/styles.xml | 11 +
.../example/res/values-v14/styles.xml | 12 +
.../example/res/values/colors.xml | 10 +
.../example/res/values/strings.xml | 10 +
.../example/res/values/styles.xml | 20 +
.../com/hb/examples/PinnedSectionListActivity.java | 285 ++++++++++++
.../library/AndroidManifest.xml | 13 +
.../library/assets/.gitignore | 1 +
.../pinned-section-listview/library/build.gradle | 24 +
.../library/proguard-project.txt | 20 +
.../library/project.properties | 15 +
.../pinned-section-listview/library/res/.gitignore | 1 +
.../src/com/hb/views/PinnedSectionListView.java | 513 +++++++++++++++++++++
libraries/pinned-section-listview/screen1.png | Bin 0 -> 23124 bytes
libraries/pinned-section-listview/screen2.png | Bin 0 -> 22743 bytes
libraries/pinned-section-listview/screen3.png | Bin 0 -> 22885 bytes
30 files changed, 1114 insertions(+)
create mode 100644 libraries/pinned-section-listview/.gitignore
create mode 100644 libraries/pinned-section-listview/README.md
create mode 100644 libraries/pinned-section-listview/example/AndroidManifest.xml
create mode 100644 libraries/pinned-section-listview/example/assets/.gitignore
create mode 100644 libraries/pinned-section-listview/example/ic_launcher-web.png
create mode 100644 libraries/pinned-section-listview/example/libs/android-support-v4.jar
create mode 100644 libraries/pinned-section-listview/example/proguard-project.txt
create mode 100644 libraries/pinned-section-listview/example/project.properties
create mode 100644 libraries/pinned-section-listview/example/res/drawable-hdpi/ic_launcher.png
create mode 100644 libraries/pinned-section-listview/example/res/drawable-mdpi/ic_launcher.png
create mode 100644 libraries/pinned-section-listview/example/res/drawable-xhdpi/ic_launcher.png
create mode 100644 libraries/pinned-section-listview/example/res/drawable-xxhdpi/ic_launcher.png
create mode 100644 libraries/pinned-section-listview/example/res/layout/activity_main.xml
create mode 100644 libraries/pinned-section-listview/example/res/menu/main.xml
create mode 100644 libraries/pinned-section-listview/example/res/values-v11/styles.xml
create mode 100644 libraries/pinned-section-listview/example/res/values-v14/styles.xml
create mode 100644 libraries/pinned-section-listview/example/res/values/colors.xml
create mode 100644 libraries/pinned-section-listview/example/res/values/strings.xml
create mode 100644 libraries/pinned-section-listview/example/res/values/styles.xml
create mode 100644 libraries/pinned-section-listview/example/src/com/hb/examples/PinnedSectionListActivity.java
create mode 100644 libraries/pinned-section-listview/library/AndroidManifest.xml
create mode 100644 libraries/pinned-section-listview/library/assets/.gitignore
create mode 100644 libraries/pinned-section-listview/library/build.gradle
create mode 100644 libraries/pinned-section-listview/library/proguard-project.txt
create mode 100644 libraries/pinned-section-listview/library/project.properties
create mode 100644 libraries/pinned-section-listview/library/res/.gitignore
create mode 100644 libraries/pinned-section-listview/library/src/com/hb/views/PinnedSectionListView.java
create mode 100644 libraries/pinned-section-listview/screen1.png
create mode 100644 libraries/pinned-section-listview/screen2.png
create mode 100644 libraries/pinned-section-listview/screen3.png
diff --git a/libraries/pinned-section-listview/.gitignore b/libraries/pinned-section-listview/.gitignore
new file mode 100644
index 000000000..ba354bcae
--- /dev/null
+++ b/libraries/pinned-section-listview/.gitignore
@@ -0,0 +1,6 @@
+bin
+gen
+.project
+.classpath
+.settings
+.DS_Store
diff --git a/libraries/pinned-section-listview/README.md b/libraries/pinned-section-listview/README.md
new file mode 100644
index 000000000..e89cde3bb
--- /dev/null
+++ b/libraries/pinned-section-listview/README.md
@@ -0,0 +1,75 @@
+Introduction
+============
+
+Easy to use ListView with pinned sections for Android. Pinned section is a header view which sticks to the top
+of the list until at least one item of that section is visible.
+
+
+
+
+
+Features
+========
+This list properly implements many features which are missing from other implementations. These are
+ * Fast scroll
+ * Headers and footers
+ * Clickable pinned sections
+
+Besides this it doesn't create any unnecessary views, layouts etc. It's really lean.
+
+Watch [this video][1] to see `PinnedSectionListView` in action.
+
+Usage
+=====
+ 1. Replace standard `ListView` with `com.hb.views.PinnedSectionListView` in your `layout.xml` file.
+
+
+
+ 2. Extend your `ListAdapter` in a way that it implements `PinnedSectionListAdapter` interface, in addition to
+ what it already implements. Basically you need to add a single `isItemViewTypePinned(int viewType)`
+ method. This method must return `true` for all view types which have to be pinned.
+
+ // Our adapter class implements 'PinnedSectionListAdapter' interface
+ class MyPinnedSectionListAdapter extends BaseAdapter implements PinnedSectionListAdapter {
+
+ ...
+
+ // We implement this method to return 'true' for all view types we want to pin
+ @Override
+ public boolean isItemViewTypePinned(int viewType) {
+ return viewType == ;
+ }
+ }
+
+That's all. You are done! A working example can also be found in `example` folder.
+
+Used by
+=======
+Let us know if you use this library in your application and we will mention it here.
+
+[Grocery Sum][2]
+
+License
+=======
+
+ Copyright 2013 Sergej Shafarenka, halfbit.de
+
+ 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.
+
+
+[1]: http://www.youtube.com/watch?v=mI3DpuoIIhQ
+[2]: https://play.google.com/store/apps/details?id=org.codechimp.grocerysum
diff --git a/libraries/pinned-section-listview/example/AndroidManifest.xml b/libraries/pinned-section-listview/example/AndroidManifest.xml
new file mode 100644
index 000000000..56621893d
--- /dev/null
+++ b/libraries/pinned-section-listview/example/AndroidManifest.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/libraries/pinned-section-listview/example/assets/.gitignore b/libraries/pinned-section-listview/example/assets/.gitignore
new file mode 100644
index 000000000..fdffa2a0f
--- /dev/null
+++ b/libraries/pinned-section-listview/example/assets/.gitignore
@@ -0,0 +1 @@
+# placeholder
diff --git a/libraries/pinned-section-listview/example/ic_launcher-web.png b/libraries/pinned-section-listview/example/ic_launcher-web.png
new file mode 100644
index 000000000..8bd40412e
Binary files /dev/null and b/libraries/pinned-section-listview/example/ic_launcher-web.png differ
diff --git a/libraries/pinned-section-listview/example/libs/android-support-v4.jar b/libraries/pinned-section-listview/example/libs/android-support-v4.jar
new file mode 100644
index 000000000..65ebaf8dc
Binary files /dev/null and b/libraries/pinned-section-listview/example/libs/android-support-v4.jar differ
diff --git a/libraries/pinned-section-listview/example/proguard-project.txt b/libraries/pinned-section-listview/example/proguard-project.txt
new file mode 100644
index 000000000..f2fe1559a
--- /dev/null
+++ b/libraries/pinned-section-listview/example/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/libraries/pinned-section-listview/example/project.properties b/libraries/pinned-section-listview/example/project.properties
new file mode 100644
index 000000000..1561d7a9a
--- /dev/null
+++ b/libraries/pinned-section-listview/example/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-17
+android.library.reference.1=../library
diff --git a/libraries/pinned-section-listview/example/res/drawable-hdpi/ic_launcher.png b/libraries/pinned-section-listview/example/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 000000000..3841c3408
Binary files /dev/null and b/libraries/pinned-section-listview/example/res/drawable-hdpi/ic_launcher.png differ
diff --git a/libraries/pinned-section-listview/example/res/drawable-mdpi/ic_launcher.png b/libraries/pinned-section-listview/example/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 000000000..e65953cce
Binary files /dev/null and b/libraries/pinned-section-listview/example/res/drawable-mdpi/ic_launcher.png differ
diff --git a/libraries/pinned-section-listview/example/res/drawable-xhdpi/ic_launcher.png b/libraries/pinned-section-listview/example/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..4ea093d86
Binary files /dev/null and b/libraries/pinned-section-listview/example/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/libraries/pinned-section-listview/example/res/drawable-xxhdpi/ic_launcher.png b/libraries/pinned-section-listview/example/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..87e928a35
Binary files /dev/null and b/libraries/pinned-section-listview/example/res/drawable-xxhdpi/ic_launcher.png differ
diff --git a/libraries/pinned-section-listview/example/res/layout/activity_main.xml b/libraries/pinned-section-listview/example/res/layout/activity_main.xml
new file mode 100644
index 000000000..b641f4c80
--- /dev/null
+++ b/libraries/pinned-section-listview/example/res/layout/activity_main.xml
@@ -0,0 +1,8 @@
+
\ No newline at end of file
diff --git a/libraries/pinned-section-listview/example/res/menu/main.xml b/libraries/pinned-section-listview/example/res/menu/main.xml
new file mode 100644
index 000000000..091b6c770
--- /dev/null
+++ b/libraries/pinned-section-listview/example/res/menu/main.xml
@@ -0,0 +1,27 @@
+
\ No newline at end of file
diff --git a/libraries/pinned-section-listview/example/res/values-v11/styles.xml b/libraries/pinned-section-listview/example/res/values-v11/styles.xml
new file mode 100644
index 000000000..541752f6e
--- /dev/null
+++ b/libraries/pinned-section-listview/example/res/values-v11/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/libraries/pinned-section-listview/example/res/values-v14/styles.xml b/libraries/pinned-section-listview/example/res/values-v14/styles.xml
new file mode 100644
index 000000000..f20e01501
--- /dev/null
+++ b/libraries/pinned-section-listview/example/res/values-v14/styles.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/libraries/pinned-section-listview/example/res/values/colors.xml b/libraries/pinned-section-listview/example/res/values/colors.xml
new file mode 100644
index 000000000..b4da27c93
--- /dev/null
+++ b/libraries/pinned-section-listview/example/res/values/colors.xml
@@ -0,0 +1,10 @@
+
+
+ #ffff4444
+ #ff99cc00
+ #ffffbb33
+ #ff33b5e5
+
+
+
+
\ No newline at end of file
diff --git a/libraries/pinned-section-listview/example/res/values/strings.xml b/libraries/pinned-section-listview/example/res/values/strings.xml
new file mode 100644
index 000000000..e9d43a4aa
--- /dev/null
+++ b/libraries/pinned-section-listview/example/res/values/strings.xml
@@ -0,0 +1,10 @@
+
+
+
+ Pinned Section Demo
+ Fast scroll
+ Add padding
+ Show shadow
+ Show Header & Footer
+
+
\ No newline at end of file
diff --git a/libraries/pinned-section-listview/example/res/values/styles.xml b/libraries/pinned-section-listview/example/res/values/styles.xml
new file mode 100644
index 000000000..4a10ca492
--- /dev/null
+++ b/libraries/pinned-section-listview/example/res/values/styles.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/libraries/pinned-section-listview/example/src/com/hb/examples/PinnedSectionListActivity.java b/libraries/pinned-section-listview/example/src/com/hb/examples/PinnedSectionListActivity.java
new file mode 100644
index 000000000..223718a06
--- /dev/null
+++ b/libraries/pinned-section-listview/example/src/com/hb/examples/PinnedSectionListActivity.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2013 Sergej Shafarenka, halfbit.de
+ *
+ * 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.hb.examples;
+
+import java.util.Locale;
+
+import android.annotation.SuppressLint;
+import android.app.ListActivity;
+import android.content.Context;
+import android.graphics.Color;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.SectionIndexer;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.hb.examples.pinnedsection.R;
+import com.hb.views.PinnedSectionListView;
+import com.hb.views.PinnedSectionListView.PinnedSectionListAdapter;
+
+public class PinnedSectionListActivity extends ListActivity implements OnClickListener {
+
+ static class SimpleAdapter extends ArrayAdapter implements PinnedSectionListAdapter {
+
+ private static final int[] COLORS = new int[] {
+ R.color.green_light, R.color.orange_light,
+ R.color.blue_light, R.color.red_light };
+
+ public SimpleAdapter(Context context, int resource, int textViewResourceId) {
+ super(context, resource, textViewResourceId);
+
+ final int sectionsNumber = 'Z' - 'A' + 1;
+ prepareSections(sectionsNumber);
+
+ int sectionPosition = 0, listPosition = 0;
+ for (char i=0; i= sections.length) {
+ section = sections.length - 1;
+ }
+ return sections[section].listPosition;
+ }
+
+ @Override public int getSectionForPosition(int position) {
+ if (position >= getCount()) {
+ position = getCount() - 1;
+ }
+ return getItem(position).sectionPosition;
+ }
+
+ }
+
+ static class Item {
+
+ public static final int ITEM = 0;
+ public static final int SECTION = 1;
+
+ public final int type;
+ public final String text;
+
+ public int sectionPosition;
+ public int listPosition;
+
+ public Item(int type, String text) {
+ this.type = type;
+ this.text = text;
+ }
+
+ @Override public String toString() {
+ return text;
+ }
+
+ }
+
+ private boolean hasHeaderAndFooter;
+ private boolean isFastScroll;
+ private boolean addPadding;
+ private boolean isShadowVisible = true;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ if (savedInstanceState != null) {
+ isFastScroll = savedInstanceState.getBoolean("isFastScroll");
+ addPadding = savedInstanceState.getBoolean("addPadding");
+ isShadowVisible = savedInstanceState.getBoolean("isShadowVisible");
+ hasHeaderAndFooter = savedInstanceState.getBoolean("hasHeaderAndFooter");
+ }
+ initializeHeaderAndFooter();
+ initializeAdapter();
+ initializePadding();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean("isFastScroll", isFastScroll);
+ outState.putBoolean("addPadding", addPadding);
+ outState.putBoolean("isShadowVisible", isShadowVisible);
+ outState.putBoolean("hasHeaderAndFooter", hasHeaderAndFooter);
+ }
+
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ Item item = (Item) getListView().getAdapter().getItem(position);
+ if (item != null) {
+ Toast.makeText(this, "Item " + position + ": " + item.text, Toast.LENGTH_SHORT).show();
+ } else {
+ Toast.makeText(this, "Item " + position, Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.main, menu);
+ menu.getItem(0).setChecked(isFastScroll);
+ menu.getItem(1).setChecked(addPadding);
+ menu.getItem(2).setChecked(isShadowVisible);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.action_fastscroll:
+ isFastScroll = !isFastScroll;
+ item.setChecked(isFastScroll);
+ initializeAdapter();
+ break;
+ case R.id.action_addpadding:
+ addPadding = !addPadding;
+ item.setChecked(addPadding);
+ initializePadding();
+ break;
+ case R.id.action_showShadow:
+ isShadowVisible = !isShadowVisible;
+ item.setChecked(isShadowVisible);
+ ((PinnedSectionListView)getListView()).setShadowVisible(isShadowVisible);
+ break;
+ case R.id.action_showHeaderAndFooter:
+ hasHeaderAndFooter = !hasHeaderAndFooter;
+ item.setChecked(hasHeaderAndFooter);
+ initializeHeaderAndFooter();
+ break;
+ }
+ return true;
+ }
+
+ private void initializePadding() {
+ float density = getResources().getDisplayMetrics().density;
+ int padding = addPadding ? (int) (16 * density) : 0;
+ getListView().setPadding(padding, padding, padding, padding);
+ }
+
+ private void initializeHeaderAndFooter() {
+ setListAdapter(null);
+ if (hasHeaderAndFooter) {
+ ListView list = getListView();
+
+ LayoutInflater inflater = LayoutInflater.from(this);
+ TextView header1 = (TextView) inflater.inflate(android.R.layout.simple_list_item_1, list, false);
+ header1.setText("First header");
+ list.addHeaderView(header1);
+
+ TextView header2 = (TextView) inflater.inflate(android.R.layout.simple_list_item_1, list, false);
+ header2.setText("Second header");
+ list.addHeaderView(header2);
+
+ TextView footer = (TextView) inflater.inflate(android.R.layout.simple_list_item_1, list, false);
+ footer.setText("Single footer");
+ list.addFooterView(footer);
+ }
+ initializeAdapter();
+ }
+
+ @SuppressLint("NewApi")
+ private void initializeAdapter() {
+ getListView().setFastScrollEnabled(isFastScroll);
+ if (isFastScroll) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ getListView().setFastScrollAlwaysVisible(true);
+ }
+ setListAdapter(new FastScrollAdapter(this, android.R.layout.simple_list_item_1, android.R.id.text1));
+ } else {
+ setListAdapter(new SimpleAdapter(this, android.R.layout.simple_list_item_1, android.R.id.text1));
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+ Toast.makeText(this, "Item: " + v.getTag() , Toast.LENGTH_SHORT).show();
+ }
+
+}
\ No newline at end of file
diff --git a/libraries/pinned-section-listview/library/AndroidManifest.xml b/libraries/pinned-section-listview/library/AndroidManifest.xml
new file mode 100644
index 000000000..2e2ee9173
--- /dev/null
+++ b/libraries/pinned-section-listview/library/AndroidManifest.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/libraries/pinned-section-listview/library/assets/.gitignore b/libraries/pinned-section-listview/library/assets/.gitignore
new file mode 100644
index 000000000..fdffa2a0f
--- /dev/null
+++ b/libraries/pinned-section-listview/library/assets/.gitignore
@@ -0,0 +1 @@
+# placeholder
diff --git a/libraries/pinned-section-listview/library/build.gradle b/libraries/pinned-section-listview/library/build.gradle
new file mode 100644
index 000000000..d77f4746f
--- /dev/null
+++ b/libraries/pinned-section-listview/library/build.gradle
@@ -0,0 +1,24 @@
+buildscript {
+ repositories {
+ mavenCentral()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:0.6.3'
+ }
+}
+
+apply plugin: 'android-library'
+
+
+android {
+ compileSdkVersion 17
+ buildToolsVersion '19'
+
+ sourceSets {
+ main {
+ manifest.srcFile 'AndroidManifest.xml'
+ java.srcDirs = ['src']
+ res.srcDirs = ['res']
+ }
+ }
+}
diff --git a/libraries/pinned-section-listview/library/proguard-project.txt b/libraries/pinned-section-listview/library/proguard-project.txt
new file mode 100644
index 000000000..f2fe1559a
--- /dev/null
+++ b/libraries/pinned-section-listview/library/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/libraries/pinned-section-listview/library/project.properties b/libraries/pinned-section-listview/library/project.properties
new file mode 100644
index 000000000..484dab075
--- /dev/null
+++ b/libraries/pinned-section-listview/library/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-17
+android.library=true
diff --git a/libraries/pinned-section-listview/library/res/.gitignore b/libraries/pinned-section-listview/library/res/.gitignore
new file mode 100644
index 000000000..fdffa2a0f
--- /dev/null
+++ b/libraries/pinned-section-listview/library/res/.gitignore
@@ -0,0 +1 @@
+# placeholder
diff --git a/libraries/pinned-section-listview/library/src/com/hb/views/PinnedSectionListView.java b/libraries/pinned-section-listview/library/src/com/hb/views/PinnedSectionListView.java
new file mode 100644
index 000000000..8100ad693
--- /dev/null
+++ b/libraries/pinned-section-listview/library/src/com/hb/views/PinnedSectionListView.java
@@ -0,0 +1,513 @@
+/*
+ * Copyright (C) 2013 Sergej Shafarenka, halfbit.de
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file kt 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.hb.views;
+
+import android.content.Context;
+import android.database.DataSetObserver;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.GradientDrawable.Orientation;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.SoundEffectConstants;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.AbsListView;
+import android.widget.HeaderViewListAdapter;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.SectionIndexer;
+
+import com.hb.views.pinnedsection.BuildConfig;
+
+/**
+ * ListView, which is capable to pin section views at its top while the rest is still scrolled.
+ */
+public class PinnedSectionListView extends ListView {
+
+ //-- inner classes
+
+ /** List adapter to be implemented for being used with PinnedSectionListView adapter. */
+ public static interface PinnedSectionListAdapter extends ListAdapter {
+ /** This method shall return 'true' if views of given type has to be pinned. */
+ boolean isItemViewTypePinned(int viewType);
+ }
+
+ /** Wrapper class for pinned section view and its position in the list. */
+ static class PinnedSection {
+ public View view;
+ public int position;
+ public long id;
+ }
+
+ //-- class fields
+
+ // fields used for handling touch events
+ private final Rect mTouchRect = new Rect();
+ private final PointF mTouchPoint = new PointF();
+ private int mTouchSlop;
+ private View mTouchTarget;
+ private MotionEvent mDownEvent;
+
+ // fields used for drawing shadow under a pinned section
+ private GradientDrawable mShadowDrawable;
+ private int mSectionsDistanceY;
+ private int mShadowHeight;
+
+ /** Delegating listener, can be null. */
+ OnScrollListener mDelegateOnScrollListener;
+
+ /** Shadow for being recycled, can be null. */
+ PinnedSection mRecycleSection;
+
+ /** shadow instance with a pinned view, can be null. */
+ PinnedSection mPinnedSection;
+
+ /** Pinned view Y-translation. We use it to stick pinned view to the next section. */
+ int mTranslateY;
+
+ /** Scroll listener which does the magic */
+ private final OnScrollListener mOnScrollListener = new OnScrollListener() {
+
+ @Override public void onScrollStateChanged(AbsListView view, int scrollState) {
+ if (mDelegateOnScrollListener != null) { // delegate
+ mDelegateOnScrollListener.onScrollStateChanged(view, scrollState);
+ }
+ }
+
+ @Override
+ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
+
+ if (mDelegateOnScrollListener != null) { // delegate
+ mDelegateOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
+ }
+
+ // get expected adapter or fail fast
+ ListAdapter adapter = getAdapter();
+ if (adapter == null || visibleItemCount == 0) return; // nothing to do
+
+ final boolean isFirstVisibleItemSection =
+ isItemViewTypePinned(adapter, adapter.getItemViewType(firstVisibleItem));
+
+ if (isFirstVisibleItemSection) {
+ View sectionView = getChildAt(0);
+ if (sectionView.getTop() == getPaddingTop()) { // view sticks to the top, no need for pinned shadow
+ destroyPinnedShadow();
+ } else { // section doesn't stick to the top, make sure we have a pinned shadow
+ ensureShadowForPosition(firstVisibleItem, firstVisibleItem, visibleItemCount);
+ }
+
+ } else { // section is not at the first visible position
+ int sectionPosition = findCurrentSectionPosition(firstVisibleItem);
+ if (sectionPosition > -1) { // we have section position
+ ensureShadowForPosition(sectionPosition, firstVisibleItem, visibleItemCount);
+ } else { // there is no section for the first visible item, destroy shadow
+ destroyPinnedShadow();
+ }
+ }
+ };
+
+ };
+
+ /** Default change observer. */
+ private final DataSetObserver mDataSetObserver = new DataSetObserver() {
+ @Override public void onChanged() {
+ recreatePinnedShadow();
+ };
+ @Override public void onInvalidated() {
+ recreatePinnedShadow();
+ }
+ };
+
+ //-- constructors
+
+ public PinnedSectionListView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initView();
+ }
+
+ public PinnedSectionListView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ initView();
+ }
+
+ private void initView() {
+ setOnScrollListener(mOnScrollListener);
+ mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
+ initShadow(true);
+ }
+
+ //-- public API methods
+
+ public void setShadowVisible(boolean visible) {
+ initShadow(visible);
+ if (mPinnedSection != null) {
+ View v = mPinnedSection.view;
+ invalidate(v.getLeft(), v.getTop(), v.getRight(), v.getBottom() + mShadowHeight);
+ }
+ }
+
+ //-- pinned section drawing methods
+
+ public void initShadow(boolean visible) {
+ if (visible) {
+ if (mShadowDrawable == null) {
+ mShadowDrawable = new GradientDrawable(Orientation.TOP_BOTTOM,
+ new int[] { Color.parseColor("#ffa0a0a0"), Color.parseColor("#50a0a0a0"), Color.parseColor("#00a0a0a0")});
+ mShadowHeight = (int) (8 * getResources().getDisplayMetrics().density);
+ }
+ } else {
+ if (mShadowDrawable != null) {
+ mShadowDrawable = null;
+ mShadowHeight = 0;
+ }
+ }
+ }
+
+ /** Create shadow wrapper with a pinned view for a view at given position */
+ void createPinnedShadow(int position) {
+
+ // try to recycle shadow
+ PinnedSection pinnedShadow = mRecycleSection;
+ mRecycleSection = null;
+
+ // create new shadow, if needed
+ if (pinnedShadow == null) pinnedShadow = new PinnedSection();
+ // request new view using recycled view, if such
+ View pinnedView = getAdapter().getView(position, pinnedShadow.view, PinnedSectionListView.this);
+
+ // read layout parameters
+ LayoutParams layoutParams = (LayoutParams) pinnedView.getLayoutParams();
+ if (layoutParams == null) { // create default layout params
+ layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+ }
+
+ int heightMode = MeasureSpec.getMode(layoutParams.height);
+ int heightSize = MeasureSpec.getSize(layoutParams.height);
+
+ if (heightMode == MeasureSpec.UNSPECIFIED) heightMode = MeasureSpec.EXACTLY;
+
+ int maxHeight = getHeight() - getListPaddingTop() - getListPaddingBottom();
+ if (heightSize > maxHeight) heightSize = maxHeight;
+
+ // measure & layout
+ int ws = MeasureSpec.makeMeasureSpec(getWidth() - getListPaddingLeft() - getListPaddingRight(), MeasureSpec.EXACTLY);
+ int hs = MeasureSpec.makeMeasureSpec(heightSize, heightMode);
+ pinnedView.measure(ws, hs);
+ pinnedView.layout(0, 0, pinnedView.getMeasuredWidth(), pinnedView.getMeasuredHeight());
+ mTranslateY = 0;
+
+ // initialize pinned shadow
+ pinnedShadow.view = pinnedView;
+ pinnedShadow.position = position;
+ pinnedShadow.id = getAdapter().getItemId(position);
+
+ // store pinned shadow
+ mPinnedSection = pinnedShadow;
+ }
+
+ /** Destroy shadow wrapper for currently pinned view */
+ void destroyPinnedShadow() {
+ if (mPinnedSection != null) {
+ // keep shadow for being recycled later
+ mRecycleSection = mPinnedSection;
+ mPinnedSection = null;
+ }
+ }
+
+ /** Makes sure we have an actual pinned shadow for given position. */
+ void ensureShadowForPosition(int sectionPosition, int firstVisibleItem, int visibleItemCount) {
+ if (visibleItemCount < 2) { // no need for creating shadow at all, we have a single visible item
+ destroyPinnedShadow();
+ return;
+ }
+
+ if (mPinnedSection != null
+ && mPinnedSection.position != sectionPosition) { // invalidate shadow, if required
+ destroyPinnedShadow();
+ }
+
+ if (mPinnedSection == null) { // create shadow, if empty
+ createPinnedShadow(sectionPosition);
+ }
+
+ // align shadow according to next section position, if needed
+ int nextPosition = sectionPosition + 1;
+ if (nextPosition < getCount()) {
+ int nextSectionPosition = findFirstVisibleSectionPosition(nextPosition,
+ visibleItemCount - (nextPosition - firstVisibleItem));
+ if (nextSectionPosition > -1) {
+ View nextSectionView = getChildAt(nextSectionPosition - firstVisibleItem);
+ final int bottom = mPinnedSection.view.getBottom() + getPaddingTop();
+ mSectionsDistanceY = nextSectionView.getTop() - bottom;
+ if (mSectionsDistanceY < 0) {
+ // next section overlaps pinned shadow, move it up
+ mTranslateY = mSectionsDistanceY;
+ } else {
+ // next section does not overlap with pinned, stick to top
+ mTranslateY = 0;
+ }
+ } else {
+ // no other sections are visible, stick to top
+ mTranslateY = 0;
+ mSectionsDistanceY = Integer.MAX_VALUE;
+ }
+ }
+
+ }
+
+ int findFirstVisibleSectionPosition(int firstVisibleItem, int visibleItemCount) {
+ ListAdapter adapter = getAdapter();
+ for (int childIndex = 0; childIndex < visibleItemCount; childIndex++) {
+ int position = firstVisibleItem + childIndex;
+ int viewType = adapter.getItemViewType(position);
+ if (isItemViewTypePinned(adapter, viewType)) return position;
+ }
+ return -1;
+ }
+
+ int findCurrentSectionPosition(int fromPosition) {
+ ListAdapter adapter = getAdapter();
+
+ if (adapter instanceof SectionIndexer) {
+ // try fast way by asking section indexer
+ SectionIndexer indexer = (SectionIndexer) adapter;
+ int sectionPosition = indexer.getSectionForPosition(fromPosition);
+ int itemPosition = indexer.getPositionForSection(sectionPosition);
+ int typeView = adapter.getItemViewType(itemPosition);
+ if (isItemViewTypePinned(adapter, typeView)) {
+ return itemPosition;
+ } // else, no luck
+ }
+
+ // try slow way by looking through to the next section item above
+ for (int position=fromPosition; position>=0; position--) {
+ int viewType = adapter.getItemViewType(position);
+ if (isItemViewTypePinned(adapter, viewType)) return position;
+ }
+ return -1; // no candidate found
+ }
+
+ void recreatePinnedShadow() {
+ destroyPinnedShadow();
+ ListAdapter adapter = getAdapter();
+ if (adapter != null && adapter.getCount() > 0) {
+ int firstVisiblePosition = getFirstVisiblePosition();
+ int sectionPosition = findCurrentSectionPosition(firstVisiblePosition);
+ if (sectionPosition == -1) return; // no views to pin, exit
+ ensureShadowForPosition(sectionPosition,
+ firstVisiblePosition, getLastVisiblePosition() - firstVisiblePosition);
+ }
+ }
+
+ @Override
+ public void setOnScrollListener(OnScrollListener listener) {
+ if (listener == mOnScrollListener) {
+ super.setOnScrollListener(listener);
+ } else {
+ mDelegateOnScrollListener = listener;
+ }
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ super.onRestoreInstanceState(state);
+ post(new Runnable() {
+ @Override public void run() { // restore pinned view after configuration change
+ recreatePinnedShadow();
+ }
+ });
+ }
+
+ @Override
+ public void setAdapter(ListAdapter adapter) {
+
+ // assert adapter in debug mode
+ if (BuildConfig.DEBUG && adapter != null) {
+ if (!(adapter instanceof PinnedSectionListAdapter))
+ throw new IllegalArgumentException("Does your adapter implement PinnedSectionListAdapter?");
+ if (adapter.getViewTypeCount() < 2)
+ throw new IllegalArgumentException("Does your adapter handle at least two types" +
+ " of views in getViewTypeCount() method: items and sections?");
+ }
+
+ // unregister observer at old adapter and register on new one
+ ListAdapter oldAdapter = getAdapter();
+ if (oldAdapter != null) oldAdapter.unregisterDataSetObserver(mDataSetObserver);
+ if (adapter != null) adapter.registerDataSetObserver(mDataSetObserver);
+
+ // destroy pinned shadow, if new adapter is not same as old one
+ if (oldAdapter != adapter) destroyPinnedShadow();
+
+ super.setAdapter(adapter);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ if (mPinnedSection != null) {
+ int parentWidth = r - l - getPaddingLeft() - getPaddingRight();
+ int shadowWidth = mPinnedSection.view.getWidth();
+ if (parentWidth != shadowWidth) {
+ recreatePinnedShadow();
+ }
+ }
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ super.dispatchDraw(canvas);
+
+ if (mPinnedSection != null) {
+
+ // prepare variables
+ int pLeft = getListPaddingLeft();
+ int pTop = getListPaddingTop();
+ View view = mPinnedSection.view;
+
+ // draw child
+ canvas.save();
+
+ int clipHeight = view.getHeight() +
+ (mShadowDrawable == null ? 0 : Math.min(mShadowHeight, mSectionsDistanceY));
+ canvas.clipRect(pLeft, pTop, pLeft + view.getWidth(), pTop + clipHeight);
+
+ canvas.translate(pLeft, pTop + mTranslateY);
+ drawChild(canvas, mPinnedSection.view, getDrawingTime());
+
+ if (mShadowDrawable != null && mSectionsDistanceY > 0) {
+ mShadowDrawable.setBounds(mPinnedSection.view.getLeft(),
+ mPinnedSection.view.getBottom(),
+ mPinnedSection.view.getRight(),
+ mPinnedSection.view.getBottom() + mShadowHeight);
+ mShadowDrawable.draw(canvas);
+ }
+
+ canvas.restore();
+ }
+ }
+
+ //-- touch handling methods
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+
+ final float x = ev.getX();
+ final float y = ev.getY();
+ final int action = ev.getAction();
+
+ if (action == MotionEvent.ACTION_DOWN
+ && mTouchTarget == null
+ && mPinnedSection != null
+ && isPinnedViewTouched(mPinnedSection.view, x, y)) { // create touch target
+
+ // user touched pinned view
+ mTouchTarget = mPinnedSection.view;
+ mTouchPoint.x = x;
+ mTouchPoint.y = y;
+
+ // copy down event for eventually be used later
+ mDownEvent = MotionEvent.obtain(ev);
+ }
+
+ if (mTouchTarget != null) {
+ if (isPinnedViewTouched(mTouchTarget, x, y)) { // forward event to pinned view
+ mTouchTarget.dispatchTouchEvent(ev);
+ }
+
+ if (action == MotionEvent.ACTION_UP) { // perform onClick on pinned view
+ super.dispatchTouchEvent(ev);
+ performPinnedItemClick();
+ clearTouchTarget();
+
+ } else if (action == MotionEvent.ACTION_CANCEL) { // cancel
+ clearTouchTarget();
+
+ } else if (action == MotionEvent.ACTION_MOVE) {
+ if (Math.abs(y - mTouchPoint.y) > mTouchSlop) {
+
+ // cancel sequence on touch target
+ MotionEvent event = MotionEvent.obtain(ev);
+ event.setAction(MotionEvent.ACTION_CANCEL);
+ mTouchTarget.dispatchTouchEvent(event);
+ event.recycle();
+
+ // provide correct sequence to super class for further handling
+ super.dispatchTouchEvent(mDownEvent);
+ super.dispatchTouchEvent(ev);
+ clearTouchTarget();
+
+ }
+ }
+
+ return true;
+ }
+
+ // call super if this was not our pinned view
+ return super.dispatchTouchEvent(ev);
+ }
+
+ private boolean isPinnedViewTouched(View view, float x, float y) {
+ view.getHitRect(mTouchRect);
+
+ // by taping top or bottom padding, the list performs on click on a border item.
+ // we don't add top padding here to keep behavior consistent.
+ mTouchRect.top += mTranslateY;
+
+ mTouchRect.bottom += mTranslateY + getPaddingTop();
+ mTouchRect.left += getPaddingLeft();
+ mTouchRect.right -= getPaddingRight();
+ return mTouchRect.contains((int)x, (int)y);
+ }
+
+ private void clearTouchTarget() {
+ mTouchTarget = null;
+ if (mDownEvent != null) {
+ mDownEvent.recycle();
+ mDownEvent = null;
+ }
+ }
+
+ private boolean performPinnedItemClick() {
+ if (mPinnedSection == null) return false;
+
+ OnItemClickListener listener = getOnItemClickListener();
+ if (listener != null) {
+ View view = mPinnedSection.view;
+ playSoundEffect(SoundEffectConstants.CLICK);
+ if (view != null) {
+ view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
+ }
+ listener.onItemClick(this, view, mPinnedSection.position, mPinnedSection.id);
+ return true;
+ }
+ return false;
+ }
+
+ public static boolean isItemViewTypePinned(ListAdapter adapter, int viewType) {
+ if (adapter instanceof HeaderViewListAdapter) {
+ adapter = ((HeaderViewListAdapter)adapter).getWrappedAdapter();
+ }
+ return ((PinnedSectionListAdapter) adapter).isItemViewTypePinned(viewType);
+ }
+
+}
diff --git a/libraries/pinned-section-listview/screen1.png b/libraries/pinned-section-listview/screen1.png
new file mode 100644
index 000000000..9cca20109
Binary files /dev/null and b/libraries/pinned-section-listview/screen1.png differ
diff --git a/libraries/pinned-section-listview/screen2.png b/libraries/pinned-section-listview/screen2.png
new file mode 100644
index 000000000..ad59f6f2f
Binary files /dev/null and b/libraries/pinned-section-listview/screen2.png differ
diff --git a/libraries/pinned-section-listview/screen3.png b/libraries/pinned-section-listview/screen3.png
new file mode 100644
index 000000000..0fd528fa9
Binary files /dev/null and b/libraries/pinned-section-listview/screen3.png differ
--
cgit v1.2.3
From 52c55aaabeac7f35b4800e4cbf0ca098d3d6c54e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Tue, 31 Dec 2013 01:41:37 +0100
Subject: code styling
---
OpenPGP-Keychain/res/layout/key_view.xml | 8 +-
.../keychain/pgp/PgpKeyHelper.java | 20 +--
.../keychain/ui/KeyDetailsActivity.java | 153 ++++++++++++---------
3 files changed, 100 insertions(+), 81 deletions(-)
diff --git a/OpenPGP-Keychain/res/layout/key_view.xml b/OpenPGP-Keychain/res/layout/key_view.xml
index 5649fe338..88dc2a8eb 100644
--- a/OpenPGP-Keychain/res/layout/key_view.xml
+++ b/OpenPGP-Keychain/res/layout/key_view.xml
@@ -95,10 +95,12 @@
android:layout_height="wrap_content"
android:padding="4dp"
android:text="@string/section_user_ids" />
-
-
+ android:layout_height="wrap_content" >
+
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
package org.sufficientlysecure.keychain.ui;
import java.util.Date;
@@ -19,72 +37,71 @@ import com.actionbarsherlock.app.SherlockActivity;
public class KeyDetailsActivity extends SherlockActivity {
- private PGPPublicKey publicKey;
- private TextView mAlgorithm;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- Bundle extras = getIntent().getExtras();
- setContentView(R.layout.key_view);
- if (extras == null) {
- return;
- }
-
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- getSupportActionBar().setHomeButtonEnabled(true);
-
- long key = extras.getLong("key");
-
- KeyRings.buildPublicKeyRingsByMasterKeyIdUri(key + "");
- String[] projection = new String[]{""};
-
- this.publicKey = ProviderHelper.getPGPPublicKeyByKeyId(
- getApplicationContext(), key);
-
- TextView fingerprint = (TextView) this.findViewById(R.id.fingerprint);
- fingerprint.setText(PgpKeyHelper.shortifyFingerprint(PgpKeyHelper.getFingerPrint(getApplicationContext(), key)));
- String[] mainUserId = splitUserId("");
-
- TextView expiry = (TextView) this.findViewById(R.id.expiry);
- Date expiryDate = PgpKeyHelper.getExpiryDate(publicKey);
- if (expiryDate == null) {
- expiry.setText("");
- } else {
- expiry.setText(DateFormat.getDateFormat(getApplicationContext())
- .format(expiryDate));
- }
-
- TextView creation = (TextView) this.findViewById(R.id.creation);
- creation.setText(DateFormat.getDateFormat(getApplicationContext())
- .format(PgpKeyHelper.getCreationDate(publicKey)));
- mAlgorithm = (TextView) this.findViewById(R.id.algorithm);
- mAlgorithm.setText(PgpKeyHelper.getAlgorithmInfo(publicKey));
-
- }
-
- private String[] splitUserId(String userId) {
-
- String[] result = new String[]{"", "", ""};
- Log.v("UserID", userId);
-
- Pattern withComment = Pattern.compile("^(.*) [(](.*)[)] <(.*)>$");
- Matcher matcher = withComment.matcher(userId);
- if (matcher.matches()) {
- result[0] = matcher.group(1);
- result[1] = matcher.group(2);
- result[2] = matcher.group(3);
- return result;
- }
-
- Pattern withoutComment = Pattern.compile("^(.*) <(.*)>$");
- matcher = withoutComment.matcher(userId);
- if (matcher.matches()) {
- result[0] = matcher.group(1);
- result[1] = matcher.group(2);
- return result;
- }
- return result;
- }
+ private PGPPublicKey publicKey;
+ private TextView mAlgorithm;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Bundle extras = getIntent().getExtras();
+ setContentView(R.layout.key_view);
+ if (extras == null) {
+ return;
+ }
+
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setHomeButtonEnabled(true);
+
+ long key = extras.getLong("key");
+
+ KeyRings.buildPublicKeyRingsByMasterKeyIdUri(key + "");
+ String[] projection = new String[] { "" };
+
+ this.publicKey = ProviderHelper.getPGPPublicKeyByKeyId(getApplicationContext(), key);
+
+ TextView fingerprint = (TextView) this.findViewById(R.id.fingerprint);
+ fingerprint.setText(PgpKeyHelper.shortifyFingerprint(PgpKeyHelper.getFingerPrint(
+ getApplicationContext(), key)));
+ String[] mainUserId = splitUserId("");
+
+ TextView expiry = (TextView) this.findViewById(R.id.expiry);
+ Date expiryDate = PgpKeyHelper.getExpiryDate(publicKey);
+ if (expiryDate == null) {
+ expiry.setText("");
+ } else {
+ expiry.setText(DateFormat.getDateFormat(getApplicationContext()).format(expiryDate));
+ }
+
+ TextView creation = (TextView) this.findViewById(R.id.creation);
+ creation.setText(DateFormat.getDateFormat(getApplicationContext()).format(
+ PgpKeyHelper.getCreationDate(publicKey)));
+ mAlgorithm = (TextView) this.findViewById(R.id.algorithm);
+ mAlgorithm.setText(PgpKeyHelper.getAlgorithmInfo(publicKey));
+
+ }
+
+ private String[] splitUserId(String userId) {
+
+ String[] result = new String[] { "", "", "" };
+ Log.v("UserID", userId);
+
+ Pattern withComment = Pattern.compile("^(.*) [(](.*)[)] <(.*)>$");
+ Matcher matcher = withComment.matcher(userId);
+ if (matcher.matches()) {
+ result[0] = matcher.group(1);
+ result[1] = matcher.group(2);
+ result[2] = matcher.group(3);
+ return result;
+ }
+
+ Pattern withoutComment = Pattern.compile("^(.*) <(.*)>$");
+ matcher = withoutComment.matcher(userId);
+ if (matcher.matches()) {
+ result[0] = matcher.group(1);
+ result[1] = matcher.group(2);
+ return result;
+ }
+ return result;
+ }
}
--
cgit v1.2.3
From d45462bb337270c3310b29cc4af392ed65df796a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Tue, 31 Dec 2013 01:45:49 +0100
Subject: key listview
---
OpenPGP-Keychain/res/layout/key_view.xml | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/OpenPGP-Keychain/res/layout/key_view.xml b/OpenPGP-Keychain/res/layout/key_view.xml
index 88dc2a8eb..326959e97 100644
--- a/OpenPGP-Keychain/res/layout/key_view.xml
+++ b/OpenPGP-Keychain/res/layout/key_view.xml
@@ -99,8 +99,7 @@
-
+ android:layout_height="wrap_content" />
+
+
\ No newline at end of file
--
cgit v1.2.3
From 6c1a58ef15e6b5d826326fa9636e44cc6f501d12 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Wed, 1 Jan 2014 16:54:55 +0100
Subject: Use data uri instead of extra for key details
---
.../keychain/provider/ProviderHelper.java | 2 +-
.../keychain/ui/KeyDetailsActivity.java | 63 +++++++++++++---------
.../keychain/ui/KeyListPublicFragment.java | 16 +++---
3 files changed, 49 insertions(+), 32 deletions(-)
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java
index f12048277..b07ac53f8 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java
@@ -60,7 +60,7 @@ public class ProviderHelper {
* @param queryUri
* @return
*/
- private static PGPKeyRing getPGPKeyRing(Context context, Uri queryUri) {
+ public static PGPKeyRing getPGPKeyRing(Context context, Uri queryUri) {
Cursor cursor = context.getContentResolver().query(queryUri,
new String[] { KeyRings._ID, KeyRings.KEY_RING_DATA }, null, null, null);
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyDetailsActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyDetailsActivity.java
index 82d66e2c0..5c8444d80 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyDetailsActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyDetailsActivity.java
@@ -23,12 +23,15 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.Log;
+import android.content.Intent;
+import android.net.Uri;
import android.os.Bundle;
import android.text.format.DateFormat;
import android.widget.TextView;
@@ -36,49 +39,59 @@ import android.widget.TextView;
import com.actionbarsherlock.app.SherlockActivity;
public class KeyDetailsActivity extends SherlockActivity {
+ private Uri mDataUri;
+
+ private PGPPublicKey mPublicKey;
- private PGPPublicKey publicKey;
private TextView mAlgorithm;
+ private TextView mFingerint;
+ private TextView mExpiry;
+ private TextView mCreation;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- Bundle extras = getIntent().getExtras();
- setContentView(R.layout.key_view);
- if (extras == null) {
- return;
- }
-
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
- long key = extras.getLong("key");
+ setContentView(R.layout.key_view);
+
+ mFingerint = (TextView) this.findViewById(R.id.fingerprint);
+ mExpiry = (TextView) this.findViewById(R.id.expiry);
+ mCreation = (TextView) this.findViewById(R.id.creation);
+ mAlgorithm = (TextView) this.findViewById(R.id.algorithm);
- KeyRings.buildPublicKeyRingsByMasterKeyIdUri(key + "");
- String[] projection = new String[] { "" };
+ Intent intent = getIntent();
+ mDataUri = intent.getData();
+ if (mDataUri == null) {
+ Log.e(Constants.TAG, "Intent data missing. Should be Uri of app!");
+ finish();
+ return;
+ } else {
+ Log.d(Constants.TAG, "uri: " + mDataUri);
+ loadData(mDataUri);
+ }
+ }
- this.publicKey = ProviderHelper.getPGPPublicKeyByKeyId(getApplicationContext(), key);
+ private void loadData(Uri dataUri) {
+ PGPPublicKeyRing ring = (PGPPublicKeyRing) ProviderHelper.getPGPKeyRing(this, dataUri);
+ mPublicKey = ring.getPublicKey();
- TextView fingerprint = (TextView) this.findViewById(R.id.fingerprint);
- fingerprint.setText(PgpKeyHelper.shortifyFingerprint(PgpKeyHelper.getFingerPrint(
- getApplicationContext(), key)));
+ mFingerint.setText(PgpKeyHelper.shortifyFingerprint(PgpKeyHelper
+ .convertFingerprintToHex(mPublicKey.getFingerprint())));
String[] mainUserId = splitUserId("");
- TextView expiry = (TextView) this.findViewById(R.id.expiry);
- Date expiryDate = PgpKeyHelper.getExpiryDate(publicKey);
+ Date expiryDate = PgpKeyHelper.getExpiryDate(mPublicKey);
if (expiryDate == null) {
- expiry.setText("");
+ mExpiry.setText("");
} else {
- expiry.setText(DateFormat.getDateFormat(getApplicationContext()).format(expiryDate));
+ mExpiry.setText(DateFormat.getDateFormat(getApplicationContext()).format(expiryDate));
}
- TextView creation = (TextView) this.findViewById(R.id.creation);
- creation.setText(DateFormat.getDateFormat(getApplicationContext()).format(
- PgpKeyHelper.getCreationDate(publicKey)));
- mAlgorithm = (TextView) this.findViewById(R.id.algorithm);
- mAlgorithm.setText(PgpKeyHelper.getAlgorithmInfo(publicKey));
-
+ mCreation.setText(DateFormat.getDateFormat(getApplicationContext()).format(
+ PgpKeyHelper.getCreationDate(mPublicKey)));
+ mAlgorithm.setText(PgpKeyHelper.getAlgorithmInfo(mPublicKey));
}
private String[] splitUserId(String userId) {
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
index 790ec5ccf..0cf15a451 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
@@ -20,12 +20,15 @@ package org.sufficientlysecure.keychain.ui;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
+import org.sufficientlysecure.keychain.service.remote.AppSettingsActivity;
import org.sufficientlysecure.keychain.ui.adapter.KeyListAdapter;
import org.sufficientlysecure.keychain.R;
+import android.content.ContentUris;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
@@ -114,16 +117,17 @@ public class KeyListPublicFragment extends KeyListFragment implements
return true;
case 23:
-
- Intent detailsIntent = new Intent(mKeyListActivity, KeyDetailsActivity.class);
- detailsIntent.putExtra("key", ProviderHelper.getPublicMasterKeyId(mKeyListActivity, keyRingRowId));
- startActivity(detailsIntent);
+
+ Intent detailsIntent = new Intent(mKeyListActivity, KeyDetailsActivity.class);
+ detailsIntent.setData(KeychainContract.KeyRings.buildPublicKeyRingsUri(Long
+ .toString(keyRingRowId)));
+ startActivity(detailsIntent);
return true;
-
+
case Id.menu.exportToServer:
Intent uploadIntent = new Intent(mKeyListActivity, KeyServerUploadActivity.class);
uploadIntent.setAction(KeyServerUploadActivity.ACTION_EXPORT_KEY_TO_SERVER);
- uploadIntent.putExtra(KeyServerUploadActivity.EXTRA_KEYRING_ROW_ID, (int)keyRingRowId);
+ uploadIntent.putExtra(KeyServerUploadActivity.EXTRA_KEYRING_ROW_ID, (int) keyRingRowId);
startActivityForResult(uploadIntent, Id.request.export_to_server);
return true;
--
cgit v1.2.3
From c8d0ff77b10bc5e0103df473076fc7cacadf9014 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Wed, 1 Jan 2014 20:29:56 +0100
Subject: show layout not before passphrase is entered
---
.../keychain/ui/EditKeyActivity.java | 56 +++++++++++-----------
1 file changed, 28 insertions(+), 28 deletions(-)
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
index 7abee78f3..2995a839a 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
@@ -119,12 +119,6 @@ public class EditKeyActivity extends SherlockFragmentActivity {
}
});
- setContentView(R.layout.edit_key);
-
- // find views
- mChangePassPhrase = (Button) findViewById(R.id.edit_key_btn_change_pass_phrase);
- mNoPassphrase = (CheckBox) findViewById(R.id.edit_key_no_passphrase);
-
mUserIds = new Vector();
mKeys = new Vector();
mKeysUsages = new Vector();
@@ -138,28 +132,6 @@ public class EditKeyActivity extends SherlockFragmentActivity {
handleActionEditKey(intent);
}
- mChangePassPhrase.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- showSetPassphraseDialog();
- }
- });
-
- // disable passphrase when no passphrase checkobox is checked!
- mNoPassphrase.setOnCheckedChangeListener(new OnCheckedChangeListener() {
-
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- if (isChecked) {
- // remove passphrase
- mNewPassPhrase = null;
-
- mChangePassPhrase.setVisibility(View.GONE);
- } else {
- mChangePassPhrase.setVisibility(View.VISIBLE);
- }
- }
- });
-
if (mBuildLayout) {
buildLayout();
}
@@ -402,6 +374,12 @@ public class EditKeyActivity extends SherlockFragmentActivity {
* id and key.
*/
private void buildLayout() {
+ setContentView(R.layout.edit_key);
+
+ // find views
+ mChangePassPhrase = (Button) findViewById(R.id.edit_key_btn_change_pass_phrase);
+ mNoPassphrase = (CheckBox) findViewById(R.id.edit_key_no_passphrase);
+
// Build layout based on given userIds and keys
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
@@ -418,6 +396,28 @@ public class EditKeyActivity extends SherlockFragmentActivity {
container.addView(mKeysView);
updatePassPhraseButtonText();
+
+ mChangePassPhrase.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ showSetPassphraseDialog();
+ }
+ });
+
+ // disable passphrase when no passphrase checkobox is checked!
+ mNoPassphrase.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (isChecked) {
+ // remove passphrase
+ mNewPassPhrase = null;
+
+ mChangePassPhrase.setVisibility(View.GONE);
+ } else {
+ mChangePassPhrase.setVisibility(View.VISIBLE);
+ }
+ }
+ });
}
private long getMasterKeyId() {
--
cgit v1.2.3
From 1d91804dc7943e7149d02141a46c3eb0763e2b94 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Wed, 1 Jan 2014 22:26:19 +0100
Subject: Simple new list implementations, remove library, use simple adapter
with headings
---
OpenPGP-Keychain/AndroidManifest.xml | 5 +-
OpenPGP-Keychain/project.properties | 1 -
OpenPGP-Keychain/res/layout/key_view.xml | 116 -----
OpenPGP-Keychain/res/layout/key_view_activity.xml | 116 +++++
OpenPGP-Keychain/res/menu/key_view.xml | 38 ++
.../service/remote/RegisteredAppsListFragment.java | 8 +-
.../keychain/ui/EditKeyActivity.java | 4 +-
.../keychain/ui/KeyDetailsActivity.java | 120 -----
.../keychain/ui/KeyListActivity.java | 22 +-
.../keychain/ui/KeyListFragment.java | 88 ----
.../keychain/ui/KeyListPublicFragment.java | 174 ++-----
.../keychain/ui/KeyListSecretActivity.java | 22 +-
.../keychain/ui/KeyListSecretFragment.java | 112 ++---
.../keychain/ui/KeyViewActivity.java | 228 +++++++++
.../keychain/ui/adapter/KeyListPublicAdapter.java | 77 ++++
.../keychain/ui/adapter/KeyListSecretAdapter.java | 82 ++++
.../keychain/util/SectionCursorAdapter.java | 266 +++++++++++
README.md | 11 +-
libraries/pinned-section-listview/.gitignore | 6 -
libraries/pinned-section-listview/README.md | 75 ---
.../example/AndroidManifest.xml | 27 --
.../example/assets/.gitignore | 1 -
.../example/ic_launcher-web.png | Bin 45500 -> 0 bytes
.../example/libs/android-support-v4.jar | Bin 393154 -> 0 bytes
.../example/proguard-project.txt | 20 -
.../example/project.properties | 15 -
.../example/res/drawable-hdpi/ic_launcher.png | Bin 5819 -> 0 bytes
.../example/res/drawable-mdpi/ic_launcher.png | Bin 3479 -> 0 bytes
.../example/res/drawable-xhdpi/ic_launcher.png | Bin 7534 -> 0 bytes
.../example/res/drawable-xxhdpi/ic_launcher.png | Bin 11732 -> 0 bytes
.../example/res/layout/activity_main.xml | 8 -
.../example/res/menu/main.xml | 27 --
.../example/res/values-v11/styles.xml | 11 -
.../example/res/values-v14/styles.xml | 12 -
.../example/res/values/colors.xml | 10 -
.../example/res/values/strings.xml | 10 -
.../example/res/values/styles.xml | 20 -
.../com/hb/examples/PinnedSectionListActivity.java | 285 ------------
.../library/AndroidManifest.xml | 13 -
.../library/assets/.gitignore | 1 -
.../pinned-section-listview/library/build.gradle | 24 -
.../library/proguard-project.txt | 20 -
.../library/project.properties | 15 -
.../pinned-section-listview/library/res/.gitignore | 1 -
.../src/com/hb/views/PinnedSectionListView.java | 513 ---------------------
libraries/pinned-section-listview/screen1.png | Bin 23124 -> 0 bytes
libraries/pinned-section-listview/screen2.png | Bin 22743 -> 0 bytes
libraries/pinned-section-listview/screen3.png | Bin 22885 -> 0 bytes
settings.gradle | 3 +-
49 files changed, 919 insertions(+), 1688 deletions(-)
delete mode 100644 OpenPGP-Keychain/res/layout/key_view.xml
create mode 100644 OpenPGP-Keychain/res/layout/key_view_activity.xml
create mode 100644 OpenPGP-Keychain/res/menu/key_view.xml
delete mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyDetailsActivity.java
delete mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListFragment.java
create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java
create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java
create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java
create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/SectionCursorAdapter.java
delete mode 100644 libraries/pinned-section-listview/.gitignore
delete mode 100644 libraries/pinned-section-listview/README.md
delete mode 100644 libraries/pinned-section-listview/example/AndroidManifest.xml
delete mode 100644 libraries/pinned-section-listview/example/assets/.gitignore
delete mode 100644 libraries/pinned-section-listview/example/ic_launcher-web.png
delete mode 100644 libraries/pinned-section-listview/example/libs/android-support-v4.jar
delete mode 100644 libraries/pinned-section-listview/example/proguard-project.txt
delete mode 100644 libraries/pinned-section-listview/example/project.properties
delete mode 100644 libraries/pinned-section-listview/example/res/drawable-hdpi/ic_launcher.png
delete mode 100644 libraries/pinned-section-listview/example/res/drawable-mdpi/ic_launcher.png
delete mode 100644 libraries/pinned-section-listview/example/res/drawable-xhdpi/ic_launcher.png
delete mode 100644 libraries/pinned-section-listview/example/res/drawable-xxhdpi/ic_launcher.png
delete mode 100644 libraries/pinned-section-listview/example/res/layout/activity_main.xml
delete mode 100644 libraries/pinned-section-listview/example/res/menu/main.xml
delete mode 100644 libraries/pinned-section-listview/example/res/values-v11/styles.xml
delete mode 100644 libraries/pinned-section-listview/example/res/values-v14/styles.xml
delete mode 100644 libraries/pinned-section-listview/example/res/values/colors.xml
delete mode 100644 libraries/pinned-section-listview/example/res/values/strings.xml
delete mode 100644 libraries/pinned-section-listview/example/res/values/styles.xml
delete mode 100644 libraries/pinned-section-listview/example/src/com/hb/examples/PinnedSectionListActivity.java
delete mode 100644 libraries/pinned-section-listview/library/AndroidManifest.xml
delete mode 100644 libraries/pinned-section-listview/library/assets/.gitignore
delete mode 100644 libraries/pinned-section-listview/library/build.gradle
delete mode 100644 libraries/pinned-section-listview/library/proguard-project.txt
delete mode 100644 libraries/pinned-section-listview/library/project.properties
delete mode 100644 libraries/pinned-section-listview/library/res/.gitignore
delete mode 100644 libraries/pinned-section-listview/library/src/com/hb/views/PinnedSectionListView.java
delete mode 100644 libraries/pinned-section-listview/screen1.png
delete mode 100644 libraries/pinned-section-listview/screen2.png
delete mode 100644 libraries/pinned-section-listview/screen3.png
diff --git a/OpenPGP-Keychain/AndroidManifest.xml b/OpenPGP-Keychain/AndroidManifest.xml
index 4b843bc01..be9fe222c 100644
--- a/OpenPGP-Keychain/AndroidManifest.xml
+++ b/OpenPGP-Keychain/AndroidManifest.xml
@@ -123,11 +123,10 @@
android:uiOptions="splitActionBarWhenNarrow"
android:windowSoftInputMode="stateHidden" />
+ android:parentActivityName=".ui.KeyListPublicActivity" >
diff --git a/OpenPGP-Keychain/project.properties b/OpenPGP-Keychain/project.properties
index 7347abfcd..7acfa6b58 100644
--- a/OpenPGP-Keychain/project.properties
+++ b/OpenPGP-Keychain/project.properties
@@ -11,4 +11,3 @@
target=android-19
android.library.reference.1=../libraries/ActionBarSherlock
android.library.reference.2=../libraries/HtmlTextView
-android.library.reference.3=../libraries/pinned-section-listview/library
diff --git a/OpenPGP-Keychain/res/layout/key_view.xml b/OpenPGP-Keychain/res/layout/key_view.xml
deleted file mode 100644
index 326959e97..000000000
--- a/OpenPGP-Keychain/res/layout/key_view.xml
+++ /dev/null
@@ -1,116 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/key_view_activity.xml b/OpenPGP-Keychain/res/layout/key_view_activity.xml
new file mode 100644
index 000000000..326959e97
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/key_view_activity.xml
@@ -0,0 +1,116 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/menu/key_view.xml b/OpenPGP-Keychain/res/menu/key_view.xml
new file mode 100644
index 000000000..ac5c96c5d
--- /dev/null
+++ b/OpenPGP-Keychain/res/menu/key_view.xml
@@ -0,0 +1,38 @@
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListFragment.java
index 1b504a374..4c9d553ad 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListFragment.java
@@ -41,9 +41,6 @@ public class RegisteredAppsListFragment extends SherlockListFragment implements
// This is the Adapter being used to display the list's data.
RegisteredAppsAdapter mAdapter;
- // If non-null, this is the current filter the user has provided.
- String mCurFilter;
-
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
@@ -75,8 +72,7 @@ public class RegisteredAppsListFragment extends SherlockListFragment implements
}
// These are the Contacts rows that we will retrieve.
- static final String[] CONSUMERS_SUMMARY_PROJECTION = new String[] { ApiApps._ID,
- ApiApps.PACKAGE_NAME };
+ static final String[] PROJECTION = new String[] { ApiApps._ID, ApiApps.PACKAGE_NAME };
public Loader onCreateLoader(int id, Bundle args) {
// This is called when a new Loader needs to be created. This
@@ -87,7 +83,7 @@ public class RegisteredAppsListFragment extends SherlockListFragment implements
// Now create and return a CursorLoader that will take care of
// creating a Cursor for the data being displayed.
- return new CursorLoader(getActivity(), baseUri, CONSUMERS_SUMMARY_PROJECTION, null, null,
+ return new CursorLoader(getActivity(), baseUri, PROJECTION, null, null,
ApiApps.PACKAGE_NAME + " COLLATE LOCALIZED ASC");
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
index 2995a839a..7aeb51c8f 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Dominik Schürmann
+ * Copyright (C) 2012-2013 Dominik Schürmann
* Copyright (C) 2010 Thialfihar
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -268,7 +268,6 @@ public class EditKeyActivity extends SherlockFragmentActivity {
*
* @param intent
*/
- @SuppressWarnings("unchecked")
private void handleActionEditKey(Intent intent) {
Bundle extras = intent.getExtras();
@@ -291,7 +290,6 @@ public class EditKeyActivity extends SherlockFragmentActivity {
finallyEdit(masterKeyId, masterCanSign);
}
-
}
}
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyDetailsActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyDetailsActivity.java
deleted file mode 100644
index 5c8444d80..000000000
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyDetailsActivity.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2013 Bahtiar 'kalkin' Gadimov
- * Copyright (C) 2013 Dominik Schürmann
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package org.sufficientlysecure.keychain.ui;
-
-import java.util.Date;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.spongycastle.openpgp.PGPPublicKey;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.util.Log;
-
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.text.format.DateFormat;
-import android.widget.TextView;
-
-import com.actionbarsherlock.app.SherlockActivity;
-
-public class KeyDetailsActivity extends SherlockActivity {
- private Uri mDataUri;
-
- private PGPPublicKey mPublicKey;
-
- private TextView mAlgorithm;
- private TextView mFingerint;
- private TextView mExpiry;
- private TextView mCreation;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- getSupportActionBar().setHomeButtonEnabled(true);
-
- setContentView(R.layout.key_view);
-
- mFingerint = (TextView) this.findViewById(R.id.fingerprint);
- mExpiry = (TextView) this.findViewById(R.id.expiry);
- mCreation = (TextView) this.findViewById(R.id.creation);
- mAlgorithm = (TextView) this.findViewById(R.id.algorithm);
-
- Intent intent = getIntent();
- mDataUri = intent.getData();
- if (mDataUri == null) {
- Log.e(Constants.TAG, "Intent data missing. Should be Uri of app!");
- finish();
- return;
- } else {
- Log.d(Constants.TAG, "uri: " + mDataUri);
- loadData(mDataUri);
- }
- }
-
- private void loadData(Uri dataUri) {
- PGPPublicKeyRing ring = (PGPPublicKeyRing) ProviderHelper.getPGPKeyRing(this, dataUri);
- mPublicKey = ring.getPublicKey();
-
- mFingerint.setText(PgpKeyHelper.shortifyFingerprint(PgpKeyHelper
- .convertFingerprintToHex(mPublicKey.getFingerprint())));
- String[] mainUserId = splitUserId("");
-
- Date expiryDate = PgpKeyHelper.getExpiryDate(mPublicKey);
- if (expiryDate == null) {
- mExpiry.setText("");
- } else {
- mExpiry.setText(DateFormat.getDateFormat(getApplicationContext()).format(expiryDate));
- }
-
- mCreation.setText(DateFormat.getDateFormat(getApplicationContext()).format(
- PgpKeyHelper.getCreationDate(mPublicKey)));
- mAlgorithm.setText(PgpKeyHelper.getAlgorithmInfo(mPublicKey));
- }
-
- private String[] splitUserId(String userId) {
-
- String[] result = new String[] { "", "", "" };
- Log.v("UserID", userId);
-
- Pattern withComment = Pattern.compile("^(.*) [(](.*)[)] <(.*)>$");
- Matcher matcher = withComment.matcher(userId);
- if (matcher.matches()) {
- result[0] = matcher.group(1);
- result[1] = matcher.group(2);
- result[2] = matcher.group(3);
- return result;
- }
-
- Pattern withoutComment = Pattern.compile("^(.*) <(.*)>$");
- matcher = withoutComment.matcher(userId);
- if (matcher.matches()) {
- result[0] = matcher.group(1);
- result[1] = matcher.group(2);
- return result;
- }
- return result;
- }
-}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListActivity.java
index 7b844fe08..4d1849029 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListActivity.java
@@ -1,18 +1,18 @@
/*
- * Copyright (C) 2012 Dominik Schürmann
- * Copyright (C) 2010 Thialfihar
+ * Copyright (C) 2013 Dominik Schürmann
*
- * 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
*/
package org.sufficientlysecure.keychain.ui;
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListFragment.java
deleted file mode 100644
index 0d61b1108..000000000
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListFragment.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2012-2013 Dominik Schürmann
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package org.sufficientlysecure.keychain.ui;
-
-import org.sufficientlysecure.keychain.Id;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.ui.widget.ExpandableListFragment;
-import org.sufficientlysecure.keychain.R;
-
-import android.os.Bundle;
-import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.view.View;
-import android.widget.ExpandableListView;
-import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
-
-public class KeyListFragment extends ExpandableListFragment {
- protected KeyListActivity mKeyListActivity;
-
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
-
- mKeyListActivity = (KeyListActivity) getActivity();
-
- // register long press context menu
- registerForContextMenu(getListView());
-
- // Give some text to display if there is no data. In a real
- // application this would come from a resource.
- setEmptyText(getString(R.string.list_empty));
- }
-
- /**
- * Context Menu on Long Click
- */
- @Override
- public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, v, menuInfo);
- menu.add(0, Id.menu.export, 5, R.string.menu_export_key);
- menu.add(0, Id.menu.delete, 111, R.string.menu_delete_key);
- }
-
- @Override
- public boolean onContextItemSelected(android.view.MenuItem item) {
- ExpandableListContextMenuInfo expInfo = (ExpandableListContextMenuInfo) item.getMenuInfo();
-
- // expInfo.id would also return row id of childs, but we always want to get the row id of
- // the group item, thus we are using the following way
- int groupPosition = ExpandableListView.getPackedPositionGroup(expInfo.packedPosition);
- long keyRingRowId = getExpandableListAdapter().getGroupId(groupPosition);
-
- switch (item.getItemId()) {
- case Id.menu.export:
- long masterKeyId = ProviderHelper.getPublicMasterKeyId(mKeyListActivity, keyRingRowId);
- if (masterKeyId == -1) {
- masterKeyId = ProviderHelper.getSecretMasterKeyId(mKeyListActivity, keyRingRowId);
- }
-
- mKeyListActivity.showExportKeysDialog(masterKeyId);
- return true;
-
- case Id.menu.delete:
- mKeyListActivity.showDeleteKeyDialog(keyRingRowId);
- return true;
-
- default:
- return super.onContextItemSelected(item);
-
- }
- }
-
-}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
index 0cf15a451..3dafc84ca 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2013 Dominik Schürmann
+ * Copyright (C) 2013 Dominik Schürmann
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,37 +17,32 @@
package org.sufficientlysecure.keychain.ui;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.sufficientlysecure.keychain.Id;
-import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.provider.KeychainContract;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
-import org.sufficientlysecure.keychain.service.remote.AppSettingsActivity;
-import org.sufficientlysecure.keychain.ui.adapter.KeyListAdapter;
-import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.ui.adapter.KeyListPublicAdapter;
-import android.content.ContentUris;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
+import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
-import android.support.v4.app.LoaderManager;
-import android.view.ContextMenu;
import android.view.View;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.widget.ExpandableListView;
-import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+
+import com.actionbarsherlock.app.SherlockListFragment;
-public class KeyListPublicFragment extends KeyListFragment implements
+public class KeyListPublicFragment extends SherlockListFragment implements
LoaderManager.LoaderCallbacks {
private KeyListPublicActivity mKeyListPublicActivity;
- private KeyListAdapter mAdapter;
+ private KeyListPublicAdapter mAdapter;
/**
* Define Adapter and Loader on create of Activity
@@ -58,135 +53,34 @@ public class KeyListPublicFragment extends KeyListFragment implements
mKeyListPublicActivity = (KeyListPublicActivity) getActivity();
- mAdapter = new KeyListAdapter(mKeyListPublicActivity, null, Id.type.public_key);
- setListAdapter(mAdapter);
-
- // Start out with a progress indicator.
- setListShown(false);
-
- // Prepare the loader. Either re-connect with an existing one,
- // or start a new one.
- // id is -1 as the child cursors are numbered 0,...,n
- getLoaderManager().initLoader(-1, null, this);
- }
-
- /**
- * Context Menu on Long Click
- */
- @Override
- public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, v, menuInfo);
- menu.add(0, 23, 1, R.string.title_key_details); // :TODO: Fix magic number
- menu.add(0, Id.menu.update, 2, R.string.menu_update_key);
- menu.add(0, Id.menu.signKey, 3, R.string.menu_sign_key);
- menu.add(0, Id.menu.exportToServer, 4, R.string.menu_export_key_to_server);
- menu.add(0, Id.menu.share, 6, R.string.menu_share);
- menu.add(0, Id.menu.share_qr_code, 7, R.string.menu_share_qr_code);
- menu.add(0, Id.menu.share_nfc, 8, R.string.menu_share_nfc);
-
- }
-
- @Override
- public boolean onContextItemSelected(android.view.MenuItem item) {
- ExpandableListContextMenuInfo expInfo = (ExpandableListContextMenuInfo) item.getMenuInfo();
-
- // expInfo.id would also return row id of childs, but we always want to get the row id of
- // the group item, thus we are using the following way
- int groupPosition = ExpandableListView.getPackedPositionGroup(expInfo.packedPosition);
- long keyRingRowId = getExpandableListAdapter().getGroupId(groupPosition);
-
- switch (item.getItemId()) {
- case Id.menu.update:
- long updateKeyId = 0;
- PGPPublicKeyRing updateKeyRing = ProviderHelper.getPGPPublicKeyRingByRowId(
- mKeyListActivity, keyRingRowId);
- if (updateKeyRing != null) {
- updateKeyId = PgpKeyHelper.getMasterKey(updateKeyRing).getKeyID();
+ getListView().setOnItemClickListener(new OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView> adapterView, View view, int position, long id) {
+ // start key view on click
+ Intent detailsIntent = new Intent(mKeyListPublicActivity, KeyViewActivity.class);
+ detailsIntent.setData(KeychainContract.KeyRings.buildPublicKeyRingsUri(Long
+ .toString(id)));
+ startActivity(detailsIntent);
}
- if (updateKeyId == 0) {
- // this shouldn't happen
- return true;
- }
-
- Intent queryIntent = new Intent(mKeyListActivity, KeyServerQueryActivity.class);
- queryIntent.setAction(KeyServerQueryActivity.ACTION_LOOK_UP_KEY_ID_AND_RETURN);
- queryIntent.putExtra(KeyServerQueryActivity.EXTRA_KEY_ID, updateKeyId);
+ });
- // TODO: lookup??
- startActivityForResult(queryIntent, Id.request.look_up_key_id);
+ // Give some text to display if there is no data. In a real
+ // application this would come from a resource.
+ setEmptyText(getString(R.string.list_empty));
- return true;
- case 23:
+ // We have a menu item to show in action bar.
+ setHasOptionsMenu(true);
- Intent detailsIntent = new Intent(mKeyListActivity, KeyDetailsActivity.class);
- detailsIntent.setData(KeychainContract.KeyRings.buildPublicKeyRingsUri(Long
- .toString(keyRingRowId)));
- startActivity(detailsIntent);
- return true;
-
- case Id.menu.exportToServer:
- Intent uploadIntent = new Intent(mKeyListActivity, KeyServerUploadActivity.class);
- uploadIntent.setAction(KeyServerUploadActivity.ACTION_EXPORT_KEY_TO_SERVER);
- uploadIntent.putExtra(KeyServerUploadActivity.EXTRA_KEYRING_ROW_ID, (int) keyRingRowId);
- startActivityForResult(uploadIntent, Id.request.export_to_server);
-
- return true;
-
- case Id.menu.signKey:
- long keyId = 0;
- PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByRowId(
- mKeyListActivity, keyRingRowId);
- if (signKeyRing != null) {
- keyId = PgpKeyHelper.getMasterKey(signKeyRing).getKeyID();
- }
- if (keyId == 0) {
- // this shouldn't happen
- return true;
- }
-
- Intent signIntent = new Intent(mKeyListActivity, SignKeyActivity.class);
- signIntent.putExtra(SignKeyActivity.EXTRA_KEY_ID, keyId);
- startActivity(signIntent);
-
- return true;
-
- case Id.menu.share_qr_code:
- // get master key id using row id
- long masterKeyId = ProviderHelper.getPublicMasterKeyId(mKeyListActivity, keyRingRowId);
-
- Intent qrCodeIntent = new Intent(mKeyListActivity, ShareActivity.class);
- qrCodeIntent.setAction(ShareActivity.ACTION_SHARE_KEYRING_WITH_QR_CODE);
- qrCodeIntent.putExtra(ShareActivity.EXTRA_MASTER_KEY_ID, masterKeyId);
- startActivityForResult(qrCodeIntent, 0);
-
- return true;
-
- case Id.menu.share_nfc:
- // get master key id using row id
- long masterKeyId2 = ProviderHelper.getPublicMasterKeyId(mKeyListActivity, keyRingRowId);
-
- Intent nfcIntent = new Intent(mKeyListActivity, ShareNfcBeamActivity.class);
- nfcIntent.setAction(ShareNfcBeamActivity.ACTION_SHARE_KEYRING_WITH_NFC);
- nfcIntent.putExtra(ShareNfcBeamActivity.EXTRA_MASTER_KEY_ID, masterKeyId2);
- startActivityForResult(nfcIntent, 0);
-
- return true;
-
- case Id.menu.share:
- // get master key id using row id
- long masterKeyId3 = ProviderHelper.getPublicMasterKeyId(mKeyListActivity, keyRingRowId);
-
- Intent shareIntent = new Intent(mKeyListActivity, ShareActivity.class);
- shareIntent.setAction(ShareActivity.ACTION_SHARE_KEYRING);
- shareIntent.putExtra(ShareActivity.EXTRA_MASTER_KEY_ID, masterKeyId3);
- startActivityForResult(shareIntent, 0);
-
- return true;
+ // Start out with a progress indicator.
+ setListShown(false);
- default:
- return super.onContextItemSelected(item);
+ // Create an empty adapter we will use to display the loaded data.
+ mAdapter = new KeyListPublicAdapter(mKeyListPublicActivity, null, Id.type.public_key);
+ setListAdapter(mAdapter);
- }
+ // Prepare the loader. Either re-connect with an existing one,
+ // or start a new one.
+ getLoaderManager().initLoader(0, null, this);
}
// These are the rows that we will retrieve.
@@ -210,7 +104,7 @@ public class KeyListPublicFragment extends KeyListFragment implements
public void onLoadFinished(Loader loader, Cursor data) {
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
- mAdapter.setGroupCursor(data);
+ mAdapter.swapCursor(data);
// The list should now be shown.
if (isResumed()) {
@@ -225,7 +119,7 @@ public class KeyListPublicFragment extends KeyListFragment implements
// This is called when the last Cursor provided to onLoadFinished()
// above is about to be closed. We need to make sure we are no
// longer using it.
- mAdapter.setGroupCursor(null);
+ mAdapter.swapCursor(null);
}
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java
index 822c73872..d98b5ab37 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java
@@ -1,18 +1,18 @@
/*
- * Copyright (C) 2012 Dominik Schürmann
- * Copyright (C) 2010 Thialfihar
+ * Copyright (C) 2013 Dominik Schürmann
*
- * 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
*/
package org.sufficientlysecure.keychain.ui;
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java
index 4bbf3d6ef..e8a306063 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java
@@ -1,18 +1,18 @@
/*
- * Copyright (C) 2012 Dominik Schürmann
- * Copyright (C) 2010 Thialfihar
+ * Copyright (C) 2013 Dominik Schürmann
*
- * 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
*/
package org.sufficientlysecure.keychain.ui;
@@ -22,7 +22,7 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.ui.adapter.KeyListAdapter;
+import org.sufficientlysecure.keychain.ui.adapter.KeyListSecretAdapter;
import android.database.Cursor;
import android.net.Uri;
@@ -30,18 +30,18 @@ import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
-import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
import android.view.View;
-import android.widget.ExpandableListView;
-import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
-public class KeyListSecretFragment extends KeyListFragment implements
+import com.actionbarsherlock.app.SherlockListFragment;
+
+public class KeyListSecretFragment extends SherlockListFragment implements
LoaderManager.LoaderCallbacks {
private KeyListSecretActivity mKeyListSecretActivity;
- private KeyListAdapter mAdapter;
+ private KeyListSecretAdapter mAdapter;
/**
* Define Adapter and Loader on create of Activity
@@ -52,53 +52,38 @@ public class KeyListSecretFragment extends KeyListFragment implements
mKeyListSecretActivity = (KeyListSecretActivity) getActivity();
- mAdapter = new KeyListAdapter(mKeyListSecretActivity, null, Id.type.secret_key);
- setListAdapter(mAdapter);
+ getListView().setOnItemClickListener(new OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView> adapterView, View view, int position, long id) {
+ // TODO: currently this is EDIT directly, later first show VIEW
- // Start out with a progress indicator.
- setListShown(false);
+ // get master key id using row id
+ long masterKeyId = ProviderHelper.getSecretMasterKeyId(mKeyListSecretActivity, id);
- // Prepare the loader. Either re-connect with an existing one,
- // or start a new one.
- // id is -1 as the child cursors are numbered 0,...,n
- getLoaderManager().initLoader(-1, null, this);
- }
+ boolean masterCanSign = ProviderHelper.getSecretMasterKeyCanSign(
+ mKeyListSecretActivity, id);
- /**
- * Context Menu on Long Click
- */
- @Override
- public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, v, menuInfo);
- menu.add(0, Id.menu.edit, 0, R.string.menu_edit_key);
- }
-
- @Override
- public boolean onContextItemSelected(android.view.MenuItem item) {
- ExpandableListContextMenuInfo expInfo = (ExpandableListContextMenuInfo) item.getMenuInfo();
+ mKeyListSecretActivity.editKey(masterKeyId, masterCanSign);
+ }
+ });
- // expInfo.id would also return row id of childs, but we always want to get the row id of
- // the group item, thus we are using the following way
- int groupPosition = ExpandableListView.getPackedPositionGroup(expInfo.packedPosition);
- long keyRingRowId = getExpandableListAdapter().getGroupId(groupPosition);
+ // Give some text to display if there is no data. In a real
+ // application this would come from a resource.
+ setEmptyText(getString(R.string.list_empty));
- // get master key id using row id
- long masterKeyId = ProviderHelper
- .getSecretMasterKeyId(mKeyListSecretActivity, keyRingRowId);
+ // We have a menu item to show in action bar.
+ setHasOptionsMenu(true);
- boolean masterCanSign = ProviderHelper.getSecretMasterKeyCanSign(mKeyListSecretActivity,
- keyRingRowId);
-
- switch (item.getItemId()) {
- case Id.menu.edit:
- mKeyListSecretActivity.editKey(masterKeyId, masterCanSign);
-
- return true;
+ // Start out with a progress indicator.
+ setListShown(false);
- default:
- return super.onContextItemSelected(item);
+ // Create an empty adapter we will use to display the loaded data.
+ mAdapter = new KeyListSecretAdapter(mKeyListSecretActivity, null, Id.type.secret_key);
+ setListAdapter(mAdapter);
- }
+ // Prepare the loader. Either re-connect with an existing one,
+ // or start a new one.
+ getLoaderManager().initLoader(0, null, this);
}
// These are the rows that we will retrieve.
@@ -107,22 +92,23 @@ public class KeyListSecretFragment extends KeyListFragment implements
static final String SORT_ORDER = UserIds.USER_ID + " ASC";
- @Override
public Loader onCreateLoader(int id, Bundle args) {
// This is called when a new Loader needs to be created. This
// sample only has one Loader, so we don't care about the ID.
+ // First, pick the base URI to use depending on whether we are
+ // currently filtering.
Uri baseUri = KeyRings.buildSecretKeyRingsUri();
// Now create and return a CursorLoader that will take care of
// creating a Cursor for the data being displayed.
- return new CursorLoader(getActivity(), baseUri, PROJECTION, null, null, SORT_ORDER);
+ return new CursorLoader(getActivity(), baseUri, PROJECTION, null, null, UserIds.USER_ID
+ + " COLLATE LOCALIZED ASC");
}
- @Override
public void onLoadFinished(Loader loader, Cursor data) {
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
- mAdapter.setGroupCursor(data);
+ mAdapter.swapCursor(data);
// The list should now be shown.
if (isResumed()) {
@@ -132,12 +118,10 @@ public class KeyListSecretFragment extends KeyListFragment implements
}
}
- @Override
public void onLoaderReset(Loader loader) {
// This is called when the last Cursor provided to onLoadFinished()
// above is about to be closed. We need to make sure we are no
// longer using it.
- mAdapter.setGroupCursor(null);
+ mAdapter.swapCursor(null);
}
-
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java
new file mode 100644
index 000000000..c2ef64d51
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2013 Bahtiar 'kalkin' Gadimov
+ * Copyright (C) 2013 Dominik Schürmann
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+import java.util.Date;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.Id;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.util.Log;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract.DataUsageFeedback;
+import android.text.format.DateFormat;
+import android.widget.TextView;
+
+import com.actionbarsherlock.app.SherlockActivity;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
+
+public class KeyViewActivity extends SherlockActivity {
+ private Uri mDataUri;
+
+ private PGPPublicKey mPublicKey;
+
+ private TextView mAlgorithm;
+ private TextView mFingerint;
+ private TextView mExpiry;
+ private TextView mCreation;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setHomeButtonEnabled(true);
+
+ setContentView(R.layout.key_view_activity);
+
+ mFingerint = (TextView) this.findViewById(R.id.fingerprint);
+ mExpiry = (TextView) this.findViewById(R.id.expiry);
+ mCreation = (TextView) this.findViewById(R.id.creation);
+ mAlgorithm = (TextView) this.findViewById(R.id.algorithm);
+
+ Intent intent = getIntent();
+ mDataUri = intent.getData();
+ if (mDataUri == null) {
+ Log.e(Constants.TAG, "Intent data missing. Should be Uri of app!");
+ finish();
+ return;
+ } else {
+ Log.d(Constants.TAG, "uri: " + mDataUri);
+ loadData(mDataUri);
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ getSupportMenuInflater().inflate(R.menu.key_view, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // TODO: use data uri in the other activities instead of givin key ring row id!
+
+ long keyRingRowId = Long.valueOf(mDataUri.getLastPathSegment());
+
+ switch (item.getItemId()) {
+ case R.id.menu_key_view_update:
+ long updateKeyId = 0;
+ PGPPublicKeyRing updateKeyRing = ProviderHelper.getPGPPublicKeyRingByRowId(this,
+ keyRingRowId);
+ if (updateKeyRing != null) {
+ updateKeyId = PgpKeyHelper.getMasterKey(updateKeyRing).getKeyID();
+ }
+ if (updateKeyId == 0) {
+ // this shouldn't happen
+ return true;
+ }
+
+ Intent queryIntent = new Intent(this, KeyServerQueryActivity.class);
+ queryIntent.setAction(KeyServerQueryActivity.ACTION_LOOK_UP_KEY_ID_AND_RETURN);
+ queryIntent.putExtra(KeyServerQueryActivity.EXTRA_KEY_ID, updateKeyId);
+
+ // TODO: lookup??
+ startActivityForResult(queryIntent, Id.request.look_up_key_id);
+
+ return true;
+ case R.id.menu_key_view_sign:
+ long keyId = 0;
+ PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByRowId(this,
+ keyRingRowId);
+ if (signKeyRing != null) {
+ keyId = PgpKeyHelper.getMasterKey(signKeyRing).getKeyID();
+ }
+ if (keyId == 0) {
+ // this shouldn't happen
+ return true;
+ }
+
+ Intent signIntent = new Intent(this, SignKeyActivity.class);
+ signIntent.putExtra(SignKeyActivity.EXTRA_KEY_ID, keyId);
+ startActivity(signIntent);
+ return true;
+ case R.id.menu_key_view_export_keyserver:
+ Intent uploadIntent = new Intent(this, KeyServerUploadActivity.class);
+ uploadIntent.setAction(KeyServerUploadActivity.ACTION_EXPORT_KEY_TO_SERVER);
+ uploadIntent.putExtra(KeyServerUploadActivity.EXTRA_KEYRING_ROW_ID, (int) keyRingRowId);
+ startActivityForResult(uploadIntent, Id.request.export_to_server);
+
+ return true;
+ case R.id.menu_key_view_export_file:
+// long masterKeyId = ProviderHelper.getPublicMasterKeyId(mKeyListActivity, keyRingRowId);
+// if (masterKeyId == -1) {
+// masterKeyId = ProviderHelper.getSecretMasterKeyId(mKeyListActivity, keyRingRowId);
+// }
+//
+// mKeyListActivity.showExportKeysDialog(masterKeyId);
+ return true;
+ case R.id.menu_key_view_share:
+ // get master key id using row id
+ long masterKeyId3 = ProviderHelper.getPublicMasterKeyId(this, keyRingRowId);
+
+ Intent shareIntent = new Intent(this, ShareActivity.class);
+ shareIntent.setAction(ShareActivity.ACTION_SHARE_KEYRING);
+ shareIntent.putExtra(ShareActivity.EXTRA_MASTER_KEY_ID, masterKeyId3);
+ startActivityForResult(shareIntent, 0);
+
+ return true;
+ case R.id.menu_key_view_share_qr_code:
+ // get master key id using row id
+ long masterKeyId = ProviderHelper.getPublicMasterKeyId(this, keyRingRowId);
+
+ Intent qrCodeIntent = new Intent(this, ShareActivity.class);
+ qrCodeIntent.setAction(ShareActivity.ACTION_SHARE_KEYRING_WITH_QR_CODE);
+ qrCodeIntent.putExtra(ShareActivity.EXTRA_MASTER_KEY_ID, masterKeyId);
+ startActivityForResult(qrCodeIntent, 0);
+
+ return true;
+ case R.id.menu_key_view_share_nfc:
+ // get master key id using row id
+ long masterKeyId2 = ProviderHelper.getPublicMasterKeyId(this, keyRingRowId);
+
+ Intent nfcIntent = new Intent(this, ShareNfcBeamActivity.class);
+ nfcIntent.setAction(ShareNfcBeamActivity.ACTION_SHARE_KEYRING_WITH_NFC);
+ nfcIntent.putExtra(ShareNfcBeamActivity.EXTRA_MASTER_KEY_ID, masterKeyId2);
+ startActivityForResult(nfcIntent, 0);
+
+ return true;
+ case R.id.menu_key_view_delete:
+// mKeyListActivity.showDeleteKeyDialog(keyRingRowId);
+
+ return true;
+
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void loadData(Uri dataUri) {
+ PGPPublicKeyRing ring = (PGPPublicKeyRing) ProviderHelper.getPGPKeyRing(this, dataUri);
+ mPublicKey = ring.getPublicKey();
+
+ mFingerint.setText(PgpKeyHelper.shortifyFingerprint(PgpKeyHelper
+ .convertFingerprintToHex(mPublicKey.getFingerprint())));
+ String[] mainUserId = splitUserId("");
+
+ Date expiryDate = PgpKeyHelper.getExpiryDate(mPublicKey);
+ if (expiryDate == null) {
+ mExpiry.setText("");
+ } else {
+ mExpiry.setText(DateFormat.getDateFormat(getApplicationContext()).format(expiryDate));
+ }
+
+ mCreation.setText(DateFormat.getDateFormat(getApplicationContext()).format(
+ PgpKeyHelper.getCreationDate(mPublicKey)));
+ mAlgorithm.setText(PgpKeyHelper.getAlgorithmInfo(mPublicKey));
+ }
+
+ private String[] splitUserId(String userId) {
+
+ String[] result = new String[] { "", "", "" };
+ Log.v("UserID", userId);
+
+ Pattern withComment = Pattern.compile("^(.*) [(](.*)[)] <(.*)>$");
+ Matcher matcher = withComment.matcher(userId);
+ if (matcher.matches()) {
+ result[0] = matcher.group(1);
+ result[1] = matcher.group(2);
+ result[2] = matcher.group(3);
+ return result;
+ }
+
+ Pattern withoutComment = Pattern.compile("^(.*) <(.*)>$");
+ matcher = withoutComment.matcher(userId);
+ if (matcher.matches()) {
+ result[0] = matcher.group(1);
+ result[1] = matcher.group(2);
+ return result;
+ }
+ return result;
+ }
+}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java
new file mode 100644
index 000000000..d72c9d42a
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2013 Dominik Schürmann
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package org.sufficientlysecure.keychain.ui.adapter;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.OtherHelper;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
+import org.sufficientlysecure.keychain.util.SectionCursorAdapter;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+public class KeyListPublicAdapter extends SectionCursorAdapter {
+
+ private LayoutInflater mInflater;
+
+ public KeyListPublicAdapter(Context context, Cursor c, int flags) {
+ super(context, c, android.R.layout.preference_category, 2); // TODO: 2 is user id
+
+ mInflater = LayoutInflater.from(context);
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ int userIdIndex = cursor.getColumnIndex(UserIds.USER_ID);
+
+ TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
+ mainUserId.setText(R.string.unknown_user_id);
+ TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
+ mainUserIdRest.setText("");
+
+ String userId = cursor.getString(userIdIndex);
+ if (userId != null) {
+ String[] userIdSplit = OtherHelper.splitUserId(userId);
+
+ if (userIdSplit[1] != null) {
+ mainUserIdRest.setText(userIdSplit[1]);
+ }
+ mainUserId.setText(userIdSplit[0]);
+ }
+
+ if (mainUserId.getText().length() == 0) {
+ mainUserId.setText(R.string.unknown_user_id);
+ }
+
+ if (mainUserIdRest.getText().length() == 0) {
+ mainUserIdRest.setVisibility(View.GONE);
+ } else {
+ mainUserIdRest.setVisibility(View.VISIBLE);
+ }
+ }
+
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return mInflater.inflate(R.layout.key_list_group_item, null);
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java
new file mode 100644
index 000000000..6f3129e4f
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2013 Dominik Schürmann
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package org.sufficientlysecure.keychain.ui.adapter;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.OtherHelper;
+import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.database.Cursor;
+import android.support.v4.widget.CursorAdapter;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+public class KeyListSecretAdapter extends CursorAdapter {
+
+ private LayoutInflater mInflater;
+
+ public KeyListSecretAdapter(Context context, Cursor c, int flags) {
+ super(context, c, flags);
+
+ mInflater = LayoutInflater.from(context);
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ int userIdIndex = cursor.getColumnIndex(UserIds.USER_ID);
+
+ TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
+ mainUserId.setText(R.string.unknown_user_id);
+ TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
+ mainUserIdRest.setText("");
+
+ String userId = cursor.getString(userIdIndex);
+ if (userId != null) {
+ String[] userIdSplit = OtherHelper.splitUserId(userId);
+
+ if (userIdSplit[1] != null) {
+ mainUserIdRest.setText(userIdSplit[1]);
+ }
+ mainUserId.setText(userIdSplit[0]);
+ }
+
+ if (mainUserId.getText().length() == 0) {
+ mainUserId.setText(R.string.unknown_user_id);
+ }
+
+ if (mainUserIdRest.getText().length() == 0) {
+ mainUserIdRest.setVisibility(View.GONE);
+ } else {
+ mainUserIdRest.setVisibility(View.VISIBLE);
+ }
+ }
+
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return mInflater.inflate(R.layout.key_list_group_item, null);
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/SectionCursorAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/SectionCursorAdapter.java
new file mode 100644
index 000000000..0d4092d9e
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/SectionCursorAdapter.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2013 Dominik Schürmann
+ * Copyright (C) 2011 Gonçalo Ferreira
+ *
+ * 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.util;
+
+import java.util.LinkedHashMap;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.database.DataSetObserver;
+import android.support.v4.widget.CursorAdapter;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+/**
+ * Originally from https://github.com/monxalo/android-section-adapter
+ *
+ * getCustomGroup() has been modified
+ */
+public abstract class SectionCursorAdapter extends CursorAdapter {
+
+ private static final String TAG = "SectionCursorAdapter";
+ private static final boolean LOGV = false;
+
+ private static final int TYPE_NORMAL = 1;
+ private static final int TYPE_HEADER = 0;
+ private static final int TYPE_COUNT = 2;
+
+ private final int mHeaderRes;
+ private final int mGroupColumn;
+ private final LayoutInflater mLayoutInflater;
+
+ private LinkedHashMap sectionsIndexer;
+
+ public static class ViewHolder {
+ public TextView textView;
+ }
+
+ public SectionCursorAdapter(Context context, Cursor c, int headerLayout, int groupColumn) {
+ super(context, c, 0);
+
+ sectionsIndexer = new LinkedHashMap();
+
+ mHeaderRes = headerLayout;
+ mGroupColumn = groupColumn;
+ mLayoutInflater = LayoutInflater.from(context);
+
+ if (c != null) {
+ calculateSectionHeaders();
+ c.registerDataSetObserver(mDataSetObserver);
+ }
+ }
+
+ private DataSetObserver mDataSetObserver = new DataSetObserver() {
+ public void onChanged() {
+ calculateSectionHeaders();
+ };
+
+ public void onInvalidated() {
+ sectionsIndexer.clear();
+ };
+ };
+
+ /**
+ *
+ * This method serve as an intercepter before the sections are calculated so you can transform
+ * some computer data into human readable, e.g. format a unix timestamp, or a status.
+ *
+ *
+ *
+ * By default this method returns the original data for the group column.
+ *
+ *
+ * @param groupData
+ * @return
+ */
+ protected String getCustomGroup(String groupData) {
+ return groupData.substring(0, 1);
+ }
+
+ private void calculateSectionHeaders() {
+ int i = 0;
+
+ String previous = "";
+ int count = 0;
+
+ final Cursor c = getCursor();
+
+ sectionsIndexer.clear();
+
+ if (c == null) {
+ return;
+ }
+
+ c.moveToPosition(-1);
+
+ while (c.moveToNext()) {
+ final String group = getCustomGroup(c.getString(mGroupColumn));
+
+ if (!previous.equals(group)) {
+ sectionsIndexer.put(i + count, group);
+ previous = group;
+
+ if (LOGV)
+ Log.v(TAG, "Group " + group + "at position: " + (i + count));
+
+ count++;
+ }
+
+ i++;
+ }
+ }
+
+ public String getGroupCustomFormat(Object obj) {
+ return null;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ int viewType = getItemViewType(position);
+
+ if (viewType == TYPE_NORMAL) {
+ Cursor c = (Cursor) getItem(position);
+
+ if (c == null) {
+ if (LOGV)
+ Log.d(TAG, "getItem(" + position + ") = null");
+ return mLayoutInflater.inflate(mHeaderRes, parent, false);
+ }
+
+ final int mapCursorPos = getSectionForPosition(position);
+ c.moveToPosition(mapCursorPos);
+
+ return super.getView(mapCursorPos, convertView, parent);
+ } else {
+ ViewHolder holder = null;
+
+ if (convertView == null) {
+ if (LOGV)
+ Log.v(TAG, "Creating new view for section");
+
+ holder = new ViewHolder();
+ convertView = mLayoutInflater.inflate(mHeaderRes, parent, false);
+ holder.textView = (TextView) convertView;
+
+ convertView.setTag(holder);
+ } else {
+ if (LOGV)
+ Log.v(TAG, "Reusing view for section");
+
+ holder = (ViewHolder) convertView.getTag();
+ }
+
+ TextView sectionText = holder.textView;
+
+ final String group = sectionsIndexer.get(position);
+ final String customFormat = getGroupCustomFormat(group);
+
+ sectionText.setText(customFormat == null ? group : customFormat);
+
+ return sectionText;
+ }
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return TYPE_COUNT;
+ }
+
+ @Override
+ public int getCount() {
+ return super.getCount() + sectionsIndexer.size();
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ return getItemViewType(position) == TYPE_NORMAL;
+ }
+
+ public int getPositionForSection(int section) {
+ if (sectionsIndexer.containsKey(section)) {
+ return section + 1;
+ }
+ return section;
+ }
+
+ public int getSectionForPosition(int position) {
+ int offset = 0;
+ for (Integer key : sectionsIndexer.keySet()) {
+ if (position > key) {
+ offset++;
+ } else {
+ break;
+ }
+ }
+
+ return position - offset;
+ }
+
+ @Override
+ public Object getItem(int position) {
+ if (getItemViewType(position) == TYPE_NORMAL) {
+ return super.getItem(getSectionForPosition(position));
+ }
+ return super.getItem(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ if (getItemViewType(position) == TYPE_NORMAL) {
+ return super.getItemId(getSectionForPosition(position));
+ }
+ return super.getItemId(position);
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ if (position == getPositionForSection(position)) {
+ return TYPE_NORMAL;
+ }
+ return TYPE_HEADER;
+ }
+
+ @Override
+ public void changeCursor(Cursor cursor) {
+ final Cursor old = swapCursor(cursor);
+
+ if (old != null) {
+ old.close();
+ }
+ }
+
+ @Override
+ public Cursor swapCursor(Cursor newCursor) {
+ if (getCursor() != null) {
+ getCursor().unregisterDataSetObserver(mDataSetObserver);
+ }
+
+ final Cursor oldCursor = super.swapCursor(newCursor);
+
+ calculateSectionHeaders();
+
+ if (newCursor != null) {
+ newCursor.registerDataSetObserver(mDataSetObserver);
+ }
+
+ return oldCursor;
+ }
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index 0bea8d377..80b34ab05 100644
--- a/README.md
+++ b/README.md
@@ -107,13 +107,6 @@ TODO
# Libraries
-All JAR-Libraries are provided in this repository under "libs", all Android Library projects are under "libraries".
-
-* ActionBarSherlock to provide an ActionBar for Android < 3.0
-* HtmlTextView for non-crashing TextViews with HTML content
-* forked Spongy Castle Crypto Lib (Android version of Bouncy Castle)
-* barcodescanner-android-integration-supportv4.jar: Barcode Scanner Integration
-
## Build Barcode Scanner Integration
1. Checkout their SVN (see http://code.google.com/p/zxing/source/checkout)
@@ -212,8 +205,8 @@ Some parts (older parts and some libraries are Apache License v2, MIT X11 Licens
http://code.google.com/p/zxing/
Apache License v2
-* pinned-section-listview
- https://github.com/beworker/pinned-section-listview
+* android-section-adapter
+ https://github.com/monxalo/android-section-adapter
Apache License v2
diff --git a/libraries/pinned-section-listview/.gitignore b/libraries/pinned-section-listview/.gitignore
deleted file mode 100644
index ba354bcae..000000000
--- a/libraries/pinned-section-listview/.gitignore
+++ /dev/null
@@ -1,6 +0,0 @@
-bin
-gen
-.project
-.classpath
-.settings
-.DS_Store
diff --git a/libraries/pinned-section-listview/README.md b/libraries/pinned-section-listview/README.md
deleted file mode 100644
index e89cde3bb..000000000
--- a/libraries/pinned-section-listview/README.md
+++ /dev/null
@@ -1,75 +0,0 @@
-Introduction
-============
-
-Easy to use ListView with pinned sections for Android. Pinned section is a header view which sticks to the top
-of the list until at least one item of that section is visible.
-
-
-
-
-
-Features
-========
-This list properly implements many features which are missing from other implementations. These are
- * Fast scroll
- * Headers and footers
- * Clickable pinned sections
-
-Besides this it doesn't create any unnecessary views, layouts etc. It's really lean.
-
-Watch [this video][1] to see `PinnedSectionListView` in action.
-
-Usage
-=====
- 1. Replace standard `ListView` with `com.hb.views.PinnedSectionListView` in your `layout.xml` file.
-
-
-
- 2. Extend your `ListAdapter` in a way that it implements `PinnedSectionListAdapter` interface, in addition to
- what it already implements. Basically you need to add a single `isItemViewTypePinned(int viewType)`
- method. This method must return `true` for all view types which have to be pinned.
-
- // Our adapter class implements 'PinnedSectionListAdapter' interface
- class MyPinnedSectionListAdapter extends BaseAdapter implements PinnedSectionListAdapter {
-
- ...
-
- // We implement this method to return 'true' for all view types we want to pin
- @Override
- public boolean isItemViewTypePinned(int viewType) {
- return viewType == ;
- }
- }
-
-That's all. You are done! A working example can also be found in `example` folder.
-
-Used by
-=======
-Let us know if you use this library in your application and we will mention it here.
-
-[Grocery Sum][2]
-
-License
-=======
-
- Copyright 2013 Sergej Shafarenka, halfbit.de
-
- 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.
-
-
-[1]: http://www.youtube.com/watch?v=mI3DpuoIIhQ
-[2]: https://play.google.com/store/apps/details?id=org.codechimp.grocerysum
diff --git a/libraries/pinned-section-listview/example/AndroidManifest.xml b/libraries/pinned-section-listview/example/AndroidManifest.xml
deleted file mode 100644
index 56621893d..000000000
--- a/libraries/pinned-section-listview/example/AndroidManifest.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/libraries/pinned-section-listview/example/assets/.gitignore b/libraries/pinned-section-listview/example/assets/.gitignore
deleted file mode 100644
index fdffa2a0f..000000000
--- a/libraries/pinned-section-listview/example/assets/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-# placeholder
diff --git a/libraries/pinned-section-listview/example/ic_launcher-web.png b/libraries/pinned-section-listview/example/ic_launcher-web.png
deleted file mode 100644
index 8bd40412e..000000000
Binary files a/libraries/pinned-section-listview/example/ic_launcher-web.png and /dev/null differ
diff --git a/libraries/pinned-section-listview/example/libs/android-support-v4.jar b/libraries/pinned-section-listview/example/libs/android-support-v4.jar
deleted file mode 100644
index 65ebaf8dc..000000000
Binary files a/libraries/pinned-section-listview/example/libs/android-support-v4.jar and /dev/null differ
diff --git a/libraries/pinned-section-listview/example/proguard-project.txt b/libraries/pinned-section-listview/example/proguard-project.txt
deleted file mode 100644
index f2fe1559a..000000000
--- a/libraries/pinned-section-listview/example/proguard-project.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-# To enable ProGuard in your project, edit project.properties
-# to define the proguard.config property as described in that file.
-#
-# Add project specific ProGuard rules here.
-# By default, the flags in this file are appended to flags specified
-# in ${sdk.dir}/tools/proguard/proguard-android.txt
-# You can edit the include path and order by changing the ProGuard
-# include property in project.properties.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# Add any project specific keep options here:
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
diff --git a/libraries/pinned-section-listview/example/project.properties b/libraries/pinned-section-listview/example/project.properties
deleted file mode 100644
index 1561d7a9a..000000000
--- a/libraries/pinned-section-listview/example/project.properties
+++ /dev/null
@@ -1,15 +0,0 @@
-# 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-17
-android.library.reference.1=../library
diff --git a/libraries/pinned-section-listview/example/res/drawable-hdpi/ic_launcher.png b/libraries/pinned-section-listview/example/res/drawable-hdpi/ic_launcher.png
deleted file mode 100644
index 3841c3408..000000000
Binary files a/libraries/pinned-section-listview/example/res/drawable-hdpi/ic_launcher.png and /dev/null differ
diff --git a/libraries/pinned-section-listview/example/res/drawable-mdpi/ic_launcher.png b/libraries/pinned-section-listview/example/res/drawable-mdpi/ic_launcher.png
deleted file mode 100644
index e65953cce..000000000
Binary files a/libraries/pinned-section-listview/example/res/drawable-mdpi/ic_launcher.png and /dev/null differ
diff --git a/libraries/pinned-section-listview/example/res/drawable-xhdpi/ic_launcher.png b/libraries/pinned-section-listview/example/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100644
index 4ea093d86..000000000
Binary files a/libraries/pinned-section-listview/example/res/drawable-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/libraries/pinned-section-listview/example/res/drawable-xxhdpi/ic_launcher.png b/libraries/pinned-section-listview/example/res/drawable-xxhdpi/ic_launcher.png
deleted file mode 100644
index 87e928a35..000000000
Binary files a/libraries/pinned-section-listview/example/res/drawable-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/libraries/pinned-section-listview/example/res/layout/activity_main.xml b/libraries/pinned-section-listview/example/res/layout/activity_main.xml
deleted file mode 100644
index b641f4c80..000000000
--- a/libraries/pinned-section-listview/example/res/layout/activity_main.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
\ No newline at end of file
diff --git a/libraries/pinned-section-listview/example/res/menu/main.xml b/libraries/pinned-section-listview/example/res/menu/main.xml
deleted file mode 100644
index 091b6c770..000000000
--- a/libraries/pinned-section-listview/example/res/menu/main.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
\ No newline at end of file
diff --git a/libraries/pinned-section-listview/example/res/values-v11/styles.xml b/libraries/pinned-section-listview/example/res/values-v11/styles.xml
deleted file mode 100644
index 541752f6e..000000000
--- a/libraries/pinned-section-listview/example/res/values-v11/styles.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/libraries/pinned-section-listview/example/res/values-v14/styles.xml b/libraries/pinned-section-listview/example/res/values-v14/styles.xml
deleted file mode 100644
index f20e01501..000000000
--- a/libraries/pinned-section-listview/example/res/values-v14/styles.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/libraries/pinned-section-listview/example/res/values/colors.xml b/libraries/pinned-section-listview/example/res/values/colors.xml
deleted file mode 100644
index b4da27c93..000000000
--- a/libraries/pinned-section-listview/example/res/values/colors.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- #ffff4444
- #ff99cc00
- #ffffbb33
- #ff33b5e5
-
-
-
-
\ No newline at end of file
diff --git a/libraries/pinned-section-listview/example/res/values/strings.xml b/libraries/pinned-section-listview/example/res/values/strings.xml
deleted file mode 100644
index e9d43a4aa..000000000
--- a/libraries/pinned-section-listview/example/res/values/strings.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
- Pinned Section Demo
- Fast scroll
- Add padding
- Show shadow
- Show Header & Footer
-
-
\ No newline at end of file
diff --git a/libraries/pinned-section-listview/example/res/values/styles.xml b/libraries/pinned-section-listview/example/res/values/styles.xml
deleted file mode 100644
index 4a10ca492..000000000
--- a/libraries/pinned-section-listview/example/res/values/styles.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/libraries/pinned-section-listview/example/src/com/hb/examples/PinnedSectionListActivity.java b/libraries/pinned-section-listview/example/src/com/hb/examples/PinnedSectionListActivity.java
deleted file mode 100644
index 223718a06..000000000
--- a/libraries/pinned-section-listview/example/src/com/hb/examples/PinnedSectionListActivity.java
+++ /dev/null
@@ -1,285 +0,0 @@
-/*
- * Copyright (C) 2013 Sergej Shafarenka, halfbit.de
- *
- * 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.hb.examples;
-
-import java.util.Locale;
-
-import android.annotation.SuppressLint;
-import android.app.ListActivity;
-import android.content.Context;
-import android.graphics.Color;
-import android.os.Build;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.ListView;
-import android.widget.SectionIndexer;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.hb.examples.pinnedsection.R;
-import com.hb.views.PinnedSectionListView;
-import com.hb.views.PinnedSectionListView.PinnedSectionListAdapter;
-
-public class PinnedSectionListActivity extends ListActivity implements OnClickListener {
-
- static class SimpleAdapter extends ArrayAdapter implements PinnedSectionListAdapter {
-
- private static final int[] COLORS = new int[] {
- R.color.green_light, R.color.orange_light,
- R.color.blue_light, R.color.red_light };
-
- public SimpleAdapter(Context context, int resource, int textViewResourceId) {
- super(context, resource, textViewResourceId);
-
- final int sectionsNumber = 'Z' - 'A' + 1;
- prepareSections(sectionsNumber);
-
- int sectionPosition = 0, listPosition = 0;
- for (char i=0; i= sections.length) {
- section = sections.length - 1;
- }
- return sections[section].listPosition;
- }
-
- @Override public int getSectionForPosition(int position) {
- if (position >= getCount()) {
- position = getCount() - 1;
- }
- return getItem(position).sectionPosition;
- }
-
- }
-
- static class Item {
-
- public static final int ITEM = 0;
- public static final int SECTION = 1;
-
- public final int type;
- public final String text;
-
- public int sectionPosition;
- public int listPosition;
-
- public Item(int type, String text) {
- this.type = type;
- this.text = text;
- }
-
- @Override public String toString() {
- return text;
- }
-
- }
-
- private boolean hasHeaderAndFooter;
- private boolean isFastScroll;
- private boolean addPadding;
- private boolean isShadowVisible = true;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- if (savedInstanceState != null) {
- isFastScroll = savedInstanceState.getBoolean("isFastScroll");
- addPadding = savedInstanceState.getBoolean("addPadding");
- isShadowVisible = savedInstanceState.getBoolean("isShadowVisible");
- hasHeaderAndFooter = savedInstanceState.getBoolean("hasHeaderAndFooter");
- }
- initializeHeaderAndFooter();
- initializeAdapter();
- initializePadding();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putBoolean("isFastScroll", isFastScroll);
- outState.putBoolean("addPadding", addPadding);
- outState.putBoolean("isShadowVisible", isShadowVisible);
- outState.putBoolean("hasHeaderAndFooter", hasHeaderAndFooter);
- }
-
- @Override
- protected void onListItemClick(ListView l, View v, int position, long id) {
- Item item = (Item) getListView().getAdapter().getItem(position);
- if (item != null) {
- Toast.makeText(this, "Item " + position + ": " + item.text, Toast.LENGTH_SHORT).show();
- } else {
- Toast.makeText(this, "Item " + position, Toast.LENGTH_SHORT).show();
- }
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.main, menu);
- menu.getItem(0).setChecked(isFastScroll);
- menu.getItem(1).setChecked(addPadding);
- menu.getItem(2).setChecked(isShadowVisible);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.action_fastscroll:
- isFastScroll = !isFastScroll;
- item.setChecked(isFastScroll);
- initializeAdapter();
- break;
- case R.id.action_addpadding:
- addPadding = !addPadding;
- item.setChecked(addPadding);
- initializePadding();
- break;
- case R.id.action_showShadow:
- isShadowVisible = !isShadowVisible;
- item.setChecked(isShadowVisible);
- ((PinnedSectionListView)getListView()).setShadowVisible(isShadowVisible);
- break;
- case R.id.action_showHeaderAndFooter:
- hasHeaderAndFooter = !hasHeaderAndFooter;
- item.setChecked(hasHeaderAndFooter);
- initializeHeaderAndFooter();
- break;
- }
- return true;
- }
-
- private void initializePadding() {
- float density = getResources().getDisplayMetrics().density;
- int padding = addPadding ? (int) (16 * density) : 0;
- getListView().setPadding(padding, padding, padding, padding);
- }
-
- private void initializeHeaderAndFooter() {
- setListAdapter(null);
- if (hasHeaderAndFooter) {
- ListView list = getListView();
-
- LayoutInflater inflater = LayoutInflater.from(this);
- TextView header1 = (TextView) inflater.inflate(android.R.layout.simple_list_item_1, list, false);
- header1.setText("First header");
- list.addHeaderView(header1);
-
- TextView header2 = (TextView) inflater.inflate(android.R.layout.simple_list_item_1, list, false);
- header2.setText("Second header");
- list.addHeaderView(header2);
-
- TextView footer = (TextView) inflater.inflate(android.R.layout.simple_list_item_1, list, false);
- footer.setText("Single footer");
- list.addFooterView(footer);
- }
- initializeAdapter();
- }
-
- @SuppressLint("NewApi")
- private void initializeAdapter() {
- getListView().setFastScrollEnabled(isFastScroll);
- if (isFastScroll) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- getListView().setFastScrollAlwaysVisible(true);
- }
- setListAdapter(new FastScrollAdapter(this, android.R.layout.simple_list_item_1, android.R.id.text1));
- } else {
- setListAdapter(new SimpleAdapter(this, android.R.layout.simple_list_item_1, android.R.id.text1));
- }
- }
-
- @Override
- public void onClick(View v) {
- Toast.makeText(this, "Item: " + v.getTag() , Toast.LENGTH_SHORT).show();
- }
-
-}
\ No newline at end of file
diff --git a/libraries/pinned-section-listview/library/AndroidManifest.xml b/libraries/pinned-section-listview/library/AndroidManifest.xml
deleted file mode 100644
index 2e2ee9173..000000000
--- a/libraries/pinned-section-listview/library/AndroidManifest.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/libraries/pinned-section-listview/library/assets/.gitignore b/libraries/pinned-section-listview/library/assets/.gitignore
deleted file mode 100644
index fdffa2a0f..000000000
--- a/libraries/pinned-section-listview/library/assets/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-# placeholder
diff --git a/libraries/pinned-section-listview/library/build.gradle b/libraries/pinned-section-listview/library/build.gradle
deleted file mode 100644
index d77f4746f..000000000
--- a/libraries/pinned-section-listview/library/build.gradle
+++ /dev/null
@@ -1,24 +0,0 @@
-buildscript {
- repositories {
- mavenCentral()
- }
- dependencies {
- classpath 'com.android.tools.build:gradle:0.6.3'
- }
-}
-
-apply plugin: 'android-library'
-
-
-android {
- compileSdkVersion 17
- buildToolsVersion '19'
-
- sourceSets {
- main {
- manifest.srcFile 'AndroidManifest.xml'
- java.srcDirs = ['src']
- res.srcDirs = ['res']
- }
- }
-}
diff --git a/libraries/pinned-section-listview/library/proguard-project.txt b/libraries/pinned-section-listview/library/proguard-project.txt
deleted file mode 100644
index f2fe1559a..000000000
--- a/libraries/pinned-section-listview/library/proguard-project.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-# To enable ProGuard in your project, edit project.properties
-# to define the proguard.config property as described in that file.
-#
-# Add project specific ProGuard rules here.
-# By default, the flags in this file are appended to flags specified
-# in ${sdk.dir}/tools/proguard/proguard-android.txt
-# You can edit the include path and order by changing the ProGuard
-# include property in project.properties.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# Add any project specific keep options here:
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
diff --git a/libraries/pinned-section-listview/library/project.properties b/libraries/pinned-section-listview/library/project.properties
deleted file mode 100644
index 484dab075..000000000
--- a/libraries/pinned-section-listview/library/project.properties
+++ /dev/null
@@ -1,15 +0,0 @@
-# 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-17
-android.library=true
diff --git a/libraries/pinned-section-listview/library/res/.gitignore b/libraries/pinned-section-listview/library/res/.gitignore
deleted file mode 100644
index fdffa2a0f..000000000
--- a/libraries/pinned-section-listview/library/res/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-# placeholder
diff --git a/libraries/pinned-section-listview/library/src/com/hb/views/PinnedSectionListView.java b/libraries/pinned-section-listview/library/src/com/hb/views/PinnedSectionListView.java
deleted file mode 100644
index 8100ad693..000000000
--- a/libraries/pinned-section-listview/library/src/com/hb/views/PinnedSectionListView.java
+++ /dev/null
@@ -1,513 +0,0 @@
-/*
- * Copyright (C) 2013 Sergej Shafarenka, halfbit.de
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file kt 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.hb.views;
-
-import android.content.Context;
-import android.database.DataSetObserver;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.GradientDrawable.Orientation;
-import android.os.Parcelable;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.SoundEffectConstants;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.accessibility.AccessibilityEvent;
-import android.widget.AbsListView;
-import android.widget.HeaderViewListAdapter;
-import android.widget.ListAdapter;
-import android.widget.ListView;
-import android.widget.SectionIndexer;
-
-import com.hb.views.pinnedsection.BuildConfig;
-
-/**
- * ListView, which is capable to pin section views at its top while the rest is still scrolled.
- */
-public class PinnedSectionListView extends ListView {
-
- //-- inner classes
-
- /** List adapter to be implemented for being used with PinnedSectionListView adapter. */
- public static interface PinnedSectionListAdapter extends ListAdapter {
- /** This method shall return 'true' if views of given type has to be pinned. */
- boolean isItemViewTypePinned(int viewType);
- }
-
- /** Wrapper class for pinned section view and its position in the list. */
- static class PinnedSection {
- public View view;
- public int position;
- public long id;
- }
-
- //-- class fields
-
- // fields used for handling touch events
- private final Rect mTouchRect = new Rect();
- private final PointF mTouchPoint = new PointF();
- private int mTouchSlop;
- private View mTouchTarget;
- private MotionEvent mDownEvent;
-
- // fields used for drawing shadow under a pinned section
- private GradientDrawable mShadowDrawable;
- private int mSectionsDistanceY;
- private int mShadowHeight;
-
- /** Delegating listener, can be null. */
- OnScrollListener mDelegateOnScrollListener;
-
- /** Shadow for being recycled, can be null. */
- PinnedSection mRecycleSection;
-
- /** shadow instance with a pinned view, can be null. */
- PinnedSection mPinnedSection;
-
- /** Pinned view Y-translation. We use it to stick pinned view to the next section. */
- int mTranslateY;
-
- /** Scroll listener which does the magic */
- private final OnScrollListener mOnScrollListener = new OnScrollListener() {
-
- @Override public void onScrollStateChanged(AbsListView view, int scrollState) {
- if (mDelegateOnScrollListener != null) { // delegate
- mDelegateOnScrollListener.onScrollStateChanged(view, scrollState);
- }
- }
-
- @Override
- public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
-
- if (mDelegateOnScrollListener != null) { // delegate
- mDelegateOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
- }
-
- // get expected adapter or fail fast
- ListAdapter adapter = getAdapter();
- if (adapter == null || visibleItemCount == 0) return; // nothing to do
-
- final boolean isFirstVisibleItemSection =
- isItemViewTypePinned(adapter, adapter.getItemViewType(firstVisibleItem));
-
- if (isFirstVisibleItemSection) {
- View sectionView = getChildAt(0);
- if (sectionView.getTop() == getPaddingTop()) { // view sticks to the top, no need for pinned shadow
- destroyPinnedShadow();
- } else { // section doesn't stick to the top, make sure we have a pinned shadow
- ensureShadowForPosition(firstVisibleItem, firstVisibleItem, visibleItemCount);
- }
-
- } else { // section is not at the first visible position
- int sectionPosition = findCurrentSectionPosition(firstVisibleItem);
- if (sectionPosition > -1) { // we have section position
- ensureShadowForPosition(sectionPosition, firstVisibleItem, visibleItemCount);
- } else { // there is no section for the first visible item, destroy shadow
- destroyPinnedShadow();
- }
- }
- };
-
- };
-
- /** Default change observer. */
- private final DataSetObserver mDataSetObserver = new DataSetObserver() {
- @Override public void onChanged() {
- recreatePinnedShadow();
- };
- @Override public void onInvalidated() {
- recreatePinnedShadow();
- }
- };
-
- //-- constructors
-
- public PinnedSectionListView(Context context, AttributeSet attrs) {
- super(context, attrs);
- initView();
- }
-
- public PinnedSectionListView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- initView();
- }
-
- private void initView() {
- setOnScrollListener(mOnScrollListener);
- mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
- initShadow(true);
- }
-
- //-- public API methods
-
- public void setShadowVisible(boolean visible) {
- initShadow(visible);
- if (mPinnedSection != null) {
- View v = mPinnedSection.view;
- invalidate(v.getLeft(), v.getTop(), v.getRight(), v.getBottom() + mShadowHeight);
- }
- }
-
- //-- pinned section drawing methods
-
- public void initShadow(boolean visible) {
- if (visible) {
- if (mShadowDrawable == null) {
- mShadowDrawable = new GradientDrawable(Orientation.TOP_BOTTOM,
- new int[] { Color.parseColor("#ffa0a0a0"), Color.parseColor("#50a0a0a0"), Color.parseColor("#00a0a0a0")});
- mShadowHeight = (int) (8 * getResources().getDisplayMetrics().density);
- }
- } else {
- if (mShadowDrawable != null) {
- mShadowDrawable = null;
- mShadowHeight = 0;
- }
- }
- }
-
- /** Create shadow wrapper with a pinned view for a view at given position */
- void createPinnedShadow(int position) {
-
- // try to recycle shadow
- PinnedSection pinnedShadow = mRecycleSection;
- mRecycleSection = null;
-
- // create new shadow, if needed
- if (pinnedShadow == null) pinnedShadow = new PinnedSection();
- // request new view using recycled view, if such
- View pinnedView = getAdapter().getView(position, pinnedShadow.view, PinnedSectionListView.this);
-
- // read layout parameters
- LayoutParams layoutParams = (LayoutParams) pinnedView.getLayoutParams();
- if (layoutParams == null) { // create default layout params
- layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
- }
-
- int heightMode = MeasureSpec.getMode(layoutParams.height);
- int heightSize = MeasureSpec.getSize(layoutParams.height);
-
- if (heightMode == MeasureSpec.UNSPECIFIED) heightMode = MeasureSpec.EXACTLY;
-
- int maxHeight = getHeight() - getListPaddingTop() - getListPaddingBottom();
- if (heightSize > maxHeight) heightSize = maxHeight;
-
- // measure & layout
- int ws = MeasureSpec.makeMeasureSpec(getWidth() - getListPaddingLeft() - getListPaddingRight(), MeasureSpec.EXACTLY);
- int hs = MeasureSpec.makeMeasureSpec(heightSize, heightMode);
- pinnedView.measure(ws, hs);
- pinnedView.layout(0, 0, pinnedView.getMeasuredWidth(), pinnedView.getMeasuredHeight());
- mTranslateY = 0;
-
- // initialize pinned shadow
- pinnedShadow.view = pinnedView;
- pinnedShadow.position = position;
- pinnedShadow.id = getAdapter().getItemId(position);
-
- // store pinned shadow
- mPinnedSection = pinnedShadow;
- }
-
- /** Destroy shadow wrapper for currently pinned view */
- void destroyPinnedShadow() {
- if (mPinnedSection != null) {
- // keep shadow for being recycled later
- mRecycleSection = mPinnedSection;
- mPinnedSection = null;
- }
- }
-
- /** Makes sure we have an actual pinned shadow for given position. */
- void ensureShadowForPosition(int sectionPosition, int firstVisibleItem, int visibleItemCount) {
- if (visibleItemCount < 2) { // no need for creating shadow at all, we have a single visible item
- destroyPinnedShadow();
- return;
- }
-
- if (mPinnedSection != null
- && mPinnedSection.position != sectionPosition) { // invalidate shadow, if required
- destroyPinnedShadow();
- }
-
- if (mPinnedSection == null) { // create shadow, if empty
- createPinnedShadow(sectionPosition);
- }
-
- // align shadow according to next section position, if needed
- int nextPosition = sectionPosition + 1;
- if (nextPosition < getCount()) {
- int nextSectionPosition = findFirstVisibleSectionPosition(nextPosition,
- visibleItemCount - (nextPosition - firstVisibleItem));
- if (nextSectionPosition > -1) {
- View nextSectionView = getChildAt(nextSectionPosition - firstVisibleItem);
- final int bottom = mPinnedSection.view.getBottom() + getPaddingTop();
- mSectionsDistanceY = nextSectionView.getTop() - bottom;
- if (mSectionsDistanceY < 0) {
- // next section overlaps pinned shadow, move it up
- mTranslateY = mSectionsDistanceY;
- } else {
- // next section does not overlap with pinned, stick to top
- mTranslateY = 0;
- }
- } else {
- // no other sections are visible, stick to top
- mTranslateY = 0;
- mSectionsDistanceY = Integer.MAX_VALUE;
- }
- }
-
- }
-
- int findFirstVisibleSectionPosition(int firstVisibleItem, int visibleItemCount) {
- ListAdapter adapter = getAdapter();
- for (int childIndex = 0; childIndex < visibleItemCount; childIndex++) {
- int position = firstVisibleItem + childIndex;
- int viewType = adapter.getItemViewType(position);
- if (isItemViewTypePinned(adapter, viewType)) return position;
- }
- return -1;
- }
-
- int findCurrentSectionPosition(int fromPosition) {
- ListAdapter adapter = getAdapter();
-
- if (adapter instanceof SectionIndexer) {
- // try fast way by asking section indexer
- SectionIndexer indexer = (SectionIndexer) adapter;
- int sectionPosition = indexer.getSectionForPosition(fromPosition);
- int itemPosition = indexer.getPositionForSection(sectionPosition);
- int typeView = adapter.getItemViewType(itemPosition);
- if (isItemViewTypePinned(adapter, typeView)) {
- return itemPosition;
- } // else, no luck
- }
-
- // try slow way by looking through to the next section item above
- for (int position=fromPosition; position>=0; position--) {
- int viewType = adapter.getItemViewType(position);
- if (isItemViewTypePinned(adapter, viewType)) return position;
- }
- return -1; // no candidate found
- }
-
- void recreatePinnedShadow() {
- destroyPinnedShadow();
- ListAdapter adapter = getAdapter();
- if (adapter != null && adapter.getCount() > 0) {
- int firstVisiblePosition = getFirstVisiblePosition();
- int sectionPosition = findCurrentSectionPosition(firstVisiblePosition);
- if (sectionPosition == -1) return; // no views to pin, exit
- ensureShadowForPosition(sectionPosition,
- firstVisiblePosition, getLastVisiblePosition() - firstVisiblePosition);
- }
- }
-
- @Override
- public void setOnScrollListener(OnScrollListener listener) {
- if (listener == mOnScrollListener) {
- super.setOnScrollListener(listener);
- } else {
- mDelegateOnScrollListener = listener;
- }
- }
-
- @Override
- public void onRestoreInstanceState(Parcelable state) {
- super.onRestoreInstanceState(state);
- post(new Runnable() {
- @Override public void run() { // restore pinned view after configuration change
- recreatePinnedShadow();
- }
- });
- }
-
- @Override
- public void setAdapter(ListAdapter adapter) {
-
- // assert adapter in debug mode
- if (BuildConfig.DEBUG && adapter != null) {
- if (!(adapter instanceof PinnedSectionListAdapter))
- throw new IllegalArgumentException("Does your adapter implement PinnedSectionListAdapter?");
- if (adapter.getViewTypeCount() < 2)
- throw new IllegalArgumentException("Does your adapter handle at least two types" +
- " of views in getViewTypeCount() method: items and sections?");
- }
-
- // unregister observer at old adapter and register on new one
- ListAdapter oldAdapter = getAdapter();
- if (oldAdapter != null) oldAdapter.unregisterDataSetObserver(mDataSetObserver);
- if (adapter != null) adapter.registerDataSetObserver(mDataSetObserver);
-
- // destroy pinned shadow, if new adapter is not same as old one
- if (oldAdapter != adapter) destroyPinnedShadow();
-
- super.setAdapter(adapter);
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- super.onLayout(changed, l, t, r, b);
- if (mPinnedSection != null) {
- int parentWidth = r - l - getPaddingLeft() - getPaddingRight();
- int shadowWidth = mPinnedSection.view.getWidth();
- if (parentWidth != shadowWidth) {
- recreatePinnedShadow();
- }
- }
- }
-
- @Override
- protected void dispatchDraw(Canvas canvas) {
- super.dispatchDraw(canvas);
-
- if (mPinnedSection != null) {
-
- // prepare variables
- int pLeft = getListPaddingLeft();
- int pTop = getListPaddingTop();
- View view = mPinnedSection.view;
-
- // draw child
- canvas.save();
-
- int clipHeight = view.getHeight() +
- (mShadowDrawable == null ? 0 : Math.min(mShadowHeight, mSectionsDistanceY));
- canvas.clipRect(pLeft, pTop, pLeft + view.getWidth(), pTop + clipHeight);
-
- canvas.translate(pLeft, pTop + mTranslateY);
- drawChild(canvas, mPinnedSection.view, getDrawingTime());
-
- if (mShadowDrawable != null && mSectionsDistanceY > 0) {
- mShadowDrawable.setBounds(mPinnedSection.view.getLeft(),
- mPinnedSection.view.getBottom(),
- mPinnedSection.view.getRight(),
- mPinnedSection.view.getBottom() + mShadowHeight);
- mShadowDrawable.draw(canvas);
- }
-
- canvas.restore();
- }
- }
-
- //-- touch handling methods
-
- @Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
-
- final float x = ev.getX();
- final float y = ev.getY();
- final int action = ev.getAction();
-
- if (action == MotionEvent.ACTION_DOWN
- && mTouchTarget == null
- && mPinnedSection != null
- && isPinnedViewTouched(mPinnedSection.view, x, y)) { // create touch target
-
- // user touched pinned view
- mTouchTarget = mPinnedSection.view;
- mTouchPoint.x = x;
- mTouchPoint.y = y;
-
- // copy down event for eventually be used later
- mDownEvent = MotionEvent.obtain(ev);
- }
-
- if (mTouchTarget != null) {
- if (isPinnedViewTouched(mTouchTarget, x, y)) { // forward event to pinned view
- mTouchTarget.dispatchTouchEvent(ev);
- }
-
- if (action == MotionEvent.ACTION_UP) { // perform onClick on pinned view
- super.dispatchTouchEvent(ev);
- performPinnedItemClick();
- clearTouchTarget();
-
- } else if (action == MotionEvent.ACTION_CANCEL) { // cancel
- clearTouchTarget();
-
- } else if (action == MotionEvent.ACTION_MOVE) {
- if (Math.abs(y - mTouchPoint.y) > mTouchSlop) {
-
- // cancel sequence on touch target
- MotionEvent event = MotionEvent.obtain(ev);
- event.setAction(MotionEvent.ACTION_CANCEL);
- mTouchTarget.dispatchTouchEvent(event);
- event.recycle();
-
- // provide correct sequence to super class for further handling
- super.dispatchTouchEvent(mDownEvent);
- super.dispatchTouchEvent(ev);
- clearTouchTarget();
-
- }
- }
-
- return true;
- }
-
- // call super if this was not our pinned view
- return super.dispatchTouchEvent(ev);
- }
-
- private boolean isPinnedViewTouched(View view, float x, float y) {
- view.getHitRect(mTouchRect);
-
- // by taping top or bottom padding, the list performs on click on a border item.
- // we don't add top padding here to keep behavior consistent.
- mTouchRect.top += mTranslateY;
-
- mTouchRect.bottom += mTranslateY + getPaddingTop();
- mTouchRect.left += getPaddingLeft();
- mTouchRect.right -= getPaddingRight();
- return mTouchRect.contains((int)x, (int)y);
- }
-
- private void clearTouchTarget() {
- mTouchTarget = null;
- if (mDownEvent != null) {
- mDownEvent.recycle();
- mDownEvent = null;
- }
- }
-
- private boolean performPinnedItemClick() {
- if (mPinnedSection == null) return false;
-
- OnItemClickListener listener = getOnItemClickListener();
- if (listener != null) {
- View view = mPinnedSection.view;
- playSoundEffect(SoundEffectConstants.CLICK);
- if (view != null) {
- view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
- }
- listener.onItemClick(this, view, mPinnedSection.position, mPinnedSection.id);
- return true;
- }
- return false;
- }
-
- public static boolean isItemViewTypePinned(ListAdapter adapter, int viewType) {
- if (adapter instanceof HeaderViewListAdapter) {
- adapter = ((HeaderViewListAdapter)adapter).getWrappedAdapter();
- }
- return ((PinnedSectionListAdapter) adapter).isItemViewTypePinned(viewType);
- }
-
-}
diff --git a/libraries/pinned-section-listview/screen1.png b/libraries/pinned-section-listview/screen1.png
deleted file mode 100644
index 9cca20109..000000000
Binary files a/libraries/pinned-section-listview/screen1.png and /dev/null differ
diff --git a/libraries/pinned-section-listview/screen2.png b/libraries/pinned-section-listview/screen2.png
deleted file mode 100644
index ad59f6f2f..000000000
Binary files a/libraries/pinned-section-listview/screen2.png and /dev/null differ
diff --git a/libraries/pinned-section-listview/screen3.png b/libraries/pinned-section-listview/screen3.png
deleted file mode 100644
index 0fd528fa9..000000000
Binary files a/libraries/pinned-section-listview/screen3.png and /dev/null differ
diff --git a/settings.gradle b/settings.gradle
index 2e582798c..08454c958 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,4 +1,3 @@
include ':OpenPGP-Keychain'
include ':libraries:ActionBarSherlock'
-include ':libraries:HtmlTextView'
-include ':libraries:pinned-section-listview:library'
\ No newline at end of file
+include ':libraries:HtmlTextView'
\ No newline at end of file
--
cgit v1.2.3
From f5da63f9882e1807c6bd2adb5205ad7482c45339 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Thu, 2 Jan 2014 21:10:08 +0100
Subject: New list with sticky list headers library
---
OpenPGP-Keychain/AndroidManifest.xml | 6 +-
OpenPGP-Keychain/build.gradle | 3 +-
OpenPGP-Keychain/project.properties | 1 +
OpenPGP-Keychain/res/drawable/header_selector.xml | 7 +
.../res/layout/key_list_public_activity.xml | 13 +-
.../res/layout/key_list_public_fragment.xml | 14 +
.../res/layout/key_list_secret_activity.xml | 13 +-
OpenPGP-Keychain/res/layout/stickylist_header.xml | 17 +
OpenPGP-Keychain/res/values/colors.xml | 5 +-
OpenPGP-Keychain/res/values/strings.xml | 1 -
.../keychain/ui/KeyListPublicFragment.java | 93 ++-
.../keychain/ui/adapter/KeyListPublicAdapter.java | 80 +-
.../stickylistheaders/views/UnderlineTextView.java | 50 ++
libraries/StickyListHeaders/.gitignore | 98 +++
libraries/StickyListHeaders/LICENSE | 191 +++++
libraries/StickyListHeaders/README.md | 203 +++++
libraries/StickyListHeaders/build.gradle | 24 +
libraries/StickyListHeaders/demo.gif | Bin 0 -> 286171 bytes
.../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 49896 bytes
.../gradle/wrapper/gradle-wrapper.properties | 6 +
libraries/StickyListHeaders/gradlew | 164 ++++
libraries/StickyListHeaders/gradlew.bat | 90 ++
libraries/StickyListHeaders/library/.gitignore | 2 +
.../StickyListHeaders/library/AndroidManifest.xml | 11 +
libraries/StickyListHeaders/library/build.gradle | 16 +
libraries/StickyListHeaders/library/build.xml | 92 ++
.../StickyListHeaders/library/proguard-project.txt | 20 +
.../StickyListHeaders/library/project.properties | 15 +
.../StickyListHeaders/library/res/values/attrs.xml | 32 +
.../stickylistheaders/AdapterWrapper.java | 225 +++++
.../stickylistheaders/ApiLevelTooLowException.java | 11 +
.../stickylistheaders/CheckableWrapperView.java | 31 +
.../SectionIndexerAdapterWrapper.java | 32 +
.../StickyListHeadersAdapter.java | 38 +
.../StickyListHeadersListView.java | 925 +++++++++++++++++++++
.../stickylistheaders/WrapperView.java | 150 ++++
.../stickylistheaders/WrapperViewList.java | 175 ++++
.../StickyListHeaders/sample/AndroidManifest.xml | 27 +
libraries/StickyListHeaders/sample/build.gradle | 23 +
.../sample/libs/android-support-v4.jar | Bin 0 -> 556198 bytes
.../StickyListHeaders/sample/project.properties | 15 +
.../sample/res/drawable-hdpi/ic_drawer.png | Bin 0 -> 2826 bytes
.../sample/res/drawable-hdpi/ic_launcher.png | Bin 0 -> 9397 bytes
.../sample/res/drawable-ldpi/ic_launcher.png | Bin 0 -> 2729 bytes
.../sample/res/drawable-mdpi/ic_drawer.png | Bin 0 -> 2816 bytes
.../sample/res/drawable-mdpi/ic_launcher.png | Bin 0 -> 5237 bytes
.../sample/res/drawable-xhdpi/ic_drawer.png | Bin 0 -> 1038 bytes
.../sample/res/drawable-xhdpi/ic_launcher.png | Bin 0 -> 14383 bytes
.../sample/res/drawable/header_selector.xml | 7 +
.../StickyListHeaders/sample/res/layout/header.xml | 17 +
.../sample/res/layout/list_footer.xml | 13 +
.../sample/res/layout/list_header.xml | 13 +
.../StickyListHeaders/sample/res/layout/main.xml | 114 +++
.../sample/res/layout/test_list_item_layout.xml | 8 +
.../StickyListHeaders/sample/res/values/arrays.xml | 230 +++++
.../StickyListHeaders/sample/res/values/colors.xml | 7 +
.../sample/res/values/strings.xml | 20 +
.../StickyListHeaders/sample/res/values/style.xml | 9 +
.../stickylistheaders/TestActivity.java | 169 ++++
.../stickylistheaders/TestBaseAdapter.java | 169 ++++
.../stickylistheaders/views/UnderlineTextView.java | 50 ++
libraries/StickyListHeaders/settings.gradle | 2 +
settings.gradle | 3 +-
63 files changed, 3688 insertions(+), 62 deletions(-)
create mode 100644 OpenPGP-Keychain/res/drawable/header_selector.xml
create mode 100644 OpenPGP-Keychain/res/layout/key_list_public_fragment.xml
create mode 100644 OpenPGP-Keychain/res/layout/stickylist_header.xml
create mode 100644 OpenPGP-Keychain/src/se/emilsjolander/stickylistheaders/views/UnderlineTextView.java
create mode 100644 libraries/StickyListHeaders/.gitignore
create mode 100644 libraries/StickyListHeaders/LICENSE
create mode 100644 libraries/StickyListHeaders/README.md
create mode 100644 libraries/StickyListHeaders/build.gradle
create mode 100644 libraries/StickyListHeaders/demo.gif
create mode 100644 libraries/StickyListHeaders/gradle/wrapper/gradle-wrapper.jar
create mode 100644 libraries/StickyListHeaders/gradle/wrapper/gradle-wrapper.properties
create mode 100644 libraries/StickyListHeaders/gradlew
create mode 100644 libraries/StickyListHeaders/gradlew.bat
create mode 100644 libraries/StickyListHeaders/library/.gitignore
create mode 100644 libraries/StickyListHeaders/library/AndroidManifest.xml
create mode 100644 libraries/StickyListHeaders/library/build.gradle
create mode 100644 libraries/StickyListHeaders/library/build.xml
create mode 100644 libraries/StickyListHeaders/library/proguard-project.txt
create mode 100644 libraries/StickyListHeaders/library/project.properties
create mode 100644 libraries/StickyListHeaders/library/res/values/attrs.xml
create mode 100644 libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/AdapterWrapper.java
create mode 100644 libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/ApiLevelTooLowException.java
create mode 100644 libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/CheckableWrapperView.java
create mode 100644 libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/SectionIndexerAdapterWrapper.java
create mode 100644 libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/StickyListHeadersAdapter.java
create mode 100644 libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/StickyListHeadersListView.java
create mode 100644 libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/WrapperView.java
create mode 100644 libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/WrapperViewList.java
create mode 100644 libraries/StickyListHeaders/sample/AndroidManifest.xml
create mode 100644 libraries/StickyListHeaders/sample/build.gradle
create mode 100644 libraries/StickyListHeaders/sample/libs/android-support-v4.jar
create mode 100644 libraries/StickyListHeaders/sample/project.properties
create mode 100644 libraries/StickyListHeaders/sample/res/drawable-hdpi/ic_drawer.png
create mode 100644 libraries/StickyListHeaders/sample/res/drawable-hdpi/ic_launcher.png
create mode 100644 libraries/StickyListHeaders/sample/res/drawable-ldpi/ic_launcher.png
create mode 100644 libraries/StickyListHeaders/sample/res/drawable-mdpi/ic_drawer.png
create mode 100644 libraries/StickyListHeaders/sample/res/drawable-mdpi/ic_launcher.png
create mode 100644 libraries/StickyListHeaders/sample/res/drawable-xhdpi/ic_drawer.png
create mode 100644 libraries/StickyListHeaders/sample/res/drawable-xhdpi/ic_launcher.png
create mode 100644 libraries/StickyListHeaders/sample/res/drawable/header_selector.xml
create mode 100644 libraries/StickyListHeaders/sample/res/layout/header.xml
create mode 100644 libraries/StickyListHeaders/sample/res/layout/list_footer.xml
create mode 100644 libraries/StickyListHeaders/sample/res/layout/list_header.xml
create mode 100644 libraries/StickyListHeaders/sample/res/layout/main.xml
create mode 100644 libraries/StickyListHeaders/sample/res/layout/test_list_item_layout.xml
create mode 100755 libraries/StickyListHeaders/sample/res/values/arrays.xml
create mode 100644 libraries/StickyListHeaders/sample/res/values/colors.xml
create mode 100644 libraries/StickyListHeaders/sample/res/values/strings.xml
create mode 100644 libraries/StickyListHeaders/sample/res/values/style.xml
create mode 100644 libraries/StickyListHeaders/sample/src/se/emilsjolander/stickylistheaders/TestActivity.java
create mode 100644 libraries/StickyListHeaders/sample/src/se/emilsjolander/stickylistheaders/TestBaseAdapter.java
create mode 100644 libraries/StickyListHeaders/sample/src/se/emilsjolander/stickylistheaders/views/UnderlineTextView.java
create mode 100644 libraries/StickyListHeaders/settings.gradle
diff --git a/OpenPGP-Keychain/AndroidManifest.xml b/OpenPGP-Keychain/AndroidManifest.xml
index be9fe222c..8433323f4 100644
--- a/OpenPGP-Keychain/AndroidManifest.xml
+++ b/OpenPGP-Keychain/AndroidManifest.xml
@@ -88,8 +88,7 @@
android:name=".ui.KeyListPublicActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_manage_public_keys"
- android:launchMode="singleTop"
- android:uiOptions="splitActionBarWhenNarrow" >
+ android:launchMode="singleTop" >
@@ -104,8 +103,7 @@
android:name=".ui.KeyListSecretActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_manage_secret_keys"
- android:launchMode="singleTop"
- android:uiOptions="splitActionBarWhenNarrow" >
+ android:launchMode="singleTop" >
diff --git a/OpenPGP-Keychain/build.gradle b/OpenPGP-Keychain/build.gradle
index 80c0f05cd..f31b6908c 100644
--- a/OpenPGP-Keychain/build.gradle
+++ b/OpenPGP-Keychain/build.gradle
@@ -4,6 +4,7 @@ buildscript {
}
dependencies {
+ // NOTE: Avoid using dynamic versions (+). This breaks offline builds!
classpath 'com.android.tools.build:gradle:0.6.3'
}
}
@@ -22,7 +23,7 @@ dependencies {
compile 'com.android.support:support-v4:18.0.+' // already in actionbarsherlock
compile project(':libraries:ActionBarSherlock')
compile project(':libraries:HtmlTextView')
- compile project(':libraries:pinned-section-listview:library')
+ compile project(':libraries:StickyListHeaders:library')
}
android {
diff --git a/OpenPGP-Keychain/project.properties b/OpenPGP-Keychain/project.properties
index 7acfa6b58..8e240f3c3 100644
--- a/OpenPGP-Keychain/project.properties
+++ b/OpenPGP-Keychain/project.properties
@@ -11,3 +11,4 @@
target=android-19
android.library.reference.1=../libraries/ActionBarSherlock
android.library.reference.2=../libraries/HtmlTextView
+android.library.reference.3=../libraries/StickyListHeaders/library
diff --git a/OpenPGP-Keychain/res/drawable/header_selector.xml b/OpenPGP-Keychain/res/drawable/header_selector.xml
new file mode 100644
index 000000000..5dfb8265c
--- /dev/null
+++ b/OpenPGP-Keychain/res/drawable/header_selector.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/key_list_public_activity.xml b/OpenPGP-Keychain/res/layout/key_list_public_activity.xml
index a35e23038..07ec253cc 100644
--- a/OpenPGP-Keychain/res/layout/key_list_public_activity.xml
+++ b/OpenPGP-Keychain/res/layout/key_list_public_activity.xml
@@ -8,18 +8,7 @@
android:id="@+id/key_list_public_fragment"
android:name="org.sufficientlysecure.keychain.ui.KeyListPublicFragment"
android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_height="0dip"
android:layout_weight="1" />
-
-
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/key_list_public_fragment.xml b/OpenPGP-Keychain/res/layout/key_list_public_fragment.xml
new file mode 100644
index 000000000..052dd4249
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/key_list_public_fragment.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/key_list_secret_activity.xml b/OpenPGP-Keychain/res/layout/key_list_secret_activity.xml
index d4397c444..b8df9faa7 100644
--- a/OpenPGP-Keychain/res/layout/key_list_secret_activity.xml
+++ b/OpenPGP-Keychain/res/layout/key_list_secret_activity.xml
@@ -8,18 +8,7 @@
android:id="@+id/key_list_secret_fragment"
android:name="org.sufficientlysecure.keychain.ui.KeyListSecretFragment"
android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_height="0dip"
android:layout_weight="1" />
-
-
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/stickylist_header.xml b/OpenPGP-Keychain/res/layout/stickylist_header.xml
new file mode 100644
index 000000000..475d1c4db
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/stickylist_header.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/values/colors.xml b/OpenPGP-Keychain/res/values/colors.xml
index d1dc6c1f4..7831a63a0 100644
--- a/OpenPGP-Keychain/res/values/colors.xml
+++ b/OpenPGP-Keychain/res/values/colors.xml
@@ -3,5 +3,8 @@
#31b6e7#cecbce
-
+ #ffe74c3c
+ #ffc0392b
+ #FFDDDDDD
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/values/strings.xml b/OpenPGP-Keychain/res/values/strings.xml
index c3736815c..d900f5a71 100644
--- a/OpenPGP-Keychain/res/values/strings.xml
+++ b/OpenPGP-Keychain/res/values/strings.xml
@@ -210,7 +210,6 @@
Unknown key %s, do you want to try finding it on a keyserver?Successfully sent key to serverSuccessfully signed key
- Long press one entry in this list to show more options!This list is empty!Successfully sent key with NFC Beam!
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
index 3dafc84ca..8167ff439 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
@@ -24,6 +24,9 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.ui.adapter.KeyListPublicAdapter;
+import se.emilsjolander.stickylistheaders.ApiLevelTooLowException;
+import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
+import android.annotation.SuppressLint;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
@@ -31,58 +34,84 @@ import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
+import android.view.LayoutInflater;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
-import com.actionbarsherlock.app.SherlockListFragment;
-
-public class KeyListPublicFragment extends SherlockListFragment implements
- LoaderManager.LoaderCallbacks {
+import com.actionbarsherlock.app.SherlockFragment;
+
+/**
+ * Public key list with sticky list headers.
+ *
+ * - uses StickyListHeaders library
+ * - custom adapter: KeyListPublicAdapter
+ *
+ * TODO:
+ * - fix loader with spinning animation
+ * - fix design
+ * - fix view holder in adapter
+ *
+ */
+public class KeyListPublicFragment extends SherlockFragment implements
+ AdapterView.OnItemClickListener, LoaderManager.LoaderCallbacks {
private KeyListPublicActivity mKeyListPublicActivity;
private KeyListPublicAdapter mAdapter;
+ StickyListHeadersListView stickyList;
+
/**
* Define Adapter and Loader on create of Activity
*/
+ @SuppressLint("NewApi")
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mKeyListPublicActivity = (KeyListPublicActivity) getActivity();
- getListView().setOnItemClickListener(new OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView> adapterView, View view, int position, long id) {
- // start key view on click
- Intent detailsIntent = new Intent(mKeyListPublicActivity, KeyViewActivity.class);
- detailsIntent.setData(KeychainContract.KeyRings.buildPublicKeyRingsUri(Long
- .toString(id)));
- startActivity(detailsIntent);
- }
- });
+ stickyList = (StickyListHeadersListView) getActivity().findViewById(
+ R.id.key_list_public_fragment_stickylist);
+
+ stickyList.setOnItemClickListener(this);
+ // stickyList.setOnHeaderClickListener(this);
+ // stickyList.setOnStickyHeaderOffsetChangedListener(this);
+ // mStickyList.addHeaderView(inflater.inflate(R.layout.list_header, null));
+ // mStickyList.addFooterView(inflater.inflate(R.layout.list_footer, null));
+ // stickyList.setEmptyView(findViewById(R.id.empty));
+ stickyList.setAreHeadersSticky(true);
+ stickyList.setDrawingListUnderStickyHeader(true);
+ stickyList.setFastScrollEnabled(true);
+ try {
+ stickyList.setFastScrollAlwaysVisible(true);
+ } catch (ApiLevelTooLowException e) {
+ }
// Give some text to display if there is no data. In a real
// application this would come from a resource.
- setEmptyText(getString(R.string.list_empty));
-
- // We have a menu item to show in action bar.
- setHasOptionsMenu(true);
+ // setEmptyText(getString(R.string.list_empty));
// Start out with a progress indicator.
- setListShown(false);
+ // setListShown(false);
// Create an empty adapter we will use to display the loaded data.
- mAdapter = new KeyListPublicAdapter(mKeyListPublicActivity, null, Id.type.public_key);
- setListAdapter(mAdapter);
+ // mAdapter = new KeyListPublicAdapter(mKeyListPublicActivity, null, Id.type.public_key);
+ // setListAdapter(mAdapter);
+ // stickyList.setAdapter(mAdapter);
// Prepare the loader. Either re-connect with an existing one,
// or start a new one.
getLoaderManager().initLoader(0, null, this);
}
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.key_list_public_fragment, container, false);
+ return view;
+ }
+
// These are the rows that we will retrieve.
static final String[] PROJECTION = new String[] { KeyRings._ID, KeyRings.MASTER_KEY_ID,
UserIds.USER_ID };
@@ -104,13 +133,19 @@ public class KeyListPublicFragment extends SherlockListFragment implements
public void onLoadFinished(Loader loader, Cursor data) {
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
- mAdapter.swapCursor(data);
+ // mAdapter.swapCursor(data);
+ int userIdIndex = data.getColumnIndex(UserIds.USER_ID);
+
+ mAdapter = new KeyListPublicAdapter(mKeyListPublicActivity, data, Id.type.public_key,
+ userIdIndex);
+
+ stickyList.setAdapter(mAdapter);
// The list should now be shown.
if (isResumed()) {
- setListShown(true);
+ // setListShown(true);
} else {
- setListShownNoAnimation(true);
+ // setListShownNoAnimation(true);
}
}
@@ -122,4 +157,12 @@ public class KeyListPublicFragment extends SherlockListFragment implements
mAdapter.swapCursor(null);
}
+ @Override
+ public void onItemClick(AdapterView> adapterView, View view, int position, long id) {
+ // start key view on click
+ Intent detailsIntent = new Intent(mKeyListPublicActivity, KeyViewActivity.class);
+ detailsIntent.setData(KeychainContract.KeyRings.buildPublicKeyRingsUri(Long.toString(id)));
+ startActivity(detailsIntent);
+ }
+
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java
index d72c9d42a..86a47d4d7 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java
@@ -17,30 +17,40 @@
package org.sufficientlysecure.keychain.ui.adapter;
+import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.OtherHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
-import org.sufficientlysecure.keychain.util.SectionCursorAdapter;
+import org.sufficientlysecure.keychain.util.Log;
+import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
import android.content.Context;
import android.database.Cursor;
+import android.support.v4.widget.CursorAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
-public class KeyListPublicAdapter extends SectionCursorAdapter {
-
+/**
+ * - implements StickyListHeadersAdapter from library - uses view holder pattern for performance
+ *
+ */
+public class KeyListPublicAdapter extends CursorAdapter implements StickyListHeadersAdapter {
private LayoutInflater mInflater;
- public KeyListPublicAdapter(Context context, Cursor c, int flags) {
- super(context, c, android.R.layout.preference_category, 2); // TODO: 2 is user id
+ int mSectionColumnIndex;
+
+ public KeyListPublicAdapter(Context context, Cursor c, int flags, int sectionColumnIndex) {
+ super(context, c, flags);
mInflater = LayoutInflater.from(context);
+ mSectionColumnIndex = sectionColumnIndex;
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
+ // TODO: view holder pattern?
int userIdIndex = cursor.getColumnIndex(UserIds.USER_ID);
TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
@@ -74,4 +84,64 @@ public class KeyListPublicAdapter extends SectionCursorAdapter {
return mInflater.inflate(R.layout.key_list_group_item, null);
}
+ @Override
+ public View getHeaderView(int position, View convertView, ViewGroup parent) {
+
+ HeaderViewHolder holder;
+ if (convertView == null) {
+ holder = new HeaderViewHolder();
+ convertView = mInflater.inflate(R.layout.stickylist_header, parent, false);
+ holder.text = (TextView) convertView.findViewById(R.id.stickylist_header_text);
+ convertView.setTag(holder);
+ } else {
+ holder = (HeaderViewHolder) convertView.getTag();
+ }
+
+ if (!mDataValid) {
+ // no data available at this point
+ Log.d(Constants.TAG, "getHeaderView: No data available at this point!");
+ return convertView;
+ }
+
+ // similar to getView in CursorAdapter
+ if (!mCursor.moveToPosition(position)) {
+ throw new IllegalStateException("couldn't move cursor to position " + position);
+ }
+
+ // set header text as first char in name
+ String headerText = "" + mCursor.getString(mSectionColumnIndex).subSequence(0, 1).charAt(0);
+ holder.text.setText(headerText);
+ return convertView;
+ }
+
+ /**
+ * Remember that these have to be static, position=1 should always return the same Id that is.
+ */
+ @Override
+ public long getHeaderId(int position) {
+ if (!mDataValid) {
+ // no data available at this point
+ Log.d(Constants.TAG, "getHeaderView: No data available at this point!");
+ return -1;
+ }
+
+ // similar to getView in CursorAdapter
+ if (!mCursor.moveToPosition(position)) {
+ throw new IllegalStateException("couldn't move cursor to position " + position);
+ }
+
+ // return the first character of the name as ID because this is what
+ // headers are based upon
+ return mCursor.getString(mSectionColumnIndex).subSequence(0, 1).charAt(0);
+ }
+
+ class HeaderViewHolder {
+ TextView text;
+ }
+
+ class ViewHolder {
+ TextView mainUserId;
+ TextView mainUserIdRest;
+ }
+
}
diff --git a/OpenPGP-Keychain/src/se/emilsjolander/stickylistheaders/views/UnderlineTextView.java b/OpenPGP-Keychain/src/se/emilsjolander/stickylistheaders/views/UnderlineTextView.java
new file mode 100644
index 000000000..c202c00b8
--- /dev/null
+++ b/OpenPGP-Keychain/src/se/emilsjolander/stickylistheaders/views/UnderlineTextView.java
@@ -0,0 +1,50 @@
+package se.emilsjolander.stickylistheaders.views;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.widget.TextView;
+
+/**
+ * @author Eric Frohnhoefer
+ */
+public class UnderlineTextView extends TextView {
+ private final Paint mPaint = new Paint();
+ private int mUnderlineHeight = 0;
+
+ public UnderlineTextView(Context context) {
+ this(context, null);
+ }
+
+ public UnderlineTextView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public UnderlineTextView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ init(context, attrs);
+ }
+
+ private void init(Context context, AttributeSet attrs) {
+ Resources r = getResources();
+ mUnderlineHeight = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, r.getDisplayMetrics());
+ }
+
+ @Override
+ public void setPadding(int left, int top, int right, int bottom) {
+ super.setPadding(left, top, right, bottom + mUnderlineHeight);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ // Draw the underline the same color as the text
+ mPaint.setColor(getTextColors().getDefaultColor());
+ canvas.drawRect(0, getHeight() - mUnderlineHeight, getWidth(), getHeight(), mPaint);
+ }
+}
diff --git a/libraries/StickyListHeaders/.gitignore b/libraries/StickyListHeaders/.gitignore
new file mode 100644
index 000000000..71d84cca6
--- /dev/null
+++ b/libraries/StickyListHeaders/.gitignore
@@ -0,0 +1,98 @@
+###Android###
+
+# built application files
+*.apk
+*.ap_
+
+# files for the dex VM
+*.dex
+
+# Java class files
+*.class
+
+# generated files
+bin/
+gen/
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+# Eclipse project files
+.classpath
+.project
+
+
+###Eclipse###
+
+*.pydevproject
+.project
+.metadata
+bin/**
+tmp/**
+tmp/**/*
+*.tmp
+*.bak
+*.swp
+*~.nib
+local.properties
+.classpath
+.settings/
+.loadpath
+
+# External tool builders
+.externalToolBuilders/
+
+# Locally stored "Eclipse launch configurations"
+*.launch
+
+# CDT-specific
+.cproject
+
+# PDT-specific
+.buildpath
+
+
+###Maven###
+
+target/
+
+
+###Gradle###
+
+.gradle/
+build/
+
+
+###IntelliJ###
+
+*.iml
+*.ipr
+*.iws
+.idea/
+
+
+###Actionscript###
+
+# Build and Release Folders
+bin/
+bin-debug/
+bin-release/
+
+# Project property files
+.actionScriptProperties
+.flexProperties
+.settings/
+.project
+
+###OSX###
+
+.DS_Store
+
+# Thumbnails
+._*
+
+# Files that might appear on external disk
+.Spotlight-V100
+.Trashes
+
+
diff --git a/libraries/StickyListHeaders/LICENSE b/libraries/StickyListHeaders/LICENSE
new file mode 100644
index 000000000..37ec93a14
--- /dev/null
+++ b/libraries/StickyListHeaders/LICENSE
@@ -0,0 +1,191 @@
+Apache License
+Version 2.0, January 2004
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and
+distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright
+owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities
+that control, are controlled by, or are under common control with that entity.
+For the purposes of this definition, "control" means (i) the power, direct or
+indirect, to cause the direction or management of such entity, whether by
+contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
+outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising
+permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including
+but not limited to software source code, documentation source, and configuration
+files.
+
+"Object" form shall mean any form resulting from mechanical transformation or
+translation of a Source form, including but not limited to compiled object code,
+generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made
+available under the License, as indicated by a copyright notice that is included
+in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that
+is based on (or derived from) the Work and for which the editorial revisions,
+annotations, elaborations, or other modifications represent, as a whole, an
+original work of authorship. For the purposes of this License, Derivative Works
+shall not include works that remain separable from, or merely link (or bind by
+name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version
+of the Work and any modifications or additions to that Work or Derivative Works
+thereof, that is intentionally submitted to Licensor for inclusion in the Work
+by the copyright owner or by an individual or Legal Entity authorized to submit
+on behalf of the copyright owner. For the purposes of this definition,
+"submitted" means any form of electronic, verbal, or written communication sent
+to the Licensor or its representatives, including but not limited to
+communication on electronic mailing lists, source code control systems, and
+issue tracking systems that are managed by, or on behalf of, the Licensor for
+the purpose of discussing and improving the Work, but excluding communication
+that is conspicuously marked or otherwise designated in writing by the copyright
+owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
+of whom a Contribution has been received by Licensor and subsequently
+incorporated within the Work.
+
+2. Grant of Copyright License.
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable copyright license to reproduce, prepare Derivative Works of,
+publicly display, publicly perform, sublicense, and distribute the Work and such
+Derivative Works in Source or Object form.
+
+3. Grant of Patent License.
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable (except as stated in this section) patent license to make, have
+made, use, offer to sell, sell, import, and otherwise transfer the Work, where
+such license applies only to those patent claims licensable by such Contributor
+that are necessarily infringed by their Contribution(s) alone or by combination
+of their Contribution(s) with the Work to which such Contribution(s) was
+submitted. If You institute patent litigation against any entity (including a
+cross-claim or counterclaim in a lawsuit) alleging that the Work or a
+Contribution incorporated within the Work constitutes direct or contributory
+patent infringement, then any patent licenses granted to You under this License
+for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution.
+
+You may reproduce and distribute copies of the Work or Derivative Works thereof
+in any medium, with or without modifications, and in Source or Object form,
+provided that You meet the following conditions:
+
+You must give any other recipients of the Work or Derivative Works a copy of
+this License; and
+You must cause any modified files to carry prominent notices stating that You
+changed the files; and
+You must retain, in the Source form of any Derivative Works that You distribute,
+all copyright, patent, trademark, and attribution notices from the Source form
+of the Work, excluding those notices that do not pertain to any part of the
+Derivative Works; and
+If the Work includes a "NOTICE" text file as part of its distribution, then any
+Derivative Works that You distribute must include a readable copy of the
+attribution notices contained within such NOTICE file, excluding those notices
+that do not pertain to any part of the Derivative Works, in at least one of the
+following places: within a NOTICE text file distributed as part of the
+Derivative Works; within the Source form or documentation, if provided along
+with the Derivative Works; or, within a display generated by the Derivative
+Works, if and wherever such third-party notices normally appear. The contents of
+the NOTICE file are for informational purposes only and do not modify the
+License. You may add Your own attribution notices within Derivative Works that
+You distribute, alongside or as an addendum to the NOTICE text from the Work,
+provided that such additional attribution notices cannot be construed as
+modifying the License.
+You may add Your own copyright statement to Your modifications and may provide
+additional or different license terms and conditions for use, reproduction, or
+distribution of Your modifications, or for any such Derivative Works as a whole,
+provided Your use, reproduction, and distribution of the Work otherwise complies
+with the conditions stated in this License.
+
+5. Submission of Contributions.
+
+Unless You explicitly state otherwise, any Contribution intentionally submitted
+for inclusion in the Work by You to the Licensor shall be under the terms and
+conditions of this License, without any additional terms or conditions.
+Notwithstanding the above, nothing herein shall supersede or modify the terms of
+any separate license agreement you may have executed with Licensor regarding
+such Contributions.
+
+6. Trademarks.
+
+This License does not grant permission to use the trade names, trademarks,
+service marks, or product names of the Licensor, except as required for
+reasonable and customary use in describing the origin of the Work and
+reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty.
+
+Unless required by applicable law or agreed to in writing, Licensor provides the
+Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
+including, without limitation, any warranties or conditions of TITLE,
+NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
+solely responsible for determining the appropriateness of using or
+redistributing the Work and assume any risks associated with Your exercise of
+permissions under this License.
+
+8. Limitation of Liability.
+
+In no event and under no legal theory, whether in tort (including negligence),
+contract, or otherwise, unless required by applicable law (such as deliberate
+and grossly negligent acts) or agreed to in writing, shall any Contributor be
+liable to You for damages, including any direct, indirect, special, incidental,
+or consequential damages of any character arising as a result of this License or
+out of the use or inability to use the Work (including but not limited to
+damages for loss of goodwill, work stoppage, computer failure or malfunction, or
+any and all other commercial damages or losses), even if such Contributor has
+been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability.
+
+While redistributing the Work or Derivative Works thereof, You may choose to
+offer, and charge a fee for, acceptance of support, warranty, indemnity, or
+other liability obligations and/or rights consistent with this License. However,
+in accepting such obligations, You may act only on Your own behalf and on Your
+sole responsibility, not on behalf of any other Contributor, and only if You
+agree to indemnify, defend, and hold each Contributor harmless for any liability
+incurred by, or claims asserted against, such Contributor by reason of your
+accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work
+
+To apply the Apache License to your work, attach the following boilerplate
+notice, with the fields enclosed by brackets "[]" replaced with your own
+identifying information. (Don't include the brackets!) The text should be
+enclosed in the appropriate comment syntax for the file format. We also
+recommend that a file or class name and description of purpose be included on
+the same "printed page" as the copyright notice for easier identification within
+third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
diff --git a/libraries/StickyListHeaders/README.md b/libraries/StickyListHeaders/README.md
new file mode 100644
index 000000000..2cfff2991
--- /dev/null
+++ b/libraries/StickyListHeaders/README.md
@@ -0,0 +1,203 @@
+StickyListHeaders
+=================
+StickyListHeaders is an Android library that makes it easy to integrate section headers in your `ListView`. These section headers stick to the top like in the new People app of Android 4.0 Ice Cream Sandwich. This behavior is also found in lists with sections on iOS devices. This library can also be used without the sticky functionality if you just want section headers.
+
+StickyListHeaders actively supports android versions 2.3 (gingerbread) and above.
+That said, it works all the way down to 2.1 but is not actively tested or working perfectly.
+
+Here is a short gif showing the functionality you get with this library:
+
+
+
+
+Goal
+----
+The goal of this project is to deliver a high performance replacement to `ListView`. You should with minimal effort and time be able to add section headers to a list. This should be done via a simple to use API without any special features. This library will always priorities general use cases over special ones. This means that the library will add very few public methods to the standard `ListView` and will not try to work for every use case. While I will want to support even narrow use cases I will not do so if it compromises the API or any other feature.
+
+
+Installing
+---------------
+###Gradle
+Add the following gradle dependency exchanging `x.x.x` for the latest release.
+```groovy
+dependencies {
+ compile 'se.emilsjolander:stickylistheaders:x.x.x'
+}
+```
+
+###Cloning
+First of all you will have to clone the library.
+```shell
+git clone https://github.com/emilsjolander/StickyListHeaders.git
+```
+
+Now that you have the library you will have to import it into Android Studio.
+In Android Studio navigate the menus like this.
+```
+File -> Import Project ...
+```
+In the following dialog navigate to StickyListHeaders which you cloned to your computer in the previous steps and select the `build.gradle`.
+
+Getting Started
+---------------
+Ok lets start with your activities or fragments xml file. It might look something like this.
+```xml
+
+```
+
+Now in your activities `onCreate()` or your fragments `onCreateView()` you would want to do something like this
+```java
+StickyListHeadersListView stickyList = (StickyListHeadersListView) findViewById(R.id.list);
+MyAdapter adapter = new MyAdapter(this);
+stickyList.setAdapter(adapter);
+```
+
+`MyAdapter` in the above example would look something like this if your list was a list of countries where each header was for a letter in the alphabet.
+```java
+public class MyAdapter extends BaseAdapter implements StickyListHeadersAdapter {
+
+ private String[] countries;
+ private LayoutInflater inflater;
+
+ public TestBaseAdapter(Context context) {
+ inflater = LayoutInflater.from(context);
+ countries = context.getResources().getStringArray(R.array.countries);
+ }
+
+ @Override
+ public int getCount() {
+ return countries.length;
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return countries[position];
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ ViewHolder holder;
+
+ if (convertView == null) {
+ holder = new ViewHolder();
+ convertView = inflater.inflate(R.layout.test_list_item_layout, parent, false);
+ holder.text = (TextView) convertView.findViewById(R.id.text);
+ convertView.setTag(holder);
+ } else {
+ holder = (ViewHolder) convertView.getTag();
+ }
+
+ holder.text.setText(countries[position]);
+
+ return convertView;
+ }
+
+ @Override
+ public View getHeaderView(int position, View convertView, ViewGroup parent) {
+ HeaderViewHolder holder;
+ if (convertView == null) {
+ holder = new HeaderViewHolder();
+ convertView = inflater.inflate(R.layout.header, parent, false);
+ holder.text = (TextView) convertView.findViewById(R.id.text);
+ convertView.setTag(holder);
+ } else {
+ holder = (HeaderViewHolder) convertView.getTag();
+ }
+ //set header text as first char in name
+ String headerText = "" + countries[position].subSequence(0, 1).charAt(0);
+ holder.text.setText(headerText);
+ return convertView;
+ }
+
+ @Override
+ public long getHeaderId(int position) {
+ //return the first character of the country as ID because this is what headers are based upon
+ return countries[position].subSequence(0, 1).charAt(0);
+ }
+
+ class HeaderViewHolder {
+ TextView text;
+ }
+
+ class ViewHolder {
+ TextView text;
+ }
+
+}
+```
+
+That's it! Look through the API docs below to get know about things to customize and if you have any problems getting started please open an issue as it probably means the getting started guide need some improvement!
+
+Upgrading from 1.x versions
+---------------------------
+First of all the package name has changed from `com.emilsjolander.components.stickylistheaders` -> `se.emilsjolander.stickylistheaders` so update all your imports and xml files using StickyListHeaders!
+
+If you are Upgrading from a version prior to 2.x you might run into the following problems.
+1. `StickyListHeadersListView` is no longer a `ListView` subclass. This means that it cannot be passed into a method expecting a ListView. You can retrieve an instance of the `ListView` via `getWrappedList()` but use this with caution as things will probably break if you start setting things directly on that list.
+2. Because `StickyListHeadersListView` is no longer a `ListView` it does not support all the methods. I have implemented delegate methods for all the usual methods and gladly accept pull requests for more.
+
+API
+---
+###StickyListHeadersAdapter
+```java
+public interface StickyListHeadersAdapter extends ListAdapter {
+ View getHeaderView(int position, View convertView, ViewGroup parent);
+ long getHeaderId(int position);
+}
+```
+Your adapter must implement this interface to function with `StickyListHeadersListView`.
+`getHeaderId()` must return a unique integer for every section. A valid implementation for a list with alphabetical sections is the return the char value of the section that `position` is a part of.
+
+`getHeaderView()` works exactly like `getView()` in a regular `ListAdapter`.
+
+
+###StickyListHeadersListView
+Headers are sticky by default but that can easily be changed with this setter. There is of course also a matching getter for the sticky property.
+```java
+public void setAreHeadersSticky(boolean areHeadersSticky);
+public boolean areHeadersSticky();
+```
+
+A `OnHeaderClickListener` is the header version of OnItemClickListener. This is the setter for it and the interface of the listener. The currentlySticky boolean flag indicated if the header that was clicked was sticking to the top at the time it was clicked.
+```java
+public void setOnHeaderClickListener(OnHeaderClickListener listener);
+
+public interface OnHeaderClickListener {
+ public void onHeaderClick(StickyListHeadersListView l, View header, int itemPosition, long headerId, boolean currentlySticky);
+}
+```
+
+A `OnStickyHeaderOffsetChangedListener` is a Listener used for listening to when the sticky header slides out of the screen. The offset parameter will slowly grow to be the same size as the headers height. Use the listeners callback to transform the header in any way you see fit, the standard android contacts app dims the text for example.
+```java
+public void setOnStickyHeaderOffsetChangedListener(OnStickyHeaderOffsetChangedListener listener);
+
+public interface OnStickyHeaderOffsetChangedListener {
+ public void onStickyHeaderOffsetChanged(StickyListHeadersListView l, View header, int offset);
+}
+```
+
+Here are two methods added to the API for inspecting the children of the underlying `ListView`. I could not override the normal `getChildAt()` and `getChildCount()` methods as that would mess up the underlying measurement system of the `FrameLayout` wrapping the `ListView`.
+```java
+public View getListChildAt(int index);
+public int getListChildCount();
+```
+
+This is a setter and getter for an internal attribute that controls if the list should be drawn under the stuck header. The default value is true. If you do not want to see the list scroll under your header you will want to set this attribute to `false`.
+```java
+public void setDrawingListUnderStickyHeader(boolean drawingListUnderStickyHeader);
+public boolean isDrawingListUnderStickyHeader();
+```
+
+Contributing
+------------
+Contributions are very welcome. Now that this library has grown in popularity i have a hard time keeping upp with all the issues while tending to a multitude of other projects as well as school. So if you find a bug in the library or want a feature and think you can fix it yourself, fork + pull request and i will greatly appreciate it!
+
+I love getting pull requests for new features as well as bugs. However, when it comes to new features please also explain the use case and way you think the library should include it. If you don't want to start coding a feature without knowing if the feature will have chance of being included, open an issue and we can discuss the feature!
diff --git a/libraries/StickyListHeaders/build.gradle b/libraries/StickyListHeaders/build.gradle
new file mode 100644
index 000000000..849b1323c
--- /dev/null
+++ b/libraries/StickyListHeaders/build.gradle
@@ -0,0 +1,24 @@
+buildscript {
+ repositories {
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:0.6.+'
+ }
+}
+
+def isReleaseBuild() {
+ return version.contains("SNAPSHOT") == false
+}
+
+allprojects {
+ version = VERSION_NAME
+ group = GROUP
+
+ repositories {
+ mavenCentral()
+ }
+}
+
+apply plugin: 'android-reporting'
diff --git a/libraries/StickyListHeaders/demo.gif b/libraries/StickyListHeaders/demo.gif
new file mode 100644
index 000000000..d73e3297b
Binary files /dev/null and b/libraries/StickyListHeaders/demo.gif differ
diff --git a/libraries/StickyListHeaders/gradle/wrapper/gradle-wrapper.jar b/libraries/StickyListHeaders/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 000000000..8c0fb64a8
Binary files /dev/null and b/libraries/StickyListHeaders/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/libraries/StickyListHeaders/gradle/wrapper/gradle-wrapper.properties b/libraries/StickyListHeaders/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..0a7effc64
--- /dev/null
+++ b/libraries/StickyListHeaders/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Tue Nov 19 08:36:06 CET 2013
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=http\://services.gradle.org/distributions/gradle-1.8-all.zip
diff --git a/libraries/StickyListHeaders/gradlew b/libraries/StickyListHeaders/gradlew
new file mode 100644
index 000000000..91a7e269e
--- /dev/null
+++ b/libraries/StickyListHeaders/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/libraries/StickyListHeaders/gradlew.bat b/libraries/StickyListHeaders/gradlew.bat
new file mode 100644
index 000000000..aec99730b
--- /dev/null
+++ b/libraries/StickyListHeaders/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/libraries/StickyListHeaders/library/.gitignore b/libraries/StickyListHeaders/library/.gitignore
new file mode 100644
index 000000000..f8b92c3aa
--- /dev/null
+++ b/libraries/StickyListHeaders/library/.gitignore
@@ -0,0 +1,2 @@
+.gradle
+build
diff --git a/libraries/StickyListHeaders/library/AndroidManifest.xml b/libraries/StickyListHeaders/library/AndroidManifest.xml
new file mode 100644
index 000000000..328dcc65c
--- /dev/null
+++ b/libraries/StickyListHeaders/library/AndroidManifest.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/libraries/StickyListHeaders/library/build.gradle b/libraries/StickyListHeaders/library/build.gradle
new file mode 100644
index 000000000..a15de94c2
--- /dev/null
+++ b/libraries/StickyListHeaders/library/build.gradle
@@ -0,0 +1,16 @@
+apply plugin: 'android-library'
+
+android {
+ compileSdkVersion 19
+ buildToolsVersion '19.0.0'
+
+ sourceSets {
+ main {
+ manifest.srcFile 'AndroidManifest.xml'
+ java.srcDirs = ['src']
+ res.srcDirs = ['res']
+ }
+ }
+}
+
+apply from: 'https://raw.github.com/chrisbanes/gradle-mvn-push/eaa6b5404b7594e6c23b884fdc5795f545db55dd/gradle-mvn-push.gradle'
diff --git a/libraries/StickyListHeaders/library/build.xml b/libraries/StickyListHeaders/library/build.xml
new file mode 100644
index 000000000..2f6f323a2
--- /dev/null
+++ b/libraries/StickyListHeaders/library/build.xml
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/libraries/StickyListHeaders/library/proguard-project.txt b/libraries/StickyListHeaders/library/proguard-project.txt
new file mode 100644
index 000000000..f2fe1559a
--- /dev/null
+++ b/libraries/StickyListHeaders/library/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/libraries/StickyListHeaders/library/project.properties b/libraries/StickyListHeaders/library/project.properties
new file mode 100644
index 000000000..91d2b0246
--- /dev/null
+++ b/libraries/StickyListHeaders/library/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/StickyListHeaders/library/res/values/attrs.xml b/libraries/StickyListHeaders/library/res/values/attrs.xml
new file mode 100644
index 000000000..8d09a9c1b
--- /dev/null
+++ b/libraries/StickyListHeaders/library/res/values/attrs.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/AdapterWrapper.java b/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/AdapterWrapper.java
new file mode 100644
index 000000000..e67de0dbf
--- /dev/null
+++ b/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/AdapterWrapper.java
@@ -0,0 +1,225 @@
+package se.emilsjolander.stickylistheaders;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import android.content.Context;
+import android.database.DataSetObserver;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.Checkable;
+import android.widget.ListAdapter;
+
+/**
+ * A {@link ListAdapter} which wraps a {@link StickyListHeadersAdapter} and
+ * automatically handles wrapping the result of
+ * {@link StickyListHeadersAdapter#getView(int, android.view.View, android.view.ViewGroup)}
+ * and
+ * {@link StickyListHeadersAdapter#getHeaderView(int, android.view.View, android.view.ViewGroup)}
+ * appropriately.
+ *
+ * @author Jake Wharton (jakewharton@gmail.com)
+ */
+class AdapterWrapper extends BaseAdapter implements StickyListHeadersAdapter {
+
+ interface OnHeaderClickListener {
+ public void onHeaderClick(View header, int itemPosition, long headerId);
+ }
+
+ final StickyListHeadersAdapter mDelegate;
+ private final List mHeaderCache = new LinkedList();
+ private final Context mContext;
+ private Drawable mDivider;
+ private int mDividerHeight;
+ private OnHeaderClickListener mOnHeaderClickListener;
+ private DataSetObserver mDataSetObserver = new DataSetObserver() {
+
+ @Override
+ public void onInvalidated() {
+ mHeaderCache.clear();
+ AdapterWrapper.super.notifyDataSetInvalidated();
+ }
+
+ @Override
+ public void onChanged() {
+ AdapterWrapper.super.notifyDataSetChanged();
+ }
+ };
+
+ AdapterWrapper(Context context,
+ StickyListHeadersAdapter delegate) {
+ this.mContext = context;
+ this.mDelegate = delegate;
+ delegate.registerDataSetObserver(mDataSetObserver);
+ }
+
+ void setDivider(Drawable divider, int dividerHeight) {
+ this.mDivider = divider;
+ this.mDividerHeight = dividerHeight;
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public boolean areAllItemsEnabled() {
+ return mDelegate.areAllItemsEnabled();
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ return mDelegate.isEnabled(position);
+ }
+
+ @Override
+ public int getCount() {
+ return mDelegate.getCount();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return mDelegate.getItem(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return mDelegate.getItemId(position);
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return mDelegate.hasStableIds();
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ return mDelegate.getItemViewType(position);
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return mDelegate.getViewTypeCount();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return mDelegate.isEmpty();
+ }
+
+ /**
+ * Will recycle header from {@link WrapperView} if it exists
+ */
+ private void recycleHeaderIfExists(WrapperView wv) {
+ View header = wv.mHeader;
+ if (header != null) {
+ // reset the headers visibility when adding it to the cache
+ header.setVisibility(View.VISIBLE);
+ mHeaderCache.add(header);
+ }
+ }
+
+ /**
+ * Get a header view. This optionally pulls a header from the supplied
+ * {@link WrapperView} and will also recycle the divider if it exists.
+ */
+ private View configureHeader(WrapperView wv, final int position) {
+ View header = wv.mHeader == null ? popHeader() : wv.mHeader;
+ header = mDelegate.getHeaderView(position, header, wv);
+ if (header == null) {
+ throw new NullPointerException("Header view must not be null.");
+ }
+ //if the header isn't clickable, the listselector will be drawn on top of the header
+ header.setClickable(true);
+ header.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if(mOnHeaderClickListener != null){
+ long headerId = mDelegate.getHeaderId(position);
+ mOnHeaderClickListener.onHeaderClick(v, position, headerId);
+ }
+ }
+ });
+ return header;
+ }
+
+ private View popHeader() {
+ if(mHeaderCache.size() > 0) {
+ return mHeaderCache.remove(0);
+ }
+ return null;
+ }
+
+ /** Returns {@code true} if the previous position has the same header ID. */
+ private boolean previousPositionHasSameHeader(int position) {
+ return position != 0
+ && mDelegate.getHeaderId(position) == mDelegate
+ .getHeaderId(position - 1);
+ }
+
+ @Override
+ public WrapperView getView(int position, View convertView, ViewGroup parent) {
+ WrapperView wv = (convertView == null) ? new WrapperView(mContext) : (WrapperView) convertView;
+ View item = mDelegate.getView(position, wv.mItem, parent);
+ View header = null;
+ if (previousPositionHasSameHeader(position)) {
+ recycleHeaderIfExists(wv);
+ } else {
+ header = configureHeader(wv, position);
+ }
+ if((item instanceof Checkable) && !(wv instanceof CheckableWrapperView)) {
+ // Need to create Checkable subclass of WrapperView for ListView to work correctly
+ wv = new CheckableWrapperView(mContext);
+ } else if(!(item instanceof Checkable) && (wv instanceof CheckableWrapperView)) {
+ wv = new WrapperView(mContext);
+ }
+ wv.update(item, header, mDivider, mDividerHeight);
+ return wv;
+ }
+
+ public void setOnHeaderClickListener(OnHeaderClickListener onHeaderClickListener){
+ this.mOnHeaderClickListener = onHeaderClickListener;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return mDelegate.equals(o);
+ }
+
+ @Override
+ public View getDropDownView(int position, View convertView, ViewGroup parent) {
+ return ((BaseAdapter) mDelegate).getDropDownView(position, convertView, parent);
+ }
+
+ @Override
+ public int hashCode() {
+ return mDelegate.hashCode();
+ }
+
+ @Override
+ public void notifyDataSetChanged() {
+ ((BaseAdapter) mDelegate).notifyDataSetChanged();
+ }
+
+ @Override
+ public void notifyDataSetInvalidated() {
+ ((BaseAdapter) mDelegate).notifyDataSetInvalidated();
+ }
+
+ @Override
+ public String toString() {
+ return mDelegate.toString();
+ }
+
+ @Override
+ public View getHeaderView(int position, View convertView, ViewGroup parent) {
+ return mDelegate.getHeaderView(position, convertView, parent);
+ }
+
+ @Override
+ public long getHeaderId(int position) {
+ return mDelegate.getHeaderId(position);
+ }
+
+}
diff --git a/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/ApiLevelTooLowException.java b/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/ApiLevelTooLowException.java
new file mode 100644
index 000000000..5b0a83827
--- /dev/null
+++ b/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/ApiLevelTooLowException.java
@@ -0,0 +1,11 @@
+package se.emilsjolander.stickylistheaders;
+
+public class ApiLevelTooLowException extends RuntimeException {
+
+ private static final long serialVersionUID = -5480068364264456757L;
+
+ public ApiLevelTooLowException(int versionCode) {
+ super("Requires API level " + versionCode);
+ }
+
+}
diff --git a/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/CheckableWrapperView.java b/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/CheckableWrapperView.java
new file mode 100644
index 000000000..9039c3f5c
--- /dev/null
+++ b/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/CheckableWrapperView.java
@@ -0,0 +1,31 @@
+package se.emilsjolander.stickylistheaders;
+
+import android.content.Context;
+import android.widget.Checkable;
+
+/**
+ * A WrapperView that implements the checkable interface
+ *
+ * @author Emil Sjölander
+ */
+class CheckableWrapperView extends WrapperView implements Checkable {
+
+ public CheckableWrapperView(final Context context) {
+ super(context);
+ }
+
+ @Override
+ public boolean isChecked() {
+ return ((Checkable) mItem).isChecked();
+ }
+
+ @Override
+ public void setChecked(final boolean checked) {
+ ((Checkable) mItem).setChecked(checked);
+ }
+
+ @Override
+ public void toggle() {
+ setChecked(!isChecked());
+ }
+}
diff --git a/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/SectionIndexerAdapterWrapper.java b/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/SectionIndexerAdapterWrapper.java
new file mode 100644
index 000000000..ff7e293af
--- /dev/null
+++ b/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/SectionIndexerAdapterWrapper.java
@@ -0,0 +1,32 @@
+package se.emilsjolander.stickylistheaders;
+
+import android.content.Context;
+import android.widget.SectionIndexer;
+
+class SectionIndexerAdapterWrapper extends
+ AdapterWrapper implements SectionIndexer {
+
+ final SectionIndexer mSectionIndexerDelegate;
+
+ SectionIndexerAdapterWrapper(Context context,
+ StickyListHeadersAdapter delegate) {
+ super(context, delegate);
+ mSectionIndexerDelegate = (SectionIndexer) delegate;
+ }
+
+ @Override
+ public int getPositionForSection(int section) {
+ return mSectionIndexerDelegate.getPositionForSection(section);
+ }
+
+ @Override
+ public int getSectionForPosition(int position) {
+ return mSectionIndexerDelegate.getSectionForPosition(position);
+ }
+
+ @Override
+ public Object[] getSections() {
+ return mSectionIndexerDelegate.getSections();
+ }
+
+}
diff --git a/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/StickyListHeadersAdapter.java b/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/StickyListHeadersAdapter.java
new file mode 100644
index 000000000..8b80b71f1
--- /dev/null
+++ b/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/StickyListHeadersAdapter.java
@@ -0,0 +1,38 @@
+package se.emilsjolander.stickylistheaders;
+
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ListAdapter;
+
+public interface StickyListHeadersAdapter extends ListAdapter {
+ /**
+ * Get a View that displays the header data at the specified position in the
+ * set. You can either create a View manually or inflate it from an XML layout
+ * file.
+ *
+ * @param position
+ * The position of the item within the adapter's data set of the item whose
+ * header view we want.
+ * @param convertView
+ * The old view to reuse, if possible. Note: You should check that this view is
+ * non-null and of an appropriate type before using. If it is not possible to
+ * convert this view to display the correct data, this method can create a new
+ * view.
+ * @param parent
+ * The parent that this view will eventually be attached to.
+ * @return
+ * A View corresponding to the data at the specified position.
+ */
+ View getHeaderView(int position, View convertView, ViewGroup parent);
+
+ /**
+ * Get the header id associated with the specified position in the list.
+ *
+ * @param position
+ * The position of the item within the adapter's data set whose header id we
+ * want.
+ * @return
+ * The id of the header at the specified position.
+ */
+ long getHeaderId(int position);
+}
diff --git a/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/StickyListHeadersListView.java b/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/StickyListHeadersListView.java
new file mode 100644
index 000000000..476f6cfad
--- /dev/null
+++ b/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/StickyListHeadersListView.java
@@ -0,0 +1,925 @@
+package se.emilsjolander.stickylistheaders;
+
+import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.database.DataSetObserver;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.SparseBooleanArray;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.AbsListView.OnScrollListener;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.AdapterView.OnItemLongClickListener;
+import android.widget.FrameLayout;
+import android.widget.ListView;
+import android.widget.SectionIndexer;
+
+import se.emilsjolander.stickylistheaders.WrapperViewList.LifeCycleListener;
+
+/**
+ * Even though this is a FrameLayout subclass we it is called a ListView. This
+ * is because of 2 reasons. 1. It acts like as ListView 2. It used to be a
+ * ListView subclass and i did not was to change to name causing compatibility
+ * errors.
+ *
+ * @author Emil Sjölander
+ */
+public class StickyListHeadersListView extends FrameLayout {
+
+ public interface OnHeaderClickListener {
+ public void onHeaderClick(StickyListHeadersListView l, View header,
+ int itemPosition, long headerId, boolean currentlySticky);
+ }
+
+ /**
+ * Notifies the listener when the sticky headers top offset has changed.
+ */
+ public interface OnStickyHeaderOffsetChangedListener {
+ /**
+ * @param l The view parent
+ * @param header The currently sticky header being offset.
+ * This header is not guaranteed to have it's measurements set.
+ * It is however guaranteed that this view has been measured,
+ * therefor you should user getMeasured* methods instead of
+ * get* methods for determining the view's size.
+ * @param offset The amount the sticky header is offset by towards to top of the screen.
+ */
+ public void onStickyHeaderOffsetChanged(StickyListHeadersListView l, View header, int offset);
+ }
+
+ /* --- Children --- */
+ private WrapperViewList mList;
+ private View mHeader;
+
+ /* --- Header state --- */
+ private Long mHeaderId;
+ // used to not have to call getHeaderId() all the time
+ private Integer mHeaderPosition;
+ private Integer mHeaderOffset;
+
+ /* --- Delegates --- */
+ private OnScrollListener mOnScrollListenerDelegate;
+ private AdapterWrapper mAdapter;
+
+ /* --- Settings --- */
+ private boolean mAreHeadersSticky = true;
+ private boolean mClippingToPadding = true;
+ private boolean mIsDrawingListUnderStickyHeader = true;
+ private int mPaddingLeft = 0;
+ private int mPaddingTop = 0;
+ private int mPaddingRight = 0;
+ private int mPaddingBottom = 0;
+
+ /* --- Other --- */
+ private OnHeaderClickListener mOnHeaderClickListener;
+ private OnStickyHeaderOffsetChangedListener mOnStickyHeaderOffsetChangedListener;
+ private AdapterWrapperDataSetObserver mDataSetObserver;
+ private Drawable mDivider;
+ private int mDividerHeight;
+
+ public StickyListHeadersListView(Context context) {
+ this(context, null);
+ }
+
+ public StickyListHeadersListView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public StickyListHeadersListView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ // Initialize the wrapped list
+ mList = new WrapperViewList(context);
+
+ // null out divider, dividers are handled by adapter so they look good with headers
+ mDivider = mList.getDivider();
+ mDividerHeight = mList.getDividerHeight();
+ mList.setDivider(null);
+ mList.setDividerHeight(0);
+
+ mList.setVerticalScrollBarEnabled(isVerticalScrollBarEnabled());
+ mList.setHorizontalScrollBarEnabled(isHorizontalScrollBarEnabled());
+
+ if (attrs != null) {
+ TypedArray a = context.getTheme().obtainStyledAttributes(attrs,R.styleable.StickyListHeadersListView, 0, 0);
+
+ try {
+ // -- View attributes --
+ int padding = a.getDimensionPixelSize(R.styleable.StickyListHeadersListView_android_padding, 0);
+ mPaddingLeft = a.getDimensionPixelSize(R.styleable.StickyListHeadersListView_android_paddingLeft, padding);
+ mPaddingTop = a.getDimensionPixelSize(R.styleable.StickyListHeadersListView_android_paddingTop, padding);
+ mPaddingRight = a.getDimensionPixelSize(R.styleable.StickyListHeadersListView_android_paddingRight, padding);
+ mPaddingBottom = a.getDimensionPixelSize(R.styleable.StickyListHeadersListView_android_paddingBottom, padding);
+
+ setPadding(mPaddingLeft, mPaddingTop, mPaddingRight,
+ mPaddingBottom);
+
+ // Set clip to padding on the list and reset value to default on
+ // wrapper
+ mClippingToPadding = a.getBoolean(R.styleable.StickyListHeadersListView_android_clipToPadding, true);
+ super.setClipToPadding(true);
+ mList.setClipToPadding(mClippingToPadding);
+
+ // -- ListView attributes --
+ mList.setFadingEdgeLength(a.getDimensionPixelSize(R.styleable.StickyListHeadersListView_android_fadingEdgeLength,
+ mList.getVerticalFadingEdgeLength()));
+ final int fadingEdge = a.getInt(R.styleable.StickyListHeadersListView_android_requiresFadingEdge, 0);
+ if (fadingEdge == 0x00001000) {
+ mList.setVerticalFadingEdgeEnabled(false);
+ mList.setHorizontalFadingEdgeEnabled(true);
+ } else if (fadingEdge == 0x00002000) {
+ mList.setVerticalFadingEdgeEnabled(true);
+ mList.setHorizontalFadingEdgeEnabled(false);
+ } else {
+ mList.setVerticalFadingEdgeEnabled(false);
+ mList.setHorizontalFadingEdgeEnabled(false);
+ }
+ mList.setCacheColorHint(a
+ .getColor(R.styleable.StickyListHeadersListView_android_cacheColorHint, mList.getCacheColorHint()));
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ mList.setChoiceMode(a.getInt(R.styleable.StickyListHeadersListView_android_choiceMode,
+ mList.getChoiceMode()));
+ }
+ mList.setDrawSelectorOnTop(a.getBoolean(R.styleable.StickyListHeadersListView_android_drawSelectorOnTop, false));
+ mList.setFastScrollEnabled(a.getBoolean(R.styleable.StickyListHeadersListView_android_fastScrollEnabled,
+ mList.isFastScrollEnabled()));
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ mList.setFastScrollAlwaysVisible(a.getBoolean(
+ R.styleable.StickyListHeadersListView_android_fastScrollAlwaysVisible,
+ mList.isFastScrollAlwaysVisible()));
+ }
+
+ mList.setScrollBarStyle(a.getInt(R.styleable.StickyListHeadersListView_android_scrollbarStyle, 0));
+
+ if (a.hasValue(R.styleable.StickyListHeadersListView_android_listSelector)) {
+ mList.setSelector(a.getDrawable(R.styleable.StickyListHeadersListView_android_listSelector));
+ }
+
+ mList.setScrollingCacheEnabled(a.getBoolean(R.styleable.StickyListHeadersListView_android_scrollingCache,
+ mList.isScrollingCacheEnabled()));
+
+ if (a.hasValue(R.styleable.StickyListHeadersListView_android_divider)) {
+ mDivider = a.getDrawable(R.styleable.StickyListHeadersListView_android_divider);
+ }
+
+ mDividerHeight = a.getDimensionPixelSize(R.styleable.StickyListHeadersListView_android_dividerHeight,
+ mDividerHeight);
+
+ // -- StickyListHeaders attributes --
+ mAreHeadersSticky = a.getBoolean(R.styleable.StickyListHeadersListView_hasStickyHeaders, true);
+ mIsDrawingListUnderStickyHeader = a.getBoolean(
+ R.styleable.StickyListHeadersListView_isDrawingListUnderStickyHeader,
+ true);
+ } finally {
+ a.recycle();
+ }
+ }
+
+ // attach some listeners to the wrapped list
+ mList.setLifeCycleListener(new WrapperViewListLifeCycleListener());
+ mList.setOnScrollListener(new WrapperListScrollListener());
+
+ addView(mList);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ measureHeader(mHeader);
+ }
+
+ private void ensureHeaderHasCorrectLayoutParams(View header) {
+ ViewGroup.LayoutParams lp = header.getLayoutParams();
+ if (lp == null) {
+ lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+ } else if (lp.height == LayoutParams.MATCH_PARENT) {
+ lp.height = LayoutParams.WRAP_CONTENT;
+ }
+ header.setLayoutParams(lp);
+ }
+
+ private void measureHeader(View header) {
+ if (header != null) {
+ final int width = getMeasuredWidth() - mPaddingLeft - mPaddingRight;
+ final int parentWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
+ width, MeasureSpec.EXACTLY);
+ final int parentHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0,
+ MeasureSpec.UNSPECIFIED);
+ measureChild(header, parentWidthMeasureSpec,
+ parentHeightMeasureSpec);
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right,
+ int bottom) {
+ mList.layout(0, 0, mList.getMeasuredWidth(), getHeight());
+ if (mHeader != null) {
+ MarginLayoutParams lp = (MarginLayoutParams) mHeader
+ .getLayoutParams();
+ int headerTop = lp.topMargin
+ + (mClippingToPadding ? mPaddingTop : 0);
+ // The left parameter must for some reason be set to 0.
+ // I think it should be set to mPaddingLeft but apparently not
+ mHeader.layout(mPaddingLeft, headerTop, mHeader.getMeasuredWidth()
+ + mPaddingLeft, headerTop + mHeader.getMeasuredHeight());
+ }
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ // Only draw the list here.
+ // The header should be drawn right after the lists children are drawn.
+ // This is done so that the header is above the list items
+ // but below the list decorators (scroll bars etc).
+ drawChild(canvas, mList, 0);
+ }
+
+ // Reset values tied the header. also remove header form layout
+ // This is called in response to the data set or the adapter changing
+ private void clearHeader() {
+ if (mHeader != null) {
+ removeView(mHeader);
+ mHeader = null;
+ mHeaderId = null;
+ mHeaderPosition = null;
+ mHeaderOffset = null;
+
+ // reset the top clipping length
+ mList.setTopClippingLength(0);
+ updateHeaderVisibilities();
+ }
+ }
+
+ private void updateOrClearHeader(int firstVisiblePosition) {
+ final int adapterCount = mAdapter == null ? 0 : mAdapter.getCount();
+ if (adapterCount == 0 || !mAreHeadersSticky) {
+ return;
+ }
+
+ final int headerViewCount = mList.getHeaderViewsCount();
+ final int realFirstVisibleItem = firstVisiblePosition - headerViewCount;
+
+ // It is not a mistake to call getFirstVisiblePosition() here.
+ // Most of the time getFixedFirstVisibleItem() should be called
+ // but that does not work great together with getChildAt()
+ final boolean doesListHaveChildren = mList.getChildCount() != 0;
+ final boolean isFirstViewBelowTop = doesListHaveChildren && mList
+ .getFirstVisiblePosition() == 0
+ && mList.getChildAt(0).getTop() > (mClippingToPadding ? mPaddingTop : 0);
+ final boolean isFirstVisibleItemOutsideAdapterRange = realFirstVisibleItem > adapterCount - 1
+ || realFirstVisibleItem < 0;
+ if (!doesListHaveChildren || isFirstVisibleItemOutsideAdapterRange
+ || isFirstViewBelowTop) {
+ clearHeader();
+ return;
+ }
+
+ updateHeader(realFirstVisibleItem);
+ }
+
+ private void updateHeader(int firstVisiblePosition) {
+
+ // check if there is a new header should be sticky
+ if (mHeaderPosition == null || mHeaderPosition != firstVisiblePosition) {
+ mHeaderPosition = firstVisiblePosition;
+ final long headerId = mAdapter.getHeaderId(firstVisiblePosition);
+ if (mHeaderId == null || mHeaderId != headerId) {
+ mHeaderId = headerId;
+ final View header = mAdapter.getHeaderView(mHeaderPosition,
+ mHeader, this);
+ if (mHeader != header) {
+ if (header == null) {
+ throw new NullPointerException("header may not be null");
+ }
+ swapHeader(header);
+ }
+
+ ensureHeaderHasCorrectLayoutParams(mHeader);
+ measureHeader(mHeader);
+
+ // Reset mHeaderOffset to null ensuring
+ // that it will be set on the header and
+ // not skipped for performance reasons.
+ mHeaderOffset = null;
+ }
+ }
+
+ int headerOffset = 0;
+
+ // Calculate new header offset
+ // Skip looking at the first view. it never matters because it always
+ // results in a headerOffset = 0
+ int headerBottom = mHeader.getMeasuredHeight()
+ + (mClippingToPadding ? mPaddingTop : 0);
+ for (int i = 0; i < mList.getChildCount(); i++) {
+ final View child = mList.getChildAt(i);
+ final boolean doesChildHaveHeader = child instanceof WrapperView
+ && ((WrapperView) child).hasHeader();
+ final boolean isChildFooter = mList.containsFooterView(child);
+ if (child.getTop() >= (mClippingToPadding ? mPaddingTop : 0)
+ && (doesChildHaveHeader || isChildFooter)) {
+ headerOffset = Math.min(child.getTop() - headerBottom, 0);
+ break;
+ }
+ }
+
+ setHeaderOffet(headerOffset);
+
+ if (!mIsDrawingListUnderStickyHeader) {
+ mList.setTopClippingLength(mHeader.getMeasuredHeight()
+ + mHeaderOffset);
+ }
+
+ updateHeaderVisibilities();
+ }
+
+ private void swapHeader(View newHeader) {
+ if (mHeader != null) {
+ removeView(mHeader);
+ }
+ mHeader = newHeader;
+ addView(mHeader);
+ mHeader.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (mOnHeaderClickListener != null) {
+ mOnHeaderClickListener.onHeaderClick(
+ StickyListHeadersListView.this, mHeader,
+ mHeaderPosition, mHeaderId, true);
+ }
+ }
+
+ });
+ }
+
+ // hides the headers in the list under the sticky header.
+ // Makes sure the other ones are showing
+ private void updateHeaderVisibilities() {
+ int top;
+ if (mHeader != null) {
+ top = mHeader.getMeasuredHeight()
+ + (mHeaderOffset != null ? mHeaderOffset : 0);
+ } else {
+ top = mClippingToPadding ? mPaddingTop : 0;
+ }
+ int childCount = mList.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+
+ // ensure child is a wrapper view
+ View child = mList.getChildAt(i);
+ if (!(child instanceof WrapperView)) {
+ continue;
+ }
+
+ // ensure wrapper view child has a header
+ WrapperView wrapperViewChild = (WrapperView) child;
+ if (!wrapperViewChild.hasHeader()) {
+ continue;
+ }
+
+ // update header views visibility
+ View childHeader = wrapperViewChild.mHeader;
+ if (wrapperViewChild.getTop() < top) {
+ if (childHeader.getVisibility() != View.INVISIBLE) {
+ childHeader.setVisibility(View.INVISIBLE);
+ }
+ } else {
+ if (childHeader.getVisibility() != View.VISIBLE) {
+ childHeader.setVisibility(View.VISIBLE);
+ }
+ }
+ }
+ }
+
+ // Wrapper around setting the header offset in different ways depending on
+ // the API version
+ @SuppressLint("NewApi")
+ private void setHeaderOffet(int offset) {
+ if (mHeaderOffset == null || mHeaderOffset != offset) {
+ mHeaderOffset = offset;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ mHeader.setTranslationY(mHeaderOffset);
+ } else {
+ MarginLayoutParams params = (MarginLayoutParams) mHeader
+ .getLayoutParams();
+ params.topMargin = mHeaderOffset;
+ mHeader.setLayoutParams(params);
+ }
+ if (mOnStickyHeaderOffsetChangedListener != null) {
+ mOnStickyHeaderOffsetChangedListener
+ .onStickyHeaderOffsetChanged(this, mHeader, -mHeaderOffset);
+ }
+ }
+ }
+
+ private class AdapterWrapperDataSetObserver extends DataSetObserver {
+
+ @Override
+ public void onChanged() {
+ clearHeader();
+ }
+
+ @Override
+ public void onInvalidated() {
+ clearHeader();
+ }
+
+ }
+
+ private class WrapperListScrollListener implements OnScrollListener {
+
+ @Override
+ public void onScroll(AbsListView view, int firstVisibleItem,
+ int visibleItemCount, int totalItemCount) {
+ if (mOnScrollListenerDelegate != null) {
+ mOnScrollListenerDelegate.onScroll(view, firstVisibleItem,
+ visibleItemCount, totalItemCount);
+ }
+ updateOrClearHeader(mList.getFixedFirstVisibleItem());
+ }
+
+ @Override
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+ if (mOnScrollListenerDelegate != null) {
+ mOnScrollListenerDelegate.onScrollStateChanged(view,
+ scrollState);
+ }
+ }
+
+ }
+
+ private class WrapperViewListLifeCycleListener implements LifeCycleListener {
+
+ @Override
+ public void onDispatchDrawOccurred(Canvas canvas) {
+ // onScroll is not called often at all before froyo
+ // therefor we need to update the header here as well.
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) {
+ updateOrClearHeader(mList.getFixedFirstVisibleItem());
+ }
+ if (mHeader != null) {
+ if (mClippingToPadding) {
+ canvas.save();
+ canvas.clipRect(0, mPaddingTop, getRight(), getBottom());
+ drawChild(canvas, mHeader, 0);
+ canvas.restore();
+ } else {
+ drawChild(canvas, mHeader, 0);
+ }
+ }
+ }
+
+ }
+
+ private class AdapterWrapperHeaderClickHandler implements
+ AdapterWrapper.OnHeaderClickListener {
+
+ @Override
+ public void onHeaderClick(View header, int itemPosition, long headerId) {
+ mOnHeaderClickListener.onHeaderClick(
+ StickyListHeadersListView.this, header, itemPosition,
+ headerId, false);
+ }
+
+ }
+
+ private boolean isStartOfSection(int position) {
+ return position == 0
+ || mAdapter.getHeaderId(position) != mAdapter
+ .getHeaderId(position - 1);
+ }
+
+ private int getHeaderOverlap(int position) {
+ boolean isStartOfSection = isStartOfSection(position);
+ if (!isStartOfSection) {
+ View header = mAdapter.getHeaderView(position, null, mList);
+ if (header == null) {
+ throw new NullPointerException("header may not be null");
+ }
+ ensureHeaderHasCorrectLayoutParams(header);
+ measureHeader(header);
+ return header.getMeasuredHeight();
+ }
+ return 0;
+ }
+
+ /* ---------- StickyListHeaders specific API ---------- */
+
+ public void setAreHeadersSticky(boolean areHeadersSticky) {
+ mAreHeadersSticky = areHeadersSticky;
+ if (!areHeadersSticky) {
+ clearHeader();
+ } else {
+ updateOrClearHeader(mList.getFixedFirstVisibleItem());
+ }
+ // invalidating the list will trigger dispatchDraw()
+ mList.invalidate();
+ }
+
+ public boolean areHeadersSticky() {
+ return mAreHeadersSticky;
+ }
+
+ /**
+ * Use areHeadersSticky() method instead
+ */
+ @Deprecated
+ public boolean getAreHeadersSticky() {
+ return areHeadersSticky();
+ }
+
+ public void setDrawingListUnderStickyHeader(
+ boolean drawingListUnderStickyHeader) {
+ mIsDrawingListUnderStickyHeader = drawingListUnderStickyHeader;
+ // reset the top clipping length
+ mList.setTopClippingLength(0);
+ }
+
+ public boolean isDrawingListUnderStickyHeader() {
+ return mIsDrawingListUnderStickyHeader;
+ }
+
+ public void setOnHeaderClickListener(OnHeaderClickListener listener) {
+ mOnHeaderClickListener = listener;
+ if (mAdapter != null) {
+ if (mOnHeaderClickListener != null) {
+ mAdapter.setOnHeaderClickListener(new AdapterWrapperHeaderClickHandler());
+ } else {
+ mAdapter.setOnHeaderClickListener(null);
+ }
+ }
+ }
+
+ public void setOnStickyHeaderOffsetChangedListener(OnStickyHeaderOffsetChangedListener listener) {
+ mOnStickyHeaderOffsetChangedListener = listener;
+ }
+
+ public View getListChildAt(int index) {
+ return mList.getChildAt(index);
+ }
+
+ public int getListChildCount() {
+ return mList.getChildCount();
+ }
+
+ /**
+ * Use the method with extreme caution!! Changing any values on the
+ * underlying ListView might break everything.
+ *
+ * @return the ListView backing this view.
+ */
+ public ListView getWrappedList() {
+ return mList;
+ }
+
+ /* ---------- ListView delegate methods ---------- */
+
+ public void setAdapter(StickyListHeadersAdapter adapter) {
+ if (adapter == null) {
+ mList.setAdapter(null);
+ clearHeader();
+ return;
+ }
+ if (mAdapter != null) {
+ mAdapter.unregisterDataSetObserver(mDataSetObserver);
+ }
+
+ if (adapter instanceof SectionIndexer) {
+ mAdapter = new SectionIndexerAdapterWrapper(getContext(), adapter);
+ } else {
+ mAdapter = new AdapterWrapper(getContext(), adapter);
+ }
+ mDataSetObserver = new AdapterWrapperDataSetObserver();
+ mAdapter.registerDataSetObserver(mDataSetObserver);
+
+ if (mOnHeaderClickListener != null) {
+ mAdapter.setOnHeaderClickListener(new AdapterWrapperHeaderClickHandler());
+ } else {
+ mAdapter.setOnHeaderClickListener(null);
+ }
+
+ mAdapter.setDivider(mDivider, mDividerHeight);
+
+ mList.setAdapter(mAdapter);
+ clearHeader();
+ }
+
+ public StickyListHeadersAdapter getAdapter() {
+ return mAdapter == null ? null : mAdapter.mDelegate;
+ }
+
+ public void setDivider(Drawable divider) {
+ mDivider = divider;
+ if (mAdapter != null) {
+ mAdapter.setDivider(mDivider, mDividerHeight);
+ }
+ }
+
+ public void setDividerHeight(int dividerHeight) {
+ mDividerHeight = dividerHeight;
+ if (mAdapter != null) {
+ mAdapter.setDivider(mDivider, mDividerHeight);
+ }
+ }
+
+ public Drawable getDivider() {
+ return mDivider;
+ }
+
+ public int getDividerHeight() {
+ return mDividerHeight;
+ }
+
+ public void setOnScrollListener(OnScrollListener onScrollListener) {
+ mOnScrollListenerDelegate = onScrollListener;
+ }
+
+ public void setOnItemClickListener(OnItemClickListener listener) {
+ mList.setOnItemClickListener(listener);
+ }
+
+ public void setOnItemLongClickListener(OnItemLongClickListener listener) {
+ mList.setOnItemLongClickListener(listener);
+ }
+
+ public void addHeaderView(View v, Object data, boolean isSelectable) {
+ mList.addHeaderView(v, data, isSelectable);
+ }
+
+ public void addHeaderView(View v) {
+ mList.addHeaderView(v);
+ }
+
+ public void removeHeaderView(View v) {
+ mList.removeHeaderView(v);
+ }
+
+ public int getHeaderViewsCount() {
+ return mList.getHeaderViewsCount();
+ }
+
+ public void addFooterView(View v) {
+ mList.addFooterView(v);
+ }
+
+ public void removeFooterView(View v) {
+ mList.removeFooterView(v);
+ }
+
+ public int getFooterViewsCount() {
+ return mList.getFooterViewsCount();
+ }
+
+ public void setEmptyView(View v) {
+ mList.setEmptyView(v);
+ }
+
+ public View getEmptyView() {
+ return mList.getEmptyView();
+ }
+
+ @Override
+ public void setVerticalScrollBarEnabled(boolean verticalScrollBarEnabled) {
+ mList.setVerticalScrollBarEnabled(verticalScrollBarEnabled);
+ }
+
+ @Override
+ public void setHorizontalScrollBarEnabled(boolean horizontalScrollBarEnabled) {
+ mList.setHorizontalScrollBarEnabled(horizontalScrollBarEnabled);
+ }
+
+ @TargetApi(Build.VERSION_CODES.FROYO)
+ public void smoothScrollBy(int distance, int duration) {
+ requireSdkVersion(Build.VERSION_CODES.FROYO);
+ mList.smoothScrollBy(distance, duration);
+ }
+
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public void smoothScrollByOffset(int offset) {
+ requireSdkVersion(Build.VERSION_CODES.HONEYCOMB);
+ mList.smoothScrollByOffset(offset);
+ }
+
+ @SuppressLint("NewApi")
+ @TargetApi(Build.VERSION_CODES.FROYO)
+ public void smoothScrollToPosition(int position) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
+ mList.smoothScrollToPosition(position);
+ } else {
+ int offset = mAdapter == null ? 0 : getHeaderOverlap(position);
+ offset -= mClippingToPadding ? 0 : mPaddingTop;
+ mList.smoothScrollToPositionFromTop(position, offset);
+ }
+ }
+
+ @TargetApi(Build.VERSION_CODES.FROYO)
+ public void smoothScrollToPosition(int position, int boundPosition) {
+ requireSdkVersion(Build.VERSION_CODES.FROYO);
+ mList.smoothScrollToPosition(position, boundPosition);
+ }
+
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public void smoothScrollToPositionFromTop(int position, int offset) {
+ requireSdkVersion(Build.VERSION_CODES.HONEYCOMB);
+ offset += mAdapter == null ? 0 : getHeaderOverlap(position);
+ offset -= mClippingToPadding ? 0 : mPaddingTop;
+ mList.smoothScrollToPositionFromTop(position, offset);
+ }
+
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public void smoothScrollToPositionFromTop(int position, int offset,
+ int duration) {
+ requireSdkVersion(Build.VERSION_CODES.HONEYCOMB);
+ offset += mAdapter == null ? 0 : getHeaderOverlap(position);
+ offset -= mClippingToPadding ? 0 : mPaddingTop;
+ mList.smoothScrollToPositionFromTop(position, offset, duration);
+ }
+
+ public void setSelection(int position) {
+ setSelectionFromTop(position, 0);
+ }
+
+ public void setSelectionAfterHeaderView() {
+ mList.setSelectionAfterHeaderView();
+ }
+
+ public void setSelectionFromTop(int position, int y) {
+ y += mAdapter == null ? 0 : getHeaderOverlap(position);
+ y -= mClippingToPadding ? 0 : mPaddingTop;
+ mList.setSelectionFromTop(position, y);
+ }
+
+ public void setSelector(Drawable sel) {
+ mList.setSelector(sel);
+ }
+
+ public void setSelector(int resID) {
+ mList.setSelector(resID);
+ }
+
+ public int getFirstVisiblePosition() {
+ return mList.getFirstVisiblePosition();
+ }
+
+ public int getLastVisiblePosition() {
+ return mList.getLastVisiblePosition();
+ }
+
+ public void setChoiceMode(int choiceMode) {
+ mList.setChoiceMode(choiceMode);
+ }
+
+ public void setItemChecked(int position, boolean value) {
+ mList.setItemChecked(position, value);
+ }
+
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public int getCheckedItemCount() {
+ requireSdkVersion(Build.VERSION_CODES.HONEYCOMB);
+ return mList.getCheckedItemCount();
+ }
+
+ @TargetApi(Build.VERSION_CODES.FROYO)
+ public long[] getCheckedItemIds() {
+ requireSdkVersion(Build.VERSION_CODES.FROYO);
+ return mList.getCheckedItemIds();
+ }
+
+ public int getCheckedItemPosition() {
+ return mList.getCheckedItemPosition();
+ }
+
+ public SparseBooleanArray getCheckedItemPositions() {
+ return mList.getCheckedItemPositions();
+ }
+
+ public int getCount() {
+ return mList.getCount();
+ }
+
+ public Object getItemAtPosition(int position) {
+ return mList.getItemAtPosition(position);
+ }
+
+ public long getItemIdAtPosition(int position) {
+ return mList.getItemIdAtPosition(position);
+ }
+
+ @Override
+ public void setOnCreateContextMenuListener(OnCreateContextMenuListener l) {
+ mList.setOnCreateContextMenuListener(l);
+ }
+
+ @Override
+ public boolean showContextMenu() {
+ return mList.showContextMenu();
+ }
+
+ public void invalidateViews() {
+ mList.invalidateViews();
+ }
+
+ @Override
+ public void setClipToPadding(boolean clipToPadding) {
+ if (mList != null) {
+ mList.setClipToPadding(clipToPadding);
+ }
+ mClippingToPadding = clipToPadding;
+ }
+
+ @Override
+ public void setPadding(int left, int top, int right, int bottom) {
+ mPaddingLeft = left;
+ mPaddingTop = top;
+ mPaddingRight = right;
+ mPaddingBottom = bottom;
+
+ if (mList != null) {
+ mList.setPadding(left, top, right, bottom);
+ }
+ super.setPadding(0, 0, 0, 0);
+ requestLayout();
+ }
+
+ /*
+ * Overrides an @hide method in View
+ */
+ protected void recomputePadding() {
+ setPadding(mPaddingLeft, mPaddingTop, mPaddingRight, mPaddingBottom);
+ }
+
+ @Override
+ public int getPaddingLeft() {
+ return mPaddingLeft;
+ }
+
+ @Override
+ public int getPaddingTop() {
+ return mPaddingTop;
+ }
+
+ @Override
+ public int getPaddingRight() {
+ return mPaddingRight;
+ }
+
+ @Override
+ public int getPaddingBottom() {
+ return mPaddingBottom;
+ }
+
+ public void setFastScrollEnabled(boolean fastScrollEnabled) {
+ mList.setFastScrollEnabled(fastScrollEnabled);
+ }
+
+ /**
+ * @throws ApiLevelTooLowException on pre-Honeycomb device.
+ * @see android.widget.AbsListView#setFastScrollAlwaysVisible(boolean)
+ */
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public void setFastScrollAlwaysVisible(boolean alwaysVisible) {
+ requireSdkVersion(Build.VERSION_CODES.HONEYCOMB);
+ mList.setFastScrollAlwaysVisible(alwaysVisible);
+ }
+
+ /**
+ * @return true if the fast scroller will always show. False on pre-Honeycomb devices.
+ * @see android.widget.AbsListView#isFastScrollAlwaysVisible()
+ */
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public boolean isFastScrollAlwaysVisible() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
+ return false;
+ }
+ return mList.isFastScrollAlwaysVisible();
+ }
+
+ public void setScrollBarStyle(int style) {
+ mList.setScrollBarStyle(style);
+ }
+
+ public int getScrollBarStyle() {
+ return mList.getScrollBarStyle();
+ }
+
+ private void requireSdkVersion(int versionCode) {
+ if (Build.VERSION.SDK_INT < versionCode) {
+ throw new ApiLevelTooLowException(versionCode);
+ }
+ }
+
+ public int getPositionForView(View view) {
+ return mList.getPositionForView(view);
+ }
+
+}
diff --git a/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/WrapperView.java b/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/WrapperView.java
new file mode 100644
index 000000000..f51416c1c
--- /dev/null
+++ b/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/WrapperView.java
@@ -0,0 +1,150 @@
+package se.emilsjolander.stickylistheaders;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+
+/**
+ *
+ * the view that wrapps a divider header and a normal list item. The listview sees this as 1 item
+ *
+ * @author Emil Sjölander
+ */
+public class WrapperView extends ViewGroup {
+
+ View mItem;
+ Drawable mDivider;
+ int mDividerHeight;
+ View mHeader;
+ int mItemTop;
+
+ WrapperView(Context c) {
+ super(c);
+ }
+
+ public boolean hasHeader() {
+ return mHeader != null;
+ }
+
+ public View getItem() {
+ return mItem;
+ }
+
+ public View getHeader() {
+ return mHeader;
+ }
+
+ void update(View item, View header, Drawable divider, int dividerHeight) {
+
+ //every wrapperview must have a list item
+ if (item == null) {
+ throw new NullPointerException("List view item must not be null.");
+ }
+
+ //only remove the current item if it is not the same as the new item. this can happen if wrapping a recycled view
+ if (this.mItem != item) {
+ removeView(this.mItem);
+ this.mItem = item;
+ final ViewParent parent = item.getParent();
+ if(parent != null && parent != this) {
+ if(parent instanceof ViewGroup) {
+ ((ViewGroup) parent).removeView(item);
+ }
+ }
+ addView(item);
+ }
+
+ //same logik as above but for the header
+ if (this.mHeader != header) {
+ if (this.mHeader != null) {
+ removeView(this.mHeader);
+ }
+ this.mHeader = header;
+ if (header != null) {
+ addView(header);
+ }
+ }
+
+ if (this.mDivider != divider) {
+ this.mDivider = divider;
+ this.mDividerHeight = dividerHeight;
+ invalidate();
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int measuredWidth = MeasureSpec.getSize(widthMeasureSpec);
+ int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(measuredWidth,
+ MeasureSpec.EXACTLY);
+ int measuredHeight = 0;
+
+ //measure header or divider. when there is a header visible it acts as the divider
+ if (mHeader != null) {
+ ViewGroup.LayoutParams params = mHeader.getLayoutParams();
+ if (params != null && params.height > 0) {
+ mHeader.measure(childWidthMeasureSpec,
+ MeasureSpec.makeMeasureSpec(params.height, MeasureSpec.EXACTLY));
+ } else {
+ mHeader.measure(childWidthMeasureSpec,
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+ }
+ measuredHeight += mHeader.getMeasuredHeight();
+ } else if (mDivider != null) {
+ measuredHeight += mDividerHeight;
+ }
+
+ //measure item
+ ViewGroup.LayoutParams params = mItem.getLayoutParams();
+ if (params != null && params.height > 0) {
+ mItem.measure(childWidthMeasureSpec,
+ MeasureSpec.makeMeasureSpec(params.height, MeasureSpec.EXACTLY));
+ } else {
+ mItem.measure(childWidthMeasureSpec,
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+ }
+ measuredHeight += mItem.getMeasuredHeight();
+
+ setMeasuredDimension(measuredWidth, measuredHeight);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+
+ l = 0;
+ t = 0;
+ r = getWidth();
+ b = getHeight();
+
+ if (mHeader != null) {
+ int headerHeight = mHeader.getMeasuredHeight();
+ mHeader.layout(l, t, r, headerHeight);
+ mItemTop = headerHeight;
+ mItem.layout(l, headerHeight, r, b);
+ } else if (mDivider != null) {
+ mDivider.setBounds(l, t, r, mDividerHeight);
+ mItemTop = mDividerHeight;
+ mItem.layout(l, mDividerHeight, r, b);
+ } else {
+ mItemTop = t;
+ mItem.layout(l, t, r, b);
+ }
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ super.dispatchDraw(canvas);
+ if (mHeader == null && mDivider != null) {
+ // Drawable.setBounds() does not seem to work pre-honeycomb. So have
+ // to do this instead
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
+ canvas.clipRect(0, 0, getWidth(), mDividerHeight);
+ }
+ mDivider.draw(canvas);
+ }
+ }
+}
diff --git a/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/WrapperViewList.java b/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/WrapperViewList.java
new file mode 100644
index 000000000..3d68e98db
--- /dev/null
+++ b/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/WrapperViewList.java
@@ -0,0 +1,175 @@
+package se.emilsjolander.stickylistheaders;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.os.Build;
+import android.view.View;
+import android.widget.AbsListView;
+import android.widget.ListView;
+
+class WrapperViewList extends ListView {
+
+ interface LifeCycleListener {
+ void onDispatchDrawOccurred(Canvas canvas);
+ }
+
+ private LifeCycleListener mLifeCycleListener;
+ private List mFooterViews;
+ private int mTopClippingLength;
+ private Rect mSelectorRect = new Rect();// for if reflection fails
+ private Field mSelectorPositionField;
+ private boolean mClippingToPadding = true;
+
+ public WrapperViewList(Context context) {
+ super(context);
+
+ // Use reflection to be able to change the size/position of the list
+ // selector so it does not come under/over the header
+ try {
+ Field selectorRectField = AbsListView.class.getDeclaredField("mSelectorRect");
+ selectorRectField.setAccessible(true);
+ mSelectorRect = (Rect) selectorRectField.get(this);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ mSelectorPositionField = AbsListView.class.getDeclaredField("mSelectorPosition");
+ mSelectorPositionField.setAccessible(true);
+ }
+ } catch (NoSuchFieldException e) {
+ e.printStackTrace();
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public boolean performItemClick(View view, int position, long id) {
+ if (view instanceof WrapperView) {
+ view = ((WrapperView) view).mItem;
+ }
+ return super.performItemClick(view, position, id);
+ }
+
+ private void positionSelectorRect() {
+ if (!mSelectorRect.isEmpty()) {
+ int selectorPosition = getSelectorPosition();
+ if (selectorPosition >= 0) {
+ int firstVisibleItem = getFixedFirstVisibleItem();
+ View v = getChildAt(selectorPosition - firstVisibleItem);
+ if (v instanceof WrapperView) {
+ WrapperView wrapper = ((WrapperView) v);
+ mSelectorRect.top = wrapper.getTop() + wrapper.mItemTop;
+ }
+ }
+ }
+ }
+
+ private int getSelectorPosition() {
+ if (mSelectorPositionField == null) { // not all supported andorid
+ // version have this variable
+ for (int i = 0; i < getChildCount(); i++) {
+ if (getChildAt(i).getBottom() == mSelectorRect.bottom) {
+ return i + getFixedFirstVisibleItem();
+ }
+ }
+ } else {
+ try {
+ return mSelectorPositionField.getInt(this);
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ }
+ }
+ return -1;
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ positionSelectorRect();
+ if (mTopClippingLength != 0) {
+ canvas.save();
+ Rect clipping = canvas.getClipBounds();
+ clipping.top = mTopClippingLength;
+ canvas.clipRect(clipping);
+ super.dispatchDraw(canvas);
+ canvas.restore();
+ } else {
+ super.dispatchDraw(canvas);
+ }
+ mLifeCycleListener.onDispatchDrawOccurred(canvas);
+ }
+
+ void setLifeCycleListener(LifeCycleListener lifeCycleListener) {
+ mLifeCycleListener = lifeCycleListener;
+ }
+
+ @Override
+ public void addFooterView(View v) {
+ super.addFooterView(v);
+ if (mFooterViews == null) {
+ mFooterViews = new ArrayList();
+ }
+ mFooterViews.add(v);
+ }
+
+ @Override
+ public boolean removeFooterView(View v) {
+ if (super.removeFooterView(v)) {
+ mFooterViews.remove(v);
+ return true;
+ }
+ return false;
+ }
+
+ boolean containsFooterView(View v) {
+ if (mFooterViews == null) {
+ return false;
+ }
+ return mFooterViews.contains(v);
+ }
+
+ void setTopClippingLength(int topClipping) {
+ mTopClippingLength = topClipping;
+ }
+
+ int getFixedFirstVisibleItem() {
+ int firstVisibleItem = getFirstVisiblePosition();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ return firstVisibleItem;
+ }
+
+ // first getFirstVisiblePosition() reports items
+ // outside the view sometimes on old versions of android
+ for (int i = 0; i < getChildCount(); i++) {
+ if (getChildAt(i).getBottom() >= 0) {
+ firstVisibleItem += i;
+ break;
+ }
+ }
+
+ // work around to fix bug with firstVisibleItem being to high
+ // because list view does not take clipToPadding=false into account
+ // on old versions of android
+ if (!mClippingToPadding && getPaddingTop() > 0 && firstVisibleItem > 0) {
+ if (getChildAt(0).getTop() > 0) {
+ firstVisibleItem -= 1;
+ }
+ }
+
+ return firstVisibleItem;
+ }
+
+ @Override
+ public void setClipToPadding(boolean clipToPadding) {
+ mClippingToPadding = clipToPadding;
+ super.setClipToPadding(clipToPadding);
+ }
+
+}
diff --git a/libraries/StickyListHeaders/sample/AndroidManifest.xml b/libraries/StickyListHeaders/sample/AndroidManifest.xml
new file mode 100644
index 000000000..4bf61a441
--- /dev/null
+++ b/libraries/StickyListHeaders/sample/AndroidManifest.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/libraries/StickyListHeaders/sample/build.gradle b/libraries/StickyListHeaders/sample/build.gradle
new file mode 100644
index 000000000..b620008e3
--- /dev/null
+++ b/libraries/StickyListHeaders/sample/build.gradle
@@ -0,0 +1,23 @@
+apply plugin: 'android'
+
+repositories {
+ mavenCentral()
+}
+dependencies {
+ compile project(':library')
+ compile 'com.android.support:appcompat-v7:19.0.+'
+ compile 'com.android.support:support-v4:19.0.0'
+}
+
+android {
+ compileSdkVersion 19
+ buildToolsVersion '19.0.0'
+
+ sourceSets {
+ main {
+ manifest.srcFile 'AndroidManifest.xml'
+ java.srcDirs = ['src']
+ res.srcDirs = ['res']
+ }
+ }
+}
diff --git a/libraries/StickyListHeaders/sample/libs/android-support-v4.jar b/libraries/StickyListHeaders/sample/libs/android-support-v4.jar
new file mode 100644
index 000000000..cf12d2839
Binary files /dev/null and b/libraries/StickyListHeaders/sample/libs/android-support-v4.jar differ
diff --git a/libraries/StickyListHeaders/sample/project.properties b/libraries/StickyListHeaders/sample/project.properties
new file mode 100644
index 000000000..a6cf15dae
--- /dev/null
+++ b/libraries/StickyListHeaders/sample/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.reference.1=../library
diff --git a/libraries/StickyListHeaders/sample/res/drawable-hdpi/ic_drawer.png b/libraries/StickyListHeaders/sample/res/drawable-hdpi/ic_drawer.png
new file mode 100644
index 000000000..6614ea4f4
Binary files /dev/null and b/libraries/StickyListHeaders/sample/res/drawable-hdpi/ic_drawer.png differ
diff --git a/libraries/StickyListHeaders/sample/res/drawable-hdpi/ic_launcher.png b/libraries/StickyListHeaders/sample/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 000000000..96a442e5b
Binary files /dev/null and b/libraries/StickyListHeaders/sample/res/drawable-hdpi/ic_launcher.png differ
diff --git a/libraries/StickyListHeaders/sample/res/drawable-ldpi/ic_launcher.png b/libraries/StickyListHeaders/sample/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 000000000..99238729d
Binary files /dev/null and b/libraries/StickyListHeaders/sample/res/drawable-ldpi/ic_launcher.png differ
diff --git a/libraries/StickyListHeaders/sample/res/drawable-mdpi/ic_drawer.png b/libraries/StickyListHeaders/sample/res/drawable-mdpi/ic_drawer.png
new file mode 100644
index 000000000..b05c026c1
Binary files /dev/null and b/libraries/StickyListHeaders/sample/res/drawable-mdpi/ic_drawer.png differ
diff --git a/libraries/StickyListHeaders/sample/res/drawable-mdpi/ic_launcher.png b/libraries/StickyListHeaders/sample/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 000000000..359047dfa
Binary files /dev/null and b/libraries/StickyListHeaders/sample/res/drawable-mdpi/ic_launcher.png differ
diff --git a/libraries/StickyListHeaders/sample/res/drawable-xhdpi/ic_drawer.png b/libraries/StickyListHeaders/sample/res/drawable-xhdpi/ic_drawer.png
new file mode 100644
index 000000000..bcf49dd73
Binary files /dev/null and b/libraries/StickyListHeaders/sample/res/drawable-xhdpi/ic_drawer.png differ
diff --git a/libraries/StickyListHeaders/sample/res/drawable-xhdpi/ic_launcher.png b/libraries/StickyListHeaders/sample/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..71c6d760f
Binary files /dev/null and b/libraries/StickyListHeaders/sample/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/libraries/StickyListHeaders/sample/res/drawable/header_selector.xml b/libraries/StickyListHeaders/sample/res/drawable/header_selector.xml
new file mode 100644
index 000000000..5dfb8265c
--- /dev/null
+++ b/libraries/StickyListHeaders/sample/res/drawable/header_selector.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/libraries/StickyListHeaders/sample/res/layout/header.xml b/libraries/StickyListHeaders/sample/res/layout/header.xml
new file mode 100644
index 000000000..177e40c4e
--- /dev/null
+++ b/libraries/StickyListHeaders/sample/res/layout/header.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/libraries/StickyListHeaders/sample/res/layout/list_footer.xml b/libraries/StickyListHeaders/sample/res/layout/list_footer.xml
new file mode 100644
index 000000000..4fa22c1f3
--- /dev/null
+++ b/libraries/StickyListHeaders/sample/res/layout/list_footer.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/libraries/StickyListHeaders/sample/res/layout/list_header.xml b/libraries/StickyListHeaders/sample/res/layout/list_header.xml
new file mode 100644
index 000000000..97d46c69b
--- /dev/null
+++ b/libraries/StickyListHeaders/sample/res/layout/list_header.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/libraries/StickyListHeaders/sample/res/layout/main.xml b/libraries/StickyListHeaders/sample/res/layout/main.xml
new file mode 100644
index 000000000..2d22c3266
--- /dev/null
+++ b/libraries/StickyListHeaders/sample/res/layout/main.xml
@@ -0,0 +1,114 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/libraries/StickyListHeaders/sample/res/layout/test_list_item_layout.xml b/libraries/StickyListHeaders/sample/res/layout/test_list_item_layout.xml
new file mode 100644
index 000000000..0f8efc32e
--- /dev/null
+++ b/libraries/StickyListHeaders/sample/res/layout/test_list_item_layout.xml
@@ -0,0 +1,8 @@
+
+
diff --git a/libraries/StickyListHeaders/sample/res/values/arrays.xml b/libraries/StickyListHeaders/sample/res/values/arrays.xml
new file mode 100755
index 000000000..5cd71de49
--- /dev/null
+++ b/libraries/StickyListHeaders/sample/res/values/arrays.xml
@@ -0,0 +1,230 @@
+
+
+
+
+
+ Afghanistan
+ Albania
+ Algeria
+ Andorra
+ Angola
+ Antigua & Barbuda
+ Argentina
+ Armenia
+ Australia
+ Austria
+ Azerbaijan
+
+ Bahamas
+ Bahrain
+ Bangladesh
+ Barbados
+ Belarus
+ Belgium
+ Belize
+ Benin
+ Bhutan
+ Bolivia
+ Bosnia and Herzegovina
+ Botswana
+ Brazil
+ Brunei
+ Bulgaria
+ Burkina Faso
+ Burma
+ Burundi
+
+ Cambodia
+ Cameroon
+ Canada
+ Cape Verde
+ Central African Republic
+ Chad
+ Chile
+ China
+ Colombia
+ Comoros
+ Congo, Democratic Republic of the
+ Congo, Republic of the
+ Costa Rica
+ Cote d\'voire
+ Croatia
+ Cuba
+ Cyprus
+ Czech Republic
+
+ Denmark
+ Djibouti
+ Dominica
+ Dominican Republic
+
+ Ecuador
+ Egypt
+ El Salvador
+ Equatorial Guinea
+ Eritrea
+ Estonia
+ Ethiopia
+
+ Fiji
+ Finland
+ France
+
+ Gabon
+ Gambia, The
+ Georgia
+ Ghana
+ Greece
+ Grenada
+ Guatemala
+ Guinea
+ Guinea-Bissau
+ Guyana
+
+ Haiti
+ Holy See
+ Honduras
+ Hong Kong
+ Hungary
+
+ Iceland
+ India
+ Indonesia
+ Iran
+ Iraq
+ Ireland
+ Israel
+ Italy
+
+ Jamaica
+ Japan
+ Jordan
+
+ Kazakhstan
+ Kenya
+ Kiribati
+ Korea, North
+ Korea, South
+ Kosovo
+ Kuwait
+ Kyrgyzstan
+
+ Laos
+ Latvia
+ Lebanon
+ Lesotho
+ Liberia
+ Libya
+ Liechtenstein
+ Lithuania
+ Luxembourg
+
+ Macau
+ Macedonia
+ Madagascar
+ Malawi
+ Malaysia
+ Maldives
+ Mali
+ Malta
+ Marshall Islands
+ Mauritania
+ Mauritius
+ Mexico
+ Micronesia
+ Moldova
+ Monaco
+ Mongolia
+ Montenegro
+ Morocco
+ Mozambique
+
+ Namibia
+ Nauru
+ Nepal
+ Netherlands
+ Netherlands Antilles
+ New Zealand
+ Nicaragua
+ Niger
+ Nigeria
+ North Korea
+ Norway
+
+ Oman
+
+ Pakistan
+ Palau
+ Palestinian Territories
+ Panama
+ Papua New Guinea
+ Paraguay
+ Peru
+ Philippines
+ Poland
+ Portugal
+
+ Qatar
+
+ Romania
+ Russia
+ Rwanda
+
+ Saint Kitts and Nevis
+ Saint Lucia
+ Saint Vincent and the Grenadines
+ Samoa
+ San Marino
+ Sao Tome and Principe
+ Saudi Arabia
+ Senegal
+ Serbia
+ Seychelles
+ Sierra Leone
+ Singapore
+ Slovakia
+ Slovenia
+ Solomon Islands
+ Somalia
+ South Africa
+ South Korea
+ South Sudan
+ Spain
+ Sri Lanka
+ Sudan
+ Suriname
+ Swaziland
+ Sweden
+ Switzerland
+ Syria
+
+ Taiwan
+ Tajikistan
+ Tanzania
+ Thailand
+ Timor-Leste
+ Togo
+ Tonga
+ Trinidad and Tobago
+ Tunisia
+ Turkey
+ Turkmenistan
+ Tuvalu
+
+ Uganda
+ Ukraine
+ United Arab Emirates
+ United Kingdom
+ Uruguay
+ Uzbekistan
+
+ Vanuatu
+ Venezuela
+ Vietnam
+
+ Yemen
+
+ Zambia
+ Zimbabwe
+
+
diff --git a/libraries/StickyListHeaders/sample/res/values/colors.xml b/libraries/StickyListHeaders/sample/res/values/colors.xml
new file mode 100644
index 000000000..601687e2e
--- /dev/null
+++ b/libraries/StickyListHeaders/sample/res/values/colors.xml
@@ -0,0 +1,7 @@
+
+
+ #ffe74c3c
+ #ffc0392b
+ #FFDDDDDD
+
+
\ No newline at end of file
diff --git a/libraries/StickyListHeaders/sample/res/values/strings.xml b/libraries/StickyListHeaders/sample/res/values/strings.xml
new file mode 100644
index 000000000..6beadbe75
--- /dev/null
+++ b/libraries/StickyListHeaders/sample/res/values/strings.xml
@@ -0,0 +1,20 @@
+
+
+ StickyListHeaders Sample
+ Restore
+ Update
+ Clear
+ Empty Adapter
+ Drawer Open
+ Drawer Close
+ OPTIONS
+ ACTIONS
+ Restore list
+ Update list
+ Clear list
+ Sticky header
+ Fade header
+ Draw behind header
+ Fast scroll
+
+
\ No newline at end of file
diff --git a/libraries/StickyListHeaders/sample/res/values/style.xml b/libraries/StickyListHeaders/sample/res/values/style.xml
new file mode 100644
index 000000000..b34634b58
--- /dev/null
+++ b/libraries/StickyListHeaders/sample/res/values/style.xml
@@ -0,0 +1,9 @@
+
+
+
+
\ No newline at end of file
diff --git a/libraries/StickyListHeaders/sample/src/se/emilsjolander/stickylistheaders/TestActivity.java b/libraries/StickyListHeaders/sample/src/se/emilsjolander/stickylistheaders/TestActivity.java
new file mode 100644
index 000000000..dc795ff4e
--- /dev/null
+++ b/libraries/StickyListHeaders/sample/src/se/emilsjolander/stickylistheaders/TestActivity.java
@@ -0,0 +1,169 @@
+package se.emilsjolander.stickylistheaders;
+
+import android.annotation.TargetApi;
+import android.content.res.Configuration;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.v4.app.ActionBarDrawerToggle;
+import android.support.v4.widget.DrawerLayout;
+import android.support.v7.app.ActionBarActivity;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.*;
+
+/**
+ * @author Emil Sjölander
+ */
+public class TestActivity extends ActionBarActivity implements
+ AdapterView.OnItemClickListener, StickyListHeadersListView.OnHeaderClickListener,
+ StickyListHeadersListView.OnStickyHeaderOffsetChangedListener {
+
+ private TestBaseAdapter mAdapter;
+ private DrawerLayout mDrawerLayout;
+ private ActionBarDrawerToggle mDrawerToggle;
+ private boolean fadeHeader = true;
+
+ private StickyListHeadersListView stickyList;
+
+ private Button restoreButton;
+ private Button updateButton;
+ private Button clearButton;
+
+ private CheckBox stickyCheckBox;
+ private CheckBox fadeCheckBox;
+ private CheckBox drawBehindCheckBox;
+ private CheckBox fastScrollCheckBox;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ mAdapter = new TestBaseAdapter(this);
+
+ stickyList = (StickyListHeadersListView) findViewById(R.id.list);
+ stickyList.setOnItemClickListener(this);
+ stickyList.setOnHeaderClickListener(this);
+ stickyList.setOnStickyHeaderOffsetChangedListener(this);
+// mStickyList.addHeaderView(inflater.inflate(R.layout.list_header, null));
+// mStickyList.addFooterView(inflater.inflate(R.layout.list_footer, null));
+ stickyList.setEmptyView(findViewById(R.id.empty));
+ stickyList.setDrawingListUnderStickyHeader(true);
+ stickyList.setAreHeadersSticky(true);
+ stickyList.setAdapter(mAdapter);
+
+ mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
+ mDrawerToggle = new ActionBarDrawerToggle(
+ this, /* host Activity */
+ mDrawerLayout, /* DrawerLayout object */
+ R.drawable.ic_drawer, /* nav drawer icon to replace 'Up' caret */
+ R.string.drawer_open, /* "open drawer" description */
+ R.string.drawer_close /* "close drawer" description */
+ );
+
+ // Set the drawer toggle as the DrawerListener
+ mDrawerLayout.setDrawerListener(mDrawerToggle);
+
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setHomeButtonEnabled(true);
+
+ restoreButton = (Button) findViewById(R.id.restore_button);
+ restoreButton.setOnClickListener(buttonListener);
+ updateButton = (Button) findViewById(R.id.update_button);
+ updateButton.setOnClickListener(buttonListener);
+ clearButton = (Button) findViewById(R.id.clear_button);
+ clearButton.setOnClickListener(buttonListener);
+
+ stickyCheckBox = (CheckBox) findViewById(R.id.sticky_checkBox);
+ stickyCheckBox.setOnCheckedChangeListener(checkBoxListener);
+ fadeCheckBox = (CheckBox) findViewById(R.id.fade_checkBox);
+ fadeCheckBox.setOnCheckedChangeListener(checkBoxListener);
+ drawBehindCheckBox = (CheckBox) findViewById(R.id.draw_behind_checkBox);
+ drawBehindCheckBox.setOnCheckedChangeListener(checkBoxListener);
+ fastScrollCheckBox = (CheckBox) findViewById(R.id.fast_scroll_checkBox);
+ fastScrollCheckBox.setOnCheckedChangeListener(checkBoxListener);
+ }
+
+ @Override
+ protected void onPostCreate(Bundle savedInstanceState) {
+ super.onPostCreate(savedInstanceState);
+ // Sync the toggle state after onRestoreInstanceState has occurred.
+ mDrawerToggle.syncState();
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ mDrawerToggle.onConfigurationChanged(newConfig);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (mDrawerToggle.onOptionsItemSelected(item)) {
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+
+ CompoundButton.OnCheckedChangeListener checkBoxListener = new CompoundButton.OnCheckedChangeListener() {
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ switch (buttonView.getId()) {
+ case R.id.sticky_checkBox:
+ stickyList.setAreHeadersSticky(isChecked);
+ break;
+ case R.id.fade_checkBox:
+ fadeHeader = isChecked;
+ break;
+ case R.id.draw_behind_checkBox:
+ stickyList.setDrawingListUnderStickyHeader(isChecked);
+ break;
+ case R.id.fast_scroll_checkBox:
+ stickyList.setFastScrollEnabled(isChecked);
+ stickyList.setFastScrollAlwaysVisible(isChecked);
+ break;
+ }
+ }
+ };
+
+ View.OnClickListener buttonListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ switch (view.getId()) {
+ case R.id.restore_button:
+ mAdapter.restore();
+ break;
+ case R.id.update_button:
+ mAdapter.notifyDataSetChanged();
+ break;
+ case R.id.clear_button:
+ mAdapter.clear();
+ break;
+ }
+ }
+ };
+
+ @Override
+ public void onItemClick(AdapterView> parent, View view, int position,
+ long id) {
+ Toast.makeText(this, "Item " + position + " clicked!",
+ Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public void onHeaderClick(StickyListHeadersListView l, View header,
+ int itemPosition, long headerId, boolean currentlySticky) {
+ Toast.makeText(this, "Header " + headerId + " currentlySticky ? " + currentlySticky,
+ Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public void onStickyHeaderOffsetChanged(StickyListHeadersListView l, View header, int offset) {
+ if (fadeHeader && Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ header.setAlpha(1 - (offset / (float) header.getMeasuredHeight()));
+ }
+ }
+}
\ No newline at end of file
diff --git a/libraries/StickyListHeaders/sample/src/se/emilsjolander/stickylistheaders/TestBaseAdapter.java b/libraries/StickyListHeaders/sample/src/se/emilsjolander/stickylistheaders/TestBaseAdapter.java
new file mode 100644
index 000000000..777b7bd57
--- /dev/null
+++ b/libraries/StickyListHeaders/sample/src/se/emilsjolander/stickylistheaders/TestBaseAdapter.java
@@ -0,0 +1,169 @@
+package se.emilsjolander.stickylistheaders;
+
+import java.util.ArrayList;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.SectionIndexer;
+import android.widget.TextView;
+
+/**
+ * @author Emil Sjölander
+ */
+public class TestBaseAdapter extends BaseAdapter implements
+ StickyListHeadersAdapter, SectionIndexer {
+
+ private final Context mContext;
+ private String[] mCountries;
+ private int[] mSectionIndices;
+ private Character[] mSectionLetters;
+ private LayoutInflater mInflater;
+
+ public TestBaseAdapter(Context context) {
+ mContext = context;
+ mInflater = LayoutInflater.from(context);
+ mCountries = context.getResources().getStringArray(R.array.countries);
+ mSectionIndices = getSectionIndices();
+ mSectionLetters = getSectionLetters();
+ }
+
+ private int[] getSectionIndices() {
+ ArrayList sectionIndices = new ArrayList();
+ char lastFirstChar = mCountries[0].charAt(0);
+ sectionIndices.add(0);
+ for (int i = 1; i < mCountries.length; i++) {
+ if (mCountries[i].charAt(0) != lastFirstChar) {
+ lastFirstChar = mCountries[i].charAt(0);
+ sectionIndices.add(i);
+ }
+ }
+ int[] sections = new int[sectionIndices.size()];
+ for (int i = 0; i < sectionIndices.size(); i++) {
+ sections[i] = sectionIndices.get(i);
+ }
+ return sections;
+ }
+
+ private Character[] getSectionLetters() {
+ Character[] letters = new Character[mSectionIndices.length];
+ for (int i = 0; i < mSectionIndices.length; i++) {
+ letters[i] = mCountries[mSectionIndices[i]].charAt(0);
+ }
+ return letters;
+ }
+
+ @Override
+ public int getCount() {
+ return mCountries.length;
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return mCountries[position];
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ ViewHolder holder;
+
+ if (convertView == null) {
+ holder = new ViewHolder();
+ convertView = mInflater.inflate(R.layout.test_list_item_layout, parent, false);
+ holder.text = (TextView) convertView.findViewById(R.id.text);
+ convertView.setTag(holder);
+ } else {
+ holder = (ViewHolder) convertView.getTag();
+ }
+
+ holder.text.setText(mCountries[position]);
+
+ return convertView;
+ }
+
+ @Override
+ public View getHeaderView(int position, View convertView, ViewGroup parent) {
+ HeaderViewHolder holder;
+
+ if (convertView == null) {
+ holder = new HeaderViewHolder();
+ convertView = mInflater.inflate(R.layout.header, parent, false);
+ holder.text = (TextView) convertView.findViewById(R.id.text1);
+ convertView.setTag(holder);
+ } else {
+ holder = (HeaderViewHolder) convertView.getTag();
+ }
+
+ // set header text as first char in name
+ CharSequence headerChar = mCountries[position].subSequence(0, 1);
+ holder.text.setText(headerChar);
+
+ return convertView;
+ }
+
+ /**
+ * Remember that these have to be static, postion=1 should always return
+ * the same Id that is.
+ */
+ @Override
+ public long getHeaderId(int position) {
+ // return the first character of the country as ID because this is what
+ // headers are based upon
+ return mCountries[position].subSequence(0, 1).charAt(0);
+ }
+
+ @Override
+ public int getPositionForSection(int section) {
+ if (section >= mSectionIndices.length) {
+ section = mSectionIndices.length - 1;
+ } else if (section < 0) {
+ section = 0;
+ }
+ return mSectionIndices[section];
+ }
+
+ @Override
+ public int getSectionForPosition(int position) {
+ for (int i = 0; i < mSectionIndices.length; i++) {
+ if (position < mSectionIndices[i]) {
+ return i - 1;
+ }
+ }
+ return mSectionIndices.length - 1;
+ }
+
+ @Override
+ public Object[] getSections() {
+ return mSectionLetters;
+ }
+
+ public void clear() {
+ mCountries = new String[0];
+ mSectionIndices = new int[0];
+ mSectionLetters = new Character[0];
+ notifyDataSetChanged();
+ }
+
+ public void restore() {
+ mCountries = mContext.getResources().getStringArray(R.array.countries);
+ mSectionIndices = getSectionIndices();
+ mSectionLetters = getSectionLetters();
+ notifyDataSetChanged();
+ }
+
+ class HeaderViewHolder {
+ TextView text;
+ }
+
+ class ViewHolder {
+ TextView text;
+ }
+
+}
diff --git a/libraries/StickyListHeaders/sample/src/se/emilsjolander/stickylistheaders/views/UnderlineTextView.java b/libraries/StickyListHeaders/sample/src/se/emilsjolander/stickylistheaders/views/UnderlineTextView.java
new file mode 100644
index 000000000..c202c00b8
--- /dev/null
+++ b/libraries/StickyListHeaders/sample/src/se/emilsjolander/stickylistheaders/views/UnderlineTextView.java
@@ -0,0 +1,50 @@
+package se.emilsjolander.stickylistheaders.views;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.widget.TextView;
+
+/**
+ * @author Eric Frohnhoefer
+ */
+public class UnderlineTextView extends TextView {
+ private final Paint mPaint = new Paint();
+ private int mUnderlineHeight = 0;
+
+ public UnderlineTextView(Context context) {
+ this(context, null);
+ }
+
+ public UnderlineTextView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public UnderlineTextView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ init(context, attrs);
+ }
+
+ private void init(Context context, AttributeSet attrs) {
+ Resources r = getResources();
+ mUnderlineHeight = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, r.getDisplayMetrics());
+ }
+
+ @Override
+ public void setPadding(int left, int top, int right, int bottom) {
+ super.setPadding(left, top, right, bottom + mUnderlineHeight);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ // Draw the underline the same color as the text
+ mPaint.setColor(getTextColors().getDefaultColor());
+ canvas.drawRect(0, getHeight() - mUnderlineHeight, getWidth(), getHeight(), mPaint);
+ }
+}
diff --git a/libraries/StickyListHeaders/settings.gradle b/libraries/StickyListHeaders/settings.gradle
new file mode 100644
index 000000000..2c81ac7fd
--- /dev/null
+++ b/libraries/StickyListHeaders/settings.gradle
@@ -0,0 +1,2 @@
+include 'library'
+include 'sample'
diff --git a/settings.gradle b/settings.gradle
index 08454c958..3cb8012d3 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,3 +1,4 @@
include ':OpenPGP-Keychain'
include ':libraries:ActionBarSherlock'
-include ':libraries:HtmlTextView'
\ No newline at end of file
+include ':libraries:HtmlTextView'
+include ':libraries:StickyListHeaders:library'
--
cgit v1.2.3
From 3b5878f86dd9bb1fbd88f02473fd7e7238689b11 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Thu, 2 Jan 2014 21:12:31 +0100
Subject: fix library in README
---
README.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/README.md b/README.md
index 80b34ab05..85e1bd498 100644
--- a/README.md
+++ b/README.md
@@ -27,7 +27,7 @@ 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/pinned-section-listview/library"
+3. File -> Import -> Android -> Existing Android Code Into Workspace, choose "libraries/StickyListHeaders/library"
4. File -> Import -> Android -> Existing Android Code Into Workspace, choose "OpenPGP-Keychain"
5. OpenPGP-Kechain can now be build
@@ -205,8 +205,8 @@ Some parts (older parts and some libraries are Apache License v2, MIT X11 Licens
http://code.google.com/p/zxing/
Apache License v2
-* android-section-adapter
- https://github.com/monxalo/android-section-adapter
+* StickyListHeaders
+ https://github.com/emilsjolander/StickyListHeaders
Apache License v2
--
cgit v1.2.3
From faabf8eca264d2fa41d467e66e546f724281c47c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Thu, 2 Jan 2014 22:01:25 +0100
Subject: fixing some layout problems
---
.../res/layout/key_list_group_item.xml | 40 ----------------------
OpenPGP-Keychain/res/layout/key_list_item.xml | 24 +++++++++++++
.../res/layout/key_list_public_fragment.xml | 25 +++++++++++---
OpenPGP-Keychain/res/layout/stickylist_header.xml | 6 ++--
.../keychain/ui/KeyListPublicFragment.java | 15 +++-----
.../keychain/ui/adapter/KeyListAdapter.java | 2 +-
.../keychain/ui/adapter/KeyListPublicAdapter.java | 2 +-
.../keychain/ui/adapter/KeyListSecretAdapter.java | 2 +-
8 files changed, 55 insertions(+), 61 deletions(-)
delete mode 100644 OpenPGP-Keychain/res/layout/key_list_group_item.xml
create mode 100644 OpenPGP-Keychain/res/layout/key_list_item.xml
diff --git a/OpenPGP-Keychain/res/layout/key_list_group_item.xml b/OpenPGP-Keychain/res/layout/key_list_group_item.xml
deleted file mode 100644
index 240be54b0..000000000
--- a/OpenPGP-Keychain/res/layout/key_list_group_item.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/key_list_item.xml b/OpenPGP-Keychain/res/layout/key_list_item.xml
new file mode 100644
index 000000000..2571bb6e7
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/key_list_item.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/key_list_public_fragment.xml b/OpenPGP-Keychain/res/layout/key_list_public_fragment.xml
index 052dd4249..451230a1f 100644
--- a/OpenPGP-Keychain/res/layout/key_list_public_fragment.xml
+++ b/OpenPGP-Keychain/res/layout/key_list_public_fragment.xml
@@ -2,13 +2,28 @@
+ android:orientation="vertical" >
+ android:layout_height="match_parent"
+ android:clipToPadding="false"
+ android:drawSelectorOnTop="true"
+ android:fastScrollEnabled="true"
+ android:paddingBottom="16dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="32dp"
+ android:paddingTop="16dp"
+ android:scrollbarStyle="outsideOverlay" />
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/stickylist_header.xml b/OpenPGP-Keychain/res/layout/stickylist_header.xml
index 475d1c4db..b8906c2db 100644
--- a/OpenPGP-Keychain/res/layout/stickylist_header.xml
+++ b/OpenPGP-Keychain/res/layout/stickylist_header.xml
@@ -2,15 +2,15 @@
+ android:background="@color/abs__background_holo_light" >
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
index 8167ff439..84adaef99 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
@@ -44,14 +44,10 @@ import com.actionbarsherlock.app.SherlockFragment;
/**
* Public key list with sticky list headers.
*
- * - uses StickyListHeaders library
- * - custom adapter: KeyListPublicAdapter
+ * - uses StickyListHeaders library - custom adapter: KeyListPublicAdapter
+ *
+ * TODO: - fix loader with spinning animation - fix design - fix view holder in adapter
*
- * TODO:
- * - fix loader with spinning animation
- * - fix design
- * - fix view holder in adapter
- *
*/
public class KeyListPublicFragment extends SherlockFragment implements
AdapterView.OnItemClickListener, LoaderManager.LoaderCallbacks {
@@ -72,15 +68,14 @@ public class KeyListPublicFragment extends SherlockFragment implements
mKeyListPublicActivity = (KeyListPublicActivity) getActivity();
- stickyList = (StickyListHeadersListView) getActivity().findViewById(
- R.id.key_list_public_fragment_stickylist);
+ stickyList = (StickyListHeadersListView) getActivity().findViewById(R.id.list);
stickyList.setOnItemClickListener(this);
// stickyList.setOnHeaderClickListener(this);
// stickyList.setOnStickyHeaderOffsetChangedListener(this);
// mStickyList.addHeaderView(inflater.inflate(R.layout.list_header, null));
// mStickyList.addFooterView(inflater.inflate(R.layout.list_footer, null));
- // stickyList.setEmptyView(findViewById(R.id.empty));
+ stickyList.setEmptyView(getActivity().findViewById(R.id.empty));
stickyList.setAreHeadersSticky(true);
stickyList.setDrawingListUnderStickyHeader(true);
stickyList.setFastScrollEnabled(true);
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListAdapter.java
index e94934008..4719d1d1c 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListAdapter.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListAdapter.java
@@ -61,7 +61,7 @@ public class KeyListAdapter extends CursorTreeAdapter {
*/
@Override
public View newGroupView(Context context, Cursor cursor, boolean isExpanded, ViewGroup parent) {
- return mInflater.inflate(R.layout.key_list_group_item, null);
+ return mInflater.inflate(R.layout.key_list_item, null);
}
/**
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java
index 86a47d4d7..108fad917 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java
@@ -81,7 +81,7 @@ public class KeyListPublicAdapter extends CursorAdapter implements StickyListHea
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
- return mInflater.inflate(R.layout.key_list_group_item, null);
+ return mInflater.inflate(R.layout.key_list_item, null);
}
@Override
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java
index 6f3129e4f..6315f84fb 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java
@@ -76,7 +76,7 @@ public class KeyListSecretAdapter extends CursorAdapter {
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
- return mInflater.inflate(R.layout.key_list_group_item, null);
+ return mInflater.inflate(R.layout.key_list_item, null);
}
}
--
cgit v1.2.3
From 7b9b3d07bbb507823ec4621d37e2723460124e0b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Thu, 2 Jan 2014 22:36:57 +0100
Subject: final fixes for public key list
---
OpenPGP-Keychain/res/drawable/header_selector.xml | 7 ----
.../res/layout/key_list_public_fragment.xml | 1 -
OpenPGP-Keychain/res/layout/stickylist_header.xml | 3 +-
OpenPGP-Keychain/res/values/colors.xml | 3 --
.../keychain/ui/KeyListPublicFragment.java | 46 +++++++++++-----------
.../keychain/ui/adapter/KeyListPublicAdapter.java | 7 ++--
6 files changed, 29 insertions(+), 38 deletions(-)
delete mode 100644 OpenPGP-Keychain/res/drawable/header_selector.xml
diff --git a/OpenPGP-Keychain/res/drawable/header_selector.xml b/OpenPGP-Keychain/res/drawable/header_selector.xml
deleted file mode 100644
index 5dfb8265c..000000000
--- a/OpenPGP-Keychain/res/drawable/header_selector.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/key_list_public_fragment.xml b/OpenPGP-Keychain/res/layout/key_list_public_fragment.xml
index 451230a1f..2aca81cd6 100644
--- a/OpenPGP-Keychain/res/layout/key_list_public_fragment.xml
+++ b/OpenPGP-Keychain/res/layout/key_list_public_fragment.xml
@@ -14,7 +14,6 @@
android:paddingBottom="16dp"
android:paddingLeft="16dp"
android:paddingRight="32dp"
- android:paddingTop="16dp"
android:scrollbarStyle="outsideOverlay" />
+ android:layout_height="wrap_content" >
#31b6e7
#cecbce
- #ffe74c3c
- #ffc0392b
- #FFDDDDDD
\ No newline at end of file
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
index 84adaef99..7bc0bcd96 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
@@ -44,19 +44,28 @@ import com.actionbarsherlock.app.SherlockFragment;
/**
* Public key list with sticky list headers.
*
- * - uses StickyListHeaders library - custom adapter: KeyListPublicAdapter
+ * - uses StickyListHeaders library
*
- * TODO: - fix loader with spinning animation - fix design - fix view holder in adapter
+ * - custom adapter: KeyListPublicAdapter
+ *
+ * TODO: - fix view holder in adapter, fix loader
*
*/
public class KeyListPublicFragment extends SherlockFragment implements
AdapterView.OnItemClickListener, LoaderManager.LoaderCallbacks {
private KeyListPublicActivity mKeyListPublicActivity;
-
private KeyListPublicAdapter mAdapter;
+ private StickyListHeadersListView mStickyList;
- StickyListHeadersListView stickyList;
+ /**
+ * Load custom layout with StickyListView from library
+ */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.key_list_public_fragment, container, false);
+ return view;
+ }
/**
* Define Adapter and Loader on create of Activity
@@ -67,20 +76,17 @@ public class KeyListPublicFragment extends SherlockFragment implements
super.onActivityCreated(savedInstanceState);
mKeyListPublicActivity = (KeyListPublicActivity) getActivity();
+ mStickyList = (StickyListHeadersListView) getActivity().findViewById(R.id.list);
- stickyList = (StickyListHeadersListView) getActivity().findViewById(R.id.list);
-
- stickyList.setOnItemClickListener(this);
- // stickyList.setOnHeaderClickListener(this);
- // stickyList.setOnStickyHeaderOffsetChangedListener(this);
+ mStickyList.setOnItemClickListener(this);
// mStickyList.addHeaderView(inflater.inflate(R.layout.list_header, null));
// mStickyList.addFooterView(inflater.inflate(R.layout.list_footer, null));
- stickyList.setEmptyView(getActivity().findViewById(R.id.empty));
- stickyList.setAreHeadersSticky(true);
- stickyList.setDrawingListUnderStickyHeader(true);
- stickyList.setFastScrollEnabled(true);
+ mStickyList.setEmptyView(getActivity().findViewById(R.id.empty));
+ mStickyList.setAreHeadersSticky(true);
+ mStickyList.setDrawingListUnderStickyHeader(false);
+ mStickyList.setFastScrollEnabled(true);
try {
- stickyList.setFastScrollAlwaysVisible(true);
+ mStickyList.setFastScrollAlwaysVisible(true);
} catch (ApiLevelTooLowException e) {
}
@@ -101,12 +107,6 @@ public class KeyListPublicFragment extends SherlockFragment implements
getLoaderManager().initLoader(0, null, this);
}
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.key_list_public_fragment, container, false);
- return view;
- }
-
// These are the rows that we will retrieve.
static final String[] PROJECTION = new String[] { KeyRings._ID, KeyRings.MASTER_KEY_ID,
UserIds.USER_ID };
@@ -134,7 +134,7 @@ public class KeyListPublicFragment extends SherlockFragment implements
mAdapter = new KeyListPublicAdapter(mKeyListPublicActivity, data, Id.type.public_key,
userIdIndex);
- stickyList.setAdapter(mAdapter);
+ mStickyList.setAdapter(mAdapter);
// The list should now be shown.
if (isResumed()) {
@@ -152,9 +152,11 @@ public class KeyListPublicFragment extends SherlockFragment implements
mAdapter.swapCursor(null);
}
+ /**
+ * On click on item, start key view activity
+ */
@Override
public void onItemClick(AdapterView> adapterView, View view, int position, long id) {
- // start key view on click
Intent detailsIntent = new Intent(mKeyListPublicActivity, KeyViewActivity.class);
detailsIntent.setData(KeychainContract.KeyRings.buildPublicKeyRingsUri(Long.toString(id)));
startActivity(detailsIntent);
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java
index 108fad917..ef3a0d228 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java
@@ -86,7 +86,6 @@ public class KeyListPublicAdapter extends CursorAdapter implements StickyListHea
@Override
public View getHeaderView(int position, View convertView, ViewGroup parent) {
-
HeaderViewHolder holder;
if (convertView == null) {
holder = new HeaderViewHolder();
@@ -97,6 +96,7 @@ public class KeyListPublicAdapter extends CursorAdapter implements StickyListHea
holder = (HeaderViewHolder) convertView.getTag();
}
+ // similar to getView in CursorAdapter
if (!mDataValid) {
// no data available at this point
Log.d(Constants.TAG, "getHeaderView: No data available at this point!");
@@ -108,17 +108,18 @@ public class KeyListPublicAdapter extends CursorAdapter implements StickyListHea
throw new IllegalStateException("couldn't move cursor to position " + position);
}
- // set header text as first char in name
+ // set header text as first char in user id
String headerText = "" + mCursor.getString(mSectionColumnIndex).subSequence(0, 1).charAt(0);
holder.text.setText(headerText);
return convertView;
}
/**
- * Remember that these have to be static, position=1 should always return the same Id that is.
+ * Header IDs should be static, position=1 should always return the same Id that is.
*/
@Override
public long getHeaderId(int position) {
+ // similar to getView in CursorAdapter
if (!mDataValid) {
// no data available at this point
Log.d(Constants.TAG, "getHeaderView: No data available at this point!");
--
cgit v1.2.3
From bc0edfda0bae21a567b9ea340cab323f9a15a086 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Thu, 2 Jan 2014 22:45:24 +0100
Subject: fix nullpointer in EditKeyActivity
---
OpenPGP-Keychain/res/values/strings.xml | 4 ++--
.../keychain/ui/EditKeyActivity.java | 22 +++++++++++-----------
2 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/OpenPGP-Keychain/res/values/strings.xml b/OpenPGP-Keychain/res/values/strings.xml
index d900f5a71..bfc7434b2 100644
--- a/OpenPGP-Keychain/res/values/strings.xml
+++ b/OpenPGP-Keychain/res/values/strings.xml
@@ -19,8 +19,8 @@
- Manage Public Keys
- Manage Secret Keys
+ Public Keys
+ Secret KeysSelect Public KeySelect Secret KeyEncrypt
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
index 7aeb51c8f..4dcaa6d73 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
@@ -295,17 +295,6 @@ public class EditKeyActivity extends SherlockFragmentActivity {
}
private void finallyEdit(final long masterKeyId, final boolean masterCanSign) {
- // TODO: ???
- if (mCurrentPassPhrase == null) {
- mCurrentPassPhrase = "";
- }
-
- if (mCurrentPassPhrase.equals("")) {
- // check "no passphrase" checkbox and remove button
- mNoPassphrase.setChecked(true);
- mChangePassPhrase.setVisibility(View.GONE);
- }
-
if (masterKeyId != 0) {
PGPSecretKey masterKey = null;
mKeyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(this, masterKeyId);
@@ -328,6 +317,17 @@ public class EditKeyActivity extends SherlockFragmentActivity {
}
buildLayout();
+
+ // TODO: ???
+ if (mCurrentPassPhrase == null) {
+ mCurrentPassPhrase = "";
+ }
+
+ if (mCurrentPassPhrase.equals("")) {
+ // check "no passphrase" checkbox and remove button
+ mNoPassphrase.setChecked(true);
+ mChangePassPhrase.setVisibility(View.GONE);
+ }
}
/**
--
cgit v1.2.3
From 9576aeabca5453578f297d3546f2d3526eb377de Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Thu, 2 Jan 2014 23:09:10 +0100
Subject: integrate share activity in view activity
---
OpenPGP-Keychain/AndroidManifest.xml | 1 -
.../keychain/ui/KeyViewActivity.java | 69 ++++++++++++-------
.../keychain/ui/ShareActivity.java | 79 ----------------------
3 files changed, 44 insertions(+), 105 deletions(-)
delete mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ShareActivity.java
diff --git a/OpenPGP-Keychain/AndroidManifest.xml b/OpenPGP-Keychain/AndroidManifest.xml
index 8433323f4..c01f85088 100644
--- a/OpenPGP-Keychain/AndroidManifest.xml
+++ b/OpenPGP-Keychain/AndroidManifest.xml
@@ -351,7 +351,6 @@
-
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java
index c2ef64d51..4a628a3c3 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java
@@ -18,6 +18,7 @@
package org.sufficientlysecure.keychain.ui;
+import java.util.ArrayList;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -29,20 +30,21 @@ import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.ui.dialog.ShareQrCodeDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
-import android.provider.ContactsContract.DataUsageFeedback;
import android.text.format.DateFormat;
import android.widget.TextView;
import com.actionbarsherlock.app.SherlockActivity;
+import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
-public class KeyViewActivity extends SherlockActivity {
+public class KeyViewActivity extends SherlockFragmentActivity {
private Uri mDataUri;
private PGPPublicKey mPublicKey;
@@ -136,32 +138,19 @@ public class KeyViewActivity extends SherlockActivity {
return true;
case R.id.menu_key_view_export_file:
-// long masterKeyId = ProviderHelper.getPublicMasterKeyId(mKeyListActivity, keyRingRowId);
-// if (masterKeyId == -1) {
-// masterKeyId = ProviderHelper.getSecretMasterKeyId(mKeyListActivity, keyRingRowId);
-// }
-//
-// mKeyListActivity.showExportKeysDialog(masterKeyId);
+ // long masterKeyId = ProviderHelper.getPublicMasterKeyId(mKeyListActivity,
+ // keyRingRowId);
+ // if (masterKeyId == -1) {
+ // masterKeyId = ProviderHelper.getSecretMasterKeyId(mKeyListActivity, keyRingRowId);
+ // }
+ //
+ // mKeyListActivity.showExportKeysDialog(masterKeyId);
return true;
case R.id.menu_key_view_share:
- // get master key id using row id
- long masterKeyId3 = ProviderHelper.getPublicMasterKeyId(this, keyRingRowId);
-
- Intent shareIntent = new Intent(this, ShareActivity.class);
- shareIntent.setAction(ShareActivity.ACTION_SHARE_KEYRING);
- shareIntent.putExtra(ShareActivity.EXTRA_MASTER_KEY_ID, masterKeyId3);
- startActivityForResult(shareIntent, 0);
-
+ shareKey();
return true;
case R.id.menu_key_view_share_qr_code:
- // get master key id using row id
- long masterKeyId = ProviderHelper.getPublicMasterKeyId(this, keyRingRowId);
-
- Intent qrCodeIntent = new Intent(this, ShareActivity.class);
- qrCodeIntent.setAction(ShareActivity.ACTION_SHARE_KEYRING_WITH_QR_CODE);
- qrCodeIntent.putExtra(ShareActivity.EXTRA_MASTER_KEY_ID, masterKeyId);
- startActivityForResult(qrCodeIntent, 0);
-
+ shareKeyQrCode();
return true;
case R.id.menu_key_view_share_nfc:
// get master key id using row id
@@ -174,7 +163,7 @@ public class KeyViewActivity extends SherlockActivity {
return true;
case R.id.menu_key_view_delete:
-// mKeyListActivity.showDeleteKeyDialog(keyRingRowId);
+ // mKeyListActivity.showDeleteKeyDialog(keyRingRowId);
return true;
@@ -225,4 +214,34 @@ public class KeyViewActivity extends SherlockActivity {
}
return result;
}
+
+ private void shareKey() {
+ // TODO: use data uri!
+ long keyRingRowId = Long.valueOf(mDataUri.getLastPathSegment());
+ long masterKeyId = ProviderHelper.getPublicMasterKeyId(this, keyRingRowId);
+
+ // get public keyring as ascii armored string
+ ArrayList keyringArmored = ProviderHelper.getPublicKeyRingsAsArmoredString(this,
+ new long[] { masterKeyId });
+
+ // let user choose application
+ Intent sendIntent = new Intent(Intent.ACTION_SEND);
+ sendIntent.putExtra(Intent.EXTRA_TEXT, keyringArmored.get(0));
+ sendIntent.setType("text/plain");
+ startActivity(Intent.createChooser(sendIntent,
+ getResources().getText(R.string.action_share_key_with)));
+ }
+
+ private void shareKeyQrCode() {
+ // TODO: use data uri!
+ long keyRingRowId = Long.valueOf(mDataUri.getLastPathSegment());
+ long masterKeyId = ProviderHelper.getPublicMasterKeyId(this, keyRingRowId);
+ // get public keyring as ascii armored string
+ ArrayList keyringArmored = ProviderHelper.getPublicKeyRingsAsArmoredString(this,
+ new long[] { masterKeyId });
+
+ ShareQrCodeDialogFragment dialog = ShareQrCodeDialogFragment.newInstance(keyringArmored
+ .get(0));
+ dialog.show(getSupportFragmentManager(), "qrCodeShareDialog");
+ }
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ShareActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ShareActivity.java
deleted file mode 100644
index 159b2b63a..000000000
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ShareActivity.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2013 Dominik Schürmann
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package org.sufficientlysecure.keychain.ui;
-
-import java.util.ArrayList;
-
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.ui.dialog.ShareQrCodeDialogFragment;
-
-import android.content.Intent;
-import android.os.Bundle;
-
-import com.actionbarsherlock.app.SherlockFragmentActivity;
-
-public class ShareActivity extends SherlockFragmentActivity {
- // Actions for internal use only:
- public static final String ACTION_SHARE_KEYRING = Constants.INTENT_PREFIX + "SHARE_KEYRING";
- public static final String ACTION_SHARE_KEYRING_WITH_QR_CODE = Constants.INTENT_PREFIX
- + "SHARE_KEYRING_WITH_QR_CODE";
-
- public static final String EXTRA_MASTER_KEY_ID = "master_key_id";
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- handleActions(getIntent());
- }
-
- protected void handleActions(Intent intent) {
- String action = intent.getAction();
- Bundle extras = intent.getExtras();
-
- if (extras == null) {
- extras = new Bundle();
- }
-
- long masterKeyId = extras.getLong(EXTRA_MASTER_KEY_ID);
-
- // get public keyring as ascii armored string
- ArrayList keyringArmored = ProviderHelper.getPublicKeyRingsAsArmoredString(this,
- new long[] { masterKeyId });
-
- if (ACTION_SHARE_KEYRING.equals(action)) {
- // let user choose application
- Intent sendIntent = new Intent(Intent.ACTION_SEND);
- sendIntent.putExtra(Intent.EXTRA_TEXT, keyringArmored.get(0));
- sendIntent.setType("text/plain");
- startActivity(Intent.createChooser(sendIntent,
- getResources().getText(R.string.action_share_key_with)));
- } else if (ACTION_SHARE_KEYRING_WITH_QR_CODE.equals(action)) {
- ShareQrCodeDialogFragment dialog = ShareQrCodeDialogFragment.newInstance(keyringArmored
- .get(0));
- dialog.show(getSupportFragmentManager(), "qrCodeShareDialog");
- }
-
- // close this activity
- // TODO: finish() would also close dialog...
- // integrate this into new KeyViewActivity when ready
- // finish();
- }
-}
--
cgit v1.2.3
From 15be9fc99b55889a175debb57185a6a75a34b1a3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Fri, 3 Jan 2014 12:24:08 +0100
Subject: integrate NFC into key view and import activities
---
OpenPGP-Keychain/AndroidManifest.xml | 24 +--
OpenPGP-Keychain/res/menu/nfc_beam.xml | 10 -
.../keychain/ui/ImportKeysActivity.java | 49 ++++-
.../keychain/ui/KeyViewActivity.java | 117 ++++++++++-
.../keychain/ui/ShareNfcBeamActivity.java | 226 ---------------------
.../keychain/ui/dialog/ShareNfcDialogFragment.java | 101 +++++++++
6 files changed, 261 insertions(+), 266 deletions(-)
delete mode 100644 OpenPGP-Keychain/res/menu/nfc_beam.xml
delete mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ShareNfcBeamActivity.java
create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/ShareNfcDialogFragment.java
diff --git a/OpenPGP-Keychain/AndroidManifest.xml b/OpenPGP-Keychain/AndroidManifest.xml
index c01f85088..8a340708d 100644
--- a/OpenPGP-Keychain/AndroidManifest.xml
+++ b/OpenPGP-Keychain/AndroidManifest.xml
@@ -276,8 +276,17 @@
+
+
+
+
+
+
+
+
@@ -336,21 +345,6 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/OpenPGP-Keychain/res/menu/nfc_beam.xml b/OpenPGP-Keychain/res/menu/nfc_beam.xml
deleted file mode 100644
index e1c088b86..000000000
--- a/OpenPGP-Keychain/res/menu/nfc_beam.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
\ No newline at end of file
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
index 37edc8f49..f83d469ac 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
@@ -19,7 +19,6 @@ package org.sufficientlysecure.keychain.ui;
import java.util.ArrayList;
import java.util.List;
-
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.ActionBarHelper;
@@ -29,21 +28,23 @@ import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
-
+import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.nfc.NdefMessage;
+import android.nfc.NfcAdapter;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
+import android.os.Parcelable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Toast;
-
import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.app.ActionBar.OnNavigationListener;
import com.actionbarsherlock.app.SherlockFragmentActivity;
@@ -345,7 +346,8 @@ public class ImportKeysActivity extends SherlockFragmentActivity implements OnNa
int bad = returnData.getInt(KeychainIntentService.RESULT_IMPORT_BAD);
String toastMessage;
if (added > 0 && updated > 0) {
- toastMessage = getString(R.string.keys_added_and_updated, added, updated);
+ toastMessage = getString(R.string.keys_added_and_updated, added,
+ updated);
} else if (added > 0) {
toastMessage = getString(R.string.keys_added, added);
} else if (updated > 0) {
@@ -409,4 +411,43 @@ public class ImportKeysActivity extends SherlockFragmentActivity implements OnNa
.show();
}
+ /**
+ * NFC
+ */
+ @Override
+ public void onResume() {
+ super.onResume();
+ // Check to see that the Activity started due to an Android Beam
+ if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) {
+ handleActionNdefDiscovered(getIntent());
+ }
+ }
+
+ /**
+ * NFC
+ */
+ @Override
+ public void onNewIntent(Intent intent) {
+ // onResume gets called after this to handle the intent
+ setIntent(intent);
+ }
+
+ /**
+ * NFC: Parses the NDEF Message from the intent and prints to the TextView
+ */
+ @SuppressLint("NewApi")
+ void handleActionNdefDiscovered(Intent intent) {
+ Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
+ // only one message sent during the beam
+ NdefMessage msg = (NdefMessage) rawMsgs[0];
+ // record 0 contains the MIME type, record 1 is the AAR, if present
+ byte[] receivedKeyringBytes = msg.getRecords()[0].getPayload();
+
+ Intent importIntent = new Intent(this, ImportKeysActivity.class);
+ importIntent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY);
+ importIntent.putExtra(ImportKeysActivity.EXTRA_KEY_BYTES, receivedKeyringBytes);
+
+ handleActions(null, importIntent);
+ }
+
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java
index 4a628a3c3..ad63f022d 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java
@@ -1,6 +1,6 @@
/*
- * Copyright (C) 2013 Bahtiar 'kalkin' Gadimov
* Copyright (C) 2013 Dominik Schürmann
+ * Copyright (C) 2013 Bahtiar 'kalkin' Gadimov
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -30,21 +30,34 @@ import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.ui.dialog.ShareNfcDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.ShareQrCodeDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
+import android.annotation.SuppressLint;
import android.content.Intent;
import android.net.Uri;
+import android.nfc.NdefMessage;
+import android.nfc.NdefRecord;
+import android.nfc.NfcAdapter;
+import android.nfc.NfcAdapter.CreateNdefMessageCallback;
+import android.nfc.NfcAdapter.OnNdefPushCompleteCallback;
+import android.nfc.NfcEvent;
+import android.os.Build;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
import android.text.format.DateFormat;
import android.widget.TextView;
+import android.widget.Toast;
-import com.actionbarsherlock.app.SherlockActivity;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
-public class KeyViewActivity extends SherlockFragmentActivity {
+@SuppressLint("NewApi")
+public class KeyViewActivity extends SherlockFragmentActivity implements CreateNdefMessageCallback,
+ OnNdefPushCompleteCallback {
private Uri mDataUri;
private PGPPublicKey mPublicKey;
@@ -54,6 +67,11 @@ public class KeyViewActivity extends SherlockFragmentActivity {
private TextView mExpiry;
private TextView mCreation;
+ // NFC
+ private NfcAdapter mNfcAdapter;
+ private byte[] mSharedKeyringBytes;
+ private static final int NFC_SENT = 1;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -77,6 +95,7 @@ public class KeyViewActivity extends SherlockFragmentActivity {
} else {
Log.d(Constants.TAG, "uri: " + mDataUri);
loadData(mDataUri);
+ initNfc();
}
}
@@ -154,12 +173,14 @@ public class KeyViewActivity extends SherlockFragmentActivity {
return true;
case R.id.menu_key_view_share_nfc:
// get master key id using row id
- long masterKeyId2 = ProviderHelper.getPublicMasterKeyId(this, keyRingRowId);
+ // long masterKeyId2 = ProviderHelper.getPublicMasterKeyId(this, keyRingRowId);
+ //
+ // Intent nfcIntent = new Intent(this, ShareNfcBeamActivity.class);
+ // nfcIntent.setAction(ShareNfcBeamActivity.ACTION_SHARE_KEYRING_WITH_NFC);
+ // nfcIntent.putExtra(ShareNfcBeamActivity.EXTRA_MASTER_KEY_ID, masterKeyId2);
+ // startActivityForResult(nfcIntent, 0);
- Intent nfcIntent = new Intent(this, ShareNfcBeamActivity.class);
- nfcIntent.setAction(ShareNfcBeamActivity.ACTION_SHARE_KEYRING_WITH_NFC);
- nfcIntent.putExtra(ShareNfcBeamActivity.EXTRA_MASTER_KEY_ID, masterKeyId2);
- startActivityForResult(nfcIntent, 0);
+ shareNfc();
return true;
case R.id.menu_key_view_delete:
@@ -191,8 +212,10 @@ public class KeyViewActivity extends SherlockFragmentActivity {
mAlgorithm.setText(PgpKeyHelper.getAlgorithmInfo(mPublicKey));
}
+ /**
+ * TODO: does this duplicate functionality from elsewhere? put in helper!
+ */
private String[] splitUserId(String userId) {
-
String[] result = new String[] { "", "", "" };
Log.v("UserID", userId);
@@ -219,7 +242,6 @@ public class KeyViewActivity extends SherlockFragmentActivity {
// TODO: use data uri!
long keyRingRowId = Long.valueOf(mDataUri.getLastPathSegment());
long masterKeyId = ProviderHelper.getPublicMasterKeyId(this, keyRingRowId);
-
// get public keyring as ascii armored string
ArrayList keyringArmored = ProviderHelper.getPublicKeyRingsAsArmoredString(this,
new long[] { masterKeyId });
@@ -242,6 +264,79 @@ public class KeyViewActivity extends SherlockFragmentActivity {
ShareQrCodeDialogFragment dialog = ShareQrCodeDialogFragment.newInstance(keyringArmored
.get(0));
- dialog.show(getSupportFragmentManager(), "qrCodeShareDialog");
+ dialog.show(getSupportFragmentManager(), "shareQrCodeDialog");
+ }
+
+ private void shareNfc() {
+ ShareNfcDialogFragment dialog = ShareNfcDialogFragment.newInstance();
+ dialog.show(getSupportFragmentManager(), "shareNfcDialog");
+ }
+
+ /**
+ * NFC: Initialize NFC sharing if OS and device supports it
+ */
+ private void initNfc() {
+ // check if NFC Beam is supported (>= Android 4.1)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ // Check for available NFC Adapter
+ mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
+ if (mNfcAdapter != null) {
+ // init nfc
+ // TODO: use data uri!
+ long keyRingRowId = Long.valueOf(mDataUri.getLastPathSegment());
+ long masterKeyId = ProviderHelper.getPublicMasterKeyId(this, keyRingRowId);
+
+ // get public keyring as byte array
+ mSharedKeyringBytes = ProviderHelper.getPublicKeyRingsAsByteArray(this,
+ new long[] { masterKeyId });
+
+ // Register callback to set NDEF message
+ mNfcAdapter.setNdefPushMessageCallback(this, this);
+ // Register callback to listen for message-sent success
+ mNfcAdapter.setOnNdefPushCompleteCallback(this, this);
+ }
+ }
+ }
+
+ /**
+ * NFC: Implementation for the CreateNdefMessageCallback interface
+ */
+ @Override
+ public NdefMessage createNdefMessage(NfcEvent event) {
+ /**
+ * When a device receives a push with an AAR in it, the application specified in the AAR is
+ * guaranteed to run. The AAR overrides the tag dispatch system. You can add it back in to
+ * guarantee that this activity starts when receiving a beamed message. For now, this code
+ * uses the tag dispatch system.
+ */
+ NdefMessage msg = new NdefMessage(NdefRecord.createMime(Constants.NFC_MIME,
+ mSharedKeyringBytes), NdefRecord.createApplicationRecord(Constants.PACKAGE_NAME));
+ return msg;
}
+
+ /**
+ * NFC: Implementation for the OnNdefPushCompleteCallback interface
+ */
+ @Override
+ public void onNdefPushComplete(NfcEvent arg0) {
+ // A handler is needed to send messages to the activity when this
+ // callback occurs, because it happens from a binder thread
+ mNfcHandler.obtainMessage(NFC_SENT).sendToTarget();
+ }
+
+ /**
+ * NFC: This handler receives a message from onNdefPushComplete
+ */
+ private final Handler mNfcHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case NFC_SENT:
+ Toast.makeText(getApplicationContext(), R.string.nfc_successfull, Toast.LENGTH_LONG)
+ .show();
+ break;
+ }
+ }
+ };
+
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ShareNfcBeamActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ShareNfcBeamActivity.java
deleted file mode 100644
index db6a156c7..000000000
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ShareNfcBeamActivity.java
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * Copyright (C) 2013 Dominik Schürmann
- * 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;
-
-import org.sufficientlysecure.htmltextview.HtmlTextView;
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.helper.ActionBarHelper;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-
-import android.annotation.TargetApi;
-import android.content.Intent;
-import android.nfc.NdefMessage;
-import android.nfc.NdefRecord;
-import android.nfc.NfcAdapter;
-import android.nfc.NfcAdapter.CreateNdefMessageCallback;
-import android.nfc.NfcAdapter.OnNdefPushCompleteCallback;
-import android.nfc.NfcEvent;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Parcelable;
-import android.provider.Settings;
-import android.widget.Toast;
-
-import com.actionbarsherlock.app.SherlockFragmentActivity;
-import com.actionbarsherlock.view.Menu;
-import com.actionbarsherlock.view.MenuInflater;
-import com.actionbarsherlock.view.MenuItem;
-
-@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
-public class ShareNfcBeamActivity extends SherlockFragmentActivity implements
- CreateNdefMessageCallback, OnNdefPushCompleteCallback {
- public static final String ACTION_SHARE_KEYRING_WITH_NFC = Constants.INTENT_PREFIX
- + "SHARE_KEYRING_WITH_NFC";
-
- public static final String EXTRA_MASTER_KEY_ID = "master_key_id";
-
- NfcAdapter mNfcAdapter;
-
- byte[] mSharedKeyringBytes;
-
- private static final int MESSAGE_SENT = 1;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
- Toast.makeText(this,
- getString(R.string.error) + ": " + getString(R.string.error_jelly_bean_needed),
- Toast.LENGTH_LONG).show();
- finish();
- } else {
- // Check for available NFC Adapter
- mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
- if (mNfcAdapter == null) {
- Toast.makeText(this,
- getString(R.string.error) + ": " + getString(R.string.error_nfc_needed),
- Toast.LENGTH_LONG).show();
- finish();
- } else {
- // handle actions after verifying that nfc works...
- handleActions(getIntent());
- }
- }
- }
-
- protected void handleActions(Intent intent) {
- String action = intent.getAction();
- Bundle extras = intent.getExtras();
-
- if (extras == null) {
- extras = new Bundle();
- }
-
- if (ACTION_SHARE_KEYRING_WITH_NFC.equals(action)) {
- long masterKeyId = extras.getLong(EXTRA_MASTER_KEY_ID);
-
- // get public keyring as byte array
- mSharedKeyringBytes = ProviderHelper.getPublicKeyRingsAsByteArray(this,
- new long[] { masterKeyId });
-
- // Register callback to set NDEF message
- mNfcAdapter.setNdefPushMessageCallback(this, this);
- // Register callback to listen for message-sent success
- mNfcAdapter.setOnNdefPushCompleteCallback(this, this);
- }
- }
-
- /**
- * Parses the NDEF Message from the intent and prints to the TextView
- */
- void handleActionNdefDiscovered(Intent intent) {
- Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
- // only one message sent during the beam
- NdefMessage msg = (NdefMessage) rawMsgs[0];
- // record 0 contains the MIME type, record 1 is the AAR, if present
- byte[] receivedKeyringBytes = msg.getRecords()[0].getPayload();
-
- Intent importIntent = new Intent(this, ImportKeysActivity.class);
- importIntent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY);
- importIntent.putExtra(ImportKeysActivity.EXTRA_KEY_BYTES, receivedKeyringBytes);
-
- finish();
-
- startActivity(importIntent);
- }
-
- private void buildView() {
- setContentView(R.layout.share_nfc_beam);
-
- HtmlTextView aboutTextView = (HtmlTextView) findViewById(R.id.nfc_beam_text);
-
- // load html from raw resource (Parsing handled by HtmlTextView library)
- aboutTextView.setHtmlFromRawResource(this, R.raw.nfc_beam_share);
-
- // no flickering when clicking textview for Android < 4
- aboutTextView.setTextColor(getResources().getColor(android.R.color.black));
-
- // set actionbar without home button if called from another app
- ActionBarHelper.setBackButton(this);
- }
-
- /**
- * Implementation for the CreateNdefMessageCallback interface
- */
- @Override
- public NdefMessage createNdefMessage(NfcEvent event) {
- /**
- * When a device receives a push with an AAR in it, the application specified in the AAR is
- * guaranteed to run. The AAR overrides the tag dispatch system. You can add it back in to
- * guarantee that this activity starts when receiving a beamed message. For now, this code
- * uses the tag dispatch system.
- */
- NdefMessage msg = new NdefMessage(NdefRecord.createMime(Constants.NFC_MIME,
- mSharedKeyringBytes), NdefRecord.createApplicationRecord(Constants.PACKAGE_NAME));
- return msg;
- }
-
- /**
- * Implementation for the OnNdefPushCompleteCallback interface
- */
- @Override
- public void onNdefPushComplete(NfcEvent arg0) {
- // A handler is needed to send messages to the activity when this
- // callback occurs, because it happens from a binder thread
- mHandler.obtainMessage(MESSAGE_SENT).sendToTarget();
- }
-
- /** This handler receives a message from onNdefPushComplete */
- private final Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MESSAGE_SENT:
- Toast.makeText(getApplicationContext(), R.string.nfc_successfull, Toast.LENGTH_LONG)
- .show();
- break;
- }
- }
- };
-
- @Override
- public void onResume() {
- super.onResume();
- // Check to see that the Activity started due to an Android Beam
- if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) {
- handleActionNdefDiscovered(getIntent());
- } else {
- // build view only when sending nfc, not when receiving, as it gets directly into Import
- // activity on receiving
- buildView();
- }
- }
-
- @Override
- public void onNewIntent(Intent intent) {
- // onResume gets called after this to handle the intent
- setIntent(intent);
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- MenuInflater inflater = getSupportMenuInflater();
- inflater.inflate(R.menu.nfc_beam, menu);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
-
- case android.R.id.home:
- // app icon in Action Bar clicked; go to KeyListPublicActivity
- Intent intent = new Intent(this, KeyListPublicActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(intent);
- return true;
-
- case R.id.menu_settings:
- Intent intentSettings = new Intent(Settings.ACTION_NFCSHARING_SETTINGS);
- startActivity(intentSettings);
- return true;
-
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/ShareNfcDialogFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/ShareNfcDialogFragment.java
new file mode 100644
index 000000000..1a19390ad
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/ShareNfcDialogFragment.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2013 Dominik Schürmann
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package org.sufficientlysecure.keychain.ui.dialog;
+
+import org.sufficientlysecure.htmltextview.HtmlTextView;
+import org.sufficientlysecure.keychain.R;
+
+import android.annotation.TargetApi;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.nfc.NfcAdapter;
+import android.os.Build;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.support.v4.app.DialogFragment;
+import android.support.v4.app.FragmentActivity;
+
+@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+public class ShareNfcDialogFragment extends DialogFragment {
+
+ /**
+ * Creates new instance of this fragment
+ */
+ public static ShareNfcDialogFragment newInstance() {
+ ShareNfcDialogFragment frag = new ShareNfcDialogFragment();
+
+ return frag;
+ }
+
+ /**
+ * Creates dialog
+ */
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final FragmentActivity activity = getActivity();
+
+ AlertDialog.Builder alert = new AlertDialog.Builder(activity);
+
+ alert.setIcon(android.R.drawable.ic_dialog_alert);
+ alert.setTitle(R.string.warning);
+ alert.setCancelable(true);
+
+ alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ dismiss();
+ }
+ });
+
+ HtmlTextView textView = new HtmlTextView(getActivity());
+ textView.setPadding(8, 8, 8, 8);
+ alert.setView(textView);
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
+ textView.setText(getString(R.string.error) + ": "
+ + getString(R.string.error_jelly_bean_needed));
+ } else {
+ // check if NFC Adapter is available
+ NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity());
+ if (nfcAdapter == null) {
+ textView.setText(getString(R.string.error) + ": "
+ + getString(R.string.error_nfc_needed));
+ } else {
+ // nfc works...
+ textView.setHtmlFromRawResource(getActivity(), R.raw.nfc_beam_share);
+
+ alert.setNegativeButton(R.string.menu_beam_preferences,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ Intent intentSettings = new Intent(
+ Settings.ACTION_NFCSHARING_SETTINGS);
+ startActivity(intentSettings);
+ }
+ });
+ }
+ }
+
+ // no flickering when clicking textview for Android < 4
+ // aboutTextView.setTextColor(getResources().getColor(android.R.color.black));
+
+ return alert.create();
+ }
+}
\ No newline at end of file
--
cgit v1.2.3
From 606caee145bfdb706a23e29b6b0434b66519a47d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Fri, 3 Jan 2014 12:28:03 +0100
Subject: prettify nfc dialog
---
OpenPGP-Keychain/res/values/strings.xml | 4 +++-
.../sufficientlysecure/keychain/ui/dialog/ShareNfcDialogFragment.java | 4 ++--
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/OpenPGP-Keychain/res/values/strings.xml b/OpenPGP-Keychain/res/values/strings.xml
index bfc7434b2..0b38af7fc 100644
--- a/OpenPGP-Keychain/res/values/strings.xml
+++ b/OpenPGP-Keychain/res/values/strings.xml
@@ -346,4 +346,6 @@
Go through all QR Codes using \'Next\', and scan them one by one.QR Code %1$d of %2$d
-
+ Share with NFC
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/ShareNfcDialogFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/ShareNfcDialogFragment.java
index 1a19390ad..03e09cdcb 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/ShareNfcDialogFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/ShareNfcDialogFragment.java
@@ -53,8 +53,8 @@ public class ShareNfcDialogFragment extends DialogFragment {
AlertDialog.Builder alert = new AlertDialog.Builder(activity);
- alert.setIcon(android.R.drawable.ic_dialog_alert);
- alert.setTitle(R.string.warning);
+ alert.setIcon(android.R.drawable.ic_dialog_info);
+ alert.setTitle(R.string.share_nfc_dialog);
alert.setCancelable(true);
alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
--
cgit v1.2.3
From 0e4cfed9691118080506d55270842e2ba127c878 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Fri, 3 Jan 2014 18:07:42 +0100
Subject: cleanup and documentation
---
.../keychain/ui/KeyListPublicFragment.java | 44 +++++++++-------------
.../keychain/ui/adapter/KeyListPublicAdapter.java | 29 +++++++-------
2 files changed, 32 insertions(+), 41 deletions(-)
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
index 7bc0bcd96..27461875a 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
@@ -42,14 +42,8 @@ import android.widget.AdapterView;
import com.actionbarsherlock.app.SherlockFragment;
/**
- * Public key list with sticky list headers.
- *
- * - uses StickyListHeaders library
- *
- * - custom adapter: KeyListPublicAdapter
- *
- * TODO: - fix view holder in adapter, fix loader
- *
+ * Public key list with sticky list headers. It does _not_ extend ListFragment because it uses
+ * StickyListHeaders library which does not extend upon ListView.
*/
public class KeyListPublicFragment extends SherlockFragment implements
AdapterView.OnItemClickListener, LoaderManager.LoaderCallbacks {
@@ -79,9 +73,6 @@ public class KeyListPublicFragment extends SherlockFragment implements
mStickyList = (StickyListHeadersListView) getActivity().findViewById(R.id.list);
mStickyList.setOnItemClickListener(this);
- // mStickyList.addHeaderView(inflater.inflate(R.layout.list_header, null));
- // mStickyList.addFooterView(inflater.inflate(R.layout.list_footer, null));
- mStickyList.setEmptyView(getActivity().findViewById(R.id.empty));
mStickyList.setAreHeadersSticky(true);
mStickyList.setDrawingListUnderStickyHeader(false);
mStickyList.setFastScrollEnabled(true);
@@ -90,17 +81,17 @@ public class KeyListPublicFragment extends SherlockFragment implements
} catch (ApiLevelTooLowException e) {
}
- // Give some text to display if there is no data. In a real
- // application this would come from a resource.
- // setEmptyText(getString(R.string.list_empty));
+ // this view is made visible if no data is available
+ mStickyList.setEmptyView(getActivity().findViewById(R.id.empty));
+ // NOTE: Not supported by StickyListHeader, thus no indicator is shown while loading
// Start out with a progress indicator.
// setListShown(false);
// Create an empty adapter we will use to display the loaded data.
- // mAdapter = new KeyListPublicAdapter(mKeyListPublicActivity, null, Id.type.public_key);
- // setListAdapter(mAdapter);
- // stickyList.setAdapter(mAdapter);
+ mAdapter = new KeyListPublicAdapter(mKeyListPublicActivity, null, Id.type.public_key,
+ USER_ID_INDEX);
+ mStickyList.setAdapter(mAdapter);
// Prepare the loader. Either re-connect with an existing one,
// or start a new one.
@@ -111,6 +102,8 @@ public class KeyListPublicFragment extends SherlockFragment implements
static final String[] PROJECTION = new String[] { KeyRings._ID, KeyRings.MASTER_KEY_ID,
UserIds.USER_ID };
+ static final int USER_ID_INDEX = 2;
+
static final String SORT_ORDER = UserIds.USER_ID + " ASC";
@Override
@@ -128,20 +121,17 @@ public class KeyListPublicFragment extends SherlockFragment implements
public void onLoadFinished(Loader loader, Cursor data) {
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
- // mAdapter.swapCursor(data);
- int userIdIndex = data.getColumnIndex(UserIds.USER_ID);
-
- mAdapter = new KeyListPublicAdapter(mKeyListPublicActivity, data, Id.type.public_key,
- userIdIndex);
+ mAdapter.swapCursor(data);
mStickyList.setAdapter(mAdapter);
+ // NOTE: Not supported by StickyListHeader, thus no indicator is shown while loading
// The list should now be shown.
- if (isResumed()) {
- // setListShown(true);
- } else {
- // setListShownNoAnimation(true);
- }
+ // if (isResumed()) {
+ // setListShown(true);
+ // } else {
+ // setListShownNoAnimation(true);
+ // }
}
@Override
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java
index ef3a0d228..83c46021c 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java
@@ -33,13 +33,11 @@ import android.view.ViewGroup;
import android.widget.TextView;
/**
- * - implements StickyListHeadersAdapter from library - uses view holder pattern for performance
- *
+ * Implements StickyListHeadersAdapter from library
*/
public class KeyListPublicAdapter extends CursorAdapter implements StickyListHeadersAdapter {
private LayoutInflater mInflater;
-
- int mSectionColumnIndex;
+ private int mSectionColumnIndex;
public KeyListPublicAdapter(Context context, Cursor c, int flags, int sectionColumnIndex) {
super(context, c, flags);
@@ -48,9 +46,14 @@ public class KeyListPublicAdapter extends CursorAdapter implements StickyListHea
mSectionColumnIndex = sectionColumnIndex;
}
+ /**
+ * Bind cursor data to the item list view
+ *
+ * NOTE: CursorAdapter already implements the ViewHolder pattern in its getView() method. Thus
+ * no ViewHolder is required here.
+ */
@Override
public void bindView(View view, Context context, Cursor cursor) {
- // TODO: view holder pattern?
int userIdIndex = cursor.getColumnIndex(UserIds.USER_ID);
TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
@@ -84,6 +87,13 @@ public class KeyListPublicAdapter extends CursorAdapter implements StickyListHea
return mInflater.inflate(R.layout.key_list_item, null);
}
+ /**
+ * Creates a new header view and binds the section headers to it. It uses the ViewHolder
+ * pattern. Most functionality is similar to getView() from Android's CursorAdapter.
+ *
+ * NOTE: The variables mDataValid and mCursor are available due to the super class
+ * CursorAdapter.
+ */
@Override
public View getHeaderView(int position, View convertView, ViewGroup parent) {
HeaderViewHolder holder;
@@ -96,14 +106,12 @@ public class KeyListPublicAdapter extends CursorAdapter implements StickyListHea
holder = (HeaderViewHolder) convertView.getTag();
}
- // similar to getView in CursorAdapter
if (!mDataValid) {
// no data available at this point
Log.d(Constants.TAG, "getHeaderView: No data available at this point!");
return convertView;
}
- // similar to getView in CursorAdapter
if (!mCursor.moveToPosition(position)) {
throw new IllegalStateException("couldn't move cursor to position " + position);
}
@@ -119,14 +127,12 @@ public class KeyListPublicAdapter extends CursorAdapter implements StickyListHea
*/
@Override
public long getHeaderId(int position) {
- // similar to getView in CursorAdapter
if (!mDataValid) {
// no data available at this point
Log.d(Constants.TAG, "getHeaderView: No data available at this point!");
return -1;
}
- // similar to getView in CursorAdapter
if (!mCursor.moveToPosition(position)) {
throw new IllegalStateException("couldn't move cursor to position " + position);
}
@@ -140,9 +146,4 @@ public class KeyListPublicAdapter extends CursorAdapter implements StickyListHea
TextView text;
}
- class ViewHolder {
- TextView mainUserId;
- TextView mainUserIdRest;
- }
-
}
--
cgit v1.2.3
From 28d9a2f26b71158823d73451bf25e1f5cf0c5431 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Fri, 3 Jan 2014 18:33:44 +0100
Subject: reorder menu in key view
---
OpenPGP-Keychain/res/menu/key_view.xml | 60 ++++++++++++++--------
.../keychain/ui/KeyViewActivity.java | 2 +-
2 files changed, 40 insertions(+), 22 deletions(-)
diff --git a/OpenPGP-Keychain/res/menu/key_view.xml b/OpenPGP-Keychain/res/menu/key_view.xml
index ac5c96c5d..0785abd32 100644
--- a/OpenPGP-Keychain/res/menu/key_view.xml
+++ b/OpenPGP-Keychain/res/menu/key_view.xml
@@ -2,37 +2,55 @@
+
+
+
+
+
+
-
-
-
-
-
+ android:title="@string/menu_delete_key">
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java
index ad63f022d..45c79fc8e 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java
@@ -165,7 +165,7 @@ public class KeyViewActivity extends SherlockFragmentActivity implements CreateN
//
// mKeyListActivity.showExportKeysDialog(masterKeyId);
return true;
- case R.id.menu_key_view_share:
+ case R.id.menu_key_view_share_default:
shareKey();
return true;
case R.id.menu_key_view_share_qr_code:
--
cgit v1.2.3
From 708baaa68c76391a3e15cd086d3829eb058819c9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Mon, 6 Jan 2014 00:58:04 +0100
Subject: Work on multiselect in key list
---
OpenPGP-Keychain/res/values/strings.xml | 6 +
.../keychain/ui/KeyListActivity.java | 6 +-
.../keychain/ui/KeyListPublicFragment.java | 117 ++++++++-
.../keychain/ui/adapter/KeyListAdapter.java | 273 ---------------------
.../keychain/ui/adapter/KeyListPublicAdapter.java | 53 +++-
.../keychain/ui/adapter/KeyListSecretAdapter.java | 5 -
.../ui/dialog/DeleteKeyDialogFragment.java | 52 ++--
README.md | 4 +-
8 files changed, 206 insertions(+), 310 deletions(-)
delete mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListAdapter.java
diff --git a/OpenPGP-Keychain/res/values/strings.xml b/OpenPGP-Keychain/res/values/strings.xml
index 0b38af7fc..6ab668934 100644
--- a/OpenPGP-Keychain/res/values/strings.xml
+++ b/OpenPGP-Keychain/res/values/strings.xml
@@ -194,6 +194,7 @@
Please specify which file to export to.\nWARNING! File will be overwritten if it exists.Please specify which file to export to.\nWARNING! You are about to export SECRET keys.\nWARNING! File will be overwritten if it exists.Do you really want to delete the key \'%s\'?\nYou can\'t undo this!
+ Do you really want to delete all selected keys?\nYou can\'t undo this!Do you really want to delete the SECRET key \'%s\'?\nYou can\'t undo this!Successfully added %1$s key(s) and updated %2$s key(s).Successfully added %s key(s).
@@ -348,4 +349,9 @@
QR Code %1$d of %2$dShare with NFC
+
+ 1 key selected.
+ %d keys selected.
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListActivity.java
index 4d1849029..5b69f9218 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListActivity.java
@@ -225,9 +225,9 @@ public class KeyListActivity extends SherlockFragmentActivity {
/**
* Show dialog to delete key
*
- * @param keyRingId
+ * @param keyRingIds
*/
- public void showDeleteKeyDialog(long keyRingId) {
+ public void showDeleteKeyDialog(long[] keyRingIds) {
// Message is received after key is deleted
Handler returnHandler = new Handler() {
@Override
@@ -242,7 +242,7 @@ public class KeyListActivity extends SherlockFragmentActivity {
Messenger messenger = new Messenger(returnHandler);
DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger,
- keyRingId, mKeyType);
+ keyRingIds, mKeyType);
deleteKeyDialog.show(getSupportFragmentManager(), "deleteKeyDialog");
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
index 27461875a..ed8427fd0 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
@@ -17,12 +17,16 @@
package org.sufficientlysecure.keychain.ui;
+import java.util.ArrayList;
+import java.util.Set;
+
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.ui.adapter.KeyListPublicAdapter;
+import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
import se.emilsjolander.stickylistheaders.ApiLevelTooLowException;
import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
@@ -30,23 +34,32 @@ import android.annotation.SuppressLint;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
+import android.provider.BaseColumns;
+import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
+import android.view.ActionMode;
import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.AbsListView.MultiChoiceModeListener;
import android.widget.AdapterView;
-
-import com.actionbarsherlock.app.SherlockFragment;
+import android.widget.ListView;
/**
* Public key list with sticky list headers. It does _not_ extend ListFragment because it uses
* StickyListHeaders library which does not extend upon ListView.
*/
-public class KeyListPublicFragment extends SherlockFragment implements
- AdapterView.OnItemClickListener, LoaderManager.LoaderCallbacks {
+public class KeyListPublicFragment extends Fragment implements AdapterView.OnItemClickListener,
+ LoaderManager.LoaderCallbacks {
private KeyListPublicActivity mKeyListPublicActivity;
private KeyListPublicAdapter mAdapter;
@@ -84,6 +97,73 @@ public class KeyListPublicFragment extends SherlockFragment implements
// this view is made visible if no data is available
mStickyList.setEmptyView(getActivity().findViewById(R.id.empty));
+ /*
+ * ActionBarSherlock does not support MultiChoiceModeListener. Thus multi-selection is only
+ * available for Android >= 3.0
+ */
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ mStickyList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
+ mStickyList.getWrappedList().setMultiChoiceModeListener(new MultiChoiceModeListener() {
+
+ private int count = 0;
+
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ android.view.MenuInflater inflater = getActivity().getMenuInflater();
+ inflater.inflate(R.menu.key_list_multi_selection, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ return false;
+ }
+
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ // StringBuilder sb = new StringBuilder();
+ Set positions = mAdapter.getCurrentCheckedPosition();
+ // for (Integer pos : positions) {
+ // sb.append(" " + pos + ",");
+ // }
+ switch (item.getItemId()) {
+ case R.id.delete_entry:
+ long[] ids = new long[positions.size()];
+ for (int i=0; i < positions.size(); i++) {
+ ids[i] = mAdapter.getItemId(positions.);
+ }
+ showDeleteKeyDialog(ids.to);
+
+ break;
+ }
+ return false;
+ }
+
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {
+ count = 0;
+ mAdapter.clearSelection();
+ }
+
+ @Override
+ public void onItemCheckedStateChanged(ActionMode mode, int position, long id,
+ boolean checked) {
+ if (checked) {
+ count++;
+ mAdapter.setNewSelection(position, checked);
+ } else {
+ count--;
+ mAdapter.removeSelection(position);
+ }
+
+ String keysSelected = getResources().getQuantityString(
+ R.plurals.key_list_selected_keys, count, count);
+ mode.setTitle(keysSelected);
+ }
+
+ });
+ }
+
// NOTE: Not supported by StickyListHeader, thus no indicator is shown while loading
// Start out with a progress indicator.
// setListShown(false);
@@ -152,4 +232,31 @@ public class KeyListPublicFragment extends SherlockFragment implements
startActivity(detailsIntent);
}
-}
+ /**
+ * Show dialog to delete key
+ *
+ * TODO: no messenger needed etc!
+ *
+ * @param keyRingRowIds
+ */
+ public void showDeleteKeyDialog(long[] keyRingRowIds) {
+ // Message is received after key is deleted
+ Handler returnHandler = new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) {
+ // no further actions needed
+ }
+ }
+ };
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(returnHandler);
+
+ DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger,
+ keyRingRowIds, Id.type.public_key);
+
+ deleteKeyDialog.show(getActivity().getSupportFragmentManager(), "deleteKeyDialog");
+ }
+
+}
\ No newline at end of file
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListAdapter.java
deleted file mode 100644
index 4719d1d1c..000000000
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListAdapter.java
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * Copyright (C) 2012-2013 Dominik Schürmann
- *
- * 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.adapter;
-
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.Id;
-import org.sufficientlysecure.keychain.helper.OtherHelper;
-import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
-import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
-import org.sufficientlysecure.keychain.util.Log;
-import org.sufficientlysecure.keychain.R;
-
-import android.content.Context;
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.database.MergeCursor;
-import android.net.Uri;
-import android.provider.BaseColumns;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.CursorTreeAdapter;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-public class KeyListAdapter extends CursorTreeAdapter {
- private Context mContext;
- private LayoutInflater mInflater;
-
- protected int mKeyType;
-
- private static final int CHILD_KEY = 0;
- private static final int CHILD_USER_ID = 1;
- private static final int CHILD_FINGERPRINT = 2;
-
- public KeyListAdapter(Context context, Cursor groupCursor, int keyType) {
- super(groupCursor, context);
- mContext = context;
- mInflater = LayoutInflater.from(context);
- mKeyType = keyType;
- }
-
- /**
- * Inflate new view for group items
- */
- @Override
- public View newGroupView(Context context, Cursor cursor, boolean isExpanded, ViewGroup parent) {
- return mInflater.inflate(R.layout.key_list_item, null);
- }
-
- /**
- * Binds TextViews from group view to results from database group cursor.
- */
- @Override
- protected void bindGroupView(View view, Context context, Cursor cursor, boolean isExpanded) {
- int userIdIndex = cursor.getColumnIndex(UserIds.USER_ID);
-
- TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
- mainUserId.setText(R.string.unknown_user_id);
- TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
- mainUserIdRest.setText("");
-
- String userId = cursor.getString(userIdIndex);
- if (userId != null) {
- String[] userIdSplit = OtherHelper.splitUserId(userId);
-
- if (userIdSplit[1] != null) {
- mainUserIdRest.setText(userIdSplit[1]);
- }
- mainUserId.setText(userIdSplit[0]);
- }
-
- if (mainUserId.getText().length() == 0) {
- mainUserId.setText(R.string.unknown_user_id);
- }
-
- if (mainUserIdRest.getText().length() == 0) {
- mainUserIdRest.setVisibility(View.GONE);
- } else {
- mainUserIdRest.setVisibility(View.VISIBLE);
- }
- }
-
- /**
- * Inflate new view for child items
- */
- @Override
- public View newChildView(Context context, Cursor cursor, boolean isLastChild, ViewGroup parent) {
- return mInflater.inflate(R.layout.key_list_child_item, null);
- }
-
- /**
- * Bind TextViews from view of childs based on query results
- */
- @Override
- protected void bindChildView(View view, Context context, Cursor cursor, boolean isLastChild) {
- LinearLayout keyLayout = (LinearLayout) view.findViewById(R.id.keyLayout);
- LinearLayout userIdLayout = (LinearLayout) view.findViewById(R.id.userIdLayout);
-
- // first entry is fingerprint
- if (cursor.getPosition() == 0) {
- // show only userId layout
- keyLayout.setVisibility(View.GONE);
- userIdLayout.setVisibility(View.VISIBLE);
-
- String fingerprint = PgpKeyHelper.getFingerPrint(context,
- cursor.getLong(cursor.getColumnIndex(Keys.KEY_ID)));
- fingerprint = fingerprint.replace(" ", "\n");
-
- TextView userId = (TextView) view.findViewById(R.id.userId);
- if (userId == null) {
- Log.d(Constants.TAG, "userId is null!");
- }
- userId.setText(context.getString(R.string.fingerprint) + "\n" + fingerprint);
- } else {
- // differentiate between keys and userIds in MergeCursor
- if (cursor.getColumnIndex(Keys.KEY_ID) != -1) {
- keyLayout.setVisibility(View.VISIBLE);
- userIdLayout.setVisibility(View.GONE);
-
- String keyIdStr = PgpKeyHelper.convertKeyIdToHex(cursor.getLong(cursor
- .getColumnIndex(Keys.KEY_ID)));
- String algorithmStr = PgpKeyHelper.getAlgorithmInfo(
- cursor.getInt(cursor.getColumnIndex(Keys.ALGORITHM)),
- cursor.getInt(cursor.getColumnIndex(Keys.KEY_SIZE)));
-
- TextView keyId = (TextView) view.findViewById(R.id.keyId);
- keyId.setText(keyIdStr);
-
- TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails);
- keyDetails.setText("(" + algorithmStr + ")");
-
- ImageView masterKeyIcon = (ImageView) view.findViewById(R.id.ic_masterKey);
- if (cursor.getInt(cursor.getColumnIndex(Keys.IS_MASTER_KEY)) != 1) {
- masterKeyIcon.setVisibility(View.INVISIBLE);
- } else {
- masterKeyIcon.setVisibility(View.VISIBLE);
- }
-
- ImageView certifyIcon = (ImageView) view.findViewById(R.id.ic_certifyKey);
- if (cursor.getInt(cursor.getColumnIndex(Keys.CAN_CERTIFY)) != 1) {
- certifyIcon.setVisibility(View.GONE);
- } else {
- certifyIcon.setVisibility(View.VISIBLE);
- }
-
- ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey);
- if (cursor.getInt(cursor.getColumnIndex(Keys.CAN_ENCRYPT)) != 1) {
- encryptIcon.setVisibility(View.GONE);
- } else {
- encryptIcon.setVisibility(View.VISIBLE);
- }
-
- ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey);
- if (cursor.getInt(cursor.getColumnIndex(Keys.CAN_SIGN)) != 1) {
- signIcon.setVisibility(View.GONE);
- } else {
- signIcon.setVisibility(View.VISIBLE);
- }
- } else {
- keyLayout.setVisibility(View.GONE);
- userIdLayout.setVisibility(View.VISIBLE);
-
- String userIdStr = cursor.getString(cursor.getColumnIndex(UserIds.USER_ID));
-
- TextView userId = (TextView) view.findViewById(R.id.userId);
- userId.setText(userIdStr);
- }
- }
- }
-
- /**
- * Given the group cursor, we start cursors for a fingerprint, keys, and userIds, which are
- * merged together and build the child cursor
- */
- @Override
- protected Cursor getChildrenCursor(Cursor groupCursor) {
- final long keyRingRowId = groupCursor.getLong(groupCursor.getColumnIndex(BaseColumns._ID));
-
- Cursor fingerprintCursor = getChildCursor(keyRingRowId, CHILD_FINGERPRINT);
- Cursor keyCursor = getChildCursor(keyRingRowId, CHILD_KEY);
- Cursor userIdCursor = getChildCursor(keyRingRowId, CHILD_USER_ID);
-
- MergeCursor mergeCursor = new MergeCursor(new Cursor[] { fingerprintCursor, keyCursor,
- userIdCursor });
- Log.d(Constants.TAG, "mergeCursor:" + DatabaseUtils.dumpCursorToString(mergeCursor));
-
- return mergeCursor;
- }
-
- /**
- * This builds a cursor for a specific type of children
- *
- * @param keyRingRowId
- * foreign row id of the keyRing
- * @param type
- * @return
- */
- private Cursor getChildCursor(long keyRingRowId, int type) {
- Uri uri = null;
- String[] projection = null;
- String sortOrder = null;
- String selection = null;
-
- switch (type) {
- case CHILD_FINGERPRINT:
- projection = new String[] { Keys._ID, Keys.KEY_ID, Keys.IS_MASTER_KEY, Keys.ALGORITHM,
- Keys.KEY_SIZE, Keys.CAN_CERTIFY, Keys.CAN_SIGN, Keys.CAN_ENCRYPT, };
- sortOrder = Keys.RANK + " ASC";
-
- // use only master key for fingerprint
- selection = Keys.IS_MASTER_KEY + " = 1 ";
-
- if (mKeyType == Id.type.public_key) {
- uri = Keys.buildPublicKeysUri(String.valueOf(keyRingRowId));
- } else {
- uri = Keys.buildSecretKeysUri(String.valueOf(keyRingRowId));
- }
- break;
-
- case CHILD_KEY:
- projection = new String[] { Keys._ID, Keys.KEY_ID, Keys.IS_MASTER_KEY, Keys.ALGORITHM,
- Keys.KEY_SIZE, Keys.CAN_CERTIFY, Keys.CAN_SIGN, Keys.CAN_ENCRYPT, };
- sortOrder = Keys.RANK + " ASC";
-
- if (mKeyType == Id.type.public_key) {
- uri = Keys.buildPublicKeysUri(String.valueOf(keyRingRowId));
- } else {
- uri = Keys.buildSecretKeysUri(String.valueOf(keyRingRowId));
- }
-
- break;
-
- case CHILD_USER_ID:
- projection = new String[] { UserIds._ID, UserIds.USER_ID, UserIds.RANK, };
- sortOrder = UserIds.RANK + " ASC";
-
- // not the main user id
- selection = UserIds.RANK + " > 0 ";
-
- if (mKeyType == Id.type.public_key) {
- uri = UserIds.buildPublicUserIdsUri(String.valueOf(keyRingRowId));
- } else {
- uri = UserIds.buildSecretUserIdsUri(String.valueOf(keyRingRowId));
- }
-
- break;
-
- default:
- return null;
-
- }
-
- return mContext.getContentResolver().query(uri, projection, selection, null, sortOrder);
- }
-
-}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java
index 83c46021c..5f71c9805 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java
@@ -17,15 +17,18 @@
package org.sufficientlysecure.keychain.ui.adapter;
+import java.util.HashMap;
+import java.util.Set;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.OtherHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.util.Log;
-
import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.database.Cursor;
+import android.graphics.Color;
import android.support.v4.widget.CursorAdapter;
import android.view.LayoutInflater;
import android.view.View;
@@ -39,6 +42,9 @@ public class KeyListPublicAdapter extends CursorAdapter implements StickyListHea
private LayoutInflater mInflater;
private int mSectionColumnIndex;
+ @SuppressLint("UseSparseArrays")
+ private HashMap mSelection = new HashMap();
+
public KeyListPublicAdapter(Context context, Cursor c, int flags, int sectionColumnIndex) {
super(context, c, flags);
@@ -138,7 +144,8 @@ public class KeyListPublicAdapter extends CursorAdapter implements StickyListHea
}
// return the first character of the name as ID because this is what
- // headers are based upon
+ // headers private HashMap mSelection = new HashMap();are based upon
return mCursor.getString(mSectionColumnIndex).subSequence(0, 1).charAt(0);
}
@@ -146,4 +153,46 @@ public class KeyListPublicAdapter extends CursorAdapter implements StickyListHea
TextView text;
}
+ /** -------------------------- MULTI-SELECTION METHODS -------------- */
+ public void setNewSelection(int position, boolean value) {
+ mSelection.put(position, value);
+ notifyDataSetChanged();
+ }
+
+ public boolean isPositionChecked(int position) {
+ Boolean result = mSelection.get(position);
+ return result == null ? false : result;
+ }
+
+ public Set getCurrentCheckedPosition() {
+ return mSelection.keySet();
+ }
+
+ public void removeSelection(int position) {
+ mSelection.remove(position);
+ notifyDataSetChanged();
+ }
+
+ public void clearSelection() {
+ mSelection.clear();
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ // let the adapter handle setting up the row views
+ View v = super.getView(position, convertView, parent);
+
+ /**
+ * Change color for multi-selection
+ */
+ // default color
+ v.setBackgroundColor(Color.TRANSPARENT);
+ if (mSelection.get(position) != null) {
+ // this is a selected position, change color!
+ v.setBackgroundColor(parent.getResources().getColor(R.color.emphasis));
+ }
+ return v;
+ }
+
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java
index 6315f84fb..2fda8a9f7 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java
@@ -19,19 +19,14 @@ package org.sufficientlysecure.keychain.ui.adapter;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.OtherHelper;
-import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.database.Cursor;
import android.support.v4.widget.CursorAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.ImageView;
import android.widget.TextView;
public class KeyListSecretAdapter extends CursorAdapter {
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java
index 638702b57..101167777 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java
@@ -38,7 +38,7 @@ import android.support.v4.app.FragmentActivity;
public class DeleteKeyDialogFragment extends DialogFragment {
private static final String ARG_MESSENGER = "messenger";
- private static final String ARG_DELETE_KEY_RING_ROW_ID = "delete_file";
+ private static final String ARG_DELETE_KEY_RING_ROW_IDS = "delete_file";
private static final String ARG_KEY_TYPE = "key_type";
public static final int MESSAGE_OKAY = 1;
@@ -48,13 +48,13 @@ public class DeleteKeyDialogFragment extends DialogFragment {
/**
* Creates new instance of this delete file dialog fragment
*/
- public static DeleteKeyDialogFragment newInstance(Messenger messenger, long deleteKeyRingRowId,
+ public static DeleteKeyDialogFragment newInstance(Messenger messenger, long[] keyRingRowIds,
int keyType) {
DeleteKeyDialogFragment frag = new DeleteKeyDialogFragment();
Bundle args = new Bundle();
args.putParcelable(ARG_MESSENGER, messenger);
- args.putLong(ARG_DELETE_KEY_RING_ROW_ID, deleteKeyRingRowId);
+ args.putLongArray(ARG_DELETE_KEY_RING_ROW_IDS, keyRingRowIds);
args.putInt(ARG_KEY_TYPE, keyType);
frag.setArguments(args);
@@ -70,36 +70,48 @@ public class DeleteKeyDialogFragment extends DialogFragment {
final FragmentActivity activity = getActivity();
mMessenger = getArguments().getParcelable(ARG_MESSENGER);
- final long deleteKeyRingRowId = getArguments().getLong(ARG_DELETE_KEY_RING_ROW_ID);
+ final long[] keyRingRowIds = getArguments().getLongArray(ARG_DELETE_KEY_RING_ROW_IDS);
final int keyType = getArguments().getInt(ARG_KEY_TYPE);
- // TODO: better way to do this?
- String userId = activity.getString(R.string.unknown_user_id);
+ AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+ builder.setTitle(R.string.warning);
+
+ if (keyRingRowIds.length == 1) {
+ // TODO: better way to do this?
+ String userId = activity.getString(R.string.unknown_user_id);
+
+ if (keyType == Id.type.public_key) {
+ PGPPublicKeyRing keyRing = ProviderHelper.getPGPPublicKeyRingByRowId(activity,
+ keyRingRowIds[0]);
+ userId = PgpKeyHelper.getMainUserIdSafe(activity,
+ PgpKeyHelper.getMasterKey(keyRing));
+ } else {
+ PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByRowId(activity,
+ keyRingRowIds[0]);
+ userId = PgpKeyHelper.getMainUserIdSafe(activity,
+ PgpKeyHelper.getMasterKey(keyRing));
+ }
- if (keyType == Id.type.public_key) {
- PGPPublicKeyRing keyRing = ProviderHelper.getPGPPublicKeyRingByRowId(activity,
- deleteKeyRingRowId);
- userId = PgpKeyHelper.getMainUserIdSafe(activity, PgpKeyHelper.getMasterKey(keyRing));
+ builder.setMessage(getString(
+ keyType == Id.type.public_key ? R.string.key_deletion_confirmation
+ : R.string.secret_key_deletion_confirmation, userId));
} else {
- PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByRowId(activity,
- deleteKeyRingRowId);
- userId = PgpKeyHelper.getMainUserIdSafe(activity, PgpKeyHelper.getMasterKey(keyRing));
+ builder.setMessage(R.string.key_deletion_confirmation_multi);
}
- AlertDialog.Builder builder = new AlertDialog.Builder(activity);
- builder.setTitle(R.string.warning);
- builder.setMessage(getString(
- keyType == Id.type.public_key ? R.string.key_deletion_confirmation
- : R.string.secret_key_deletion_confirmation, userId));
builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.setPositiveButton(R.string.btn_delete, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
if (keyType == Id.type.public_key) {
- ProviderHelper.deletePublicKeyRing(activity, deleteKeyRingRowId);
+ for (long keyRowId : keyRingRowIds) {
+ ProviderHelper.deletePublicKeyRing(activity, keyRowId);
+ }
} else {
- ProviderHelper.deleteSecretKeyRing(activity, deleteKeyRingRowId);
+ for (long keyRowId : keyRingRowIds) {
+ ProviderHelper.deleteSecretKeyRing(activity, keyRowId);
+ }
}
dismiss();
diff --git a/README.md b/README.md
index 85e1bd498..30c3f01e5 100644
--- a/README.md
+++ b/README.md
@@ -17,8 +17,8 @@ I am happy about every code contribution and appreciate your effort to help us d
## Build with Gradle
1. Have Android SDK "tools", "platform-tools", and "build-tools" directories in your PATH (http://developer.android.com/sdk/index.html)
-2. Export ANDROID_HOME pointing to your Android SDK
-3. Download Android Support Repository, and Google Repository using Android SDK Manager
+2. Open the Android SDK Manager (shell command: ``android``). Expand the Extras directory and install "Android Support Repository"
+3. Export ANDROID_HOME pointing to your Android SDK
4. Execute ``./gradlew build``
## Development with Eclipse
--
cgit v1.2.3
From de496deffa77c1297c11131894f9ec942d9d8115 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Mon, 6 Jan 2014 14:35:14 +0100
Subject: multi select and delete, needs testing
---
.../res/menu/key_list_multi_selection.xml | 9 +
.../keychain/ui/KeyListPublicFragment.java | 16 +-
.../keychain/ui/adapter/KeyListAdapterOLD.java | 273 +++++++++++++++++++++
3 files changed, 289 insertions(+), 9 deletions(-)
create mode 100644 OpenPGP-Keychain/res/menu/key_list_multi_selection.xml
create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListAdapterOLD.java
diff --git a/OpenPGP-Keychain/res/menu/key_list_multi_selection.xml b/OpenPGP-Keychain/res/menu/key_list_multi_selection.xml
new file mode 100644
index 000000000..64f8564c1
--- /dev/null
+++ b/OpenPGP-Keychain/res/menu/key_list_multi_selection.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
index ed8427fd0..f0a009482 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
@@ -17,7 +17,6 @@
package org.sufficientlysecure.keychain.ui;
-import java.util.ArrayList;
import java.util.Set;
import org.sufficientlysecure.keychain.Id;
@@ -39,7 +38,6 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
-import android.provider.BaseColumns;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
@@ -121,18 +119,18 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
- // StringBuilder sb = new StringBuilder();
Set positions = mAdapter.getCurrentCheckedPosition();
- // for (Integer pos : positions) {
- // sb.append(" " + pos + ",");
- // }
switch (item.getItemId()) {
case R.id.delete_entry:
+
+ // get IDs for checked positions as long array
long[] ids = new long[positions.size()];
- for (int i=0; i < positions.size(); i++) {
- ids[i] = mAdapter.getItemId(positions.);
+ int i = 0;
+ for (int pos : positions) {
+ ids[i] = mAdapter.getItemId(pos);
+ i++;
}
- showDeleteKeyDialog(ids.to);
+ showDeleteKeyDialog(ids);
break;
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListAdapterOLD.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListAdapterOLD.java
new file mode 100644
index 000000000..0c709d25a
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListAdapterOLD.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2012-2013 Dominik Schürmann
+ *
+ * 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.adapter;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.Id;
+import org.sufficientlysecure.keychain.helper.OtherHelper;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
+import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.R;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.database.MergeCursor;
+import android.net.Uri;
+import android.provider.BaseColumns;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CursorTreeAdapter;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class KeyListAdapterOLD extends CursorTreeAdapter {
+ private Context mContext;
+ private LayoutInflater mInflater;
+
+ protected int mKeyType;
+
+ private static final int CHILD_KEY = 0;
+ private static final int CHILD_USER_ID = 1;
+ private static final int CHILD_FINGERPRINT = 2;
+
+ public KeyListAdapterOLD(Context context, Cursor groupCursor, int keyType) {
+ super(groupCursor, context);
+ mContext = context;
+ mInflater = LayoutInflater.from(context);
+ mKeyType = keyType;
+ }
+
+ /**
+ * Inflate new view for group items
+ */
+ @Override
+ public View newGroupView(Context context, Cursor cursor, boolean isExpanded, ViewGroup parent) {
+ return mInflater.inflate(R.layout.key_list_item, null);
+ }
+
+ /**
+ * Binds TextViews from group view to results from database group cursor.
+ */
+ @Override
+ protected void bindGroupView(View view, Context context, Cursor cursor, boolean isExpanded) {
+ int userIdIndex = cursor.getColumnIndex(UserIds.USER_ID);
+
+ TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
+ mainUserId.setText(R.string.unknown_user_id);
+ TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
+ mainUserIdRest.setText("");
+
+ String userId = cursor.getString(userIdIndex);
+ if (userId != null) {
+ String[] userIdSplit = OtherHelper.splitUserId(userId);
+
+ if (userIdSplit[1] != null) {
+ mainUserIdRest.setText(userIdSplit[1]);
+ }
+ mainUserId.setText(userIdSplit[0]);
+ }
+
+ if (mainUserId.getText().length() == 0) {
+ mainUserId.setText(R.string.unknown_user_id);
+ }
+
+ if (mainUserIdRest.getText().length() == 0) {
+ mainUserIdRest.setVisibility(View.GONE);
+ } else {
+ mainUserIdRest.setVisibility(View.VISIBLE);
+ }
+ }
+
+ /**
+ * Inflate new view for child items
+ */
+ @Override
+ public View newChildView(Context context, Cursor cursor, boolean isLastChild, ViewGroup parent) {
+ return mInflater.inflate(R.layout.key_list_child_item, null);
+ }
+
+ /**
+ * Bind TextViews from view of childs based on query results
+ */
+ @Override
+ protected void bindChildView(View view, Context context, Cursor cursor, boolean isLastChild) {
+ LinearLayout keyLayout = (LinearLayout) view.findViewById(R.id.keyLayout);
+ LinearLayout userIdLayout = (LinearLayout) view.findViewById(R.id.userIdLayout);
+
+ // first entry is fingerprint
+ if (cursor.getPosition() == 0) {
+ // show only userId layout
+ keyLayout.setVisibility(View.GONE);
+ userIdLayout.setVisibility(View.VISIBLE);
+
+ String fingerprint = PgpKeyHelper.getFingerPrint(context,
+ cursor.getLong(cursor.getColumnIndex(Keys.KEY_ID)));
+ fingerprint = fingerprint.replace(" ", "\n");
+
+ TextView userId = (TextView) view.findViewById(R.id.userId);
+ if (userId == null) {
+ Log.d(Constants.TAG, "userId is null!");
+ }
+ userId.setText(context.getString(R.string.fingerprint) + "\n" + fingerprint);
+ } else {
+ // differentiate between keys and userIds in MergeCursor
+ if (cursor.getColumnIndex(Keys.KEY_ID) != -1) {
+ keyLayout.setVisibility(View.VISIBLE);
+ userIdLayout.setVisibility(View.GONE);
+
+ String keyIdStr = PgpKeyHelper.convertKeyIdToHex(cursor.getLong(cursor
+ .getColumnIndex(Keys.KEY_ID)));
+ String algorithmStr = PgpKeyHelper.getAlgorithmInfo(
+ cursor.getInt(cursor.getColumnIndex(Keys.ALGORITHM)),
+ cursor.getInt(cursor.getColumnIndex(Keys.KEY_SIZE)));
+
+ TextView keyId = (TextView) view.findViewById(R.id.keyId);
+ keyId.setText(keyIdStr);
+
+ TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails);
+ keyDetails.setText("(" + algorithmStr + ")");
+
+ ImageView masterKeyIcon = (ImageView) view.findViewById(R.id.ic_masterKey);
+ if (cursor.getInt(cursor.getColumnIndex(Keys.IS_MASTER_KEY)) != 1) {
+ masterKeyIcon.setVisibility(View.INVISIBLE);
+ } else {
+ masterKeyIcon.setVisibility(View.VISIBLE);
+ }
+
+ ImageView certifyIcon = (ImageView) view.findViewById(R.id.ic_certifyKey);
+ if (cursor.getInt(cursor.getColumnIndex(Keys.CAN_CERTIFY)) != 1) {
+ certifyIcon.setVisibility(View.GONE);
+ } else {
+ certifyIcon.setVisibility(View.VISIBLE);
+ }
+
+ ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey);
+ if (cursor.getInt(cursor.getColumnIndex(Keys.CAN_ENCRYPT)) != 1) {
+ encryptIcon.setVisibility(View.GONE);
+ } else {
+ encryptIcon.setVisibility(View.VISIBLE);
+ }
+
+ ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey);
+ if (cursor.getInt(cursor.getColumnIndex(Keys.CAN_SIGN)) != 1) {
+ signIcon.setVisibility(View.GONE);
+ } else {
+ signIcon.setVisibility(View.VISIBLE);
+ }
+ } else {
+ keyLayout.setVisibility(View.GONE);
+ userIdLayout.setVisibility(View.VISIBLE);
+
+ String userIdStr = cursor.getString(cursor.getColumnIndex(UserIds.USER_ID));
+
+ TextView userId = (TextView) view.findViewById(R.id.userId);
+ userId.setText(userIdStr);
+ }
+ }
+ }
+
+ /**
+ * Given the group cursor, we start cursors for a fingerprint, keys, and userIds, which are
+ * merged together and build the child cursor
+ */
+ @Override
+ protected Cursor getChildrenCursor(Cursor groupCursor) {
+ final long keyRingRowId = groupCursor.getLong(groupCursor.getColumnIndex(BaseColumns._ID));
+
+ Cursor fingerprintCursor = getChildCursor(keyRingRowId, CHILD_FINGERPRINT);
+ Cursor keyCursor = getChildCursor(keyRingRowId, CHILD_KEY);
+ Cursor userIdCursor = getChildCursor(keyRingRowId, CHILD_USER_ID);
+
+ MergeCursor mergeCursor = new MergeCursor(new Cursor[] { fingerprintCursor, keyCursor,
+ userIdCursor });
+ Log.d(Constants.TAG, "mergeCursor:" + DatabaseUtils.dumpCursorToString(mergeCursor));
+
+ return mergeCursor;
+ }
+
+ /**
+ * This builds a cursor for a specific type of children
+ *
+ * @param keyRingRowId
+ * foreign row id of the keyRing
+ * @param type
+ * @return
+ */
+ private Cursor getChildCursor(long keyRingRowId, int type) {
+ Uri uri = null;
+ String[] projection = null;
+ String sortOrder = null;
+ String selection = null;
+
+ switch (type) {
+ case CHILD_FINGERPRINT:
+ projection = new String[] { Keys._ID, Keys.KEY_ID, Keys.IS_MASTER_KEY, Keys.ALGORITHM,
+ Keys.KEY_SIZE, Keys.CAN_CERTIFY, Keys.CAN_SIGN, Keys.CAN_ENCRYPT, };
+ sortOrder = Keys.RANK + " ASC";
+
+ // use only master key for fingerprint
+ selection = Keys.IS_MASTER_KEY + " = 1 ";
+
+ if (mKeyType == Id.type.public_key) {
+ uri = Keys.buildPublicKeysUri(String.valueOf(keyRingRowId));
+ } else {
+ uri = Keys.buildSecretKeysUri(String.valueOf(keyRingRowId));
+ }
+ break;
+
+ case CHILD_KEY:
+ projection = new String[] { Keys._ID, Keys.KEY_ID, Keys.IS_MASTER_KEY, Keys.ALGORITHM,
+ Keys.KEY_SIZE, Keys.CAN_CERTIFY, Keys.CAN_SIGN, Keys.CAN_ENCRYPT, };
+ sortOrder = Keys.RANK + " ASC";
+
+ if (mKeyType == Id.type.public_key) {
+ uri = Keys.buildPublicKeysUri(String.valueOf(keyRingRowId));
+ } else {
+ uri = Keys.buildSecretKeysUri(String.valueOf(keyRingRowId));
+ }
+
+ break;
+
+ case CHILD_USER_ID:
+ projection = new String[] { UserIds._ID, UserIds.USER_ID, UserIds.RANK, };
+ sortOrder = UserIds.RANK + " ASC";
+
+ // not the main user id
+ selection = UserIds.RANK + " > 0 ";
+
+ if (mKeyType == Id.type.public_key) {
+ uri = UserIds.buildPublicUserIdsUri(String.valueOf(keyRingRowId));
+ } else {
+ uri = UserIds.buildSecretUserIdsUri(String.valueOf(keyRingRowId));
+ }
+
+ break;
+
+ default:
+ return null;
+
+ }
+
+ return mContext.getContentResolver().query(uri, projection, selection, null, sortOrder);
+ }
+
+}
--
cgit v1.2.3
From 7671c99b97ebf62b797ee69ef8412c973199c9f8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Mon, 6 Jan 2014 20:30:22 +0100
Subject: copy to clipboard in key view
---
OpenPGP-Keychain/res/menu/key_view.xml | 4 +
OpenPGP-Keychain/res/values/strings.xml | 1 +
.../keychain/ui/KeyViewActivity.java | 148 +++++++++++++--------
3 files changed, 94 insertions(+), 59 deletions(-)
diff --git a/OpenPGP-Keychain/res/menu/key_view.xml b/OpenPGP-Keychain/res/menu/key_view.xml
index 0785abd32..3798cb935 100644
--- a/OpenPGP-Keychain/res/menu/key_view.xml
+++ b/OpenPGP-Keychain/res/menu/key_view.xml
@@ -19,6 +19,10 @@
android:id="@+id/menu_key_view_share_nfc"
android:showAsAction="never"
android:title="@string/menu_share_nfc"/>
+ Share with…
Share with QR CodeShare with NFC
+ Copy to clipboardSign keyBeam settings
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java
index 45c79fc8e..4bffd9f3d 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java
@@ -28,6 +28,7 @@ import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.ui.dialog.ShareNfcDialogFragment;
@@ -108,62 +109,18 @@ public class KeyViewActivity extends SherlockFragmentActivity implements CreateN
@Override
public boolean onOptionsItemSelected(MenuItem item) {
- // TODO: use data uri in the other activities instead of givin key ring row id!
-
- long keyRingRowId = Long.valueOf(mDataUri.getLastPathSegment());
-
switch (item.getItemId()) {
case R.id.menu_key_view_update:
- long updateKeyId = 0;
- PGPPublicKeyRing updateKeyRing = ProviderHelper.getPGPPublicKeyRingByRowId(this,
- keyRingRowId);
- if (updateKeyRing != null) {
- updateKeyId = PgpKeyHelper.getMasterKey(updateKeyRing).getKeyID();
- }
- if (updateKeyId == 0) {
- // this shouldn't happen
- return true;
- }
-
- Intent queryIntent = new Intent(this, KeyServerQueryActivity.class);
- queryIntent.setAction(KeyServerQueryActivity.ACTION_LOOK_UP_KEY_ID_AND_RETURN);
- queryIntent.putExtra(KeyServerQueryActivity.EXTRA_KEY_ID, updateKeyId);
-
- // TODO: lookup??
- startActivityForResult(queryIntent, Id.request.look_up_key_id);
-
+ updateFromKeyserver();
return true;
case R.id.menu_key_view_sign:
- long keyId = 0;
- PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByRowId(this,
- keyRingRowId);
- if (signKeyRing != null) {
- keyId = PgpKeyHelper.getMasterKey(signKeyRing).getKeyID();
- }
- if (keyId == 0) {
- // this shouldn't happen
- return true;
- }
-
- Intent signIntent = new Intent(this, SignKeyActivity.class);
- signIntent.putExtra(SignKeyActivity.EXTRA_KEY_ID, keyId);
- startActivity(signIntent);
+ signKey();
return true;
case R.id.menu_key_view_export_keyserver:
- Intent uploadIntent = new Intent(this, KeyServerUploadActivity.class);
- uploadIntent.setAction(KeyServerUploadActivity.ACTION_EXPORT_KEY_TO_SERVER);
- uploadIntent.putExtra(KeyServerUploadActivity.EXTRA_KEYRING_ROW_ID, (int) keyRingRowId);
- startActivityForResult(uploadIntent, Id.request.export_to_server);
-
+ uploadToKeyserver();
return true;
case R.id.menu_key_view_export_file:
- // long masterKeyId = ProviderHelper.getPublicMasterKeyId(mKeyListActivity,
- // keyRingRowId);
- // if (masterKeyId == -1) {
- // masterKeyId = ProviderHelper.getSecretMasterKeyId(mKeyListActivity, keyRingRowId);
- // }
- //
- // mKeyListActivity.showExportKeysDialog(masterKeyId);
+ exportToFile();
return true;
case R.id.menu_key_view_share_default:
shareKey();
@@ -172,20 +129,13 @@ public class KeyViewActivity extends SherlockFragmentActivity implements CreateN
shareKeyQrCode();
return true;
case R.id.menu_key_view_share_nfc:
- // get master key id using row id
- // long masterKeyId2 = ProviderHelper.getPublicMasterKeyId(this, keyRingRowId);
- //
- // Intent nfcIntent = new Intent(this, ShareNfcBeamActivity.class);
- // nfcIntent.setAction(ShareNfcBeamActivity.ACTION_SHARE_KEYRING_WITH_NFC);
- // nfcIntent.putExtra(ShareNfcBeamActivity.EXTRA_MASTER_KEY_ID, masterKeyId2);
- // startActivityForResult(nfcIntent, 0);
-
shareNfc();
-
+ return true;
+ case R.id.menu_key_view_share_clipboard:
+ copyToClipboard();
return true;
case R.id.menu_key_view_delete:
- // mKeyListActivity.showDeleteKeyDialog(keyRingRowId);
-
+ deleteKey();
return true;
}
@@ -238,6 +188,75 @@ public class KeyViewActivity extends SherlockFragmentActivity implements CreateN
return result;
}
+ private void exportToFile() {
+ // long masterKeyId = ProviderHelper.getPublicMasterKeyId(mKeyListActivity,
+ // keyRingRowId);
+ // if (masterKeyId == -1) {
+ // masterKeyId = ProviderHelper.getSecretMasterKeyId(mKeyListActivity, keyRingRowId);
+ // }
+ //
+ // mKeyListActivity.showExportKeysDialog(masterKeyId);
+ }
+
+ private void deleteKey() {
+ long keyRingRowId = Long.valueOf(mDataUri.getLastPathSegment());
+
+ // mKeyListActivity.showDeleteKeyDialog(keyRingRowId);
+ }
+
+ private void uploadToKeyserver() {
+ long keyRingRowId = Long.valueOf(mDataUri.getLastPathSegment());
+
+ Intent uploadIntent = new Intent(this, KeyServerUploadActivity.class);
+ uploadIntent.setAction(KeyServerUploadActivity.ACTION_EXPORT_KEY_TO_SERVER);
+ uploadIntent.putExtra(KeyServerUploadActivity.EXTRA_KEYRING_ROW_ID, (int) keyRingRowId);
+ startActivityForResult(uploadIntent, Id.request.export_to_server);
+ }
+
+ private void updateFromKeyserver() {
+ // TODO: use data uri!
+ long keyRingRowId = Long.valueOf(mDataUri.getLastPathSegment());
+
+ long updateKeyId = 0;
+ PGPPublicKeyRing updateKeyRing = ProviderHelper.getPGPPublicKeyRingByRowId(this,
+ keyRingRowId);
+ if (updateKeyRing != null) {
+ updateKeyId = PgpKeyHelper.getMasterKey(updateKeyRing).getKeyID();
+ }
+ // if (updateKeyId == 0) {
+ // // this shouldn't happen
+ // return true;
+ // }
+
+ Intent queryIntent = new Intent(this, KeyServerQueryActivity.class);
+ queryIntent.setAction(KeyServerQueryActivity.ACTION_LOOK_UP_KEY_ID_AND_RETURN);
+ queryIntent.putExtra(KeyServerQueryActivity.EXTRA_KEY_ID, updateKeyId);
+
+ // TODO: lookup??
+ startActivityForResult(queryIntent, Id.request.look_up_key_id);
+
+ }
+
+ private void signKey() {
+ // TODO: use data uri!
+ long keyRingRowId = Long.valueOf(mDataUri.getLastPathSegment());
+
+ long keyId = 0;
+ PGPPublicKeyRing signKeyRing = ProviderHelper
+ .getPGPPublicKeyRingByRowId(this, keyRingRowId);
+ if (signKeyRing != null) {
+ keyId = PgpKeyHelper.getMasterKey(signKeyRing).getKeyID();
+ }
+ // if (keyId == 0) {
+ // // this shouldn't happen
+ // return true;
+ // }
+
+ Intent signIntent = new Intent(this, SignKeyActivity.class);
+ signIntent.putExtra(SignKeyActivity.EXTRA_KEY_ID, keyId);
+ startActivity(signIntent);
+ }
+
private void shareKey() {
// TODO: use data uri!
long keyRingRowId = Long.valueOf(mDataUri.getLastPathSegment());
@@ -272,6 +291,17 @@ public class KeyViewActivity extends SherlockFragmentActivity implements CreateN
dialog.show(getSupportFragmentManager(), "shareNfcDialog");
}
+ private void copyToClipboard() {
+ // TODO: use data uri!
+ long keyRingRowId = Long.valueOf(mDataUri.getLastPathSegment());
+ long masterKeyId = ProviderHelper.getPublicMasterKeyId(this, keyRingRowId);
+ // get public keyring as ascii armored string
+ ArrayList keyringArmored = ProviderHelper.getPublicKeyRingsAsArmoredString(this,
+ new long[] { masterKeyId });
+
+ ClipboardReflection.copyToClipboard(this, keyringArmored.get(0));
+ }
+
/**
* NFC: Initialize NFC sharing if OS and device supports it
*/
--
cgit v1.2.3
From ccb462e94fffea71649e93dd4d91ef3748f5c726 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Mon, 6 Jan 2014 22:52:45 +0100
Subject: externalize zxing lib, fix gradle build
---
OpenPGP-Keychain/build.gradle | 1 +
OpenPGP-Keychain/project.properties | 1 +
.../src/com/google/zxing/BarcodeFormat.java | 106 ----
.../src/com/google/zxing/Binarizer.java | 80 ---
.../src/com/google/zxing/BinaryBitmap.java | 128 -----
.../src/com/google/zxing/ChecksumException.java | 37 --
.../src/com/google/zxing/DecodeHintType.java | 79 ---
.../src/com/google/zxing/EncodeHintType.java | 39 --
.../src/com/google/zxing/FormatException.java | 38 --
.../src/com/google/zxing/LuminanceSource.java | 113 ----
.../src/com/google/zxing/NotFoundException.java | 37 --
OpenPGP-Keychain/src/com/google/zxing/Reader.java | 64 ---
.../src/com/google/zxing/ReaderException.java | 98 ----
OpenPGP-Keychain/src/com/google/zxing/Result.java | 143 -----
.../src/com/google/zxing/ResultMetadataType.java | 109 ----
.../src/com/google/zxing/ResultPoint.java | 129 -----
.../src/com/google/zxing/ResultPointCallback.java | 29 -
OpenPGP-Keychain/src/com/google/zxing/Writer.java | 54 --
.../src/com/google/zxing/WriterException.java | 35 --
.../src/com/google/zxing/common/BitArray.java | 246 ---------
.../src/com/google/zxing/common/BitMatrix.java | 247 ---------
.../src/com/google/zxing/common/BitSource.java | 97 ----
.../com/google/zxing/common/CharacterSetECI.java | 110 ----
.../src/com/google/zxing/common/Collections.java | 53 --
.../src/com/google/zxing/common/Comparator.java | 27 -
.../src/com/google/zxing/common/DecoderResult.java | 61 ---
.../google/zxing/common/DefaultGridSampler.java | 86 ---
.../com/google/zxing/common/DetectorResult.java | 46 --
.../src/com/google/zxing/common/ECI.java | 52 --
.../zxing/common/GlobalHistogramBinarizer.java | 194 -------
.../src/com/google/zxing/common/GridSampler.java | 156 ------
.../com/google/zxing/common/HybridBinarizer.java | 185 -------
.../google/zxing/common/PerspectiveTransform.java | 148 ------
.../src/com/google/zxing/common/StringUtils.java | 192 -------
.../detector/MonochromeRectangleDetector.java | 209 --------
.../common/detector/WhiteRectangleDetector.java | 347 ------------
.../google/zxing/common/reedsolomon/GenericGF.java | 188 -------
.../zxing/common/reedsolomon/GenericGFPoly.java | 263 ---------
.../common/reedsolomon/ReedSolomonDecoder.java | 194 -------
.../common/reedsolomon/ReedSolomonEncoder.java | 75 ---
.../common/reedsolomon/ReedSolomonException.java | 31 --
.../com/google/zxing/multi/ByQuadrantReader.java | 96 ----
.../zxing/multi/GenericMultipleBarcodeReader.java | 156 ------
.../google/zxing/multi/MultipleBarcodeReader.java | 37 --
.../zxing/multi/qrcode/detector/MultiDetector.java | 72 ---
.../qrcode/detector/MultiFinderPatternFinder.java | 324 ------------
.../src/com/google/zxing/qrcode/QRCodeWriter.java | 108 ----
.../zxing/qrcode/decoder/BitMatrixParser.java | 203 -------
.../com/google/zxing/qrcode/decoder/DataBlock.java | 123 -----
.../com/google/zxing/qrcode/decoder/DataMask.java | 155 ------
.../qrcode/decoder/DecodedBitStreamParser.java | 322 -----------
.../zxing/qrcode/decoder/ErrorCorrectionLevel.java | 86 ---
.../zxing/qrcode/decoder/FormatInformation.java | 171 ------
.../src/com/google/zxing/qrcode/decoder/Mode.java | 117 ----
.../com/google/zxing/qrcode/decoder/Version.java | 586 ---------------------
.../zxing/qrcode/detector/AlignmentPattern.java | 48 --
.../qrcode/detector/AlignmentPatternFinder.java | 279 ----------
.../com/google/zxing/qrcode/detector/Detector.java | 406 --------------
.../zxing/qrcode/detector/FinderPattern.java | 63 ---
.../zxing/qrcode/detector/FinderPatternFinder.java | 585 --------------------
.../zxing/qrcode/detector/FinderPatternInfo.java | 49 --
.../com/google/zxing/qrcode/encoder/BlockPair.java | 37 --
.../google/zxing/qrcode/encoder/ByteMatrix.java | 97 ----
.../com/google/zxing/qrcode/encoder/Encoder.java | 557 --------------------
.../com/google/zxing/qrcode/encoder/MaskUtil.java | 218 --------
.../google/zxing/qrcode/encoder/MatrixUtil.java | 524 ------------------
.../com/google/zxing/qrcode/encoder/QRCode.java | 239 ---------
.../keychain/ui/KeyViewActivity.java | 2 +-
README.md | 5 +-
libraries/StickyListHeaders/build.gradle | 24 -
libraries/StickyListHeaders/demo.gif | Bin 286171 -> 0 bytes
.../gradle/wrapper/gradle-wrapper.jar | Bin 49896 -> 0 bytes
.../gradle/wrapper/gradle-wrapper.properties | 6 -
libraries/StickyListHeaders/gradlew | 164 ------
libraries/StickyListHeaders/gradlew.bat | 90 ----
libraries/StickyListHeaders/library/build.gradle | 2 -
.../StickyListHeaders/sample/AndroidManifest.xml | 27 -
libraries/StickyListHeaders/sample/build.gradle | 23 -
.../sample/libs/android-support-v4.jar | Bin 556198 -> 0 bytes
.../StickyListHeaders/sample/project.properties | 15 -
.../sample/res/drawable-hdpi/ic_drawer.png | Bin 2826 -> 0 bytes
.../sample/res/drawable-hdpi/ic_launcher.png | Bin 9397 -> 0 bytes
.../sample/res/drawable-ldpi/ic_launcher.png | Bin 2729 -> 0 bytes
.../sample/res/drawable-mdpi/ic_drawer.png | Bin 2816 -> 0 bytes
.../sample/res/drawable-mdpi/ic_launcher.png | Bin 5237 -> 0 bytes
.../sample/res/drawable-xhdpi/ic_drawer.png | Bin 1038 -> 0 bytes
.../sample/res/drawable-xhdpi/ic_launcher.png | Bin 14383 -> 0 bytes
.../sample/res/drawable/header_selector.xml | 7 -
.../StickyListHeaders/sample/res/layout/header.xml | 17 -
.../sample/res/layout/list_footer.xml | 13 -
.../sample/res/layout/list_header.xml | 13 -
.../StickyListHeaders/sample/res/layout/main.xml | 114 ----
.../sample/res/layout/test_list_item_layout.xml | 8 -
.../StickyListHeaders/sample/res/values/arrays.xml | 230 --------
.../StickyListHeaders/sample/res/values/colors.xml | 7 -
.../sample/res/values/strings.xml | 20 -
.../StickyListHeaders/sample/res/values/style.xml | 9 -
.../stickylistheaders/TestActivity.java | 169 ------
.../stickylistheaders/TestBaseAdapter.java | 169 ------
.../stickylistheaders/views/UnderlineTextView.java | 50 --
libraries/StickyListHeaders/settings.gradle | 2 -
libraries/zxing/AndroidManifest.xml | 11 +
libraries/zxing/build.gradle | 14 +
libraries/zxing/project.properties | 15 +
.../zxing/src/com/google/zxing/BarcodeFormat.java | 106 ++++
.../zxing/src/com/google/zxing/Binarizer.java | 80 +++
.../zxing/src/com/google/zxing/BinaryBitmap.java | 128 +++++
.../src/com/google/zxing/ChecksumException.java | 37 ++
.../zxing/src/com/google/zxing/DecodeHintType.java | 79 +++
.../zxing/src/com/google/zxing/EncodeHintType.java | 39 ++
.../src/com/google/zxing/FormatException.java | 38 ++
.../src/com/google/zxing/LuminanceSource.java | 113 ++++
.../src/com/google/zxing/NotFoundException.java | 37 ++
libraries/zxing/src/com/google/zxing/Reader.java | 64 +++
.../src/com/google/zxing/ReaderException.java | 98 ++++
libraries/zxing/src/com/google/zxing/Result.java | 143 +++++
.../src/com/google/zxing/ResultMetadataType.java | 109 ++++
.../zxing/src/com/google/zxing/ResultPoint.java | 129 +++++
.../src/com/google/zxing/ResultPointCallback.java | 29 +
libraries/zxing/src/com/google/zxing/Writer.java | 54 ++
.../src/com/google/zxing/WriterException.java | 35 ++
.../src/com/google/zxing/common/BitArray.java | 246 +++++++++
.../src/com/google/zxing/common/BitMatrix.java | 247 +++++++++
.../src/com/google/zxing/common/BitSource.java | 97 ++++
.../com/google/zxing/common/CharacterSetECI.java | 110 ++++
.../src/com/google/zxing/common/Collections.java | 53 ++
.../src/com/google/zxing/common/Comparator.java | 27 +
.../src/com/google/zxing/common/DecoderResult.java | 61 +++
.../google/zxing/common/DefaultGridSampler.java | 86 +++
.../com/google/zxing/common/DetectorResult.java | 46 ++
.../zxing/src/com/google/zxing/common/ECI.java | 52 ++
.../zxing/common/GlobalHistogramBinarizer.java | 194 +++++++
.../src/com/google/zxing/common/GridSampler.java | 156 ++++++
.../com/google/zxing/common/HybridBinarizer.java | 185 +++++++
.../google/zxing/common/PerspectiveTransform.java | 148 ++++++
.../src/com/google/zxing/common/StringUtils.java | 192 +++++++
.../detector/MonochromeRectangleDetector.java | 209 ++++++++
.../common/detector/WhiteRectangleDetector.java | 347 ++++++++++++
.../google/zxing/common/reedsolomon/GenericGF.java | 188 +++++++
.../zxing/common/reedsolomon/GenericGFPoly.java | 263 +++++++++
.../common/reedsolomon/ReedSolomonDecoder.java | 194 +++++++
.../common/reedsolomon/ReedSolomonEncoder.java | 75 +++
.../common/reedsolomon/ReedSolomonException.java | 31 ++
.../com/google/zxing/multi/ByQuadrantReader.java | 96 ++++
.../zxing/multi/GenericMultipleBarcodeReader.java | 156 ++++++
.../google/zxing/multi/MultipleBarcodeReader.java | 37 ++
.../zxing/multi/qrcode/detector/MultiDetector.java | 72 +++
.../qrcode/detector/MultiFinderPatternFinder.java | 324 ++++++++++++
.../src/com/google/zxing/qrcode/QRCodeWriter.java | 108 ++++
.../zxing/qrcode/decoder/BitMatrixParser.java | 203 +++++++
.../com/google/zxing/qrcode/decoder/DataBlock.java | 123 +++++
.../com/google/zxing/qrcode/decoder/DataMask.java | 155 ++++++
.../qrcode/decoder/DecodedBitStreamParser.java | 322 +++++++++++
.../zxing/qrcode/decoder/ErrorCorrectionLevel.java | 86 +++
.../zxing/qrcode/decoder/FormatInformation.java | 171 ++++++
.../src/com/google/zxing/qrcode/decoder/Mode.java | 117 ++++
.../com/google/zxing/qrcode/decoder/Version.java | 586 +++++++++++++++++++++
.../zxing/qrcode/detector/AlignmentPattern.java | 48 ++
.../qrcode/detector/AlignmentPatternFinder.java | 279 ++++++++++
.../com/google/zxing/qrcode/detector/Detector.java | 406 ++++++++++++++
.../zxing/qrcode/detector/FinderPattern.java | 63 +++
.../zxing/qrcode/detector/FinderPatternFinder.java | 585 ++++++++++++++++++++
.../zxing/qrcode/detector/FinderPatternInfo.java | 49 ++
.../com/google/zxing/qrcode/encoder/BlockPair.java | 37 ++
.../google/zxing/qrcode/encoder/ByteMatrix.java | 97 ++++
.../com/google/zxing/qrcode/encoder/Encoder.java | 557 ++++++++++++++++++++
.../com/google/zxing/qrcode/encoder/MaskUtil.java | 218 ++++++++
.../google/zxing/qrcode/encoder/MatrixUtil.java | 524 ++++++++++++++++++
.../com/google/zxing/qrcode/encoder/QRCode.java | 239 +++++++++
settings.gradle | 1 +
170 files changed, 10230 insertions(+), 11365 deletions(-)
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/BarcodeFormat.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/Binarizer.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/BinaryBitmap.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/ChecksumException.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/DecodeHintType.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/EncodeHintType.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/FormatException.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/LuminanceSource.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/NotFoundException.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/Reader.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/ReaderException.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/Result.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/ResultMetadataType.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/ResultPoint.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/ResultPointCallback.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/Writer.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/WriterException.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/common/BitArray.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/common/BitMatrix.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/common/BitSource.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/common/CharacterSetECI.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/common/Collections.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/common/Comparator.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/common/DecoderResult.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/common/DefaultGridSampler.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/common/DetectorResult.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/common/ECI.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/common/GlobalHistogramBinarizer.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/common/GridSampler.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/common/HybridBinarizer.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/common/PerspectiveTransform.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/common/StringUtils.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/common/detector/MonochromeRectangleDetector.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/common/detector/WhiteRectangleDetector.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/common/reedsolomon/GenericGF.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/common/reedsolomon/GenericGFPoly.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/common/reedsolomon/ReedSolomonDecoder.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/common/reedsolomon/ReedSolomonException.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/multi/ByQuadrantReader.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/multi/GenericMultipleBarcodeReader.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/multi/MultipleBarcodeReader.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/multi/qrcode/detector/MultiDetector.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/multi/qrcode/detector/MultiFinderPatternFinder.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/qrcode/QRCodeWriter.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/BitMatrixParser.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/DataBlock.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/DataMask.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/ErrorCorrectionLevel.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/FormatInformation.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/Mode.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/Version.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/AlignmentPattern.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/AlignmentPatternFinder.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/Detector.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/FinderPattern.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/FinderPatternFinder.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/FinderPatternInfo.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/BlockPair.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/ByteMatrix.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/Encoder.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/MaskUtil.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/MatrixUtil.java
delete mode 100644 OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/QRCode.java
delete mode 100644 libraries/StickyListHeaders/build.gradle
delete mode 100644 libraries/StickyListHeaders/demo.gif
delete mode 100644 libraries/StickyListHeaders/gradle/wrapper/gradle-wrapper.jar
delete mode 100644 libraries/StickyListHeaders/gradle/wrapper/gradle-wrapper.properties
delete mode 100644 libraries/StickyListHeaders/gradlew
delete mode 100644 libraries/StickyListHeaders/gradlew.bat
delete mode 100644 libraries/StickyListHeaders/sample/AndroidManifest.xml
delete mode 100644 libraries/StickyListHeaders/sample/build.gradle
delete mode 100644 libraries/StickyListHeaders/sample/libs/android-support-v4.jar
delete mode 100644 libraries/StickyListHeaders/sample/project.properties
delete mode 100644 libraries/StickyListHeaders/sample/res/drawable-hdpi/ic_drawer.png
delete mode 100644 libraries/StickyListHeaders/sample/res/drawable-hdpi/ic_launcher.png
delete mode 100644 libraries/StickyListHeaders/sample/res/drawable-ldpi/ic_launcher.png
delete mode 100644 libraries/StickyListHeaders/sample/res/drawable-mdpi/ic_drawer.png
delete mode 100644 libraries/StickyListHeaders/sample/res/drawable-mdpi/ic_launcher.png
delete mode 100644 libraries/StickyListHeaders/sample/res/drawable-xhdpi/ic_drawer.png
delete mode 100644 libraries/StickyListHeaders/sample/res/drawable-xhdpi/ic_launcher.png
delete mode 100644 libraries/StickyListHeaders/sample/res/drawable/header_selector.xml
delete mode 100644 libraries/StickyListHeaders/sample/res/layout/header.xml
delete mode 100644 libraries/StickyListHeaders/sample/res/layout/list_footer.xml
delete mode 100644 libraries/StickyListHeaders/sample/res/layout/list_header.xml
delete mode 100644 libraries/StickyListHeaders/sample/res/layout/main.xml
delete mode 100644 libraries/StickyListHeaders/sample/res/layout/test_list_item_layout.xml
delete mode 100755 libraries/StickyListHeaders/sample/res/values/arrays.xml
delete mode 100644 libraries/StickyListHeaders/sample/res/values/colors.xml
delete mode 100644 libraries/StickyListHeaders/sample/res/values/strings.xml
delete mode 100644 libraries/StickyListHeaders/sample/res/values/style.xml
delete mode 100644 libraries/StickyListHeaders/sample/src/se/emilsjolander/stickylistheaders/TestActivity.java
delete mode 100644 libraries/StickyListHeaders/sample/src/se/emilsjolander/stickylistheaders/TestBaseAdapter.java
delete mode 100644 libraries/StickyListHeaders/sample/src/se/emilsjolander/stickylistheaders/views/UnderlineTextView.java
delete mode 100644 libraries/StickyListHeaders/settings.gradle
create mode 100644 libraries/zxing/AndroidManifest.xml
create mode 100644 libraries/zxing/build.gradle
create mode 100644 libraries/zxing/project.properties
create mode 100644 libraries/zxing/src/com/google/zxing/BarcodeFormat.java
create mode 100644 libraries/zxing/src/com/google/zxing/Binarizer.java
create mode 100644 libraries/zxing/src/com/google/zxing/BinaryBitmap.java
create mode 100644 libraries/zxing/src/com/google/zxing/ChecksumException.java
create mode 100644 libraries/zxing/src/com/google/zxing/DecodeHintType.java
create mode 100644 libraries/zxing/src/com/google/zxing/EncodeHintType.java
create mode 100644 libraries/zxing/src/com/google/zxing/FormatException.java
create mode 100644 libraries/zxing/src/com/google/zxing/LuminanceSource.java
create mode 100644 libraries/zxing/src/com/google/zxing/NotFoundException.java
create mode 100644 libraries/zxing/src/com/google/zxing/Reader.java
create mode 100644 libraries/zxing/src/com/google/zxing/ReaderException.java
create mode 100644 libraries/zxing/src/com/google/zxing/Result.java
create mode 100644 libraries/zxing/src/com/google/zxing/ResultMetadataType.java
create mode 100644 libraries/zxing/src/com/google/zxing/ResultPoint.java
create mode 100644 libraries/zxing/src/com/google/zxing/ResultPointCallback.java
create mode 100644 libraries/zxing/src/com/google/zxing/Writer.java
create mode 100644 libraries/zxing/src/com/google/zxing/WriterException.java
create mode 100644 libraries/zxing/src/com/google/zxing/common/BitArray.java
create mode 100644 libraries/zxing/src/com/google/zxing/common/BitMatrix.java
create mode 100644 libraries/zxing/src/com/google/zxing/common/BitSource.java
create mode 100644 libraries/zxing/src/com/google/zxing/common/CharacterSetECI.java
create mode 100644 libraries/zxing/src/com/google/zxing/common/Collections.java
create mode 100644 libraries/zxing/src/com/google/zxing/common/Comparator.java
create mode 100644 libraries/zxing/src/com/google/zxing/common/DecoderResult.java
create mode 100644 libraries/zxing/src/com/google/zxing/common/DefaultGridSampler.java
create mode 100644 libraries/zxing/src/com/google/zxing/common/DetectorResult.java
create mode 100644 libraries/zxing/src/com/google/zxing/common/ECI.java
create mode 100644 libraries/zxing/src/com/google/zxing/common/GlobalHistogramBinarizer.java
create mode 100644 libraries/zxing/src/com/google/zxing/common/GridSampler.java
create mode 100644 libraries/zxing/src/com/google/zxing/common/HybridBinarizer.java
create mode 100644 libraries/zxing/src/com/google/zxing/common/PerspectiveTransform.java
create mode 100644 libraries/zxing/src/com/google/zxing/common/StringUtils.java
create mode 100644 libraries/zxing/src/com/google/zxing/common/detector/MonochromeRectangleDetector.java
create mode 100644 libraries/zxing/src/com/google/zxing/common/detector/WhiteRectangleDetector.java
create mode 100644 libraries/zxing/src/com/google/zxing/common/reedsolomon/GenericGF.java
create mode 100644 libraries/zxing/src/com/google/zxing/common/reedsolomon/GenericGFPoly.java
create mode 100644 libraries/zxing/src/com/google/zxing/common/reedsolomon/ReedSolomonDecoder.java
create mode 100644 libraries/zxing/src/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.java
create mode 100644 libraries/zxing/src/com/google/zxing/common/reedsolomon/ReedSolomonException.java
create mode 100644 libraries/zxing/src/com/google/zxing/multi/ByQuadrantReader.java
create mode 100644 libraries/zxing/src/com/google/zxing/multi/GenericMultipleBarcodeReader.java
create mode 100644 libraries/zxing/src/com/google/zxing/multi/MultipleBarcodeReader.java
create mode 100644 libraries/zxing/src/com/google/zxing/multi/qrcode/detector/MultiDetector.java
create mode 100644 libraries/zxing/src/com/google/zxing/multi/qrcode/detector/MultiFinderPatternFinder.java
create mode 100644 libraries/zxing/src/com/google/zxing/qrcode/QRCodeWriter.java
create mode 100644 libraries/zxing/src/com/google/zxing/qrcode/decoder/BitMatrixParser.java
create mode 100644 libraries/zxing/src/com/google/zxing/qrcode/decoder/DataBlock.java
create mode 100644 libraries/zxing/src/com/google/zxing/qrcode/decoder/DataMask.java
create mode 100644 libraries/zxing/src/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java
create mode 100644 libraries/zxing/src/com/google/zxing/qrcode/decoder/ErrorCorrectionLevel.java
create mode 100644 libraries/zxing/src/com/google/zxing/qrcode/decoder/FormatInformation.java
create mode 100644 libraries/zxing/src/com/google/zxing/qrcode/decoder/Mode.java
create mode 100644 libraries/zxing/src/com/google/zxing/qrcode/decoder/Version.java
create mode 100644 libraries/zxing/src/com/google/zxing/qrcode/detector/AlignmentPattern.java
create mode 100644 libraries/zxing/src/com/google/zxing/qrcode/detector/AlignmentPatternFinder.java
create mode 100644 libraries/zxing/src/com/google/zxing/qrcode/detector/Detector.java
create mode 100644 libraries/zxing/src/com/google/zxing/qrcode/detector/FinderPattern.java
create mode 100644 libraries/zxing/src/com/google/zxing/qrcode/detector/FinderPatternFinder.java
create mode 100644 libraries/zxing/src/com/google/zxing/qrcode/detector/FinderPatternInfo.java
create mode 100644 libraries/zxing/src/com/google/zxing/qrcode/encoder/BlockPair.java
create mode 100644 libraries/zxing/src/com/google/zxing/qrcode/encoder/ByteMatrix.java
create mode 100644 libraries/zxing/src/com/google/zxing/qrcode/encoder/Encoder.java
create mode 100644 libraries/zxing/src/com/google/zxing/qrcode/encoder/MaskUtil.java
create mode 100644 libraries/zxing/src/com/google/zxing/qrcode/encoder/MatrixUtil.java
create mode 100644 libraries/zxing/src/com/google/zxing/qrcode/encoder/QRCode.java
diff --git a/OpenPGP-Keychain/build.gradle b/OpenPGP-Keychain/build.gradle
index f31b6908c..afc95fee8 100644
--- a/OpenPGP-Keychain/build.gradle
+++ b/OpenPGP-Keychain/build.gradle
@@ -24,6 +24,7 @@ dependencies {
compile project(':libraries:ActionBarSherlock')
compile project(':libraries:HtmlTextView')
compile project(':libraries:StickyListHeaders:library')
+ compile project(':libraries:zxing')
}
android {
diff --git a/OpenPGP-Keychain/project.properties b/OpenPGP-Keychain/project.properties
index 8e240f3c3..73fc44378 100644
--- a/OpenPGP-Keychain/project.properties
+++ b/OpenPGP-Keychain/project.properties
@@ -12,3 +12,4 @@ target=android-19
android.library.reference.1=../libraries/ActionBarSherlock
android.library.reference.2=../libraries/HtmlTextView
android.library.reference.3=../libraries/StickyListHeaders/library
+android.library.reference.4=../libraries/zxing
diff --git a/OpenPGP-Keychain/src/com/google/zxing/BarcodeFormat.java b/OpenPGP-Keychain/src/com/google/zxing/BarcodeFormat.java
deleted file mode 100644
index 1e5d47958..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/BarcodeFormat.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright 2007 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;
-
-import java.util.Hashtable;
-
-/**
- * Enumerates barcode formats known to this package. Please keep alphabetized.
- *
- * @author Sean Owen
- */
-public final class BarcodeFormat {
-
- // No, we can't use an enum here. J2ME doesn't support it.
-
- private static final Hashtable VALUES = new Hashtable();
-
- /** Aztec 2D barcode format. */
- public static final BarcodeFormat AZTEC = new BarcodeFormat("AZTEC");
-
- /** CODABAR 1D format. */
- public static final BarcodeFormat CODABAR = new BarcodeFormat("CODABAR");
-
- /** Code 39 1D format. */
- public static final BarcodeFormat CODE_39 = new BarcodeFormat("CODE_39");
-
- /** Code 93 1D format. */
- public static final BarcodeFormat CODE_93 = new BarcodeFormat("CODE_93");
-
- /** Code 128 1D format. */
- public static final BarcodeFormat CODE_128 = new BarcodeFormat("CODE_128");
-
- /** Data Matrix 2D barcode format. */
- public static final BarcodeFormat DATA_MATRIX = new BarcodeFormat("DATA_MATRIX");
-
- /** EAN-8 1D format. */
- public static final BarcodeFormat EAN_8 = new BarcodeFormat("EAN_8");
-
- /** EAN-13 1D format. */
- public static final BarcodeFormat EAN_13 = new BarcodeFormat("EAN_13");
-
- /** ITF (Interleaved Two of Five) 1D format. */
- public static final BarcodeFormat ITF = new BarcodeFormat("ITF");
-
- /** PDF417 format. */
- public static final BarcodeFormat PDF_417 = new BarcodeFormat("PDF_417");
-
- /** QR Code 2D barcode format. */
- public static final BarcodeFormat QR_CODE = new BarcodeFormat("QR_CODE");
-
- /** RSS 14 */
- public static final BarcodeFormat RSS_14 = new BarcodeFormat("RSS_14");
-
- /** RSS EXPANDED */
- public static final BarcodeFormat RSS_EXPANDED = new BarcodeFormat("RSS_EXPANDED");
-
- /** UPC-A 1D format. */
- public static final BarcodeFormat UPC_A = new BarcodeFormat("UPC_A");
-
- /** UPC-E 1D format. */
- public static final BarcodeFormat UPC_E = new BarcodeFormat("UPC_E");
-
- /** UPC/EAN extension format. Not a stand-alone format. */
- public static final BarcodeFormat UPC_EAN_EXTENSION = new BarcodeFormat("UPC_EAN_EXTENSION");
-
- private final String name;
-
- private BarcodeFormat(String name) {
- this.name = name;
- VALUES.put(name, this);
- }
-
- public String getName() {
- return name;
- }
-
- public String toString() {
- return name;
- }
-
- public static BarcodeFormat valueOf(String name) {
- if (name == null || name.length() == 0) {
- throw new IllegalArgumentException();
- }
- BarcodeFormat format = (BarcodeFormat) VALUES.get(name);
- if (format == null) {
- throw new IllegalArgumentException();
- }
- return format;
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/Binarizer.java b/OpenPGP-Keychain/src/com/google/zxing/Binarizer.java
deleted file mode 100644
index 912a3b556..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/Binarizer.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * 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;
-
-import com.google.zxing.common.BitArray;
-import com.google.zxing.common.BitMatrix;
-
-/**
- * This class hierarchy provides a set of methods to convert luminance data to 1 bit data.
- * It allows the algorithm to vary polymorphically, for example allowing a very expensive
- * thresholding technique for servers and a fast one for mobile. It also permits the implementation
- * to vary, e.g. a JNI version for Android and a Java fallback version for other platforms.
- *
- * @author dswitkin@google.com (Daniel Switkin)
- */
-public abstract class Binarizer {
-
- private final LuminanceSource source;
-
- protected Binarizer(LuminanceSource source) {
- if (source == null) {
- throw new IllegalArgumentException("Source must be non-null.");
- }
- this.source = source;
- }
-
- public LuminanceSource getLuminanceSource() {
- return source;
- }
-
- /**
- * Converts one row of luminance data to 1 bit data. May actually do the conversion, or return
- * cached data. Callers should assume this method is expensive and call it as seldom as possible.
- * This method is intended for decoding 1D barcodes and may choose to apply sharpening.
- * For callers which only examine one row of pixels at a time, the same BitArray should be reused
- * and passed in with each call for performance. However it is legal to keep more than one row
- * at a time if needed.
- *
- * @param y The row to fetch, 0 <= y < bitmap height.
- * @param row An optional preallocated array. If null or too small, it will be ignored.
- * If used, the Binarizer will call BitArray.clear(). Always use the returned object.
- * @return The array of bits for this row (true means black).
- */
- public abstract BitArray getBlackRow(int y, BitArray row) throws NotFoundException;
-
- /**
- * Converts a 2D array of luminance data to 1 bit data. As above, assume this method is expensive
- * and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or
- * may not apply sharpening. Therefore, a row from this matrix may not be identical to one
- * fetched using getBlackRow(), so don't mix and match between them.
- *
- * @return The 2D array of bits for the image (true means black).
- */
- public abstract BitMatrix getBlackMatrix() throws NotFoundException;
-
- /**
- * Creates a new object with the same type as this Binarizer implementation, but with pristine
- * state. This is needed because Binarizer implementations may be stateful, e.g. keeping a cache
- * of 1 bit data. See Effective Java for why we can't use Java's clone() method.
- *
- * @param source The LuminanceSource this Binarizer will operate on.
- * @return A new concrete Binarizer implementation object.
- */
- public abstract Binarizer createBinarizer(LuminanceSource source);
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/BinaryBitmap.java b/OpenPGP-Keychain/src/com/google/zxing/BinaryBitmap.java
deleted file mode 100644
index b97e46705..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/BinaryBitmap.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * 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;
-
-import com.google.zxing.common.BitArray;
-import com.google.zxing.common.BitMatrix;
-
-/**
- * This class is the core bitmap class used by ZXing to represent 1 bit data. Reader objects
- * accept a BinaryBitmap and attempt to decode it.
- *
- * @author dswitkin@google.com (Daniel Switkin)
- */
-public final class BinaryBitmap {
-
- private final Binarizer binarizer;
- private BitMatrix matrix;
-
- public BinaryBitmap(Binarizer binarizer) {
- if (binarizer == null) {
- throw new IllegalArgumentException("Binarizer must be non-null.");
- }
- this.binarizer = binarizer;
- matrix = null;
- }
-
- /**
- * @return The width of the bitmap.
- */
- public int getWidth() {
- return binarizer.getLuminanceSource().getWidth();
- }
-
- /**
- * @return The height of the bitmap.
- */
- public int getHeight() {
- return binarizer.getLuminanceSource().getHeight();
- }
-
- /**
- * Converts one row of luminance data to 1 bit data. May actually do the conversion, or return
- * cached data. Callers should assume this method is expensive and call it as seldom as possible.
- * This method is intended for decoding 1D barcodes and may choose to apply sharpening.
- *
- * @param y The row to fetch, 0 <= y < bitmap height.
- * @param row An optional preallocated array. If null or too small, it will be ignored.
- * If used, the Binarizer will call BitArray.clear(). Always use the returned object.
- * @return The array of bits for this row (true means black).
- */
- public BitArray getBlackRow(int y, BitArray row) throws NotFoundException {
- return binarizer.getBlackRow(y, row);
- }
-
- /**
- * Converts a 2D array of luminance data to 1 bit. As above, assume this method is expensive
- * and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or
- * may not apply sharpening. Therefore, a row from this matrix may not be identical to one
- * fetched using getBlackRow(), so don't mix and match between them.
- *
- * @return The 2D array of bits for the image (true means black).
- */
- public BitMatrix getBlackMatrix() throws NotFoundException {
- // The matrix is created on demand the first time it is requested, then cached. There are two
- // reasons for this:
- // 1. This work will never be done if the caller only installs 1D Reader objects, or if a
- // 1D Reader finds a barcode before the 2D Readers run.
- // 2. This work will only be done once even if the caller installs multiple 2D Readers.
- if (matrix == null) {
- matrix = binarizer.getBlackMatrix();
- }
- return matrix;
- }
-
- /**
- * @return Whether this bitmap can be cropped.
- */
- public boolean isCropSupported() {
- return binarizer.getLuminanceSource().isCropSupported();
- }
-
- /**
- * Returns a new object with cropped image data. Implementations may keep a reference to the
- * original data rather than a copy. Only callable if isCropSupported() is true.
- *
- * @param left The left coordinate, 0 <= left < getWidth().
- * @param top The top coordinate, 0 <= top <= getHeight().
- * @param width The width of the rectangle to crop.
- * @param height The height of the rectangle to crop.
- * @return A cropped version of this object.
- */
- public BinaryBitmap crop(int left, int top, int width, int height) {
- LuminanceSource newSource = binarizer.getLuminanceSource().crop(left, top, width, height);
- return new BinaryBitmap(binarizer.createBinarizer(newSource));
- }
-
- /**
- * @return Whether this bitmap supports counter-clockwise rotation.
- */
- public boolean isRotateSupported() {
- return binarizer.getLuminanceSource().isRotateSupported();
- }
-
- /**
- * Returns a new object with rotated image data. Only callable if isRotateSupported() is true.
- *
- * @return A rotated version of this object.
- */
- public BinaryBitmap rotateCounterClockwise() {
- LuminanceSource newSource = binarizer.getLuminanceSource().rotateCounterClockwise();
- return new BinaryBitmap(binarizer.createBinarizer(newSource));
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/ChecksumException.java b/OpenPGP-Keychain/src/com/google/zxing/ChecksumException.java
deleted file mode 100644
index dedb4be99..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/ChecksumException.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2007 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;
-
-/**
- * Thrown when a barcode was successfully detected and decoded, but
- * was not returned because its checksum feature failed.
- *
- * @author Sean Owen
- */
-public final class ChecksumException extends ReaderException {
-
- private static final ChecksumException instance = new ChecksumException();
-
- private ChecksumException() {
- // do nothing
- }
-
- public static ChecksumException getChecksumInstance() {
- return instance;
- }
-
-}
\ No newline at end of file
diff --git a/OpenPGP-Keychain/src/com/google/zxing/DecodeHintType.java b/OpenPGP-Keychain/src/com/google/zxing/DecodeHintType.java
deleted file mode 100644
index 20b922ca1..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/DecodeHintType.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2007 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;
-
-/**
- * Encapsulates a type of hint that a caller may pass to a barcode reader to help it
- * more quickly or accurately decode it. It is up to implementations to decide what,
- * if anything, to do with the information that is supplied.
- *
- * @author Sean Owen
- * @author dswitkin@google.com (Daniel Switkin)
- * @see Reader#decode(BinaryBitmap,java.util.Hashtable)
- */
-public final class DecodeHintType {
-
- // No, we can't use an enum here. J2ME doesn't support it.
-
- /**
- * Unspecified, application-specific hint. Maps to an unspecified {@link Object}.
- */
- public static final DecodeHintType OTHER = new DecodeHintType();
-
- /**
- * Image is a pure monochrome image of a barcode. Doesn't matter what it maps to;
- * use {@link Boolean#TRUE}.
- */
- public static final DecodeHintType PURE_BARCODE = new DecodeHintType();
-
- /**
- * Image is known to be of one of a few possible formats.
- * Maps to a {@link java.util.Vector} of {@link BarcodeFormat}s.
- */
- public static final DecodeHintType POSSIBLE_FORMATS = new DecodeHintType();
-
- /**
- * Spend more time to try to find a barcode; optimize for accuracy, not speed.
- * Doesn't matter what it maps to; use {@link Boolean#TRUE}.
- */
- public static final DecodeHintType TRY_HARDER = new DecodeHintType();
-
- /**
- * Specifies what character encoding to use when decoding, where applicable (type String)
- */
- public static final DecodeHintType CHARACTER_SET = new DecodeHintType();
-
- /**
- * Allowed lengths of encoded data -- reject anything else. Maps to an int[].
- */
- public static final DecodeHintType ALLOWED_LENGTHS = new DecodeHintType();
-
- /**
- * Assume Code 39 codes employ a check digit. Maps to {@link Boolean}.
- */
- public static final DecodeHintType ASSUME_CODE_39_CHECK_DIGIT = new DecodeHintType();
-
- /**
- * The caller needs to be notified via callback when a possible {@link ResultPoint}
- * is found. Maps to a {@link ResultPointCallback}.
- */
- public static final DecodeHintType NEED_RESULT_POINT_CALLBACK = new DecodeHintType();
-
- private DecodeHintType() {
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/EncodeHintType.java b/OpenPGP-Keychain/src/com/google/zxing/EncodeHintType.java
deleted file mode 100644
index 35afc1530..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/EncodeHintType.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 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;
-
-/**
- * These are a set of hints that you may pass to Writers to specify their behavior.
- *
- * @author dswitkin@google.com (Daniel Switkin)
- */
-public final class EncodeHintType {
-
- /**
- * Specifies what degree of error correction to use, for example in QR Codes (type Integer).
- */
- public static final EncodeHintType ERROR_CORRECTION = new EncodeHintType();
-
- /**
- * Specifies what character encoding to use where applicable (type String)
- */
- public static final EncodeHintType CHARACTER_SET = new EncodeHintType();
-
- private EncodeHintType() {
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/FormatException.java b/OpenPGP-Keychain/src/com/google/zxing/FormatException.java
deleted file mode 100644
index 6967e93de..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/FormatException.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2007 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;
-
-/**
- * Thrown when a barcode was successfully detected, but some aspect of
- * the content did not conform to the barcode's format rules. This could have
- * been due to a mis-detection.
- *
- * @author Sean Owen
- */
-public final class FormatException extends ReaderException {
-
- private static final FormatException instance = new FormatException();
-
- private FormatException() {
- // do nothing
- }
-
- public static FormatException getFormatInstance() {
- return instance;
- }
-
-}
\ No newline at end of file
diff --git a/OpenPGP-Keychain/src/com/google/zxing/LuminanceSource.java b/OpenPGP-Keychain/src/com/google/zxing/LuminanceSource.java
deleted file mode 100644
index 4b6d4539f..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/LuminanceSource.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * 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;
-
-/**
- * The purpose of this class hierarchy is to abstract different bitmap implementations across
- * platforms into a standard interface for requesting greyscale luminance values. The interface
- * only provides immutable methods; therefore crop and rotation create copies. This is to ensure
- * that one Reader does not modify the original luminance source and leave it in an unknown state
- * for other Readers in the chain.
- *
- * @author dswitkin@google.com (Daniel Switkin)
- */
-public abstract class LuminanceSource {
-
- private final int width;
- private final int height;
-
- protected LuminanceSource(int width, int height) {
- this.width = width;
- this.height = height;
- }
-
- /**
- * Fetches one row of luminance data from the underlying platform's bitmap. Values range from
- * 0 (black) to 255 (white). Because Java does not have an unsigned byte type, callers will have
- * to bitwise and with 0xff for each value. It is preferable for implementations of this method
- * to only fetch this row rather than the whole image, since no 2D Readers may be installed and
- * getMatrix() may never be called.
- *
- * @param y The row to fetch, 0 <= y < getHeight().
- * @param row An optional preallocated array. If null or too small, it will be ignored.
- * Always use the returned object, and ignore the .length of the array.
- * @return An array containing the luminance data.
- */
- public abstract byte[] getRow(int y, byte[] row);
-
- /**
- * Fetches luminance data for the underlying bitmap. Values should be fetched using:
- * int luminance = array[y * width + x] & 0xff;
- *
- * @return A row-major 2D array of luminance values. Do not use result.length as it may be
- * larger than width * height bytes on some platforms. Do not modify the contents
- * of the result.
- */
- public abstract byte[] getMatrix();
-
- /**
- * @return The width of the bitmap.
- */
- public final int getWidth() {
- return width;
- }
-
- /**
- * @return The height of the bitmap.
- */
- public final int getHeight() {
- return height;
- }
-
- /**
- * @return Whether this subclass supports cropping.
- */
- public boolean isCropSupported() {
- return false;
- }
-
- /**
- * Returns a new object with cropped image data. Implementations may keep a reference to the
- * original data rather than a copy. Only callable if isCropSupported() is true.
- *
- * @param left The left coordinate, 0 <= left < getWidth().
- * @param top The top coordinate, 0 <= top <= getHeight().
- * @param width The width of the rectangle to crop.
- * @param height The height of the rectangle to crop.
- * @return A cropped version of this object.
- */
- public LuminanceSource crop(int left, int top, int width, int height) {
- throw new RuntimeException("This luminance source does not support cropping.");
- }
-
- /**
- * @return Whether this subclass supports counter-clockwise rotation.
- */
- public boolean isRotateSupported() {
- return false;
- }
-
- /**
- * Returns a new object with rotated image data. Only callable if isRotateSupported() is true.
- *
- * @return A rotated version of this object.
- */
- public LuminanceSource rotateCounterClockwise() {
- throw new RuntimeException("This luminance source does not support rotation.");
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/NotFoundException.java b/OpenPGP-Keychain/src/com/google/zxing/NotFoundException.java
deleted file mode 100644
index dedab8dfc..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/NotFoundException.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2007 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;
-
-/**
- * Thrown when a barcode was not found in the image. It might have been
- * partially detected but could not be confirmed.
- *
- * @author Sean Owen
- */
-public final class NotFoundException extends ReaderException {
-
- private static final NotFoundException instance = new NotFoundException();
-
- private NotFoundException() {
- // do nothing
- }
-
- public static NotFoundException getNotFoundInstance() {
- return instance;
- }
-
-}
\ No newline at end of file
diff --git a/OpenPGP-Keychain/src/com/google/zxing/Reader.java b/OpenPGP-Keychain/src/com/google/zxing/Reader.java
deleted file mode 100644
index 47e843ba6..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/Reader.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2007 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;
-
-import java.util.Hashtable;
-
-/**
- * Implementations of this interface can decode an image of a barcode in some format into
- * the String it encodes. For example, {@link com.google.zxing.qrcode.QRCodeReader} can
- * decode a QR code. The decoder may optionally receive hints from the caller which may help
- * it decode more quickly or accurately.
- *
- * See {@link com.google.zxing.MultiFormatReader}, which attempts to determine what barcode
- * format is present within the image as well, and then decodes it accordingly.
- *
- * @author Sean Owen
- * @author dswitkin@google.com (Daniel Switkin)
- */
-public interface Reader {
-
- /**
- * Locates and decodes a barcode in some format within an image.
- *
- * @param image image of barcode to decode
- * @return String which the barcode encodes
- * @throws NotFoundException if the barcode cannot be located or decoded for any reason
- */
- Result decode(BinaryBitmap image) throws NotFoundException, ChecksumException, FormatException;
-
- /**
- * Locates and decodes a barcode in some format within an image. This method also accepts
- * hints, each possibly associated to some data, which may help the implementation decode.
- *
- * @param image image of barcode to decode
- * @param hints passed as a {@link java.util.Hashtable} from {@link com.google.zxing.DecodeHintType}
- * to arbitrary data. The
- * meaning of the data depends upon the hint type. The implementation may or may not do
- * anything with these hints.
- * @return String which the barcode encodes
- * @throws NotFoundException if the barcode cannot be located or decoded for any reason
- */
- Result decode(BinaryBitmap image, Hashtable hints) throws NotFoundException, ChecksumException, FormatException;
-
- /**
- * Resets any internal state the implementation has after a decode, to prepare it
- * for reuse.
- */
- void reset();
-
-}
\ No newline at end of file
diff --git a/OpenPGP-Keychain/src/com/google/zxing/ReaderException.java b/OpenPGP-Keychain/src/com/google/zxing/ReaderException.java
deleted file mode 100644
index 224a497e5..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/ReaderException.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright 2007 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;
-
-/**
- * The general exception class throw when something goes wrong during decoding of a barcode.
- * This includes, but is not limited to, failing checksums / error correction algorithms, being
- * unable to locate finder timing patterns, and so on.
- *
- * @author Sean Owen
- */
-public abstract class ReaderException extends Exception {
-
- // TODO: Currently we throw up to 400 ReaderExceptions while scanning a single 240x240 image before
- // rejecting it. This involves a lot of overhead and memory allocation, and affects both performance
- // and latency on continuous scan clients. In the future, we should change all the decoders not to
- // throw exceptions for routine events, like not finding a barcode on a given row. Instead, we
- // should return error codes back to the callers, and simply delete this class. In the mean time, I
- // have altered this class to be as lightweight as possible, by ignoring the exception string, and
- // by disabling the generation of stack traces, which is especially time consuming. These are just
- // temporary measures, pending the big cleanup.
-
- //private static final ReaderException instance = new ReaderException();
-
- // EXCEPTION TRACKING SUPPORT
- // Identifies who is throwing exceptions and how often. To use:
- //
- // 1. Uncomment these lines and the code below which uses them.
- // 2. Uncomment the two corresponding lines in j2se/CommandLineRunner.decode()
- // 3. Change core to build as Java 1.5 temporarily
-// private static int exceptionCount = 0;
-// private static Map throwers = new HashMap(32);
-
- ReaderException() {
- // do nothing
- }
-
- //public static ReaderException getInstance() {
-// Exception e = new Exception();
-// // Take the stack frame before this one.
-// StackTraceElement stack = e.getStackTrace()[1];
-// String key = stack.getClassName() + "." + stack.getMethodName() + "(), line " +
-// stack.getLineNumber();
-// if (throwers.containsKey(key)) {
-// Integer value = throwers.get(key);
-// value++;
-// throwers.put(key, value);
-// } else {
-// throwers.put(key, 1);
-// }
-// exceptionCount++;
-
- //return instance;
- //}
-
-// public static int getExceptionCountAndReset() {
-// int temp = exceptionCount;
-// exceptionCount = 0;
-// return temp;
-// }
-//
-// public static String getThrowersAndReset() {
-// StringBuilder builder = new StringBuilder(1024);
-// Object[] keys = throwers.keySet().toArray();
-// for (int x = 0; x < keys.length; x++) {
-// String key = (String) keys[x];
-// Integer value = throwers.get(key);
-// builder.append(key);
-// builder.append(": ");
-// builder.append(value);
-// builder.append("\n");
-// }
-// throwers.clear();
-// return builder.toString();
-// }
-
- // Prevent stack traces from being taken
- // srowen says: huh, my IDE is saying this is not an override. native methods can't be overridden?
- // This, at least, does not hurt. Because we use a singleton pattern here, it doesn't matter anyhow.
- public final Throwable fillInStackTrace() {
- return null;
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/Result.java b/OpenPGP-Keychain/src/com/google/zxing/Result.java
deleted file mode 100644
index ee1af527e..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/Result.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright 2007 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;
-
-import java.util.Enumeration;
-import java.util.Hashtable;
-
-/**
- *
Encapsulates the result of decoding a barcode within an image.
- *
- * @author Sean Owen
- */
-public final class Result {
-
- private final String text;
- private final byte[] rawBytes;
- private ResultPoint[] resultPoints;
- private final BarcodeFormat format;
- private Hashtable resultMetadata;
- private final long timestamp;
-
- public Result(String text,
- byte[] rawBytes,
- ResultPoint[] resultPoints,
- BarcodeFormat format) {
- this(text, rawBytes, resultPoints, format, System.currentTimeMillis());
- }
-
- public Result(String text,
- byte[] rawBytes,
- ResultPoint[] resultPoints,
- BarcodeFormat format,
- long timestamp) {
- if (text == null && rawBytes == null) {
- throw new IllegalArgumentException("Text and bytes are null");
- }
- this.text = text;
- this.rawBytes = rawBytes;
- this.resultPoints = resultPoints;
- this.format = format;
- this.resultMetadata = null;
- this.timestamp = timestamp;
- }
-
- /**
- * @return raw text encoded by the barcode, if applicable, otherwise null
- */
- public String getText() {
- return text;
- }
-
- /**
- * @return raw bytes encoded by the barcode, if applicable, otherwise null
- */
- public byte[] getRawBytes() {
- return rawBytes;
- }
-
- /**
- * @return points related to the barcode in the image. These are typically points
- * identifying finder patterns or the corners of the barcode. The exact meaning is
- * specific to the type of barcode that was decoded.
- */
- public ResultPoint[] getResultPoints() {
- return resultPoints;
- }
-
- /**
- * @return {@link BarcodeFormat} representing the format of the barcode that was decoded
- */
- public BarcodeFormat getBarcodeFormat() {
- return format;
- }
-
- /**
- * @return {@link Hashtable} mapping {@link ResultMetadataType} keys to values. May be
- * null. This contains optional metadata about what was detected about the barcode,
- * like orientation.
- */
- public Hashtable getResultMetadata() {
- return resultMetadata;
- }
-
- public void putMetadata(ResultMetadataType type, Object value) {
- if (resultMetadata == null) {
- resultMetadata = new Hashtable(3);
- }
- resultMetadata.put(type, value);
- }
-
- public void putAllMetadata(Hashtable metadata) {
- if (metadata != null) {
- if (resultMetadata == null) {
- resultMetadata = metadata;
- } else {
- Enumeration e = metadata.keys();
- while (e.hasMoreElements()) {
- ResultMetadataType key = (ResultMetadataType) e.nextElement();
- Object value = metadata.get(key);
- resultMetadata.put(key, value);
- }
- }
- }
- }
-
- public void addResultPoints(ResultPoint[] newPoints) {
- if (resultPoints == null) {
- resultPoints = newPoints;
- } else if (newPoints != null && newPoints.length > 0) {
- ResultPoint[] allPoints = new ResultPoint[resultPoints.length + newPoints.length];
- System.arraycopy(resultPoints, 0, allPoints, 0, resultPoints.length);
- System.arraycopy(newPoints, 0, allPoints, resultPoints.length, newPoints.length);
- resultPoints = allPoints;
- }
- }
-
- public long getTimestamp() {
- return timestamp;
- }
-
- public String toString() {
- if (text == null) {
- return "[" + rawBytes.length + " bytes]";
- } else {
- return text;
- }
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/ResultMetadataType.java b/OpenPGP-Keychain/src/com/google/zxing/ResultMetadataType.java
deleted file mode 100644
index 33d69d9c5..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/ResultMetadataType.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright 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;
-
-import java.util.Hashtable;
-
-/**
- * Represents some type of metadata about the result of the decoding that the decoder
- * wishes to communicate back to the caller.
- *
- * @author Sean Owen
- */
-public final class ResultMetadataType {
-
- // No, we can't use an enum here. J2ME doesn't support it.
-
- private static final Hashtable VALUES = new Hashtable();
-
- // No, we can't use an enum here. J2ME doesn't support it.
-
- /**
- * Unspecified, application-specific metadata. Maps to an unspecified {@link Object}.
- */
- public static final ResultMetadataType OTHER = new ResultMetadataType("OTHER");
-
- /**
- * Denotes the likely approximate orientation of the barcode in the image. This value
- * is given as degrees rotated clockwise from the normal, upright orientation.
- * For example a 1D barcode which was found by reading top-to-bottom would be
- * said to have orientation "90". This key maps to an {@link Integer} whose
- * value is in the range [0,360).
- */
- public static final ResultMetadataType ORIENTATION = new ResultMetadataType("ORIENTATION");
-
- /**
- *
2D barcode formats typically encode text, but allow for a sort of 'byte mode'
- * which is sometimes used to encode binary data. While {@link Result} makes available
- * the complete raw bytes in the barcode for these formats, it does not offer the bytes
- * from the byte segments alone.
- *
- *
This maps to a {@link java.util.Vector} of byte arrays corresponding to the
- * raw bytes in the byte segments in the barcode, in order.
- */
- public static final ResultMetadataType BYTE_SEGMENTS = new ResultMetadataType("BYTE_SEGMENTS");
-
- /**
- * Error correction level used, if applicable. The value type depends on the
- * format, but is typically a String.
- */
- public static final ResultMetadataType ERROR_CORRECTION_LEVEL = new ResultMetadataType("ERROR_CORRECTION_LEVEL");
-
- /**
- * For some periodicals, indicates the issue number as an {@link Integer}.
- */
- public static final ResultMetadataType ISSUE_NUMBER = new ResultMetadataType("ISSUE_NUMBER");
-
- /**
- * For some products, indicates the suggested retail price in the barcode as a
- * formatted {@link String}.
- */
- public static final ResultMetadataType SUGGESTED_PRICE = new ResultMetadataType("SUGGESTED_PRICE");
-
- /**
- * For some products, the possible country of manufacture as a {@link String} denoting the
- * ISO country code. Some map to multiple possible countries, like "US/CA".
- */
- public static final ResultMetadataType POSSIBLE_COUNTRY = new ResultMetadataType("POSSIBLE_COUNTRY");
-
- private final String name;
-
- private ResultMetadataType(String name) {
- this.name = name;
- VALUES.put(name, this);
- }
-
- public String getName() {
- return name;
- }
-
- public String toString() {
- return name;
- }
-
- public static ResultMetadataType valueOf(String name) {
- if (name == null || name.length() == 0) {
- throw new IllegalArgumentException();
- }
- ResultMetadataType format = (ResultMetadataType) VALUES.get(name);
- if (format == null) {
- throw new IllegalArgumentException();
- }
- return format;
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/ResultPoint.java b/OpenPGP-Keychain/src/com/google/zxing/ResultPoint.java
deleted file mode 100644
index 366ae3855..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/ResultPoint.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright 2007 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;
-
-/**
- *
Encapsulates a point of interest in an image containing a barcode. Typically, this
- * would be the location of a finder pattern or the corner of the barcode, for example.
- *
- * @author Sean Owen
- */
-public class ResultPoint {
-
- private final float x;
- private final float y;
-
- public ResultPoint(float x, float y) {
- this.x = x;
- this.y = y;
- }
-
- public final float getX() {
- return x;
- }
-
- public final float getY() {
- return y;
- }
-
- public boolean equals(Object other) {
- if (other instanceof ResultPoint) {
- ResultPoint otherPoint = (ResultPoint) other;
- return x == otherPoint.x && y == otherPoint.y;
- }
- return false;
- }
-
- public int hashCode() {
- return 31 * Float.floatToIntBits(x) + Float.floatToIntBits(y);
- }
-
- public String toString() {
- StringBuffer result = new StringBuffer(25);
- result.append('(');
- result.append(x);
- result.append(',');
- result.append(y);
- result.append(')');
- return result.toString();
- }
-
- /**
- *
Orders an array of three ResultPoints in an order [A,B,C] such that AB < AC and
- * BC < AC and the angle between BC and BA is less than 180 degrees.
- */
- public static void orderBestPatterns(ResultPoint[] patterns) {
-
- // Find distances between pattern centers
- float zeroOneDistance = distance(patterns[0], patterns[1]);
- float oneTwoDistance = distance(patterns[1], patterns[2]);
- float zeroTwoDistance = distance(patterns[0], patterns[2]);
-
- ResultPoint pointA;
- ResultPoint pointB;
- ResultPoint pointC;
- // Assume one closest to other two is B; A and C will just be guesses at first
- if (oneTwoDistance >= zeroOneDistance && oneTwoDistance >= zeroTwoDistance) {
- pointB = patterns[0];
- pointA = patterns[1];
- pointC = patterns[2];
- } else if (zeroTwoDistance >= oneTwoDistance && zeroTwoDistance >= zeroOneDistance) {
- pointB = patterns[1];
- pointA = patterns[0];
- pointC = patterns[2];
- } else {
- pointB = patterns[2];
- pointA = patterns[0];
- pointC = patterns[1];
- }
-
- // Use cross product to figure out whether A and C are correct or flipped.
- // This asks whether BC x BA has a positive z component, which is the arrangement
- // we want for A, B, C. If it's negative, then we've got it flipped around and
- // should swap A and C.
- if (crossProductZ(pointA, pointB, pointC) < 0.0f) {
- ResultPoint temp = pointA;
- pointA = pointC;
- pointC = temp;
- }
-
- patterns[0] = pointA;
- patterns[1] = pointB;
- patterns[2] = pointC;
- }
-
-
- /**
- * @return distance between two points
- */
- public static float distance(ResultPoint pattern1, ResultPoint pattern2) {
- float xDiff = pattern1.getX() - pattern2.getX();
- float yDiff = pattern1.getY() - pattern2.getY();
- return (float) Math.sqrt((double) (xDiff * xDiff + yDiff * yDiff));
- }
-
- /**
- * Returns the z component of the cross product between vectors BC and BA.
- */
- private static float crossProductZ(ResultPoint pointA, ResultPoint pointB, ResultPoint pointC) {
- float bX = pointB.x;
- float bY = pointB.y;
- return ((pointC.x - bX) * (pointA.y - bY)) - ((pointC.y - bY) * (pointA.x - bX));
- }
-
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/ResultPointCallback.java b/OpenPGP-Keychain/src/com/google/zxing/ResultPointCallback.java
deleted file mode 100644
index 0c85410bc..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/ResultPointCallback.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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;
-
-/**
- * Callback which is invoked when a possible result point (significant
- * point in the barcode image such as a corner) is found.
- *
- * @see DecodeHintType#NEED_RESULT_POINT_CALLBACK
- */
-public interface ResultPointCallback {
-
- void foundPossibleResultPoint(ResultPoint point);
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/Writer.java b/OpenPGP-Keychain/src/com/google/zxing/Writer.java
deleted file mode 100644
index 6474ca7e2..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/Writer.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 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;
-
-import com.google.zxing.common.BitMatrix;
-
-import java.util.Hashtable;
-
-/**
- * The base class for all objects which encode/generate a barcode image.
- *
- * @author dswitkin@google.com (Daniel Switkin)
- */
-public interface Writer {
-
- /**
- * Encode a barcode using the default settings.
- *
- * @param contents The contents to encode in the barcode
- * @param format The barcode format to generate
- * @param width The preferred width in pixels
- * @param height The preferred height in pixels
- * @return The generated barcode as a Matrix of unsigned bytes (0 == black, 255 == white)
- */
- BitMatrix encode(String contents, BarcodeFormat format, int width, int height)
- throws WriterException;
-
- /**
- *
- * @param contents The contents to encode in the barcode
- * @param format The barcode format to generate
- * @param width The preferred width in pixels
- * @param height The preferred height in pixels
- * @param hints Additional parameters to supply to the encoder
- * @return The generated barcode as a Matrix of unsigned bytes (0 == black, 255 == white)
- */
- BitMatrix encode(String contents, BarcodeFormat format, int width, int height, Hashtable hints)
- throws WriterException;
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/WriterException.java b/OpenPGP-Keychain/src/com/google/zxing/WriterException.java
deleted file mode 100644
index 0c19af01d..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/WriterException.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 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;
-
-/**
- * A base class which covers the range of exceptions which may occur when encoding a barcode using
- * the Writer framework.
- *
- * @author dswitkin@google.com (Daniel Switkin)
- */
-public final class WriterException extends Exception {
-
- public WriterException() {
- super();
- }
-
- public WriterException(String message) {
- super(message);
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/BitArray.java b/OpenPGP-Keychain/src/com/google/zxing/common/BitArray.java
deleted file mode 100644
index 6eb0d57c6..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/common/BitArray.java
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * Copyright 2007 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.common;
-
-/**
- *
A simple, fast array of bits, represented compactly by an array of ints internally.
- *
- * @author Sean Owen
- */
-public final class BitArray {
- // I have changed these members to be public so ProGuard can inline get() and set(). Ideally
- // they'd be private and we'd use the -allowaccessmodification flag, but Dalvik rejects the
- // resulting binary at runtime on Android. If we find a solution to this, these should be changed
- // back to private.
- public int[] bits;
- public int size;
-
- public BitArray() {
- this.size = 0;
- this.bits = new int[1];
- }
-
- public BitArray(int size) {
- this.size = size;
- this.bits = makeArray(size);
- }
-
- public int getSize() {
- return size;
- }
-
- public int getSizeInBytes() {
- return (size + 7) >> 3;
- }
-
- private void ensureCapacity(int size) {
- if (size > bits.length << 5) {
- int[] newBits = makeArray(size);
- System.arraycopy(bits, 0, newBits, 0, bits.length);
- this.bits = newBits;
- }
- }
-
- /**
- * @param i bit to get
- * @return true iff bit i is set
- */
- public boolean get(int i) {
- return (bits[i >> 5] & (1 << (i & 0x1F))) != 0;
- }
-
- /**
- * Sets bit i.
- *
- * @param i bit to set
- */
- public void set(int i) {
- bits[i >> 5] |= 1 << (i & 0x1F);
- }
-
- /**
- * Flips bit i.
- *
- * @param i bit to set
- */
- public void flip(int i) {
- bits[i >> 5] ^= 1 << (i & 0x1F);
- }
-
- /**
- * Sets a block of 32 bits, starting at bit i.
- *
- * @param i first bit to set
- * @param newBits the new value of the next 32 bits. Note again that the least-significant bit
- * corresponds to bit i, the next-least-significant to i+1, and so on.
- */
- public void setBulk(int i, int newBits) {
- bits[i >> 5] = newBits;
- }
-
- /**
- * Clears all bits (sets to false).
- */
- public void clear() {
- int max = bits.length;
- for (int i = 0; i < max; i++) {
- bits[i] = 0;
- }
- }
-
- /**
- * Efficient method to check if a range of bits is set, or not set.
- *
- * @param start start of range, inclusive.
- * @param end end of range, exclusive
- * @param value if true, checks that bits in range are set, otherwise checks that they are not set
- * @return true iff all bits are set or not set in range, according to value argument
- * @throws IllegalArgumentException if end is less than or equal to start
- */
- public boolean isRange(int start, int end, boolean value) {
- if (end < start) {
- throw new IllegalArgumentException();
- }
- if (end == start) {
- return true; // empty range matches
- }
- end--; // will be easier to treat this as the last actually set bit -- inclusive
- int firstInt = start >> 5;
- int lastInt = end >> 5;
- for (int i = firstInt; i <= lastInt; i++) {
- int firstBit = i > firstInt ? 0 : start & 0x1F;
- int lastBit = i < lastInt ? 31 : end & 0x1F;
- int mask;
- if (firstBit == 0 && lastBit == 31) {
- mask = -1;
- } else {
- mask = 0;
- for (int j = firstBit; j <= lastBit; j++) {
- mask |= 1 << j;
- }
- }
-
- // Return false if we're looking for 1s and the masked bits[i] isn't all 1s (that is,
- // equals the mask, or we're looking for 0s and the masked portion is not all 0s
- if ((bits[i] & mask) != (value ? mask : 0)) {
- return false;
- }
- }
- return true;
- }
-
- public void appendBit(boolean bit) {
- ensureCapacity(size + 1);
- if (bit) {
- bits[size >> 5] |= (1 << (size & 0x1F));
- }
- size++;
- }
-
- /**
- * Appends the least-significant bits, from value, in order from most-significant to
- * least-significant. For example, appending 6 bits from 0x000001E will append the bits
- * 0, 1, 1, 1, 1, 0 in that order.
- */
- public void appendBits(int value, int numBits) {
- if (numBits < 0 || numBits > 32) {
- throw new IllegalArgumentException("Num bits must be between 0 and 32");
- }
- ensureCapacity(size + numBits);
- for (int numBitsLeft = numBits; numBitsLeft > 0; numBitsLeft--) {
- appendBit(((value >> (numBitsLeft - 1)) & 0x01) == 1);
- }
- }
-
- public void appendBitArray(BitArray other) {
- int otherSize = other.getSize();
- ensureCapacity(size + otherSize);
- for (int i = 0; i < otherSize; i++) {
- appendBit(other.get(i));
- }
- }
-
- public void xor(BitArray other) {
- if (bits.length != other.bits.length) {
- throw new IllegalArgumentException("Sizes don't match");
- }
- for (int i = 0; i < bits.length; i++) {
- // The last byte could be incomplete (i.e. not have 8 bits in
- // it) but there is no problem since 0 XOR 0 == 0.
- bits[i] ^= other.bits[i];
- }
- }
-
- /**
- *
- * @param bitOffset first bit to start writing
- * @param array array to write into. Bytes are written most-significant byte first. This is the opposite
- * of the internal representation, which is exposed by {@link #getBitArray()}
- * @param offset position in array to start writing
- * @param numBytes how many bytes to write
- */
- public void toBytes(int bitOffset, byte[] array, int offset, int numBytes) {
- for (int i = 0; i < numBytes; i++) {
- int theByte = 0;
- for (int j = 0; j < 8; j++) {
- if (get(bitOffset)) {
- theByte |= 1 << (7 - j);
- }
- bitOffset++;
- }
- array[offset + i] = (byte) theByte;
- }
- }
-
- /**
- * @return underlying array of ints. The first element holds the first 32 bits, and the least
- * significant bit is bit 0.
- */
- public int[] getBitArray() {
- return bits;
- }
-
- /**
- * Reverses all bits in the array.
- */
- public void reverse() {
- int[] newBits = new int[bits.length];
- int size = this.size;
- for (int i = 0; i < size; i++) {
- if (get(size - i - 1)) {
- newBits[i >> 5] |= 1 << (i & 0x1F);
- }
- }
- bits = newBits;
- }
-
- private static int[] makeArray(int size) {
- return new int[(size + 31) >> 5];
- }
-
- public String toString() {
- StringBuffer result = new StringBuffer(size);
- for (int i = 0; i < size; i++) {
- if ((i & 0x07) == 0) {
- result.append(' ');
- }
- result.append(get(i) ? 'X' : '.');
- }
- return result.toString();
- }
-
-}
\ No newline at end of file
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/BitMatrix.java b/OpenPGP-Keychain/src/com/google/zxing/common/BitMatrix.java
deleted file mode 100644
index 8bf75b289..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/common/BitMatrix.java
+++ /dev/null
@@ -1,247 +0,0 @@
-/*
- * Copyright 2007 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.common;
-
-/**
- *
Represents a 2D matrix of bits. In function arguments below, and throughout the common
- * module, x is the column position, and y is the row position. The ordering is always x, y.
- * The origin is at the top-left.
- *
- *
Internally the bits are represented in a 1-D array of 32-bit ints. However, each row begins
- * with a new int. This is done intentionally so that we can copy out a row into a BitArray very
- * efficiently.
- *
- *
The ordering of bits is row-major. Within each int, the least significant bits are used first,
- * meaning they represent lower x values. This is compatible with BitArray's implementation.
- *
- * @author Sean Owen
- * @author dswitkin@google.com (Daniel Switkin)
- */
-public final class BitMatrix {
- // Just like BitArray, these need to be public so ProGuard can inline them.
- public final int width;
- public final int height;
- public final int rowSize;
- public final int[] bits;
-
- // A helper to construct a square matrix.
- public BitMatrix(int dimension) {
- this(dimension, dimension);
- }
-
- public BitMatrix(int width, int height) {
- if (width < 1 || height < 1) {
- throw new IllegalArgumentException("Both dimensions must be greater than 0");
- }
- this.width = width;
- this.height = height;
- this.rowSize = (width + 31) >> 5;
- bits = new int[rowSize * height];
- }
-
- /**
- *
Gets the requested bit, where true means black.
- *
- * @param x The horizontal component (i.e. which column)
- * @param y The vertical component (i.e. which row)
- * @return value of given bit in matrix
- */
- public boolean get(int x, int y) {
- int offset = y * rowSize + (x >> 5);
- return ((bits[offset] >>> (x & 0x1f)) & 1) != 0;
- }
-
- /**
- *
Sets the given bit to true.
- *
- * @param x The horizontal component (i.e. which column)
- * @param y The vertical component (i.e. which row)
- */
- public void set(int x, int y) {
- int offset = y * rowSize + (x >> 5);
- bits[offset] |= 1 << (x & 0x1f);
- }
-
- /**
- *
Flips the given bit.
- *
- * @param x The horizontal component (i.e. which column)
- * @param y The vertical component (i.e. which row)
- */
- public void flip(int x, int y) {
- int offset = y * rowSize + (x >> 5);
- bits[offset] ^= 1 << (x & 0x1f);
- }
-
- /**
- * Clears all bits (sets to false).
- */
- public void clear() {
- int max = bits.length;
- for (int i = 0; i < max; i++) {
- bits[i] = 0;
- }
- }
-
- /**
- *
Sets a square region of the bit matrix to true.
- *
- * @param left The horizontal position to begin at (inclusive)
- * @param top The vertical position to begin at (inclusive)
- * @param width The width of the region
- * @param height The height of the region
- */
- public void setRegion(int left, int top, int width, int height) {
- if (top < 0 || left < 0) {
- throw new IllegalArgumentException("Left and top must be nonnegative");
- }
- if (height < 1 || width < 1) {
- throw new IllegalArgumentException("Height and width must be at least 1");
- }
- int right = left + width;
- int bottom = top + height;
- if (bottom > this.height || right > this.width) {
- throw new IllegalArgumentException("The region must fit inside the matrix");
- }
- for (int y = top; y < bottom; y++) {
- int offset = y * rowSize;
- for (int x = left; x < right; x++) {
- bits[offset + (x >> 5)] |= 1 << (x & 0x1f);
- }
- }
- }
-
- /**
- * A fast method to retrieve one row of data from the matrix as a BitArray.
- *
- * @param y The row to retrieve
- * @param row An optional caller-allocated BitArray, will be allocated if null or too small
- * @return The resulting BitArray - this reference should always be used even when passing
- * your own row
- */
- public BitArray getRow(int y, BitArray row) {
- if (row == null || row.getSize() < width) {
- row = new BitArray(width);
- }
- int offset = y * rowSize;
- for (int x = 0; x < rowSize; x++) {
- row.setBulk(x << 5, bits[offset + x]);
- }
- return row;
- }
-
- /**
- * This is useful in detecting a corner of a 'pure' barcode.
- *
- * @return {x,y} coordinate of top-left-most 1 bit, or null if it is all white
- */
- public int[] getTopLeftOnBit() {
- int bitsOffset = 0;
- while (bitsOffset < bits.length && bits[bitsOffset] == 0) {
- bitsOffset++;
- }
- if (bitsOffset == bits.length) {
- return null;
- }
- int y = bitsOffset / rowSize;
- int x = (bitsOffset % rowSize) << 5;
-
- int theBits = bits[bitsOffset];
- int bit = 0;
- while ((theBits << (31-bit)) == 0) {
- bit++;
- }
- x += bit;
- return new int[] {x, y};
- }
-
- public int[] getBottomRightOnBit() {
- int bitsOffset = bits.length - 1;
- while (bitsOffset >= 0 && bits[bitsOffset] == 0) {
- bitsOffset--;
- }
- if (bitsOffset < 0) {
- return null;
- }
-
- int y = bitsOffset / rowSize;
- int x = (bitsOffset % rowSize) << 5;
-
- int theBits = bits[bitsOffset];
- int bit = 31;
- while ((theBits >>> bit) == 0) {
- bit--;
- }
- x += bit;
-
- return new int[] {x, y};
- }
-
- /**
- * @return The width of the matrix
- */
- public int getWidth() {
- return width;
- }
-
- /**
- * @return The height of the matrix
- */
- public int getHeight() {
- return height;
- }
-
- public boolean equals(Object o) {
- if (!(o instanceof BitMatrix)) {
- return false;
- }
- BitMatrix other = (BitMatrix) o;
- if (width != other.width || height != other.height ||
- rowSize != other.rowSize || bits.length != other.bits.length) {
- return false;
- }
- for (int i = 0; i < bits.length; i++) {
- if (bits[i] != other.bits[i]) {
- return false;
- }
- }
- return true;
- }
-
- public int hashCode() {
- int hash = width;
- hash = 31 * hash + width;
- hash = 31 * hash + height;
- hash = 31 * hash + rowSize;
- for (int i = 0; i < bits.length; i++) {
- hash = 31 * hash + bits[i];
- }
- return hash;
- }
-
- public String toString() {
- StringBuffer result = new StringBuffer(height * (width + 1));
- for (int y = 0; y < height; y++) {
- for (int x = 0; x < width; x++) {
- result.append(get(x, y) ? "X " : " ");
- }
- result.append('\n');
- }
- return result.toString();
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/BitSource.java b/OpenPGP-Keychain/src/com/google/zxing/common/BitSource.java
deleted file mode 100644
index a61ac5105..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/common/BitSource.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright 2007 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.common;
-
-/**
- *
This provides an easy abstraction to read bits at a time from a sequence of bytes, where the
- * number of bits read is not often a multiple of 8.
- *
- *
This class is thread-safe but not reentrant. Unless the caller modifies the bytes array
- * it passed in, in which case all bets are off.
- *
- * @author Sean Owen
- */
-public final class BitSource {
-
- private final byte[] bytes;
- private int byteOffset;
- private int bitOffset;
-
- /**
- * @param bytes bytes from which this will read bits. Bits will be read from the first byte first.
- * Bits are read within a byte from most-significant to least-significant bit.
- */
- public BitSource(byte[] bytes) {
- this.bytes = bytes;
- }
-
- /**
- * @param numBits number of bits to read
- * @return int representing the bits read. The bits will appear as the least-significant
- * bits of the int
- * @throws IllegalArgumentException if numBits isn't in [1,32]
- */
- public int readBits(int numBits) {
- if (numBits < 1 || numBits > 32) {
- throw new IllegalArgumentException();
- }
-
- int result = 0;
-
- // First, read remainder from current byte
- if (bitOffset > 0) {
- int bitsLeft = 8 - bitOffset;
- int toRead = numBits < bitsLeft ? numBits : bitsLeft;
- int bitsToNotRead = bitsLeft - toRead;
- int mask = (0xFF >> (8 - toRead)) << bitsToNotRead;
- result = (bytes[byteOffset] & mask) >> bitsToNotRead;
- numBits -= toRead;
- bitOffset += toRead;
- if (bitOffset == 8) {
- bitOffset = 0;
- byteOffset++;
- }
- }
-
- // Next read whole bytes
- if (numBits > 0) {
- while (numBits >= 8) {
- result = (result << 8) | (bytes[byteOffset] & 0xFF);
- byteOffset++;
- numBits -= 8;
- }
-
- // Finally read a partial byte
- if (numBits > 0) {
- int bitsToNotRead = 8 - numBits;
- int mask = (0xFF >> bitsToNotRead) << bitsToNotRead;
- result = (result << numBits) | ((bytes[byteOffset] & mask) >> bitsToNotRead);
- bitOffset += numBits;
- }
- }
-
- return result;
- }
-
- /**
- * @return number of bits that can be read successfully
- */
- public int available() {
- return 8 * (bytes.length - byteOffset) - bitOffset;
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/CharacterSetECI.java b/OpenPGP-Keychain/src/com/google/zxing/common/CharacterSetECI.java
deleted file mode 100644
index 42b7fa9f6..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/common/CharacterSetECI.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright 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.common;
-
-import java.util.Hashtable;
-
-/**
- * Encapsulates a Character Set ECI, according to "Extended Channel Interpretations" 5.3.1.1
- * of ISO 18004.
- *
- * @author Sean Owen
- */
-public final class CharacterSetECI extends ECI {
-
- private static Hashtable VALUE_TO_ECI;
- private static Hashtable NAME_TO_ECI;
-
- private static void initialize() {
- VALUE_TO_ECI = new Hashtable(29);
- NAME_TO_ECI = new Hashtable(29);
- // TODO figure out if these values are even right!
- addCharacterSet(0, "Cp437");
- addCharacterSet(1, new String[] {"ISO8859_1", "ISO-8859-1"});
- addCharacterSet(2, "Cp437");
- addCharacterSet(3, new String[] {"ISO8859_1", "ISO-8859-1"});
- addCharacterSet(4, "ISO8859_2");
- addCharacterSet(5, "ISO8859_3");
- addCharacterSet(6, "ISO8859_4");
- addCharacterSet(7, "ISO8859_5");
- addCharacterSet(8, "ISO8859_6");
- addCharacterSet(9, "ISO8859_7");
- addCharacterSet(10, "ISO8859_8");
- addCharacterSet(11, "ISO8859_9");
- addCharacterSet(12, "ISO8859_10");
- addCharacterSet(13, "ISO8859_11");
- addCharacterSet(15, "ISO8859_13");
- addCharacterSet(16, "ISO8859_14");
- addCharacterSet(17, "ISO8859_15");
- addCharacterSet(18, "ISO8859_16");
- addCharacterSet(20, new String[] {"SJIS", "Shift_JIS"});
- }
-
- private final String encodingName;
-
- private CharacterSetECI(int value, String encodingName) {
- super(value);
- this.encodingName = encodingName;
- }
-
- public String getEncodingName() {
- return encodingName;
- }
-
- private static void addCharacterSet(int value, String encodingName) {
- CharacterSetECI eci = new CharacterSetECI(value, encodingName);
- VALUE_TO_ECI.put(new Integer(value), eci); // can't use valueOf
- NAME_TO_ECI.put(encodingName, eci);
- }
-
- private static void addCharacterSet(int value, String[] encodingNames) {
- CharacterSetECI eci = new CharacterSetECI(value, encodingNames[0]);
- VALUE_TO_ECI.put(new Integer(value), eci); // can't use valueOf
- for (int i = 0; i < encodingNames.length; i++) {
- NAME_TO_ECI.put(encodingNames[i], eci);
- }
- }
-
- /**
- * @param value character set ECI value
- * @return CharacterSetECI representing ECI of given value, or null if it is legal but
- * unsupported
- * @throws IllegalArgumentException if ECI value is invalid
- */
- public static CharacterSetECI getCharacterSetECIByValue(int value) {
- if (VALUE_TO_ECI == null) {
- initialize();
- }
- if (value < 0 || value >= 900) {
- throw new IllegalArgumentException("Bad ECI value: " + value);
- }
- return (CharacterSetECI) VALUE_TO_ECI.get(new Integer(value));
- }
-
- /**
- * @param name character set ECI encoding name
- * @return CharacterSetECI representing ECI for character encoding, or null if it is legal
- * but unsupported
- */
- public static CharacterSetECI getCharacterSetECIByName(String name) {
- if (NAME_TO_ECI == null) {
- initialize();
- }
- return (CharacterSetECI) NAME_TO_ECI.get(name);
- }
-
-}
\ No newline at end of file
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/Collections.java b/OpenPGP-Keychain/src/com/google/zxing/common/Collections.java
deleted file mode 100644
index 319ebfe6d..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/common/Collections.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2007 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.common;
-
-import java.util.Vector;
-
-/**
- *
This is basically a substitute for java.util.Collections, which is not
- * present in MIDP 2.0 / CLDC 1.1.
- *
- * @author Sean Owen
- */
-public final class Collections {
-
- private Collections() {
- }
-
- /**
- * Sorts its argument (destructively) using insert sort; in the context of this package
- * insertion sort is simple and efficient given its relatively small inputs.
- *
- * @param vector vector to sort
- * @param comparator comparator to define sort ordering
- */
- public static void insertionSort(Vector vector, Comparator comparator) {
- int max = vector.size();
- for (int i = 1; i < max; i++) {
- Object value = vector.elementAt(i);
- int j = i - 1;
- Object valueB;
- while (j >= 0 && comparator.compare((valueB = vector.elementAt(j)), value) > 0) {
- vector.setElementAt(valueB, j + 1);
- j--;
- }
- vector.setElementAt(value, j + 1);
- }
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/Comparator.java b/OpenPGP-Keychain/src/com/google/zxing/common/Comparator.java
deleted file mode 100644
index e1be15e31..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/common/Comparator.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2007 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.common;
-
-/**
- * This is merely a clone of Comparator since it is not available in
- * CLDC 1.1 / MIDP 2.0.
- */
-public interface Comparator {
-
- int compare(Object o1, Object o2);
-
-}
\ No newline at end of file
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/DecoderResult.java b/OpenPGP-Keychain/src/com/google/zxing/common/DecoderResult.java
deleted file mode 100644
index 7e0855333..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/common/DecoderResult.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2007 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.common;
-
-import java.util.Vector;
-
-/**
- *
Encapsulates the result of decoding a matrix of bits. This typically
- * applies to 2D barcode formats. For now it contains the raw bytes obtained,
- * as well as a String interpretation of those bytes, if applicable.
- *
- * @author Sean Owen
- */
-public final class DecoderResult {
-
- private final byte[] rawBytes;
- private final String text;
- private final Vector byteSegments;
- private final String ecLevel;
-
- public DecoderResult(byte[] rawBytes, String text, Vector byteSegments, String ecLevel) {
- if (rawBytes == null && text == null) {
- throw new IllegalArgumentException();
- }
- this.rawBytes = rawBytes;
- this.text = text;
- this.byteSegments = byteSegments;
- this.ecLevel = ecLevel;
- }
-
- public byte[] getRawBytes() {
- return rawBytes;
- }
-
- public String getText() {
- return text;
- }
-
- public Vector getByteSegments() {
- return byteSegments;
- }
-
- public String getECLevel() {
- return ecLevel;
- }
-
-}
\ No newline at end of file
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/DefaultGridSampler.java b/OpenPGP-Keychain/src/com/google/zxing/common/DefaultGridSampler.java
deleted file mode 100644
index 74c9e7c6b..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/common/DefaultGridSampler.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright 2007 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.common;
-
-import com.google.zxing.NotFoundException;
-
-/**
- * @author Sean Owen
- */
-public final class DefaultGridSampler extends GridSampler {
-
- public BitMatrix sampleGrid(BitMatrix image,
- int dimensionX,
- int dimensionY,
- float p1ToX, float p1ToY,
- float p2ToX, float p2ToY,
- float p3ToX, float p3ToY,
- float p4ToX, float p4ToY,
- float p1FromX, float p1FromY,
- float p2FromX, float p2FromY,
- float p3FromX, float p3FromY,
- float p4FromX, float p4FromY) throws NotFoundException {
-
- PerspectiveTransform transform = PerspectiveTransform.quadrilateralToQuadrilateral(
- p1ToX, p1ToY, p2ToX, p2ToY, p3ToX, p3ToY, p4ToX, p4ToY,
- p1FromX, p1FromY, p2FromX, p2FromY, p3FromX, p3FromY, p4FromX, p4FromY);
-
- return sampleGrid(image, dimensionX, dimensionY, transform);
- }
-
- public BitMatrix sampleGrid(BitMatrix image,
- int dimensionX,
- int dimensionY,
- PerspectiveTransform transform) throws NotFoundException {
- if (dimensionX <= 0 || dimensionY <= 0) {
- throw NotFoundException.getNotFoundInstance();
- }
- BitMatrix bits = new BitMatrix(dimensionX, dimensionY);
- float[] points = new float[dimensionX << 1];
- for (int y = 0; y < dimensionY; y++) {
- int max = points.length;
- float iValue = (float) y + 0.5f;
- for (int x = 0; x < max; x += 2) {
- points[x] = (float) (x >> 1) + 0.5f;
- points[x + 1] = iValue;
- }
- transform.transformPoints(points);
- // Quick check to see if points transformed to something inside the image;
- // sufficient to check the endpoints
- checkAndNudgePoints(image, points);
- try {
- for (int x = 0; x < max; x += 2) {
- if (image.get((int) points[x], (int) points[x + 1])) {
- // Black(-ish) pixel
- bits.set(x >> 1, y);
- }
- }
- } catch (ArrayIndexOutOfBoundsException aioobe) {
- // This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting
- // transform gets "twisted" such that it maps a straight line of points to a set of points
- // whose endpoints are in bounds, but others are not. There is probably some mathematical
- // way to detect this about the transformation that I don't know yet.
- // This results in an ugly runtime exception despite our clever checks above -- can't have
- // that. We could check each point's coordinates but that feels duplicative. We settle for
- // catching and wrapping ArrayIndexOutOfBoundsException.
- throw NotFoundException.getNotFoundInstance();
- }
- }
- return bits;
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/DetectorResult.java b/OpenPGP-Keychain/src/com/google/zxing/common/DetectorResult.java
deleted file mode 100644
index ea4794d17..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/common/DetectorResult.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2007 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.common;
-
-import com.google.zxing.ResultPoint;
-
-/**
- *
Encapsulates the result of detecting a barcode in an image. This includes the raw
- * matrix of black/white pixels corresponding to the barcode, and possibly points of interest
- * in the image, like the location of finder patterns or corners of the barcode in the image.
- *
- * @author Sean Owen
- */
-public class DetectorResult {
-
- private final BitMatrix bits;
- private final ResultPoint[] points;
-
- public DetectorResult(BitMatrix bits, ResultPoint[] points) {
- this.bits = bits;
- this.points = points;
- }
-
- public BitMatrix getBits() {
- return bits;
- }
-
- public ResultPoint[] getPoints() {
- return points;
- }
-
-}
\ No newline at end of file
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/ECI.java b/OpenPGP-Keychain/src/com/google/zxing/common/ECI.java
deleted file mode 100644
index 444c779c2..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/common/ECI.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 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.common;
-
-/**
- * Superclass of classes encapsulating types ECIs, according to "Extended Channel Interpretations"
- * 5.3 of ISO 18004.
- *
- * @author Sean Owen
- */
-public abstract class ECI {
-
- private final int value;
-
- ECI(int value) {
- this.value = value;
- }
-
- public int getValue() {
- return value;
- }
-
- /**
- * @param value ECI value
- * @return ECI representing ECI of given value, or null if it is legal but unsupported
- * @throws IllegalArgumentException if ECI value is invalid
- */
- public static ECI getECIByValue(int value) {
- if (value < 0 || value > 999999) {
- throw new IllegalArgumentException("Bad ECI value: " + value);
- }
- if (value < 900) { // Character set ECIs use 000000 - 000899
- return CharacterSetECI.getCharacterSetECIByValue(value);
- }
- return null;
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/GlobalHistogramBinarizer.java b/OpenPGP-Keychain/src/com/google/zxing/common/GlobalHistogramBinarizer.java
deleted file mode 100644
index 4fa2a887b..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/common/GlobalHistogramBinarizer.java
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * 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.common;
-
-import com.google.zxing.Binarizer;
-import com.google.zxing.LuminanceSource;
-import com.google.zxing.NotFoundException;
-
-/**
- * This Binarizer implementation uses the old ZXing global histogram approach. It is suitable
- * for low-end mobile devices which don't have enough CPU or memory to use a local thresholding
- * algorithm. However, because it picks a global black point, it cannot handle difficult shadows
- * and gradients.
- *
- * Faster mobile devices and all desktop applications should probably use HybridBinarizer instead.
- *
- * @author dswitkin@google.com (Daniel Switkin)
- * @author Sean Owen
- */
-public class GlobalHistogramBinarizer extends Binarizer {
-
- private static final int LUMINANCE_BITS = 5;
- private static final int LUMINANCE_SHIFT = 8 - LUMINANCE_BITS;
- private static final int LUMINANCE_BUCKETS = 1 << LUMINANCE_BITS;
-
- private byte[] luminances = null;
- private int[] buckets = null;
-
- public GlobalHistogramBinarizer(LuminanceSource source) {
- super(source);
- }
-
- // Applies simple sharpening to the row data to improve performance of the 1D Readers.
- public BitArray getBlackRow(int y, BitArray row) throws NotFoundException {
- LuminanceSource source = getLuminanceSource();
- int width = source.getWidth();
- if (row == null || row.getSize() < width) {
- row = new BitArray(width);
- } else {
- row.clear();
- }
-
- initArrays(width);
- byte[] localLuminances = source.getRow(y, luminances);
- int[] localBuckets = buckets;
- for (int x = 0; x < width; x++) {
- int pixel = localLuminances[x] & 0xff;
- localBuckets[pixel >> LUMINANCE_SHIFT]++;
- }
- int blackPoint = estimateBlackPoint(localBuckets);
-
- int left = localLuminances[0] & 0xff;
- int center = localLuminances[1] & 0xff;
- for (int x = 1; x < width - 1; x++) {
- int right = localLuminances[x + 1] & 0xff;
- // A simple -1 4 -1 box filter with a weight of 2.
- int luminance = ((center << 2) - left - right) >> 1;
- if (luminance < blackPoint) {
- row.set(x);
- }
- left = center;
- center = right;
- }
- return row;
- }
-
- // Does not sharpen the data, as this call is intended to only be used by 2D Readers.
- public BitMatrix getBlackMatrix() throws NotFoundException {
- LuminanceSource source = getLuminanceSource();
- int width = source.getWidth();
- int height = source.getHeight();
- BitMatrix matrix = new BitMatrix(width, height);
-
- // Quickly calculates the histogram by sampling four rows from the image. This proved to be
- // more robust on the blackbox tests than sampling a diagonal as we used to do.
- initArrays(width);
- int[] localBuckets = buckets;
- for (int y = 1; y < 5; y++) {
- int row = height * y / 5;
- byte[] localLuminances = source.getRow(row, luminances);
- int right = (width << 2) / 5;
- for (int x = width / 5; x < right; x++) {
- int pixel = localLuminances[x] & 0xff;
- localBuckets[pixel >> LUMINANCE_SHIFT]++;
- }
- }
- int blackPoint = estimateBlackPoint(localBuckets);
-
- // We delay reading the entire image luminance until the black point estimation succeeds.
- // Although we end up reading four rows twice, it is consistent with our motto of
- // "fail quickly" which is necessary for continuous scanning.
- byte[] localLuminances = source.getMatrix();
- for (int y = 0; y < height; y++) {
- int offset = y * width;
- for (int x = 0; x< width; x++) {
- int pixel = localLuminances[offset + x] & 0xff;
- if (pixel < blackPoint) {
- matrix.set(x, y);
- }
- }
- }
-
- return matrix;
- }
-
- public Binarizer createBinarizer(LuminanceSource source) {
- return new GlobalHistogramBinarizer(source);
- }
-
- private void initArrays(int luminanceSize) {
- if (luminances == null || luminances.length < luminanceSize) {
- luminances = new byte[luminanceSize];
- }
- if (buckets == null) {
- buckets = new int[LUMINANCE_BUCKETS];
- } else {
- for (int x = 0; x < LUMINANCE_BUCKETS; x++) {
- buckets[x] = 0;
- }
- }
- }
-
- private static int estimateBlackPoint(int[] buckets) throws NotFoundException {
- // Find the tallest peak in the histogram.
- int numBuckets = buckets.length;
- int maxBucketCount = 0;
- int firstPeak = 0;
- int firstPeakSize = 0;
- for (int x = 0; x < numBuckets; x++) {
- if (buckets[x] > firstPeakSize) {
- firstPeak = x;
- firstPeakSize = buckets[x];
- }
- if (buckets[x] > maxBucketCount) {
- maxBucketCount = buckets[x];
- }
- }
-
- // Find the second-tallest peak which is somewhat far from the tallest peak.
- int secondPeak = 0;
- int secondPeakScore = 0;
- for (int x = 0; x < numBuckets; x++) {
- int distanceToBiggest = x - firstPeak;
- // Encourage more distant second peaks by multiplying by square of distance.
- int score = buckets[x] * distanceToBiggest * distanceToBiggest;
- if (score > secondPeakScore) {
- secondPeak = x;
- secondPeakScore = score;
- }
- }
-
- // Make sure firstPeak corresponds to the black peak.
- if (firstPeak > secondPeak) {
- int temp = firstPeak;
- firstPeak = secondPeak;
- secondPeak = temp;
- }
-
- // If there is too little contrast in the image to pick a meaningful black point, throw rather
- // than waste time trying to decode the image, and risk false positives.
- if (secondPeak - firstPeak <= numBuckets >> 4) {
- throw NotFoundException.getNotFoundInstance();
- }
-
- // Find a valley between them that is low and closer to the white peak.
- int bestValley = secondPeak - 1;
- int bestValleyScore = -1;
- for (int x = secondPeak - 1; x > firstPeak; x--) {
- int fromFirst = x - firstPeak;
- int score = fromFirst * fromFirst * (secondPeak - x) * (maxBucketCount - buckets[x]);
- if (score > bestValleyScore) {
- bestValley = x;
- bestValleyScore = score;
- }
- }
-
- return bestValley << LUMINANCE_SHIFT;
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/GridSampler.java b/OpenPGP-Keychain/src/com/google/zxing/common/GridSampler.java
deleted file mode 100644
index 7f26c264e..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/common/GridSampler.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright 2007 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.common;
-
-import com.google.zxing.NotFoundException;
-
-/**
- * Implementations of this class can, given locations of finder patterns for a QR code in an
- * image, sample the right points in the image to reconstruct the QR code, accounting for
- * perspective distortion. It is abstracted since it is relatively expensive and should be allowed
- * to take advantage of platform-specific optimized implementations, like Sun's Java Advanced
- * Imaging library, but which may not be available in other environments such as J2ME, and vice
- * versa.
- *
- * The implementation used can be controlled by calling {@link #setGridSampler(GridSampler)}
- * with an instance of a class which implements this interface.
- *
- * @author Sean Owen
- */
-public abstract class GridSampler {
-
- private static GridSampler gridSampler = new DefaultGridSampler();
-
- /**
- * Sets the implementation of GridSampler used by the library. One global
- * instance is stored, which may sound problematic. But, the implementation provided
- * ought to be appropriate for the entire platform, and all uses of this library
- * in the whole lifetime of the JVM. For instance, an Android activity can swap in
- * an implementation that takes advantage of native platform libraries.
- *
- * @param newGridSampler The platform-specific object to install.
- */
- public static void setGridSampler(GridSampler newGridSampler) {
- if (newGridSampler == null) {
- throw new IllegalArgumentException();
- }
- gridSampler = newGridSampler;
- }
-
- /**
- * @return the current implementation of GridSampler
- */
- public static GridSampler getInstance() {
- return gridSampler;
- }
-
- /**
- * Samples an image for a rectangular matrix of bits of the given dimension.
- * @param image image to sample
- * @param dimensionX width of {@link BitMatrix} to sample from image
- * @param dimensionY height of {@link BitMatrix} to sample from image
- * @return {@link BitMatrix} representing a grid of points sampled from the image within a region
- * defined by the "from" parameters
- * @throws NotFoundException if image can't be sampled, for example, if the transformation defined
- * by the given points is invalid or results in sampling outside the image boundaries
- */
- public abstract BitMatrix sampleGrid(BitMatrix image,
- int dimensionX,
- int dimensionY,
- float p1ToX, float p1ToY,
- float p2ToX, float p2ToY,
- float p3ToX, float p3ToY,
- float p4ToX, float p4ToY,
- float p1FromX, float p1FromY,
- float p2FromX, float p2FromY,
- float p3FromX, float p3FromY,
- float p4FromX, float p4FromY) throws NotFoundException;
-
- public abstract BitMatrix sampleGrid(BitMatrix image,
- int dimensionX,
- int dimensionY,
- PerspectiveTransform transform) throws NotFoundException;
-
- /**
- *
Checks a set of points that have been transformed to sample points on an image against
- * the image's dimensions to see if the point are even within the image.
- *
- *
This method will actually "nudge" the endpoints back onto the image if they are found to be
- * barely (less than 1 pixel) off the image. This accounts for imperfect detection of finder
- * patterns in an image where the QR Code runs all the way to the image border.
- *
- *
For efficiency, the method will check points from either end of the line until one is found
- * to be within the image. Because the set of points are assumed to be linear, this is valid.
- *
- * @param image image into which the points should map
- * @param points actual points in x1,y1,...,xn,yn form
- * @throws NotFoundException if an endpoint is lies outside the image boundaries
- */
- protected static void checkAndNudgePoints(BitMatrix image, float[] points) throws NotFoundException {
- int width = image.getWidth();
- int height = image.getHeight();
- // Check and nudge points from start until we see some that are OK:
- boolean nudged = true;
- for (int offset = 0; offset < points.length && nudged; offset += 2) {
- int x = (int) points[offset];
- int y = (int) points[offset + 1];
- if (x < -1 || x > width || y < -1 || y > height) {
- throw NotFoundException.getNotFoundInstance();
- }
- nudged = false;
- if (x == -1) {
- points[offset] = 0.0f;
- nudged = true;
- } else if (x == width) {
- points[offset] = width - 1;
- nudged = true;
- }
- if (y == -1) {
- points[offset + 1] = 0.0f;
- nudged = true;
- } else if (y == height) {
- points[offset + 1] = height - 1;
- nudged = true;
- }
- }
- // Check and nudge points from end:
- nudged = true;
- for (int offset = points.length - 2; offset >= 0 && nudged; offset -= 2) {
- int x = (int) points[offset];
- int y = (int) points[offset + 1];
- if (x < -1 || x > width || y < -1 || y > height) {
- throw NotFoundException.getNotFoundInstance();
- }
- nudged = false;
- if (x == -1) {
- points[offset] = 0.0f;
- nudged = true;
- } else if (x == width) {
- points[offset] = width - 1;
- nudged = true;
- }
- if (y == -1) {
- points[offset + 1] = 0.0f;
- nudged = true;
- } else if (y == height) {
- points[offset + 1] = height - 1;
- nudged = true;
- }
- }
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/HybridBinarizer.java b/OpenPGP-Keychain/src/com/google/zxing/common/HybridBinarizer.java
deleted file mode 100644
index b482c1a22..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/common/HybridBinarizer.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * 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.common;
-
-import com.google.zxing.Binarizer;
-import com.google.zxing.LuminanceSource;
-import com.google.zxing.NotFoundException;
-
-/**
- * This class implements a local thresholding algorithm, which while slower than the
- * GlobalHistogramBinarizer, is fairly efficient for what it does. It is designed for
- * high frequency images of barcodes with black data on white backgrounds. For this application,
- * it does a much better job than a global blackpoint with severe shadows and gradients.
- * However it tends to produce artifacts on lower frequency images and is therefore not
- * a good general purpose binarizer for uses outside ZXing.
- *
- * This class extends GlobalHistogramBinarizer, using the older histogram approach for 1D readers,
- * and the newer local approach for 2D readers. 1D decoding using a per-row histogram is already
- * inherently local, and only fails for horizontal gradients. We can revisit that problem later,
- * but for now it was not a win to use local blocks for 1D.
- *
- * This Binarizer is the default for the unit tests and the recommended class for library users.
- *
- * @author dswitkin@google.com (Daniel Switkin)
- */
-public final class HybridBinarizer extends GlobalHistogramBinarizer {
-
- // This class uses 5x5 blocks to compute local luminance, where each block is 8x8 pixels.
- // So this is the smallest dimension in each axis we can accept.
- private static final int MINIMUM_DIMENSION = 40;
-
- private BitMatrix matrix = null;
-
- public HybridBinarizer(LuminanceSource source) {
- super(source);
- }
-
- public BitMatrix getBlackMatrix() throws NotFoundException {
- binarizeEntireImage();
- return matrix;
- }
-
- public Binarizer createBinarizer(LuminanceSource source) {
- return new HybridBinarizer(source);
- }
-
- // Calculates the final BitMatrix once for all requests. This could be called once from the
- // constructor instead, but there are some advantages to doing it lazily, such as making
- // profiling easier, and not doing heavy lifting when callers don't expect it.
- private void binarizeEntireImage() throws NotFoundException {
- if (matrix == null) {
- LuminanceSource source = getLuminanceSource();
- if (source.getWidth() >= MINIMUM_DIMENSION && source.getHeight() >= MINIMUM_DIMENSION) {
- byte[] luminances = source.getMatrix();
- int width = source.getWidth();
- int height = source.getHeight();
- int subWidth = width >> 3;
- if ((width & 0x07) != 0) {
- subWidth++;
- }
- int subHeight = height >> 3;
- if ((height & 0x07) != 0) {
- subHeight++;
- }
- int[][] blackPoints = calculateBlackPoints(luminances, subWidth, subHeight, width, height);
-
- matrix = new BitMatrix(width, height);
- calculateThresholdForBlock(luminances, subWidth, subHeight, width, height, blackPoints, matrix);
- } else {
- // If the image is too small, fall back to the global histogram approach.
- matrix = super.getBlackMatrix();
- }
- }
- }
-
- // For each 8x8 block in the image, calculate the average black point using a 5x5 grid
- // of the blocks around it. Also handles the corner cases (fractional blocks are computed based
- // on the last 8 pixels in the row/column which are also used in the previous block).
- private static void calculateThresholdForBlock(byte[] luminances, int subWidth, int subHeight,
- int width, int height, int[][] blackPoints, BitMatrix matrix) {
- for (int y = 0; y < subHeight; y++) {
- int yoffset = y << 3;
- if ((yoffset + 8) >= height) {
- yoffset = height - 8;
- }
- for (int x = 0; x < subWidth; x++) {
- int xoffset = x << 3;
- if ((xoffset + 8) >= width) {
- xoffset = width - 8;
- }
- int left = x > 1 ? x : 2;
- left = left < subWidth - 2 ? left : subWidth - 3;
- int top = y > 1 ? y : 2;
- top = top < subHeight - 2 ? top : subHeight - 3;
- int sum = 0;
- for (int z = -2; z <= 2; z++) {
- int[] blackRow = blackPoints[top + z];
- sum += blackRow[left - 2];
- sum += blackRow[left - 1];
- sum += blackRow[left];
- sum += blackRow[left + 1];
- sum += blackRow[left + 2];
- }
- int average = sum / 25;
- threshold8x8Block(luminances, xoffset, yoffset, average, width, matrix);
- }
- }
- }
-
- // Applies a single threshold to an 8x8 block of pixels.
- private static void threshold8x8Block(byte[] luminances, int xoffset, int yoffset, int threshold,
- int stride, BitMatrix matrix) {
- for (int y = 0; y < 8; y++) {
- int offset = (yoffset + y) * stride + xoffset;
- for (int x = 0; x < 8; x++) {
- int pixel = luminances[offset + x] & 0xff;
- if (pixel < threshold) {
- matrix.set(xoffset + x, yoffset + y);
- }
- }
- }
- }
-
- // Calculates a single black point for each 8x8 block of pixels and saves it away.
- private static int[][] calculateBlackPoints(byte[] luminances, int subWidth, int subHeight,
- int width, int height) {
- int[][] blackPoints = new int[subHeight][subWidth];
- for (int y = 0; y < subHeight; y++) {
- int yoffset = y << 3;
- if ((yoffset + 8) >= height) {
- yoffset = height - 8;
- }
- for (int x = 0; x < subWidth; x++) {
- int xoffset = x << 3;
- if ((xoffset + 8) >= width) {
- xoffset = width - 8;
- }
- int sum = 0;
- int min = 255;
- int max = 0;
- for (int yy = 0; yy < 8; yy++) {
- int offset = (yoffset + yy) * width + xoffset;
- for (int xx = 0; xx < 8; xx++) {
- int pixel = luminances[offset + xx] & 0xff;
- sum += pixel;
- if (pixel < min) {
- min = pixel;
- }
- if (pixel > max) {
- max = pixel;
- }
- }
- }
-
- // If the contrast is inadequate, use half the minimum, so that this block will be
- // treated as part of the white background, but won't drag down neighboring blocks
- // too much.
- int average;
- if (max - min > 24) {
- average = sum >> 6;
- } else {
- // When min == max == 0, let average be 1 so all is black
- average = max == 0 ? 1 : min >> 1;
- }
- blackPoints[y][x] = average;
- }
- }
- return blackPoints;
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/PerspectiveTransform.java b/OpenPGP-Keychain/src/com/google/zxing/common/PerspectiveTransform.java
deleted file mode 100644
index 9e65baff1..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/common/PerspectiveTransform.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright 2007 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.common;
-
-/**
- *
This class implements a perspective transform in two dimensions. Given four source and four
- * destination points, it will compute the transformation implied between them. The code is based
- * directly upon section 3.4.2 of George Wolberg's "Digital Image Warping"; see pages 54-56.
- *
- * @author Sean Owen
- */
-public final class PerspectiveTransform {
-
- private final float a11, a12, a13, a21, a22, a23, a31, a32, a33;
-
- private PerspectiveTransform(float a11, float a21, float a31,
- float a12, float a22, float a32,
- float a13, float a23, float a33) {
- this.a11 = a11;
- this.a12 = a12;
- this.a13 = a13;
- this.a21 = a21;
- this.a22 = a22;
- this.a23 = a23;
- this.a31 = a31;
- this.a32 = a32;
- this.a33 = a33;
- }
-
- public static PerspectiveTransform quadrilateralToQuadrilateral(float x0, float y0,
- float x1, float y1,
- float x2, float y2,
- float x3, float y3,
- float x0p, float y0p,
- float x1p, float y1p,
- float x2p, float y2p,
- float x3p, float y3p) {
-
- PerspectiveTransform qToS = quadrilateralToSquare(x0, y0, x1, y1, x2, y2, x3, y3);
- PerspectiveTransform sToQ = squareToQuadrilateral(x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p);
- return sToQ.times(qToS);
- }
-
- public void transformPoints(float[] points) {
- int max = points.length;
- float a11 = this.a11;
- float a12 = this.a12;
- float a13 = this.a13;
- float a21 = this.a21;
- float a22 = this.a22;
- float a23 = this.a23;
- float a31 = this.a31;
- float a32 = this.a32;
- float a33 = this.a33;
- for (int i = 0; i < max; i += 2) {
- float x = points[i];
- float y = points[i + 1];
- float denominator = a13 * x + a23 * y + a33;
- points[i] = (a11 * x + a21 * y + a31) / denominator;
- points[i + 1] = (a12 * x + a22 * y + a32) / denominator;
- }
- }
-
- /** Convenience method, not optimized for performance. */
- public void transformPoints(float[] xValues, float[] yValues) {
- int n = xValues.length;
- for (int i = 0; i < n; i ++) {
- float x = xValues[i];
- float y = yValues[i];
- float denominator = a13 * x + a23 * y + a33;
- xValues[i] = (a11 * x + a21 * y + a31) / denominator;
- yValues[i] = (a12 * x + a22 * y + a32) / denominator;
- }
- }
-
- public static PerspectiveTransform squareToQuadrilateral(float x0, float y0,
- float x1, float y1,
- float x2, float y2,
- float x3, float y3) {
- float dy2 = y3 - y2;
- float dy3 = y0 - y1 + y2 - y3;
- if (dy2 == 0.0f && dy3 == 0.0f) {
- return new PerspectiveTransform(x1 - x0, x2 - x1, x0,
- y1 - y0, y2 - y1, y0,
- 0.0f, 0.0f, 1.0f);
- } else {
- float dx1 = x1 - x2;
- float dx2 = x3 - x2;
- float dx3 = x0 - x1 + x2 - x3;
- float dy1 = y1 - y2;
- float denominator = dx1 * dy2 - dx2 * dy1;
- float a13 = (dx3 * dy2 - dx2 * dy3) / denominator;
- float a23 = (dx1 * dy3 - dx3 * dy1) / denominator;
- return new PerspectiveTransform(x1 - x0 + a13 * x1, x3 - x0 + a23 * x3, x0,
- y1 - y0 + a13 * y1, y3 - y0 + a23 * y3, y0,
- a13, a23, 1.0f);
- }
- }
-
- public static PerspectiveTransform quadrilateralToSquare(float x0, float y0,
- float x1, float y1,
- float x2, float y2,
- float x3, float y3) {
- // Here, the adjoint serves as the inverse:
- return squareToQuadrilateral(x0, y0, x1, y1, x2, y2, x3, y3).buildAdjoint();
- }
-
- PerspectiveTransform buildAdjoint() {
- // Adjoint is the transpose of the cofactor matrix:
- return new PerspectiveTransform(a22 * a33 - a23 * a32,
- a23 * a31 - a21 * a33,
- a21 * a32 - a22 * a31,
- a13 * a32 - a12 * a33,
- a11 * a33 - a13 * a31,
- a12 * a31 - a11 * a32,
- a12 * a23 - a13 * a22,
- a13 * a21 - a11 * a23,
- a11 * a22 - a12 * a21);
- }
-
- PerspectiveTransform times(PerspectiveTransform other) {
- return new PerspectiveTransform(a11 * other.a11 + a21 * other.a12 + a31 * other.a13,
- a11 * other.a21 + a21 * other.a22 + a31 * other.a23,
- a11 * other.a31 + a21 * other.a32 + a31 * other.a33,
- a12 * other.a11 + a22 * other.a12 + a32 * other.a13,
- a12 * other.a21 + a22 * other.a22 + a32 * other.a23,
- a12 * other.a31 + a22 * other.a32 + a32 * other.a33,
- a13 * other.a11 + a23 * other.a12 + a33 * other.a13,
- a13 * other.a21 + a23 * other.a22 + a33 * other.a23,
- a13 * other.a31 + a23 * other.a32 + a33 * other.a33);
-
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/StringUtils.java b/OpenPGP-Keychain/src/com/google/zxing/common/StringUtils.java
deleted file mode 100644
index 97999f997..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/common/StringUtils.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * Copyright (C) 2010 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.common;
-
-import java.util.Hashtable;
-
-import com.google.zxing.DecodeHintType;
-
-/**
- * Common string-related functions.
- *
- * @author Sean Owen
- */
-public final class StringUtils {
-
- private static final String PLATFORM_DEFAULT_ENCODING =
- System.getProperty("file.encoding");
- public static final String SHIFT_JIS = "SJIS";
- public static final String GB2312 = "GB2312";
- private static final String EUC_JP = "EUC_JP";
- private static final String UTF8 = "UTF8";
- private static final String ISO88591 = "ISO8859_1";
- private static final boolean ASSUME_SHIFT_JIS =
- SHIFT_JIS.equalsIgnoreCase(PLATFORM_DEFAULT_ENCODING) ||
- EUC_JP.equalsIgnoreCase(PLATFORM_DEFAULT_ENCODING);
-
- private StringUtils() {}
-
- /**
- * @param bytes bytes encoding a string, whose encoding should be guessed
- * @param hints decode hints if applicable
- * @return name of guessed encoding; at the moment will only guess one of:
- * {@link #SHIFT_JIS}, {@link #UTF8}, {@link #ISO88591}, or the platform
- * default encoding if none of these can possibly be correct
- */
- public static String guessEncoding(byte[] bytes, Hashtable hints) {
- if (hints != null) {
- String characterSet = (String) hints.get(DecodeHintType.CHARACTER_SET);
- if (characterSet != null) {
- return characterSet;
- }
- }
- // Does it start with the UTF-8 byte order mark? then guess it's UTF-8
- if (bytes.length > 3 &&
- bytes[0] == (byte) 0xEF &&
- bytes[1] == (byte) 0xBB &&
- bytes[2] == (byte) 0xBF) {
- return UTF8;
- }
- // For now, merely tries to distinguish ISO-8859-1, UTF-8 and Shift_JIS,
- // which should be by far the most common encodings. ISO-8859-1
- // should not have bytes in the 0x80 - 0x9F range, while Shift_JIS
- // uses this as a first byte of a two-byte character. If we see this
- // followed by a valid second byte in Shift_JIS, assume it is Shift_JIS.
- // If we see something else in that second byte, we'll make the risky guess
- // that it's UTF-8.
- int length = bytes.length;
- boolean canBeISO88591 = true;
- boolean canBeShiftJIS = true;
- boolean canBeUTF8 = true;
- int utf8BytesLeft = 0;
- int maybeDoubleByteCount = 0;
- int maybeSingleByteKatakanaCount = 0;
- boolean sawLatin1Supplement = false;
- boolean sawUTF8Start = false;
- boolean lastWasPossibleDoubleByteStart = false;
-
- for (int i = 0;
- i < length && (canBeISO88591 || canBeShiftJIS || canBeUTF8);
- i++) {
-
- int value = bytes[i] & 0xFF;
-
- // UTF-8 stuff
- if (value >= 0x80 && value <= 0xBF) {
- if (utf8BytesLeft > 0) {
- utf8BytesLeft--;
- }
- } else {
- if (utf8BytesLeft > 0) {
- canBeUTF8 = false;
- }
- if (value >= 0xC0 && value <= 0xFD) {
- sawUTF8Start = true;
- int valueCopy = value;
- while ((valueCopy & 0x40) != 0) {
- utf8BytesLeft++;
- valueCopy <<= 1;
- }
- }
- }
-
- // ISO-8859-1 stuff
-
- if ((value == 0xC2 || value == 0xC3) && i < length - 1) {
- // This is really a poor hack. The slightly more exotic characters people might want to put in
- // a QR Code, by which I mean the Latin-1 supplement characters (e.g. u-umlaut) have encodings
- // that start with 0xC2 followed by [0xA0,0xBF], or start with 0xC3 followed by [0x80,0xBF].
- int nextValue = bytes[i + 1] & 0xFF;
- if (nextValue <= 0xBF &&
- ((value == 0xC2 && nextValue >= 0xA0) || (value == 0xC3 && nextValue >= 0x80))) {
- sawLatin1Supplement = true;
- }
- }
- if (value >= 0x7F && value <= 0x9F) {
- canBeISO88591 = false;
- }
-
- // Shift_JIS stuff
-
- if (value >= 0xA1 && value <= 0xDF) {
- // count the number of characters that might be a Shift_JIS single-byte Katakana character
- if (!lastWasPossibleDoubleByteStart) {
- maybeSingleByteKatakanaCount++;
- }
- }
- if (!lastWasPossibleDoubleByteStart &&
- ((value >= 0xF0 && value <= 0xFF) || value == 0x80 || value == 0xA0)) {
- canBeShiftJIS = false;
- }
- if ((value >= 0x81 && value <= 0x9F) || (value >= 0xE0 && value <= 0xEF)) {
- // These start double-byte characters in Shift_JIS. Let's see if it's followed by a valid
- // second byte.
- if (lastWasPossibleDoubleByteStart) {
- // If we just checked this and the last byte for being a valid double-byte
- // char, don't check starting on this byte. If this and the last byte
- // formed a valid pair, then this shouldn't be checked to see if it starts
- // a double byte pair of course.
- lastWasPossibleDoubleByteStart = false;
- } else {
- // ... otherwise do check to see if this plus the next byte form a valid
- // double byte pair encoding a character.
- lastWasPossibleDoubleByteStart = true;
- if (i >= bytes.length - 1) {
- canBeShiftJIS = false;
- } else {
- int nextValue = bytes[i + 1] & 0xFF;
- if (nextValue < 0x40 || nextValue > 0xFC) {
- canBeShiftJIS = false;
- } else {
- maybeDoubleByteCount++;
- }
- // There is some conflicting information out there about which bytes can follow which in
- // double-byte Shift_JIS characters. The rule above seems to be the one that matches practice.
- }
- }
- } else {
- lastWasPossibleDoubleByteStart = false;
- }
- }
- if (utf8BytesLeft > 0) {
- canBeUTF8 = false;
- }
-
- // Easy -- if assuming Shift_JIS and no evidence it can't be, done
- if (canBeShiftJIS && ASSUME_SHIFT_JIS) {
- return SHIFT_JIS;
- }
- if (canBeUTF8 && sawUTF8Start) {
- return UTF8;
- }
- // Distinguishing Shift_JIS and ISO-8859-1 can be a little tough. The crude heuristic is:
- // - If we saw
- // - at least 3 bytes that starts a double-byte value (bytes that are rare in ISO-8859-1), or
- // - over 5% of bytes could be single-byte Katakana (also rare in ISO-8859-1),
- // - and, saw no sequences that are invalid in Shift_JIS, then we conclude Shift_JIS
- if (canBeShiftJIS && (maybeDoubleByteCount >= 3 || 20 * maybeSingleByteKatakanaCount > length)) {
- return SHIFT_JIS;
- }
- // Otherwise, we default to ISO-8859-1 unless we know it can't be
- if (!sawLatin1Supplement && canBeISO88591) {
- return ISO88591;
- }
- // Otherwise, we take a wild guess with platform encoding
- return PLATFORM_DEFAULT_ENCODING;
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/detector/MonochromeRectangleDetector.java b/OpenPGP-Keychain/src/com/google/zxing/common/detector/MonochromeRectangleDetector.java
deleted file mode 100644
index 950a22364..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/common/detector/MonochromeRectangleDetector.java
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * 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.common.detector;
-
-import com.google.zxing.NotFoundException;
-import com.google.zxing.ResultPoint;
-import com.google.zxing.common.BitMatrix;
-
-/**
- *
A somewhat generic detector that looks for a barcode-like rectangular region within an image.
- * It looks within a mostly white region of an image for a region of black and white, but mostly
- * black. It returns the four corners of the region, as best it can determine.
- *
- * @author Sean Owen
- */
-public final class MonochromeRectangleDetector {
-
- private static final int MAX_MODULES = 32;
-
- private final BitMatrix image;
-
- public MonochromeRectangleDetector(BitMatrix image) {
- this.image = image;
- }
-
- /**
- *
Detects a rectangular region of black and white -- mostly black -- with a region of mostly
- * white, in an image.
- *
- * @return {@link ResultPoint}[] describing the corners of the rectangular region. The first and
- * last points are opposed on the diagonal, as are the second and third. The first point will be
- * the topmost point and the last, the bottommost. The second point will be leftmost and the
- * third, the rightmost
- * @throws NotFoundException if no Data Matrix Code can be found
- */
- public ResultPoint[] detect() throws NotFoundException {
- int height = image.getHeight();
- int width = image.getWidth();
- int halfHeight = height >> 1;
- int halfWidth = width >> 1;
- int deltaY = Math.max(1, height / (MAX_MODULES << 3));
- int deltaX = Math.max(1, width / (MAX_MODULES << 3));
-
- int top = 0;
- int bottom = height;
- int left = 0;
- int right = width;
- ResultPoint pointA = findCornerFromCenter(halfWidth, 0, left, right,
- halfHeight, -deltaY, top, bottom, halfWidth >> 1);
- top = (int) pointA.getY() - 1;
- ResultPoint pointB = findCornerFromCenter(halfWidth, -deltaX, left, right,
- halfHeight, 0, top, bottom, halfHeight >> 1);
- left = (int) pointB.getX() - 1;
- ResultPoint pointC = findCornerFromCenter(halfWidth, deltaX, left, right,
- halfHeight, 0, top, bottom, halfHeight >> 1);
- right = (int) pointC.getX() + 1;
- ResultPoint pointD = findCornerFromCenter(halfWidth, 0, left, right,
- halfHeight, deltaY, top, bottom, halfWidth >> 1);
- bottom = (int) pointD.getY() + 1;
-
- // Go try to find point A again with better information -- might have been off at first.
- pointA = findCornerFromCenter(halfWidth, 0, left, right,
- halfHeight, -deltaY, top, bottom, halfWidth >> 2);
-
- return new ResultPoint[] { pointA, pointB, pointC, pointD };
- }
-
- /**
- * Attempts to locate a corner of the barcode by scanning up, down, left or right from a center
- * point which should be within the barcode.
- *
- * @param centerX center's x component (horizontal)
- * @param deltaX same as deltaY but change in x per step instead
- * @param left minimum value of x
- * @param right maximum value of x
- * @param centerY center's y component (vertical)
- * @param deltaY change in y per step. If scanning up this is negative; down, positive;
- * left or right, 0
- * @param top minimum value of y to search through (meaningless when di == 0)
- * @param bottom maximum value of y
- * @param maxWhiteRun maximum run of white pixels that can still be considered to be within
- * the barcode
- * @return a {@link com.google.zxing.ResultPoint} encapsulating the corner that was found
- * @throws NotFoundException if such a point cannot be found
- */
- private ResultPoint findCornerFromCenter(int centerX, int deltaX, int left, int right,
- int centerY, int deltaY, int top, int bottom, int maxWhiteRun) throws NotFoundException {
- int[] lastRange = null;
- for (int y = centerY, x = centerX;
- y < bottom && y >= top && x < right && x >= left;
- y += deltaY, x += deltaX) {
- int[] range;
- if (deltaX == 0) {
- // horizontal slices, up and down
- range = blackWhiteRange(y, maxWhiteRun, left, right, true);
- } else {
- // vertical slices, left and right
- range = blackWhiteRange(x, maxWhiteRun, top, bottom, false);
- }
- if (range == null) {
- if (lastRange == null) {
- throw NotFoundException.getNotFoundInstance();
- }
- // lastRange was found
- if (deltaX == 0) {
- int lastY = y - deltaY;
- if (lastRange[0] < centerX) {
- if (lastRange[1] > centerX) {
- // straddle, choose one or the other based on direction
- return new ResultPoint(deltaY > 0 ? lastRange[0] : lastRange[1], lastY);
- }
- return new ResultPoint(lastRange[0], lastY);
- } else {
- return new ResultPoint(lastRange[1], lastY);
- }
- } else {
- int lastX = x - deltaX;
- if (lastRange[0] < centerY) {
- if (lastRange[1] > centerY) {
- return new ResultPoint(lastX, deltaX < 0 ? lastRange[0] : lastRange[1]);
- }
- return new ResultPoint(lastX, lastRange[0]);
- } else {
- return new ResultPoint(lastX, lastRange[1]);
- }
- }
- }
- lastRange = range;
- }
- throw NotFoundException.getNotFoundInstance();
- }
-
- /**
- * Computes the start and end of a region of pixels, either horizontally or vertically, that could
- * be part of a Data Matrix barcode.
- *
- * @param fixedDimension if scanning horizontally, this is the row (the fixed vertical location)
- * where we are scanning. If scanning vertically it's the column, the fixed horizontal location
- * @param maxWhiteRun largest run of white pixels that can still be considered part of the
- * barcode region
- * @param minDim minimum pixel location, horizontally or vertically, to consider
- * @param maxDim maximum pixel location, horizontally or vertically, to consider
- * @param horizontal if true, we're scanning left-right, instead of up-down
- * @return int[] with start and end of found range, or null if no such range is found
- * (e.g. only white was found)
- */
- private int[] blackWhiteRange(int fixedDimension, int maxWhiteRun, int minDim, int maxDim,
- boolean horizontal) {
-
- int center = (minDim + maxDim) >> 1;
-
- // Scan left/up first
- int start = center;
- while (start >= minDim) {
- if (horizontal ? image.get(start, fixedDimension) : image.get(fixedDimension, start)) {
- start--;
- } else {
- int whiteRunStart = start;
- do {
- start--;
- } while (start >= minDim && !(horizontal ? image.get(start, fixedDimension) :
- image.get(fixedDimension, start)));
- int whiteRunSize = whiteRunStart - start;
- if (start < minDim || whiteRunSize > maxWhiteRun) {
- start = whiteRunStart;
- break;
- }
- }
- }
- start++;
-
- // Then try right/down
- int end = center;
- while (end < maxDim) {
- if (horizontal ? image.get(end, fixedDimension) : image.get(fixedDimension, end)) {
- end++;
- } else {
- int whiteRunStart = end;
- do {
- end++;
- } while (end < maxDim && !(horizontal ? image.get(end, fixedDimension) :
- image.get(fixedDimension, end)));
- int whiteRunSize = end - whiteRunStart;
- if (end >= maxDim || whiteRunSize > maxWhiteRun) {
- end = whiteRunStart;
- break;
- }
- }
- }
- end--;
-
- return end > start ? new int[]{start, end} : null;
- }
-
-}
\ No newline at end of file
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/detector/WhiteRectangleDetector.java b/OpenPGP-Keychain/src/com/google/zxing/common/detector/WhiteRectangleDetector.java
deleted file mode 100644
index 31d87e9d0..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/common/detector/WhiteRectangleDetector.java
+++ /dev/null
@@ -1,347 +0,0 @@
-/*
- * Copyright 2010 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.common.detector;
-
-import com.google.zxing.NotFoundException;
-import com.google.zxing.ResultPoint;
-import com.google.zxing.common.BitMatrix;
-
-/**
- *
- * Detects a candidate barcode-like rectangular region within an image. It
- * starts around the center of the image, increases the size of the candidate
- * region until it finds a white rectangular region. By keeping track of the
- * last black points it encountered, it determines the corners of the barcode.
- *
- *
- * @author David Olivier
- */
-public final class WhiteRectangleDetector {
-
- private static final int INIT_SIZE = 30;
- private static final int CORR = 1;
-
- private final BitMatrix image;
- private final int height;
- private final int width;
- private final int leftInit;
- private final int rightInit;
- private final int downInit;
- private final int upInit;
-
- /**
- * @throws NotFoundException if image is too small
- */
- public WhiteRectangleDetector(BitMatrix image) throws NotFoundException {
- this.image = image;
- height = image.getHeight();
- width = image.getWidth();
- leftInit = (width - INIT_SIZE) >> 1;
- rightInit = (width + INIT_SIZE) >> 1;
- upInit = (height - INIT_SIZE) >> 1;
- downInit = (height + INIT_SIZE) >> 1;
- if (upInit < 0 || leftInit < 0 || downInit >= height || rightInit >= width) {
- throw NotFoundException.getNotFoundInstance();
- }
- }
-
- /**
- * @throws NotFoundException if image is too small
- */
- public WhiteRectangleDetector(BitMatrix image, int initSize, int x, int y) throws NotFoundException {
- this.image = image;
- height = image.getHeight();
- width = image.getWidth();
- int halfsize = initSize >> 1;
- leftInit = x - halfsize;
- rightInit = x + halfsize;
- upInit = y - halfsize;
- downInit = y + halfsize;
- if (upInit < 0 || leftInit < 0 || downInit >= height || rightInit >= width) {
- throw NotFoundException.getNotFoundInstance();
- }
- }
-
- /**
- *
- * Detects a candidate barcode-like rectangular region within an image. It
- * starts around the center of the image, increases the size of the candidate
- * region until it finds a white rectangular region.
- *
- *
- * @return {@link ResultPoint[]} describing the corners of the rectangular
- * region. The first and last points are opposed on the diagonal, as
- * are the second and third. The first point will be the topmost
- * point and the last, the bottommost. The second point will be
- * leftmost and the third, the rightmost
- * @throws NotFoundException if no Data Matrix Code can be found
- */
- public ResultPoint[] detect() throws NotFoundException {
-
- int left = leftInit;
- int right = rightInit;
- int up = upInit;
- int down = downInit;
- boolean sizeExceeded = false;
- boolean aBlackPointFoundOnBorder = true;
- boolean atLeastOneBlackPointFoundOnBorder = false;
-
- while (aBlackPointFoundOnBorder) {
-
- aBlackPointFoundOnBorder = false;
-
- // .....
- // . |
- // .....
- boolean rightBorderNotWhite = true;
- while (rightBorderNotWhite && right < width) {
- rightBorderNotWhite = containsBlackPoint(up, down, right, false);
- if (rightBorderNotWhite) {
- right++;
- aBlackPointFoundOnBorder = true;
- }
- }
-
- if (right >= width) {
- sizeExceeded = true;
- break;
- }
-
- // .....
- // . .
- // .___.
- boolean bottomBorderNotWhite = true;
- while (bottomBorderNotWhite && down < height) {
- bottomBorderNotWhite = containsBlackPoint(left, right, down, true);
- if (bottomBorderNotWhite) {
- down++;
- aBlackPointFoundOnBorder = true;
- }
- }
-
- if (down >= height) {
- sizeExceeded = true;
- break;
- }
-
- // .....
- // | .
- // .....
- boolean leftBorderNotWhite = true;
- while (leftBorderNotWhite && left >= 0) {
- leftBorderNotWhite = containsBlackPoint(up, down, left, false);
- if (leftBorderNotWhite) {
- left--;
- aBlackPointFoundOnBorder = true;
- }
- }
-
- if (left < 0) {
- sizeExceeded = true;
- break;
- }
-
- // .___.
- // . .
- // .....
- boolean topBorderNotWhite = true;
- while (topBorderNotWhite && up >= 0) {
- topBorderNotWhite = containsBlackPoint(left, right, up, true);
- if (topBorderNotWhite) {
- up--;
- aBlackPointFoundOnBorder = true;
- }
- }
-
- if (up < 0) {
- sizeExceeded = true;
- break;
- }
-
- if (aBlackPointFoundOnBorder) {
- atLeastOneBlackPointFoundOnBorder = true;
- }
-
- }
-
- if (!sizeExceeded && atLeastOneBlackPointFoundOnBorder) {
-
- int maxSize = right - left;
-
- ResultPoint z = null;
- for (int i = 1; i < maxSize; i++) {
- z = getBlackPointOnSegment(left, down - i, left + i, down);
- if (z != null) {
- break;
- }
- }
-
- if (z == null) {
- throw NotFoundException.getNotFoundInstance();
- }
-
- ResultPoint t = null;
- //go down right
- for (int i = 1; i < maxSize; i++) {
- t = getBlackPointOnSegment(left, up + i, left + i, up);
- if (t != null) {
- break;
- }
- }
-
- if (t == null) {
- throw NotFoundException.getNotFoundInstance();
- }
-
- ResultPoint x = null;
- //go down left
- for (int i = 1; i < maxSize; i++) {
- x = getBlackPointOnSegment(right, up + i, right - i, up);
- if (x != null) {
- break;
- }
- }
-
- if (x == null) {
- throw NotFoundException.getNotFoundInstance();
- }
-
- ResultPoint y = null;
- //go up left
- for (int i = 1; i < maxSize; i++) {
- y = getBlackPointOnSegment(right, down - i, right - i, down);
- if (y != null) {
- break;
- }
- }
-
- if (y == null) {
- throw NotFoundException.getNotFoundInstance();
- }
-
- return centerEdges(y, z, x, t);
-
- } else {
- throw NotFoundException.getNotFoundInstance();
- }
- }
-
- /**
- * Ends up being a bit faster than Math.round(). This merely rounds its
- * argument to the nearest int, where x.5 rounds up.
- */
- private static int round(float d) {
- return (int) (d + 0.5f);
- }
-
- private ResultPoint getBlackPointOnSegment(float aX, float aY, float bX, float bY) {
- int dist = distanceL2(aX, aY, bX, bY);
- float xStep = (bX - aX) / dist;
- float yStep = (bY - aY) / dist;
-
- for (int i = 0; i < dist; i++) {
- int x = round(aX + i * xStep);
- int y = round(aY + i * yStep);
- if (image.get(x, y)) {
- return new ResultPoint(x, y);
- }
- }
- return null;
- }
-
- private static int distanceL2(float aX, float aY, float bX, float bY) {
- float xDiff = aX - bX;
- float yDiff = aY - bY;
- return round((float) Math.sqrt(xDiff * xDiff + yDiff * yDiff));
- }
-
- /**
- * recenters the points of a constant distance towards the center
- *
- * @param y bottom most point
- * @param z left most point
- * @param x right most point
- * @param t top most point
- * @return {@link ResultPoint}[] describing the corners of the rectangular
- * region. The first and last points are opposed on the diagonal, as
- * are the second and third. The first point will be the topmost
- * point and the last, the bottommost. The second point will be
- * leftmost and the third, the rightmost
- */
- private ResultPoint[] centerEdges(ResultPoint y, ResultPoint z,
- ResultPoint x, ResultPoint t) {
-
- //
- // t t
- // z x
- // x OR z
- // y y
- //
-
- float yi = y.getX();
- float yj = y.getY();
- float zi = z.getX();
- float zj = z.getY();
- float xi = x.getX();
- float xj = x.getY();
- float ti = t.getX();
- float tj = t.getY();
-
- if (yi < width / 2) {
- return new ResultPoint[]{
- new ResultPoint(ti - CORR, tj + CORR),
- new ResultPoint(zi + CORR, zj + CORR),
- new ResultPoint(xi - CORR, xj - CORR),
- new ResultPoint(yi + CORR, yj - CORR)};
- } else {
- return new ResultPoint[]{
- new ResultPoint(ti + CORR, tj + CORR),
- new ResultPoint(zi + CORR, zj - CORR),
- new ResultPoint(xi - CORR, xj + CORR),
- new ResultPoint(yi - CORR, yj - CORR)};
- }
- }
-
- /**
- * Determines whether a segment contains a black point
- *
- * @param a min value of the scanned coordinate
- * @param b max value of the scanned coordinate
- * @param fixed value of fixed coordinate
- * @param horizontal set to true if scan must be horizontal, false if vertical
- * @return true if a black point has been found, else false.
- */
- private boolean containsBlackPoint(int a, int b, int fixed, boolean horizontal) {
-
- if (horizontal) {
- for (int x = a; x <= b; x++) {
- if (image.get(x, fixed)) {
- return true;
- }
- }
- } else {
- for (int y = a; y <= b; y++) {
- if (image.get(fixed, y)) {
- return true;
- }
- }
- }
-
- return false;
- }
-
-}
\ No newline at end of file
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/reedsolomon/GenericGF.java b/OpenPGP-Keychain/src/com/google/zxing/common/reedsolomon/GenericGF.java
deleted file mode 100644
index 859c379ee..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/common/reedsolomon/GenericGF.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * Copyright 2007 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.common.reedsolomon;
-
-/**
- *
This class contains utility methods for performing mathematical operations over
- * the Galois Fields. Operations use a given primitive polynomial in calculations.
- *
- *
Throughout this package, elements of the GF are represented as an int
- * for convenience and speed (but at the cost of memory).
- *
- *
- * @author Sean Owen
- * @author David Olivier
- */
-public final class GenericGF {
-
- public static final GenericGF AZTEC_DATA_12 = new GenericGF(0x1069, 4096); // x^12 + x^6 + x^5 + x^3 + 1
- public static final GenericGF AZTEC_DATA_10 = new GenericGF(0x409, 1024); // x^10 + x^3 + 1
- public static final GenericGF AZTEC_DATA_6 = new GenericGF(0x43, 64); // x^6 + x + 1
- public static final GenericGF AZTEC_PARAM = new GenericGF(0x13, 16); // x^4 + x + 1
- public static final GenericGF QR_CODE_FIELD_256 = new GenericGF(0x011D, 256); // x^8 + x^4 + x^3 + x^2 + 1
- public static final GenericGF DATA_MATRIX_FIELD_256 = new GenericGF(0x012D, 256); // x^8 + x^5 + x^3 + x^2 + 1
- public static final GenericGF AZTEC_DATA_8 = DATA_MATRIX_FIELD_256;
-
- private static final int INITIALIZATION_THRESHOLD = 0;
-
- private int[] expTable;
- private int[] logTable;
- private GenericGFPoly zero;
- private GenericGFPoly one;
- private final int size;
- private final int primitive;
- private boolean initialized = false;
-
- /**
- * Create a representation of GF(size) using the given primitive polynomial.
- *
- * @param primitive irreducible polynomial whose coefficients are represented by
- * the bits of an int, where the least-significant bit represents the constant
- * coefficient
- */
- public GenericGF(int primitive, int size) {
- this.primitive = primitive;
- this.size = size;
-
- if (size <= INITIALIZATION_THRESHOLD){
- initialize();
- }
- }
-
- private void initialize(){
- expTable = new int[size];
- logTable = new int[size];
- int x = 1;
- for (int i = 0; i < size; i++) {
- expTable[i] = x;
- x <<= 1; // x = x * 2; we're assuming the generator alpha is 2
- if (x >= size) {
- x ^= primitive;
- x &= size-1;
- }
- }
- for (int i = 0; i < size-1; i++) {
- logTable[expTable[i]] = i;
- }
- // logTable[0] == 0 but this should never be used
- zero = new GenericGFPoly(this, new int[]{0});
- one = new GenericGFPoly(this, new int[]{1});
- initialized = true;
- }
-
- private void checkInit(){
- if (!initialized) {
- initialize();
- }
- }
-
- GenericGFPoly getZero() {
- checkInit();
-
- return zero;
- }
-
- GenericGFPoly getOne() {
- checkInit();
-
- return one;
- }
-
- /**
- * @return the monomial representing coefficient * x^degree
- */
- GenericGFPoly buildMonomial(int degree, int coefficient) {
- checkInit();
-
- if (degree < 0) {
- throw new IllegalArgumentException();
- }
- if (coefficient == 0) {
- return zero;
- }
- int[] coefficients = new int[degree + 1];
- coefficients[0] = coefficient;
- return new GenericGFPoly(this, coefficients);
- }
-
- /**
- * Implements both addition and subtraction -- they are the same in GF(size).
- *
- * @return sum/difference of a and b
- */
- static int addOrSubtract(int a, int b) {
- return a ^ b;
- }
-
- /**
- * @return 2 to the power of a in GF(size)
- */
- int exp(int a) {
- checkInit();
-
- return expTable[a];
- }
-
- /**
- * @return base 2 log of a in GF(size)
- */
- int log(int a) {
- checkInit();
-
- if (a == 0) {
- throw new IllegalArgumentException();
- }
- return logTable[a];
- }
-
- /**
- * @return multiplicative inverse of a
- */
- int inverse(int a) {
- checkInit();
-
- if (a == 0) {
- throw new ArithmeticException();
- }
- return expTable[size - logTable[a] - 1];
- }
-
- /**
- * @param a
- * @param b
- * @return product of a and b in GF(size)
- */
- int multiply(int a, int b) {
- checkInit();
-
- if (a == 0 || b == 0) {
- return 0;
- }
-
- if (a<0 || b <0 || a>=size || b >=size){
- a++;
- }
-
- int logSum = logTable[a] + logTable[b];
- return expTable[(logSum % size) + logSum / size];
- }
-
- public int getSize(){
- return size;
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/reedsolomon/GenericGFPoly.java b/OpenPGP-Keychain/src/com/google/zxing/common/reedsolomon/GenericGFPoly.java
deleted file mode 100644
index 056802287..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/common/reedsolomon/GenericGFPoly.java
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * Copyright 2007 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.common.reedsolomon;
-
-/**
- *
Represents a polynomial whose coefficients are elements of a GF.
- * Instances of this class are immutable.
- *
- *
Much credit is due to William Rucklidge since portions of this code are an indirect
- * port of his C++ Reed-Solomon implementation.
- *
- * @author Sean Owen
- */
-final class GenericGFPoly {
-
- private final GenericGF field;
- private final int[] coefficients;
-
- /**
- * @param field the {@link GenericGF} instance representing the field to use
- * to perform computations
- * @param coefficients coefficients as ints representing elements of GF(size), arranged
- * from most significant (highest-power term) coefficient to least significant
- * @throws IllegalArgumentException if argument is null or empty,
- * or if leading coefficient is 0 and this is not a
- * constant polynomial (that is, it is not the monomial "0")
- */
- GenericGFPoly(GenericGF field, int[] coefficients) {
- if (coefficients == null || coefficients.length == 0) {
- throw new IllegalArgumentException();
- }
- this.field = field;
- int coefficientsLength = coefficients.length;
- if (coefficientsLength > 1 && coefficients[0] == 0) {
- // Leading term must be non-zero for anything except the constant polynomial "0"
- int firstNonZero = 1;
- while (firstNonZero < coefficientsLength && coefficients[firstNonZero] == 0) {
- firstNonZero++;
- }
- if (firstNonZero == coefficientsLength) {
- this.coefficients = field.getZero().coefficients;
- } else {
- this.coefficients = new int[coefficientsLength - firstNonZero];
- System.arraycopy(coefficients,
- firstNonZero,
- this.coefficients,
- 0,
- this.coefficients.length);
- }
- } else {
- this.coefficients = coefficients;
- }
- }
-
- int[] getCoefficients() {
- return coefficients;
- }
-
- /**
- * @return degree of this polynomial
- */
- int getDegree() {
- return coefficients.length - 1;
- }
-
- /**
- * @return true iff this polynomial is the monomial "0"
- */
- boolean isZero() {
- return coefficients[0] == 0;
- }
-
- /**
- * @return coefficient of x^degree term in this polynomial
- */
- int getCoefficient(int degree) {
- return coefficients[coefficients.length - 1 - degree];
- }
-
- /**
- * @return evaluation of this polynomial at a given point
- */
- int evaluateAt(int a) {
- if (a == 0) {
- // Just return the x^0 coefficient
- return getCoefficient(0);
- }
- int size = coefficients.length;
- if (a == 1) {
- // Just the sum of the coefficients
- int result = 0;
- for (int i = 0; i < size; i++) {
- result = GenericGF.addOrSubtract(result, coefficients[i]);
- }
- return result;
- }
- int result = coefficients[0];
- for (int i = 1; i < size; i++) {
- result = GenericGF.addOrSubtract(field.multiply(a, result), coefficients[i]);
- }
- return result;
- }
-
- GenericGFPoly addOrSubtract(GenericGFPoly other) {
- if (!field.equals(other.field)) {
- throw new IllegalArgumentException("GenericGFPolys do not have same GenericGF field");
- }
- if (isZero()) {
- return other;
- }
- if (other.isZero()) {
- return this;
- }
-
- int[] smallerCoefficients = this.coefficients;
- int[] largerCoefficients = other.coefficients;
- if (smallerCoefficients.length > largerCoefficients.length) {
- int[] temp = smallerCoefficients;
- smallerCoefficients = largerCoefficients;
- largerCoefficients = temp;
- }
- int[] sumDiff = new int[largerCoefficients.length];
- int lengthDiff = largerCoefficients.length - smallerCoefficients.length;
- // Copy high-order terms only found in higher-degree polynomial's coefficients
- System.arraycopy(largerCoefficients, 0, sumDiff, 0, lengthDiff);
-
- for (int i = lengthDiff; i < largerCoefficients.length; i++) {
- sumDiff[i] = GenericGF.addOrSubtract(smallerCoefficients[i - lengthDiff], largerCoefficients[i]);
- }
-
- return new GenericGFPoly(field, sumDiff);
- }
-
- GenericGFPoly multiply(GenericGFPoly other) {
- if (!field.equals(other.field)) {
- throw new IllegalArgumentException("GenericGFPolys do not have same GenericGF field");
- }
- if (isZero() || other.isZero()) {
- return field.getZero();
- }
- int[] aCoefficients = this.coefficients;
- int aLength = aCoefficients.length;
- int[] bCoefficients = other.coefficients;
- int bLength = bCoefficients.length;
- int[] product = new int[aLength + bLength - 1];
- for (int i = 0; i < aLength; i++) {
- int aCoeff = aCoefficients[i];
- for (int j = 0; j < bLength; j++) {
- product[i + j] = GenericGF.addOrSubtract(product[i + j],
- field.multiply(aCoeff, bCoefficients[j]));
- }
- }
- return new GenericGFPoly(field, product);
- }
-
- GenericGFPoly multiply(int scalar) {
- if (scalar == 0) {
- return field.getZero();
- }
- if (scalar == 1) {
- return this;
- }
- int size = coefficients.length;
- int[] product = new int[size];
- for (int i = 0; i < size; i++) {
- product[i] = field.multiply(coefficients[i], scalar);
- }
- return new GenericGFPoly(field, product);
- }
-
- GenericGFPoly multiplyByMonomial(int degree, int coefficient) {
- if (degree < 0) {
- throw new IllegalArgumentException();
- }
- if (coefficient == 0) {
- return field.getZero();
- }
- int size = coefficients.length;
- int[] product = new int[size + degree];
- for (int i = 0; i < size; i++) {
- product[i] = field.multiply(coefficients[i], coefficient);
- }
- return new GenericGFPoly(field, product);
- }
-
- GenericGFPoly[] divide(GenericGFPoly other) {
- if (!field.equals(other.field)) {
- throw new IllegalArgumentException("GenericGFPolys do not have same GenericGF field");
- }
- if (other.isZero()) {
- throw new IllegalArgumentException("Divide by 0");
- }
-
- GenericGFPoly quotient = field.getZero();
- GenericGFPoly remainder = this;
-
- int denominatorLeadingTerm = other.getCoefficient(other.getDegree());
- int inverseDenominatorLeadingTerm = field.inverse(denominatorLeadingTerm);
-
- while (remainder.getDegree() >= other.getDegree() && !remainder.isZero()) {
- int degreeDifference = remainder.getDegree() - other.getDegree();
- int scale = field.multiply(remainder.getCoefficient(remainder.getDegree()), inverseDenominatorLeadingTerm);
- GenericGFPoly term = other.multiplyByMonomial(degreeDifference, scale);
- GenericGFPoly iterationQuotient = field.buildMonomial(degreeDifference, scale);
- quotient = quotient.addOrSubtract(iterationQuotient);
- remainder = remainder.addOrSubtract(term);
- }
-
- return new GenericGFPoly[] { quotient, remainder };
- }
-
- public String toString() {
- StringBuffer result = new StringBuffer(8 * getDegree());
- for (int degree = getDegree(); degree >= 0; degree--) {
- int coefficient = getCoefficient(degree);
- if (coefficient != 0) {
- if (coefficient < 0) {
- result.append(" - ");
- coefficient = -coefficient;
- } else {
- if (result.length() > 0) {
- result.append(" + ");
- }
- }
- if (degree == 0 || coefficient != 1) {
- int alphaPower = field.log(coefficient);
- if (alphaPower == 0) {
- result.append('1');
- } else if (alphaPower == 1) {
- result.append('a');
- } else {
- result.append("a^");
- result.append(alphaPower);
- }
- }
- if (degree != 0) {
- if (degree == 1) {
- result.append('x');
- } else {
- result.append("x^");
- result.append(degree);
- }
- }
- }
- }
- return result.toString();
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/reedsolomon/ReedSolomonDecoder.java b/OpenPGP-Keychain/src/com/google/zxing/common/reedsolomon/ReedSolomonDecoder.java
deleted file mode 100644
index b523fd34b..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/common/reedsolomon/ReedSolomonDecoder.java
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * Copyright 2007 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.common.reedsolomon;
-
-/**
- *
Implements Reed-Solomon decoding, as the name implies.
- *
- *
The algorithm will not be explained here, but the following references were helpful
- * in creating this implementation:
Much credit is due to William Rucklidge since portions of this code are an indirect
- * port of his C++ Reed-Solomon implementation.
- *
- * @author Sean Owen
- * @author William Rucklidge
- * @author sanfordsquires
- */
-public final class ReedSolomonDecoder {
-
- private final GenericGF field;
-
- public ReedSolomonDecoder(GenericGF field) {
- this.field = field;
- }
-
- /**
- *
Decodes given set of received codewords, which include both data and error-correction
- * codewords. Really, this means it uses Reed-Solomon to detect and correct errors, in-place,
- * in the input.
- *
- * @param received data and error-correction codewords
- * @param twoS number of error-correction codewords available
- * @throws ReedSolomonException if decoding fails for any reason
- */
- public void decode(int[] received, int twoS) throws ReedSolomonException {
- GenericGFPoly poly = new GenericGFPoly(field, received);
- int[] syndromeCoefficients = new int[twoS];
- boolean dataMatrix = field.equals(GenericGF.DATA_MATRIX_FIELD_256);
- boolean noError = true;
- for (int i = 0; i < twoS; i++) {
- // Thanks to sanfordsquires for this fix:
- int eval = poly.evaluateAt(field.exp(dataMatrix ? i + 1 : i));
- syndromeCoefficients[syndromeCoefficients.length - 1 - i] = eval;
- if (eval != 0) {
- noError = false;
- }
- }
- if (noError) {
- return;
- }
- GenericGFPoly syndrome = new GenericGFPoly(field, syndromeCoefficients);
- GenericGFPoly[] sigmaOmega =
- runEuclideanAlgorithm(field.buildMonomial(twoS, 1), syndrome, twoS);
- GenericGFPoly sigma = sigmaOmega[0];
- GenericGFPoly omega = sigmaOmega[1];
- int[] errorLocations = findErrorLocations(sigma);
- int[] errorMagnitudes = findErrorMagnitudes(omega, errorLocations, dataMatrix);
- for (int i = 0; i < errorLocations.length; i++) {
- int position = received.length - 1 - field.log(errorLocations[i]);
- if (position < 0) {
- throw new ReedSolomonException("Bad error location");
- }
- received[position] = GenericGF.addOrSubtract(received[position], errorMagnitudes[i]);
- }
- }
-
- private GenericGFPoly[] runEuclideanAlgorithm(GenericGFPoly a, GenericGFPoly b, int R)
- throws ReedSolomonException {
- // Assume a's degree is >= b's
- if (a.getDegree() < b.getDegree()) {
- GenericGFPoly temp = a;
- a = b;
- b = temp;
- }
-
- GenericGFPoly rLast = a;
- GenericGFPoly r = b;
- GenericGFPoly sLast = field.getOne();
- GenericGFPoly s = field.getZero();
- GenericGFPoly tLast = field.getZero();
- GenericGFPoly t = field.getOne();
-
- // Run Euclidean algorithm until r's degree is less than R/2
- while (r.getDegree() >= R / 2) {
- GenericGFPoly rLastLast = rLast;
- GenericGFPoly sLastLast = sLast;
- GenericGFPoly tLastLast = tLast;
- rLast = r;
- sLast = s;
- tLast = t;
-
- // Divide rLastLast by rLast, with quotient in q and remainder in r
- if (rLast.isZero()) {
- // Oops, Euclidean algorithm already terminated?
- throw new ReedSolomonException("r_{i-1} was zero");
- }
- r = rLastLast;
- GenericGFPoly q = field.getZero();
- int denominatorLeadingTerm = rLast.getCoefficient(rLast.getDegree());
- int dltInverse = field.inverse(denominatorLeadingTerm);
- while (r.getDegree() >= rLast.getDegree() && !r.isZero()) {
- int degreeDiff = r.getDegree() - rLast.getDegree();
- int scale = field.multiply(r.getCoefficient(r.getDegree()), dltInverse);
- q = q.addOrSubtract(field.buildMonomial(degreeDiff, scale));
- r = r.addOrSubtract(rLast.multiplyByMonomial(degreeDiff, scale));
- }
-
- s = q.multiply(sLast).addOrSubtract(sLastLast);
- t = q.multiply(tLast).addOrSubtract(tLastLast);
- }
-
- int sigmaTildeAtZero = t.getCoefficient(0);
- if (sigmaTildeAtZero == 0) {
- throw new ReedSolomonException("sigmaTilde(0) was zero");
- }
-
- int inverse = field.inverse(sigmaTildeAtZero);
- GenericGFPoly sigma = t.multiply(inverse);
- GenericGFPoly omega = r.multiply(inverse);
- return new GenericGFPoly[]{sigma, omega};
- }
-
- private int[] findErrorLocations(GenericGFPoly errorLocator) throws ReedSolomonException {
- // This is a direct application of Chien's search
- int numErrors = errorLocator.getDegree();
- if (numErrors == 1) { // shortcut
- return new int[] { errorLocator.getCoefficient(1) };
- }
- int[] result = new int[numErrors];
- int e = 0;
- for (int i = 1; i < field.getSize() && e < numErrors; i++) {
- if (errorLocator.evaluateAt(i) == 0) {
- result[e] = field.inverse(i);
- e++;
- }
- }
- if (e != numErrors) {
- throw new ReedSolomonException("Error locator degree does not match number of roots");
- }
- return result;
- }
-
- private int[] findErrorMagnitudes(GenericGFPoly errorEvaluator, int[] errorLocations, boolean dataMatrix) {
- // This is directly applying Forney's Formula
- int s = errorLocations.length;
- int[] result = new int[s];
- for (int i = 0; i < s; i++) {
- int xiInverse = field.inverse(errorLocations[i]);
- int denominator = 1;
- for (int j = 0; j < s; j++) {
- if (i != j) {
- //denominator = field.multiply(denominator,
- // GenericGF.addOrSubtract(1, field.multiply(errorLocations[j], xiInverse)));
- // Above should work but fails on some Apple and Linux JDKs due to a Hotspot bug.
- // Below is a funny-looking workaround from Steven Parkes
- int term = field.multiply(errorLocations[j], xiInverse);
- int termPlus1 = (term & 0x1) == 0 ? term | 1 : term & ~1;
- denominator = field.multiply(denominator, termPlus1);
- }
- }
- result[i] = field.multiply(errorEvaluator.evaluateAt(xiInverse),
- field.inverse(denominator));
- // Thanks to sanfordsquires for this fix:
- if (dataMatrix) {
- result[i] = field.multiply(result[i], xiInverse);
- }
- }
- return result;
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.java b/OpenPGP-Keychain/src/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.java
deleted file mode 100644
index 05e2ae03a..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright 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.common.reedsolomon;
-
-import java.util.Vector;
-
-/**
- *
Implements Reed-Solomon enbcoding, as the name implies.
- *
- * @author Sean Owen
- * @author William Rucklidge
- */
-public final class ReedSolomonEncoder {
-
- private final GenericGF field;
- private final Vector cachedGenerators;
-
- public ReedSolomonEncoder(GenericGF field) {
- if (!GenericGF.QR_CODE_FIELD_256.equals(field)) {
- throw new IllegalArgumentException("Only QR Code is supported at this time");
- }
- this.field = field;
- this.cachedGenerators = new Vector();
- cachedGenerators.addElement(new GenericGFPoly(field, new int[] { 1 }));
- }
-
- private GenericGFPoly buildGenerator(int degree) {
- if (degree >= cachedGenerators.size()) {
- GenericGFPoly lastGenerator = (GenericGFPoly) cachedGenerators.elementAt(cachedGenerators.size() - 1);
- for (int d = cachedGenerators.size(); d <= degree; d++) {
- GenericGFPoly nextGenerator = lastGenerator.multiply(new GenericGFPoly(field, new int[] { 1, field.exp(d - 1) }));
- cachedGenerators.addElement(nextGenerator);
- lastGenerator = nextGenerator;
- }
- }
- return (GenericGFPoly) cachedGenerators.elementAt(degree);
- }
-
- public void encode(int[] toEncode, int ecBytes) {
- if (ecBytes == 0) {
- throw new IllegalArgumentException("No error correction bytes");
- }
- int dataBytes = toEncode.length - ecBytes;
- if (dataBytes <= 0) {
- throw new IllegalArgumentException("No data bytes provided");
- }
- GenericGFPoly generator = buildGenerator(ecBytes);
- int[] infoCoefficients = new int[dataBytes];
- System.arraycopy(toEncode, 0, infoCoefficients, 0, dataBytes);
- GenericGFPoly info = new GenericGFPoly(field, infoCoefficients);
- info = info.multiplyByMonomial(ecBytes, 1);
- GenericGFPoly remainder = info.divide(generator)[1];
- int[] coefficients = remainder.getCoefficients();
- int numZeroCoefficients = ecBytes - coefficients.length;
- for (int i = 0; i < numZeroCoefficients; i++) {
- toEncode[dataBytes + i] = 0;
- }
- System.arraycopy(coefficients, 0, toEncode, dataBytes + numZeroCoefficients, coefficients.length);
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/reedsolomon/ReedSolomonException.java b/OpenPGP-Keychain/src/com/google/zxing/common/reedsolomon/ReedSolomonException.java
deleted file mode 100644
index d5b45a612..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/common/reedsolomon/ReedSolomonException.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2007 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.common.reedsolomon;
-
-/**
- *
Thrown when an exception occurs during Reed-Solomon decoding, such as when
- * there are too many errors to correct.
- *
- * @author Sean Owen
- */
-public final class ReedSolomonException extends Exception {
-
- public ReedSolomonException(String message) {
- super(message);
- }
-
-}
\ No newline at end of file
diff --git a/OpenPGP-Keychain/src/com/google/zxing/multi/ByQuadrantReader.java b/OpenPGP-Keychain/src/com/google/zxing/multi/ByQuadrantReader.java
deleted file mode 100644
index 35904d364..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/multi/ByQuadrantReader.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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.multi;
-
-import com.google.zxing.BinaryBitmap;
-import com.google.zxing.ChecksumException;
-import com.google.zxing.FormatException;
-import com.google.zxing.NotFoundException;
-import com.google.zxing.Reader;
-import com.google.zxing.Result;
-
-import java.util.Hashtable;
-
-/**
- * This class attempts to decode a barcode from an image, not by scanning the whole image,
- * but by scanning subsets of the image. This is important when there may be multiple barcodes in
- * an image, and detecting a barcode may find parts of multiple barcode and fail to decode
- * (e.g. QR Codes). Instead this scans the four quadrants of the image -- and also the center
- * 'quadrant' to cover the case where a barcode is found in the center.
- *
- * @see GenericMultipleBarcodeReader
- */
-public final class ByQuadrantReader implements Reader {
-
- private final Reader delegate;
-
- public ByQuadrantReader(Reader delegate) {
- this.delegate = delegate;
- }
-
- public Result decode(BinaryBitmap image)
- throws NotFoundException, ChecksumException, FormatException {
- return decode(image, null);
- }
-
- public Result decode(BinaryBitmap image, Hashtable hints)
- throws NotFoundException, ChecksumException, FormatException {
-
- int width = image.getWidth();
- int height = image.getHeight();
- int halfWidth = width / 2;
- int halfHeight = height / 2;
-
- BinaryBitmap topLeft = image.crop(0, 0, halfWidth, halfHeight);
- try {
- return delegate.decode(topLeft, hints);
- } catch (NotFoundException re) {
- // continue
- }
-
- BinaryBitmap topRight = image.crop(halfWidth, 0, halfWidth, halfHeight);
- try {
- return delegate.decode(topRight, hints);
- } catch (NotFoundException re) {
- // continue
- }
-
- BinaryBitmap bottomLeft = image.crop(0, halfHeight, halfWidth, halfHeight);
- try {
- return delegate.decode(bottomLeft, hints);
- } catch (NotFoundException re) {
- // continue
- }
-
- BinaryBitmap bottomRight = image.crop(halfWidth, halfHeight, halfWidth, halfHeight);
- try {
- return delegate.decode(bottomRight, hints);
- } catch (NotFoundException re) {
- // continue
- }
-
- int quarterWidth = halfWidth / 2;
- int quarterHeight = halfHeight / 2;
- BinaryBitmap center = image.crop(quarterWidth, quarterHeight, halfWidth, halfHeight);
- return delegate.decode(center, hints);
- }
-
- public void reset() {
- delegate.reset();
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/multi/GenericMultipleBarcodeReader.java b/OpenPGP-Keychain/src/com/google/zxing/multi/GenericMultipleBarcodeReader.java
deleted file mode 100644
index 70d454251..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/multi/GenericMultipleBarcodeReader.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * 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.multi;
-
-import com.google.zxing.BinaryBitmap;
-import com.google.zxing.NotFoundException;
-import com.google.zxing.Reader;
-import com.google.zxing.ReaderException;
-import com.google.zxing.Result;
-import com.google.zxing.ResultPoint;
-
-import java.util.Hashtable;
-import java.util.Vector;
-
-/**
- *
Attempts to locate multiple barcodes in an image by repeatedly decoding portion of the image.
- * After one barcode is found, the areas left, above, right and below the barcode's
- * {@link com.google.zxing.ResultPoint}s are scanned, recursively.
- *
- *
A caller may want to also employ {@link ByQuadrantReader} when attempting to find multiple
- * 2D barcodes, like QR Codes, in an image, where the presence of multiple barcodes might prevent
- * detecting any one of them.
- *
- *
That is, instead of passing a {@link Reader} a caller might pass
- * new ByQuadrantReader(reader).
- *
- * @author Sean Owen
- */
-public final class GenericMultipleBarcodeReader implements MultipleBarcodeReader {
-
- private static final int MIN_DIMENSION_TO_RECUR = 100;
-
- private final Reader delegate;
-
- public GenericMultipleBarcodeReader(Reader delegate) {
- this.delegate = delegate;
- }
-
- public Result[] decodeMultiple(BinaryBitmap image) throws NotFoundException {
- return decodeMultiple(image, null);
- }
-
- public Result[] decodeMultiple(BinaryBitmap image, Hashtable hints)
- throws NotFoundException {
- Vector results = new Vector();
- doDecodeMultiple(image, hints, results, 0, 0);
- if (results.isEmpty()) {
- throw NotFoundException.getNotFoundInstance();
- }
- int numResults = results.size();
- Result[] resultArray = new Result[numResults];
- for (int i = 0; i < numResults; i++) {
- resultArray[i] = (Result) results.elementAt(i);
- }
- return resultArray;
- }
-
- private void doDecodeMultiple(BinaryBitmap image,
- Hashtable hints,
- Vector results,
- int xOffset,
- int yOffset) {
- Result result;
- try {
- result = delegate.decode(image, hints);
- } catch (ReaderException re) {
- return;
- }
- boolean alreadyFound = false;
- for (int i = 0; i < results.size(); i++) {
- Result existingResult = (Result) results.elementAt(i);
- if (existingResult.getText().equals(result.getText())) {
- alreadyFound = true;
- break;
- }
- }
- if (alreadyFound) {
- return;
- }
- results.addElement(translateResultPoints(result, xOffset, yOffset));
- ResultPoint[] resultPoints = result.getResultPoints();
- if (resultPoints == null || resultPoints.length == 0) {
- return;
- }
- int width = image.getWidth();
- int height = image.getHeight();
- float minX = width;
- float minY = height;
- float maxX = 0.0f;
- float maxY = 0.0f;
- for (int i = 0; i < resultPoints.length; i++) {
- ResultPoint point = resultPoints[i];
- float x = point.getX();
- float y = point.getY();
- if (x < minX) {
- minX = x;
- }
- if (y < minY) {
- minY = y;
- }
- if (x > maxX) {
- maxX = x;
- }
- if (y > maxY) {
- maxY = y;
- }
- }
-
- // Decode left of barcode
- if (minX > MIN_DIMENSION_TO_RECUR) {
- doDecodeMultiple(image.crop(0, 0, (int) minX, height),
- hints, results, xOffset, yOffset);
- }
- // Decode above barcode
- if (minY > MIN_DIMENSION_TO_RECUR) {
- doDecodeMultiple(image.crop(0, 0, width, (int) minY),
- hints, results, xOffset, yOffset);
- }
- // Decode right of barcode
- if (maxX < width - MIN_DIMENSION_TO_RECUR) {
- doDecodeMultiple(image.crop((int) maxX, 0, width - (int) maxX, height),
- hints, results, xOffset + (int) maxX, yOffset);
- }
- // Decode below barcode
- if (maxY < height - MIN_DIMENSION_TO_RECUR) {
- doDecodeMultiple(image.crop(0, (int) maxY, width, height - (int) maxY),
- hints, results, xOffset, yOffset + (int) maxY);
- }
- }
-
- private static Result translateResultPoints(Result result, int xOffset, int yOffset) {
- ResultPoint[] oldResultPoints = result.getResultPoints();
- ResultPoint[] newResultPoints = new ResultPoint[oldResultPoints.length];
- for (int i = 0; i < oldResultPoints.length; i++) {
- ResultPoint oldPoint = oldResultPoints[i];
- newResultPoints[i] = new ResultPoint(oldPoint.getX() + xOffset, oldPoint.getY() + yOffset);
- }
- return new Result(result.getText(), result.getRawBytes(), newResultPoints,
- result.getBarcodeFormat());
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/multi/MultipleBarcodeReader.java b/OpenPGP-Keychain/src/com/google/zxing/multi/MultipleBarcodeReader.java
deleted file mode 100644
index 5f0c7eb5d..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/multi/MultipleBarcodeReader.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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.multi;
-
-import com.google.zxing.BinaryBitmap;
-import com.google.zxing.NotFoundException;
-import com.google.zxing.Result;
-
-import java.util.Hashtable;
-
-/**
- * Implementation of this interface attempt to read several barcodes from one image.
- *
- * @see com.google.zxing.Reader
- * @author Sean Owen
- */
-public interface MultipleBarcodeReader {
-
- Result[] decodeMultiple(BinaryBitmap image) throws NotFoundException;
-
- Result[] decodeMultiple(BinaryBitmap image, Hashtable hints) throws NotFoundException;
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/multi/qrcode/detector/MultiDetector.java b/OpenPGP-Keychain/src/com/google/zxing/multi/qrcode/detector/MultiDetector.java
deleted file mode 100644
index 584c41404..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/multi/qrcode/detector/MultiDetector.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * 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.multi.qrcode.detector;
-
-import com.google.zxing.NotFoundException;
-import com.google.zxing.ReaderException;
-import com.google.zxing.common.BitMatrix;
-import com.google.zxing.common.DetectorResult;
-import com.google.zxing.qrcode.detector.Detector;
-import com.google.zxing.qrcode.detector.FinderPatternInfo;
-
-import java.util.Hashtable;
-import java.util.Vector;
-
-/**
- *
Encapsulates logic that can detect one or more QR Codes in an image, even if the QR Code
- * is rotated or skewed, or partially obscured.
- *
- * @author Sean Owen
- * @author Hannes Erven
- */
-public final class MultiDetector extends Detector {
-
- private static final DetectorResult[] EMPTY_DETECTOR_RESULTS = new DetectorResult[0];
-
- public MultiDetector(BitMatrix image) {
- super(image);
- }
-
- public DetectorResult[] detectMulti(Hashtable hints) throws NotFoundException {
- BitMatrix image = getImage();
- MultiFinderPatternFinder finder = new MultiFinderPatternFinder(image);
- FinderPatternInfo[] info = finder.findMulti(hints);
-
- if (info == null || info.length == 0) {
- throw NotFoundException.getNotFoundInstance();
- }
-
- Vector result = new Vector();
- for (int i = 0; i < info.length; i++) {
- try {
- result.addElement(processFinderPatternInfo(info[i]));
- } catch (ReaderException e) {
- // ignore
- }
- }
- if (result.isEmpty()) {
- return EMPTY_DETECTOR_RESULTS;
- } else {
- DetectorResult[] resultArray = new DetectorResult[result.size()];
- for (int i = 0; i < result.size(); i++) {
- resultArray[i] = (DetectorResult) result.elementAt(i);
- }
- return resultArray;
- }
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/multi/qrcode/detector/MultiFinderPatternFinder.java b/OpenPGP-Keychain/src/com/google/zxing/multi/qrcode/detector/MultiFinderPatternFinder.java
deleted file mode 100644
index 1162324e2..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/multi/qrcode/detector/MultiFinderPatternFinder.java
+++ /dev/null
@@ -1,324 +0,0 @@
-/*
- * 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.multi.qrcode.detector;
-
-import com.google.zxing.DecodeHintType;
-import com.google.zxing.NotFoundException;
-import com.google.zxing.ResultPoint;
-import com.google.zxing.ResultPointCallback;
-import com.google.zxing.common.BitMatrix;
-import com.google.zxing.common.Collections;
-import com.google.zxing.common.Comparator;
-import com.google.zxing.qrcode.detector.FinderPattern;
-import com.google.zxing.qrcode.detector.FinderPatternFinder;
-import com.google.zxing.qrcode.detector.FinderPatternInfo;
-
-import java.util.Hashtable;
-import java.util.Vector;
-
-/**
- *
This class attempts to find finder patterns in a QR Code. Finder patterns are the square
- * markers at three corners of a QR Code.
- *
- *
This class is thread-safe but not reentrant. Each thread must allocate its own object.
- *
- *
In contrast to {@link FinderPatternFinder}, this class will return an array of all possible
- * QR code locations in the image.
- *
- *
Use the TRY_HARDER hint to ask for a more thorough detection.
- *
- * @author Sean Owen
- * @author Hannes Erven
- */
-final class MultiFinderPatternFinder extends FinderPatternFinder {
-
- private static final FinderPatternInfo[] EMPTY_RESULT_ARRAY = new FinderPatternInfo[0];
-
- // TODO MIN_MODULE_COUNT and MAX_MODULE_COUNT would be great hints to ask the user for
- // since it limits the number of regions to decode
-
- // max. legal count of modules per QR code edge (177)
- private static final float MAX_MODULE_COUNT_PER_EDGE = 180;
- // min. legal count per modules per QR code edge (11)
- private static final float MIN_MODULE_COUNT_PER_EDGE = 9;
-
- /**
- * More or less arbitrary cutoff point for determining if two finder patterns might belong
- * to the same code if they differ less than DIFF_MODSIZE_CUTOFF_PERCENT percent in their
- * estimated modules sizes.
- */
- private static final float DIFF_MODSIZE_CUTOFF_PERCENT = 0.05f;
-
- /**
- * More or less arbitrary cutoff point for determining if two finder patterns might belong
- * to the same code if they differ less than DIFF_MODSIZE_CUTOFF pixels/module in their
- * estimated modules sizes.
- */
- private static final float DIFF_MODSIZE_CUTOFF = 0.5f;
-
-
- /**
- * A comparator that orders FinderPatterns by their estimated module size.
- */
- private static class ModuleSizeComparator implements Comparator {
- public int compare(Object center1, Object center2) {
- float value = ((FinderPattern) center2).getEstimatedModuleSize() -
- ((FinderPattern) center1).getEstimatedModuleSize();
- return value < 0.0 ? -1 : value > 0.0 ? 1 : 0;
- }
- }
-
- /**
- *
Creates a finder that will search the image for three finder patterns.
- *
- * @param image image to search
- */
- MultiFinderPatternFinder(BitMatrix image) {
- super(image);
- }
-
- MultiFinderPatternFinder(BitMatrix image, ResultPointCallback resultPointCallback) {
- super(image, resultPointCallback);
- }
-
- /**
- * @return the 3 best {@link FinderPattern}s from our list of candidates. The "best" are
- * those that have been detected at least {@link #CENTER_QUORUM} times, and whose module
- * size differs from the average among those patterns the least
- * @throws NotFoundException if 3 such finder patterns do not exist
- */
- private FinderPattern[][] selectBestPatterns() throws NotFoundException {
- Vector possibleCenters = getPossibleCenters();
- int size = possibleCenters.size();
-
- if (size < 3) {
- // Couldn't find enough finder patterns
- throw NotFoundException.getNotFoundInstance();
- }
-
- /*
- * Begin HE modifications to safely detect multiple codes of equal size
- */
- if (size == 3) {
- return new FinderPattern[][]{
- new FinderPattern[]{
- (FinderPattern) possibleCenters.elementAt(0),
- (FinderPattern) possibleCenters.elementAt(1),
- (FinderPattern) possibleCenters.elementAt(2)
- }
- };
- }
-
- // Sort by estimated module size to speed up the upcoming checks
- Collections.insertionSort(possibleCenters, new ModuleSizeComparator());
-
- /*
- * Now lets start: build a list of tuples of three finder locations that
- * - feature similar module sizes
- * - are placed in a distance so the estimated module count is within the QR specification
- * - have similar distance between upper left/right and left top/bottom finder patterns
- * - form a triangle with 90° angle (checked by comparing top right/bottom left distance
- * with pythagoras)
- *
- * Note: we allow each point to be used for more than one code region: this might seem
- * counterintuitive at first, but the performance penalty is not that big. At this point,
- * we cannot make a good quality decision whether the three finders actually represent
- * a QR code, or are just by chance layouted so it looks like there might be a QR code there.
- * So, if the layout seems right, lets have the decoder try to decode.
- */
-
- Vector results = new Vector(); // holder for the results
-
- for (int i1 = 0; i1 < (size - 2); i1++) {
- FinderPattern p1 = (FinderPattern) possibleCenters.elementAt(i1);
- if (p1 == null) {
- continue;
- }
-
- for (int i2 = i1 + 1; i2 < (size - 1); i2++) {
- FinderPattern p2 = (FinderPattern) possibleCenters.elementAt(i2);
- if (p2 == null) {
- continue;
- }
-
- // Compare the expected module sizes; if they are really off, skip
- float vModSize12 = (p1.getEstimatedModuleSize() - p2.getEstimatedModuleSize()) /
- Math.min(p1.getEstimatedModuleSize(), p2.getEstimatedModuleSize());
- float vModSize12A = Math.abs(p1.getEstimatedModuleSize() - p2.getEstimatedModuleSize());
- if (vModSize12A > DIFF_MODSIZE_CUTOFF && vModSize12 >= DIFF_MODSIZE_CUTOFF_PERCENT) {
- // break, since elements are ordered by the module size deviation there cannot be
- // any more interesting elements for the given p1.
- break;
- }
-
- for (int i3 = i2 + 1; i3 < size; i3++) {
- FinderPattern p3 = (FinderPattern) possibleCenters.elementAt(i3);
- if (p3 == null) {
- continue;
- }
-
- // Compare the expected module sizes; if they are really off, skip
- float vModSize23 = (p2.getEstimatedModuleSize() - p3.getEstimatedModuleSize()) /
- Math.min(p2.getEstimatedModuleSize(), p3.getEstimatedModuleSize());
- float vModSize23A = Math.abs(p2.getEstimatedModuleSize() - p3.getEstimatedModuleSize());
- if (vModSize23A > DIFF_MODSIZE_CUTOFF && vModSize23 >= DIFF_MODSIZE_CUTOFF_PERCENT) {
- // break, since elements are ordered by the module size deviation there cannot be
- // any more interesting elements for the given p1.
- break;
- }
-
- FinderPattern[] test = {p1, p2, p3};
- ResultPoint.orderBestPatterns(test);
-
- // Calculate the distances: a = topleft-bottomleft, b=topleft-topright, c = diagonal
- FinderPatternInfo info = new FinderPatternInfo(test);
- float dA = ResultPoint.distance(info.getTopLeft(), info.getBottomLeft());
- float dC = ResultPoint.distance(info.getTopRight(), info.getBottomLeft());
- float dB = ResultPoint.distance(info.getTopLeft(), info.getTopRight());
-
- // Check the sizes
- float estimatedModuleCount = (dA + dB) / (p1.getEstimatedModuleSize() * 2.0f);
- if (estimatedModuleCount > MAX_MODULE_COUNT_PER_EDGE ||
- estimatedModuleCount < MIN_MODULE_COUNT_PER_EDGE) {
- continue;
- }
-
- // Calculate the difference of the edge lengths in percent
- float vABBC = Math.abs((dA - dB) / Math.min(dA, dB));
- if (vABBC >= 0.1f) {
- continue;
- }
-
- // Calculate the diagonal length by assuming a 90° angle at topleft
- float dCpy = (float) Math.sqrt(dA * dA + dB * dB);
- // Compare to the real distance in %
- float vPyC = Math.abs((dC - dCpy) / Math.min(dC, dCpy));
-
- if (vPyC >= 0.1f) {
- continue;
- }
-
- // All tests passed!
- results.addElement(test);
- } // end iterate p3
- } // end iterate p2
- } // end iterate p1
-
- if (!results.isEmpty()) {
- FinderPattern[][] resultArray = new FinderPattern[results.size()][];
- for (int i = 0; i < results.size(); i++) {
- resultArray[i] = (FinderPattern[]) results.elementAt(i);
- }
- return resultArray;
- }
-
- // Nothing found!
- throw NotFoundException.getNotFoundInstance();
- }
-
- public FinderPatternInfo[] findMulti(Hashtable hints) throws NotFoundException {
- boolean tryHarder = hints != null && hints.containsKey(DecodeHintType.TRY_HARDER);
- BitMatrix image = getImage();
- int maxI = image.getHeight();
- int maxJ = image.getWidth();
- // We are looking for black/white/black/white/black modules in
- // 1:1:3:1:1 ratio; this tracks the number of such modules seen so far
-
- // Let's assume that the maximum version QR Code we support takes up 1/4 the height of the
- // image, and then account for the center being 3 modules in size. This gives the smallest
- // number of pixels the center could be, so skip this often. When trying harder, look for all
- // QR versions regardless of how dense they are.
- int iSkip = (int) (maxI / (MAX_MODULES * 4.0f) * 3);
- if (iSkip < MIN_SKIP || tryHarder) {
- iSkip = MIN_SKIP;
- }
-
- int[] stateCount = new int[5];
- for (int i = iSkip - 1; i < maxI; i += iSkip) {
- // Get a row of black/white values
- stateCount[0] = 0;
- stateCount[1] = 0;
- stateCount[2] = 0;
- stateCount[3] = 0;
- stateCount[4] = 0;
- int currentState = 0;
- for (int j = 0; j < maxJ; j++) {
- if (image.get(j, i)) {
- // Black pixel
- if ((currentState & 1) == 1) { // Counting white pixels
- currentState++;
- }
- stateCount[currentState]++;
- } else { // White pixel
- if ((currentState & 1) == 0) { // Counting black pixels
- if (currentState == 4) { // A winner?
- if (foundPatternCross(stateCount)) { // Yes
- boolean confirmed = handlePossibleCenter(stateCount, i, j);
- if (!confirmed) {
- do { // Advance to next black pixel
- j++;
- } while (j < maxJ && !image.get(j, i));
- j--; // back up to that last white pixel
- }
- // Clear state to start looking again
- currentState = 0;
- stateCount[0] = 0;
- stateCount[1] = 0;
- stateCount[2] = 0;
- stateCount[3] = 0;
- stateCount[4] = 0;
- } else { // No, shift counts back by two
- stateCount[0] = stateCount[2];
- stateCount[1] = stateCount[3];
- stateCount[2] = stateCount[4];
- stateCount[3] = 1;
- stateCount[4] = 0;
- currentState = 3;
- }
- } else {
- stateCount[++currentState]++;
- }
- } else { // Counting white pixels
- stateCount[currentState]++;
- }
- }
- } // for j=...
-
- if (foundPatternCross(stateCount)) {
- handlePossibleCenter(stateCount, i, maxJ);
- } // end if foundPatternCross
- } // for i=iSkip-1 ...
- FinderPattern[][] patternInfo = selectBestPatterns();
- Vector result = new Vector();
- for (int i = 0; i < patternInfo.length; i++) {
- FinderPattern[] pattern = patternInfo[i];
- ResultPoint.orderBestPatterns(pattern);
- result.addElement(new FinderPatternInfo(pattern));
- }
-
- if (result.isEmpty()) {
- return EMPTY_RESULT_ARRAY;
- } else {
- FinderPatternInfo[] resultArray = new FinderPatternInfo[result.size()];
- for (int i = 0; i < result.size(); i++) {
- resultArray[i] = (FinderPatternInfo) result.elementAt(i);
- }
- return resultArray;
- }
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/QRCodeWriter.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/QRCodeWriter.java
deleted file mode 100644
index fff4f5d1e..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/QRCodeWriter.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright 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.qrcode;
-
-import com.google.zxing.BarcodeFormat;
-import com.google.zxing.EncodeHintType;
-import com.google.zxing.Writer;
-import com.google.zxing.WriterException;
-import com.google.zxing.common.BitMatrix;
-import com.google.zxing.qrcode.encoder.ByteMatrix;
-import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
-import com.google.zxing.qrcode.encoder.Encoder;
-import com.google.zxing.qrcode.encoder.QRCode;
-
-import java.util.Hashtable;
-
-/**
- * This object renders a QR Code as a BitMatrix 2D array of greyscale values.
- *
- * @author dswitkin@google.com (Daniel Switkin)
- */
-public final class QRCodeWriter implements Writer {
-
- private static final int QUIET_ZONE_SIZE = 0; // patched for Bitcoin Wallet
-
- public BitMatrix encode(String contents, BarcodeFormat format, int width, int height)
- throws WriterException {
-
- return encode(contents, format, width, height, null);
- }
-
- public BitMatrix encode(String contents, BarcodeFormat format, int width, int height,
- Hashtable hints) throws WriterException {
-
- if (contents == null || contents.length() == 0) {
- throw new IllegalArgumentException("Found empty contents");
- }
-
- if (format != BarcodeFormat.QR_CODE) {
- throw new IllegalArgumentException("Can only encode QR_CODE, but got " + format);
- }
-
- if (width < 0 || height < 0) {
- throw new IllegalArgumentException("Requested dimensions are too small: " + width + 'x' +
- height);
- }
-
- ErrorCorrectionLevel errorCorrectionLevel = ErrorCorrectionLevel.L;
- if (hints != null) {
- ErrorCorrectionLevel requestedECLevel = (ErrorCorrectionLevel) hints.get(EncodeHintType.ERROR_CORRECTION);
- if (requestedECLevel != null) {
- errorCorrectionLevel = requestedECLevel;
- }
- }
-
- QRCode code = new QRCode();
- Encoder.encode(contents, errorCorrectionLevel, hints, code);
- return renderResult(code, width, height);
- }
-
- // Note that the input matrix uses 0 == white, 1 == black, while the output matrix uses
- // 0 == black, 255 == white (i.e. an 8 bit greyscale bitmap).
- private static BitMatrix renderResult(QRCode code, int width, int height) {
- ByteMatrix input = code.getMatrix();
- int inputWidth = input.getWidth();
- int inputHeight = input.getHeight();
- int qrWidth = inputWidth + (QUIET_ZONE_SIZE << 1);
- int qrHeight = inputHeight + (QUIET_ZONE_SIZE << 1);
- int outputWidth = Math.max(width, qrWidth);
- int outputHeight = Math.max(height, qrHeight);
-
- int multiple = Math.min(outputWidth / qrWidth, outputHeight / qrHeight);
- // Padding includes both the quiet zone and the extra white pixels to accommodate the requested
- // dimensions. For example, if input is 25x25 the QR will be 33x33 including the quiet zone.
- // If the requested size is 200x160, the multiple will be 4, for a QR of 132x132. These will
- // handle all the padding from 100x100 (the actual QR) up to 200x160.
- int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
- int topPadding = (outputHeight - (inputHeight * multiple)) / 2;
-
- BitMatrix output = new BitMatrix(outputWidth, outputHeight);
-
- for (int inputY = 0, outputY = topPadding; inputY < inputHeight; inputY++, outputY += multiple) {
- // Write the contents of this row of the barcode
- for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) {
- if (input.get(inputX, inputY) == 1) {
- output.setRegion(outputX, outputY, multiple, multiple);
- }
- }
- }
-
- return output;
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/BitMatrixParser.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/BitMatrixParser.java
deleted file mode 100644
index 9d131a554..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/BitMatrixParser.java
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright 2007 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.qrcode.decoder;
-
-import com.google.zxing.FormatException;
-import com.google.zxing.common.BitMatrix;
-
-/**
- * @author Sean Owen
- */
-final class BitMatrixParser {
-
- private final BitMatrix bitMatrix;
- private Version parsedVersion;
- private FormatInformation parsedFormatInfo;
-
- /**
- * @param bitMatrix {@link BitMatrix} to parse
- * @throws FormatException if dimension is not >= 21 and 1 mod 4
- */
- BitMatrixParser(BitMatrix bitMatrix) throws FormatException {
- int dimension = bitMatrix.getHeight();
- if (dimension < 21 || (dimension & 0x03) != 1) {
- throw FormatException.getFormatInstance();
- }
- this.bitMatrix = bitMatrix;
- }
-
- /**
- *
Reads format information from one of its two locations within the QR Code.
- *
- * @return {@link FormatInformation} encapsulating the QR Code's format info
- * @throws FormatException if both format information locations cannot be parsed as
- * the valid encoding of format information
- */
- FormatInformation readFormatInformation() throws FormatException {
-
- if (parsedFormatInfo != null) {
- return parsedFormatInfo;
- }
-
- // Read top-left format info bits
- int formatInfoBits1 = 0;
- for (int i = 0; i < 6; i++) {
- formatInfoBits1 = copyBit(i, 8, formatInfoBits1);
- }
- // .. and skip a bit in the timing pattern ...
- formatInfoBits1 = copyBit(7, 8, formatInfoBits1);
- formatInfoBits1 = copyBit(8, 8, formatInfoBits1);
- formatInfoBits1 = copyBit(8, 7, formatInfoBits1);
- // .. and skip a bit in the timing pattern ...
- for (int j = 5; j >= 0; j--) {
- formatInfoBits1 = copyBit(8, j, formatInfoBits1);
- }
-
- // Read the top-right/bottom-left pattern too
- int dimension = bitMatrix.getHeight();
- int formatInfoBits2 = 0;
- int jMin = dimension - 7;
- for (int j = dimension - 1; j >= jMin; j--) {
- formatInfoBits2 = copyBit(8, j, formatInfoBits2);
- }
- for (int i = dimension - 8; i < dimension; i++) {
- formatInfoBits2 = copyBit(i, 8, formatInfoBits2);
- }
-
- parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits1, formatInfoBits2);
- if (parsedFormatInfo != null) {
- return parsedFormatInfo;
- }
- throw FormatException.getFormatInstance();
- }
-
- /**
- *
Reads version information from one of its two locations within the QR Code.
- *
- * @return {@link Version} encapsulating the QR Code's version
- * @throws FormatException if both version information locations cannot be parsed as
- * the valid encoding of version information
- */
- Version readVersion() throws FormatException {
-
- if (parsedVersion != null) {
- return parsedVersion;
- }
-
- int dimension = bitMatrix.getHeight();
-
- int provisionalVersion = (dimension - 17) >> 2;
- if (provisionalVersion <= 6) {
- return Version.getVersionForNumber(provisionalVersion);
- }
-
- // Read top-right version info: 3 wide by 6 tall
- int versionBits = 0;
- int ijMin = dimension - 11;
- for (int j = 5; j >= 0; j--) {
- for (int i = dimension - 9; i >= ijMin; i--) {
- versionBits = copyBit(i, j, versionBits);
- }
- }
-
- parsedVersion = Version.decodeVersionInformation(versionBits);
- if (parsedVersion != null && parsedVersion.getDimensionForVersion() == dimension) {
- return parsedVersion;
- }
-
- // Hmm, failed. Try bottom left: 6 wide by 3 tall
- versionBits = 0;
- for (int i = 5; i >= 0; i--) {
- for (int j = dimension - 9; j >= ijMin; j--) {
- versionBits = copyBit(i, j, versionBits);
- }
- }
-
- parsedVersion = Version.decodeVersionInformation(versionBits);
- if (parsedVersion != null && parsedVersion.getDimensionForVersion() == dimension) {
- return parsedVersion;
- }
- throw FormatException.getFormatInstance();
- }
-
- private int copyBit(int i, int j, int versionBits) {
- return bitMatrix.get(i, j) ? (versionBits << 1) | 0x1 : versionBits << 1;
- }
-
- /**
- *
Reads the bits in the {@link BitMatrix} representing the finder pattern in the
- * correct order in order to reconstitute the codewords bytes contained within the
- * QR Code.
- *
- * @return bytes encoded within the QR Code
- * @throws FormatException if the exact number of bytes expected is not read
- */
- byte[] readCodewords() throws FormatException {
-
- FormatInformation formatInfo = readFormatInformation();
- Version version = readVersion();
-
- // Get the data mask for the format used in this QR Code. This will exclude
- // some bits from reading as we wind through the bit matrix.
- DataMask dataMask = DataMask.forReference((int) formatInfo.getDataMask());
- int dimension = bitMatrix.getHeight();
- dataMask.unmaskBitMatrix(bitMatrix, dimension);
-
- BitMatrix functionPattern = version.buildFunctionPattern();
-
- boolean readingUp = true;
- byte[] result = new byte[version.getTotalCodewords()];
- int resultOffset = 0;
- int currentByte = 0;
- int bitsRead = 0;
- // Read columns in pairs, from right to left
- for (int j = dimension - 1; j > 0; j -= 2) {
- if (j == 6) {
- // Skip whole column with vertical alignment pattern;
- // saves time and makes the other code proceed more cleanly
- j--;
- }
- // Read alternatingly from bottom to top then top to bottom
- for (int count = 0; count < dimension; count++) {
- int i = readingUp ? dimension - 1 - count : count;
- for (int col = 0; col < 2; col++) {
- // Ignore bits covered by the function pattern
- if (!functionPattern.get(j - col, i)) {
- // Read a bit
- bitsRead++;
- currentByte <<= 1;
- if (bitMatrix.get(j - col, i)) {
- currentByte |= 1;
- }
- // If we've made a whole byte, save it off
- if (bitsRead == 8) {
- result[resultOffset++] = (byte) currentByte;
- bitsRead = 0;
- currentByte = 0;
- }
- }
- }
- }
- readingUp ^= true; // readingUp = !readingUp; // switch directions
- }
- if (resultOffset != version.getTotalCodewords()) {
- throw FormatException.getFormatInstance();
- }
- return result;
- }
-
-}
\ No newline at end of file
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/DataBlock.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/DataBlock.java
deleted file mode 100644
index 12959d9c1..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/DataBlock.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright 2007 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.qrcode.decoder;
-
-/**
- *
Encapsulates a block of data within a QR Code. QR Codes may split their data into
- * multiple blocks, each of which is a unit of data and error-correction codewords. Each
- * is represented by an instance of this class.
- *
- * @author Sean Owen
- */
-final class DataBlock {
-
- private final int numDataCodewords;
- private final byte[] codewords;
-
- private DataBlock(int numDataCodewords, byte[] codewords) {
- this.numDataCodewords = numDataCodewords;
- this.codewords = codewords;
- }
-
- /**
- *
When QR Codes use multiple data blocks, they are actually interleaved.
- * That is, the first byte of data block 1 to n is written, then the second bytes, and so on. This
- * method will separate the data into original blocks.
- *
- * @param rawCodewords bytes as read directly from the QR Code
- * @param version version of the QR Code
- * @param ecLevel error-correction level of the QR Code
- * @return DataBlocks containing original bytes, "de-interleaved" from representation in the
- * QR Code
- */
- static DataBlock[] getDataBlocks(byte[] rawCodewords,
- Version version,
- ErrorCorrectionLevel ecLevel) {
-
- if (rawCodewords.length != version.getTotalCodewords()) {
- throw new IllegalArgumentException();
- }
-
- // Figure out the number and size of data blocks used by this version and
- // error correction level
- Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel);
-
- // First count the total number of data blocks
- int totalBlocks = 0;
- Version.ECB[] ecBlockArray = ecBlocks.getECBlocks();
- for (int i = 0; i < ecBlockArray.length; i++) {
- totalBlocks += ecBlockArray[i].getCount();
- }
-
- // Now establish DataBlocks of the appropriate size and number of data codewords
- DataBlock[] result = new DataBlock[totalBlocks];
- int numResultBlocks = 0;
- for (int j = 0; j < ecBlockArray.length; j++) {
- Version.ECB ecBlock = ecBlockArray[j];
- for (int i = 0; i < ecBlock.getCount(); i++) {
- int numDataCodewords = ecBlock.getDataCodewords();
- int numBlockCodewords = ecBlocks.getECCodewordsPerBlock() + numDataCodewords;
- result[numResultBlocks++] = new DataBlock(numDataCodewords, new byte[numBlockCodewords]);
- }
- }
-
- // All blocks have the same amount of data, except that the last n
- // (where n may be 0) have 1 more byte. Figure out where these start.
- int shorterBlocksTotalCodewords = result[0].codewords.length;
- int longerBlocksStartAt = result.length - 1;
- while (longerBlocksStartAt >= 0) {
- int numCodewords = result[longerBlocksStartAt].codewords.length;
- if (numCodewords == shorterBlocksTotalCodewords) {
- break;
- }
- longerBlocksStartAt--;
- }
- longerBlocksStartAt++;
-
- int shorterBlocksNumDataCodewords = shorterBlocksTotalCodewords - ecBlocks.getECCodewordsPerBlock();
- // The last elements of result may be 1 element longer;
- // first fill out as many elements as all of them have
- int rawCodewordsOffset = 0;
- for (int i = 0; i < shorterBlocksNumDataCodewords; i++) {
- for (int j = 0; j < numResultBlocks; j++) {
- result[j].codewords[i] = rawCodewords[rawCodewordsOffset++];
- }
- }
- // Fill out the last data block in the longer ones
- for (int j = longerBlocksStartAt; j < numResultBlocks; j++) {
- result[j].codewords[shorterBlocksNumDataCodewords] = rawCodewords[rawCodewordsOffset++];
- }
- // Now add in error correction blocks
- int max = result[0].codewords.length;
- for (int i = shorterBlocksNumDataCodewords; i < max; i++) {
- for (int j = 0; j < numResultBlocks; j++) {
- int iOffset = j < longerBlocksStartAt ? i : i + 1;
- result[j].codewords[iOffset] = rawCodewords[rawCodewordsOffset++];
- }
- }
- return result;
- }
-
- int getNumDataCodewords() {
- return numDataCodewords;
- }
-
- byte[] getCodewords() {
- return codewords;
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/DataMask.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/DataMask.java
deleted file mode 100644
index d29dbd47f..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/DataMask.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright 2007 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.qrcode.decoder;
-
-import com.google.zxing.common.BitMatrix;
-
-/**
- *
Encapsulates data masks for the data bits in a QR code, per ISO 18004:2006 6.8. Implementations
- * of this class can un-mask a raw BitMatrix. For simplicity, they will unmask the entire BitMatrix,
- * including areas used for finder patterns, timing patterns, etc. These areas should be unused
- * after the point they are unmasked anyway.
- *
- *
Note that the diagram in section 6.8.1 is misleading since it indicates that i is column position
- * and j is row position. In fact, as the text says, i is row position and j is column position.
- *
- * @author Sean Owen
- */
-abstract class DataMask {
-
- /**
- * See ISO 18004:2006 6.8.1
- */
- private static final DataMask[] DATA_MASKS = {
- new DataMask000(),
- new DataMask001(),
- new DataMask010(),
- new DataMask011(),
- new DataMask100(),
- new DataMask101(),
- new DataMask110(),
- new DataMask111(),
- };
-
- private DataMask() {
- }
-
- /**
- *
Implementations of this method reverse the data masking process applied to a QR Code and
- * make its bits ready to read.
- *
- * @param bits representation of QR Code bits
- * @param dimension dimension of QR Code, represented by bits, being unmasked
- */
- final void unmaskBitMatrix(BitMatrix bits, int dimension) {
- for (int i = 0; i < dimension; i++) {
- for (int j = 0; j < dimension; j++) {
- if (isMasked(i, j)) {
- bits.flip(j, i);
- }
- }
- }
- }
-
- abstract boolean isMasked(int i, int j);
-
- /**
- * @param reference a value between 0 and 7 indicating one of the eight possible
- * data mask patterns a QR Code may use
- * @return DataMask encapsulating the data mask pattern
- */
- static DataMask forReference(int reference) {
- if (reference < 0 || reference > 7) {
- throw new IllegalArgumentException();
- }
- return DATA_MASKS[reference];
- }
-
- /**
- * 000: mask bits for which (x + y) mod 2 == 0
- */
- private static class DataMask000 extends DataMask {
- boolean isMasked(int i, int j) {
- return ((i + j) & 0x01) == 0;
- }
- }
-
- /**
- * 001: mask bits for which x mod 2 == 0
- */
- private static class DataMask001 extends DataMask {
- boolean isMasked(int i, int j) {
- return (i & 0x01) == 0;
- }
- }
-
- /**
- * 010: mask bits for which y mod 3 == 0
- */
- private static class DataMask010 extends DataMask {
- boolean isMasked(int i, int j) {
- return j % 3 == 0;
- }
- }
-
- /**
- * 011: mask bits for which (x + y) mod 3 == 0
- */
- private static class DataMask011 extends DataMask {
- boolean isMasked(int i, int j) {
- return (i + j) % 3 == 0;
- }
- }
-
- /**
- * 100: mask bits for which (x/2 + y/3) mod 2 == 0
- */
- private static class DataMask100 extends DataMask {
- boolean isMasked(int i, int j) {
- return (((i >>> 1) + (j /3)) & 0x01) == 0;
- }
- }
-
- /**
- * 101: mask bits for which xy mod 2 + xy mod 3 == 0
- */
- private static class DataMask101 extends DataMask {
- boolean isMasked(int i, int j) {
- int temp = i * j;
- return (temp & 0x01) + (temp % 3) == 0;
- }
- }
-
- /**
- * 110: mask bits for which (xy mod 2 + xy mod 3) mod 2 == 0
- */
- private static class DataMask110 extends DataMask {
- boolean isMasked(int i, int j) {
- int temp = i * j;
- return (((temp & 0x01) + (temp % 3)) & 0x01) == 0;
- }
- }
-
- /**
- * 111: mask bits for which ((x+y)mod 2 + xy mod 3) mod 2 == 0
- */
- private static class DataMask111 extends DataMask {
- boolean isMasked(int i, int j) {
- return ((((i + j) & 0x01) + ((i * j) % 3)) & 0x01) == 0;
- }
- }
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java
deleted file mode 100644
index ff374ac50..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java
+++ /dev/null
@@ -1,322 +0,0 @@
-/*
- * Copyright 2007 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.qrcode.decoder;
-
-import com.google.zxing.FormatException;
-import com.google.zxing.common.BitSource;
-import com.google.zxing.common.CharacterSetECI;
-import com.google.zxing.common.DecoderResult;
-import com.google.zxing.common.StringUtils;
-
-import java.io.UnsupportedEncodingException;
-import java.util.Hashtable;
-import java.util.Vector;
-
-/**
- *
QR Codes can encode text as bits in one of several modes, and can use multiple modes
- * in one QR Code. This class decodes the bits back into text.
- *
- *
See ISO 18004:2006, 6.4.3 - 6.4.7
- *
- * @author Sean Owen
- */
-final class DecodedBitStreamParser {
-
- /**
- * See ISO 18004:2006, 6.4.4 Table 5
- */
- private static final char[] ALPHANUMERIC_CHARS = {
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',
- 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
- 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
- ' ', '$', '%', '*', '+', '-', '.', '/', ':'
- };
- private static final int GB2312_SUBSET = 1;
-
- private DecodedBitStreamParser() {
- }
-
- static DecoderResult decode(byte[] bytes, Version version, ErrorCorrectionLevel ecLevel, Hashtable hints)
- throws FormatException {
- BitSource bits = new BitSource(bytes);
- StringBuffer result = new StringBuffer(50);
- CharacterSetECI currentCharacterSetECI = null;
- boolean fc1InEffect = false;
- Vector byteSegments = new Vector(1);
- Mode mode;
- do {
- // While still another segment to read...
- if (bits.available() < 4) {
- // OK, assume we're done. Really, a TERMINATOR mode should have been recorded here
- mode = Mode.TERMINATOR;
- } else {
- try {
- mode = Mode.forBits(bits.readBits(4)); // mode is encoded by 4 bits
- } catch (IllegalArgumentException iae) {
- throw FormatException.getFormatInstance();
- }
- }
- if (!mode.equals(Mode.TERMINATOR)) {
- if (mode.equals(Mode.FNC1_FIRST_POSITION) || mode.equals(Mode.FNC1_SECOND_POSITION)) {
- // We do little with FNC1 except alter the parsed result a bit according to the spec
- fc1InEffect = true;
- } else if (mode.equals(Mode.STRUCTURED_APPEND)) {
- // not really supported; all we do is ignore it
- // Read next 8 bits (symbol sequence #) and 8 bits (parity data), then continue
- bits.readBits(16);
- } else if (mode.equals(Mode.ECI)) {
- // Count doesn't apply to ECI
- int value = parseECIValue(bits);
- currentCharacterSetECI = CharacterSetECI.getCharacterSetECIByValue(value);
- if (currentCharacterSetECI == null) {
- throw FormatException.getFormatInstance();
- }
- } else {
- // First handle Hanzi mode which does not start with character count
- if (mode.equals(Mode.HANZI)) {
- //chinese mode contains a sub set indicator right after mode indicator
- int subset = bits.readBits(4);
- int countHanzi = bits.readBits(mode.getCharacterCountBits(version));
- if (subset == GB2312_SUBSET) {
- decodeHanziSegment(bits, result, countHanzi);
- }
- } else {
- // "Normal" QR code modes:
- // How many characters will follow, encoded in this mode?
- int count = bits.readBits(mode.getCharacterCountBits(version));
- if (mode.equals(Mode.NUMERIC)) {
- decodeNumericSegment(bits, result, count);
- } else if (mode.equals(Mode.ALPHANUMERIC)) {
- decodeAlphanumericSegment(bits, result, count, fc1InEffect);
- } else if (mode.equals(Mode.BYTE)) {
- decodeByteSegment(bits, result, count, currentCharacterSetECI, byteSegments, hints);
- } else if (mode.equals(Mode.KANJI)) {
- decodeKanjiSegment(bits, result, count);
- } else {
- throw FormatException.getFormatInstance();
- }
- }
- }
- }
- } while (!mode.equals(Mode.TERMINATOR));
-
- return new DecoderResult(bytes,
- result.toString(),
- byteSegments.isEmpty() ? null : byteSegments,
- ecLevel == null ? null : ecLevel.toString());
- }
-
- /**
- * See specification GBT 18284-2000
- */
- private static void decodeHanziSegment(BitSource bits,
- StringBuffer result,
- int count) throws FormatException {
- // Don't crash trying to read more bits than we have available.
- if (count * 13 > bits.available()) {
- throw FormatException.getFormatInstance();
- }
-
- // Each character will require 2 bytes. Read the characters as 2-byte pairs
- // and decode as GB2312 afterwards
- byte[] buffer = new byte[2 * count];
- int offset = 0;
- while (count > 0) {
- // Each 13 bits encodes a 2-byte character
- int twoBytes = bits.readBits(13);
- int assembledTwoBytes = ((twoBytes / 0x060) << 8) | (twoBytes % 0x060);
- if (assembledTwoBytes < 0x003BF) {
- // In the 0xA1A1 to 0xAAFE range
- assembledTwoBytes += 0x0A1A1;
- } else {
- // In the 0xB0A1 to 0xFAFE range
- assembledTwoBytes += 0x0A6A1;
- }
- buffer[offset] = (byte) ((assembledTwoBytes >> 8) & 0xFF);
- buffer[offset + 1] = (byte) (assembledTwoBytes & 0xFF);
- offset += 2;
- count--;
- }
-
- try {
- result.append(new String(buffer, StringUtils.GB2312));
- } catch (UnsupportedEncodingException uee) {
- throw FormatException.getFormatInstance();
- }
- }
-
- private static void decodeKanjiSegment(BitSource bits,
- StringBuffer result,
- int count) throws FormatException {
- // Don't crash trying to read more bits than we have available.
- if (count * 13 > bits.available()) {
- throw FormatException.getFormatInstance();
- }
-
- // Each character will require 2 bytes. Read the characters as 2-byte pairs
- // and decode as Shift_JIS afterwards
- byte[] buffer = new byte[2 * count];
- int offset = 0;
- while (count > 0) {
- // Each 13 bits encodes a 2-byte character
- int twoBytes = bits.readBits(13);
- int assembledTwoBytes = ((twoBytes / 0x0C0) << 8) | (twoBytes % 0x0C0);
- if (assembledTwoBytes < 0x01F00) {
- // In the 0x8140 to 0x9FFC range
- assembledTwoBytes += 0x08140;
- } else {
- // In the 0xE040 to 0xEBBF range
- assembledTwoBytes += 0x0C140;
- }
- buffer[offset] = (byte) (assembledTwoBytes >> 8);
- buffer[offset + 1] = (byte) assembledTwoBytes;
- offset += 2;
- count--;
- }
- // Shift_JIS may not be supported in some environments:
- try {
- result.append(new String(buffer, StringUtils.SHIFT_JIS));
- } catch (UnsupportedEncodingException uee) {
- throw FormatException.getFormatInstance();
- }
- }
-
- private static void decodeByteSegment(BitSource bits,
- StringBuffer result,
- int count,
- CharacterSetECI currentCharacterSetECI,
- Vector byteSegments,
- Hashtable hints) throws FormatException {
- // Don't crash trying to read more bits than we have available.
- if (count << 3 > bits.available()) {
- throw FormatException.getFormatInstance();
- }
-
- byte[] readBytes = new byte[count];
- for (int i = 0; i < count; i++) {
- readBytes[i] = (byte) bits.readBits(8);
- }
- String encoding;
- if (currentCharacterSetECI == null) {
- // The spec isn't clear on this mode; see
- // section 6.4.5: t does not say which encoding to assuming
- // upon decoding. I have seen ISO-8859-1 used as well as
- // Shift_JIS -- without anything like an ECI designator to
- // give a hint.
- encoding = StringUtils.guessEncoding(readBytes, hints);
- } else {
- encoding = currentCharacterSetECI.getEncodingName();
- }
- try {
- result.append(new String(readBytes, encoding));
- } catch (UnsupportedEncodingException uce) {
- throw FormatException.getFormatInstance();
- }
- byteSegments.addElement(readBytes);
- }
-
- private static char toAlphaNumericChar(int value) throws FormatException {
- if (value >= ALPHANUMERIC_CHARS.length) {
- throw FormatException.getFormatInstance();
- }
- return ALPHANUMERIC_CHARS[value];
- }
-
- private static void decodeAlphanumericSegment(BitSource bits,
- StringBuffer result,
- int count,
- boolean fc1InEffect) throws FormatException {
- // Read two characters at a time
- int start = result.length();
- while (count > 1) {
- int nextTwoCharsBits = bits.readBits(11);
- result.append(toAlphaNumericChar(nextTwoCharsBits / 45));
- result.append(toAlphaNumericChar(nextTwoCharsBits % 45));
- count -= 2;
- }
- if (count == 1) {
- // special case: one character left
- result.append(toAlphaNumericChar(bits.readBits(6)));
- }
- // See section 6.4.8.1, 6.4.8.2
- if (fc1InEffect) {
- // We need to massage the result a bit if in an FNC1 mode:
- for (int i = start; i < result.length(); i++) {
- if (result.charAt(i) == '%') {
- if (i < result.length() - 1 && result.charAt(i + 1) == '%') {
- // %% is rendered as %
- result.deleteCharAt(i + 1);
- } else {
- // In alpha mode, % should be converted to FNC1 separator 0x1D
- result.setCharAt(i, (char) 0x1D);
- }
- }
- }
- }
- }
-
- private static void decodeNumericSegment(BitSource bits,
- StringBuffer result,
- int count) throws FormatException {
- // Read three digits at a time
- while (count >= 3) {
- // Each 10 bits encodes three digits
- int threeDigitsBits = bits.readBits(10);
- if (threeDigitsBits >= 1000) {
- throw FormatException.getFormatInstance();
- }
- result.append(toAlphaNumericChar(threeDigitsBits / 100));
- result.append(toAlphaNumericChar((threeDigitsBits / 10) % 10));
- result.append(toAlphaNumericChar(threeDigitsBits % 10));
- count -= 3;
- }
- if (count == 2) {
- // Two digits left over to read, encoded in 7 bits
- int twoDigitsBits = bits.readBits(7);
- if (twoDigitsBits >= 100) {
- throw FormatException.getFormatInstance();
- }
- result.append(toAlphaNumericChar(twoDigitsBits / 10));
- result.append(toAlphaNumericChar(twoDigitsBits % 10));
- } else if (count == 1) {
- // One digit left over to read
- int digitBits = bits.readBits(4);
- if (digitBits >= 10) {
- throw FormatException.getFormatInstance();
- }
- result.append(toAlphaNumericChar(digitBits));
- }
- }
-
- private static int parseECIValue(BitSource bits) {
- int firstByte = bits.readBits(8);
- if ((firstByte & 0x80) == 0) {
- // just one byte
- return firstByte & 0x7F;
- } else if ((firstByte & 0xC0) == 0x80) {
- // two bytes
- int secondByte = bits.readBits(8);
- return ((firstByte & 0x3F) << 8) | secondByte;
- } else if ((firstByte & 0xE0) == 0xC0) {
- // three bytes
- int secondThirdBytes = bits.readBits(16);
- return ((firstByte & 0x1F) << 16) | secondThirdBytes;
- }
- throw new IllegalArgumentException("Bad ECI bits starting with byte " + firstByte);
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/ErrorCorrectionLevel.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/ErrorCorrectionLevel.java
deleted file mode 100644
index e8d6c2589..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/ErrorCorrectionLevel.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright 2007 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.qrcode.decoder;
-
-/**
- *
See ISO 18004:2006, 6.5.1. This enum encapsulates the four error correction levels
- * defined by the QR code standard.
- *
- * @author Sean Owen
- */
-public final class ErrorCorrectionLevel {
-
- // No, we can't use an enum here. J2ME doesn't support it.
-
- /**
- * L = ~7% correction
- */
- public static final ErrorCorrectionLevel L = new ErrorCorrectionLevel(0, 0x01, "L");
- /**
- * M = ~15% correction
- */
- public static final ErrorCorrectionLevel M = new ErrorCorrectionLevel(1, 0x00, "M");
- /**
- * Q = ~25% correction
- */
- public static final ErrorCorrectionLevel Q = new ErrorCorrectionLevel(2, 0x03, "Q");
- /**
- * H = ~30% correction
- */
- public static final ErrorCorrectionLevel H = new ErrorCorrectionLevel(3, 0x02, "H");
-
- private static final ErrorCorrectionLevel[] FOR_BITS = {M, L, H, Q};
-
- private final int ordinal;
- private final int bits;
- private final String name;
-
- private ErrorCorrectionLevel(int ordinal, int bits, String name) {
- this.ordinal = ordinal;
- this.bits = bits;
- this.name = name;
- }
-
- public int ordinal() {
- return ordinal;
- }
-
- public int getBits() {
- return bits;
- }
-
- public String getName() {
- return name;
- }
-
- public String toString() {
- return name;
- }
-
- /**
- * @param bits int containing the two bits encoding a QR Code's error correction level
- * @return ErrorCorrectionLevel representing the encoded error correction level
- */
- public static ErrorCorrectionLevel forBits(int bits) {
- if (bits < 0 || bits >= FOR_BITS.length) {
- throw new IllegalArgumentException();
- }
- return FOR_BITS[bits];
- }
-
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/FormatInformation.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/FormatInformation.java
deleted file mode 100644
index 1b76b0de5..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/FormatInformation.java
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright 2007 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.qrcode.decoder;
-
-/**
- *
Encapsulates a QR Code's format information, including the data mask used and
- * error correction level.
- *
- * @author Sean Owen
- * @see DataMask
- * @see ErrorCorrectionLevel
- */
-final class FormatInformation {
-
- private static final int FORMAT_INFO_MASK_QR = 0x5412;
-
- /**
- * See ISO 18004:2006, Annex C, Table C.1
- */
- private static final int[][] FORMAT_INFO_DECODE_LOOKUP = {
- {0x5412, 0x00},
- {0x5125, 0x01},
- {0x5E7C, 0x02},
- {0x5B4B, 0x03},
- {0x45F9, 0x04},
- {0x40CE, 0x05},
- {0x4F97, 0x06},
- {0x4AA0, 0x07},
- {0x77C4, 0x08},
- {0x72F3, 0x09},
- {0x7DAA, 0x0A},
- {0x789D, 0x0B},
- {0x662F, 0x0C},
- {0x6318, 0x0D},
- {0x6C41, 0x0E},
- {0x6976, 0x0F},
- {0x1689, 0x10},
- {0x13BE, 0x11},
- {0x1CE7, 0x12},
- {0x19D0, 0x13},
- {0x0762, 0x14},
- {0x0255, 0x15},
- {0x0D0C, 0x16},
- {0x083B, 0x17},
- {0x355F, 0x18},
- {0x3068, 0x19},
- {0x3F31, 0x1A},
- {0x3A06, 0x1B},
- {0x24B4, 0x1C},
- {0x2183, 0x1D},
- {0x2EDA, 0x1E},
- {0x2BED, 0x1F},
- };
-
- /**
- * Offset i holds the number of 1 bits in the binary representation of i
- */
- private static final int[] BITS_SET_IN_HALF_BYTE =
- {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4};
-
- private final ErrorCorrectionLevel errorCorrectionLevel;
- private final byte dataMask;
-
- private FormatInformation(int formatInfo) {
- // Bits 3,4
- errorCorrectionLevel = ErrorCorrectionLevel.forBits((formatInfo >> 3) & 0x03);
- // Bottom 3 bits
- dataMask = (byte) (formatInfo & 0x07);
- }
-
- static int numBitsDiffering(int a, int b) {
- a ^= b; // a now has a 1 bit exactly where its bit differs with b's
- // Count bits set quickly with a series of lookups:
- return BITS_SET_IN_HALF_BYTE[a & 0x0F] +
- BITS_SET_IN_HALF_BYTE[(a >>> 4 & 0x0F)] +
- BITS_SET_IN_HALF_BYTE[(a >>> 8 & 0x0F)] +
- BITS_SET_IN_HALF_BYTE[(a >>> 12 & 0x0F)] +
- BITS_SET_IN_HALF_BYTE[(a >>> 16 & 0x0F)] +
- BITS_SET_IN_HALF_BYTE[(a >>> 20 & 0x0F)] +
- BITS_SET_IN_HALF_BYTE[(a >>> 24 & 0x0F)] +
- BITS_SET_IN_HALF_BYTE[(a >>> 28 & 0x0F)];
- }
-
- /**
- * @param maskedFormatInfo1 format info indicator, with mask still applied
- * @param maskedFormatInfo2 second copy of same info; both are checked at the same time
- * to establish best match
- * @return information about the format it specifies, or null
- * if doesn't seem to match any known pattern
- */
- static FormatInformation decodeFormatInformation(int maskedFormatInfo1, int maskedFormatInfo2) {
- FormatInformation formatInfo = doDecodeFormatInformation(maskedFormatInfo1, maskedFormatInfo2);
- if (formatInfo != null) {
- return formatInfo;
- }
- // Should return null, but, some QR codes apparently
- // do not mask this info. Try again by actually masking the pattern
- // first
- return doDecodeFormatInformation(maskedFormatInfo1 ^ FORMAT_INFO_MASK_QR,
- maskedFormatInfo2 ^ FORMAT_INFO_MASK_QR);
- }
-
- private static FormatInformation doDecodeFormatInformation(int maskedFormatInfo1, int maskedFormatInfo2) {
- // Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing
- int bestDifference = Integer.MAX_VALUE;
- int bestFormatInfo = 0;
- for (int i = 0; i < FORMAT_INFO_DECODE_LOOKUP.length; i++) {
- int[] decodeInfo = FORMAT_INFO_DECODE_LOOKUP[i];
- int targetInfo = decodeInfo[0];
- if (targetInfo == maskedFormatInfo1 || targetInfo == maskedFormatInfo2) {
- // Found an exact match
- return new FormatInformation(decodeInfo[1]);
- }
- int bitsDifference = numBitsDiffering(maskedFormatInfo1, targetInfo);
- if (bitsDifference < bestDifference) {
- bestFormatInfo = decodeInfo[1];
- bestDifference = bitsDifference;
- }
- if (maskedFormatInfo1 != maskedFormatInfo2) {
- // also try the other option
- bitsDifference = numBitsDiffering(maskedFormatInfo2, targetInfo);
- if (bitsDifference < bestDifference) {
- bestFormatInfo = decodeInfo[1];
- bestDifference = bitsDifference;
- }
- }
- }
- // Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits
- // differing means we found a match
- if (bestDifference <= 3) {
- return new FormatInformation(bestFormatInfo);
- }
- return null;
- }
-
- ErrorCorrectionLevel getErrorCorrectionLevel() {
- return errorCorrectionLevel;
- }
-
- byte getDataMask() {
- return dataMask;
- }
-
- public int hashCode() {
- return (errorCorrectionLevel.ordinal() << 3) | (int) dataMask;
- }
-
- public boolean equals(Object o) {
- if (!(o instanceof FormatInformation)) {
- return false;
- }
- FormatInformation other = (FormatInformation) o;
- return this.errorCorrectionLevel == other.errorCorrectionLevel &&
- this.dataMask == other.dataMask;
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/Mode.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/Mode.java
deleted file mode 100644
index 3c66217d3..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/Mode.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright 2007 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.qrcode.decoder;
-
-/**
- *
See ISO 18004:2006, 6.4.1, Tables 2 and 3. This enum encapsulates the various modes in which
- * data can be encoded to bits in the QR code standard.
- *
- * @author Sean Owen
- */
-public final class Mode {
-
- // No, we can't use an enum here. J2ME doesn't support it.
-
- public static final Mode TERMINATOR = new Mode(new int[]{0, 0, 0}, 0x00, "TERMINATOR"); // Not really a mode...
- public static final Mode NUMERIC = new Mode(new int[]{10, 12, 14}, 0x01, "NUMERIC");
- public static final Mode ALPHANUMERIC = new Mode(new int[]{9, 11, 13}, 0x02, "ALPHANUMERIC");
- public static final Mode STRUCTURED_APPEND = new Mode(new int[]{0, 0, 0}, 0x03, "STRUCTURED_APPEND"); // Not supported
- public static final Mode BYTE = new Mode(new int[]{8, 16, 16}, 0x04, "BYTE");
- public static final Mode ECI = new Mode(null, 0x07, "ECI"); // character counts don't apply
- public static final Mode KANJI = new Mode(new int[]{8, 10, 12}, 0x08, "KANJI");
- public static final Mode FNC1_FIRST_POSITION = new Mode(null, 0x05, "FNC1_FIRST_POSITION");
- public static final Mode FNC1_SECOND_POSITION = new Mode(null, 0x09, "FNC1_SECOND_POSITION");
- /** See GBT 18284-2000; "Hanzi" is a transliteration of this mode name. */
- public static final Mode HANZI = new Mode(new int[]{8, 10, 12}, 0x0D, "HANZI");
-
- private final int[] characterCountBitsForVersions;
- private final int bits;
- private final String name;
-
- private Mode(int[] characterCountBitsForVersions, int bits, String name) {
- this.characterCountBitsForVersions = characterCountBitsForVersions;
- this.bits = bits;
- this.name = name;
- }
-
- /**
- * @param bits four bits encoding a QR Code data mode
- * @return Mode encoded by these bits
- * @throws IllegalArgumentException if bits do not correspond to a known mode
- */
- public static Mode forBits(int bits) {
- switch (bits) {
- case 0x0:
- return TERMINATOR;
- case 0x1:
- return NUMERIC;
- case 0x2:
- return ALPHANUMERIC;
- case 0x3:
- return STRUCTURED_APPEND;
- case 0x4:
- return BYTE;
- case 0x5:
- return FNC1_FIRST_POSITION;
- case 0x7:
- return ECI;
- case 0x8:
- return KANJI;
- case 0x9:
- return FNC1_SECOND_POSITION;
- case 0xD:
- // 0xD is defined in GBT 18284-2000, may not be supported in foreign country
- return HANZI;
- default:
- throw new IllegalArgumentException();
- }
- }
-
- /**
- * @param version version in question
- * @return number of bits used, in this QR Code symbol {@link Version}, to encode the
- * count of characters that will follow encoded in this Mode
- */
- public int getCharacterCountBits(Version version) {
- if (characterCountBitsForVersions == null) {
- throw new IllegalArgumentException("Character count doesn't apply to this mode");
- }
- int number = version.getVersionNumber();
- int offset;
- if (number <= 9) {
- offset = 0;
- } else if (number <= 26) {
- offset = 1;
- } else {
- offset = 2;
- }
- return characterCountBitsForVersions[offset];
- }
-
- public int getBits() {
- return bits;
- }
-
- public String getName() {
- return name;
- }
-
- public String toString() {
- return name;
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/Version.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/Version.java
deleted file mode 100644
index ba795de42..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/Version.java
+++ /dev/null
@@ -1,586 +0,0 @@
-/*
- * Copyright 2007 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.qrcode.decoder;
-
-import com.google.zxing.FormatException;
-import com.google.zxing.common.BitMatrix;
-
-/**
- * See ISO 18004:2006 Annex D
- *
- * @author Sean Owen
- */
-public final class Version {
-
- /**
- * See ISO 18004:2006 Annex D.
- * Element i represents the raw version bits that specify version i + 7
- */
- private static final int[] VERSION_DECODE_INFO = {
- 0x07C94, 0x085BC, 0x09A99, 0x0A4D3, 0x0BBF6,
- 0x0C762, 0x0D847, 0x0E60D, 0x0F928, 0x10B78,
- 0x1145D, 0x12A17, 0x13532, 0x149A6, 0x15683,
- 0x168C9, 0x177EC, 0x18EC4, 0x191E1, 0x1AFAB,
- 0x1B08E, 0x1CC1A, 0x1D33F, 0x1ED75, 0x1F250,
- 0x209D5, 0x216F0, 0x228BA, 0x2379F, 0x24B0B,
- 0x2542E, 0x26A64, 0x27541, 0x28C69
- };
-
- private static final Version[] VERSIONS = buildVersions();
-
- private final int versionNumber;
- private final int[] alignmentPatternCenters;
- private final ECBlocks[] ecBlocks;
- private final int totalCodewords;
-
- private Version(int versionNumber,
- int[] alignmentPatternCenters,
- ECBlocks ecBlocks1,
- ECBlocks ecBlocks2,
- ECBlocks ecBlocks3,
- ECBlocks ecBlocks4) {
- this.versionNumber = versionNumber;
- this.alignmentPatternCenters = alignmentPatternCenters;
- this.ecBlocks = new ECBlocks[]{ecBlocks1, ecBlocks2, ecBlocks3, ecBlocks4};
- int total = 0;
- int ecCodewords = ecBlocks1.getECCodewordsPerBlock();
- ECB[] ecbArray = ecBlocks1.getECBlocks();
- for (int i = 0; i < ecbArray.length; i++) {
- ECB ecBlock = ecbArray[i];
- total += ecBlock.getCount() * (ecBlock.getDataCodewords() + ecCodewords);
- }
- this.totalCodewords = total;
- }
-
- public int getVersionNumber() {
- return versionNumber;
- }
-
- public int[] getAlignmentPatternCenters() {
- return alignmentPatternCenters;
- }
-
- public int getTotalCodewords() {
- return totalCodewords;
- }
-
- public int getDimensionForVersion() {
- return 17 + 4 * versionNumber;
- }
-
- public ECBlocks getECBlocksForLevel(ErrorCorrectionLevel ecLevel) {
- return ecBlocks[ecLevel.ordinal()];
- }
-
- /**
- *
Deduces version information purely from QR Code dimensions.
- *
- * @param dimension dimension in modules
- * @return Version for a QR Code of that dimension
- * @throws FormatException if dimension is not 1 mod 4
- */
- public static Version getProvisionalVersionForDimension(int dimension) throws FormatException {
- if (dimension % 4 != 1) {
- throw FormatException.getFormatInstance();
- }
- try {
- return getVersionForNumber((dimension - 17) >> 2);
- } catch (IllegalArgumentException iae) {
- throw FormatException.getFormatInstance();
- }
- }
-
- public static Version getVersionForNumber(int versionNumber) {
- if (versionNumber < 1 || versionNumber > 40) {
- throw new IllegalArgumentException();
- }
- return VERSIONS[versionNumber - 1];
- }
-
- static Version decodeVersionInformation(int versionBits) {
- int bestDifference = Integer.MAX_VALUE;
- int bestVersion = 0;
- for (int i = 0; i < VERSION_DECODE_INFO.length; i++) {
- int targetVersion = VERSION_DECODE_INFO[i];
- // Do the version info bits match exactly? done.
- if (targetVersion == versionBits) {
- return getVersionForNumber(i + 7);
- }
- // Otherwise see if this is the closest to a real version info bit string
- // we have seen so far
- int bitsDifference = FormatInformation.numBitsDiffering(versionBits, targetVersion);
- if (bitsDifference < bestDifference) {
- bestVersion = i + 7;
- bestDifference = bitsDifference;
- }
- }
- // We can tolerate up to 3 bits of error since no two version info codewords will
- // differ in less than 8 bits.
- if (bestDifference <= 3) {
- return getVersionForNumber(bestVersion);
- }
- // If we didn't find a close enough match, fail
- return null;
- }
-
- /**
- * See ISO 18004:2006 Annex E
- */
- BitMatrix buildFunctionPattern() {
- int dimension = getDimensionForVersion();
- BitMatrix bitMatrix = new BitMatrix(dimension);
-
- // Top left finder pattern + separator + format
- bitMatrix.setRegion(0, 0, 9, 9);
- // Top right finder pattern + separator + format
- bitMatrix.setRegion(dimension - 8, 0, 8, 9);
- // Bottom left finder pattern + separator + format
- bitMatrix.setRegion(0, dimension - 8, 9, 8);
-
- // Alignment patterns
- int max = alignmentPatternCenters.length;
- for (int x = 0; x < max; x++) {
- int i = alignmentPatternCenters[x] - 2;
- for (int y = 0; y < max; y++) {
- if ((x == 0 && (y == 0 || y == max - 1)) || (x == max - 1 && y == 0)) {
- // No alignment patterns near the three finder paterns
- continue;
- }
- bitMatrix.setRegion(alignmentPatternCenters[y] - 2, i, 5, 5);
- }
- }
-
- // Vertical timing pattern
- bitMatrix.setRegion(6, 9, 1, dimension - 17);
- // Horizontal timing pattern
- bitMatrix.setRegion(9, 6, dimension - 17, 1);
-
- if (versionNumber > 6) {
- // Version info, top right
- bitMatrix.setRegion(dimension - 11, 0, 3, 6);
- // Version info, bottom left
- bitMatrix.setRegion(0, dimension - 11, 6, 3);
- }
-
- return bitMatrix;
- }
-
- /**
- *
Encapsulates a set of error-correction blocks in one symbol version. Most versions will
- * use blocks of differing sizes within one version, so, this encapsulates the parameters for
- * each set of blocks. It also holds the number of error-correction codewords per block since it
- * will be the same across all blocks within one version.
- */
- public static final class ECBlocks {
- private final int ecCodewordsPerBlock;
- private final ECB[] ecBlocks;
-
- ECBlocks(int ecCodewordsPerBlock, ECB ecBlocks) {
- this.ecCodewordsPerBlock = ecCodewordsPerBlock;
- this.ecBlocks = new ECB[]{ecBlocks};
- }
-
- ECBlocks(int ecCodewordsPerBlock, ECB ecBlocks1, ECB ecBlocks2) {
- this.ecCodewordsPerBlock = ecCodewordsPerBlock;
- this.ecBlocks = new ECB[]{ecBlocks1, ecBlocks2};
- }
-
- public int getECCodewordsPerBlock() {
- return ecCodewordsPerBlock;
- }
-
- public int getNumBlocks() {
- int total = 0;
- for (int i = 0; i < ecBlocks.length; i++) {
- total += ecBlocks[i].getCount();
- }
- return total;
- }
-
- public int getTotalECCodewords() {
- return ecCodewordsPerBlock * getNumBlocks();
- }
-
- public ECB[] getECBlocks() {
- return ecBlocks;
- }
- }
-
- /**
- *
Encapsualtes the parameters for one error-correction block in one symbol version.
- * This includes the number of data codewords, and the number of times a block with these
- * parameters is used consecutively in the QR code version's format.
- */
- public static final class ECB {
- private final int count;
- private final int dataCodewords;
-
- ECB(int count, int dataCodewords) {
- this.count = count;
- this.dataCodewords = dataCodewords;
- }
-
- public int getCount() {
- return count;
- }
-
- public int getDataCodewords() {
- return dataCodewords;
- }
- }
-
- public String toString() {
- return String.valueOf(versionNumber);
- }
-
- /**
- * See ISO 18004:2006 6.5.1 Table 9
- */
- private static Version[] buildVersions() {
- return new Version[]{
- new Version(1, new int[]{},
- new ECBlocks(7, new ECB(1, 19)),
- new ECBlocks(10, new ECB(1, 16)),
- new ECBlocks(13, new ECB(1, 13)),
- new ECBlocks(17, new ECB(1, 9))),
- new Version(2, new int[]{6, 18},
- new ECBlocks(10, new ECB(1, 34)),
- new ECBlocks(16, new ECB(1, 28)),
- new ECBlocks(22, new ECB(1, 22)),
- new ECBlocks(28, new ECB(1, 16))),
- new Version(3, new int[]{6, 22},
- new ECBlocks(15, new ECB(1, 55)),
- new ECBlocks(26, new ECB(1, 44)),
- new ECBlocks(18, new ECB(2, 17)),
- new ECBlocks(22, new ECB(2, 13))),
- new Version(4, new int[]{6, 26},
- new ECBlocks(20, new ECB(1, 80)),
- new ECBlocks(18, new ECB(2, 32)),
- new ECBlocks(26, new ECB(2, 24)),
- new ECBlocks(16, new ECB(4, 9))),
- new Version(5, new int[]{6, 30},
- new ECBlocks(26, new ECB(1, 108)),
- new ECBlocks(24, new ECB(2, 43)),
- new ECBlocks(18, new ECB(2, 15),
- new ECB(2, 16)),
- new ECBlocks(22, new ECB(2, 11),
- new ECB(2, 12))),
- new Version(6, new int[]{6, 34},
- new ECBlocks(18, new ECB(2, 68)),
- new ECBlocks(16, new ECB(4, 27)),
- new ECBlocks(24, new ECB(4, 19)),
- new ECBlocks(28, new ECB(4, 15))),
- new Version(7, new int[]{6, 22, 38},
- new ECBlocks(20, new ECB(2, 78)),
- new ECBlocks(18, new ECB(4, 31)),
- new ECBlocks(18, new ECB(2, 14),
- new ECB(4, 15)),
- new ECBlocks(26, new ECB(4, 13),
- new ECB(1, 14))),
- new Version(8, new int[]{6, 24, 42},
- new ECBlocks(24, new ECB(2, 97)),
- new ECBlocks(22, new ECB(2, 38),
- new ECB(2, 39)),
- new ECBlocks(22, new ECB(4, 18),
- new ECB(2, 19)),
- new ECBlocks(26, new ECB(4, 14),
- new ECB(2, 15))),
- new Version(9, new int[]{6, 26, 46},
- new ECBlocks(30, new ECB(2, 116)),
- new ECBlocks(22, new ECB(3, 36),
- new ECB(2, 37)),
- new ECBlocks(20, new ECB(4, 16),
- new ECB(4, 17)),
- new ECBlocks(24, new ECB(4, 12),
- new ECB(4, 13))),
- new Version(10, new int[]{6, 28, 50},
- new ECBlocks(18, new ECB(2, 68),
- new ECB(2, 69)),
- new ECBlocks(26, new ECB(4, 43),
- new ECB(1, 44)),
- new ECBlocks(24, new ECB(6, 19),
- new ECB(2, 20)),
- new ECBlocks(28, new ECB(6, 15),
- new ECB(2, 16))),
- new Version(11, new int[]{6, 30, 54},
- new ECBlocks(20, new ECB(4, 81)),
- new ECBlocks(30, new ECB(1, 50),
- new ECB(4, 51)),
- new ECBlocks(28, new ECB(4, 22),
- new ECB(4, 23)),
- new ECBlocks(24, new ECB(3, 12),
- new ECB(8, 13))),
- new Version(12, new int[]{6, 32, 58},
- new ECBlocks(24, new ECB(2, 92),
- new ECB(2, 93)),
- new ECBlocks(22, new ECB(6, 36),
- new ECB(2, 37)),
- new ECBlocks(26, new ECB(4, 20),
- new ECB(6, 21)),
- new ECBlocks(28, new ECB(7, 14),
- new ECB(4, 15))),
- new Version(13, new int[]{6, 34, 62},
- new ECBlocks(26, new ECB(4, 107)),
- new ECBlocks(22, new ECB(8, 37),
- new ECB(1, 38)),
- new ECBlocks(24, new ECB(8, 20),
- new ECB(4, 21)),
- new ECBlocks(22, new ECB(12, 11),
- new ECB(4, 12))),
- new Version(14, new int[]{6, 26, 46, 66},
- new ECBlocks(30, new ECB(3, 115),
- new ECB(1, 116)),
- new ECBlocks(24, new ECB(4, 40),
- new ECB(5, 41)),
- new ECBlocks(20, new ECB(11, 16),
- new ECB(5, 17)),
- new ECBlocks(24, new ECB(11, 12),
- new ECB(5, 13))),
- new Version(15, new int[]{6, 26, 48, 70},
- new ECBlocks(22, new ECB(5, 87),
- new ECB(1, 88)),
- new ECBlocks(24, new ECB(5, 41),
- new ECB(5, 42)),
- new ECBlocks(30, new ECB(5, 24),
- new ECB(7, 25)),
- new ECBlocks(24, new ECB(11, 12),
- new ECB(7, 13))),
- new Version(16, new int[]{6, 26, 50, 74},
- new ECBlocks(24, new ECB(5, 98),
- new ECB(1, 99)),
- new ECBlocks(28, new ECB(7, 45),
- new ECB(3, 46)),
- new ECBlocks(24, new ECB(15, 19),
- new ECB(2, 20)),
- new ECBlocks(30, new ECB(3, 15),
- new ECB(13, 16))),
- new Version(17, new int[]{6, 30, 54, 78},
- new ECBlocks(28, new ECB(1, 107),
- new ECB(5, 108)),
- new ECBlocks(28, new ECB(10, 46),
- new ECB(1, 47)),
- new ECBlocks(28, new ECB(1, 22),
- new ECB(15, 23)),
- new ECBlocks(28, new ECB(2, 14),
- new ECB(17, 15))),
- new Version(18, new int[]{6, 30, 56, 82},
- new ECBlocks(30, new ECB(5, 120),
- new ECB(1, 121)),
- new ECBlocks(26, new ECB(9, 43),
- new ECB(4, 44)),
- new ECBlocks(28, new ECB(17, 22),
- new ECB(1, 23)),
- new ECBlocks(28, new ECB(2, 14),
- new ECB(19, 15))),
- new Version(19, new int[]{6, 30, 58, 86},
- new ECBlocks(28, new ECB(3, 113),
- new ECB(4, 114)),
- new ECBlocks(26, new ECB(3, 44),
- new ECB(11, 45)),
- new ECBlocks(26, new ECB(17, 21),
- new ECB(4, 22)),
- new ECBlocks(26, new ECB(9, 13),
- new ECB(16, 14))),
- new Version(20, new int[]{6, 34, 62, 90},
- new ECBlocks(28, new ECB(3, 107),
- new ECB(5, 108)),
- new ECBlocks(26, new ECB(3, 41),
- new ECB(13, 42)),
- new ECBlocks(30, new ECB(15, 24),
- new ECB(5, 25)),
- new ECBlocks(28, new ECB(15, 15),
- new ECB(10, 16))),
- new Version(21, new int[]{6, 28, 50, 72, 94},
- new ECBlocks(28, new ECB(4, 116),
- new ECB(4, 117)),
- new ECBlocks(26, new ECB(17, 42)),
- new ECBlocks(28, new ECB(17, 22),
- new ECB(6, 23)),
- new ECBlocks(30, new ECB(19, 16),
- new ECB(6, 17))),
- new Version(22, new int[]{6, 26, 50, 74, 98},
- new ECBlocks(28, new ECB(2, 111),
- new ECB(7, 112)),
- new ECBlocks(28, new ECB(17, 46)),
- new ECBlocks(30, new ECB(7, 24),
- new ECB(16, 25)),
- new ECBlocks(24, new ECB(34, 13))),
- new Version(23, new int[]{6, 30, 54, 78, 102},
- new ECBlocks(30, new ECB(4, 121),
- new ECB(5, 122)),
- new ECBlocks(28, new ECB(4, 47),
- new ECB(14, 48)),
- new ECBlocks(30, new ECB(11, 24),
- new ECB(14, 25)),
- new ECBlocks(30, new ECB(16, 15),
- new ECB(14, 16))),
- new Version(24, new int[]{6, 28, 54, 80, 106},
- new ECBlocks(30, new ECB(6, 117),
- new ECB(4, 118)),
- new ECBlocks(28, new ECB(6, 45),
- new ECB(14, 46)),
- new ECBlocks(30, new ECB(11, 24),
- new ECB(16, 25)),
- new ECBlocks(30, new ECB(30, 16),
- new ECB(2, 17))),
- new Version(25, new int[]{6, 32, 58, 84, 110},
- new ECBlocks(26, new ECB(8, 106),
- new ECB(4, 107)),
- new ECBlocks(28, new ECB(8, 47),
- new ECB(13, 48)),
- new ECBlocks(30, new ECB(7, 24),
- new ECB(22, 25)),
- new ECBlocks(30, new ECB(22, 15),
- new ECB(13, 16))),
- new Version(26, new int[]{6, 30, 58, 86, 114},
- new ECBlocks(28, new ECB(10, 114),
- new ECB(2, 115)),
- new ECBlocks(28, new ECB(19, 46),
- new ECB(4, 47)),
- new ECBlocks(28, new ECB(28, 22),
- new ECB(6, 23)),
- new ECBlocks(30, new ECB(33, 16),
- new ECB(4, 17))),
- new Version(27, new int[]{6, 34, 62, 90, 118},
- new ECBlocks(30, new ECB(8, 122),
- new ECB(4, 123)),
- new ECBlocks(28, new ECB(22, 45),
- new ECB(3, 46)),
- new ECBlocks(30, new ECB(8, 23),
- new ECB(26, 24)),
- new ECBlocks(30, new ECB(12, 15),
- new ECB(28, 16))),
- new Version(28, new int[]{6, 26, 50, 74, 98, 122},
- new ECBlocks(30, new ECB(3, 117),
- new ECB(10, 118)),
- new ECBlocks(28, new ECB(3, 45),
- new ECB(23, 46)),
- new ECBlocks(30, new ECB(4, 24),
- new ECB(31, 25)),
- new ECBlocks(30, new ECB(11, 15),
- new ECB(31, 16))),
- new Version(29, new int[]{6, 30, 54, 78, 102, 126},
- new ECBlocks(30, new ECB(7, 116),
- new ECB(7, 117)),
- new ECBlocks(28, new ECB(21, 45),
- new ECB(7, 46)),
- new ECBlocks(30, new ECB(1, 23),
- new ECB(37, 24)),
- new ECBlocks(30, new ECB(19, 15),
- new ECB(26, 16))),
- new Version(30, new int[]{6, 26, 52, 78, 104, 130},
- new ECBlocks(30, new ECB(5, 115),
- new ECB(10, 116)),
- new ECBlocks(28, new ECB(19, 47),
- new ECB(10, 48)),
- new ECBlocks(30, new ECB(15, 24),
- new ECB(25, 25)),
- new ECBlocks(30, new ECB(23, 15),
- new ECB(25, 16))),
- new Version(31, new int[]{6, 30, 56, 82, 108, 134},
- new ECBlocks(30, new ECB(13, 115),
- new ECB(3, 116)),
- new ECBlocks(28, new ECB(2, 46),
- new ECB(29, 47)),
- new ECBlocks(30, new ECB(42, 24),
- new ECB(1, 25)),
- new ECBlocks(30, new ECB(23, 15),
- new ECB(28, 16))),
- new Version(32, new int[]{6, 34, 60, 86, 112, 138},
- new ECBlocks(30, new ECB(17, 115)),
- new ECBlocks(28, new ECB(10, 46),
- new ECB(23, 47)),
- new ECBlocks(30, new ECB(10, 24),
- new ECB(35, 25)),
- new ECBlocks(30, new ECB(19, 15),
- new ECB(35, 16))),
- new Version(33, new int[]{6, 30, 58, 86, 114, 142},
- new ECBlocks(30, new ECB(17, 115),
- new ECB(1, 116)),
- new ECBlocks(28, new ECB(14, 46),
- new ECB(21, 47)),
- new ECBlocks(30, new ECB(29, 24),
- new ECB(19, 25)),
- new ECBlocks(30, new ECB(11, 15),
- new ECB(46, 16))),
- new Version(34, new int[]{6, 34, 62, 90, 118, 146},
- new ECBlocks(30, new ECB(13, 115),
- new ECB(6, 116)),
- new ECBlocks(28, new ECB(14, 46),
- new ECB(23, 47)),
- new ECBlocks(30, new ECB(44, 24),
- new ECB(7, 25)),
- new ECBlocks(30, new ECB(59, 16),
- new ECB(1, 17))),
- new Version(35, new int[]{6, 30, 54, 78, 102, 126, 150},
- new ECBlocks(30, new ECB(12, 121),
- new ECB(7, 122)),
- new ECBlocks(28, new ECB(12, 47),
- new ECB(26, 48)),
- new ECBlocks(30, new ECB(39, 24),
- new ECB(14, 25)),
- new ECBlocks(30, new ECB(22, 15),
- new ECB(41, 16))),
- new Version(36, new int[]{6, 24, 50, 76, 102, 128, 154},
- new ECBlocks(30, new ECB(6, 121),
- new ECB(14, 122)),
- new ECBlocks(28, new ECB(6, 47),
- new ECB(34, 48)),
- new ECBlocks(30, new ECB(46, 24),
- new ECB(10, 25)),
- new ECBlocks(30, new ECB(2, 15),
- new ECB(64, 16))),
- new Version(37, new int[]{6, 28, 54, 80, 106, 132, 158},
- new ECBlocks(30, new ECB(17, 122),
- new ECB(4, 123)),
- new ECBlocks(28, new ECB(29, 46),
- new ECB(14, 47)),
- new ECBlocks(30, new ECB(49, 24),
- new ECB(10, 25)),
- new ECBlocks(30, new ECB(24, 15),
- new ECB(46, 16))),
- new Version(38, new int[]{6, 32, 58, 84, 110, 136, 162},
- new ECBlocks(30, new ECB(4, 122),
- new ECB(18, 123)),
- new ECBlocks(28, new ECB(13, 46),
- new ECB(32, 47)),
- new ECBlocks(30, new ECB(48, 24),
- new ECB(14, 25)),
- new ECBlocks(30, new ECB(42, 15),
- new ECB(32, 16))),
- new Version(39, new int[]{6, 26, 54, 82, 110, 138, 166},
- new ECBlocks(30, new ECB(20, 117),
- new ECB(4, 118)),
- new ECBlocks(28, new ECB(40, 47),
- new ECB(7, 48)),
- new ECBlocks(30, new ECB(43, 24),
- new ECB(22, 25)),
- new ECBlocks(30, new ECB(10, 15),
- new ECB(67, 16))),
- new Version(40, new int[]{6, 30, 58, 86, 114, 142, 170},
- new ECBlocks(30, new ECB(19, 118),
- new ECB(6, 119)),
- new ECBlocks(28, new ECB(18, 47),
- new ECB(31, 48)),
- new ECBlocks(30, new ECB(34, 24),
- new ECB(34, 25)),
- new ECBlocks(30, new ECB(20, 15),
- new ECB(61, 16)))
- };
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/AlignmentPattern.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/AlignmentPattern.java
deleted file mode 100644
index 6fc1a2c88..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/AlignmentPattern.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2007 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.qrcode.detector;
-
-import com.google.zxing.ResultPoint;
-
-/**
- *
Encapsulates an alignment pattern, which are the smaller square patterns found in
- * all but the simplest QR Codes.
Determines if this alignment pattern "about equals" an alignment pattern at the stated
- * position and size -- meaning, it is at nearly the same center with nearly the same size.
- */
- boolean aboutEquals(float moduleSize, float i, float j) {
- if (Math.abs(i - getY()) <= moduleSize && Math.abs(j - getX()) <= moduleSize) {
- float moduleSizeDiff = Math.abs(moduleSize - estimatedModuleSize);
- return moduleSizeDiff <= 1.0f || moduleSizeDiff / estimatedModuleSize <= 1.0f;
- }
- return false;
- }
-
-}
\ No newline at end of file
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/AlignmentPatternFinder.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/AlignmentPatternFinder.java
deleted file mode 100644
index 3aadf284f..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/AlignmentPatternFinder.java
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- * Copyright 2007 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.qrcode.detector;
-
-import com.google.zxing.NotFoundException;
-import com.google.zxing.ResultPoint;
-import com.google.zxing.ResultPointCallback;
-import com.google.zxing.common.BitMatrix;
-
-import java.util.Vector;
-
-/**
- *
This class attempts to find alignment patterns in a QR Code. Alignment patterns look like finder
- * patterns but are smaller and appear at regular intervals throughout the image.
- *
- *
At the moment this only looks for the bottom-right alignment pattern.
- *
- *
This is mostly a simplified copy of {@link FinderPatternFinder}. It is copied,
- * pasted and stripped down here for maximum performance but does unfortunately duplicate
- * some code.
- *
- *
This class is thread-safe but not reentrant. Each thread must allocate its own object.
- *
- * @author Sean Owen
- */
-final class AlignmentPatternFinder {
-
- private final BitMatrix image;
- private final Vector possibleCenters;
- private final int startX;
- private final int startY;
- private final int width;
- private final int height;
- private final float moduleSize;
- private final int[] crossCheckStateCount;
- private final ResultPointCallback resultPointCallback;
-
- /**
- *
Creates a finder that will look in a portion of the whole image.
- *
- * @param image image to search
- * @param startX left column from which to start searching
- * @param startY top row from which to start searching
- * @param width width of region to search
- * @param height height of region to search
- * @param moduleSize estimated module size so far
- */
- AlignmentPatternFinder(BitMatrix image,
- int startX,
- int startY,
- int width,
- int height,
- float moduleSize,
- ResultPointCallback resultPointCallback) {
- this.image = image;
- this.possibleCenters = new Vector(5);
- this.startX = startX;
- this.startY = startY;
- this.width = width;
- this.height = height;
- this.moduleSize = moduleSize;
- this.crossCheckStateCount = new int[3];
- this.resultPointCallback = resultPointCallback;
- }
-
- /**
- *
This method attempts to find the bottom-right alignment pattern in the image. It is a bit messy since
- * it's pretty performance-critical and so is written to be fast foremost.
- *
- * @return {@link AlignmentPattern} if found
- * @throws NotFoundException if not found
- */
- AlignmentPattern find() throws NotFoundException {
- int startX = this.startX;
- int height = this.height;
- int maxJ = startX + width;
- int middleI = startY + (height >> 1);
- // We are looking for black/white/black modules in 1:1:1 ratio;
- // this tracks the number of black/white/black modules seen so far
- int[] stateCount = new int[3];
- for (int iGen = 0; iGen < height; iGen++) {
- // Search from middle outwards
- int i = middleI + ((iGen & 0x01) == 0 ? (iGen + 1) >> 1 : -((iGen + 1) >> 1));
- stateCount[0] = 0;
- stateCount[1] = 0;
- stateCount[2] = 0;
- int j = startX;
- // Burn off leading white pixels before anything else; if we start in the middle of
- // a white run, it doesn't make sense to count its length, since we don't know if the
- // white run continued to the left of the start point
- while (j < maxJ && !image.get(j, i)) {
- j++;
- }
- int currentState = 0;
- while (j < maxJ) {
- if (image.get(j, i)) {
- // Black pixel
- if (currentState == 1) { // Counting black pixels
- stateCount[currentState]++;
- } else { // Counting white pixels
- if (currentState == 2) { // A winner?
- if (foundPatternCross(stateCount)) { // Yes
- AlignmentPattern confirmed = handlePossibleCenter(stateCount, i, j);
- if (confirmed != null) {
- return confirmed;
- }
- }
- stateCount[0] = stateCount[2];
- stateCount[1] = 1;
- stateCount[2] = 0;
- currentState = 1;
- } else {
- stateCount[++currentState]++;
- }
- }
- } else { // White pixel
- if (currentState == 1) { // Counting black pixels
- currentState++;
- }
- stateCount[currentState]++;
- }
- j++;
- }
- if (foundPatternCross(stateCount)) {
- AlignmentPattern confirmed = handlePossibleCenter(stateCount, i, maxJ);
- if (confirmed != null) {
- return confirmed;
- }
- }
-
- }
-
- // Hmm, nothing we saw was observed and confirmed twice. If we had
- // any guess at all, return it.
- if (!possibleCenters.isEmpty()) {
- return (AlignmentPattern) possibleCenters.elementAt(0);
- }
-
- throw NotFoundException.getNotFoundInstance();
- }
-
- /**
- * Given a count of black/white/black pixels just seen and an end position,
- * figures the location of the center of this black/white/black run.
- */
- private static float centerFromEnd(int[] stateCount, int end) {
- return (float) (end - stateCount[2]) - stateCount[1] / 2.0f;
- }
-
- /**
- * @param stateCount count of black/white/black pixels just read
- * @return true iff the proportions of the counts is close enough to the 1/1/1 ratios
- * used by alignment patterns to be considered a match
- */
- private boolean foundPatternCross(int[] stateCount) {
- float moduleSize = this.moduleSize;
- float maxVariance = moduleSize / 2.0f;
- for (int i = 0; i < 3; i++) {
- if (Math.abs(moduleSize - stateCount[i]) >= maxVariance) {
- return false;
- }
- }
- return true;
- }
-
- /**
- *
After a horizontal scan finds a potential alignment pattern, this method
- * "cross-checks" by scanning down vertically through the center of the possible
- * alignment pattern to see if the same proportion is detected.
- *
- * @param startI row where an alignment pattern was detected
- * @param centerJ center of the section that appears to cross an alignment pattern
- * @param maxCount maximum reasonable number of modules that should be
- * observed in any reading state, based on the results of the horizontal scan
- * @return vertical center of alignment pattern, or {@link Float#NaN} if not found
- */
- private float crossCheckVertical(int startI, int centerJ, int maxCount,
- int originalStateCountTotal) {
- BitMatrix image = this.image;
-
- int maxI = image.getHeight();
- int[] stateCount = crossCheckStateCount;
- stateCount[0] = 0;
- stateCount[1] = 0;
- stateCount[2] = 0;
-
- // Start counting up from center
- int i = startI;
- while (i >= 0 && image.get(centerJ, i) && stateCount[1] <= maxCount) {
- stateCount[1]++;
- i--;
- }
- // If already too many modules in this state or ran off the edge:
- if (i < 0 || stateCount[1] > maxCount) {
- return Float.NaN;
- }
- while (i >= 0 && !image.get(centerJ, i) && stateCount[0] <= maxCount) {
- stateCount[0]++;
- i--;
- }
- if (stateCount[0] > maxCount) {
- return Float.NaN;
- }
-
- // Now also count down from center
- i = startI + 1;
- while (i < maxI && image.get(centerJ, i) && stateCount[1] <= maxCount) {
- stateCount[1]++;
- i++;
- }
- if (i == maxI || stateCount[1] > maxCount) {
- return Float.NaN;
- }
- while (i < maxI && !image.get(centerJ, i) && stateCount[2] <= maxCount) {
- stateCount[2]++;
- i++;
- }
- if (stateCount[2] > maxCount) {
- return Float.NaN;
- }
-
- int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2];
- if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal) {
- return Float.NaN;
- }
-
- return foundPatternCross(stateCount) ? centerFromEnd(stateCount, i) : Float.NaN;
- }
-
- /**
- *
This is called when a horizontal scan finds a possible alignment pattern. It will
- * cross check with a vertical scan, and if successful, will see if this pattern had been
- * found on a previous horizontal scan. If so, we consider it confirmed and conclude we have
- * found the alignment pattern.
- *
- * @param stateCount reading state module counts from horizontal scan
- * @param i row where alignment pattern may be found
- * @param j end of possible alignment pattern in row
- * @return {@link AlignmentPattern} if we have found the same pattern twice, or null if not
- */
- private AlignmentPattern handlePossibleCenter(int[] stateCount, int i, int j) {
- int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2];
- float centerJ = centerFromEnd(stateCount, j);
- float centerI = crossCheckVertical(i, (int) centerJ, 2 * stateCount[1], stateCountTotal);
- if (!Float.isNaN(centerI)) {
- float estimatedModuleSize = (float) (stateCount[0] + stateCount[1] + stateCount[2]) / 3.0f;
- int max = possibleCenters.size();
- for (int index = 0; index < max; index++) {
- AlignmentPattern center = (AlignmentPattern) possibleCenters.elementAt(index);
- // Look for about the same center and module size:
- if (center.aboutEquals(estimatedModuleSize, centerI, centerJ)) {
- return new AlignmentPattern(centerJ, centerI, estimatedModuleSize);
- }
- }
- // Hadn't found this before; save it
- ResultPoint point = new AlignmentPattern(centerJ, centerI, estimatedModuleSize);
- possibleCenters.addElement(point);
- if (resultPointCallback != null) {
- resultPointCallback.foundPossibleResultPoint(point);
- }
- }
- return null;
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/Detector.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/Detector.java
deleted file mode 100644
index 724d39d59..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/Detector.java
+++ /dev/null
@@ -1,406 +0,0 @@
-/*
- * Copyright 2007 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.qrcode.detector;
-
-import com.google.zxing.DecodeHintType;
-import com.google.zxing.FormatException;
-import com.google.zxing.NotFoundException;
-import com.google.zxing.ResultPoint;
-import com.google.zxing.ResultPointCallback;
-import com.google.zxing.common.BitMatrix;
-import com.google.zxing.common.DetectorResult;
-import com.google.zxing.common.GridSampler;
-import com.google.zxing.common.PerspectiveTransform;
-import com.google.zxing.qrcode.decoder.Version;
-
-import java.util.Hashtable;
-
-/**
- *
Encapsulates logic that can detect a QR Code in an image, even if the QR Code
- * is rotated or skewed, or partially obscured.
- *
- * @return {@link DetectorResult} encapsulating results of detecting a QR Code
- * @throws NotFoundException if no QR Code can be found
- */
- public DetectorResult detect() throws NotFoundException, FormatException {
- return detect(null);
- }
-
- /**
- *
Detects a QR Code in an image, simply.
- *
- * @param hints optional hints to detector
- * @return {@link NotFoundException} encapsulating results of detecting a QR Code
- * @throws NotFoundException if QR Code cannot be found
- * @throws FormatException if a QR Code cannot be decoded
- */
- public DetectorResult detect(Hashtable hints) throws NotFoundException, FormatException {
-
- resultPointCallback = hints == null ? null :
- (ResultPointCallback) hints.get(DecodeHintType.NEED_RESULT_POINT_CALLBACK);
-
- FinderPatternFinder finder = new FinderPatternFinder(image, resultPointCallback);
- FinderPatternInfo info = finder.find(hints);
-
- return processFinderPatternInfo(info);
- }
-
- protected DetectorResult processFinderPatternInfo(FinderPatternInfo info)
- throws NotFoundException, FormatException {
-
- FinderPattern topLeft = info.getTopLeft();
- FinderPattern topRight = info.getTopRight();
- FinderPattern bottomLeft = info.getBottomLeft();
-
- float moduleSize = calculateModuleSize(topLeft, topRight, bottomLeft);
- if (moduleSize < 1.0f) {
- throw NotFoundException.getNotFoundInstance();
- }
- int dimension = computeDimension(topLeft, topRight, bottomLeft, moduleSize);
- Version provisionalVersion = Version.getProvisionalVersionForDimension(dimension);
- int modulesBetweenFPCenters = provisionalVersion.getDimensionForVersion() - 7;
-
- AlignmentPattern alignmentPattern = null;
- // Anything above version 1 has an alignment pattern
- if (provisionalVersion.getAlignmentPatternCenters().length > 0) {
-
- // Guess where a "bottom right" finder pattern would have been
- float bottomRightX = topRight.getX() - topLeft.getX() + bottomLeft.getX();
- float bottomRightY = topRight.getY() - topLeft.getY() + bottomLeft.getY();
-
- // Estimate that alignment pattern is closer by 3 modules
- // from "bottom right" to known top left location
- float correctionToTopLeft = 1.0f - 3.0f / (float) modulesBetweenFPCenters;
- int estAlignmentX = (int) (topLeft.getX() + correctionToTopLeft * (bottomRightX - topLeft.getX()));
- int estAlignmentY = (int) (topLeft.getY() + correctionToTopLeft * (bottomRightY - topLeft.getY()));
-
- // Kind of arbitrary -- expand search radius before giving up
- for (int i = 4; i <= 16; i <<= 1) {
- try {
- alignmentPattern = findAlignmentInRegion(moduleSize,
- estAlignmentX,
- estAlignmentY,
- (float) i);
- break;
- } catch (NotFoundException re) {
- // try next round
- }
- }
- // If we didn't find alignment pattern... well try anyway without it
- }
-
- PerspectiveTransform transform =
- createTransform(topLeft, topRight, bottomLeft, alignmentPattern, dimension);
-
- BitMatrix bits = sampleGrid(image, transform, dimension);
-
- ResultPoint[] points;
- if (alignmentPattern == null) {
- points = new ResultPoint[]{bottomLeft, topLeft, topRight};
- } else {
- points = new ResultPoint[]{bottomLeft, topLeft, topRight, alignmentPattern};
- }
- return new DetectorResult(bits, points);
- }
-
- public static PerspectiveTransform createTransform(ResultPoint topLeft,
- ResultPoint topRight,
- ResultPoint bottomLeft,
- ResultPoint alignmentPattern,
- int dimension) {
- float dimMinusThree = (float) dimension - 3.5f;
- float bottomRightX;
- float bottomRightY;
- float sourceBottomRightX;
- float sourceBottomRightY;
- if (alignmentPattern != null) {
- bottomRightX = alignmentPattern.getX();
- bottomRightY = alignmentPattern.getY();
- sourceBottomRightX = sourceBottomRightY = dimMinusThree - 3.0f;
- } else {
- // Don't have an alignment pattern, just make up the bottom-right point
- bottomRightX = (topRight.getX() - topLeft.getX()) + bottomLeft.getX();
- bottomRightY = (topRight.getY() - topLeft.getY()) + bottomLeft.getY();
- sourceBottomRightX = sourceBottomRightY = dimMinusThree;
- }
-
- return PerspectiveTransform.quadrilateralToQuadrilateral(
- 3.5f,
- 3.5f,
- dimMinusThree,
- 3.5f,
- sourceBottomRightX,
- sourceBottomRightY,
- 3.5f,
- dimMinusThree,
- topLeft.getX(),
- topLeft.getY(),
- topRight.getX(),
- topRight.getY(),
- bottomRightX,
- bottomRightY,
- bottomLeft.getX(),
- bottomLeft.getY());
- }
-
- private static BitMatrix sampleGrid(BitMatrix image,
- PerspectiveTransform transform,
- int dimension) throws NotFoundException {
-
- GridSampler sampler = GridSampler.getInstance();
- return sampler.sampleGrid(image, dimension, dimension, transform);
- }
-
- /**
- *
Computes the dimension (number of modules on a size) of the QR Code based on the position
- * of the finder patterns and estimated module size.
Estimates module size based on two finder patterns -- it uses
- * {@link #sizeOfBlackWhiteBlackRunBothWays(int, int, int, int)} to figure the
- * width of each, measuring along the axis between their centers.
- */
- private float calculateModuleSizeOneWay(ResultPoint pattern, ResultPoint otherPattern) {
- float moduleSizeEst1 = sizeOfBlackWhiteBlackRunBothWays((int) pattern.getX(),
- (int) pattern.getY(),
- (int) otherPattern.getX(),
- (int) otherPattern.getY());
- float moduleSizeEst2 = sizeOfBlackWhiteBlackRunBothWays((int) otherPattern.getX(),
- (int) otherPattern.getY(),
- (int) pattern.getX(),
- (int) pattern.getY());
- if (Float.isNaN(moduleSizeEst1)) {
- return moduleSizeEst2 / 7.0f;
- }
- if (Float.isNaN(moduleSizeEst2)) {
- return moduleSizeEst1 / 7.0f;
- }
- // Average them, and divide by 7 since we've counted the width of 3 black modules,
- // and 1 white and 1 black module on either side. Ergo, divide sum by 14.
- return (moduleSizeEst1 + moduleSizeEst2) / 14.0f;
- }
-
- /**
- * See {@link #sizeOfBlackWhiteBlackRun(int, int, int, int)}; computes the total width of
- * a finder pattern by looking for a black-white-black run from the center in the direction
- * of another point (another finder pattern center), and in the opposite direction too.
This method traces a line from a point in the image, in the direction towards another point.
- * It begins in a black region, and keeps going until it finds white, then black, then white again.
- * It reports the distance from the start to this point.
- *
- *
This is used when figuring out how wide a finder pattern is, when the finder pattern
- * may be skewed or rotated.
- */
- private float sizeOfBlackWhiteBlackRun(int fromX, int fromY, int toX, int toY) {
- // Mild variant of Bresenham's algorithm;
- // see http://en.wikipedia.org/wiki/Bresenham's_line_algorithm
- boolean steep = Math.abs(toY - fromY) > Math.abs(toX - fromX);
- if (steep) {
- int temp = fromX;
- fromX = fromY;
- fromY = temp;
- temp = toX;
- toX = toY;
- toY = temp;
- }
-
- int dx = Math.abs(toX - fromX);
- int dy = Math.abs(toY - fromY);
- int error = -dx >> 1;
- int xstep = fromX < toX ? 1 : -1;
- int ystep = fromY < toY ? 1 : -1;
-
- // In black pixels, looking for white, first or second time.
- int state = 0;
- for (int x = fromX, y = fromY; x != toX; x += xstep) {
- int realX = steep ? y : x;
- int realY = steep ? x : y;
-
- // In white pixels, looking for black.
- // FIXME(dswitkin): This method seems to assume square images, which can cause these calls to
- // BitMatrix.get() to throw ArrayIndexOutOfBoundsException.
- if (state == 1) {
- if (image.get(realX, realY)) {
- state++;
- }
- } else {
- if (!image.get(realX, realY)) {
- state++;
- }
- }
-
- // Found black, white, black, and stumbled back onto white, so we're done.
- if (state == 3) {
- int diffX = x - fromX;
- int diffY = y - fromY;
- if (xstep < 0) {
- diffX++;
- }
- return (float) Math.sqrt((double) (diffX * diffX + diffY * diffY));
- }
- error += dy;
- if (error > 0) {
- if (y == toY) {
- break;
- }
- y += ystep;
- error -= dx;
- }
- }
- int diffX = toX - fromX;
- int diffY = toY - fromY;
- return (float) Math.sqrt((double) (diffX * diffX + diffY * diffY));
- }
-
- /**
- *
Attempts to locate an alignment pattern in a limited region of the image, which is
- * guessed to contain it. This method uses {@link AlignmentPattern}.
- *
- * @param overallEstModuleSize estimated module size so far
- * @param estAlignmentX x coordinate of center of area probably containing alignment pattern
- * @param estAlignmentY y coordinate of above
- * @param allowanceFactor number of pixels in all directions to search from the center
- * @return {@link AlignmentPattern} if found, or null otherwise
- * @throws NotFoundException if an unexpected error occurs during detection
- */
- protected AlignmentPattern findAlignmentInRegion(float overallEstModuleSize,
- int estAlignmentX,
- int estAlignmentY,
- float allowanceFactor)
- throws NotFoundException {
- // Look for an alignment pattern (3 modules in size) around where it
- // should be
- int allowance = (int) (allowanceFactor * overallEstModuleSize);
- int alignmentAreaLeftX = Math.max(0, estAlignmentX - allowance);
- int alignmentAreaRightX = Math.min(image.getWidth() - 1, estAlignmentX + allowance);
- if (alignmentAreaRightX - alignmentAreaLeftX < overallEstModuleSize * 3) {
- throw NotFoundException.getNotFoundInstance();
- }
-
- int alignmentAreaTopY = Math.max(0, estAlignmentY - allowance);
- int alignmentAreaBottomY = Math.min(image.getHeight() - 1, estAlignmentY + allowance);
- if (alignmentAreaBottomY - alignmentAreaTopY < overallEstModuleSize * 3) {
- throw NotFoundException.getNotFoundInstance();
- }
-
- AlignmentPatternFinder alignmentFinder =
- new AlignmentPatternFinder(
- image,
- alignmentAreaLeftX,
- alignmentAreaTopY,
- alignmentAreaRightX - alignmentAreaLeftX,
- alignmentAreaBottomY - alignmentAreaTopY,
- overallEstModuleSize,
- resultPointCallback);
- return alignmentFinder.find();
- }
-
- /**
- * Ends up being a bit faster than Math.round(). This merely rounds its argument to the nearest int,
- * where x.5 rounds up.
- */
- private static int round(float d) {
- return (int) (d + 0.5f);
- }
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/FinderPattern.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/FinderPattern.java
deleted file mode 100644
index 7a9914d76..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/FinderPattern.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2007 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.qrcode.detector;
-
-import com.google.zxing.ResultPoint;
-
-/**
- *
Encapsulates a finder pattern, which are the three square patterns found in
- * the corners of QR Codes. It also encapsulates a count of similar finder patterns,
- * as a convenience to the finder's bookkeeping.
Determines if this finder pattern "about equals" a finder pattern at the stated
- * position and size -- meaning, it is at nearly the same center with nearly the same size.
- */
- boolean aboutEquals(float moduleSize, float i, float j) {
- if (Math.abs(i - getY()) <= moduleSize && Math.abs(j - getX()) <= moduleSize) {
- float moduleSizeDiff = Math.abs(moduleSize - estimatedModuleSize);
- return moduleSizeDiff <= 1.0f || moduleSizeDiff / estimatedModuleSize <= 1.0f;
- }
- return false;
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/FinderPatternFinder.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/FinderPatternFinder.java
deleted file mode 100644
index 01b3bde2a..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/FinderPatternFinder.java
+++ /dev/null
@@ -1,585 +0,0 @@
-/*
- * Copyright 2007 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.qrcode.detector;
-
-import com.google.zxing.DecodeHintType;
-import com.google.zxing.NotFoundException;
-import com.google.zxing.ResultPoint;
-import com.google.zxing.ResultPointCallback;
-import com.google.zxing.common.BitMatrix;
-import com.google.zxing.common.Collections;
-import com.google.zxing.common.Comparator;
-
-import java.util.Hashtable;
-import java.util.Vector;
-
-/**
- *
This class attempts to find finder patterns in a QR Code. Finder patterns are the square
- * markers at three corners of a QR Code.
- *
- *
This class is thread-safe but not reentrant. Each thread must allocate its own object.
- *
- * @author Sean Owen
- */
-public class FinderPatternFinder {
-
- private static final int CENTER_QUORUM = 2;
- protected static final int MIN_SKIP = 3; // 1 pixel/module times 3 modules/center
- protected static final int MAX_MODULES = 57; // support up to version 10 for mobile clients
- private static final int INTEGER_MATH_SHIFT = 8;
-
- private final BitMatrix image;
- private final Vector possibleCenters;
- private boolean hasSkipped;
- private final int[] crossCheckStateCount;
- private final ResultPointCallback resultPointCallback;
-
- /**
- *
Creates a finder that will search the image for three finder patterns.
- *
- * @param image image to search
- */
- public FinderPatternFinder(BitMatrix image) {
- this(image, null);
- }
-
- public FinderPatternFinder(BitMatrix image, ResultPointCallback resultPointCallback) {
- this.image = image;
- this.possibleCenters = new Vector();
- this.crossCheckStateCount = new int[5];
- this.resultPointCallback = resultPointCallback;
- }
-
- protected BitMatrix getImage() {
- return image;
- }
-
- protected Vector getPossibleCenters() {
- return possibleCenters;
- }
-
- FinderPatternInfo find(Hashtable hints) throws NotFoundException {
- boolean tryHarder = hints != null && hints.containsKey(DecodeHintType.TRY_HARDER);
- int maxI = image.getHeight();
- int maxJ = image.getWidth();
- // We are looking for black/white/black/white/black modules in
- // 1:1:3:1:1 ratio; this tracks the number of such modules seen so far
-
- // Let's assume that the maximum version QR Code we support takes up 1/4 the height of the
- // image, and then account for the center being 3 modules in size. This gives the smallest
- // number of pixels the center could be, so skip this often. When trying harder, look for all
- // QR versions regardless of how dense they are.
- int iSkip = (3 * maxI) / (4 * MAX_MODULES);
- if (iSkip < MIN_SKIP || tryHarder) {
- iSkip = MIN_SKIP;
- }
-
- boolean done = false;
- int[] stateCount = new int[5];
- for (int i = iSkip - 1; i < maxI && !done; i += iSkip) {
- // Get a row of black/white values
- stateCount[0] = 0;
- stateCount[1] = 0;
- stateCount[2] = 0;
- stateCount[3] = 0;
- stateCount[4] = 0;
- int currentState = 0;
- for (int j = 0; j < maxJ; j++) {
- if (image.get(j, i)) {
- // Black pixel
- if ((currentState & 1) == 1) { // Counting white pixels
- currentState++;
- }
- stateCount[currentState]++;
- } else { // White pixel
- if ((currentState & 1) == 0) { // Counting black pixels
- if (currentState == 4) { // A winner?
- if (foundPatternCross(stateCount)) { // Yes
- boolean confirmed = handlePossibleCenter(stateCount, i, j);
- if (confirmed) {
- // Start examining every other line. Checking each line turned out to be too
- // expensive and didn't improve performance.
- iSkip = 2;
- if (hasSkipped) {
- done = haveMultiplyConfirmedCenters();
- } else {
- int rowSkip = findRowSkip();
- if (rowSkip > stateCount[2]) {
- // Skip rows between row of lower confirmed center
- // and top of presumed third confirmed center
- // but back up a bit to get a full chance of detecting
- // it, entire width of center of finder pattern
-
- // Skip by rowSkip, but back off by stateCount[2] (size of last center
- // of pattern we saw) to be conservative, and also back off by iSkip which
- // is about to be re-added
- i += rowSkip - stateCount[2] - iSkip;
- j = maxJ - 1;
- }
- }
- } else {
- stateCount[0] = stateCount[2];
- stateCount[1] = stateCount[3];
- stateCount[2] = stateCount[4];
- stateCount[3] = 1;
- stateCount[4] = 0;
- currentState = 3;
- continue;
- }
- // Clear state to start looking again
- currentState = 0;
- stateCount[0] = 0;
- stateCount[1] = 0;
- stateCount[2] = 0;
- stateCount[3] = 0;
- stateCount[4] = 0;
- } else { // No, shift counts back by two
- stateCount[0] = stateCount[2];
- stateCount[1] = stateCount[3];
- stateCount[2] = stateCount[4];
- stateCount[3] = 1;
- stateCount[4] = 0;
- currentState = 3;
- }
- } else {
- stateCount[++currentState]++;
- }
- } else { // Counting white pixels
- stateCount[currentState]++;
- }
- }
- }
- if (foundPatternCross(stateCount)) {
- boolean confirmed = handlePossibleCenter(stateCount, i, maxJ);
- if (confirmed) {
- iSkip = stateCount[0];
- if (hasSkipped) {
- // Found a third one
- done = haveMultiplyConfirmedCenters();
- }
- }
- }
- }
-
- FinderPattern[] patternInfo = selectBestPatterns();
- ResultPoint.orderBestPatterns(patternInfo);
-
- return new FinderPatternInfo(patternInfo);
- }
-
- /**
- * Given a count of black/white/black/white/black pixels just seen and an end position,
- * figures the location of the center of this run.
- */
- private static float centerFromEnd(int[] stateCount, int end) {
- return (float) (end - stateCount[4] - stateCount[3]) - stateCount[2] / 2.0f;
- }
-
- /**
- * @param stateCount count of black/white/black/white/black pixels just read
- * @return true iff the proportions of the counts is close enough to the 1/1/3/1/1 ratios
- * used by finder patterns to be considered a match
- */
- protected static boolean foundPatternCross(int[] stateCount) {
- int totalModuleSize = 0;
- for (int i = 0; i < 5; i++) {
- int count = stateCount[i];
- if (count == 0) {
- return false;
- }
- totalModuleSize += count;
- }
- if (totalModuleSize < 7) {
- return false;
- }
- int moduleSize = (totalModuleSize << INTEGER_MATH_SHIFT) / 7;
- int maxVariance = moduleSize / 2;
- // Allow less than 50% variance from 1-1-3-1-1 proportions
- return Math.abs(moduleSize - (stateCount[0] << INTEGER_MATH_SHIFT)) < maxVariance &&
- Math.abs(moduleSize - (stateCount[1] << INTEGER_MATH_SHIFT)) < maxVariance &&
- Math.abs(3 * moduleSize - (stateCount[2] << INTEGER_MATH_SHIFT)) < 3 * maxVariance &&
- Math.abs(moduleSize - (stateCount[3] << INTEGER_MATH_SHIFT)) < maxVariance &&
- Math.abs(moduleSize - (stateCount[4] << INTEGER_MATH_SHIFT)) < maxVariance;
- }
-
- private int[] getCrossCheckStateCount() {
- crossCheckStateCount[0] = 0;
- crossCheckStateCount[1] = 0;
- crossCheckStateCount[2] = 0;
- crossCheckStateCount[3] = 0;
- crossCheckStateCount[4] = 0;
- return crossCheckStateCount;
- }
-
- /**
- *
After a horizontal scan finds a potential finder pattern, this method
- * "cross-checks" by scanning down vertically through the center of the possible
- * finder pattern to see if the same proportion is detected.
- *
- * @param startI row where a finder pattern was detected
- * @param centerJ center of the section that appears to cross a finder pattern
- * @param maxCount maximum reasonable number of modules that should be
- * observed in any reading state, based on the results of the horizontal scan
- * @return vertical center of finder pattern, or {@link Float#NaN} if not found
- */
- private float crossCheckVertical(int startI, int centerJ, int maxCount,
- int originalStateCountTotal) {
- BitMatrix image = this.image;
-
- int maxI = image.getHeight();
- int[] stateCount = getCrossCheckStateCount();
-
- // Start counting up from center
- int i = startI;
- while (i >= 0 && image.get(centerJ, i)) {
- stateCount[2]++;
- i--;
- }
- if (i < 0) {
- return Float.NaN;
- }
- while (i >= 0 && !image.get(centerJ, i) && stateCount[1] <= maxCount) {
- stateCount[1]++;
- i--;
- }
- // If already too many modules in this state or ran off the edge:
- if (i < 0 || stateCount[1] > maxCount) {
- return Float.NaN;
- }
- while (i >= 0 && image.get(centerJ, i) && stateCount[0] <= maxCount) {
- stateCount[0]++;
- i--;
- }
- if (stateCount[0] > maxCount) {
- return Float.NaN;
- }
-
- // Now also count down from center
- i = startI + 1;
- while (i < maxI && image.get(centerJ, i)) {
- stateCount[2]++;
- i++;
- }
- if (i == maxI) {
- return Float.NaN;
- }
- while (i < maxI && !image.get(centerJ, i) && stateCount[3] < maxCount) {
- stateCount[3]++;
- i++;
- }
- if (i == maxI || stateCount[3] >= maxCount) {
- return Float.NaN;
- }
- while (i < maxI && image.get(centerJ, i) && stateCount[4] < maxCount) {
- stateCount[4]++;
- i++;
- }
- if (stateCount[4] >= maxCount) {
- return Float.NaN;
- }
-
- // If we found a finder-pattern-like section, but its size is more than 40% different than
- // the original, assume it's a false positive
- int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] +
- stateCount[4];
- if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal) {
- return Float.NaN;
- }
-
- return foundPatternCross(stateCount) ? centerFromEnd(stateCount, i) : Float.NaN;
- }
-
- /**
- *
Like {@link #crossCheckVertical(int, int, int, int)}, and in fact is basically identical,
- * except it reads horizontally instead of vertically. This is used to cross-cross
- * check a vertical cross check and locate the real center of the alignment pattern.
- */
- private float crossCheckHorizontal(int startJ, int centerI, int maxCount,
- int originalStateCountTotal) {
- BitMatrix image = this.image;
-
- int maxJ = image.getWidth();
- int[] stateCount = getCrossCheckStateCount();
-
- int j = startJ;
- while (j >= 0 && image.get(j, centerI)) {
- stateCount[2]++;
- j--;
- }
- if (j < 0) {
- return Float.NaN;
- }
- while (j >= 0 && !image.get(j, centerI) && stateCount[1] <= maxCount) {
- stateCount[1]++;
- j--;
- }
- if (j < 0 || stateCount[1] > maxCount) {
- return Float.NaN;
- }
- while (j >= 0 && image.get(j, centerI) && stateCount[0] <= maxCount) {
- stateCount[0]++;
- j--;
- }
- if (stateCount[0] > maxCount) {
- return Float.NaN;
- }
-
- j = startJ + 1;
- while (j < maxJ && image.get(j, centerI)) {
- stateCount[2]++;
- j++;
- }
- if (j == maxJ) {
- return Float.NaN;
- }
- while (j < maxJ && !image.get(j, centerI) && stateCount[3] < maxCount) {
- stateCount[3]++;
- j++;
- }
- if (j == maxJ || stateCount[3] >= maxCount) {
- return Float.NaN;
- }
- while (j < maxJ && image.get(j, centerI) && stateCount[4] < maxCount) {
- stateCount[4]++;
- j++;
- }
- if (stateCount[4] >= maxCount) {
- return Float.NaN;
- }
-
- // If we found a finder-pattern-like section, but its size is significantly different than
- // the original, assume it's a false positive
- int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] +
- stateCount[4];
- if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= originalStateCountTotal) {
- return Float.NaN;
- }
-
- return foundPatternCross(stateCount) ? centerFromEnd(stateCount, j) : Float.NaN;
- }
-
- /**
- *
This is called when a horizontal scan finds a possible alignment pattern. It will
- * cross check with a vertical scan, and if successful, will, ah, cross-cross-check
- * with another horizontal scan. This is needed primarily to locate the real horizontal
- * center of the pattern in cases of extreme skew.
- *
- *
If that succeeds the finder pattern location is added to a list that tracks
- * the number of times each location has been nearly-matched as a finder pattern.
- * Each additional find is more evidence that the location is in fact a finder
- * pattern center
- *
- * @param stateCount reading state module counts from horizontal scan
- * @param i row where finder pattern may be found
- * @param j end of possible finder pattern in row
- * @return true if a finder pattern candidate was found this time
- */
- protected boolean handlePossibleCenter(int[] stateCount, int i, int j) {
- int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] +
- stateCount[4];
- float centerJ = centerFromEnd(stateCount, j);
- float centerI = crossCheckVertical(i, (int) centerJ, stateCount[2], stateCountTotal);
- if (!Float.isNaN(centerI)) {
- // Re-cross check
- centerJ = crossCheckHorizontal((int) centerJ, (int) centerI, stateCount[2], stateCountTotal);
- if (!Float.isNaN(centerJ)) {
- float estimatedModuleSize = (float) stateCountTotal / 7.0f;
- boolean found = false;
- int max = possibleCenters.size();
- for (int index = 0; index < max; index++) {
- FinderPattern center = (FinderPattern) possibleCenters.elementAt(index);
- // Look for about the same center and module size:
- if (center.aboutEquals(estimatedModuleSize, centerI, centerJ)) {
- center.incrementCount();
- found = true;
- break;
- }
- }
- if (!found) {
- ResultPoint point = new FinderPattern(centerJ, centerI, estimatedModuleSize);
- possibleCenters.addElement(point);
- if (resultPointCallback != null) {
- resultPointCallback.foundPossibleResultPoint(point);
- }
- }
- return true;
- }
- }
- return false;
- }
-
- /**
- * @return number of rows we could safely skip during scanning, based on the first
- * two finder patterns that have been located. In some cases their position will
- * allow us to infer that the third pattern must lie below a certain point farther
- * down in the image.
- */
- private int findRowSkip() {
- int max = possibleCenters.size();
- if (max <= 1) {
- return 0;
- }
- FinderPattern firstConfirmedCenter = null;
- for (int i = 0; i < max; i++) {
- FinderPattern center = (FinderPattern) possibleCenters.elementAt(i);
- if (center.getCount() >= CENTER_QUORUM) {
- if (firstConfirmedCenter == null) {
- firstConfirmedCenter = center;
- } else {
- // We have two confirmed centers
- // How far down can we skip before resuming looking for the next
- // pattern? In the worst case, only the difference between the
- // difference in the x / y coordinates of the two centers.
- // This is the case where you find top left last.
- hasSkipped = true;
- return (int) (Math.abs(firstConfirmedCenter.getX() - center.getX()) -
- Math.abs(firstConfirmedCenter.getY() - center.getY())) / 2;
- }
- }
- }
- return 0;
- }
-
- /**
- * @return true iff we have found at least 3 finder patterns that have been detected
- * at least {@link #CENTER_QUORUM} times each, and, the estimated module size of the
- * candidates is "pretty similar"
- */
- private boolean haveMultiplyConfirmedCenters() {
- int confirmedCount = 0;
- float totalModuleSize = 0.0f;
- int max = possibleCenters.size();
- for (int i = 0; i < max; i++) {
- FinderPattern pattern = (FinderPattern) possibleCenters.elementAt(i);
- if (pattern.getCount() >= CENTER_QUORUM) {
- confirmedCount++;
- totalModuleSize += pattern.getEstimatedModuleSize();
- }
- }
- if (confirmedCount < 3) {
- return false;
- }
- // OK, we have at least 3 confirmed centers, but, it's possible that one is a "false positive"
- // and that we need to keep looking. We detect this by asking if the estimated module sizes
- // vary too much. We arbitrarily say that when the total deviation from average exceeds
- // 5% of the total module size estimates, it's too much.
- float average = totalModuleSize / (float) max;
- float totalDeviation = 0.0f;
- for (int i = 0; i < max; i++) {
- FinderPattern pattern = (FinderPattern) possibleCenters.elementAt(i);
- totalDeviation += Math.abs(pattern.getEstimatedModuleSize() - average);
- }
- return totalDeviation <= 0.05f * totalModuleSize;
- }
-
- /**
- * @return the 3 best {@link FinderPattern}s from our list of candidates. The "best" are
- * those that have been detected at least {@link #CENTER_QUORUM} times, and whose module
- * size differs from the average among those patterns the least
- * @throws NotFoundException if 3 such finder patterns do not exist
- */
- private FinderPattern[] selectBestPatterns() throws NotFoundException {
-
- int startSize = possibleCenters.size();
- if (startSize < 3) {
- // Couldn't find enough finder patterns
- throw NotFoundException.getNotFoundInstance();
- }
-
- // Filter outlier possibilities whose module size is too different
- if (startSize > 3) {
- // But we can only afford to do so if we have at least 4 possibilities to choose from
- float totalModuleSize = 0.0f;
- float square = 0.0f;
- for (int i = 0; i < startSize; i++) {
- float size = ((FinderPattern) possibleCenters.elementAt(i)).getEstimatedModuleSize();
- totalModuleSize += size;
- square += size * size;
- }
- float average = totalModuleSize / (float) startSize;
- float stdDev = (float) Math.sqrt(square / startSize - average * average);
-
- Collections.insertionSort(possibleCenters, new FurthestFromAverageComparator(average));
-
- float limit = Math.max(0.2f * average, stdDev);
-
- for (int i = 0; i < possibleCenters.size() && possibleCenters.size() > 3; i++) {
- FinderPattern pattern = (FinderPattern) possibleCenters.elementAt(i);
- if (Math.abs(pattern.getEstimatedModuleSize() - average) > limit) {
- possibleCenters.removeElementAt(i);
- i--;
- }
- }
- }
-
- if (possibleCenters.size() > 3) {
- // Throw away all but those first size candidate points we found.
-
- float totalModuleSize = 0.0f;
- for (int i = 0; i < possibleCenters.size(); i++) {
- totalModuleSize += ((FinderPattern) possibleCenters.elementAt(i)).getEstimatedModuleSize();
- }
-
- float average = totalModuleSize / (float) possibleCenters.size();
-
- Collections.insertionSort(possibleCenters, new CenterComparator(average));
-
- possibleCenters.setSize(3);
- }
-
- return new FinderPattern[]{
- (FinderPattern) possibleCenters.elementAt(0),
- (FinderPattern) possibleCenters.elementAt(1),
- (FinderPattern) possibleCenters.elementAt(2)
- };
- }
-
- /**
- *
Orders by furthest from average
- */
- private static class FurthestFromAverageComparator implements Comparator {
- private final float average;
- private FurthestFromAverageComparator(float f) {
- average = f;
- }
- public int compare(Object center1, Object center2) {
- float dA = Math.abs(((FinderPattern) center2).getEstimatedModuleSize() - average);
- float dB = Math.abs(((FinderPattern) center1).getEstimatedModuleSize() - average);
- return dA < dB ? -1 : dA == dB ? 0 : 1;
- }
- }
-
- /**
- *
Orders by {@link FinderPattern#getCount()}, descending.
- */
- private static class CenterComparator implements Comparator {
- private final float average;
- private CenterComparator(float f) {
- average = f;
- }
- public int compare(Object center1, Object center2) {
- if (((FinderPattern) center2).getCount() == ((FinderPattern) center1).getCount()) {
- float dA = Math.abs(((FinderPattern) center2).getEstimatedModuleSize() - average);
- float dB = Math.abs(((FinderPattern) center1).getEstimatedModuleSize() - average);
- return dA < dB ? 1 : dA == dB ? 0 : -1;
- } else {
- return ((FinderPattern) center2).getCount() - ((FinderPattern) center1).getCount();
- }
- }
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/FinderPatternInfo.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/FinderPatternInfo.java
deleted file mode 100644
index 3c3401085..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/FinderPatternInfo.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2007 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.qrcode.detector;
-
-/**
- *
Encapsulates information about finder patterns in an image, including the location of
- * the three finder patterns, and their estimated module size.
- *
- * @author Sean Owen
- */
-public final class FinderPatternInfo {
-
- private final FinderPattern bottomLeft;
- private final FinderPattern topLeft;
- private final FinderPattern topRight;
-
- public FinderPatternInfo(FinderPattern[] patternCenters) {
- this.bottomLeft = patternCenters[0];
- this.topLeft = patternCenters[1];
- this.topRight = patternCenters[2];
- }
-
- public FinderPattern getBottomLeft() {
- return bottomLeft;
- }
-
- public FinderPattern getTopLeft() {
- return topLeft;
- }
-
- public FinderPattern getTopRight() {
- return topRight;
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/BlockPair.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/BlockPair.java
deleted file mode 100644
index 5714d9c3a..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/BlockPair.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 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.qrcode.encoder;
-
-final class BlockPair {
-
- private final byte[] dataBytes;
- private final byte[] errorCorrectionBytes;
-
- BlockPair(byte[] data, byte[] errorCorrection) {
- dataBytes = data;
- errorCorrectionBytes = errorCorrection;
- }
-
- public byte[] getDataBytes() {
- return dataBytes;
- }
-
- public byte[] getErrorCorrectionBytes() {
- return errorCorrectionBytes;
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/ByteMatrix.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/ByteMatrix.java
deleted file mode 100644
index eb248a26c..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/ByteMatrix.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright 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.qrcode.encoder;
-
-/**
- * A class which wraps a 2D array of bytes. The default usage is signed. If you want to use it as a
- * unsigned container, it's up to you to do byteValue & 0xff at each location.
- *
- * JAVAPORT: The original code was a 2D array of ints, but since it only ever gets assigned
- * -1, 0, and 1, I'm going to use less memory and go with bytes.
- *
- * @author dswitkin@google.com (Daniel Switkin)
- */
-public final class ByteMatrix {
-
- private final byte[][] bytes;
- private final int width;
- private final int height;
-
- public ByteMatrix(int width, int height) {
- bytes = new byte[height][width];
- this.width = width;
- this.height = height;
- }
-
- public int getHeight() {
- return height;
- }
-
- public int getWidth() {
- return width;
- }
-
- public byte get(int x, int y) {
- return bytes[y][x];
- }
-
- public byte[][] getArray() {
- return bytes;
- }
-
- public void set(int x, int y, byte value) {
- bytes[y][x] = value;
- }
-
- public void set(int x, int y, int value) {
- bytes[y][x] = (byte) value;
- }
-
- public void set(int x, int y, boolean value) {
- bytes[y][x] = (byte) (value ? 1 : 0);
- }
-
- public void clear(byte value) {
- for (int y = 0; y < height; ++y) {
- for (int x = 0; x < width; ++x) {
- bytes[y][x] = value;
- }
- }
- }
-
- public String toString() {
- StringBuffer result = new StringBuffer(2 * width * height + 2);
- for (int y = 0; y < height; ++y) {
- for (int x = 0; x < width; ++x) {
- switch (bytes[y][x]) {
- case 0:
- result.append(" 0");
- break;
- case 1:
- result.append(" 1");
- break;
- default:
- result.append(" ");
- break;
- }
- }
- result.append('\n');
- }
- return result.toString();
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/Encoder.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/Encoder.java
deleted file mode 100644
index 8796511ab..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/Encoder.java
+++ /dev/null
@@ -1,557 +0,0 @@
-/*
- * Copyright 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.qrcode.encoder;
-
-import com.google.zxing.EncodeHintType;
-import com.google.zxing.WriterException;
-import com.google.zxing.common.BitArray;
-import com.google.zxing.common.CharacterSetECI;
-import com.google.zxing.common.ECI;
-import com.google.zxing.common.reedsolomon.GenericGF;
-import com.google.zxing.common.reedsolomon.ReedSolomonEncoder;
-import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
-import com.google.zxing.qrcode.decoder.Mode;
-import com.google.zxing.qrcode.decoder.Version;
-
-import java.io.UnsupportedEncodingException;
-import java.util.Hashtable;
-import java.util.Vector;
-
-/**
- * @author satorux@google.com (Satoru Takabayashi) - creator
- * @author dswitkin@google.com (Daniel Switkin) - ported from C++
- */
-public final class Encoder {
-
- // The original table is defined in the table 5 of JISX0510:2004 (p.19).
- private static final int[] ALPHANUMERIC_TABLE = {
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x00-0x0f
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x10-0x1f
- 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, // 0x20-0x2f
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, // 0x30-0x3f
- -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 0x40-0x4f
- 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, // 0x50-0x5f
- };
-
- static final String DEFAULT_BYTE_MODE_ENCODING = "ISO-8859-1";
-
- private Encoder() {
- }
-
- // The mask penalty calculation is complicated. See Table 21 of JISX0510:2004 (p.45) for details.
- // Basically it applies four rules and summate all penalties.
- private static int calculateMaskPenalty(ByteMatrix matrix) {
- int penalty = 0;
- penalty += MaskUtil.applyMaskPenaltyRule1(matrix);
- penalty += MaskUtil.applyMaskPenaltyRule2(matrix);
- penalty += MaskUtil.applyMaskPenaltyRule3(matrix);
- penalty += MaskUtil.applyMaskPenaltyRule4(matrix);
- return penalty;
- }
-
- /**
- * Encode "bytes" with the error correction level "ecLevel". The encoding mode will be chosen
- * internally by chooseMode(). On success, store the result in "qrCode".
- *
- * We recommend you to use QRCode.EC_LEVEL_L (the lowest level) for
- * "getECLevel" since our primary use is to show QR code on desktop screens. We don't need very
- * strong error correction for this purpose.
- *
- * Note that there is no way to encode bytes in MODE_KANJI. We might want to add EncodeWithMode()
- * with which clients can specify the encoding mode. For now, we don't need the functionality.
- */
- public static void encode(String content, ErrorCorrectionLevel ecLevel, QRCode qrCode)
- throws WriterException {
- encode(content, ecLevel, null, qrCode);
- }
-
- public static void encode(String content, ErrorCorrectionLevel ecLevel, Hashtable hints,
- QRCode qrCode) throws WriterException {
-
- String encoding = hints == null ? null : (String) hints.get(EncodeHintType.CHARACTER_SET);
- if (encoding == null) {
- encoding = DEFAULT_BYTE_MODE_ENCODING;
- }
-
- // Step 1: Choose the mode (encoding).
- Mode mode = chooseMode(content, encoding);
-
- // Step 2: Append "bytes" into "dataBits" in appropriate encoding.
- BitArray dataBits = new BitArray();
- appendBytes(content, mode, dataBits, encoding);
- // Step 3: Initialize QR code that can contain "dataBits".
- int numInputBytes = dataBits.getSizeInBytes();
- initQRCode(numInputBytes, ecLevel, mode, qrCode);
-
- // Step 4: Build another bit vector that contains header and data.
- BitArray headerAndDataBits = new BitArray();
-
- // Step 4.5: Append ECI message if applicable
- if (mode == Mode.BYTE && !DEFAULT_BYTE_MODE_ENCODING.equals(encoding)) {
- CharacterSetECI eci = CharacterSetECI.getCharacterSetECIByName(encoding);
- if (eci != null) {
- appendECI(eci, headerAndDataBits);
- }
- }
-
- appendModeInfo(mode, headerAndDataBits);
-
- int numLetters = mode.equals(Mode.BYTE) ? dataBits.getSizeInBytes() : content.length();
- appendLengthInfo(numLetters, qrCode.getVersion(), mode, headerAndDataBits);
- headerAndDataBits.appendBitArray(dataBits);
-
- // Step 5: Terminate the bits properly.
- terminateBits(qrCode.getNumDataBytes(), headerAndDataBits);
-
- // Step 6: Interleave data bits with error correction code.
- BitArray finalBits = new BitArray();
- interleaveWithECBytes(headerAndDataBits, qrCode.getNumTotalBytes(), qrCode.getNumDataBytes(),
- qrCode.getNumRSBlocks(), finalBits);
-
- // Step 7: Choose the mask pattern and set to "qrCode".
- ByteMatrix matrix = new ByteMatrix(qrCode.getMatrixWidth(), qrCode.getMatrixWidth());
- qrCode.setMaskPattern(chooseMaskPattern(finalBits, qrCode.getECLevel(), qrCode.getVersion(),
- matrix));
-
- // Step 8. Build the matrix and set it to "qrCode".
- MatrixUtil.buildMatrix(finalBits, qrCode.getECLevel(), qrCode.getVersion(),
- qrCode.getMaskPattern(), matrix);
- qrCode.setMatrix(matrix);
- // Step 9. Make sure we have a valid QR Code.
- if (!qrCode.isValid()) {
- throw new WriterException("Invalid QR code: " + qrCode.toString());
- }
- }
-
- /**
- * @return the code point of the table used in alphanumeric mode or
- * -1 if there is no corresponding code in the table.
- */
- static int getAlphanumericCode(int code) {
- if (code < ALPHANUMERIC_TABLE.length) {
- return ALPHANUMERIC_TABLE[code];
- }
- return -1;
- }
-
- public static Mode chooseMode(String content) {
- return chooseMode(content, null);
- }
-
- /**
- * Choose the best mode by examining the content. Note that 'encoding' is used as a hint;
- * if it is Shift_JIS, and the input is only double-byte Kanji, then we return {@link Mode#KANJI}.
- */
- public static Mode chooseMode(String content, String encoding) {
- if ("Shift_JIS".equals(encoding)) {
- // Choose Kanji mode if all input are double-byte characters
- return isOnlyDoubleByteKanji(content) ? Mode.KANJI : Mode.BYTE;
- }
- boolean hasNumeric = false;
- boolean hasAlphanumeric = false;
- for (int i = 0; i < content.length(); ++i) {
- char c = content.charAt(i);
- if (c >= '0' && c <= '9') {
- hasNumeric = true;
- } else if (getAlphanumericCode(c) != -1) {
- hasAlphanumeric = true;
- } else {
- return Mode.BYTE;
- }
- }
- if (hasAlphanumeric) {
- return Mode.ALPHANUMERIC;
- } else if (hasNumeric) {
- return Mode.NUMERIC;
- }
- return Mode.BYTE;
- }
-
- private static boolean isOnlyDoubleByteKanji(String content) {
- byte[] bytes;
- try {
- bytes = content.getBytes("Shift_JIS");
- } catch (UnsupportedEncodingException uee) {
- return false;
- }
- int length = bytes.length;
- if (length % 2 != 0) {
- return false;
- }
- for (int i = 0; i < length; i += 2) {
- int byte1 = bytes[i] & 0xFF;
- if ((byte1 < 0x81 || byte1 > 0x9F) && (byte1 < 0xE0 || byte1 > 0xEB)) {
- return false;
- }
- }
- return true;
- }
-
- private static int chooseMaskPattern(BitArray bits, ErrorCorrectionLevel ecLevel, int version,
- ByteMatrix matrix) throws WriterException {
-
- int minPenalty = Integer.MAX_VALUE; // Lower penalty is better.
- int bestMaskPattern = -1;
- // We try all mask patterns to choose the best one.
- for (int maskPattern = 0; maskPattern < QRCode.NUM_MASK_PATTERNS; maskPattern++) {
- MatrixUtil.buildMatrix(bits, ecLevel, version, maskPattern, matrix);
- int penalty = calculateMaskPenalty(matrix);
- if (penalty < minPenalty) {
- minPenalty = penalty;
- bestMaskPattern = maskPattern;
- }
- }
- return bestMaskPattern;
- }
-
- /**
- * Initialize "qrCode" according to "numInputBytes", "ecLevel", and "mode". On success,
- * modify "qrCode".
- */
- private static void initQRCode(int numInputBytes, ErrorCorrectionLevel ecLevel, Mode mode,
- QRCode qrCode) throws WriterException {
- qrCode.setECLevel(ecLevel);
- qrCode.setMode(mode);
-
- // In the following comments, we use numbers of Version 7-H.
- for (int versionNum = 1; versionNum <= 40; versionNum++) {
- Version version = Version.getVersionForNumber(versionNum);
- // numBytes = 196
- int numBytes = version.getTotalCodewords();
- // getNumECBytes = 130
- Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel);
- int numEcBytes = ecBlocks.getTotalECCodewords();
- // getNumRSBlocks = 5
- int numRSBlocks = ecBlocks.getNumBlocks();
- // getNumDataBytes = 196 - 130 = 66
- int numDataBytes = numBytes - numEcBytes;
- // We want to choose the smallest version which can contain data of "numInputBytes" + some
- // extra bits for the header (mode info and length info). The header can be three bytes
- // (precisely 4 + 16 bits) at most. Hence we do +3 here.
- if (numDataBytes >= numInputBytes + 3) {
- // Yay, we found the proper rs block info!
- qrCode.setVersion(versionNum);
- qrCode.setNumTotalBytes(numBytes);
- qrCode.setNumDataBytes(numDataBytes);
- qrCode.setNumRSBlocks(numRSBlocks);
- // getNumECBytes = 196 - 66 = 130
- qrCode.setNumECBytes(numEcBytes);
- // matrix width = 21 + 6 * 4 = 45
- qrCode.setMatrixWidth(version.getDimensionForVersion());
- return;
- }
- }
- throw new WriterException("Cannot find proper rs block info (input data too big?)");
- }
-
- /**
- * Terminate bits as described in 8.4.8 and 8.4.9 of JISX0510:2004 (p.24).
- */
- static void terminateBits(int numDataBytes, BitArray bits) throws WriterException {
- int capacity = numDataBytes << 3;
- if (bits.getSize() > capacity) {
- throw new WriterException("data bits cannot fit in the QR Code" + bits.getSize() + " > " +
- capacity);
- }
- for (int i = 0; i < 4 && bits.getSize() < capacity; ++i) {
- bits.appendBit(false);
- }
- // Append termination bits. See 8.4.8 of JISX0510:2004 (p.24) for details.
- // If the last byte isn't 8-bit aligned, we'll add padding bits.
- int numBitsInLastByte = bits.getSize() & 0x07;
- if (numBitsInLastByte > 0) {
- for (int i = numBitsInLastByte; i < 8; i++) {
- bits.appendBit(false);
- }
- }
- // If we have more space, we'll fill the space with padding patterns defined in 8.4.9 (p.24).
- int numPaddingBytes = numDataBytes - bits.getSizeInBytes();
- for (int i = 0; i < numPaddingBytes; ++i) {
- bits.appendBits((i & 0x01) == 0 ? 0xEC : 0x11, 8);
- }
- if (bits.getSize() != capacity) {
- throw new WriterException("Bits size does not equal capacity");
- }
- }
-
- /**
- * Get number of data bytes and number of error correction bytes for block id "blockID". Store
- * the result in "numDataBytesInBlock", and "numECBytesInBlock". See table 12 in 8.5.1 of
- * JISX0510:2004 (p.30)
- */
- static void getNumDataBytesAndNumECBytesForBlockID(int numTotalBytes, int numDataBytes,
- int numRSBlocks, int blockID, int[] numDataBytesInBlock,
- int[] numECBytesInBlock) throws WriterException {
- if (blockID >= numRSBlocks) {
- throw new WriterException("Block ID too large");
- }
- // numRsBlocksInGroup2 = 196 % 5 = 1
- int numRsBlocksInGroup2 = numTotalBytes % numRSBlocks;
- // numRsBlocksInGroup1 = 5 - 1 = 4
- int numRsBlocksInGroup1 = numRSBlocks - numRsBlocksInGroup2;
- // numTotalBytesInGroup1 = 196 / 5 = 39
- int numTotalBytesInGroup1 = numTotalBytes / numRSBlocks;
- // numTotalBytesInGroup2 = 39 + 1 = 40
- int numTotalBytesInGroup2 = numTotalBytesInGroup1 + 1;
- // numDataBytesInGroup1 = 66 / 5 = 13
- int numDataBytesInGroup1 = numDataBytes / numRSBlocks;
- // numDataBytesInGroup2 = 13 + 1 = 14
- int numDataBytesInGroup2 = numDataBytesInGroup1 + 1;
- // numEcBytesInGroup1 = 39 - 13 = 26
- int numEcBytesInGroup1 = numTotalBytesInGroup1 - numDataBytesInGroup1;
- // numEcBytesInGroup2 = 40 - 14 = 26
- int numEcBytesInGroup2 = numTotalBytesInGroup2 - numDataBytesInGroup2;
- // Sanity checks.
- // 26 = 26
- if (numEcBytesInGroup1 != numEcBytesInGroup2) {
- throw new WriterException("EC bytes mismatch");
- }
- // 5 = 4 + 1.
- if (numRSBlocks != numRsBlocksInGroup1 + numRsBlocksInGroup2) {
- throw new WriterException("RS blocks mismatch");
- }
- // 196 = (13 + 26) * 4 + (14 + 26) * 1
- if (numTotalBytes !=
- ((numDataBytesInGroup1 + numEcBytesInGroup1) *
- numRsBlocksInGroup1) +
- ((numDataBytesInGroup2 + numEcBytesInGroup2) *
- numRsBlocksInGroup2)) {
- throw new WriterException("Total bytes mismatch");
- }
-
- if (blockID < numRsBlocksInGroup1) {
- numDataBytesInBlock[0] = numDataBytesInGroup1;
- numECBytesInBlock[0] = numEcBytesInGroup1;
- } else {
- numDataBytesInBlock[0] = numDataBytesInGroup2;
- numECBytesInBlock[0] = numEcBytesInGroup2;
- }
- }
-
- /**
- * Interleave "bits" with corresponding error correction bytes. On success, store the result in
- * "result". The interleave rule is complicated. See 8.6 of JISX0510:2004 (p.37) for details.
- */
- static void interleaveWithECBytes(BitArray bits, int numTotalBytes,
- int numDataBytes, int numRSBlocks, BitArray result) throws WriterException {
-
- // "bits" must have "getNumDataBytes" bytes of data.
- if (bits.getSizeInBytes() != numDataBytes) {
- throw new WriterException("Number of bits and data bytes does not match");
- }
-
- // Step 1. Divide data bytes into blocks and generate error correction bytes for them. We'll
- // store the divided data bytes blocks and error correction bytes blocks into "blocks".
- int dataBytesOffset = 0;
- int maxNumDataBytes = 0;
- int maxNumEcBytes = 0;
-
- // Since, we know the number of reedsolmon blocks, we can initialize the vector with the number.
- Vector blocks = new Vector(numRSBlocks);
-
- for (int i = 0; i < numRSBlocks; ++i) {
- int[] numDataBytesInBlock = new int[1];
- int[] numEcBytesInBlock = new int[1];
- getNumDataBytesAndNumECBytesForBlockID(
- numTotalBytes, numDataBytes, numRSBlocks, i,
- numDataBytesInBlock, numEcBytesInBlock);
-
- int size = numDataBytesInBlock[0];
- byte[] dataBytes = new byte[size];
- bits.toBytes(8*dataBytesOffset, dataBytes, 0, size);
- byte[] ecBytes = generateECBytes(dataBytes, numEcBytesInBlock[0]);
- blocks.addElement(new BlockPair(dataBytes, ecBytes));
-
- maxNumDataBytes = Math.max(maxNumDataBytes, size);
- maxNumEcBytes = Math.max(maxNumEcBytes, ecBytes.length);
- dataBytesOffset += numDataBytesInBlock[0];
- }
- if (numDataBytes != dataBytesOffset) {
- throw new WriterException("Data bytes does not match offset");
- }
-
- // First, place data blocks.
- for (int i = 0; i < maxNumDataBytes; ++i) {
- for (int j = 0; j < blocks.size(); ++j) {
- byte[] dataBytes = ((BlockPair) blocks.elementAt(j)).getDataBytes();
- if (i < dataBytes.length) {
- result.appendBits(dataBytes[i], 8);
- }
- }
- }
- // Then, place error correction blocks.
- for (int i = 0; i < maxNumEcBytes; ++i) {
- for (int j = 0; j < blocks.size(); ++j) {
- byte[] ecBytes = ((BlockPair) blocks.elementAt(j)).getErrorCorrectionBytes();
- if (i < ecBytes.length) {
- result.appendBits(ecBytes[i], 8);
- }
- }
- }
- if (numTotalBytes != result.getSizeInBytes()) { // Should be same.
- throw new WriterException("Interleaving error: " + numTotalBytes + " and " +
- result.getSizeInBytes() + " differ.");
- }
- }
-
- static byte[] generateECBytes(byte[] dataBytes, int numEcBytesInBlock) {
- int numDataBytes = dataBytes.length;
- int[] toEncode = new int[numDataBytes + numEcBytesInBlock];
- for (int i = 0; i < numDataBytes; i++) {
- toEncode[i] = dataBytes[i] & 0xFF;
- }
- new ReedSolomonEncoder(GenericGF.QR_CODE_FIELD_256).encode(toEncode, numEcBytesInBlock);
-
- byte[] ecBytes = new byte[numEcBytesInBlock];
- for (int i = 0; i < numEcBytesInBlock; i++) {
- ecBytes[i] = (byte) toEncode[numDataBytes + i];
- }
- return ecBytes;
- }
-
- /**
- * Append mode info. On success, store the result in "bits".
- */
- static void appendModeInfo(Mode mode, BitArray bits) {
- bits.appendBits(mode.getBits(), 4);
- }
-
-
- /**
- * Append length info. On success, store the result in "bits".
- */
- static void appendLengthInfo(int numLetters, int version, Mode mode, BitArray bits)
- throws WriterException {
- int numBits = mode.getCharacterCountBits(Version.getVersionForNumber(version));
- if (numLetters > ((1 << numBits) - 1)) {
- throw new WriterException(numLetters + "is bigger than" + ((1 << numBits) - 1));
- }
- bits.appendBits(numLetters, numBits);
- }
-
- /**
- * Append "bytes" in "mode" mode (encoding) into "bits". On success, store the result in "bits".
- */
- static void appendBytes(String content, Mode mode, BitArray bits, String encoding)
- throws WriterException {
- if (mode.equals(Mode.NUMERIC)) {
- appendNumericBytes(content, bits);
- } else if (mode.equals(Mode.ALPHANUMERIC)) {
- appendAlphanumericBytes(content, bits);
- } else if (mode.equals(Mode.BYTE)) {
- append8BitBytes(content, bits, encoding);
- } else if (mode.equals(Mode.KANJI)) {
- appendKanjiBytes(content, bits);
- } else {
- throw new WriterException("Invalid mode: " + mode);
- }
- }
-
- static void appendNumericBytes(String content, BitArray bits) {
- int length = content.length();
- int i = 0;
- while (i < length) {
- int num1 = content.charAt(i) - '0';
- if (i + 2 < length) {
- // Encode three numeric letters in ten bits.
- int num2 = content.charAt(i + 1) - '0';
- int num3 = content.charAt(i + 2) - '0';
- bits.appendBits(num1 * 100 + num2 * 10 + num3, 10);
- i += 3;
- } else if (i + 1 < length) {
- // Encode two numeric letters in seven bits.
- int num2 = content.charAt(i + 1) - '0';
- bits.appendBits(num1 * 10 + num2, 7);
- i += 2;
- } else {
- // Encode one numeric letter in four bits.
- bits.appendBits(num1, 4);
- i++;
- }
- }
- }
-
- static void appendAlphanumericBytes(String content, BitArray bits) throws WriterException {
- int length = content.length();
- int i = 0;
- while (i < length) {
- int code1 = getAlphanumericCode(content.charAt(i));
- if (code1 == -1) {
- throw new WriterException();
- }
- if (i + 1 < length) {
- int code2 = getAlphanumericCode(content.charAt(i + 1));
- if (code2 == -1) {
- throw new WriterException();
- }
- // Encode two alphanumeric letters in 11 bits.
- bits.appendBits(code1 * 45 + code2, 11);
- i += 2;
- } else {
- // Encode one alphanumeric letter in six bits.
- bits.appendBits(code1, 6);
- i++;
- }
- }
- }
-
- static void append8BitBytes(String content, BitArray bits, String encoding)
- throws WriterException {
- byte[] bytes;
- try {
- bytes = content.getBytes(encoding);
- } catch (UnsupportedEncodingException uee) {
- throw new WriterException(uee.toString());
- }
- for (int i = 0; i < bytes.length; ++i) {
- bits.appendBits(bytes[i], 8);
- }
- }
-
- static void appendKanjiBytes(String content, BitArray bits) throws WriterException {
- byte[] bytes;
- try {
- bytes = content.getBytes("Shift_JIS");
- } catch (UnsupportedEncodingException uee) {
- throw new WriterException(uee.toString());
- }
- int length = bytes.length;
- for (int i = 0; i < length; i += 2) {
- int byte1 = bytes[i] & 0xFF;
- int byte2 = bytes[i + 1] & 0xFF;
- int code = (byte1 << 8) | byte2;
- int subtracted = -1;
- if (code >= 0x8140 && code <= 0x9ffc) {
- subtracted = code - 0x8140;
- } else if (code >= 0xe040 && code <= 0xebbf) {
- subtracted = code - 0xc140;
- }
- if (subtracted == -1) {
- throw new WriterException("Invalid byte sequence");
- }
- int encoded = ((subtracted >> 8) * 0xc0) + (subtracted & 0xff);
- bits.appendBits(encoded, 13);
- }
- }
-
- private static void appendECI(ECI eci, BitArray bits) {
- bits.appendBits(Mode.ECI.getBits(), 4);
- // This is correct for values up to 127, which is all we need now.
- bits.appendBits(eci.getValue(), 8);
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/MaskUtil.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/MaskUtil.java
deleted file mode 100644
index 61ccf48c1..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/MaskUtil.java
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- * Copyright 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.qrcode.encoder;
-
-/**
- * @author satorux@google.com (Satoru Takabayashi) - creator
- * @author dswitkin@google.com (Daniel Switkin) - ported from C++
- */
-public final class MaskUtil {
-
- private MaskUtil() {
- // do nothing
- }
-
- // Apply mask penalty rule 1 and return the penalty. Find repetitive cells with the same color and
- // give penalty to them. Example: 00000 or 11111.
- public static int applyMaskPenaltyRule1(ByteMatrix matrix) {
- return applyMaskPenaltyRule1Internal(matrix, true) + applyMaskPenaltyRule1Internal(matrix, false);
- }
-
- // Apply mask penalty rule 2 and return the penalty. Find 2x2 blocks with the same color and give
- // penalty to them.
- public static int applyMaskPenaltyRule2(ByteMatrix matrix) {
- int penalty = 0;
- byte[][] array = matrix.getArray();
- int width = matrix.getWidth();
- int height = matrix.getHeight();
- for (int y = 0; y < height - 1; ++y) {
- for (int x = 0; x < width - 1; ++x) {
- int value = array[y][x];
- if (value == array[y][x + 1] && value == array[y + 1][x] && value == array[y + 1][x + 1]) {
- penalty += 3;
- }
- }
- }
- return penalty;
- }
-
- // Apply mask penalty rule 3 and return the penalty. Find consecutive cells of 00001011101 or
- // 10111010000, and give penalty to them. If we find patterns like 000010111010000, we give
- // penalties twice (i.e. 40 * 2).
- public static int applyMaskPenaltyRule3(ByteMatrix matrix) {
- int penalty = 0;
- byte[][] array = matrix.getArray();
- int width = matrix.getWidth();
- int height = matrix.getHeight();
- for (int y = 0; y < height; ++y) {
- for (int x = 0; x < width; ++x) {
- // Tried to simplify following conditions but failed.
- if (x + 6 < width &&
- array[y][x] == 1 &&
- array[y][x + 1] == 0 &&
- array[y][x + 2] == 1 &&
- array[y][x + 3] == 1 &&
- array[y][x + 4] == 1 &&
- array[y][x + 5] == 0 &&
- array[y][x + 6] == 1 &&
- ((x + 10 < width &&
- array[y][x + 7] == 0 &&
- array[y][x + 8] == 0 &&
- array[y][x + 9] == 0 &&
- array[y][x + 10] == 0) ||
- (x - 4 >= 0 &&
- array[y][x - 1] == 0 &&
- array[y][x - 2] == 0 &&
- array[y][x - 3] == 0 &&
- array[y][x - 4] == 0))) {
- penalty += 40;
- }
- if (y + 6 < height &&
- array[y][x] == 1 &&
- array[y + 1][x] == 0 &&
- array[y + 2][x] == 1 &&
- array[y + 3][x] == 1 &&
- array[y + 4][x] == 1 &&
- array[y + 5][x] == 0 &&
- array[y + 6][x] == 1 &&
- ((y + 10 < height &&
- array[y + 7][x] == 0 &&
- array[y + 8][x] == 0 &&
- array[y + 9][x] == 0 &&
- array[y + 10][x] == 0) ||
- (y - 4 >= 0 &&
- array[y - 1][x] == 0 &&
- array[y - 2][x] == 0 &&
- array[y - 3][x] == 0 &&
- array[y - 4][x] == 0))) {
- penalty += 40;
- }
- }
- }
- return penalty;
- }
-
- // Apply mask penalty rule 4 and return the penalty. Calculate the ratio of dark cells and give
- // penalty if the ratio is far from 50%. It gives 10 penalty for 5% distance. Examples:
- // - 0% => 100
- // - 40% => 20
- // - 45% => 10
- // - 50% => 0
- // - 55% => 10
- // - 55% => 20
- // - 100% => 100
- public static int applyMaskPenaltyRule4(ByteMatrix matrix) {
- int numDarkCells = 0;
- byte[][] array = matrix.getArray();
- int width = matrix.getWidth();
- int height = matrix.getHeight();
- for (int y = 0; y < height; ++y) {
- for (int x = 0; x < width; ++x) {
- if (array[y][x] == 1) {
- numDarkCells += 1;
- }
- }
- }
- int numTotalCells = matrix.getHeight() * matrix.getWidth();
- double darkRatio = (double) numDarkCells / numTotalCells;
- return Math.abs((int) (darkRatio * 100 - 50)) / 5 * 10;
- }
-
- // Return the mask bit for "getMaskPattern" at "x" and "y". See 8.8 of JISX0510:2004 for mask
- // pattern conditions.
- public static boolean getDataMaskBit(int maskPattern, int x, int y) {
- if (!QRCode.isValidMaskPattern(maskPattern)) {
- throw new IllegalArgumentException("Invalid mask pattern");
- }
- int intermediate;
- int temp;
- switch (maskPattern) {
- case 0:
- intermediate = (y + x) & 0x1;
- break;
- case 1:
- intermediate = y & 0x1;
- break;
- case 2:
- intermediate = x % 3;
- break;
- case 3:
- intermediate = (y + x) % 3;
- break;
- case 4:
- intermediate = ((y >>> 1) + (x / 3)) & 0x1;
- break;
- case 5:
- temp = y * x;
- intermediate = (temp & 0x1) + (temp % 3);
- break;
- case 6:
- temp = y * x;
- intermediate = ((temp & 0x1) + (temp % 3)) & 0x1;
- break;
- case 7:
- temp = y * x;
- intermediate = ((temp % 3) + ((y + x) & 0x1)) & 0x1;
- break;
- default:
- throw new IllegalArgumentException("Invalid mask pattern: " + maskPattern);
- }
- return intermediate == 0;
- }
-
- // Helper function for applyMaskPenaltyRule1. We need this for doing this calculation in both
- // vertical and horizontal orders respectively.
- private static int applyMaskPenaltyRule1Internal(ByteMatrix matrix, boolean isHorizontal) {
- int penalty = 0;
- int numSameBitCells = 0;
- int prevBit = -1;
- // Horizontal mode:
- // for (int i = 0; i < matrix.height(); ++i) {
- // for (int j = 0; j < matrix.width(); ++j) {
- // int bit = matrix.get(i, j);
- // Vertical mode:
- // for (int i = 0; i < matrix.width(); ++i) {
- // for (int j = 0; j < matrix.height(); ++j) {
- // int bit = matrix.get(j, i);
- int iLimit = isHorizontal ? matrix.getHeight() : matrix.getWidth();
- int jLimit = isHorizontal ? matrix.getWidth() : matrix.getHeight();
- byte[][] array = matrix.getArray();
- for (int i = 0; i < iLimit; ++i) {
- for (int j = 0; j < jLimit; ++j) {
- int bit = isHorizontal ? array[i][j] : array[j][i];
- if (bit == prevBit) {
- numSameBitCells += 1;
- // Found five repetitive cells with the same color (bit).
- // We'll give penalty of 3.
- if (numSameBitCells == 5) {
- penalty += 3;
- } else if (numSameBitCells > 5) {
- // After five repetitive cells, we'll add the penalty one
- // by one.
- penalty += 1;
- }
- } else {
- numSameBitCells = 1; // Include the cell itself.
- prevBit = bit;
- }
- }
- numSameBitCells = 0; // Clear at each row/column.
- }
- return penalty;
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/MatrixUtil.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/MatrixUtil.java
deleted file mode 100644
index 3d434e675..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/MatrixUtil.java
+++ /dev/null
@@ -1,524 +0,0 @@
-/*
- * Copyright 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.qrcode.encoder;
-
-import com.google.zxing.WriterException;
-import com.google.zxing.common.BitArray;
-import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
-
-/**
- * @author satorux@google.com (Satoru Takabayashi) - creator
- * @author dswitkin@google.com (Daniel Switkin) - ported from C++
- */
-public final class MatrixUtil {
-
- private MatrixUtil() {
- // do nothing
- }
-
- private static final int[][] POSITION_DETECTION_PATTERN = {
- {1, 1, 1, 1, 1, 1, 1},
- {1, 0, 0, 0, 0, 0, 1},
- {1, 0, 1, 1, 1, 0, 1},
- {1, 0, 1, 1, 1, 0, 1},
- {1, 0, 1, 1, 1, 0, 1},
- {1, 0, 0, 0, 0, 0, 1},
- {1, 1, 1, 1, 1, 1, 1},
- };
-
- private static final int[][] HORIZONTAL_SEPARATION_PATTERN = {
- {0, 0, 0, 0, 0, 0, 0, 0},
- };
-
- private static final int[][] VERTICAL_SEPARATION_PATTERN = {
- {0}, {0}, {0}, {0}, {0}, {0}, {0},
- };
-
- private static final int[][] POSITION_ADJUSTMENT_PATTERN = {
- {1, 1, 1, 1, 1},
- {1, 0, 0, 0, 1},
- {1, 0, 1, 0, 1},
- {1, 0, 0, 0, 1},
- {1, 1, 1, 1, 1},
- };
-
- // From Appendix E. Table 1, JIS0510X:2004 (p 71). The table was double-checked by komatsu.
- private static final int[][] POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE = {
- {-1, -1, -1, -1, -1, -1, -1}, // Version 1
- { 6, 18, -1, -1, -1, -1, -1}, // Version 2
- { 6, 22, -1, -1, -1, -1, -1}, // Version 3
- { 6, 26, -1, -1, -1, -1, -1}, // Version 4
- { 6, 30, -1, -1, -1, -1, -1}, // Version 5
- { 6, 34, -1, -1, -1, -1, -1}, // Version 6
- { 6, 22, 38, -1, -1, -1, -1}, // Version 7
- { 6, 24, 42, -1, -1, -1, -1}, // Version 8
- { 6, 26, 46, -1, -1, -1, -1}, // Version 9
- { 6, 28, 50, -1, -1, -1, -1}, // Version 10
- { 6, 30, 54, -1, -1, -1, -1}, // Version 11
- { 6, 32, 58, -1, -1, -1, -1}, // Version 12
- { 6, 34, 62, -1, -1, -1, -1}, // Version 13
- { 6, 26, 46, 66, -1, -1, -1}, // Version 14
- { 6, 26, 48, 70, -1, -1, -1}, // Version 15
- { 6, 26, 50, 74, -1, -1, -1}, // Version 16
- { 6, 30, 54, 78, -1, -1, -1}, // Version 17
- { 6, 30, 56, 82, -1, -1, -1}, // Version 18
- { 6, 30, 58, 86, -1, -1, -1}, // Version 19
- { 6, 34, 62, 90, -1, -1, -1}, // Version 20
- { 6, 28, 50, 72, 94, -1, -1}, // Version 21
- { 6, 26, 50, 74, 98, -1, -1}, // Version 22
- { 6, 30, 54, 78, 102, -1, -1}, // Version 23
- { 6, 28, 54, 80, 106, -1, -1}, // Version 24
- { 6, 32, 58, 84, 110, -1, -1}, // Version 25
- { 6, 30, 58, 86, 114, -1, -1}, // Version 26
- { 6, 34, 62, 90, 118, -1, -1}, // Version 27
- { 6, 26, 50, 74, 98, 122, -1}, // Version 28
- { 6, 30, 54, 78, 102, 126, -1}, // Version 29
- { 6, 26, 52, 78, 104, 130, -1}, // Version 30
- { 6, 30, 56, 82, 108, 134, -1}, // Version 31
- { 6, 34, 60, 86, 112, 138, -1}, // Version 32
- { 6, 30, 58, 86, 114, 142, -1}, // Version 33
- { 6, 34, 62, 90, 118, 146, -1}, // Version 34
- { 6, 30, 54, 78, 102, 126, 150}, // Version 35
- { 6, 24, 50, 76, 102, 128, 154}, // Version 36
- { 6, 28, 54, 80, 106, 132, 158}, // Version 37
- { 6, 32, 58, 84, 110, 136, 162}, // Version 38
- { 6, 26, 54, 82, 110, 138, 166}, // Version 39
- { 6, 30, 58, 86, 114, 142, 170}, // Version 40
- };
-
- // Type info cells at the left top corner.
- private static final int[][] TYPE_INFO_COORDINATES = {
- {8, 0},
- {8, 1},
- {8, 2},
- {8, 3},
- {8, 4},
- {8, 5},
- {8, 7},
- {8, 8},
- {7, 8},
- {5, 8},
- {4, 8},
- {3, 8},
- {2, 8},
- {1, 8},
- {0, 8},
- };
-
- // From Appendix D in JISX0510:2004 (p. 67)
- private static final int VERSION_INFO_POLY = 0x1f25; // 1 1111 0010 0101
-
- // From Appendix C in JISX0510:2004 (p.65).
- private static final int TYPE_INFO_POLY = 0x537;
- private static final int TYPE_INFO_MASK_PATTERN = 0x5412;
-
- // Set all cells to -1. -1 means that the cell is empty (not set yet).
- //
- // JAVAPORT: We shouldn't need to do this at all. The code should be rewritten to begin encoding
- // with the ByteMatrix initialized all to zero.
- public static void clearMatrix(ByteMatrix matrix) {
- matrix.clear((byte) -1);
- }
-
- // Build 2D matrix of QR Code from "dataBits" with "ecLevel", "version" and "getMaskPattern". On
- // success, store the result in "matrix" and return true.
- public static void buildMatrix(BitArray dataBits, ErrorCorrectionLevel ecLevel, int version,
- int maskPattern, ByteMatrix matrix) throws WriterException {
- clearMatrix(matrix);
- embedBasicPatterns(version, matrix);
- // Type information appear with any version.
- embedTypeInfo(ecLevel, maskPattern, matrix);
- // Version info appear if version >= 7.
- maybeEmbedVersionInfo(version, matrix);
- // Data should be embedded at end.
- embedDataBits(dataBits, maskPattern, matrix);
- }
-
- // Embed basic patterns. On success, modify the matrix and return true.
- // The basic patterns are:
- // - Position detection patterns
- // - Timing patterns
- // - Dark dot at the left bottom corner
- // - Position adjustment patterns, if need be
- public static void embedBasicPatterns(int version, ByteMatrix matrix) throws WriterException {
- // Let's get started with embedding big squares at corners.
- embedPositionDetectionPatternsAndSeparators(matrix);
- // Then, embed the dark dot at the left bottom corner.
- embedDarkDotAtLeftBottomCorner(matrix);
-
- // Position adjustment patterns appear if version >= 2.
- maybeEmbedPositionAdjustmentPatterns(version, matrix);
- // Timing patterns should be embedded after position adj. patterns.
- embedTimingPatterns(matrix);
- }
-
- // Embed type information. On success, modify the matrix.
- public static void embedTypeInfo(ErrorCorrectionLevel ecLevel, int maskPattern, ByteMatrix matrix)
- throws WriterException {
- BitArray typeInfoBits = new BitArray();
- makeTypeInfoBits(ecLevel, maskPattern, typeInfoBits);
-
- for (int i = 0; i < typeInfoBits.getSize(); ++i) {
- // Place bits in LSB to MSB order. LSB (least significant bit) is the last value in
- // "typeInfoBits".
- boolean bit = typeInfoBits.get(typeInfoBits.getSize() - 1 - i);
-
- // Type info bits at the left top corner. See 8.9 of JISX0510:2004 (p.46).
- int x1 = TYPE_INFO_COORDINATES[i][0];
- int y1 = TYPE_INFO_COORDINATES[i][1];
- matrix.set(x1, y1, bit);
-
- if (i < 8) {
- // Right top corner.
- int x2 = matrix.getWidth() - i - 1;
- int y2 = 8;
- matrix.set(x2, y2, bit);
- } else {
- // Left bottom corner.
- int x2 = 8;
- int y2 = matrix.getHeight() - 7 + (i - 8);
- matrix.set(x2, y2, bit);
- }
- }
- }
-
- // Embed version information if need be. On success, modify the matrix and return true.
- // See 8.10 of JISX0510:2004 (p.47) for how to embed version information.
- public static void maybeEmbedVersionInfo(int version, ByteMatrix matrix) throws WriterException {
- if (version < 7) { // Version info is necessary if version >= 7.
- return; // Don't need version info.
- }
- BitArray versionInfoBits = new BitArray();
- makeVersionInfoBits(version, versionInfoBits);
-
- int bitIndex = 6 * 3 - 1; // It will decrease from 17 to 0.
- for (int i = 0; i < 6; ++i) {
- for (int j = 0; j < 3; ++j) {
- // Place bits in LSB (least significant bit) to MSB order.
- boolean bit = versionInfoBits.get(bitIndex);
- bitIndex--;
- // Left bottom corner.
- matrix.set(i, matrix.getHeight() - 11 + j, bit);
- // Right bottom corner.
- matrix.set(matrix.getHeight() - 11 + j, i, bit);
- }
- }
- }
-
- // Embed "dataBits" using "getMaskPattern". On success, modify the matrix and return true.
- // For debugging purposes, it skips masking process if "getMaskPattern" is -1.
- // See 8.7 of JISX0510:2004 (p.38) for how to embed data bits.
- public static void embedDataBits(BitArray dataBits, int maskPattern, ByteMatrix matrix)
- throws WriterException {
- int bitIndex = 0;
- int direction = -1;
- // Start from the right bottom cell.
- int x = matrix.getWidth() - 1;
- int y = matrix.getHeight() - 1;
- while (x > 0) {
- // Skip the vertical timing pattern.
- if (x == 6) {
- x -= 1;
- }
- while (y >= 0 && y < matrix.getHeight()) {
- for (int i = 0; i < 2; ++i) {
- int xx = x - i;
- // Skip the cell if it's not empty.
- if (!isEmpty(matrix.get(xx, y))) {
- continue;
- }
- boolean bit;
- if (bitIndex < dataBits.getSize()) {
- bit = dataBits.get(bitIndex);
- ++bitIndex;
- } else {
- // Padding bit. If there is no bit left, we'll fill the left cells with 0, as described
- // in 8.4.9 of JISX0510:2004 (p. 24).
- bit = false;
- }
-
- // Skip masking if mask_pattern is -1.
- if (maskPattern != -1) {
- if (MaskUtil.getDataMaskBit(maskPattern, xx, y)) {
- bit = !bit;
- }
- }
- matrix.set(xx, y, bit);
- }
- y += direction;
- }
- direction = -direction; // Reverse the direction.
- y += direction;
- x -= 2; // Move to the left.
- }
- // All bits should be consumed.
- if (bitIndex != dataBits.getSize()) {
- throw new WriterException("Not all bits consumed: " + bitIndex + '/' + dataBits.getSize());
- }
- }
-
- // Return the position of the most significant bit set (to one) in the "value". The most
- // significant bit is position 32. If there is no bit set, return 0. Examples:
- // - findMSBSet(0) => 0
- // - findMSBSet(1) => 1
- // - findMSBSet(255) => 8
- public static int findMSBSet(int value) {
- int numDigits = 0;
- while (value != 0) {
- value >>>= 1;
- ++numDigits;
- }
- return numDigits;
- }
-
- // Calculate BCH (Bose-Chaudhuri-Hocquenghem) code for "value" using polynomial "poly". The BCH
- // code is used for encoding type information and version information.
- // Example: Calculation of version information of 7.
- // f(x) is created from 7.
- // - 7 = 000111 in 6 bits
- // - f(x) = x^2 + x^1 + x^0
- // g(x) is given by the standard (p. 67)
- // - g(x) = x^12 + x^11 + x^10 + x^9 + x^8 + x^5 + x^2 + 1
- // Multiply f(x) by x^(18 - 6)
- // - f'(x) = f(x) * x^(18 - 6)
- // - f'(x) = x^14 + x^13 + x^12
- // Calculate the remainder of f'(x) / g(x)
- // x^2
- // __________________________________________________
- // g(x) )x^14 + x^13 + x^12
- // x^14 + x^13 + x^12 + x^11 + x^10 + x^7 + x^4 + x^2
- // --------------------------------------------------
- // x^11 + x^10 + x^7 + x^4 + x^2
- //
- // The remainder is x^11 + x^10 + x^7 + x^4 + x^2
- // Encode it in binary: 110010010100
- // The return value is 0xc94 (1100 1001 0100)
- //
- // Since all coefficients in the polynomials are 1 or 0, we can do the calculation by bit
- // operations. We don't care if cofficients are positive or negative.
- public static int calculateBCHCode(int value, int poly) {
- // If poly is "1 1111 0010 0101" (version info poly), msbSetInPoly is 13. We'll subtract 1
- // from 13 to make it 12.
- int msbSetInPoly = findMSBSet(poly);
- value <<= msbSetInPoly - 1;
- // Do the division business using exclusive-or operations.
- while (findMSBSet(value) >= msbSetInPoly) {
- value ^= poly << (findMSBSet(value) - msbSetInPoly);
- }
- // Now the "value" is the remainder (i.e. the BCH code)
- return value;
- }
-
- // Make bit vector of type information. On success, store the result in "bits" and return true.
- // Encode error correction level and mask pattern. See 8.9 of
- // JISX0510:2004 (p.45) for details.
- public static void makeTypeInfoBits(ErrorCorrectionLevel ecLevel, int maskPattern, BitArray bits)
- throws WriterException {
- if (!QRCode.isValidMaskPattern(maskPattern)) {
- throw new WriterException("Invalid mask pattern");
- }
- int typeInfo = (ecLevel.getBits() << 3) | maskPattern;
- bits.appendBits(typeInfo, 5);
-
- int bchCode = calculateBCHCode(typeInfo, TYPE_INFO_POLY);
- bits.appendBits(bchCode, 10);
-
- BitArray maskBits = new BitArray();
- maskBits.appendBits(TYPE_INFO_MASK_PATTERN, 15);
- bits.xor(maskBits);
-
- if (bits.getSize() != 15) { // Just in case.
- throw new WriterException("should not happen but we got: " + bits.getSize());
- }
- }
-
- // Make bit vector of version information. On success, store the result in "bits" and return true.
- // See 8.10 of JISX0510:2004 (p.45) for details.
- public static void makeVersionInfoBits(int version, BitArray bits) throws WriterException {
- bits.appendBits(version, 6);
- int bchCode = calculateBCHCode(version, VERSION_INFO_POLY);
- bits.appendBits(bchCode, 12);
-
- if (bits.getSize() != 18) { // Just in case.
- throw new WriterException("should not happen but we got: " + bits.getSize());
- }
- }
-
- // Check if "value" is empty.
- private static boolean isEmpty(int value) {
- return value == -1;
- }
-
- // Check if "value" is valid.
- private static boolean isValidValue(int value) {
- return value == -1 || // Empty.
- value == 0 || // Light (white).
- value == 1; // Dark (black).
- }
-
- private static void embedTimingPatterns(ByteMatrix matrix) throws WriterException {
- // -8 is for skipping position detection patterns (size 7), and two horizontal/vertical
- // separation patterns (size 1). Thus, 8 = 7 + 1.
- for (int i = 8; i < matrix.getWidth() - 8; ++i) {
- int bit = (i + 1) % 2;
- // Horizontal line.
- if (!isValidValue(matrix.get(i, 6))) {
- throw new WriterException();
- }
- if (isEmpty(matrix.get(i, 6))) {
- matrix.set(i, 6, bit);
- }
- // Vertical line.
- if (!isValidValue(matrix.get(6, i))) {
- throw new WriterException();
- }
- if (isEmpty(matrix.get(6, i))) {
- matrix.set(6, i, bit);
- }
- }
- }
-
- // Embed the lonely dark dot at left bottom corner. JISX0510:2004 (p.46)
- private static void embedDarkDotAtLeftBottomCorner(ByteMatrix matrix) throws WriterException {
- if (matrix.get(8, matrix.getHeight() - 8) == 0) {
- throw new WriterException();
- }
- matrix.set(8, matrix.getHeight() - 8, 1);
- }
-
- private static void embedHorizontalSeparationPattern(int xStart, int yStart,
- ByteMatrix matrix) throws WriterException {
- // We know the width and height.
- if (HORIZONTAL_SEPARATION_PATTERN[0].length != 8 || HORIZONTAL_SEPARATION_PATTERN.length != 1) {
- throw new WriterException("Bad horizontal separation pattern");
- }
- for (int x = 0; x < 8; ++x) {
- if (!isEmpty(matrix.get(xStart + x, yStart))) {
- throw new WriterException();
- }
- matrix.set(xStart + x, yStart, HORIZONTAL_SEPARATION_PATTERN[0][x]);
- }
- }
-
- private static void embedVerticalSeparationPattern(int xStart, int yStart,
- ByteMatrix matrix) throws WriterException {
- // We know the width and height.
- if (VERTICAL_SEPARATION_PATTERN[0].length != 1 || VERTICAL_SEPARATION_PATTERN.length != 7) {
- throw new WriterException("Bad vertical separation pattern");
- }
- for (int y = 0; y < 7; ++y) {
- if (!isEmpty(matrix.get(xStart, yStart + y))) {
- throw new WriterException();
- }
- matrix.set(xStart, yStart + y, VERTICAL_SEPARATION_PATTERN[y][0]);
- }
- }
-
- // Note that we cannot unify the function with embedPositionDetectionPattern() despite they are
- // almost identical, since we cannot write a function that takes 2D arrays in different sizes in
- // C/C++. We should live with the fact.
- private static void embedPositionAdjustmentPattern(int xStart, int yStart,
- ByteMatrix matrix) throws WriterException {
- // We know the width and height.
- if (POSITION_ADJUSTMENT_PATTERN[0].length != 5 || POSITION_ADJUSTMENT_PATTERN.length != 5) {
- throw new WriterException("Bad position adjustment");
- }
- for (int y = 0; y < 5; ++y) {
- for (int x = 0; x < 5; ++x) {
- if (!isEmpty(matrix.get(xStart + x, yStart + y))) {
- throw new WriterException();
- }
- matrix.set(xStart + x, yStart + y, POSITION_ADJUSTMENT_PATTERN[y][x]);
- }
- }
- }
-
- private static void embedPositionDetectionPattern(int xStart, int yStart,
- ByteMatrix matrix) throws WriterException {
- // We know the width and height.
- if (POSITION_DETECTION_PATTERN[0].length != 7 || POSITION_DETECTION_PATTERN.length != 7) {
- throw new WriterException("Bad position detection pattern");
- }
- for (int y = 0; y < 7; ++y) {
- for (int x = 0; x < 7; ++x) {
- if (!isEmpty(matrix.get(xStart + x, yStart + y))) {
- throw new WriterException();
- }
- matrix.set(xStart + x, yStart + y, POSITION_DETECTION_PATTERN[y][x]);
- }
- }
- }
-
- // Embed position detection patterns and surrounding vertical/horizontal separators.
- private static void embedPositionDetectionPatternsAndSeparators(ByteMatrix matrix) throws WriterException {
- // Embed three big squares at corners.
- int pdpWidth = POSITION_DETECTION_PATTERN[0].length;
- // Left top corner.
- embedPositionDetectionPattern(0, 0, matrix);
- // Right top corner.
- embedPositionDetectionPattern(matrix.getWidth() - pdpWidth, 0, matrix);
- // Left bottom corner.
- embedPositionDetectionPattern(0, matrix.getWidth() - pdpWidth, matrix);
-
- // Embed horizontal separation patterns around the squares.
- int hspWidth = HORIZONTAL_SEPARATION_PATTERN[0].length;
- // Left top corner.
- embedHorizontalSeparationPattern(0, hspWidth - 1, matrix);
- // Right top corner.
- embedHorizontalSeparationPattern(matrix.getWidth() - hspWidth,
- hspWidth - 1, matrix);
- // Left bottom corner.
- embedHorizontalSeparationPattern(0, matrix.getWidth() - hspWidth, matrix);
-
- // Embed vertical separation patterns around the squares.
- int vspSize = VERTICAL_SEPARATION_PATTERN.length;
- // Left top corner.
- embedVerticalSeparationPattern(vspSize, 0, matrix);
- // Right top corner.
- embedVerticalSeparationPattern(matrix.getHeight() - vspSize - 1, 0, matrix);
- // Left bottom corner.
- embedVerticalSeparationPattern(vspSize, matrix.getHeight() - vspSize,
- matrix);
- }
-
- // Embed position adjustment patterns if need be.
- private static void maybeEmbedPositionAdjustmentPatterns(int version, ByteMatrix matrix)
- throws WriterException {
- if (version < 2) { // The patterns appear if version >= 2
- return;
- }
- int index = version - 1;
- int[] coordinates = POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[index];
- int numCoordinates = POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[index].length;
- for (int i = 0; i < numCoordinates; ++i) {
- for (int j = 0; j < numCoordinates; ++j) {
- int y = coordinates[i];
- int x = coordinates[j];
- if (x == -1 || y == -1) {
- continue;
- }
- // If the cell is unset, we embed the position adjustment pattern here.
- if (isEmpty(matrix.get(x, y))) {
- // -2 is necessary since the x/y coordinates point to the center of the pattern, not the
- // left top corner.
- embedPositionAdjustmentPattern(x - 2, y - 2, matrix);
- }
- }
- }
- }
-
-}
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/QRCode.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/QRCode.java
deleted file mode 100644
index 05c818513..000000000
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/QRCode.java
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * Copyright 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.qrcode.encoder;
-
-import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
-import com.google.zxing.qrcode.decoder.Mode;
-
-/**
- * @author satorux@google.com (Satoru Takabayashi) - creator
- * @author dswitkin@google.com (Daniel Switkin) - ported from C++
- */
-public final class QRCode {
-
- public static final int NUM_MASK_PATTERNS = 8;
-
- private Mode mode;
- private ErrorCorrectionLevel ecLevel;
- private int version;
- private int matrixWidth;
- private int maskPattern;
- private int numTotalBytes;
- private int numDataBytes;
- private int numECBytes;
- private int numRSBlocks;
- private ByteMatrix matrix;
-
- public QRCode() {
- mode = null;
- ecLevel = null;
- version = -1;
- matrixWidth = -1;
- maskPattern = -1;
- numTotalBytes = -1;
- numDataBytes = -1;
- numECBytes = -1;
- numRSBlocks = -1;
- matrix = null;
- }
-
- // Mode of the QR Code.
- public Mode getMode() {
- return mode;
- }
-
- // Error correction level of the QR Code.
- public ErrorCorrectionLevel getECLevel() {
- return ecLevel;
- }
-
- // Version of the QR Code. The bigger size, the bigger version.
- public int getVersion() {
- return version;
- }
-
- // ByteMatrix width of the QR Code.
- public int getMatrixWidth() {
- return matrixWidth;
- }
-
- // Mask pattern of the QR Code.
- public int getMaskPattern() {
- return maskPattern;
- }
-
- // Number of total bytes in the QR Code.
- public int getNumTotalBytes() {
- return numTotalBytes;
- }
-
- // Number of data bytes in the QR Code.
- public int getNumDataBytes() {
- return numDataBytes;
- }
-
- // Number of error correction bytes in the QR Code.
- public int getNumECBytes() {
- return numECBytes;
- }
-
- // Number of Reedsolomon blocks in the QR Code.
- public int getNumRSBlocks() {
- return numRSBlocks;
- }
-
- // ByteMatrix data of the QR Code.
- public ByteMatrix getMatrix() {
- return matrix;
- }
-
-
- // Return the value of the module (cell) pointed by "x" and "y" in the matrix of the QR Code. They
- // call cells in the matrix "modules". 1 represents a black cell, and 0 represents a white cell.
- public int at(int x, int y) {
- // The value must be zero or one.
- int value = matrix.get(x, y);
- if (!(value == 0 || value == 1)) {
- // this is really like an assert... not sure what better exception to use?
- throw new RuntimeException("Bad value");
- }
- return value;
- }
-
- // Checks all the member variables are set properly. Returns true on success. Otherwise, returns
- // false.
- public boolean isValid() {
- return
- // First check if all version are not uninitialized.
- mode != null &&
- ecLevel != null &&
- version != -1 &&
- matrixWidth != -1 &&
- maskPattern != -1 &&
- numTotalBytes != -1 &&
- numDataBytes != -1 &&
- numECBytes != -1 &&
- numRSBlocks != -1 &&
- // Then check them in other ways..
- isValidMaskPattern(maskPattern) &&
- numTotalBytes == numDataBytes + numECBytes &&
- // ByteMatrix stuff.
- matrix != null &&
- matrixWidth == matrix.getWidth() &&
- // See 7.3.1 of JISX0510:2004 (p.5).
- matrix.getWidth() == matrix.getHeight(); // Must be square.
- }
-
- // Return debug String.
- public String toString() {
- StringBuffer result = new StringBuffer(200);
- result.append("<<\n");
- result.append(" mode: ");
- result.append(mode);
- result.append("\n ecLevel: ");
- result.append(ecLevel);
- result.append("\n version: ");
- result.append(version);
- result.append("\n matrixWidth: ");
- result.append(matrixWidth);
- result.append("\n maskPattern: ");
- result.append(maskPattern);
- result.append("\n numTotalBytes: ");
- result.append(numTotalBytes);
- result.append("\n numDataBytes: ");
- result.append(numDataBytes);
- result.append("\n numECBytes: ");
- result.append(numECBytes);
- result.append("\n numRSBlocks: ");
- result.append(numRSBlocks);
- if (matrix == null) {
- result.append("\n matrix: null\n");
- } else {
- result.append("\n matrix:\n");
- result.append(matrix.toString());
- }
- result.append(">>\n");
- return result.toString();
- }
-
- public void setMode(Mode value) {
- mode = value;
- }
-
- public void setECLevel(ErrorCorrectionLevel value) {
- ecLevel = value;
- }
-
- public void setVersion(int value) {
- version = value;
- }
-
- public void setMatrixWidth(int value) {
- matrixWidth = value;
- }
-
- public void setMaskPattern(int value) {
- maskPattern = value;
- }
-
- public void setNumTotalBytes(int value) {
- numTotalBytes = value;
- }
-
- public void setNumDataBytes(int value) {
- numDataBytes = value;
- }
-
- public void setNumECBytes(int value) {
- numECBytes = value;
- }
-
- public void setNumRSBlocks(int value) {
- numRSBlocks = value;
- }
-
- // This takes ownership of the 2D array.
- public void setMatrix(ByteMatrix value) {
- matrix = value;
- }
-
- // Check if "mask_pattern" is valid.
- public static boolean isValidMaskPattern(int maskPattern) {
- return maskPattern >= 0 && maskPattern < NUM_MASK_PATTERNS;
- }
-
- // Return true if the all values in the matrix are binary numbers.
- //
- // JAVAPORT: This is going to be super expensive and unnecessary, we should not call this in
- // production. I'm leaving it because it may be useful for testing. It should be removed entirely
- // if ByteMatrix is changed never to contain a -1.
- /*
- private static boolean EverythingIsBinary(final ByteMatrix matrix) {
- for (int y = 0; y < matrix.height(); ++y) {
- for (int x = 0; x < matrix.width(); ++x) {
- int value = matrix.get(y, x);
- if (!(value == 0 || value == 1)) {
- // Found non zero/one value.
- return false;
- }
- }
- }
- return true;
- }
- */
-
-}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java
index 4bffd9f3d..13926d172 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java
@@ -90,7 +90,7 @@ public class KeyViewActivity extends SherlockFragmentActivity implements CreateN
Intent intent = getIntent();
mDataUri = intent.getData();
if (mDataUri == null) {
- Log.e(Constants.TAG, "Intent data missing. Should be Uri of app!");
+ Log.e(Constants.TAG, "Intent data missing. Should be Uri of key!");
finish();
return;
} else {
diff --git a/README.md b/README.md
index 30c3f01e5..059cbb806 100644
--- a/README.md
+++ b/README.md
@@ -28,8 +28,9 @@ 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 "OpenPGP-Keychain"
-5. OpenPGP-Kechain can now be build
+4. File -> Import -> Android -> Existing Android Code Into Workspace, choose "libraries/zxing"
+5. File -> Import -> Android -> Existing Android Code Into Workspace, choose "OpenPGP-Keychain"
+6. OpenPGP-Kechain can now be build
# Keychain API
diff --git a/libraries/StickyListHeaders/build.gradle b/libraries/StickyListHeaders/build.gradle
deleted file mode 100644
index 849b1323c..000000000
--- a/libraries/StickyListHeaders/build.gradle
+++ /dev/null
@@ -1,24 +0,0 @@
-buildscript {
- repositories {
- mavenCentral()
- }
-
- dependencies {
- classpath 'com.android.tools.build:gradle:0.6.+'
- }
-}
-
-def isReleaseBuild() {
- return version.contains("SNAPSHOT") == false
-}
-
-allprojects {
- version = VERSION_NAME
- group = GROUP
-
- repositories {
- mavenCentral()
- }
-}
-
-apply plugin: 'android-reporting'
diff --git a/libraries/StickyListHeaders/demo.gif b/libraries/StickyListHeaders/demo.gif
deleted file mode 100644
index d73e3297b..000000000
Binary files a/libraries/StickyListHeaders/demo.gif and /dev/null differ
diff --git a/libraries/StickyListHeaders/gradle/wrapper/gradle-wrapper.jar b/libraries/StickyListHeaders/gradle/wrapper/gradle-wrapper.jar
deleted file mode 100644
index 8c0fb64a8..000000000
Binary files a/libraries/StickyListHeaders/gradle/wrapper/gradle-wrapper.jar and /dev/null differ
diff --git a/libraries/StickyListHeaders/gradle/wrapper/gradle-wrapper.properties b/libraries/StickyListHeaders/gradle/wrapper/gradle-wrapper.properties
deleted file mode 100644
index 0a7effc64..000000000
--- a/libraries/StickyListHeaders/gradle/wrapper/gradle-wrapper.properties
+++ /dev/null
@@ -1,6 +0,0 @@
-#Tue Nov 19 08:36:06 CET 2013
-distributionBase=GRADLE_USER_HOME
-distributionPath=wrapper/dists
-zipStoreBase=GRADLE_USER_HOME
-zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.8-all.zip
diff --git a/libraries/StickyListHeaders/gradlew b/libraries/StickyListHeaders/gradlew
deleted file mode 100644
index 91a7e269e..000000000
--- a/libraries/StickyListHeaders/gradlew
+++ /dev/null
@@ -1,164 +0,0 @@
-#!/usr/bin/env bash
-
-##############################################################################
-##
-## Gradle start up script for UN*X
-##
-##############################################################################
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
-
-APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
-
-# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
-
-warn ( ) {
- echo "$*"
-}
-
-die ( ) {
- echo
- echo "$*"
- echo
- exit 1
-}
-
-# OS specific support (must be 'true' or 'false').
-cygwin=false
-msys=false
-darwin=false
-case "`uname`" in
- CYGWIN* )
- cygwin=true
- ;;
- Darwin* )
- darwin=true
- ;;
- MINGW* )
- msys=true
- ;;
-esac
-
-# For Cygwin, ensure paths are in UNIX format before anything is touched.
-if $cygwin ; then
- [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
-fi
-
-# Attempt to set APP_HOME
-# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG=`dirname "$PRG"`"/$link"
- fi
-done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >&-
-APP_HOME="`pwd -P`"
-cd "$SAVED" >&-
-
-CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
-
-# Determine the Java command to use to start the JVM.
-if [ -n "$JAVA_HOME" ] ; then
- if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
- # IBM's JDK on AIX uses strange locations for the executables
- JAVACMD="$JAVA_HOME/jre/sh/java"
- else
- JAVACMD="$JAVA_HOME/bin/java"
- fi
- if [ ! -x "$JAVACMD" ] ; then
- die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
- fi
-else
- JAVACMD="java"
- which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
-fi
-
-# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
- MAX_FD_LIMIT=`ulimit -H -n`
- if [ $? -eq 0 ] ; then
- if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
- MAX_FD="$MAX_FD_LIMIT"
- fi
- ulimit -n $MAX_FD
- if [ $? -ne 0 ] ; then
- warn "Could not set maximum file descriptor limit: $MAX_FD"
- fi
- else
- warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
- fi
-fi
-
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
- GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-fi
-
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin ; then
- APP_HOME=`cygpath --path --mixed "$APP_HOME"`
- CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
-
- # We build the pattern for arguments to be converted via cygpath
- ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
- SEP=""
- for dir in $ROOTDIRSRAW ; do
- ROOTDIRS="$ROOTDIRS$SEP$dir"
- SEP="|"
- done
- OURCYGPATTERN="(^($ROOTDIRS))"
- # Add a user-defined pattern to the cygpath arguments
- if [ "$GRADLE_CYGPATTERN" != "" ] ; then
- OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
- fi
- # Now convert the arguments - kludge to limit ourselves to /bin/sh
- i=0
- for arg in "$@" ; do
- CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
- CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
-
- if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
- eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
- else
- eval `echo args$i`="\"$arg\""
- fi
- i=$((i+1))
- done
- case $i in
- (0) set -- ;;
- (1) set -- "$args0" ;;
- (2) set -- "$args0" "$args1" ;;
- (3) set -- "$args0" "$args1" "$args2" ;;
- (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
- esac
-fi
-
-# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
-function splitJvmOpts() {
- JVM_OPTS=("$@")
-}
-eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
-JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
-
-exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/libraries/StickyListHeaders/gradlew.bat b/libraries/StickyListHeaders/gradlew.bat
deleted file mode 100644
index aec99730b..000000000
--- a/libraries/StickyListHeaders/gradlew.bat
+++ /dev/null
@@ -1,90 +0,0 @@
-@if "%DEBUG%" == "" @echo off
-@rem ##########################################################################
-@rem
-@rem Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
-
-set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
-
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto init
-
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:init
-@rem Get command-line arguments, handling Windowz variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-if "%@eval[2+2]" == "4" goto 4NT_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-goto execute
-
-:4NT_args
-@rem Get arguments from the 4NT Shell from JP Software
-set CMD_LINE_ARGS=%$
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
-
-:end
-@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
diff --git a/libraries/StickyListHeaders/library/build.gradle b/libraries/StickyListHeaders/library/build.gradle
index a15de94c2..21050fc98 100644
--- a/libraries/StickyListHeaders/library/build.gradle
+++ b/libraries/StickyListHeaders/library/build.gradle
@@ -12,5 +12,3 @@ android {
}
}
}
-
-apply from: 'https://raw.github.com/chrisbanes/gradle-mvn-push/eaa6b5404b7594e6c23b884fdc5795f545db55dd/gradle-mvn-push.gradle'
diff --git a/libraries/StickyListHeaders/sample/AndroidManifest.xml b/libraries/StickyListHeaders/sample/AndroidManifest.xml
deleted file mode 100644
index 4bf61a441..000000000
--- a/libraries/StickyListHeaders/sample/AndroidManifest.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/libraries/StickyListHeaders/sample/build.gradle b/libraries/StickyListHeaders/sample/build.gradle
deleted file mode 100644
index b620008e3..000000000
--- a/libraries/StickyListHeaders/sample/build.gradle
+++ /dev/null
@@ -1,23 +0,0 @@
-apply plugin: 'android'
-
-repositories {
- mavenCentral()
-}
-dependencies {
- compile project(':library')
- compile 'com.android.support:appcompat-v7:19.0.+'
- compile 'com.android.support:support-v4:19.0.0'
-}
-
-android {
- compileSdkVersion 19
- buildToolsVersion '19.0.0'
-
- sourceSets {
- main {
- manifest.srcFile 'AndroidManifest.xml'
- java.srcDirs = ['src']
- res.srcDirs = ['res']
- }
- }
-}
diff --git a/libraries/StickyListHeaders/sample/libs/android-support-v4.jar b/libraries/StickyListHeaders/sample/libs/android-support-v4.jar
deleted file mode 100644
index cf12d2839..000000000
Binary files a/libraries/StickyListHeaders/sample/libs/android-support-v4.jar and /dev/null differ
diff --git a/libraries/StickyListHeaders/sample/project.properties b/libraries/StickyListHeaders/sample/project.properties
deleted file mode 100644
index a6cf15dae..000000000
--- a/libraries/StickyListHeaders/sample/project.properties
+++ /dev/null
@@ -1,15 +0,0 @@
-# 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.reference.1=../library
diff --git a/libraries/StickyListHeaders/sample/res/drawable-hdpi/ic_drawer.png b/libraries/StickyListHeaders/sample/res/drawable-hdpi/ic_drawer.png
deleted file mode 100644
index 6614ea4f4..000000000
Binary files a/libraries/StickyListHeaders/sample/res/drawable-hdpi/ic_drawer.png and /dev/null differ
diff --git a/libraries/StickyListHeaders/sample/res/drawable-hdpi/ic_launcher.png b/libraries/StickyListHeaders/sample/res/drawable-hdpi/ic_launcher.png
deleted file mode 100644
index 96a442e5b..000000000
Binary files a/libraries/StickyListHeaders/sample/res/drawable-hdpi/ic_launcher.png and /dev/null differ
diff --git a/libraries/StickyListHeaders/sample/res/drawable-ldpi/ic_launcher.png b/libraries/StickyListHeaders/sample/res/drawable-ldpi/ic_launcher.png
deleted file mode 100644
index 99238729d..000000000
Binary files a/libraries/StickyListHeaders/sample/res/drawable-ldpi/ic_launcher.png and /dev/null differ
diff --git a/libraries/StickyListHeaders/sample/res/drawable-mdpi/ic_drawer.png b/libraries/StickyListHeaders/sample/res/drawable-mdpi/ic_drawer.png
deleted file mode 100644
index b05c026c1..000000000
Binary files a/libraries/StickyListHeaders/sample/res/drawable-mdpi/ic_drawer.png and /dev/null differ
diff --git a/libraries/StickyListHeaders/sample/res/drawable-mdpi/ic_launcher.png b/libraries/StickyListHeaders/sample/res/drawable-mdpi/ic_launcher.png
deleted file mode 100644
index 359047dfa..000000000
Binary files a/libraries/StickyListHeaders/sample/res/drawable-mdpi/ic_launcher.png and /dev/null differ
diff --git a/libraries/StickyListHeaders/sample/res/drawable-xhdpi/ic_drawer.png b/libraries/StickyListHeaders/sample/res/drawable-xhdpi/ic_drawer.png
deleted file mode 100644
index bcf49dd73..000000000
Binary files a/libraries/StickyListHeaders/sample/res/drawable-xhdpi/ic_drawer.png and /dev/null differ
diff --git a/libraries/StickyListHeaders/sample/res/drawable-xhdpi/ic_launcher.png b/libraries/StickyListHeaders/sample/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100644
index 71c6d760f..000000000
Binary files a/libraries/StickyListHeaders/sample/res/drawable-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/libraries/StickyListHeaders/sample/res/drawable/header_selector.xml b/libraries/StickyListHeaders/sample/res/drawable/header_selector.xml
deleted file mode 100644
index 5dfb8265c..000000000
--- a/libraries/StickyListHeaders/sample/res/drawable/header_selector.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/libraries/StickyListHeaders/sample/res/layout/header.xml b/libraries/StickyListHeaders/sample/res/layout/header.xml
deleted file mode 100644
index 177e40c4e..000000000
--- a/libraries/StickyListHeaders/sample/res/layout/header.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/libraries/StickyListHeaders/sample/res/layout/list_footer.xml b/libraries/StickyListHeaders/sample/res/layout/list_footer.xml
deleted file mode 100644
index 4fa22c1f3..000000000
--- a/libraries/StickyListHeaders/sample/res/layout/list_footer.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/libraries/StickyListHeaders/sample/res/layout/list_header.xml b/libraries/StickyListHeaders/sample/res/layout/list_header.xml
deleted file mode 100644
index 97d46c69b..000000000
--- a/libraries/StickyListHeaders/sample/res/layout/list_header.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/libraries/StickyListHeaders/sample/res/layout/main.xml b/libraries/StickyListHeaders/sample/res/layout/main.xml
deleted file mode 100644
index 2d22c3266..000000000
--- a/libraries/StickyListHeaders/sample/res/layout/main.xml
+++ /dev/null
@@ -1,114 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/libraries/StickyListHeaders/sample/res/layout/test_list_item_layout.xml b/libraries/StickyListHeaders/sample/res/layout/test_list_item_layout.xml
deleted file mode 100644
index 0f8efc32e..000000000
--- a/libraries/StickyListHeaders/sample/res/layout/test_list_item_layout.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
diff --git a/libraries/StickyListHeaders/sample/res/values/arrays.xml b/libraries/StickyListHeaders/sample/res/values/arrays.xml
deleted file mode 100755
index 5cd71de49..000000000
--- a/libraries/StickyListHeaders/sample/res/values/arrays.xml
+++ /dev/null
@@ -1,230 +0,0 @@
-
-
-
-
-
- Afghanistan
- Albania
- Algeria
- Andorra
- Angola
- Antigua & Barbuda
- Argentina
- Armenia
- Australia
- Austria
- Azerbaijan
-
- Bahamas
- Bahrain
- Bangladesh
- Barbados
- Belarus
- Belgium
- Belize
- Benin
- Bhutan
- Bolivia
- Bosnia and Herzegovina
- Botswana
- Brazil
- Brunei
- Bulgaria
- Burkina Faso
- Burma
- Burundi
-
- Cambodia
- Cameroon
- Canada
- Cape Verde
- Central African Republic
- Chad
- Chile
- China
- Colombia
- Comoros
- Congo, Democratic Republic of the
- Congo, Republic of the
- Costa Rica
- Cote d\'voire
- Croatia
- Cuba
- Cyprus
- Czech Republic
-
- Denmark
- Djibouti
- Dominica
- Dominican Republic
-
- Ecuador
- Egypt
- El Salvador
- Equatorial Guinea
- Eritrea
- Estonia
- Ethiopia
-
- Fiji
- Finland
- France
-
- Gabon
- Gambia, The
- Georgia
- Ghana
- Greece
- Grenada
- Guatemala
- Guinea
- Guinea-Bissau
- Guyana
-
- Haiti
- Holy See
- Honduras
- Hong Kong
- Hungary
-
- Iceland
- India
- Indonesia
- Iran
- Iraq
- Ireland
- Israel
- Italy
-
- Jamaica
- Japan
- Jordan
-
- Kazakhstan
- Kenya
- Kiribati
- Korea, North
- Korea, South
- Kosovo
- Kuwait
- Kyrgyzstan
-
- Laos
- Latvia
- Lebanon
- Lesotho
- Liberia
- Libya
- Liechtenstein
- Lithuania
- Luxembourg
-
- Macau
- Macedonia
- Madagascar
- Malawi
- Malaysia
- Maldives
- Mali
- Malta
- Marshall Islands
- Mauritania
- Mauritius
- Mexico
- Micronesia
- Moldova
- Monaco
- Mongolia
- Montenegro
- Morocco
- Mozambique
-
- Namibia
- Nauru
- Nepal
- Netherlands
- Netherlands Antilles
- New Zealand
- Nicaragua
- Niger
- Nigeria
- North Korea
- Norway
-
- Oman
-
- Pakistan
- Palau
- Palestinian Territories
- Panama
- Papua New Guinea
- Paraguay
- Peru
- Philippines
- Poland
- Portugal
-
- Qatar
-
- Romania
- Russia
- Rwanda
-
- Saint Kitts and Nevis
- Saint Lucia
- Saint Vincent and the Grenadines
- Samoa
- San Marino
- Sao Tome and Principe
- Saudi Arabia
- Senegal
- Serbia
- Seychelles
- Sierra Leone
- Singapore
- Slovakia
- Slovenia
- Solomon Islands
- Somalia
- South Africa
- South Korea
- South Sudan
- Spain
- Sri Lanka
- Sudan
- Suriname
- Swaziland
- Sweden
- Switzerland
- Syria
-
- Taiwan
- Tajikistan
- Tanzania
- Thailand
- Timor-Leste
- Togo
- Tonga
- Trinidad and Tobago
- Tunisia
- Turkey
- Turkmenistan
- Tuvalu
-
- Uganda
- Ukraine
- United Arab Emirates
- United Kingdom
- Uruguay
- Uzbekistan
-
- Vanuatu
- Venezuela
- Vietnam
-
- Yemen
-
- Zambia
- Zimbabwe
-
-
diff --git a/libraries/StickyListHeaders/sample/res/values/colors.xml b/libraries/StickyListHeaders/sample/res/values/colors.xml
deleted file mode 100644
index 601687e2e..000000000
--- a/libraries/StickyListHeaders/sample/res/values/colors.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
- #ffe74c3c
- #ffc0392b
- #FFDDDDDD
-
-
\ No newline at end of file
diff --git a/libraries/StickyListHeaders/sample/res/values/strings.xml b/libraries/StickyListHeaders/sample/res/values/strings.xml
deleted file mode 100644
index 6beadbe75..000000000
--- a/libraries/StickyListHeaders/sample/res/values/strings.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
- StickyListHeaders Sample
- Restore
- Update
- Clear
- Empty Adapter
- Drawer Open
- Drawer Close
- OPTIONS
- ACTIONS
- Restore list
- Update list
- Clear list
- Sticky header
- Fade header
- Draw behind header
- Fast scroll
-
-
\ No newline at end of file
diff --git a/libraries/StickyListHeaders/sample/res/values/style.xml b/libraries/StickyListHeaders/sample/res/values/style.xml
deleted file mode 100644
index b34634b58..000000000
--- a/libraries/StickyListHeaders/sample/res/values/style.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/libraries/StickyListHeaders/sample/src/se/emilsjolander/stickylistheaders/TestActivity.java b/libraries/StickyListHeaders/sample/src/se/emilsjolander/stickylistheaders/TestActivity.java
deleted file mode 100644
index dc795ff4e..000000000
--- a/libraries/StickyListHeaders/sample/src/se/emilsjolander/stickylistheaders/TestActivity.java
+++ /dev/null
@@ -1,169 +0,0 @@
-package se.emilsjolander.stickylistheaders;
-
-import android.annotation.TargetApi;
-import android.content.res.Configuration;
-import android.os.Build;
-import android.os.Bundle;
-import android.support.v4.app.ActionBarDrawerToggle;
-import android.support.v4.widget.DrawerLayout;
-import android.support.v7.app.ActionBarActivity;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.*;
-
-/**
- * @author Emil Sjölander
- */
-public class TestActivity extends ActionBarActivity implements
- AdapterView.OnItemClickListener, StickyListHeadersListView.OnHeaderClickListener,
- StickyListHeadersListView.OnStickyHeaderOffsetChangedListener {
-
- private TestBaseAdapter mAdapter;
- private DrawerLayout mDrawerLayout;
- private ActionBarDrawerToggle mDrawerToggle;
- private boolean fadeHeader = true;
-
- private StickyListHeadersListView stickyList;
-
- private Button restoreButton;
- private Button updateButton;
- private Button clearButton;
-
- private CheckBox stickyCheckBox;
- private CheckBox fadeCheckBox;
- private CheckBox drawBehindCheckBox;
- private CheckBox fastScrollCheckBox;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
-
- mAdapter = new TestBaseAdapter(this);
-
- stickyList = (StickyListHeadersListView) findViewById(R.id.list);
- stickyList.setOnItemClickListener(this);
- stickyList.setOnHeaderClickListener(this);
- stickyList.setOnStickyHeaderOffsetChangedListener(this);
-// mStickyList.addHeaderView(inflater.inflate(R.layout.list_header, null));
-// mStickyList.addFooterView(inflater.inflate(R.layout.list_footer, null));
- stickyList.setEmptyView(findViewById(R.id.empty));
- stickyList.setDrawingListUnderStickyHeader(true);
- stickyList.setAreHeadersSticky(true);
- stickyList.setAdapter(mAdapter);
-
- mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
- mDrawerToggle = new ActionBarDrawerToggle(
- this, /* host Activity */
- mDrawerLayout, /* DrawerLayout object */
- R.drawable.ic_drawer, /* nav drawer icon to replace 'Up' caret */
- R.string.drawer_open, /* "open drawer" description */
- R.string.drawer_close /* "close drawer" description */
- );
-
- // Set the drawer toggle as the DrawerListener
- mDrawerLayout.setDrawerListener(mDrawerToggle);
-
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- getSupportActionBar().setHomeButtonEnabled(true);
-
- restoreButton = (Button) findViewById(R.id.restore_button);
- restoreButton.setOnClickListener(buttonListener);
- updateButton = (Button) findViewById(R.id.update_button);
- updateButton.setOnClickListener(buttonListener);
- clearButton = (Button) findViewById(R.id.clear_button);
- clearButton.setOnClickListener(buttonListener);
-
- stickyCheckBox = (CheckBox) findViewById(R.id.sticky_checkBox);
- stickyCheckBox.setOnCheckedChangeListener(checkBoxListener);
- fadeCheckBox = (CheckBox) findViewById(R.id.fade_checkBox);
- fadeCheckBox.setOnCheckedChangeListener(checkBoxListener);
- drawBehindCheckBox = (CheckBox) findViewById(R.id.draw_behind_checkBox);
- drawBehindCheckBox.setOnCheckedChangeListener(checkBoxListener);
- fastScrollCheckBox = (CheckBox) findViewById(R.id.fast_scroll_checkBox);
- fastScrollCheckBox.setOnCheckedChangeListener(checkBoxListener);
- }
-
- @Override
- protected void onPostCreate(Bundle savedInstanceState) {
- super.onPostCreate(savedInstanceState);
- // Sync the toggle state after onRestoreInstanceState has occurred.
- mDrawerToggle.syncState();
- }
-
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- mDrawerToggle.onConfigurationChanged(newConfig);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (mDrawerToggle.onOptionsItemSelected(item)) {
- return true;
- }
-
- return super.onOptionsItemSelected(item);
- }
-
- CompoundButton.OnCheckedChangeListener checkBoxListener = new CompoundButton.OnCheckedChangeListener() {
-
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- switch (buttonView.getId()) {
- case R.id.sticky_checkBox:
- stickyList.setAreHeadersSticky(isChecked);
- break;
- case R.id.fade_checkBox:
- fadeHeader = isChecked;
- break;
- case R.id.draw_behind_checkBox:
- stickyList.setDrawingListUnderStickyHeader(isChecked);
- break;
- case R.id.fast_scroll_checkBox:
- stickyList.setFastScrollEnabled(isChecked);
- stickyList.setFastScrollAlwaysVisible(isChecked);
- break;
- }
- }
- };
-
- View.OnClickListener buttonListener = new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- switch (view.getId()) {
- case R.id.restore_button:
- mAdapter.restore();
- break;
- case R.id.update_button:
- mAdapter.notifyDataSetChanged();
- break;
- case R.id.clear_button:
- mAdapter.clear();
- break;
- }
- }
- };
-
- @Override
- public void onItemClick(AdapterView> parent, View view, int position,
- long id) {
- Toast.makeText(this, "Item " + position + " clicked!",
- Toast.LENGTH_SHORT).show();
- }
-
- @Override
- public void onHeaderClick(StickyListHeadersListView l, View header,
- int itemPosition, long headerId, boolean currentlySticky) {
- Toast.makeText(this, "Header " + headerId + " currentlySticky ? " + currentlySticky,
- Toast.LENGTH_SHORT).show();
- }
-
- @Override
- @TargetApi(Build.VERSION_CODES.HONEYCOMB)
- public void onStickyHeaderOffsetChanged(StickyListHeadersListView l, View header, int offset) {
- if (fadeHeader && Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- header.setAlpha(1 - (offset / (float) header.getMeasuredHeight()));
- }
- }
-}
\ No newline at end of file
diff --git a/libraries/StickyListHeaders/sample/src/se/emilsjolander/stickylistheaders/TestBaseAdapter.java b/libraries/StickyListHeaders/sample/src/se/emilsjolander/stickylistheaders/TestBaseAdapter.java
deleted file mode 100644
index 777b7bd57..000000000
--- a/libraries/StickyListHeaders/sample/src/se/emilsjolander/stickylistheaders/TestBaseAdapter.java
+++ /dev/null
@@ -1,169 +0,0 @@
-package se.emilsjolander.stickylistheaders;
-
-import java.util.ArrayList;
-
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.SectionIndexer;
-import android.widget.TextView;
-
-/**
- * @author Emil Sjölander
- */
-public class TestBaseAdapter extends BaseAdapter implements
- StickyListHeadersAdapter, SectionIndexer {
-
- private final Context mContext;
- private String[] mCountries;
- private int[] mSectionIndices;
- private Character[] mSectionLetters;
- private LayoutInflater mInflater;
-
- public TestBaseAdapter(Context context) {
- mContext = context;
- mInflater = LayoutInflater.from(context);
- mCountries = context.getResources().getStringArray(R.array.countries);
- mSectionIndices = getSectionIndices();
- mSectionLetters = getSectionLetters();
- }
-
- private int[] getSectionIndices() {
- ArrayList sectionIndices = new ArrayList();
- char lastFirstChar = mCountries[0].charAt(0);
- sectionIndices.add(0);
- for (int i = 1; i < mCountries.length; i++) {
- if (mCountries[i].charAt(0) != lastFirstChar) {
- lastFirstChar = mCountries[i].charAt(0);
- sectionIndices.add(i);
- }
- }
- int[] sections = new int[sectionIndices.size()];
- for (int i = 0; i < sectionIndices.size(); i++) {
- sections[i] = sectionIndices.get(i);
- }
- return sections;
- }
-
- private Character[] getSectionLetters() {
- Character[] letters = new Character[mSectionIndices.length];
- for (int i = 0; i < mSectionIndices.length; i++) {
- letters[i] = mCountries[mSectionIndices[i]].charAt(0);
- }
- return letters;
- }
-
- @Override
- public int getCount() {
- return mCountries.length;
- }
-
- @Override
- public Object getItem(int position) {
- return mCountries[position];
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- ViewHolder holder;
-
- if (convertView == null) {
- holder = new ViewHolder();
- convertView = mInflater.inflate(R.layout.test_list_item_layout, parent, false);
- holder.text = (TextView) convertView.findViewById(R.id.text);
- convertView.setTag(holder);
- } else {
- holder = (ViewHolder) convertView.getTag();
- }
-
- holder.text.setText(mCountries[position]);
-
- return convertView;
- }
-
- @Override
- public View getHeaderView(int position, View convertView, ViewGroup parent) {
- HeaderViewHolder holder;
-
- if (convertView == null) {
- holder = new HeaderViewHolder();
- convertView = mInflater.inflate(R.layout.header, parent, false);
- holder.text = (TextView) convertView.findViewById(R.id.text1);
- convertView.setTag(holder);
- } else {
- holder = (HeaderViewHolder) convertView.getTag();
- }
-
- // set header text as first char in name
- CharSequence headerChar = mCountries[position].subSequence(0, 1);
- holder.text.setText(headerChar);
-
- return convertView;
- }
-
- /**
- * Remember that these have to be static, postion=1 should always return
- * the same Id that is.
- */
- @Override
- public long getHeaderId(int position) {
- // return the first character of the country as ID because this is what
- // headers are based upon
- return mCountries[position].subSequence(0, 1).charAt(0);
- }
-
- @Override
- public int getPositionForSection(int section) {
- if (section >= mSectionIndices.length) {
- section = mSectionIndices.length - 1;
- } else if (section < 0) {
- section = 0;
- }
- return mSectionIndices[section];
- }
-
- @Override
- public int getSectionForPosition(int position) {
- for (int i = 0; i < mSectionIndices.length; i++) {
- if (position < mSectionIndices[i]) {
- return i - 1;
- }
- }
- return mSectionIndices.length - 1;
- }
-
- @Override
- public Object[] getSections() {
- return mSectionLetters;
- }
-
- public void clear() {
- mCountries = new String[0];
- mSectionIndices = new int[0];
- mSectionLetters = new Character[0];
- notifyDataSetChanged();
- }
-
- public void restore() {
- mCountries = mContext.getResources().getStringArray(R.array.countries);
- mSectionIndices = getSectionIndices();
- mSectionLetters = getSectionLetters();
- notifyDataSetChanged();
- }
-
- class HeaderViewHolder {
- TextView text;
- }
-
- class ViewHolder {
- TextView text;
- }
-
-}
diff --git a/libraries/StickyListHeaders/sample/src/se/emilsjolander/stickylistheaders/views/UnderlineTextView.java b/libraries/StickyListHeaders/sample/src/se/emilsjolander/stickylistheaders/views/UnderlineTextView.java
deleted file mode 100644
index c202c00b8..000000000
--- a/libraries/StickyListHeaders/sample/src/se/emilsjolander/stickylistheaders/views/UnderlineTextView.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package se.emilsjolander.stickylistheaders.views;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.widget.TextView;
-
-/**
- * @author Eric Frohnhoefer
- */
-public class UnderlineTextView extends TextView {
- private final Paint mPaint = new Paint();
- private int mUnderlineHeight = 0;
-
- public UnderlineTextView(Context context) {
- this(context, null);
- }
-
- public UnderlineTextView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public UnderlineTextView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- init(context, attrs);
- }
-
- private void init(Context context, AttributeSet attrs) {
- Resources r = getResources();
- mUnderlineHeight = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, r.getDisplayMetrics());
- }
-
- @Override
- public void setPadding(int left, int top, int right, int bottom) {
- super.setPadding(left, top, right, bottom + mUnderlineHeight);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- // Draw the underline the same color as the text
- mPaint.setColor(getTextColors().getDefaultColor());
- canvas.drawRect(0, getHeight() - mUnderlineHeight, getWidth(), getHeight(), mPaint);
- }
-}
diff --git a/libraries/StickyListHeaders/settings.gradle b/libraries/StickyListHeaders/settings.gradle
deleted file mode 100644
index 2c81ac7fd..000000000
--- a/libraries/StickyListHeaders/settings.gradle
+++ /dev/null
@@ -1,2 +0,0 @@
-include 'library'
-include 'sample'
diff --git a/libraries/zxing/AndroidManifest.xml b/libraries/zxing/AndroidManifest.xml
new file mode 100644
index 000000000..ef720bdb6
--- /dev/null
+++ b/libraries/zxing/AndroidManifest.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/libraries/zxing/build.gradle b/libraries/zxing/build.gradle
new file mode 100644
index 000000000..21050fc98
--- /dev/null
+++ b/libraries/zxing/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/project.properties b/libraries/zxing/project.properties
new file mode 100644
index 000000000..91d2b0246
--- /dev/null
+++ b/libraries/zxing/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/src/com/google/zxing/BarcodeFormat.java b/libraries/zxing/src/com/google/zxing/BarcodeFormat.java
new file mode 100644
index 000000000..1e5d47958
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/BarcodeFormat.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2007 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;
+
+import java.util.Hashtable;
+
+/**
+ * Enumerates barcode formats known to this package. Please keep alphabetized.
+ *
+ * @author Sean Owen
+ */
+public final class BarcodeFormat {
+
+ // No, we can't use an enum here. J2ME doesn't support it.
+
+ private static final Hashtable VALUES = new Hashtable();
+
+ /** Aztec 2D barcode format. */
+ public static final BarcodeFormat AZTEC = new BarcodeFormat("AZTEC");
+
+ /** CODABAR 1D format. */
+ public static final BarcodeFormat CODABAR = new BarcodeFormat("CODABAR");
+
+ /** Code 39 1D format. */
+ public static final BarcodeFormat CODE_39 = new BarcodeFormat("CODE_39");
+
+ /** Code 93 1D format. */
+ public static final BarcodeFormat CODE_93 = new BarcodeFormat("CODE_93");
+
+ /** Code 128 1D format. */
+ public static final BarcodeFormat CODE_128 = new BarcodeFormat("CODE_128");
+
+ /** Data Matrix 2D barcode format. */
+ public static final BarcodeFormat DATA_MATRIX = new BarcodeFormat("DATA_MATRIX");
+
+ /** EAN-8 1D format. */
+ public static final BarcodeFormat EAN_8 = new BarcodeFormat("EAN_8");
+
+ /** EAN-13 1D format. */
+ public static final BarcodeFormat EAN_13 = new BarcodeFormat("EAN_13");
+
+ /** ITF (Interleaved Two of Five) 1D format. */
+ public static final BarcodeFormat ITF = new BarcodeFormat("ITF");
+
+ /** PDF417 format. */
+ public static final BarcodeFormat PDF_417 = new BarcodeFormat("PDF_417");
+
+ /** QR Code 2D barcode format. */
+ public static final BarcodeFormat QR_CODE = new BarcodeFormat("QR_CODE");
+
+ /** RSS 14 */
+ public static final BarcodeFormat RSS_14 = new BarcodeFormat("RSS_14");
+
+ /** RSS EXPANDED */
+ public static final BarcodeFormat RSS_EXPANDED = new BarcodeFormat("RSS_EXPANDED");
+
+ /** UPC-A 1D format. */
+ public static final BarcodeFormat UPC_A = new BarcodeFormat("UPC_A");
+
+ /** UPC-E 1D format. */
+ public static final BarcodeFormat UPC_E = new BarcodeFormat("UPC_E");
+
+ /** UPC/EAN extension format. Not a stand-alone format. */
+ public static final BarcodeFormat UPC_EAN_EXTENSION = new BarcodeFormat("UPC_EAN_EXTENSION");
+
+ private final String name;
+
+ private BarcodeFormat(String name) {
+ this.name = name;
+ VALUES.put(name, this);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String toString() {
+ return name;
+ }
+
+ public static BarcodeFormat valueOf(String name) {
+ if (name == null || name.length() == 0) {
+ throw new IllegalArgumentException();
+ }
+ BarcodeFormat format = (BarcodeFormat) VALUES.get(name);
+ if (format == null) {
+ throw new IllegalArgumentException();
+ }
+ return format;
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/Binarizer.java b/libraries/zxing/src/com/google/zxing/Binarizer.java
new file mode 100644
index 000000000..912a3b556
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/Binarizer.java
@@ -0,0 +1,80 @@
+/*
+ * 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;
+
+import com.google.zxing.common.BitArray;
+import com.google.zxing.common.BitMatrix;
+
+/**
+ * This class hierarchy provides a set of methods to convert luminance data to 1 bit data.
+ * It allows the algorithm to vary polymorphically, for example allowing a very expensive
+ * thresholding technique for servers and a fast one for mobile. It also permits the implementation
+ * to vary, e.g. a JNI version for Android and a Java fallback version for other platforms.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+public abstract class Binarizer {
+
+ private final LuminanceSource source;
+
+ protected Binarizer(LuminanceSource source) {
+ if (source == null) {
+ throw new IllegalArgumentException("Source must be non-null.");
+ }
+ this.source = source;
+ }
+
+ public LuminanceSource getLuminanceSource() {
+ return source;
+ }
+
+ /**
+ * Converts one row of luminance data to 1 bit data. May actually do the conversion, or return
+ * cached data. Callers should assume this method is expensive and call it as seldom as possible.
+ * This method is intended for decoding 1D barcodes and may choose to apply sharpening.
+ * For callers which only examine one row of pixels at a time, the same BitArray should be reused
+ * and passed in with each call for performance. However it is legal to keep more than one row
+ * at a time if needed.
+ *
+ * @param y The row to fetch, 0 <= y < bitmap height.
+ * @param row An optional preallocated array. If null or too small, it will be ignored.
+ * If used, the Binarizer will call BitArray.clear(). Always use the returned object.
+ * @return The array of bits for this row (true means black).
+ */
+ public abstract BitArray getBlackRow(int y, BitArray row) throws NotFoundException;
+
+ /**
+ * Converts a 2D array of luminance data to 1 bit data. As above, assume this method is expensive
+ * and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or
+ * may not apply sharpening. Therefore, a row from this matrix may not be identical to one
+ * fetched using getBlackRow(), so don't mix and match between them.
+ *
+ * @return The 2D array of bits for the image (true means black).
+ */
+ public abstract BitMatrix getBlackMatrix() throws NotFoundException;
+
+ /**
+ * Creates a new object with the same type as this Binarizer implementation, but with pristine
+ * state. This is needed because Binarizer implementations may be stateful, e.g. keeping a cache
+ * of 1 bit data. See Effective Java for why we can't use Java's clone() method.
+ *
+ * @param source The LuminanceSource this Binarizer will operate on.
+ * @return A new concrete Binarizer implementation object.
+ */
+ public abstract Binarizer createBinarizer(LuminanceSource source);
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/BinaryBitmap.java b/libraries/zxing/src/com/google/zxing/BinaryBitmap.java
new file mode 100644
index 000000000..b97e46705
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/BinaryBitmap.java
@@ -0,0 +1,128 @@
+/*
+ * 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;
+
+import com.google.zxing.common.BitArray;
+import com.google.zxing.common.BitMatrix;
+
+/**
+ * This class is the core bitmap class used by ZXing to represent 1 bit data. Reader objects
+ * accept a BinaryBitmap and attempt to decode it.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+public final class BinaryBitmap {
+
+ private final Binarizer binarizer;
+ private BitMatrix matrix;
+
+ public BinaryBitmap(Binarizer binarizer) {
+ if (binarizer == null) {
+ throw new IllegalArgumentException("Binarizer must be non-null.");
+ }
+ this.binarizer = binarizer;
+ matrix = null;
+ }
+
+ /**
+ * @return The width of the bitmap.
+ */
+ public int getWidth() {
+ return binarizer.getLuminanceSource().getWidth();
+ }
+
+ /**
+ * @return The height of the bitmap.
+ */
+ public int getHeight() {
+ return binarizer.getLuminanceSource().getHeight();
+ }
+
+ /**
+ * Converts one row of luminance data to 1 bit data. May actually do the conversion, or return
+ * cached data. Callers should assume this method is expensive and call it as seldom as possible.
+ * This method is intended for decoding 1D barcodes and may choose to apply sharpening.
+ *
+ * @param y The row to fetch, 0 <= y < bitmap height.
+ * @param row An optional preallocated array. If null or too small, it will be ignored.
+ * If used, the Binarizer will call BitArray.clear(). Always use the returned object.
+ * @return The array of bits for this row (true means black).
+ */
+ public BitArray getBlackRow(int y, BitArray row) throws NotFoundException {
+ return binarizer.getBlackRow(y, row);
+ }
+
+ /**
+ * Converts a 2D array of luminance data to 1 bit. As above, assume this method is expensive
+ * and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or
+ * may not apply sharpening. Therefore, a row from this matrix may not be identical to one
+ * fetched using getBlackRow(), so don't mix and match between them.
+ *
+ * @return The 2D array of bits for the image (true means black).
+ */
+ public BitMatrix getBlackMatrix() throws NotFoundException {
+ // The matrix is created on demand the first time it is requested, then cached. There are two
+ // reasons for this:
+ // 1. This work will never be done if the caller only installs 1D Reader objects, or if a
+ // 1D Reader finds a barcode before the 2D Readers run.
+ // 2. This work will only be done once even if the caller installs multiple 2D Readers.
+ if (matrix == null) {
+ matrix = binarizer.getBlackMatrix();
+ }
+ return matrix;
+ }
+
+ /**
+ * @return Whether this bitmap can be cropped.
+ */
+ public boolean isCropSupported() {
+ return binarizer.getLuminanceSource().isCropSupported();
+ }
+
+ /**
+ * Returns a new object with cropped image data. Implementations may keep a reference to the
+ * original data rather than a copy. Only callable if isCropSupported() is true.
+ *
+ * @param left The left coordinate, 0 <= left < getWidth().
+ * @param top The top coordinate, 0 <= top <= getHeight().
+ * @param width The width of the rectangle to crop.
+ * @param height The height of the rectangle to crop.
+ * @return A cropped version of this object.
+ */
+ public BinaryBitmap crop(int left, int top, int width, int height) {
+ LuminanceSource newSource = binarizer.getLuminanceSource().crop(left, top, width, height);
+ return new BinaryBitmap(binarizer.createBinarizer(newSource));
+ }
+
+ /**
+ * @return Whether this bitmap supports counter-clockwise rotation.
+ */
+ public boolean isRotateSupported() {
+ return binarizer.getLuminanceSource().isRotateSupported();
+ }
+
+ /**
+ * Returns a new object with rotated image data. Only callable if isRotateSupported() is true.
+ *
+ * @return A rotated version of this object.
+ */
+ public BinaryBitmap rotateCounterClockwise() {
+ LuminanceSource newSource = binarizer.getLuminanceSource().rotateCounterClockwise();
+ return new BinaryBitmap(binarizer.createBinarizer(newSource));
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/ChecksumException.java b/libraries/zxing/src/com/google/zxing/ChecksumException.java
new file mode 100644
index 000000000..dedb4be99
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/ChecksumException.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2007 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;
+
+/**
+ * Thrown when a barcode was successfully detected and decoded, but
+ * was not returned because its checksum feature failed.
+ *
+ * @author Sean Owen
+ */
+public final class ChecksumException extends ReaderException {
+
+ private static final ChecksumException instance = new ChecksumException();
+
+ private ChecksumException() {
+ // do nothing
+ }
+
+ public static ChecksumException getChecksumInstance() {
+ return instance;
+ }
+
+}
\ No newline at end of file
diff --git a/libraries/zxing/src/com/google/zxing/DecodeHintType.java b/libraries/zxing/src/com/google/zxing/DecodeHintType.java
new file mode 100644
index 000000000..20b922ca1
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/DecodeHintType.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2007 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;
+
+/**
+ * Encapsulates a type of hint that a caller may pass to a barcode reader to help it
+ * more quickly or accurately decode it. It is up to implementations to decide what,
+ * if anything, to do with the information that is supplied.
+ *
+ * @author Sean Owen
+ * @author dswitkin@google.com (Daniel Switkin)
+ * @see Reader#decode(BinaryBitmap,java.util.Hashtable)
+ */
+public final class DecodeHintType {
+
+ // No, we can't use an enum here. J2ME doesn't support it.
+
+ /**
+ * Unspecified, application-specific hint. Maps to an unspecified {@link Object}.
+ */
+ public static final DecodeHintType OTHER = new DecodeHintType();
+
+ /**
+ * Image is a pure monochrome image of a barcode. Doesn't matter what it maps to;
+ * use {@link Boolean#TRUE}.
+ */
+ public static final DecodeHintType PURE_BARCODE = new DecodeHintType();
+
+ /**
+ * Image is known to be of one of a few possible formats.
+ * Maps to a {@link java.util.Vector} of {@link BarcodeFormat}s.
+ */
+ public static final DecodeHintType POSSIBLE_FORMATS = new DecodeHintType();
+
+ /**
+ * Spend more time to try to find a barcode; optimize for accuracy, not speed.
+ * Doesn't matter what it maps to; use {@link Boolean#TRUE}.
+ */
+ public static final DecodeHintType TRY_HARDER = new DecodeHintType();
+
+ /**
+ * Specifies what character encoding to use when decoding, where applicable (type String)
+ */
+ public static final DecodeHintType CHARACTER_SET = new DecodeHintType();
+
+ /**
+ * Allowed lengths of encoded data -- reject anything else. Maps to an int[].
+ */
+ public static final DecodeHintType ALLOWED_LENGTHS = new DecodeHintType();
+
+ /**
+ * Assume Code 39 codes employ a check digit. Maps to {@link Boolean}.
+ */
+ public static final DecodeHintType ASSUME_CODE_39_CHECK_DIGIT = new DecodeHintType();
+
+ /**
+ * The caller needs to be notified via callback when a possible {@link ResultPoint}
+ * is found. Maps to a {@link ResultPointCallback}.
+ */
+ public static final DecodeHintType NEED_RESULT_POINT_CALLBACK = new DecodeHintType();
+
+ private DecodeHintType() {
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/EncodeHintType.java b/libraries/zxing/src/com/google/zxing/EncodeHintType.java
new file mode 100644
index 000000000..35afc1530
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/EncodeHintType.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 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;
+
+/**
+ * These are a set of hints that you may pass to Writers to specify their behavior.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+public final class EncodeHintType {
+
+ /**
+ * Specifies what degree of error correction to use, for example in QR Codes (type Integer).
+ */
+ public static final EncodeHintType ERROR_CORRECTION = new EncodeHintType();
+
+ /**
+ * Specifies what character encoding to use where applicable (type String)
+ */
+ public static final EncodeHintType CHARACTER_SET = new EncodeHintType();
+
+ private EncodeHintType() {
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/FormatException.java b/libraries/zxing/src/com/google/zxing/FormatException.java
new file mode 100644
index 000000000..6967e93de
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/FormatException.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2007 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;
+
+/**
+ * Thrown when a barcode was successfully detected, but some aspect of
+ * the content did not conform to the barcode's format rules. This could have
+ * been due to a mis-detection.
+ *
+ * @author Sean Owen
+ */
+public final class FormatException extends ReaderException {
+
+ private static final FormatException instance = new FormatException();
+
+ private FormatException() {
+ // do nothing
+ }
+
+ public static FormatException getFormatInstance() {
+ return instance;
+ }
+
+}
\ No newline at end of file
diff --git a/libraries/zxing/src/com/google/zxing/LuminanceSource.java b/libraries/zxing/src/com/google/zxing/LuminanceSource.java
new file mode 100644
index 000000000..4b6d4539f
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/LuminanceSource.java
@@ -0,0 +1,113 @@
+/*
+ * 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;
+
+/**
+ * The purpose of this class hierarchy is to abstract different bitmap implementations across
+ * platforms into a standard interface for requesting greyscale luminance values. The interface
+ * only provides immutable methods; therefore crop and rotation create copies. This is to ensure
+ * that one Reader does not modify the original luminance source and leave it in an unknown state
+ * for other Readers in the chain.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+public abstract class LuminanceSource {
+
+ private final int width;
+ private final int height;
+
+ protected LuminanceSource(int width, int height) {
+ this.width = width;
+ this.height = height;
+ }
+
+ /**
+ * Fetches one row of luminance data from the underlying platform's bitmap. Values range from
+ * 0 (black) to 255 (white). Because Java does not have an unsigned byte type, callers will have
+ * to bitwise and with 0xff for each value. It is preferable for implementations of this method
+ * to only fetch this row rather than the whole image, since no 2D Readers may be installed and
+ * getMatrix() may never be called.
+ *
+ * @param y The row to fetch, 0 <= y < getHeight().
+ * @param row An optional preallocated array. If null or too small, it will be ignored.
+ * Always use the returned object, and ignore the .length of the array.
+ * @return An array containing the luminance data.
+ */
+ public abstract byte[] getRow(int y, byte[] row);
+
+ /**
+ * Fetches luminance data for the underlying bitmap. Values should be fetched using:
+ * int luminance = array[y * width + x] & 0xff;
+ *
+ * @return A row-major 2D array of luminance values. Do not use result.length as it may be
+ * larger than width * height bytes on some platforms. Do not modify the contents
+ * of the result.
+ */
+ public abstract byte[] getMatrix();
+
+ /**
+ * @return The width of the bitmap.
+ */
+ public final int getWidth() {
+ return width;
+ }
+
+ /**
+ * @return The height of the bitmap.
+ */
+ public final int getHeight() {
+ return height;
+ }
+
+ /**
+ * @return Whether this subclass supports cropping.
+ */
+ public boolean isCropSupported() {
+ return false;
+ }
+
+ /**
+ * Returns a new object with cropped image data. Implementations may keep a reference to the
+ * original data rather than a copy. Only callable if isCropSupported() is true.
+ *
+ * @param left The left coordinate, 0 <= left < getWidth().
+ * @param top The top coordinate, 0 <= top <= getHeight().
+ * @param width The width of the rectangle to crop.
+ * @param height The height of the rectangle to crop.
+ * @return A cropped version of this object.
+ */
+ public LuminanceSource crop(int left, int top, int width, int height) {
+ throw new RuntimeException("This luminance source does not support cropping.");
+ }
+
+ /**
+ * @return Whether this subclass supports counter-clockwise rotation.
+ */
+ public boolean isRotateSupported() {
+ return false;
+ }
+
+ /**
+ * Returns a new object with rotated image data. Only callable if isRotateSupported() is true.
+ *
+ * @return A rotated version of this object.
+ */
+ public LuminanceSource rotateCounterClockwise() {
+ throw new RuntimeException("This luminance source does not support rotation.");
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/NotFoundException.java b/libraries/zxing/src/com/google/zxing/NotFoundException.java
new file mode 100644
index 000000000..dedab8dfc
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/NotFoundException.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2007 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;
+
+/**
+ * Thrown when a barcode was not found in the image. It might have been
+ * partially detected but could not be confirmed.
+ *
+ * @author Sean Owen
+ */
+public final class NotFoundException extends ReaderException {
+
+ private static final NotFoundException instance = new NotFoundException();
+
+ private NotFoundException() {
+ // do nothing
+ }
+
+ public static NotFoundException getNotFoundInstance() {
+ return instance;
+ }
+
+}
\ No newline at end of file
diff --git a/libraries/zxing/src/com/google/zxing/Reader.java b/libraries/zxing/src/com/google/zxing/Reader.java
new file mode 100644
index 000000000..47e843ba6
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/Reader.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2007 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;
+
+import java.util.Hashtable;
+
+/**
+ * Implementations of this interface can decode an image of a barcode in some format into
+ * the String it encodes. For example, {@link com.google.zxing.qrcode.QRCodeReader} can
+ * decode a QR code. The decoder may optionally receive hints from the caller which may help
+ * it decode more quickly or accurately.
+ *
+ * See {@link com.google.zxing.MultiFormatReader}, which attempts to determine what barcode
+ * format is present within the image as well, and then decodes it accordingly.
+ *
+ * @author Sean Owen
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+public interface Reader {
+
+ /**
+ * Locates and decodes a barcode in some format within an image.
+ *
+ * @param image image of barcode to decode
+ * @return String which the barcode encodes
+ * @throws NotFoundException if the barcode cannot be located or decoded for any reason
+ */
+ Result decode(BinaryBitmap image) throws NotFoundException, ChecksumException, FormatException;
+
+ /**
+ * Locates and decodes a barcode in some format within an image. This method also accepts
+ * hints, each possibly associated to some data, which may help the implementation decode.
+ *
+ * @param image image of barcode to decode
+ * @param hints passed as a {@link java.util.Hashtable} from {@link com.google.zxing.DecodeHintType}
+ * to arbitrary data. The
+ * meaning of the data depends upon the hint type. The implementation may or may not do
+ * anything with these hints.
+ * @return String which the barcode encodes
+ * @throws NotFoundException if the barcode cannot be located or decoded for any reason
+ */
+ Result decode(BinaryBitmap image, Hashtable hints) throws NotFoundException, ChecksumException, FormatException;
+
+ /**
+ * Resets any internal state the implementation has after a decode, to prepare it
+ * for reuse.
+ */
+ void reset();
+
+}
\ No newline at end of file
diff --git a/libraries/zxing/src/com/google/zxing/ReaderException.java b/libraries/zxing/src/com/google/zxing/ReaderException.java
new file mode 100644
index 000000000..224a497e5
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/ReaderException.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2007 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;
+
+/**
+ * The general exception class throw when something goes wrong during decoding of a barcode.
+ * This includes, but is not limited to, failing checksums / error correction algorithms, being
+ * unable to locate finder timing patterns, and so on.
+ *
+ * @author Sean Owen
+ */
+public abstract class ReaderException extends Exception {
+
+ // TODO: Currently we throw up to 400 ReaderExceptions while scanning a single 240x240 image before
+ // rejecting it. This involves a lot of overhead and memory allocation, and affects both performance
+ // and latency on continuous scan clients. In the future, we should change all the decoders not to
+ // throw exceptions for routine events, like not finding a barcode on a given row. Instead, we
+ // should return error codes back to the callers, and simply delete this class. In the mean time, I
+ // have altered this class to be as lightweight as possible, by ignoring the exception string, and
+ // by disabling the generation of stack traces, which is especially time consuming. These are just
+ // temporary measures, pending the big cleanup.
+
+ //private static final ReaderException instance = new ReaderException();
+
+ // EXCEPTION TRACKING SUPPORT
+ // Identifies who is throwing exceptions and how often. To use:
+ //
+ // 1. Uncomment these lines and the code below which uses them.
+ // 2. Uncomment the two corresponding lines in j2se/CommandLineRunner.decode()
+ // 3. Change core to build as Java 1.5 temporarily
+// private static int exceptionCount = 0;
+// private static Map throwers = new HashMap(32);
+
+ ReaderException() {
+ // do nothing
+ }
+
+ //public static ReaderException getInstance() {
+// Exception e = new Exception();
+// // Take the stack frame before this one.
+// StackTraceElement stack = e.getStackTrace()[1];
+// String key = stack.getClassName() + "." + stack.getMethodName() + "(), line " +
+// stack.getLineNumber();
+// if (throwers.containsKey(key)) {
+// Integer value = throwers.get(key);
+// value++;
+// throwers.put(key, value);
+// } else {
+// throwers.put(key, 1);
+// }
+// exceptionCount++;
+
+ //return instance;
+ //}
+
+// public static int getExceptionCountAndReset() {
+// int temp = exceptionCount;
+// exceptionCount = 0;
+// return temp;
+// }
+//
+// public static String getThrowersAndReset() {
+// StringBuilder builder = new StringBuilder(1024);
+// Object[] keys = throwers.keySet().toArray();
+// for (int x = 0; x < keys.length; x++) {
+// String key = (String) keys[x];
+// Integer value = throwers.get(key);
+// builder.append(key);
+// builder.append(": ");
+// builder.append(value);
+// builder.append("\n");
+// }
+// throwers.clear();
+// return builder.toString();
+// }
+
+ // Prevent stack traces from being taken
+ // srowen says: huh, my IDE is saying this is not an override. native methods can't be overridden?
+ // This, at least, does not hurt. Because we use a singleton pattern here, it doesn't matter anyhow.
+ public final Throwable fillInStackTrace() {
+ return null;
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/Result.java b/libraries/zxing/src/com/google/zxing/Result.java
new file mode 100644
index 000000000..ee1af527e
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/Result.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2007 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;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+/**
+ *
Encapsulates the result of decoding a barcode within an image.
+ *
+ * @author Sean Owen
+ */
+public final class Result {
+
+ private final String text;
+ private final byte[] rawBytes;
+ private ResultPoint[] resultPoints;
+ private final BarcodeFormat format;
+ private Hashtable resultMetadata;
+ private final long timestamp;
+
+ public Result(String text,
+ byte[] rawBytes,
+ ResultPoint[] resultPoints,
+ BarcodeFormat format) {
+ this(text, rawBytes, resultPoints, format, System.currentTimeMillis());
+ }
+
+ public Result(String text,
+ byte[] rawBytes,
+ ResultPoint[] resultPoints,
+ BarcodeFormat format,
+ long timestamp) {
+ if (text == null && rawBytes == null) {
+ throw new IllegalArgumentException("Text and bytes are null");
+ }
+ this.text = text;
+ this.rawBytes = rawBytes;
+ this.resultPoints = resultPoints;
+ this.format = format;
+ this.resultMetadata = null;
+ this.timestamp = timestamp;
+ }
+
+ /**
+ * @return raw text encoded by the barcode, if applicable, otherwise null
+ */
+ public String getText() {
+ return text;
+ }
+
+ /**
+ * @return raw bytes encoded by the barcode, if applicable, otherwise null
+ */
+ public byte[] getRawBytes() {
+ return rawBytes;
+ }
+
+ /**
+ * @return points related to the barcode in the image. These are typically points
+ * identifying finder patterns or the corners of the barcode. The exact meaning is
+ * specific to the type of barcode that was decoded.
+ */
+ public ResultPoint[] getResultPoints() {
+ return resultPoints;
+ }
+
+ /**
+ * @return {@link BarcodeFormat} representing the format of the barcode that was decoded
+ */
+ public BarcodeFormat getBarcodeFormat() {
+ return format;
+ }
+
+ /**
+ * @return {@link Hashtable} mapping {@link ResultMetadataType} keys to values. May be
+ * null. This contains optional metadata about what was detected about the barcode,
+ * like orientation.
+ */
+ public Hashtable getResultMetadata() {
+ return resultMetadata;
+ }
+
+ public void putMetadata(ResultMetadataType type, Object value) {
+ if (resultMetadata == null) {
+ resultMetadata = new Hashtable(3);
+ }
+ resultMetadata.put(type, value);
+ }
+
+ public void putAllMetadata(Hashtable metadata) {
+ if (metadata != null) {
+ if (resultMetadata == null) {
+ resultMetadata = metadata;
+ } else {
+ Enumeration e = metadata.keys();
+ while (e.hasMoreElements()) {
+ ResultMetadataType key = (ResultMetadataType) e.nextElement();
+ Object value = metadata.get(key);
+ resultMetadata.put(key, value);
+ }
+ }
+ }
+ }
+
+ public void addResultPoints(ResultPoint[] newPoints) {
+ if (resultPoints == null) {
+ resultPoints = newPoints;
+ } else if (newPoints != null && newPoints.length > 0) {
+ ResultPoint[] allPoints = new ResultPoint[resultPoints.length + newPoints.length];
+ System.arraycopy(resultPoints, 0, allPoints, 0, resultPoints.length);
+ System.arraycopy(newPoints, 0, allPoints, resultPoints.length, newPoints.length);
+ resultPoints = allPoints;
+ }
+ }
+
+ public long getTimestamp() {
+ return timestamp;
+ }
+
+ public String toString() {
+ if (text == null) {
+ return "[" + rawBytes.length + " bytes]";
+ } else {
+ return text;
+ }
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/ResultMetadataType.java b/libraries/zxing/src/com/google/zxing/ResultMetadataType.java
new file mode 100644
index 000000000..33d69d9c5
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/ResultMetadataType.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 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;
+
+import java.util.Hashtable;
+
+/**
+ * Represents some type of metadata about the result of the decoding that the decoder
+ * wishes to communicate back to the caller.
+ *
+ * @author Sean Owen
+ */
+public final class ResultMetadataType {
+
+ // No, we can't use an enum here. J2ME doesn't support it.
+
+ private static final Hashtable VALUES = new Hashtable();
+
+ // No, we can't use an enum here. J2ME doesn't support it.
+
+ /**
+ * Unspecified, application-specific metadata. Maps to an unspecified {@link Object}.
+ */
+ public static final ResultMetadataType OTHER = new ResultMetadataType("OTHER");
+
+ /**
+ * Denotes the likely approximate orientation of the barcode in the image. This value
+ * is given as degrees rotated clockwise from the normal, upright orientation.
+ * For example a 1D barcode which was found by reading top-to-bottom would be
+ * said to have orientation "90". This key maps to an {@link Integer} whose
+ * value is in the range [0,360).
+ */
+ public static final ResultMetadataType ORIENTATION = new ResultMetadataType("ORIENTATION");
+
+ /**
+ *
2D barcode formats typically encode text, but allow for a sort of 'byte mode'
+ * which is sometimes used to encode binary data. While {@link Result} makes available
+ * the complete raw bytes in the barcode for these formats, it does not offer the bytes
+ * from the byte segments alone.
+ *
+ *
This maps to a {@link java.util.Vector} of byte arrays corresponding to the
+ * raw bytes in the byte segments in the barcode, in order.
+ */
+ public static final ResultMetadataType BYTE_SEGMENTS = new ResultMetadataType("BYTE_SEGMENTS");
+
+ /**
+ * Error correction level used, if applicable. The value type depends on the
+ * format, but is typically a String.
+ */
+ public static final ResultMetadataType ERROR_CORRECTION_LEVEL = new ResultMetadataType("ERROR_CORRECTION_LEVEL");
+
+ /**
+ * For some periodicals, indicates the issue number as an {@link Integer}.
+ */
+ public static final ResultMetadataType ISSUE_NUMBER = new ResultMetadataType("ISSUE_NUMBER");
+
+ /**
+ * For some products, indicates the suggested retail price in the barcode as a
+ * formatted {@link String}.
+ */
+ public static final ResultMetadataType SUGGESTED_PRICE = new ResultMetadataType("SUGGESTED_PRICE");
+
+ /**
+ * For some products, the possible country of manufacture as a {@link String} denoting the
+ * ISO country code. Some map to multiple possible countries, like "US/CA".
+ */
+ public static final ResultMetadataType POSSIBLE_COUNTRY = new ResultMetadataType("POSSIBLE_COUNTRY");
+
+ private final String name;
+
+ private ResultMetadataType(String name) {
+ this.name = name;
+ VALUES.put(name, this);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String toString() {
+ return name;
+ }
+
+ public static ResultMetadataType valueOf(String name) {
+ if (name == null || name.length() == 0) {
+ throw new IllegalArgumentException();
+ }
+ ResultMetadataType format = (ResultMetadataType) VALUES.get(name);
+ if (format == null) {
+ throw new IllegalArgumentException();
+ }
+ return format;
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/ResultPoint.java b/libraries/zxing/src/com/google/zxing/ResultPoint.java
new file mode 100644
index 000000000..366ae3855
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/ResultPoint.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2007 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;
+
+/**
+ *
Encapsulates a point of interest in an image containing a barcode. Typically, this
+ * would be the location of a finder pattern or the corner of the barcode, for example.
+ *
+ * @author Sean Owen
+ */
+public class ResultPoint {
+
+ private final float x;
+ private final float y;
+
+ public ResultPoint(float x, float y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ public final float getX() {
+ return x;
+ }
+
+ public final float getY() {
+ return y;
+ }
+
+ public boolean equals(Object other) {
+ if (other instanceof ResultPoint) {
+ ResultPoint otherPoint = (ResultPoint) other;
+ return x == otherPoint.x && y == otherPoint.y;
+ }
+ return false;
+ }
+
+ public int hashCode() {
+ return 31 * Float.floatToIntBits(x) + Float.floatToIntBits(y);
+ }
+
+ public String toString() {
+ StringBuffer result = new StringBuffer(25);
+ result.append('(');
+ result.append(x);
+ result.append(',');
+ result.append(y);
+ result.append(')');
+ return result.toString();
+ }
+
+ /**
+ *
Orders an array of three ResultPoints in an order [A,B,C] such that AB < AC and
+ * BC < AC and the angle between BC and BA is less than 180 degrees.
+ */
+ public static void orderBestPatterns(ResultPoint[] patterns) {
+
+ // Find distances between pattern centers
+ float zeroOneDistance = distance(patterns[0], patterns[1]);
+ float oneTwoDistance = distance(patterns[1], patterns[2]);
+ float zeroTwoDistance = distance(patterns[0], patterns[2]);
+
+ ResultPoint pointA;
+ ResultPoint pointB;
+ ResultPoint pointC;
+ // Assume one closest to other two is B; A and C will just be guesses at first
+ if (oneTwoDistance >= zeroOneDistance && oneTwoDistance >= zeroTwoDistance) {
+ pointB = patterns[0];
+ pointA = patterns[1];
+ pointC = patterns[2];
+ } else if (zeroTwoDistance >= oneTwoDistance && zeroTwoDistance >= zeroOneDistance) {
+ pointB = patterns[1];
+ pointA = patterns[0];
+ pointC = patterns[2];
+ } else {
+ pointB = patterns[2];
+ pointA = patterns[0];
+ pointC = patterns[1];
+ }
+
+ // Use cross product to figure out whether A and C are correct or flipped.
+ // This asks whether BC x BA has a positive z component, which is the arrangement
+ // we want for A, B, C. If it's negative, then we've got it flipped around and
+ // should swap A and C.
+ if (crossProductZ(pointA, pointB, pointC) < 0.0f) {
+ ResultPoint temp = pointA;
+ pointA = pointC;
+ pointC = temp;
+ }
+
+ patterns[0] = pointA;
+ patterns[1] = pointB;
+ patterns[2] = pointC;
+ }
+
+
+ /**
+ * @return distance between two points
+ */
+ public static float distance(ResultPoint pattern1, ResultPoint pattern2) {
+ float xDiff = pattern1.getX() - pattern2.getX();
+ float yDiff = pattern1.getY() - pattern2.getY();
+ return (float) Math.sqrt((double) (xDiff * xDiff + yDiff * yDiff));
+ }
+
+ /**
+ * Returns the z component of the cross product between vectors BC and BA.
+ */
+ private static float crossProductZ(ResultPoint pointA, ResultPoint pointB, ResultPoint pointC) {
+ float bX = pointB.x;
+ float bY = pointB.y;
+ return ((pointC.x - bX) * (pointA.y - bY)) - ((pointC.y - bY) * (pointA.x - bX));
+ }
+
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/ResultPointCallback.java b/libraries/zxing/src/com/google/zxing/ResultPointCallback.java
new file mode 100644
index 000000000..0c85410bc
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/ResultPointCallback.java
@@ -0,0 +1,29 @@
+/*
+ * 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;
+
+/**
+ * Callback which is invoked when a possible result point (significant
+ * point in the barcode image such as a corner) is found.
+ *
+ * @see DecodeHintType#NEED_RESULT_POINT_CALLBACK
+ */
+public interface ResultPointCallback {
+
+ void foundPossibleResultPoint(ResultPoint point);
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/Writer.java b/libraries/zxing/src/com/google/zxing/Writer.java
new file mode 100644
index 000000000..6474ca7e2
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/Writer.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 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;
+
+import com.google.zxing.common.BitMatrix;
+
+import java.util.Hashtable;
+
+/**
+ * The base class for all objects which encode/generate a barcode image.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+public interface Writer {
+
+ /**
+ * Encode a barcode using the default settings.
+ *
+ * @param contents The contents to encode in the barcode
+ * @param format The barcode format to generate
+ * @param width The preferred width in pixels
+ * @param height The preferred height in pixels
+ * @return The generated barcode as a Matrix of unsigned bytes (0 == black, 255 == white)
+ */
+ BitMatrix encode(String contents, BarcodeFormat format, int width, int height)
+ throws WriterException;
+
+ /**
+ *
+ * @param contents The contents to encode in the barcode
+ * @param format The barcode format to generate
+ * @param width The preferred width in pixels
+ * @param height The preferred height in pixels
+ * @param hints Additional parameters to supply to the encoder
+ * @return The generated barcode as a Matrix of unsigned bytes (0 == black, 255 == white)
+ */
+ BitMatrix encode(String contents, BarcodeFormat format, int width, int height, Hashtable hints)
+ throws WriterException;
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/WriterException.java b/libraries/zxing/src/com/google/zxing/WriterException.java
new file mode 100644
index 000000000..0c19af01d
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/WriterException.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 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;
+
+/**
+ * A base class which covers the range of exceptions which may occur when encoding a barcode using
+ * the Writer framework.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+public final class WriterException extends Exception {
+
+ public WriterException() {
+ super();
+ }
+
+ public WriterException(String message) {
+ super(message);
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/common/BitArray.java b/libraries/zxing/src/com/google/zxing/common/BitArray.java
new file mode 100644
index 000000000..6eb0d57c6
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/common/BitArray.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright 2007 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.common;
+
+/**
+ *
A simple, fast array of bits, represented compactly by an array of ints internally.
+ *
+ * @author Sean Owen
+ */
+public final class BitArray {
+ // I have changed these members to be public so ProGuard can inline get() and set(). Ideally
+ // they'd be private and we'd use the -allowaccessmodification flag, but Dalvik rejects the
+ // resulting binary at runtime on Android. If we find a solution to this, these should be changed
+ // back to private.
+ public int[] bits;
+ public int size;
+
+ public BitArray() {
+ this.size = 0;
+ this.bits = new int[1];
+ }
+
+ public BitArray(int size) {
+ this.size = size;
+ this.bits = makeArray(size);
+ }
+
+ public int getSize() {
+ return size;
+ }
+
+ public int getSizeInBytes() {
+ return (size + 7) >> 3;
+ }
+
+ private void ensureCapacity(int size) {
+ if (size > bits.length << 5) {
+ int[] newBits = makeArray(size);
+ System.arraycopy(bits, 0, newBits, 0, bits.length);
+ this.bits = newBits;
+ }
+ }
+
+ /**
+ * @param i bit to get
+ * @return true iff bit i is set
+ */
+ public boolean get(int i) {
+ return (bits[i >> 5] & (1 << (i & 0x1F))) != 0;
+ }
+
+ /**
+ * Sets bit i.
+ *
+ * @param i bit to set
+ */
+ public void set(int i) {
+ bits[i >> 5] |= 1 << (i & 0x1F);
+ }
+
+ /**
+ * Flips bit i.
+ *
+ * @param i bit to set
+ */
+ public void flip(int i) {
+ bits[i >> 5] ^= 1 << (i & 0x1F);
+ }
+
+ /**
+ * Sets a block of 32 bits, starting at bit i.
+ *
+ * @param i first bit to set
+ * @param newBits the new value of the next 32 bits. Note again that the least-significant bit
+ * corresponds to bit i, the next-least-significant to i+1, and so on.
+ */
+ public void setBulk(int i, int newBits) {
+ bits[i >> 5] = newBits;
+ }
+
+ /**
+ * Clears all bits (sets to false).
+ */
+ public void clear() {
+ int max = bits.length;
+ for (int i = 0; i < max; i++) {
+ bits[i] = 0;
+ }
+ }
+
+ /**
+ * Efficient method to check if a range of bits is set, or not set.
+ *
+ * @param start start of range, inclusive.
+ * @param end end of range, exclusive
+ * @param value if true, checks that bits in range are set, otherwise checks that they are not set
+ * @return true iff all bits are set or not set in range, according to value argument
+ * @throws IllegalArgumentException if end is less than or equal to start
+ */
+ public boolean isRange(int start, int end, boolean value) {
+ if (end < start) {
+ throw new IllegalArgumentException();
+ }
+ if (end == start) {
+ return true; // empty range matches
+ }
+ end--; // will be easier to treat this as the last actually set bit -- inclusive
+ int firstInt = start >> 5;
+ int lastInt = end >> 5;
+ for (int i = firstInt; i <= lastInt; i++) {
+ int firstBit = i > firstInt ? 0 : start & 0x1F;
+ int lastBit = i < lastInt ? 31 : end & 0x1F;
+ int mask;
+ if (firstBit == 0 && lastBit == 31) {
+ mask = -1;
+ } else {
+ mask = 0;
+ for (int j = firstBit; j <= lastBit; j++) {
+ mask |= 1 << j;
+ }
+ }
+
+ // Return false if we're looking for 1s and the masked bits[i] isn't all 1s (that is,
+ // equals the mask, or we're looking for 0s and the masked portion is not all 0s
+ if ((bits[i] & mask) != (value ? mask : 0)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public void appendBit(boolean bit) {
+ ensureCapacity(size + 1);
+ if (bit) {
+ bits[size >> 5] |= (1 << (size & 0x1F));
+ }
+ size++;
+ }
+
+ /**
+ * Appends the least-significant bits, from value, in order from most-significant to
+ * least-significant. For example, appending 6 bits from 0x000001E will append the bits
+ * 0, 1, 1, 1, 1, 0 in that order.
+ */
+ public void appendBits(int value, int numBits) {
+ if (numBits < 0 || numBits > 32) {
+ throw new IllegalArgumentException("Num bits must be between 0 and 32");
+ }
+ ensureCapacity(size + numBits);
+ for (int numBitsLeft = numBits; numBitsLeft > 0; numBitsLeft--) {
+ appendBit(((value >> (numBitsLeft - 1)) & 0x01) == 1);
+ }
+ }
+
+ public void appendBitArray(BitArray other) {
+ int otherSize = other.getSize();
+ ensureCapacity(size + otherSize);
+ for (int i = 0; i < otherSize; i++) {
+ appendBit(other.get(i));
+ }
+ }
+
+ public void xor(BitArray other) {
+ if (bits.length != other.bits.length) {
+ throw new IllegalArgumentException("Sizes don't match");
+ }
+ for (int i = 0; i < bits.length; i++) {
+ // The last byte could be incomplete (i.e. not have 8 bits in
+ // it) but there is no problem since 0 XOR 0 == 0.
+ bits[i] ^= other.bits[i];
+ }
+ }
+
+ /**
+ *
+ * @param bitOffset first bit to start writing
+ * @param array array to write into. Bytes are written most-significant byte first. This is the opposite
+ * of the internal representation, which is exposed by {@link #getBitArray()}
+ * @param offset position in array to start writing
+ * @param numBytes how many bytes to write
+ */
+ public void toBytes(int bitOffset, byte[] array, int offset, int numBytes) {
+ for (int i = 0; i < numBytes; i++) {
+ int theByte = 0;
+ for (int j = 0; j < 8; j++) {
+ if (get(bitOffset)) {
+ theByte |= 1 << (7 - j);
+ }
+ bitOffset++;
+ }
+ array[offset + i] = (byte) theByte;
+ }
+ }
+
+ /**
+ * @return underlying array of ints. The first element holds the first 32 bits, and the least
+ * significant bit is bit 0.
+ */
+ public int[] getBitArray() {
+ return bits;
+ }
+
+ /**
+ * Reverses all bits in the array.
+ */
+ public void reverse() {
+ int[] newBits = new int[bits.length];
+ int size = this.size;
+ for (int i = 0; i < size; i++) {
+ if (get(size - i - 1)) {
+ newBits[i >> 5] |= 1 << (i & 0x1F);
+ }
+ }
+ bits = newBits;
+ }
+
+ private static int[] makeArray(int size) {
+ return new int[(size + 31) >> 5];
+ }
+
+ public String toString() {
+ StringBuffer result = new StringBuffer(size);
+ for (int i = 0; i < size; i++) {
+ if ((i & 0x07) == 0) {
+ result.append(' ');
+ }
+ result.append(get(i) ? 'X' : '.');
+ }
+ return result.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/libraries/zxing/src/com/google/zxing/common/BitMatrix.java b/libraries/zxing/src/com/google/zxing/common/BitMatrix.java
new file mode 100644
index 000000000..8bf75b289
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/common/BitMatrix.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright 2007 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.common;
+
+/**
+ *
Represents a 2D matrix of bits. In function arguments below, and throughout the common
+ * module, x is the column position, and y is the row position. The ordering is always x, y.
+ * The origin is at the top-left.
+ *
+ *
Internally the bits are represented in a 1-D array of 32-bit ints. However, each row begins
+ * with a new int. This is done intentionally so that we can copy out a row into a BitArray very
+ * efficiently.
+ *
+ *
The ordering of bits is row-major. Within each int, the least significant bits are used first,
+ * meaning they represent lower x values. This is compatible with BitArray's implementation.
+ *
+ * @author Sean Owen
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+public final class BitMatrix {
+ // Just like BitArray, these need to be public so ProGuard can inline them.
+ public final int width;
+ public final int height;
+ public final int rowSize;
+ public final int[] bits;
+
+ // A helper to construct a square matrix.
+ public BitMatrix(int dimension) {
+ this(dimension, dimension);
+ }
+
+ public BitMatrix(int width, int height) {
+ if (width < 1 || height < 1) {
+ throw new IllegalArgumentException("Both dimensions must be greater than 0");
+ }
+ this.width = width;
+ this.height = height;
+ this.rowSize = (width + 31) >> 5;
+ bits = new int[rowSize * height];
+ }
+
+ /**
+ *
Gets the requested bit, where true means black.
+ *
+ * @param x The horizontal component (i.e. which column)
+ * @param y The vertical component (i.e. which row)
+ * @return value of given bit in matrix
+ */
+ public boolean get(int x, int y) {
+ int offset = y * rowSize + (x >> 5);
+ return ((bits[offset] >>> (x & 0x1f)) & 1) != 0;
+ }
+
+ /**
+ *
Sets the given bit to true.
+ *
+ * @param x The horizontal component (i.e. which column)
+ * @param y The vertical component (i.e. which row)
+ */
+ public void set(int x, int y) {
+ int offset = y * rowSize + (x >> 5);
+ bits[offset] |= 1 << (x & 0x1f);
+ }
+
+ /**
+ *
Flips the given bit.
+ *
+ * @param x The horizontal component (i.e. which column)
+ * @param y The vertical component (i.e. which row)
+ */
+ public void flip(int x, int y) {
+ int offset = y * rowSize + (x >> 5);
+ bits[offset] ^= 1 << (x & 0x1f);
+ }
+
+ /**
+ * Clears all bits (sets to false).
+ */
+ public void clear() {
+ int max = bits.length;
+ for (int i = 0; i < max; i++) {
+ bits[i] = 0;
+ }
+ }
+
+ /**
+ *
Sets a square region of the bit matrix to true.
+ *
+ * @param left The horizontal position to begin at (inclusive)
+ * @param top The vertical position to begin at (inclusive)
+ * @param width The width of the region
+ * @param height The height of the region
+ */
+ public void setRegion(int left, int top, int width, int height) {
+ if (top < 0 || left < 0) {
+ throw new IllegalArgumentException("Left and top must be nonnegative");
+ }
+ if (height < 1 || width < 1) {
+ throw new IllegalArgumentException("Height and width must be at least 1");
+ }
+ int right = left + width;
+ int bottom = top + height;
+ if (bottom > this.height || right > this.width) {
+ throw new IllegalArgumentException("The region must fit inside the matrix");
+ }
+ for (int y = top; y < bottom; y++) {
+ int offset = y * rowSize;
+ for (int x = left; x < right; x++) {
+ bits[offset + (x >> 5)] |= 1 << (x & 0x1f);
+ }
+ }
+ }
+
+ /**
+ * A fast method to retrieve one row of data from the matrix as a BitArray.
+ *
+ * @param y The row to retrieve
+ * @param row An optional caller-allocated BitArray, will be allocated if null or too small
+ * @return The resulting BitArray - this reference should always be used even when passing
+ * your own row
+ */
+ public BitArray getRow(int y, BitArray row) {
+ if (row == null || row.getSize() < width) {
+ row = new BitArray(width);
+ }
+ int offset = y * rowSize;
+ for (int x = 0; x < rowSize; x++) {
+ row.setBulk(x << 5, bits[offset + x]);
+ }
+ return row;
+ }
+
+ /**
+ * This is useful in detecting a corner of a 'pure' barcode.
+ *
+ * @return {x,y} coordinate of top-left-most 1 bit, or null if it is all white
+ */
+ public int[] getTopLeftOnBit() {
+ int bitsOffset = 0;
+ while (bitsOffset < bits.length && bits[bitsOffset] == 0) {
+ bitsOffset++;
+ }
+ if (bitsOffset == bits.length) {
+ return null;
+ }
+ int y = bitsOffset / rowSize;
+ int x = (bitsOffset % rowSize) << 5;
+
+ int theBits = bits[bitsOffset];
+ int bit = 0;
+ while ((theBits << (31-bit)) == 0) {
+ bit++;
+ }
+ x += bit;
+ return new int[] {x, y};
+ }
+
+ public int[] getBottomRightOnBit() {
+ int bitsOffset = bits.length - 1;
+ while (bitsOffset >= 0 && bits[bitsOffset] == 0) {
+ bitsOffset--;
+ }
+ if (bitsOffset < 0) {
+ return null;
+ }
+
+ int y = bitsOffset / rowSize;
+ int x = (bitsOffset % rowSize) << 5;
+
+ int theBits = bits[bitsOffset];
+ int bit = 31;
+ while ((theBits >>> bit) == 0) {
+ bit--;
+ }
+ x += bit;
+
+ return new int[] {x, y};
+ }
+
+ /**
+ * @return The width of the matrix
+ */
+ public int getWidth() {
+ return width;
+ }
+
+ /**
+ * @return The height of the matrix
+ */
+ public int getHeight() {
+ return height;
+ }
+
+ public boolean equals(Object o) {
+ if (!(o instanceof BitMatrix)) {
+ return false;
+ }
+ BitMatrix other = (BitMatrix) o;
+ if (width != other.width || height != other.height ||
+ rowSize != other.rowSize || bits.length != other.bits.length) {
+ return false;
+ }
+ for (int i = 0; i < bits.length; i++) {
+ if (bits[i] != other.bits[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public int hashCode() {
+ int hash = width;
+ hash = 31 * hash + width;
+ hash = 31 * hash + height;
+ hash = 31 * hash + rowSize;
+ for (int i = 0; i < bits.length; i++) {
+ hash = 31 * hash + bits[i];
+ }
+ return hash;
+ }
+
+ public String toString() {
+ StringBuffer result = new StringBuffer(height * (width + 1));
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ result.append(get(x, y) ? "X " : " ");
+ }
+ result.append('\n');
+ }
+ return result.toString();
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/common/BitSource.java b/libraries/zxing/src/com/google/zxing/common/BitSource.java
new file mode 100644
index 000000000..a61ac5105
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/common/BitSource.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2007 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.common;
+
+/**
+ *
This provides an easy abstraction to read bits at a time from a sequence of bytes, where the
+ * number of bits read is not often a multiple of 8.
+ *
+ *
This class is thread-safe but not reentrant. Unless the caller modifies the bytes array
+ * it passed in, in which case all bets are off.
+ *
+ * @author Sean Owen
+ */
+public final class BitSource {
+
+ private final byte[] bytes;
+ private int byteOffset;
+ private int bitOffset;
+
+ /**
+ * @param bytes bytes from which this will read bits. Bits will be read from the first byte first.
+ * Bits are read within a byte from most-significant to least-significant bit.
+ */
+ public BitSource(byte[] bytes) {
+ this.bytes = bytes;
+ }
+
+ /**
+ * @param numBits number of bits to read
+ * @return int representing the bits read. The bits will appear as the least-significant
+ * bits of the int
+ * @throws IllegalArgumentException if numBits isn't in [1,32]
+ */
+ public int readBits(int numBits) {
+ if (numBits < 1 || numBits > 32) {
+ throw new IllegalArgumentException();
+ }
+
+ int result = 0;
+
+ // First, read remainder from current byte
+ if (bitOffset > 0) {
+ int bitsLeft = 8 - bitOffset;
+ int toRead = numBits < bitsLeft ? numBits : bitsLeft;
+ int bitsToNotRead = bitsLeft - toRead;
+ int mask = (0xFF >> (8 - toRead)) << bitsToNotRead;
+ result = (bytes[byteOffset] & mask) >> bitsToNotRead;
+ numBits -= toRead;
+ bitOffset += toRead;
+ if (bitOffset == 8) {
+ bitOffset = 0;
+ byteOffset++;
+ }
+ }
+
+ // Next read whole bytes
+ if (numBits > 0) {
+ while (numBits >= 8) {
+ result = (result << 8) | (bytes[byteOffset] & 0xFF);
+ byteOffset++;
+ numBits -= 8;
+ }
+
+ // Finally read a partial byte
+ if (numBits > 0) {
+ int bitsToNotRead = 8 - numBits;
+ int mask = (0xFF >> bitsToNotRead) << bitsToNotRead;
+ result = (result << numBits) | ((bytes[byteOffset] & mask) >> bitsToNotRead);
+ bitOffset += numBits;
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * @return number of bits that can be read successfully
+ */
+ public int available() {
+ return 8 * (bytes.length - byteOffset) - bitOffset;
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/common/CharacterSetECI.java b/libraries/zxing/src/com/google/zxing/common/CharacterSetECI.java
new file mode 100644
index 000000000..42b7fa9f6
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/common/CharacterSetECI.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 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.common;
+
+import java.util.Hashtable;
+
+/**
+ * Encapsulates a Character Set ECI, according to "Extended Channel Interpretations" 5.3.1.1
+ * of ISO 18004.
+ *
+ * @author Sean Owen
+ */
+public final class CharacterSetECI extends ECI {
+
+ private static Hashtable VALUE_TO_ECI;
+ private static Hashtable NAME_TO_ECI;
+
+ private static void initialize() {
+ VALUE_TO_ECI = new Hashtable(29);
+ NAME_TO_ECI = new Hashtable(29);
+ // TODO figure out if these values are even right!
+ addCharacterSet(0, "Cp437");
+ addCharacterSet(1, new String[] {"ISO8859_1", "ISO-8859-1"});
+ addCharacterSet(2, "Cp437");
+ addCharacterSet(3, new String[] {"ISO8859_1", "ISO-8859-1"});
+ addCharacterSet(4, "ISO8859_2");
+ addCharacterSet(5, "ISO8859_3");
+ addCharacterSet(6, "ISO8859_4");
+ addCharacterSet(7, "ISO8859_5");
+ addCharacterSet(8, "ISO8859_6");
+ addCharacterSet(9, "ISO8859_7");
+ addCharacterSet(10, "ISO8859_8");
+ addCharacterSet(11, "ISO8859_9");
+ addCharacterSet(12, "ISO8859_10");
+ addCharacterSet(13, "ISO8859_11");
+ addCharacterSet(15, "ISO8859_13");
+ addCharacterSet(16, "ISO8859_14");
+ addCharacterSet(17, "ISO8859_15");
+ addCharacterSet(18, "ISO8859_16");
+ addCharacterSet(20, new String[] {"SJIS", "Shift_JIS"});
+ }
+
+ private final String encodingName;
+
+ private CharacterSetECI(int value, String encodingName) {
+ super(value);
+ this.encodingName = encodingName;
+ }
+
+ public String getEncodingName() {
+ return encodingName;
+ }
+
+ private static void addCharacterSet(int value, String encodingName) {
+ CharacterSetECI eci = new CharacterSetECI(value, encodingName);
+ VALUE_TO_ECI.put(new Integer(value), eci); // can't use valueOf
+ NAME_TO_ECI.put(encodingName, eci);
+ }
+
+ private static void addCharacterSet(int value, String[] encodingNames) {
+ CharacterSetECI eci = new CharacterSetECI(value, encodingNames[0]);
+ VALUE_TO_ECI.put(new Integer(value), eci); // can't use valueOf
+ for (int i = 0; i < encodingNames.length; i++) {
+ NAME_TO_ECI.put(encodingNames[i], eci);
+ }
+ }
+
+ /**
+ * @param value character set ECI value
+ * @return CharacterSetECI representing ECI of given value, or null if it is legal but
+ * unsupported
+ * @throws IllegalArgumentException if ECI value is invalid
+ */
+ public static CharacterSetECI getCharacterSetECIByValue(int value) {
+ if (VALUE_TO_ECI == null) {
+ initialize();
+ }
+ if (value < 0 || value >= 900) {
+ throw new IllegalArgumentException("Bad ECI value: " + value);
+ }
+ return (CharacterSetECI) VALUE_TO_ECI.get(new Integer(value));
+ }
+
+ /**
+ * @param name character set ECI encoding name
+ * @return CharacterSetECI representing ECI for character encoding, or null if it is legal
+ * but unsupported
+ */
+ public static CharacterSetECI getCharacterSetECIByName(String name) {
+ if (NAME_TO_ECI == null) {
+ initialize();
+ }
+ return (CharacterSetECI) NAME_TO_ECI.get(name);
+ }
+
+}
\ No newline at end of file
diff --git a/libraries/zxing/src/com/google/zxing/common/Collections.java b/libraries/zxing/src/com/google/zxing/common/Collections.java
new file mode 100644
index 000000000..319ebfe6d
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/common/Collections.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2007 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.common;
+
+import java.util.Vector;
+
+/**
+ *
This is basically a substitute for java.util.Collections, which is not
+ * present in MIDP 2.0 / CLDC 1.1.
+ *
+ * @author Sean Owen
+ */
+public final class Collections {
+
+ private Collections() {
+ }
+
+ /**
+ * Sorts its argument (destructively) using insert sort; in the context of this package
+ * insertion sort is simple and efficient given its relatively small inputs.
+ *
+ * @param vector vector to sort
+ * @param comparator comparator to define sort ordering
+ */
+ public static void insertionSort(Vector vector, Comparator comparator) {
+ int max = vector.size();
+ for (int i = 1; i < max; i++) {
+ Object value = vector.elementAt(i);
+ int j = i - 1;
+ Object valueB;
+ while (j >= 0 && comparator.compare((valueB = vector.elementAt(j)), value) > 0) {
+ vector.setElementAt(valueB, j + 1);
+ j--;
+ }
+ vector.setElementAt(value, j + 1);
+ }
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/common/Comparator.java b/libraries/zxing/src/com/google/zxing/common/Comparator.java
new file mode 100644
index 000000000..e1be15e31
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/common/Comparator.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2007 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.common;
+
+/**
+ * This is merely a clone of Comparator since it is not available in
+ * CLDC 1.1 / MIDP 2.0.
+ */
+public interface Comparator {
+
+ int compare(Object o1, Object o2);
+
+}
\ No newline at end of file
diff --git a/libraries/zxing/src/com/google/zxing/common/DecoderResult.java b/libraries/zxing/src/com/google/zxing/common/DecoderResult.java
new file mode 100644
index 000000000..7e0855333
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/common/DecoderResult.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2007 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.common;
+
+import java.util.Vector;
+
+/**
+ *
Encapsulates the result of decoding a matrix of bits. This typically
+ * applies to 2D barcode formats. For now it contains the raw bytes obtained,
+ * as well as a String interpretation of those bytes, if applicable.
+ *
+ * @author Sean Owen
+ */
+public final class DecoderResult {
+
+ private final byte[] rawBytes;
+ private final String text;
+ private final Vector byteSegments;
+ private final String ecLevel;
+
+ public DecoderResult(byte[] rawBytes, String text, Vector byteSegments, String ecLevel) {
+ if (rawBytes == null && text == null) {
+ throw new IllegalArgumentException();
+ }
+ this.rawBytes = rawBytes;
+ this.text = text;
+ this.byteSegments = byteSegments;
+ this.ecLevel = ecLevel;
+ }
+
+ public byte[] getRawBytes() {
+ return rawBytes;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public Vector getByteSegments() {
+ return byteSegments;
+ }
+
+ public String getECLevel() {
+ return ecLevel;
+ }
+
+}
\ No newline at end of file
diff --git a/libraries/zxing/src/com/google/zxing/common/DefaultGridSampler.java b/libraries/zxing/src/com/google/zxing/common/DefaultGridSampler.java
new file mode 100644
index 000000000..74c9e7c6b
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/common/DefaultGridSampler.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2007 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.common;
+
+import com.google.zxing.NotFoundException;
+
+/**
+ * @author Sean Owen
+ */
+public final class DefaultGridSampler extends GridSampler {
+
+ public BitMatrix sampleGrid(BitMatrix image,
+ int dimensionX,
+ int dimensionY,
+ float p1ToX, float p1ToY,
+ float p2ToX, float p2ToY,
+ float p3ToX, float p3ToY,
+ float p4ToX, float p4ToY,
+ float p1FromX, float p1FromY,
+ float p2FromX, float p2FromY,
+ float p3FromX, float p3FromY,
+ float p4FromX, float p4FromY) throws NotFoundException {
+
+ PerspectiveTransform transform = PerspectiveTransform.quadrilateralToQuadrilateral(
+ p1ToX, p1ToY, p2ToX, p2ToY, p3ToX, p3ToY, p4ToX, p4ToY,
+ p1FromX, p1FromY, p2FromX, p2FromY, p3FromX, p3FromY, p4FromX, p4FromY);
+
+ return sampleGrid(image, dimensionX, dimensionY, transform);
+ }
+
+ public BitMatrix sampleGrid(BitMatrix image,
+ int dimensionX,
+ int dimensionY,
+ PerspectiveTransform transform) throws NotFoundException {
+ if (dimensionX <= 0 || dimensionY <= 0) {
+ throw NotFoundException.getNotFoundInstance();
+ }
+ BitMatrix bits = new BitMatrix(dimensionX, dimensionY);
+ float[] points = new float[dimensionX << 1];
+ for (int y = 0; y < dimensionY; y++) {
+ int max = points.length;
+ float iValue = (float) y + 0.5f;
+ for (int x = 0; x < max; x += 2) {
+ points[x] = (float) (x >> 1) + 0.5f;
+ points[x + 1] = iValue;
+ }
+ transform.transformPoints(points);
+ // Quick check to see if points transformed to something inside the image;
+ // sufficient to check the endpoints
+ checkAndNudgePoints(image, points);
+ try {
+ for (int x = 0; x < max; x += 2) {
+ if (image.get((int) points[x], (int) points[x + 1])) {
+ // Black(-ish) pixel
+ bits.set(x >> 1, y);
+ }
+ }
+ } catch (ArrayIndexOutOfBoundsException aioobe) {
+ // This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting
+ // transform gets "twisted" such that it maps a straight line of points to a set of points
+ // whose endpoints are in bounds, but others are not. There is probably some mathematical
+ // way to detect this about the transformation that I don't know yet.
+ // This results in an ugly runtime exception despite our clever checks above -- can't have
+ // that. We could check each point's coordinates but that feels duplicative. We settle for
+ // catching and wrapping ArrayIndexOutOfBoundsException.
+ throw NotFoundException.getNotFoundInstance();
+ }
+ }
+ return bits;
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/common/DetectorResult.java b/libraries/zxing/src/com/google/zxing/common/DetectorResult.java
new file mode 100644
index 000000000..ea4794d17
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/common/DetectorResult.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2007 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.common;
+
+import com.google.zxing.ResultPoint;
+
+/**
+ *
Encapsulates the result of detecting a barcode in an image. This includes the raw
+ * matrix of black/white pixels corresponding to the barcode, and possibly points of interest
+ * in the image, like the location of finder patterns or corners of the barcode in the image.
+ *
+ * @author Sean Owen
+ */
+public class DetectorResult {
+
+ private final BitMatrix bits;
+ private final ResultPoint[] points;
+
+ public DetectorResult(BitMatrix bits, ResultPoint[] points) {
+ this.bits = bits;
+ this.points = points;
+ }
+
+ public BitMatrix getBits() {
+ return bits;
+ }
+
+ public ResultPoint[] getPoints() {
+ return points;
+ }
+
+}
\ No newline at end of file
diff --git a/libraries/zxing/src/com/google/zxing/common/ECI.java b/libraries/zxing/src/com/google/zxing/common/ECI.java
new file mode 100644
index 000000000..444c779c2
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/common/ECI.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 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.common;
+
+/**
+ * Superclass of classes encapsulating types ECIs, according to "Extended Channel Interpretations"
+ * 5.3 of ISO 18004.
+ *
+ * @author Sean Owen
+ */
+public abstract class ECI {
+
+ private final int value;
+
+ ECI(int value) {
+ this.value = value;
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ /**
+ * @param value ECI value
+ * @return ECI representing ECI of given value, or null if it is legal but unsupported
+ * @throws IllegalArgumentException if ECI value is invalid
+ */
+ public static ECI getECIByValue(int value) {
+ if (value < 0 || value > 999999) {
+ throw new IllegalArgumentException("Bad ECI value: " + value);
+ }
+ if (value < 900) { // Character set ECIs use 000000 - 000899
+ return CharacterSetECI.getCharacterSetECIByValue(value);
+ }
+ return null;
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/common/GlobalHistogramBinarizer.java b/libraries/zxing/src/com/google/zxing/common/GlobalHistogramBinarizer.java
new file mode 100644
index 000000000..4fa2a887b
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/common/GlobalHistogramBinarizer.java
@@ -0,0 +1,194 @@
+/*
+ * 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.common;
+
+import com.google.zxing.Binarizer;
+import com.google.zxing.LuminanceSource;
+import com.google.zxing.NotFoundException;
+
+/**
+ * This Binarizer implementation uses the old ZXing global histogram approach. It is suitable
+ * for low-end mobile devices which don't have enough CPU or memory to use a local thresholding
+ * algorithm. However, because it picks a global black point, it cannot handle difficult shadows
+ * and gradients.
+ *
+ * Faster mobile devices and all desktop applications should probably use HybridBinarizer instead.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ * @author Sean Owen
+ */
+public class GlobalHistogramBinarizer extends Binarizer {
+
+ private static final int LUMINANCE_BITS = 5;
+ private static final int LUMINANCE_SHIFT = 8 - LUMINANCE_BITS;
+ private static final int LUMINANCE_BUCKETS = 1 << LUMINANCE_BITS;
+
+ private byte[] luminances = null;
+ private int[] buckets = null;
+
+ public GlobalHistogramBinarizer(LuminanceSource source) {
+ super(source);
+ }
+
+ // Applies simple sharpening to the row data to improve performance of the 1D Readers.
+ public BitArray getBlackRow(int y, BitArray row) throws NotFoundException {
+ LuminanceSource source = getLuminanceSource();
+ int width = source.getWidth();
+ if (row == null || row.getSize() < width) {
+ row = new BitArray(width);
+ } else {
+ row.clear();
+ }
+
+ initArrays(width);
+ byte[] localLuminances = source.getRow(y, luminances);
+ int[] localBuckets = buckets;
+ for (int x = 0; x < width; x++) {
+ int pixel = localLuminances[x] & 0xff;
+ localBuckets[pixel >> LUMINANCE_SHIFT]++;
+ }
+ int blackPoint = estimateBlackPoint(localBuckets);
+
+ int left = localLuminances[0] & 0xff;
+ int center = localLuminances[1] & 0xff;
+ for (int x = 1; x < width - 1; x++) {
+ int right = localLuminances[x + 1] & 0xff;
+ // A simple -1 4 -1 box filter with a weight of 2.
+ int luminance = ((center << 2) - left - right) >> 1;
+ if (luminance < blackPoint) {
+ row.set(x);
+ }
+ left = center;
+ center = right;
+ }
+ return row;
+ }
+
+ // Does not sharpen the data, as this call is intended to only be used by 2D Readers.
+ public BitMatrix getBlackMatrix() throws NotFoundException {
+ LuminanceSource source = getLuminanceSource();
+ int width = source.getWidth();
+ int height = source.getHeight();
+ BitMatrix matrix = new BitMatrix(width, height);
+
+ // Quickly calculates the histogram by sampling four rows from the image. This proved to be
+ // more robust on the blackbox tests than sampling a diagonal as we used to do.
+ initArrays(width);
+ int[] localBuckets = buckets;
+ for (int y = 1; y < 5; y++) {
+ int row = height * y / 5;
+ byte[] localLuminances = source.getRow(row, luminances);
+ int right = (width << 2) / 5;
+ for (int x = width / 5; x < right; x++) {
+ int pixel = localLuminances[x] & 0xff;
+ localBuckets[pixel >> LUMINANCE_SHIFT]++;
+ }
+ }
+ int blackPoint = estimateBlackPoint(localBuckets);
+
+ // We delay reading the entire image luminance until the black point estimation succeeds.
+ // Although we end up reading four rows twice, it is consistent with our motto of
+ // "fail quickly" which is necessary for continuous scanning.
+ byte[] localLuminances = source.getMatrix();
+ for (int y = 0; y < height; y++) {
+ int offset = y * width;
+ for (int x = 0; x< width; x++) {
+ int pixel = localLuminances[offset + x] & 0xff;
+ if (pixel < blackPoint) {
+ matrix.set(x, y);
+ }
+ }
+ }
+
+ return matrix;
+ }
+
+ public Binarizer createBinarizer(LuminanceSource source) {
+ return new GlobalHistogramBinarizer(source);
+ }
+
+ private void initArrays(int luminanceSize) {
+ if (luminances == null || luminances.length < luminanceSize) {
+ luminances = new byte[luminanceSize];
+ }
+ if (buckets == null) {
+ buckets = new int[LUMINANCE_BUCKETS];
+ } else {
+ for (int x = 0; x < LUMINANCE_BUCKETS; x++) {
+ buckets[x] = 0;
+ }
+ }
+ }
+
+ private static int estimateBlackPoint(int[] buckets) throws NotFoundException {
+ // Find the tallest peak in the histogram.
+ int numBuckets = buckets.length;
+ int maxBucketCount = 0;
+ int firstPeak = 0;
+ int firstPeakSize = 0;
+ for (int x = 0; x < numBuckets; x++) {
+ if (buckets[x] > firstPeakSize) {
+ firstPeak = x;
+ firstPeakSize = buckets[x];
+ }
+ if (buckets[x] > maxBucketCount) {
+ maxBucketCount = buckets[x];
+ }
+ }
+
+ // Find the second-tallest peak which is somewhat far from the tallest peak.
+ int secondPeak = 0;
+ int secondPeakScore = 0;
+ for (int x = 0; x < numBuckets; x++) {
+ int distanceToBiggest = x - firstPeak;
+ // Encourage more distant second peaks by multiplying by square of distance.
+ int score = buckets[x] * distanceToBiggest * distanceToBiggest;
+ if (score > secondPeakScore) {
+ secondPeak = x;
+ secondPeakScore = score;
+ }
+ }
+
+ // Make sure firstPeak corresponds to the black peak.
+ if (firstPeak > secondPeak) {
+ int temp = firstPeak;
+ firstPeak = secondPeak;
+ secondPeak = temp;
+ }
+
+ // If there is too little contrast in the image to pick a meaningful black point, throw rather
+ // than waste time trying to decode the image, and risk false positives.
+ if (secondPeak - firstPeak <= numBuckets >> 4) {
+ throw NotFoundException.getNotFoundInstance();
+ }
+
+ // Find a valley between them that is low and closer to the white peak.
+ int bestValley = secondPeak - 1;
+ int bestValleyScore = -1;
+ for (int x = secondPeak - 1; x > firstPeak; x--) {
+ int fromFirst = x - firstPeak;
+ int score = fromFirst * fromFirst * (secondPeak - x) * (maxBucketCount - buckets[x]);
+ if (score > bestValleyScore) {
+ bestValley = x;
+ bestValleyScore = score;
+ }
+ }
+
+ return bestValley << LUMINANCE_SHIFT;
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/common/GridSampler.java b/libraries/zxing/src/com/google/zxing/common/GridSampler.java
new file mode 100644
index 000000000..7f26c264e
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/common/GridSampler.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2007 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.common;
+
+import com.google.zxing.NotFoundException;
+
+/**
+ * Implementations of this class can, given locations of finder patterns for a QR code in an
+ * image, sample the right points in the image to reconstruct the QR code, accounting for
+ * perspective distortion. It is abstracted since it is relatively expensive and should be allowed
+ * to take advantage of platform-specific optimized implementations, like Sun's Java Advanced
+ * Imaging library, but which may not be available in other environments such as J2ME, and vice
+ * versa.
+ *
+ * The implementation used can be controlled by calling {@link #setGridSampler(GridSampler)}
+ * with an instance of a class which implements this interface.
+ *
+ * @author Sean Owen
+ */
+public abstract class GridSampler {
+
+ private static GridSampler gridSampler = new DefaultGridSampler();
+
+ /**
+ * Sets the implementation of GridSampler used by the library. One global
+ * instance is stored, which may sound problematic. But, the implementation provided
+ * ought to be appropriate for the entire platform, and all uses of this library
+ * in the whole lifetime of the JVM. For instance, an Android activity can swap in
+ * an implementation that takes advantage of native platform libraries.
+ *
+ * @param newGridSampler The platform-specific object to install.
+ */
+ public static void setGridSampler(GridSampler newGridSampler) {
+ if (newGridSampler == null) {
+ throw new IllegalArgumentException();
+ }
+ gridSampler = newGridSampler;
+ }
+
+ /**
+ * @return the current implementation of GridSampler
+ */
+ public static GridSampler getInstance() {
+ return gridSampler;
+ }
+
+ /**
+ * Samples an image for a rectangular matrix of bits of the given dimension.
+ * @param image image to sample
+ * @param dimensionX width of {@link BitMatrix} to sample from image
+ * @param dimensionY height of {@link BitMatrix} to sample from image
+ * @return {@link BitMatrix} representing a grid of points sampled from the image within a region
+ * defined by the "from" parameters
+ * @throws NotFoundException if image can't be sampled, for example, if the transformation defined
+ * by the given points is invalid or results in sampling outside the image boundaries
+ */
+ public abstract BitMatrix sampleGrid(BitMatrix image,
+ int dimensionX,
+ int dimensionY,
+ float p1ToX, float p1ToY,
+ float p2ToX, float p2ToY,
+ float p3ToX, float p3ToY,
+ float p4ToX, float p4ToY,
+ float p1FromX, float p1FromY,
+ float p2FromX, float p2FromY,
+ float p3FromX, float p3FromY,
+ float p4FromX, float p4FromY) throws NotFoundException;
+
+ public abstract BitMatrix sampleGrid(BitMatrix image,
+ int dimensionX,
+ int dimensionY,
+ PerspectiveTransform transform) throws NotFoundException;
+
+ /**
+ *
Checks a set of points that have been transformed to sample points on an image against
+ * the image's dimensions to see if the point are even within the image.
+ *
+ *
This method will actually "nudge" the endpoints back onto the image if they are found to be
+ * barely (less than 1 pixel) off the image. This accounts for imperfect detection of finder
+ * patterns in an image where the QR Code runs all the way to the image border.
+ *
+ *
For efficiency, the method will check points from either end of the line until one is found
+ * to be within the image. Because the set of points are assumed to be linear, this is valid.
+ *
+ * @param image image into which the points should map
+ * @param points actual points in x1,y1,...,xn,yn form
+ * @throws NotFoundException if an endpoint is lies outside the image boundaries
+ */
+ protected static void checkAndNudgePoints(BitMatrix image, float[] points) throws NotFoundException {
+ int width = image.getWidth();
+ int height = image.getHeight();
+ // Check and nudge points from start until we see some that are OK:
+ boolean nudged = true;
+ for (int offset = 0; offset < points.length && nudged; offset += 2) {
+ int x = (int) points[offset];
+ int y = (int) points[offset + 1];
+ if (x < -1 || x > width || y < -1 || y > height) {
+ throw NotFoundException.getNotFoundInstance();
+ }
+ nudged = false;
+ if (x == -1) {
+ points[offset] = 0.0f;
+ nudged = true;
+ } else if (x == width) {
+ points[offset] = width - 1;
+ nudged = true;
+ }
+ if (y == -1) {
+ points[offset + 1] = 0.0f;
+ nudged = true;
+ } else if (y == height) {
+ points[offset + 1] = height - 1;
+ nudged = true;
+ }
+ }
+ // Check and nudge points from end:
+ nudged = true;
+ for (int offset = points.length - 2; offset >= 0 && nudged; offset -= 2) {
+ int x = (int) points[offset];
+ int y = (int) points[offset + 1];
+ if (x < -1 || x > width || y < -1 || y > height) {
+ throw NotFoundException.getNotFoundInstance();
+ }
+ nudged = false;
+ if (x == -1) {
+ points[offset] = 0.0f;
+ nudged = true;
+ } else if (x == width) {
+ points[offset] = width - 1;
+ nudged = true;
+ }
+ if (y == -1) {
+ points[offset + 1] = 0.0f;
+ nudged = true;
+ } else if (y == height) {
+ points[offset + 1] = height - 1;
+ nudged = true;
+ }
+ }
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/common/HybridBinarizer.java b/libraries/zxing/src/com/google/zxing/common/HybridBinarizer.java
new file mode 100644
index 000000000..b482c1a22
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/common/HybridBinarizer.java
@@ -0,0 +1,185 @@
+/*
+ * 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.common;
+
+import com.google.zxing.Binarizer;
+import com.google.zxing.LuminanceSource;
+import com.google.zxing.NotFoundException;
+
+/**
+ * This class implements a local thresholding algorithm, which while slower than the
+ * GlobalHistogramBinarizer, is fairly efficient for what it does. It is designed for
+ * high frequency images of barcodes with black data on white backgrounds. For this application,
+ * it does a much better job than a global blackpoint with severe shadows and gradients.
+ * However it tends to produce artifacts on lower frequency images and is therefore not
+ * a good general purpose binarizer for uses outside ZXing.
+ *
+ * This class extends GlobalHistogramBinarizer, using the older histogram approach for 1D readers,
+ * and the newer local approach for 2D readers. 1D decoding using a per-row histogram is already
+ * inherently local, and only fails for horizontal gradients. We can revisit that problem later,
+ * but for now it was not a win to use local blocks for 1D.
+ *
+ * This Binarizer is the default for the unit tests and the recommended class for library users.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+public final class HybridBinarizer extends GlobalHistogramBinarizer {
+
+ // This class uses 5x5 blocks to compute local luminance, where each block is 8x8 pixels.
+ // So this is the smallest dimension in each axis we can accept.
+ private static final int MINIMUM_DIMENSION = 40;
+
+ private BitMatrix matrix = null;
+
+ public HybridBinarizer(LuminanceSource source) {
+ super(source);
+ }
+
+ public BitMatrix getBlackMatrix() throws NotFoundException {
+ binarizeEntireImage();
+ return matrix;
+ }
+
+ public Binarizer createBinarizer(LuminanceSource source) {
+ return new HybridBinarizer(source);
+ }
+
+ // Calculates the final BitMatrix once for all requests. This could be called once from the
+ // constructor instead, but there are some advantages to doing it lazily, such as making
+ // profiling easier, and not doing heavy lifting when callers don't expect it.
+ private void binarizeEntireImage() throws NotFoundException {
+ if (matrix == null) {
+ LuminanceSource source = getLuminanceSource();
+ if (source.getWidth() >= MINIMUM_DIMENSION && source.getHeight() >= MINIMUM_DIMENSION) {
+ byte[] luminances = source.getMatrix();
+ int width = source.getWidth();
+ int height = source.getHeight();
+ int subWidth = width >> 3;
+ if ((width & 0x07) != 0) {
+ subWidth++;
+ }
+ int subHeight = height >> 3;
+ if ((height & 0x07) != 0) {
+ subHeight++;
+ }
+ int[][] blackPoints = calculateBlackPoints(luminances, subWidth, subHeight, width, height);
+
+ matrix = new BitMatrix(width, height);
+ calculateThresholdForBlock(luminances, subWidth, subHeight, width, height, blackPoints, matrix);
+ } else {
+ // If the image is too small, fall back to the global histogram approach.
+ matrix = super.getBlackMatrix();
+ }
+ }
+ }
+
+ // For each 8x8 block in the image, calculate the average black point using a 5x5 grid
+ // of the blocks around it. Also handles the corner cases (fractional blocks are computed based
+ // on the last 8 pixels in the row/column which are also used in the previous block).
+ private static void calculateThresholdForBlock(byte[] luminances, int subWidth, int subHeight,
+ int width, int height, int[][] blackPoints, BitMatrix matrix) {
+ for (int y = 0; y < subHeight; y++) {
+ int yoffset = y << 3;
+ if ((yoffset + 8) >= height) {
+ yoffset = height - 8;
+ }
+ for (int x = 0; x < subWidth; x++) {
+ int xoffset = x << 3;
+ if ((xoffset + 8) >= width) {
+ xoffset = width - 8;
+ }
+ int left = x > 1 ? x : 2;
+ left = left < subWidth - 2 ? left : subWidth - 3;
+ int top = y > 1 ? y : 2;
+ top = top < subHeight - 2 ? top : subHeight - 3;
+ int sum = 0;
+ for (int z = -2; z <= 2; z++) {
+ int[] blackRow = blackPoints[top + z];
+ sum += blackRow[left - 2];
+ sum += blackRow[left - 1];
+ sum += blackRow[left];
+ sum += blackRow[left + 1];
+ sum += blackRow[left + 2];
+ }
+ int average = sum / 25;
+ threshold8x8Block(luminances, xoffset, yoffset, average, width, matrix);
+ }
+ }
+ }
+
+ // Applies a single threshold to an 8x8 block of pixels.
+ private static void threshold8x8Block(byte[] luminances, int xoffset, int yoffset, int threshold,
+ int stride, BitMatrix matrix) {
+ for (int y = 0; y < 8; y++) {
+ int offset = (yoffset + y) * stride + xoffset;
+ for (int x = 0; x < 8; x++) {
+ int pixel = luminances[offset + x] & 0xff;
+ if (pixel < threshold) {
+ matrix.set(xoffset + x, yoffset + y);
+ }
+ }
+ }
+ }
+
+ // Calculates a single black point for each 8x8 block of pixels and saves it away.
+ private static int[][] calculateBlackPoints(byte[] luminances, int subWidth, int subHeight,
+ int width, int height) {
+ int[][] blackPoints = new int[subHeight][subWidth];
+ for (int y = 0; y < subHeight; y++) {
+ int yoffset = y << 3;
+ if ((yoffset + 8) >= height) {
+ yoffset = height - 8;
+ }
+ for (int x = 0; x < subWidth; x++) {
+ int xoffset = x << 3;
+ if ((xoffset + 8) >= width) {
+ xoffset = width - 8;
+ }
+ int sum = 0;
+ int min = 255;
+ int max = 0;
+ for (int yy = 0; yy < 8; yy++) {
+ int offset = (yoffset + yy) * width + xoffset;
+ for (int xx = 0; xx < 8; xx++) {
+ int pixel = luminances[offset + xx] & 0xff;
+ sum += pixel;
+ if (pixel < min) {
+ min = pixel;
+ }
+ if (pixel > max) {
+ max = pixel;
+ }
+ }
+ }
+
+ // If the contrast is inadequate, use half the minimum, so that this block will be
+ // treated as part of the white background, but won't drag down neighboring blocks
+ // too much.
+ int average;
+ if (max - min > 24) {
+ average = sum >> 6;
+ } else {
+ // When min == max == 0, let average be 1 so all is black
+ average = max == 0 ? 1 : min >> 1;
+ }
+ blackPoints[y][x] = average;
+ }
+ }
+ return blackPoints;
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/common/PerspectiveTransform.java b/libraries/zxing/src/com/google/zxing/common/PerspectiveTransform.java
new file mode 100644
index 000000000..9e65baff1
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/common/PerspectiveTransform.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2007 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.common;
+
+/**
+ *
This class implements a perspective transform in two dimensions. Given four source and four
+ * destination points, it will compute the transformation implied between them. The code is based
+ * directly upon section 3.4.2 of George Wolberg's "Digital Image Warping"; see pages 54-56.
+ *
+ * @author Sean Owen
+ */
+public final class PerspectiveTransform {
+
+ private final float a11, a12, a13, a21, a22, a23, a31, a32, a33;
+
+ private PerspectiveTransform(float a11, float a21, float a31,
+ float a12, float a22, float a32,
+ float a13, float a23, float a33) {
+ this.a11 = a11;
+ this.a12 = a12;
+ this.a13 = a13;
+ this.a21 = a21;
+ this.a22 = a22;
+ this.a23 = a23;
+ this.a31 = a31;
+ this.a32 = a32;
+ this.a33 = a33;
+ }
+
+ public static PerspectiveTransform quadrilateralToQuadrilateral(float x0, float y0,
+ float x1, float y1,
+ float x2, float y2,
+ float x3, float y3,
+ float x0p, float y0p,
+ float x1p, float y1p,
+ float x2p, float y2p,
+ float x3p, float y3p) {
+
+ PerspectiveTransform qToS = quadrilateralToSquare(x0, y0, x1, y1, x2, y2, x3, y3);
+ PerspectiveTransform sToQ = squareToQuadrilateral(x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p);
+ return sToQ.times(qToS);
+ }
+
+ public void transformPoints(float[] points) {
+ int max = points.length;
+ float a11 = this.a11;
+ float a12 = this.a12;
+ float a13 = this.a13;
+ float a21 = this.a21;
+ float a22 = this.a22;
+ float a23 = this.a23;
+ float a31 = this.a31;
+ float a32 = this.a32;
+ float a33 = this.a33;
+ for (int i = 0; i < max; i += 2) {
+ float x = points[i];
+ float y = points[i + 1];
+ float denominator = a13 * x + a23 * y + a33;
+ points[i] = (a11 * x + a21 * y + a31) / denominator;
+ points[i + 1] = (a12 * x + a22 * y + a32) / denominator;
+ }
+ }
+
+ /** Convenience method, not optimized for performance. */
+ public void transformPoints(float[] xValues, float[] yValues) {
+ int n = xValues.length;
+ for (int i = 0; i < n; i ++) {
+ float x = xValues[i];
+ float y = yValues[i];
+ float denominator = a13 * x + a23 * y + a33;
+ xValues[i] = (a11 * x + a21 * y + a31) / denominator;
+ yValues[i] = (a12 * x + a22 * y + a32) / denominator;
+ }
+ }
+
+ public static PerspectiveTransform squareToQuadrilateral(float x0, float y0,
+ float x1, float y1,
+ float x2, float y2,
+ float x3, float y3) {
+ float dy2 = y3 - y2;
+ float dy3 = y0 - y1 + y2 - y3;
+ if (dy2 == 0.0f && dy3 == 0.0f) {
+ return new PerspectiveTransform(x1 - x0, x2 - x1, x0,
+ y1 - y0, y2 - y1, y0,
+ 0.0f, 0.0f, 1.0f);
+ } else {
+ float dx1 = x1 - x2;
+ float dx2 = x3 - x2;
+ float dx3 = x0 - x1 + x2 - x3;
+ float dy1 = y1 - y2;
+ float denominator = dx1 * dy2 - dx2 * dy1;
+ float a13 = (dx3 * dy2 - dx2 * dy3) / denominator;
+ float a23 = (dx1 * dy3 - dx3 * dy1) / denominator;
+ return new PerspectiveTransform(x1 - x0 + a13 * x1, x3 - x0 + a23 * x3, x0,
+ y1 - y0 + a13 * y1, y3 - y0 + a23 * y3, y0,
+ a13, a23, 1.0f);
+ }
+ }
+
+ public static PerspectiveTransform quadrilateralToSquare(float x0, float y0,
+ float x1, float y1,
+ float x2, float y2,
+ float x3, float y3) {
+ // Here, the adjoint serves as the inverse:
+ return squareToQuadrilateral(x0, y0, x1, y1, x2, y2, x3, y3).buildAdjoint();
+ }
+
+ PerspectiveTransform buildAdjoint() {
+ // Adjoint is the transpose of the cofactor matrix:
+ return new PerspectiveTransform(a22 * a33 - a23 * a32,
+ a23 * a31 - a21 * a33,
+ a21 * a32 - a22 * a31,
+ a13 * a32 - a12 * a33,
+ a11 * a33 - a13 * a31,
+ a12 * a31 - a11 * a32,
+ a12 * a23 - a13 * a22,
+ a13 * a21 - a11 * a23,
+ a11 * a22 - a12 * a21);
+ }
+
+ PerspectiveTransform times(PerspectiveTransform other) {
+ return new PerspectiveTransform(a11 * other.a11 + a21 * other.a12 + a31 * other.a13,
+ a11 * other.a21 + a21 * other.a22 + a31 * other.a23,
+ a11 * other.a31 + a21 * other.a32 + a31 * other.a33,
+ a12 * other.a11 + a22 * other.a12 + a32 * other.a13,
+ a12 * other.a21 + a22 * other.a22 + a32 * other.a23,
+ a12 * other.a31 + a22 * other.a32 + a32 * other.a33,
+ a13 * other.a11 + a23 * other.a12 + a33 * other.a13,
+ a13 * other.a21 + a23 * other.a22 + a33 * other.a23,
+ a13 * other.a31 + a23 * other.a32 + a33 * other.a33);
+
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/common/StringUtils.java b/libraries/zxing/src/com/google/zxing/common/StringUtils.java
new file mode 100644
index 000000000..97999f997
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/common/StringUtils.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2010 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.common;
+
+import java.util.Hashtable;
+
+import com.google.zxing.DecodeHintType;
+
+/**
+ * Common string-related functions.
+ *
+ * @author Sean Owen
+ */
+public final class StringUtils {
+
+ private static final String PLATFORM_DEFAULT_ENCODING =
+ System.getProperty("file.encoding");
+ public static final String SHIFT_JIS = "SJIS";
+ public static final String GB2312 = "GB2312";
+ private static final String EUC_JP = "EUC_JP";
+ private static final String UTF8 = "UTF8";
+ private static final String ISO88591 = "ISO8859_1";
+ private static final boolean ASSUME_SHIFT_JIS =
+ SHIFT_JIS.equalsIgnoreCase(PLATFORM_DEFAULT_ENCODING) ||
+ EUC_JP.equalsIgnoreCase(PLATFORM_DEFAULT_ENCODING);
+
+ private StringUtils() {}
+
+ /**
+ * @param bytes bytes encoding a string, whose encoding should be guessed
+ * @param hints decode hints if applicable
+ * @return name of guessed encoding; at the moment will only guess one of:
+ * {@link #SHIFT_JIS}, {@link #UTF8}, {@link #ISO88591}, or the platform
+ * default encoding if none of these can possibly be correct
+ */
+ public static String guessEncoding(byte[] bytes, Hashtable hints) {
+ if (hints != null) {
+ String characterSet = (String) hints.get(DecodeHintType.CHARACTER_SET);
+ if (characterSet != null) {
+ return characterSet;
+ }
+ }
+ // Does it start with the UTF-8 byte order mark? then guess it's UTF-8
+ if (bytes.length > 3 &&
+ bytes[0] == (byte) 0xEF &&
+ bytes[1] == (byte) 0xBB &&
+ bytes[2] == (byte) 0xBF) {
+ return UTF8;
+ }
+ // For now, merely tries to distinguish ISO-8859-1, UTF-8 and Shift_JIS,
+ // which should be by far the most common encodings. ISO-8859-1
+ // should not have bytes in the 0x80 - 0x9F range, while Shift_JIS
+ // uses this as a first byte of a two-byte character. If we see this
+ // followed by a valid second byte in Shift_JIS, assume it is Shift_JIS.
+ // If we see something else in that second byte, we'll make the risky guess
+ // that it's UTF-8.
+ int length = bytes.length;
+ boolean canBeISO88591 = true;
+ boolean canBeShiftJIS = true;
+ boolean canBeUTF8 = true;
+ int utf8BytesLeft = 0;
+ int maybeDoubleByteCount = 0;
+ int maybeSingleByteKatakanaCount = 0;
+ boolean sawLatin1Supplement = false;
+ boolean sawUTF8Start = false;
+ boolean lastWasPossibleDoubleByteStart = false;
+
+ for (int i = 0;
+ i < length && (canBeISO88591 || canBeShiftJIS || canBeUTF8);
+ i++) {
+
+ int value = bytes[i] & 0xFF;
+
+ // UTF-8 stuff
+ if (value >= 0x80 && value <= 0xBF) {
+ if (utf8BytesLeft > 0) {
+ utf8BytesLeft--;
+ }
+ } else {
+ if (utf8BytesLeft > 0) {
+ canBeUTF8 = false;
+ }
+ if (value >= 0xC0 && value <= 0xFD) {
+ sawUTF8Start = true;
+ int valueCopy = value;
+ while ((valueCopy & 0x40) != 0) {
+ utf8BytesLeft++;
+ valueCopy <<= 1;
+ }
+ }
+ }
+
+ // ISO-8859-1 stuff
+
+ if ((value == 0xC2 || value == 0xC3) && i < length - 1) {
+ // This is really a poor hack. The slightly more exotic characters people might want to put in
+ // a QR Code, by which I mean the Latin-1 supplement characters (e.g. u-umlaut) have encodings
+ // that start with 0xC2 followed by [0xA0,0xBF], or start with 0xC3 followed by [0x80,0xBF].
+ int nextValue = bytes[i + 1] & 0xFF;
+ if (nextValue <= 0xBF &&
+ ((value == 0xC2 && nextValue >= 0xA0) || (value == 0xC3 && nextValue >= 0x80))) {
+ sawLatin1Supplement = true;
+ }
+ }
+ if (value >= 0x7F && value <= 0x9F) {
+ canBeISO88591 = false;
+ }
+
+ // Shift_JIS stuff
+
+ if (value >= 0xA1 && value <= 0xDF) {
+ // count the number of characters that might be a Shift_JIS single-byte Katakana character
+ if (!lastWasPossibleDoubleByteStart) {
+ maybeSingleByteKatakanaCount++;
+ }
+ }
+ if (!lastWasPossibleDoubleByteStart &&
+ ((value >= 0xF0 && value <= 0xFF) || value == 0x80 || value == 0xA0)) {
+ canBeShiftJIS = false;
+ }
+ if ((value >= 0x81 && value <= 0x9F) || (value >= 0xE0 && value <= 0xEF)) {
+ // These start double-byte characters in Shift_JIS. Let's see if it's followed by a valid
+ // second byte.
+ if (lastWasPossibleDoubleByteStart) {
+ // If we just checked this and the last byte for being a valid double-byte
+ // char, don't check starting on this byte. If this and the last byte
+ // formed a valid pair, then this shouldn't be checked to see if it starts
+ // a double byte pair of course.
+ lastWasPossibleDoubleByteStart = false;
+ } else {
+ // ... otherwise do check to see if this plus the next byte form a valid
+ // double byte pair encoding a character.
+ lastWasPossibleDoubleByteStart = true;
+ if (i >= bytes.length - 1) {
+ canBeShiftJIS = false;
+ } else {
+ int nextValue = bytes[i + 1] & 0xFF;
+ if (nextValue < 0x40 || nextValue > 0xFC) {
+ canBeShiftJIS = false;
+ } else {
+ maybeDoubleByteCount++;
+ }
+ // There is some conflicting information out there about which bytes can follow which in
+ // double-byte Shift_JIS characters. The rule above seems to be the one that matches practice.
+ }
+ }
+ } else {
+ lastWasPossibleDoubleByteStart = false;
+ }
+ }
+ if (utf8BytesLeft > 0) {
+ canBeUTF8 = false;
+ }
+
+ // Easy -- if assuming Shift_JIS and no evidence it can't be, done
+ if (canBeShiftJIS && ASSUME_SHIFT_JIS) {
+ return SHIFT_JIS;
+ }
+ if (canBeUTF8 && sawUTF8Start) {
+ return UTF8;
+ }
+ // Distinguishing Shift_JIS and ISO-8859-1 can be a little tough. The crude heuristic is:
+ // - If we saw
+ // - at least 3 bytes that starts a double-byte value (bytes that are rare in ISO-8859-1), or
+ // - over 5% of bytes could be single-byte Katakana (also rare in ISO-8859-1),
+ // - and, saw no sequences that are invalid in Shift_JIS, then we conclude Shift_JIS
+ if (canBeShiftJIS && (maybeDoubleByteCount >= 3 || 20 * maybeSingleByteKatakanaCount > length)) {
+ return SHIFT_JIS;
+ }
+ // Otherwise, we default to ISO-8859-1 unless we know it can't be
+ if (!sawLatin1Supplement && canBeISO88591) {
+ return ISO88591;
+ }
+ // Otherwise, we take a wild guess with platform encoding
+ return PLATFORM_DEFAULT_ENCODING;
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/common/detector/MonochromeRectangleDetector.java b/libraries/zxing/src/com/google/zxing/common/detector/MonochromeRectangleDetector.java
new file mode 100644
index 000000000..950a22364
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/common/detector/MonochromeRectangleDetector.java
@@ -0,0 +1,209 @@
+/*
+ * 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.common.detector;
+
+import com.google.zxing.NotFoundException;
+import com.google.zxing.ResultPoint;
+import com.google.zxing.common.BitMatrix;
+
+/**
+ *
A somewhat generic detector that looks for a barcode-like rectangular region within an image.
+ * It looks within a mostly white region of an image for a region of black and white, but mostly
+ * black. It returns the four corners of the region, as best it can determine.
+ *
+ * @author Sean Owen
+ */
+public final class MonochromeRectangleDetector {
+
+ private static final int MAX_MODULES = 32;
+
+ private final BitMatrix image;
+
+ public MonochromeRectangleDetector(BitMatrix image) {
+ this.image = image;
+ }
+
+ /**
+ *
Detects a rectangular region of black and white -- mostly black -- with a region of mostly
+ * white, in an image.
+ *
+ * @return {@link ResultPoint}[] describing the corners of the rectangular region. The first and
+ * last points are opposed on the diagonal, as are the second and third. The first point will be
+ * the topmost point and the last, the bottommost. The second point will be leftmost and the
+ * third, the rightmost
+ * @throws NotFoundException if no Data Matrix Code can be found
+ */
+ public ResultPoint[] detect() throws NotFoundException {
+ int height = image.getHeight();
+ int width = image.getWidth();
+ int halfHeight = height >> 1;
+ int halfWidth = width >> 1;
+ int deltaY = Math.max(1, height / (MAX_MODULES << 3));
+ int deltaX = Math.max(1, width / (MAX_MODULES << 3));
+
+ int top = 0;
+ int bottom = height;
+ int left = 0;
+ int right = width;
+ ResultPoint pointA = findCornerFromCenter(halfWidth, 0, left, right,
+ halfHeight, -deltaY, top, bottom, halfWidth >> 1);
+ top = (int) pointA.getY() - 1;
+ ResultPoint pointB = findCornerFromCenter(halfWidth, -deltaX, left, right,
+ halfHeight, 0, top, bottom, halfHeight >> 1);
+ left = (int) pointB.getX() - 1;
+ ResultPoint pointC = findCornerFromCenter(halfWidth, deltaX, left, right,
+ halfHeight, 0, top, bottom, halfHeight >> 1);
+ right = (int) pointC.getX() + 1;
+ ResultPoint pointD = findCornerFromCenter(halfWidth, 0, left, right,
+ halfHeight, deltaY, top, bottom, halfWidth >> 1);
+ bottom = (int) pointD.getY() + 1;
+
+ // Go try to find point A again with better information -- might have been off at first.
+ pointA = findCornerFromCenter(halfWidth, 0, left, right,
+ halfHeight, -deltaY, top, bottom, halfWidth >> 2);
+
+ return new ResultPoint[] { pointA, pointB, pointC, pointD };
+ }
+
+ /**
+ * Attempts to locate a corner of the barcode by scanning up, down, left or right from a center
+ * point which should be within the barcode.
+ *
+ * @param centerX center's x component (horizontal)
+ * @param deltaX same as deltaY but change in x per step instead
+ * @param left minimum value of x
+ * @param right maximum value of x
+ * @param centerY center's y component (vertical)
+ * @param deltaY change in y per step. If scanning up this is negative; down, positive;
+ * left or right, 0
+ * @param top minimum value of y to search through (meaningless when di == 0)
+ * @param bottom maximum value of y
+ * @param maxWhiteRun maximum run of white pixels that can still be considered to be within
+ * the barcode
+ * @return a {@link com.google.zxing.ResultPoint} encapsulating the corner that was found
+ * @throws NotFoundException if such a point cannot be found
+ */
+ private ResultPoint findCornerFromCenter(int centerX, int deltaX, int left, int right,
+ int centerY, int deltaY, int top, int bottom, int maxWhiteRun) throws NotFoundException {
+ int[] lastRange = null;
+ for (int y = centerY, x = centerX;
+ y < bottom && y >= top && x < right && x >= left;
+ y += deltaY, x += deltaX) {
+ int[] range;
+ if (deltaX == 0) {
+ // horizontal slices, up and down
+ range = blackWhiteRange(y, maxWhiteRun, left, right, true);
+ } else {
+ // vertical slices, left and right
+ range = blackWhiteRange(x, maxWhiteRun, top, bottom, false);
+ }
+ if (range == null) {
+ if (lastRange == null) {
+ throw NotFoundException.getNotFoundInstance();
+ }
+ // lastRange was found
+ if (deltaX == 0) {
+ int lastY = y - deltaY;
+ if (lastRange[0] < centerX) {
+ if (lastRange[1] > centerX) {
+ // straddle, choose one or the other based on direction
+ return new ResultPoint(deltaY > 0 ? lastRange[0] : lastRange[1], lastY);
+ }
+ return new ResultPoint(lastRange[0], lastY);
+ } else {
+ return new ResultPoint(lastRange[1], lastY);
+ }
+ } else {
+ int lastX = x - deltaX;
+ if (lastRange[0] < centerY) {
+ if (lastRange[1] > centerY) {
+ return new ResultPoint(lastX, deltaX < 0 ? lastRange[0] : lastRange[1]);
+ }
+ return new ResultPoint(lastX, lastRange[0]);
+ } else {
+ return new ResultPoint(lastX, lastRange[1]);
+ }
+ }
+ }
+ lastRange = range;
+ }
+ throw NotFoundException.getNotFoundInstance();
+ }
+
+ /**
+ * Computes the start and end of a region of pixels, either horizontally or vertically, that could
+ * be part of a Data Matrix barcode.
+ *
+ * @param fixedDimension if scanning horizontally, this is the row (the fixed vertical location)
+ * where we are scanning. If scanning vertically it's the column, the fixed horizontal location
+ * @param maxWhiteRun largest run of white pixels that can still be considered part of the
+ * barcode region
+ * @param minDim minimum pixel location, horizontally or vertically, to consider
+ * @param maxDim maximum pixel location, horizontally or vertically, to consider
+ * @param horizontal if true, we're scanning left-right, instead of up-down
+ * @return int[] with start and end of found range, or null if no such range is found
+ * (e.g. only white was found)
+ */
+ private int[] blackWhiteRange(int fixedDimension, int maxWhiteRun, int minDim, int maxDim,
+ boolean horizontal) {
+
+ int center = (minDim + maxDim) >> 1;
+
+ // Scan left/up first
+ int start = center;
+ while (start >= minDim) {
+ if (horizontal ? image.get(start, fixedDimension) : image.get(fixedDimension, start)) {
+ start--;
+ } else {
+ int whiteRunStart = start;
+ do {
+ start--;
+ } while (start >= minDim && !(horizontal ? image.get(start, fixedDimension) :
+ image.get(fixedDimension, start)));
+ int whiteRunSize = whiteRunStart - start;
+ if (start < minDim || whiteRunSize > maxWhiteRun) {
+ start = whiteRunStart;
+ break;
+ }
+ }
+ }
+ start++;
+
+ // Then try right/down
+ int end = center;
+ while (end < maxDim) {
+ if (horizontal ? image.get(end, fixedDimension) : image.get(fixedDimension, end)) {
+ end++;
+ } else {
+ int whiteRunStart = end;
+ do {
+ end++;
+ } while (end < maxDim && !(horizontal ? image.get(end, fixedDimension) :
+ image.get(fixedDimension, end)));
+ int whiteRunSize = end - whiteRunStart;
+ if (end >= maxDim || whiteRunSize > maxWhiteRun) {
+ end = whiteRunStart;
+ break;
+ }
+ }
+ }
+ end--;
+
+ return end > start ? new int[]{start, end} : null;
+ }
+
+}
\ No newline at end of file
diff --git a/libraries/zxing/src/com/google/zxing/common/detector/WhiteRectangleDetector.java b/libraries/zxing/src/com/google/zxing/common/detector/WhiteRectangleDetector.java
new file mode 100644
index 000000000..31d87e9d0
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/common/detector/WhiteRectangleDetector.java
@@ -0,0 +1,347 @@
+/*
+ * Copyright 2010 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.common.detector;
+
+import com.google.zxing.NotFoundException;
+import com.google.zxing.ResultPoint;
+import com.google.zxing.common.BitMatrix;
+
+/**
+ *
+ * Detects a candidate barcode-like rectangular region within an image. It
+ * starts around the center of the image, increases the size of the candidate
+ * region until it finds a white rectangular region. By keeping track of the
+ * last black points it encountered, it determines the corners of the barcode.
+ *
+ *
+ * @author David Olivier
+ */
+public final class WhiteRectangleDetector {
+
+ private static final int INIT_SIZE = 30;
+ private static final int CORR = 1;
+
+ private final BitMatrix image;
+ private final int height;
+ private final int width;
+ private final int leftInit;
+ private final int rightInit;
+ private final int downInit;
+ private final int upInit;
+
+ /**
+ * @throws NotFoundException if image is too small
+ */
+ public WhiteRectangleDetector(BitMatrix image) throws NotFoundException {
+ this.image = image;
+ height = image.getHeight();
+ width = image.getWidth();
+ leftInit = (width - INIT_SIZE) >> 1;
+ rightInit = (width + INIT_SIZE) >> 1;
+ upInit = (height - INIT_SIZE) >> 1;
+ downInit = (height + INIT_SIZE) >> 1;
+ if (upInit < 0 || leftInit < 0 || downInit >= height || rightInit >= width) {
+ throw NotFoundException.getNotFoundInstance();
+ }
+ }
+
+ /**
+ * @throws NotFoundException if image is too small
+ */
+ public WhiteRectangleDetector(BitMatrix image, int initSize, int x, int y) throws NotFoundException {
+ this.image = image;
+ height = image.getHeight();
+ width = image.getWidth();
+ int halfsize = initSize >> 1;
+ leftInit = x - halfsize;
+ rightInit = x + halfsize;
+ upInit = y - halfsize;
+ downInit = y + halfsize;
+ if (upInit < 0 || leftInit < 0 || downInit >= height || rightInit >= width) {
+ throw NotFoundException.getNotFoundInstance();
+ }
+ }
+
+ /**
+ *
+ * Detects a candidate barcode-like rectangular region within an image. It
+ * starts around the center of the image, increases the size of the candidate
+ * region until it finds a white rectangular region.
+ *
+ *
+ * @return {@link ResultPoint[]} describing the corners of the rectangular
+ * region. The first and last points are opposed on the diagonal, as
+ * are the second and third. The first point will be the topmost
+ * point and the last, the bottommost. The second point will be
+ * leftmost and the third, the rightmost
+ * @throws NotFoundException if no Data Matrix Code can be found
+ */
+ public ResultPoint[] detect() throws NotFoundException {
+
+ int left = leftInit;
+ int right = rightInit;
+ int up = upInit;
+ int down = downInit;
+ boolean sizeExceeded = false;
+ boolean aBlackPointFoundOnBorder = true;
+ boolean atLeastOneBlackPointFoundOnBorder = false;
+
+ while (aBlackPointFoundOnBorder) {
+
+ aBlackPointFoundOnBorder = false;
+
+ // .....
+ // . |
+ // .....
+ boolean rightBorderNotWhite = true;
+ while (rightBorderNotWhite && right < width) {
+ rightBorderNotWhite = containsBlackPoint(up, down, right, false);
+ if (rightBorderNotWhite) {
+ right++;
+ aBlackPointFoundOnBorder = true;
+ }
+ }
+
+ if (right >= width) {
+ sizeExceeded = true;
+ break;
+ }
+
+ // .....
+ // . .
+ // .___.
+ boolean bottomBorderNotWhite = true;
+ while (bottomBorderNotWhite && down < height) {
+ bottomBorderNotWhite = containsBlackPoint(left, right, down, true);
+ if (bottomBorderNotWhite) {
+ down++;
+ aBlackPointFoundOnBorder = true;
+ }
+ }
+
+ if (down >= height) {
+ sizeExceeded = true;
+ break;
+ }
+
+ // .....
+ // | .
+ // .....
+ boolean leftBorderNotWhite = true;
+ while (leftBorderNotWhite && left >= 0) {
+ leftBorderNotWhite = containsBlackPoint(up, down, left, false);
+ if (leftBorderNotWhite) {
+ left--;
+ aBlackPointFoundOnBorder = true;
+ }
+ }
+
+ if (left < 0) {
+ sizeExceeded = true;
+ break;
+ }
+
+ // .___.
+ // . .
+ // .....
+ boolean topBorderNotWhite = true;
+ while (topBorderNotWhite && up >= 0) {
+ topBorderNotWhite = containsBlackPoint(left, right, up, true);
+ if (topBorderNotWhite) {
+ up--;
+ aBlackPointFoundOnBorder = true;
+ }
+ }
+
+ if (up < 0) {
+ sizeExceeded = true;
+ break;
+ }
+
+ if (aBlackPointFoundOnBorder) {
+ atLeastOneBlackPointFoundOnBorder = true;
+ }
+
+ }
+
+ if (!sizeExceeded && atLeastOneBlackPointFoundOnBorder) {
+
+ int maxSize = right - left;
+
+ ResultPoint z = null;
+ for (int i = 1; i < maxSize; i++) {
+ z = getBlackPointOnSegment(left, down - i, left + i, down);
+ if (z != null) {
+ break;
+ }
+ }
+
+ if (z == null) {
+ throw NotFoundException.getNotFoundInstance();
+ }
+
+ ResultPoint t = null;
+ //go down right
+ for (int i = 1; i < maxSize; i++) {
+ t = getBlackPointOnSegment(left, up + i, left + i, up);
+ if (t != null) {
+ break;
+ }
+ }
+
+ if (t == null) {
+ throw NotFoundException.getNotFoundInstance();
+ }
+
+ ResultPoint x = null;
+ //go down left
+ for (int i = 1; i < maxSize; i++) {
+ x = getBlackPointOnSegment(right, up + i, right - i, up);
+ if (x != null) {
+ break;
+ }
+ }
+
+ if (x == null) {
+ throw NotFoundException.getNotFoundInstance();
+ }
+
+ ResultPoint y = null;
+ //go up left
+ for (int i = 1; i < maxSize; i++) {
+ y = getBlackPointOnSegment(right, down - i, right - i, down);
+ if (y != null) {
+ break;
+ }
+ }
+
+ if (y == null) {
+ throw NotFoundException.getNotFoundInstance();
+ }
+
+ return centerEdges(y, z, x, t);
+
+ } else {
+ throw NotFoundException.getNotFoundInstance();
+ }
+ }
+
+ /**
+ * Ends up being a bit faster than Math.round(). This merely rounds its
+ * argument to the nearest int, where x.5 rounds up.
+ */
+ private static int round(float d) {
+ return (int) (d + 0.5f);
+ }
+
+ private ResultPoint getBlackPointOnSegment(float aX, float aY, float bX, float bY) {
+ int dist = distanceL2(aX, aY, bX, bY);
+ float xStep = (bX - aX) / dist;
+ float yStep = (bY - aY) / dist;
+
+ for (int i = 0; i < dist; i++) {
+ int x = round(aX + i * xStep);
+ int y = round(aY + i * yStep);
+ if (image.get(x, y)) {
+ return new ResultPoint(x, y);
+ }
+ }
+ return null;
+ }
+
+ private static int distanceL2(float aX, float aY, float bX, float bY) {
+ float xDiff = aX - bX;
+ float yDiff = aY - bY;
+ return round((float) Math.sqrt(xDiff * xDiff + yDiff * yDiff));
+ }
+
+ /**
+ * recenters the points of a constant distance towards the center
+ *
+ * @param y bottom most point
+ * @param z left most point
+ * @param x right most point
+ * @param t top most point
+ * @return {@link ResultPoint}[] describing the corners of the rectangular
+ * region. The first and last points are opposed on the diagonal, as
+ * are the second and third. The first point will be the topmost
+ * point and the last, the bottommost. The second point will be
+ * leftmost and the third, the rightmost
+ */
+ private ResultPoint[] centerEdges(ResultPoint y, ResultPoint z,
+ ResultPoint x, ResultPoint t) {
+
+ //
+ // t t
+ // z x
+ // x OR z
+ // y y
+ //
+
+ float yi = y.getX();
+ float yj = y.getY();
+ float zi = z.getX();
+ float zj = z.getY();
+ float xi = x.getX();
+ float xj = x.getY();
+ float ti = t.getX();
+ float tj = t.getY();
+
+ if (yi < width / 2) {
+ return new ResultPoint[]{
+ new ResultPoint(ti - CORR, tj + CORR),
+ new ResultPoint(zi + CORR, zj + CORR),
+ new ResultPoint(xi - CORR, xj - CORR),
+ new ResultPoint(yi + CORR, yj - CORR)};
+ } else {
+ return new ResultPoint[]{
+ new ResultPoint(ti + CORR, tj + CORR),
+ new ResultPoint(zi + CORR, zj - CORR),
+ new ResultPoint(xi - CORR, xj + CORR),
+ new ResultPoint(yi - CORR, yj - CORR)};
+ }
+ }
+
+ /**
+ * Determines whether a segment contains a black point
+ *
+ * @param a min value of the scanned coordinate
+ * @param b max value of the scanned coordinate
+ * @param fixed value of fixed coordinate
+ * @param horizontal set to true if scan must be horizontal, false if vertical
+ * @return true if a black point has been found, else false.
+ */
+ private boolean containsBlackPoint(int a, int b, int fixed, boolean horizontal) {
+
+ if (horizontal) {
+ for (int x = a; x <= b; x++) {
+ if (image.get(x, fixed)) {
+ return true;
+ }
+ }
+ } else {
+ for (int y = a; y <= b; y++) {
+ if (image.get(fixed, y)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+}
\ No newline at end of file
diff --git a/libraries/zxing/src/com/google/zxing/common/reedsolomon/GenericGF.java b/libraries/zxing/src/com/google/zxing/common/reedsolomon/GenericGF.java
new file mode 100644
index 000000000..859c379ee
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/common/reedsolomon/GenericGF.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2007 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.common.reedsolomon;
+
+/**
+ *
This class contains utility methods for performing mathematical operations over
+ * the Galois Fields. Operations use a given primitive polynomial in calculations.
+ *
+ *
Throughout this package, elements of the GF are represented as an int
+ * for convenience and speed (but at the cost of memory).
+ *
+ *
+ * @author Sean Owen
+ * @author David Olivier
+ */
+public final class GenericGF {
+
+ public static final GenericGF AZTEC_DATA_12 = new GenericGF(0x1069, 4096); // x^12 + x^6 + x^5 + x^3 + 1
+ public static final GenericGF AZTEC_DATA_10 = new GenericGF(0x409, 1024); // x^10 + x^3 + 1
+ public static final GenericGF AZTEC_DATA_6 = new GenericGF(0x43, 64); // x^6 + x + 1
+ public static final GenericGF AZTEC_PARAM = new GenericGF(0x13, 16); // x^4 + x + 1
+ public static final GenericGF QR_CODE_FIELD_256 = new GenericGF(0x011D, 256); // x^8 + x^4 + x^3 + x^2 + 1
+ public static final GenericGF DATA_MATRIX_FIELD_256 = new GenericGF(0x012D, 256); // x^8 + x^5 + x^3 + x^2 + 1
+ public static final GenericGF AZTEC_DATA_8 = DATA_MATRIX_FIELD_256;
+
+ private static final int INITIALIZATION_THRESHOLD = 0;
+
+ private int[] expTable;
+ private int[] logTable;
+ private GenericGFPoly zero;
+ private GenericGFPoly one;
+ private final int size;
+ private final int primitive;
+ private boolean initialized = false;
+
+ /**
+ * Create a representation of GF(size) using the given primitive polynomial.
+ *
+ * @param primitive irreducible polynomial whose coefficients are represented by
+ * the bits of an int, where the least-significant bit represents the constant
+ * coefficient
+ */
+ public GenericGF(int primitive, int size) {
+ this.primitive = primitive;
+ this.size = size;
+
+ if (size <= INITIALIZATION_THRESHOLD){
+ initialize();
+ }
+ }
+
+ private void initialize(){
+ expTable = new int[size];
+ logTable = new int[size];
+ int x = 1;
+ for (int i = 0; i < size; i++) {
+ expTable[i] = x;
+ x <<= 1; // x = x * 2; we're assuming the generator alpha is 2
+ if (x >= size) {
+ x ^= primitive;
+ x &= size-1;
+ }
+ }
+ for (int i = 0; i < size-1; i++) {
+ logTable[expTable[i]] = i;
+ }
+ // logTable[0] == 0 but this should never be used
+ zero = new GenericGFPoly(this, new int[]{0});
+ one = new GenericGFPoly(this, new int[]{1});
+ initialized = true;
+ }
+
+ private void checkInit(){
+ if (!initialized) {
+ initialize();
+ }
+ }
+
+ GenericGFPoly getZero() {
+ checkInit();
+
+ return zero;
+ }
+
+ GenericGFPoly getOne() {
+ checkInit();
+
+ return one;
+ }
+
+ /**
+ * @return the monomial representing coefficient * x^degree
+ */
+ GenericGFPoly buildMonomial(int degree, int coefficient) {
+ checkInit();
+
+ if (degree < 0) {
+ throw new IllegalArgumentException();
+ }
+ if (coefficient == 0) {
+ return zero;
+ }
+ int[] coefficients = new int[degree + 1];
+ coefficients[0] = coefficient;
+ return new GenericGFPoly(this, coefficients);
+ }
+
+ /**
+ * Implements both addition and subtraction -- they are the same in GF(size).
+ *
+ * @return sum/difference of a and b
+ */
+ static int addOrSubtract(int a, int b) {
+ return a ^ b;
+ }
+
+ /**
+ * @return 2 to the power of a in GF(size)
+ */
+ int exp(int a) {
+ checkInit();
+
+ return expTable[a];
+ }
+
+ /**
+ * @return base 2 log of a in GF(size)
+ */
+ int log(int a) {
+ checkInit();
+
+ if (a == 0) {
+ throw new IllegalArgumentException();
+ }
+ return logTable[a];
+ }
+
+ /**
+ * @return multiplicative inverse of a
+ */
+ int inverse(int a) {
+ checkInit();
+
+ if (a == 0) {
+ throw new ArithmeticException();
+ }
+ return expTable[size - logTable[a] - 1];
+ }
+
+ /**
+ * @param a
+ * @param b
+ * @return product of a and b in GF(size)
+ */
+ int multiply(int a, int b) {
+ checkInit();
+
+ if (a == 0 || b == 0) {
+ return 0;
+ }
+
+ if (a<0 || b <0 || a>=size || b >=size){
+ a++;
+ }
+
+ int logSum = logTable[a] + logTable[b];
+ return expTable[(logSum % size) + logSum / size];
+ }
+
+ public int getSize(){
+ return size;
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/common/reedsolomon/GenericGFPoly.java b/libraries/zxing/src/com/google/zxing/common/reedsolomon/GenericGFPoly.java
new file mode 100644
index 000000000..056802287
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/common/reedsolomon/GenericGFPoly.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright 2007 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.common.reedsolomon;
+
+/**
+ *
Represents a polynomial whose coefficients are elements of a GF.
+ * Instances of this class are immutable.
+ *
+ *
Much credit is due to William Rucklidge since portions of this code are an indirect
+ * port of his C++ Reed-Solomon implementation.
+ *
+ * @author Sean Owen
+ */
+final class GenericGFPoly {
+
+ private final GenericGF field;
+ private final int[] coefficients;
+
+ /**
+ * @param field the {@link GenericGF} instance representing the field to use
+ * to perform computations
+ * @param coefficients coefficients as ints representing elements of GF(size), arranged
+ * from most significant (highest-power term) coefficient to least significant
+ * @throws IllegalArgumentException if argument is null or empty,
+ * or if leading coefficient is 0 and this is not a
+ * constant polynomial (that is, it is not the monomial "0")
+ */
+ GenericGFPoly(GenericGF field, int[] coefficients) {
+ if (coefficients == null || coefficients.length == 0) {
+ throw new IllegalArgumentException();
+ }
+ this.field = field;
+ int coefficientsLength = coefficients.length;
+ if (coefficientsLength > 1 && coefficients[0] == 0) {
+ // Leading term must be non-zero for anything except the constant polynomial "0"
+ int firstNonZero = 1;
+ while (firstNonZero < coefficientsLength && coefficients[firstNonZero] == 0) {
+ firstNonZero++;
+ }
+ if (firstNonZero == coefficientsLength) {
+ this.coefficients = field.getZero().coefficients;
+ } else {
+ this.coefficients = new int[coefficientsLength - firstNonZero];
+ System.arraycopy(coefficients,
+ firstNonZero,
+ this.coefficients,
+ 0,
+ this.coefficients.length);
+ }
+ } else {
+ this.coefficients = coefficients;
+ }
+ }
+
+ int[] getCoefficients() {
+ return coefficients;
+ }
+
+ /**
+ * @return degree of this polynomial
+ */
+ int getDegree() {
+ return coefficients.length - 1;
+ }
+
+ /**
+ * @return true iff this polynomial is the monomial "0"
+ */
+ boolean isZero() {
+ return coefficients[0] == 0;
+ }
+
+ /**
+ * @return coefficient of x^degree term in this polynomial
+ */
+ int getCoefficient(int degree) {
+ return coefficients[coefficients.length - 1 - degree];
+ }
+
+ /**
+ * @return evaluation of this polynomial at a given point
+ */
+ int evaluateAt(int a) {
+ if (a == 0) {
+ // Just return the x^0 coefficient
+ return getCoefficient(0);
+ }
+ int size = coefficients.length;
+ if (a == 1) {
+ // Just the sum of the coefficients
+ int result = 0;
+ for (int i = 0; i < size; i++) {
+ result = GenericGF.addOrSubtract(result, coefficients[i]);
+ }
+ return result;
+ }
+ int result = coefficients[0];
+ for (int i = 1; i < size; i++) {
+ result = GenericGF.addOrSubtract(field.multiply(a, result), coefficients[i]);
+ }
+ return result;
+ }
+
+ GenericGFPoly addOrSubtract(GenericGFPoly other) {
+ if (!field.equals(other.field)) {
+ throw new IllegalArgumentException("GenericGFPolys do not have same GenericGF field");
+ }
+ if (isZero()) {
+ return other;
+ }
+ if (other.isZero()) {
+ return this;
+ }
+
+ int[] smallerCoefficients = this.coefficients;
+ int[] largerCoefficients = other.coefficients;
+ if (smallerCoefficients.length > largerCoefficients.length) {
+ int[] temp = smallerCoefficients;
+ smallerCoefficients = largerCoefficients;
+ largerCoefficients = temp;
+ }
+ int[] sumDiff = new int[largerCoefficients.length];
+ int lengthDiff = largerCoefficients.length - smallerCoefficients.length;
+ // Copy high-order terms only found in higher-degree polynomial's coefficients
+ System.arraycopy(largerCoefficients, 0, sumDiff, 0, lengthDiff);
+
+ for (int i = lengthDiff; i < largerCoefficients.length; i++) {
+ sumDiff[i] = GenericGF.addOrSubtract(smallerCoefficients[i - lengthDiff], largerCoefficients[i]);
+ }
+
+ return new GenericGFPoly(field, sumDiff);
+ }
+
+ GenericGFPoly multiply(GenericGFPoly other) {
+ if (!field.equals(other.field)) {
+ throw new IllegalArgumentException("GenericGFPolys do not have same GenericGF field");
+ }
+ if (isZero() || other.isZero()) {
+ return field.getZero();
+ }
+ int[] aCoefficients = this.coefficients;
+ int aLength = aCoefficients.length;
+ int[] bCoefficients = other.coefficients;
+ int bLength = bCoefficients.length;
+ int[] product = new int[aLength + bLength - 1];
+ for (int i = 0; i < aLength; i++) {
+ int aCoeff = aCoefficients[i];
+ for (int j = 0; j < bLength; j++) {
+ product[i + j] = GenericGF.addOrSubtract(product[i + j],
+ field.multiply(aCoeff, bCoefficients[j]));
+ }
+ }
+ return new GenericGFPoly(field, product);
+ }
+
+ GenericGFPoly multiply(int scalar) {
+ if (scalar == 0) {
+ return field.getZero();
+ }
+ if (scalar == 1) {
+ return this;
+ }
+ int size = coefficients.length;
+ int[] product = new int[size];
+ for (int i = 0; i < size; i++) {
+ product[i] = field.multiply(coefficients[i], scalar);
+ }
+ return new GenericGFPoly(field, product);
+ }
+
+ GenericGFPoly multiplyByMonomial(int degree, int coefficient) {
+ if (degree < 0) {
+ throw new IllegalArgumentException();
+ }
+ if (coefficient == 0) {
+ return field.getZero();
+ }
+ int size = coefficients.length;
+ int[] product = new int[size + degree];
+ for (int i = 0; i < size; i++) {
+ product[i] = field.multiply(coefficients[i], coefficient);
+ }
+ return new GenericGFPoly(field, product);
+ }
+
+ GenericGFPoly[] divide(GenericGFPoly other) {
+ if (!field.equals(other.field)) {
+ throw new IllegalArgumentException("GenericGFPolys do not have same GenericGF field");
+ }
+ if (other.isZero()) {
+ throw new IllegalArgumentException("Divide by 0");
+ }
+
+ GenericGFPoly quotient = field.getZero();
+ GenericGFPoly remainder = this;
+
+ int denominatorLeadingTerm = other.getCoefficient(other.getDegree());
+ int inverseDenominatorLeadingTerm = field.inverse(denominatorLeadingTerm);
+
+ while (remainder.getDegree() >= other.getDegree() && !remainder.isZero()) {
+ int degreeDifference = remainder.getDegree() - other.getDegree();
+ int scale = field.multiply(remainder.getCoefficient(remainder.getDegree()), inverseDenominatorLeadingTerm);
+ GenericGFPoly term = other.multiplyByMonomial(degreeDifference, scale);
+ GenericGFPoly iterationQuotient = field.buildMonomial(degreeDifference, scale);
+ quotient = quotient.addOrSubtract(iterationQuotient);
+ remainder = remainder.addOrSubtract(term);
+ }
+
+ return new GenericGFPoly[] { quotient, remainder };
+ }
+
+ public String toString() {
+ StringBuffer result = new StringBuffer(8 * getDegree());
+ for (int degree = getDegree(); degree >= 0; degree--) {
+ int coefficient = getCoefficient(degree);
+ if (coefficient != 0) {
+ if (coefficient < 0) {
+ result.append(" - ");
+ coefficient = -coefficient;
+ } else {
+ if (result.length() > 0) {
+ result.append(" + ");
+ }
+ }
+ if (degree == 0 || coefficient != 1) {
+ int alphaPower = field.log(coefficient);
+ if (alphaPower == 0) {
+ result.append('1');
+ } else if (alphaPower == 1) {
+ result.append('a');
+ } else {
+ result.append("a^");
+ result.append(alphaPower);
+ }
+ }
+ if (degree != 0) {
+ if (degree == 1) {
+ result.append('x');
+ } else {
+ result.append("x^");
+ result.append(degree);
+ }
+ }
+ }
+ }
+ return result.toString();
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/common/reedsolomon/ReedSolomonDecoder.java b/libraries/zxing/src/com/google/zxing/common/reedsolomon/ReedSolomonDecoder.java
new file mode 100644
index 000000000..b523fd34b
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/common/reedsolomon/ReedSolomonDecoder.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2007 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.common.reedsolomon;
+
+/**
+ *
Implements Reed-Solomon decoding, as the name implies.
+ *
+ *
The algorithm will not be explained here, but the following references were helpful
+ * in creating this implementation:
Much credit is due to William Rucklidge since portions of this code are an indirect
+ * port of his C++ Reed-Solomon implementation.
+ *
+ * @author Sean Owen
+ * @author William Rucklidge
+ * @author sanfordsquires
+ */
+public final class ReedSolomonDecoder {
+
+ private final GenericGF field;
+
+ public ReedSolomonDecoder(GenericGF field) {
+ this.field = field;
+ }
+
+ /**
+ *
Decodes given set of received codewords, which include both data and error-correction
+ * codewords. Really, this means it uses Reed-Solomon to detect and correct errors, in-place,
+ * in the input.
+ *
+ * @param received data and error-correction codewords
+ * @param twoS number of error-correction codewords available
+ * @throws ReedSolomonException if decoding fails for any reason
+ */
+ public void decode(int[] received, int twoS) throws ReedSolomonException {
+ GenericGFPoly poly = new GenericGFPoly(field, received);
+ int[] syndromeCoefficients = new int[twoS];
+ boolean dataMatrix = field.equals(GenericGF.DATA_MATRIX_FIELD_256);
+ boolean noError = true;
+ for (int i = 0; i < twoS; i++) {
+ // Thanks to sanfordsquires for this fix:
+ int eval = poly.evaluateAt(field.exp(dataMatrix ? i + 1 : i));
+ syndromeCoefficients[syndromeCoefficients.length - 1 - i] = eval;
+ if (eval != 0) {
+ noError = false;
+ }
+ }
+ if (noError) {
+ return;
+ }
+ GenericGFPoly syndrome = new GenericGFPoly(field, syndromeCoefficients);
+ GenericGFPoly[] sigmaOmega =
+ runEuclideanAlgorithm(field.buildMonomial(twoS, 1), syndrome, twoS);
+ GenericGFPoly sigma = sigmaOmega[0];
+ GenericGFPoly omega = sigmaOmega[1];
+ int[] errorLocations = findErrorLocations(sigma);
+ int[] errorMagnitudes = findErrorMagnitudes(omega, errorLocations, dataMatrix);
+ for (int i = 0; i < errorLocations.length; i++) {
+ int position = received.length - 1 - field.log(errorLocations[i]);
+ if (position < 0) {
+ throw new ReedSolomonException("Bad error location");
+ }
+ received[position] = GenericGF.addOrSubtract(received[position], errorMagnitudes[i]);
+ }
+ }
+
+ private GenericGFPoly[] runEuclideanAlgorithm(GenericGFPoly a, GenericGFPoly b, int R)
+ throws ReedSolomonException {
+ // Assume a's degree is >= b's
+ if (a.getDegree() < b.getDegree()) {
+ GenericGFPoly temp = a;
+ a = b;
+ b = temp;
+ }
+
+ GenericGFPoly rLast = a;
+ GenericGFPoly r = b;
+ GenericGFPoly sLast = field.getOne();
+ GenericGFPoly s = field.getZero();
+ GenericGFPoly tLast = field.getZero();
+ GenericGFPoly t = field.getOne();
+
+ // Run Euclidean algorithm until r's degree is less than R/2
+ while (r.getDegree() >= R / 2) {
+ GenericGFPoly rLastLast = rLast;
+ GenericGFPoly sLastLast = sLast;
+ GenericGFPoly tLastLast = tLast;
+ rLast = r;
+ sLast = s;
+ tLast = t;
+
+ // Divide rLastLast by rLast, with quotient in q and remainder in r
+ if (rLast.isZero()) {
+ // Oops, Euclidean algorithm already terminated?
+ throw new ReedSolomonException("r_{i-1} was zero");
+ }
+ r = rLastLast;
+ GenericGFPoly q = field.getZero();
+ int denominatorLeadingTerm = rLast.getCoefficient(rLast.getDegree());
+ int dltInverse = field.inverse(denominatorLeadingTerm);
+ while (r.getDegree() >= rLast.getDegree() && !r.isZero()) {
+ int degreeDiff = r.getDegree() - rLast.getDegree();
+ int scale = field.multiply(r.getCoefficient(r.getDegree()), dltInverse);
+ q = q.addOrSubtract(field.buildMonomial(degreeDiff, scale));
+ r = r.addOrSubtract(rLast.multiplyByMonomial(degreeDiff, scale));
+ }
+
+ s = q.multiply(sLast).addOrSubtract(sLastLast);
+ t = q.multiply(tLast).addOrSubtract(tLastLast);
+ }
+
+ int sigmaTildeAtZero = t.getCoefficient(0);
+ if (sigmaTildeAtZero == 0) {
+ throw new ReedSolomonException("sigmaTilde(0) was zero");
+ }
+
+ int inverse = field.inverse(sigmaTildeAtZero);
+ GenericGFPoly sigma = t.multiply(inverse);
+ GenericGFPoly omega = r.multiply(inverse);
+ return new GenericGFPoly[]{sigma, omega};
+ }
+
+ private int[] findErrorLocations(GenericGFPoly errorLocator) throws ReedSolomonException {
+ // This is a direct application of Chien's search
+ int numErrors = errorLocator.getDegree();
+ if (numErrors == 1) { // shortcut
+ return new int[] { errorLocator.getCoefficient(1) };
+ }
+ int[] result = new int[numErrors];
+ int e = 0;
+ for (int i = 1; i < field.getSize() && e < numErrors; i++) {
+ if (errorLocator.evaluateAt(i) == 0) {
+ result[e] = field.inverse(i);
+ e++;
+ }
+ }
+ if (e != numErrors) {
+ throw new ReedSolomonException("Error locator degree does not match number of roots");
+ }
+ return result;
+ }
+
+ private int[] findErrorMagnitudes(GenericGFPoly errorEvaluator, int[] errorLocations, boolean dataMatrix) {
+ // This is directly applying Forney's Formula
+ int s = errorLocations.length;
+ int[] result = new int[s];
+ for (int i = 0; i < s; i++) {
+ int xiInverse = field.inverse(errorLocations[i]);
+ int denominator = 1;
+ for (int j = 0; j < s; j++) {
+ if (i != j) {
+ //denominator = field.multiply(denominator,
+ // GenericGF.addOrSubtract(1, field.multiply(errorLocations[j], xiInverse)));
+ // Above should work but fails on some Apple and Linux JDKs due to a Hotspot bug.
+ // Below is a funny-looking workaround from Steven Parkes
+ int term = field.multiply(errorLocations[j], xiInverse);
+ int termPlus1 = (term & 0x1) == 0 ? term | 1 : term & ~1;
+ denominator = field.multiply(denominator, termPlus1);
+ }
+ }
+ result[i] = field.multiply(errorEvaluator.evaluateAt(xiInverse),
+ field.inverse(denominator));
+ // Thanks to sanfordsquires for this fix:
+ if (dataMatrix) {
+ result[i] = field.multiply(result[i], xiInverse);
+ }
+ }
+ return result;
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.java b/libraries/zxing/src/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.java
new file mode 100644
index 000000000..05e2ae03a
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 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.common.reedsolomon;
+
+import java.util.Vector;
+
+/**
+ *
Implements Reed-Solomon enbcoding, as the name implies.
+ *
+ * @author Sean Owen
+ * @author William Rucklidge
+ */
+public final class ReedSolomonEncoder {
+
+ private final GenericGF field;
+ private final Vector cachedGenerators;
+
+ public ReedSolomonEncoder(GenericGF field) {
+ if (!GenericGF.QR_CODE_FIELD_256.equals(field)) {
+ throw new IllegalArgumentException("Only QR Code is supported at this time");
+ }
+ this.field = field;
+ this.cachedGenerators = new Vector();
+ cachedGenerators.addElement(new GenericGFPoly(field, new int[] { 1 }));
+ }
+
+ private GenericGFPoly buildGenerator(int degree) {
+ if (degree >= cachedGenerators.size()) {
+ GenericGFPoly lastGenerator = (GenericGFPoly) cachedGenerators.elementAt(cachedGenerators.size() - 1);
+ for (int d = cachedGenerators.size(); d <= degree; d++) {
+ GenericGFPoly nextGenerator = lastGenerator.multiply(new GenericGFPoly(field, new int[] { 1, field.exp(d - 1) }));
+ cachedGenerators.addElement(nextGenerator);
+ lastGenerator = nextGenerator;
+ }
+ }
+ return (GenericGFPoly) cachedGenerators.elementAt(degree);
+ }
+
+ public void encode(int[] toEncode, int ecBytes) {
+ if (ecBytes == 0) {
+ throw new IllegalArgumentException("No error correction bytes");
+ }
+ int dataBytes = toEncode.length - ecBytes;
+ if (dataBytes <= 0) {
+ throw new IllegalArgumentException("No data bytes provided");
+ }
+ GenericGFPoly generator = buildGenerator(ecBytes);
+ int[] infoCoefficients = new int[dataBytes];
+ System.arraycopy(toEncode, 0, infoCoefficients, 0, dataBytes);
+ GenericGFPoly info = new GenericGFPoly(field, infoCoefficients);
+ info = info.multiplyByMonomial(ecBytes, 1);
+ GenericGFPoly remainder = info.divide(generator)[1];
+ int[] coefficients = remainder.getCoefficients();
+ int numZeroCoefficients = ecBytes - coefficients.length;
+ for (int i = 0; i < numZeroCoefficients; i++) {
+ toEncode[dataBytes + i] = 0;
+ }
+ System.arraycopy(coefficients, 0, toEncode, dataBytes + numZeroCoefficients, coefficients.length);
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/common/reedsolomon/ReedSolomonException.java b/libraries/zxing/src/com/google/zxing/common/reedsolomon/ReedSolomonException.java
new file mode 100644
index 000000000..d5b45a612
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/common/reedsolomon/ReedSolomonException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2007 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.common.reedsolomon;
+
+/**
+ *
Thrown when an exception occurs during Reed-Solomon decoding, such as when
+ * there are too many errors to correct.
+ *
+ * @author Sean Owen
+ */
+public final class ReedSolomonException extends Exception {
+
+ public ReedSolomonException(String message) {
+ super(message);
+ }
+
+}
\ No newline at end of file
diff --git a/libraries/zxing/src/com/google/zxing/multi/ByQuadrantReader.java b/libraries/zxing/src/com/google/zxing/multi/ByQuadrantReader.java
new file mode 100644
index 000000000..35904d364
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/multi/ByQuadrantReader.java
@@ -0,0 +1,96 @@
+/*
+ * 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.multi;
+
+import com.google.zxing.BinaryBitmap;
+import com.google.zxing.ChecksumException;
+import com.google.zxing.FormatException;
+import com.google.zxing.NotFoundException;
+import com.google.zxing.Reader;
+import com.google.zxing.Result;
+
+import java.util.Hashtable;
+
+/**
+ * This class attempts to decode a barcode from an image, not by scanning the whole image,
+ * but by scanning subsets of the image. This is important when there may be multiple barcodes in
+ * an image, and detecting a barcode may find parts of multiple barcode and fail to decode
+ * (e.g. QR Codes). Instead this scans the four quadrants of the image -- and also the center
+ * 'quadrant' to cover the case where a barcode is found in the center.
+ *
+ * @see GenericMultipleBarcodeReader
+ */
+public final class ByQuadrantReader implements Reader {
+
+ private final Reader delegate;
+
+ public ByQuadrantReader(Reader delegate) {
+ this.delegate = delegate;
+ }
+
+ public Result decode(BinaryBitmap image)
+ throws NotFoundException, ChecksumException, FormatException {
+ return decode(image, null);
+ }
+
+ public Result decode(BinaryBitmap image, Hashtable hints)
+ throws NotFoundException, ChecksumException, FormatException {
+
+ int width = image.getWidth();
+ int height = image.getHeight();
+ int halfWidth = width / 2;
+ int halfHeight = height / 2;
+
+ BinaryBitmap topLeft = image.crop(0, 0, halfWidth, halfHeight);
+ try {
+ return delegate.decode(topLeft, hints);
+ } catch (NotFoundException re) {
+ // continue
+ }
+
+ BinaryBitmap topRight = image.crop(halfWidth, 0, halfWidth, halfHeight);
+ try {
+ return delegate.decode(topRight, hints);
+ } catch (NotFoundException re) {
+ // continue
+ }
+
+ BinaryBitmap bottomLeft = image.crop(0, halfHeight, halfWidth, halfHeight);
+ try {
+ return delegate.decode(bottomLeft, hints);
+ } catch (NotFoundException re) {
+ // continue
+ }
+
+ BinaryBitmap bottomRight = image.crop(halfWidth, halfHeight, halfWidth, halfHeight);
+ try {
+ return delegate.decode(bottomRight, hints);
+ } catch (NotFoundException re) {
+ // continue
+ }
+
+ int quarterWidth = halfWidth / 2;
+ int quarterHeight = halfHeight / 2;
+ BinaryBitmap center = image.crop(quarterWidth, quarterHeight, halfWidth, halfHeight);
+ return delegate.decode(center, hints);
+ }
+
+ public void reset() {
+ delegate.reset();
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/multi/GenericMultipleBarcodeReader.java b/libraries/zxing/src/com/google/zxing/multi/GenericMultipleBarcodeReader.java
new file mode 100644
index 000000000..70d454251
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/multi/GenericMultipleBarcodeReader.java
@@ -0,0 +1,156 @@
+/*
+ * 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.multi;
+
+import com.google.zxing.BinaryBitmap;
+import com.google.zxing.NotFoundException;
+import com.google.zxing.Reader;
+import com.google.zxing.ReaderException;
+import com.google.zxing.Result;
+import com.google.zxing.ResultPoint;
+
+import java.util.Hashtable;
+import java.util.Vector;
+
+/**
+ *
Attempts to locate multiple barcodes in an image by repeatedly decoding portion of the image.
+ * After one barcode is found, the areas left, above, right and below the barcode's
+ * {@link com.google.zxing.ResultPoint}s are scanned, recursively.
+ *
+ *
A caller may want to also employ {@link ByQuadrantReader} when attempting to find multiple
+ * 2D barcodes, like QR Codes, in an image, where the presence of multiple barcodes might prevent
+ * detecting any one of them.
+ *
+ *
That is, instead of passing a {@link Reader} a caller might pass
+ * new ByQuadrantReader(reader).
+ *
+ * @author Sean Owen
+ */
+public final class GenericMultipleBarcodeReader implements MultipleBarcodeReader {
+
+ private static final int MIN_DIMENSION_TO_RECUR = 100;
+
+ private final Reader delegate;
+
+ public GenericMultipleBarcodeReader(Reader delegate) {
+ this.delegate = delegate;
+ }
+
+ public Result[] decodeMultiple(BinaryBitmap image) throws NotFoundException {
+ return decodeMultiple(image, null);
+ }
+
+ public Result[] decodeMultiple(BinaryBitmap image, Hashtable hints)
+ throws NotFoundException {
+ Vector results = new Vector();
+ doDecodeMultiple(image, hints, results, 0, 0);
+ if (results.isEmpty()) {
+ throw NotFoundException.getNotFoundInstance();
+ }
+ int numResults = results.size();
+ Result[] resultArray = new Result[numResults];
+ for (int i = 0; i < numResults; i++) {
+ resultArray[i] = (Result) results.elementAt(i);
+ }
+ return resultArray;
+ }
+
+ private void doDecodeMultiple(BinaryBitmap image,
+ Hashtable hints,
+ Vector results,
+ int xOffset,
+ int yOffset) {
+ Result result;
+ try {
+ result = delegate.decode(image, hints);
+ } catch (ReaderException re) {
+ return;
+ }
+ boolean alreadyFound = false;
+ for (int i = 0; i < results.size(); i++) {
+ Result existingResult = (Result) results.elementAt(i);
+ if (existingResult.getText().equals(result.getText())) {
+ alreadyFound = true;
+ break;
+ }
+ }
+ if (alreadyFound) {
+ return;
+ }
+ results.addElement(translateResultPoints(result, xOffset, yOffset));
+ ResultPoint[] resultPoints = result.getResultPoints();
+ if (resultPoints == null || resultPoints.length == 0) {
+ return;
+ }
+ int width = image.getWidth();
+ int height = image.getHeight();
+ float minX = width;
+ float minY = height;
+ float maxX = 0.0f;
+ float maxY = 0.0f;
+ for (int i = 0; i < resultPoints.length; i++) {
+ ResultPoint point = resultPoints[i];
+ float x = point.getX();
+ float y = point.getY();
+ if (x < minX) {
+ minX = x;
+ }
+ if (y < minY) {
+ minY = y;
+ }
+ if (x > maxX) {
+ maxX = x;
+ }
+ if (y > maxY) {
+ maxY = y;
+ }
+ }
+
+ // Decode left of barcode
+ if (minX > MIN_DIMENSION_TO_RECUR) {
+ doDecodeMultiple(image.crop(0, 0, (int) minX, height),
+ hints, results, xOffset, yOffset);
+ }
+ // Decode above barcode
+ if (minY > MIN_DIMENSION_TO_RECUR) {
+ doDecodeMultiple(image.crop(0, 0, width, (int) minY),
+ hints, results, xOffset, yOffset);
+ }
+ // Decode right of barcode
+ if (maxX < width - MIN_DIMENSION_TO_RECUR) {
+ doDecodeMultiple(image.crop((int) maxX, 0, width - (int) maxX, height),
+ hints, results, xOffset + (int) maxX, yOffset);
+ }
+ // Decode below barcode
+ if (maxY < height - MIN_DIMENSION_TO_RECUR) {
+ doDecodeMultiple(image.crop(0, (int) maxY, width, height - (int) maxY),
+ hints, results, xOffset, yOffset + (int) maxY);
+ }
+ }
+
+ private static Result translateResultPoints(Result result, int xOffset, int yOffset) {
+ ResultPoint[] oldResultPoints = result.getResultPoints();
+ ResultPoint[] newResultPoints = new ResultPoint[oldResultPoints.length];
+ for (int i = 0; i < oldResultPoints.length; i++) {
+ ResultPoint oldPoint = oldResultPoints[i];
+ newResultPoints[i] = new ResultPoint(oldPoint.getX() + xOffset, oldPoint.getY() + yOffset);
+ }
+ return new Result(result.getText(), result.getRawBytes(), newResultPoints,
+ result.getBarcodeFormat());
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/multi/MultipleBarcodeReader.java b/libraries/zxing/src/com/google/zxing/multi/MultipleBarcodeReader.java
new file mode 100644
index 000000000..5f0c7eb5d
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/multi/MultipleBarcodeReader.java
@@ -0,0 +1,37 @@
+/*
+ * 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.multi;
+
+import com.google.zxing.BinaryBitmap;
+import com.google.zxing.NotFoundException;
+import com.google.zxing.Result;
+
+import java.util.Hashtable;
+
+/**
+ * Implementation of this interface attempt to read several barcodes from one image.
+ *
+ * @see com.google.zxing.Reader
+ * @author Sean Owen
+ */
+public interface MultipleBarcodeReader {
+
+ Result[] decodeMultiple(BinaryBitmap image) throws NotFoundException;
+
+ Result[] decodeMultiple(BinaryBitmap image, Hashtable hints) throws NotFoundException;
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/multi/qrcode/detector/MultiDetector.java b/libraries/zxing/src/com/google/zxing/multi/qrcode/detector/MultiDetector.java
new file mode 100644
index 000000000..584c41404
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/multi/qrcode/detector/MultiDetector.java
@@ -0,0 +1,72 @@
+/*
+ * 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.multi.qrcode.detector;
+
+import com.google.zxing.NotFoundException;
+import com.google.zxing.ReaderException;
+import com.google.zxing.common.BitMatrix;
+import com.google.zxing.common.DetectorResult;
+import com.google.zxing.qrcode.detector.Detector;
+import com.google.zxing.qrcode.detector.FinderPatternInfo;
+
+import java.util.Hashtable;
+import java.util.Vector;
+
+/**
+ *
Encapsulates logic that can detect one or more QR Codes in an image, even if the QR Code
+ * is rotated or skewed, or partially obscured.
+ *
+ * @author Sean Owen
+ * @author Hannes Erven
+ */
+public final class MultiDetector extends Detector {
+
+ private static final DetectorResult[] EMPTY_DETECTOR_RESULTS = new DetectorResult[0];
+
+ public MultiDetector(BitMatrix image) {
+ super(image);
+ }
+
+ public DetectorResult[] detectMulti(Hashtable hints) throws NotFoundException {
+ BitMatrix image = getImage();
+ MultiFinderPatternFinder finder = new MultiFinderPatternFinder(image);
+ FinderPatternInfo[] info = finder.findMulti(hints);
+
+ if (info == null || info.length == 0) {
+ throw NotFoundException.getNotFoundInstance();
+ }
+
+ Vector result = new Vector();
+ for (int i = 0; i < info.length; i++) {
+ try {
+ result.addElement(processFinderPatternInfo(info[i]));
+ } catch (ReaderException e) {
+ // ignore
+ }
+ }
+ if (result.isEmpty()) {
+ return EMPTY_DETECTOR_RESULTS;
+ } else {
+ DetectorResult[] resultArray = new DetectorResult[result.size()];
+ for (int i = 0; i < result.size(); i++) {
+ resultArray[i] = (DetectorResult) result.elementAt(i);
+ }
+ return resultArray;
+ }
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/multi/qrcode/detector/MultiFinderPatternFinder.java b/libraries/zxing/src/com/google/zxing/multi/qrcode/detector/MultiFinderPatternFinder.java
new file mode 100644
index 000000000..1162324e2
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/multi/qrcode/detector/MultiFinderPatternFinder.java
@@ -0,0 +1,324 @@
+/*
+ * 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.multi.qrcode.detector;
+
+import com.google.zxing.DecodeHintType;
+import com.google.zxing.NotFoundException;
+import com.google.zxing.ResultPoint;
+import com.google.zxing.ResultPointCallback;
+import com.google.zxing.common.BitMatrix;
+import com.google.zxing.common.Collections;
+import com.google.zxing.common.Comparator;
+import com.google.zxing.qrcode.detector.FinderPattern;
+import com.google.zxing.qrcode.detector.FinderPatternFinder;
+import com.google.zxing.qrcode.detector.FinderPatternInfo;
+
+import java.util.Hashtable;
+import java.util.Vector;
+
+/**
+ *
This class attempts to find finder patterns in a QR Code. Finder patterns are the square
+ * markers at three corners of a QR Code.
+ *
+ *
This class is thread-safe but not reentrant. Each thread must allocate its own object.
+ *
+ *
In contrast to {@link FinderPatternFinder}, this class will return an array of all possible
+ * QR code locations in the image.
+ *
+ *
Use the TRY_HARDER hint to ask for a more thorough detection.
+ *
+ * @author Sean Owen
+ * @author Hannes Erven
+ */
+final class MultiFinderPatternFinder extends FinderPatternFinder {
+
+ private static final FinderPatternInfo[] EMPTY_RESULT_ARRAY = new FinderPatternInfo[0];
+
+ // TODO MIN_MODULE_COUNT and MAX_MODULE_COUNT would be great hints to ask the user for
+ // since it limits the number of regions to decode
+
+ // max. legal count of modules per QR code edge (177)
+ private static final float MAX_MODULE_COUNT_PER_EDGE = 180;
+ // min. legal count per modules per QR code edge (11)
+ private static final float MIN_MODULE_COUNT_PER_EDGE = 9;
+
+ /**
+ * More or less arbitrary cutoff point for determining if two finder patterns might belong
+ * to the same code if they differ less than DIFF_MODSIZE_CUTOFF_PERCENT percent in their
+ * estimated modules sizes.
+ */
+ private static final float DIFF_MODSIZE_CUTOFF_PERCENT = 0.05f;
+
+ /**
+ * More or less arbitrary cutoff point for determining if two finder patterns might belong
+ * to the same code if they differ less than DIFF_MODSIZE_CUTOFF pixels/module in their
+ * estimated modules sizes.
+ */
+ private static final float DIFF_MODSIZE_CUTOFF = 0.5f;
+
+
+ /**
+ * A comparator that orders FinderPatterns by their estimated module size.
+ */
+ private static class ModuleSizeComparator implements Comparator {
+ public int compare(Object center1, Object center2) {
+ float value = ((FinderPattern) center2).getEstimatedModuleSize() -
+ ((FinderPattern) center1).getEstimatedModuleSize();
+ return value < 0.0 ? -1 : value > 0.0 ? 1 : 0;
+ }
+ }
+
+ /**
+ *
Creates a finder that will search the image for three finder patterns.
+ *
+ * @param image image to search
+ */
+ MultiFinderPatternFinder(BitMatrix image) {
+ super(image);
+ }
+
+ MultiFinderPatternFinder(BitMatrix image, ResultPointCallback resultPointCallback) {
+ super(image, resultPointCallback);
+ }
+
+ /**
+ * @return the 3 best {@link FinderPattern}s from our list of candidates. The "best" are
+ * those that have been detected at least {@link #CENTER_QUORUM} times, and whose module
+ * size differs from the average among those patterns the least
+ * @throws NotFoundException if 3 such finder patterns do not exist
+ */
+ private FinderPattern[][] selectBestPatterns() throws NotFoundException {
+ Vector possibleCenters = getPossibleCenters();
+ int size = possibleCenters.size();
+
+ if (size < 3) {
+ // Couldn't find enough finder patterns
+ throw NotFoundException.getNotFoundInstance();
+ }
+
+ /*
+ * Begin HE modifications to safely detect multiple codes of equal size
+ */
+ if (size == 3) {
+ return new FinderPattern[][]{
+ new FinderPattern[]{
+ (FinderPattern) possibleCenters.elementAt(0),
+ (FinderPattern) possibleCenters.elementAt(1),
+ (FinderPattern) possibleCenters.elementAt(2)
+ }
+ };
+ }
+
+ // Sort by estimated module size to speed up the upcoming checks
+ Collections.insertionSort(possibleCenters, new ModuleSizeComparator());
+
+ /*
+ * Now lets start: build a list of tuples of three finder locations that
+ * - feature similar module sizes
+ * - are placed in a distance so the estimated module count is within the QR specification
+ * - have similar distance between upper left/right and left top/bottom finder patterns
+ * - form a triangle with 90° angle (checked by comparing top right/bottom left distance
+ * with pythagoras)
+ *
+ * Note: we allow each point to be used for more than one code region: this might seem
+ * counterintuitive at first, but the performance penalty is not that big. At this point,
+ * we cannot make a good quality decision whether the three finders actually represent
+ * a QR code, or are just by chance layouted so it looks like there might be a QR code there.
+ * So, if the layout seems right, lets have the decoder try to decode.
+ */
+
+ Vector results = new Vector(); // holder for the results
+
+ for (int i1 = 0; i1 < (size - 2); i1++) {
+ FinderPattern p1 = (FinderPattern) possibleCenters.elementAt(i1);
+ if (p1 == null) {
+ continue;
+ }
+
+ for (int i2 = i1 + 1; i2 < (size - 1); i2++) {
+ FinderPattern p2 = (FinderPattern) possibleCenters.elementAt(i2);
+ if (p2 == null) {
+ continue;
+ }
+
+ // Compare the expected module sizes; if they are really off, skip
+ float vModSize12 = (p1.getEstimatedModuleSize() - p2.getEstimatedModuleSize()) /
+ Math.min(p1.getEstimatedModuleSize(), p2.getEstimatedModuleSize());
+ float vModSize12A = Math.abs(p1.getEstimatedModuleSize() - p2.getEstimatedModuleSize());
+ if (vModSize12A > DIFF_MODSIZE_CUTOFF && vModSize12 >= DIFF_MODSIZE_CUTOFF_PERCENT) {
+ // break, since elements are ordered by the module size deviation there cannot be
+ // any more interesting elements for the given p1.
+ break;
+ }
+
+ for (int i3 = i2 + 1; i3 < size; i3++) {
+ FinderPattern p3 = (FinderPattern) possibleCenters.elementAt(i3);
+ if (p3 == null) {
+ continue;
+ }
+
+ // Compare the expected module sizes; if they are really off, skip
+ float vModSize23 = (p2.getEstimatedModuleSize() - p3.getEstimatedModuleSize()) /
+ Math.min(p2.getEstimatedModuleSize(), p3.getEstimatedModuleSize());
+ float vModSize23A = Math.abs(p2.getEstimatedModuleSize() - p3.getEstimatedModuleSize());
+ if (vModSize23A > DIFF_MODSIZE_CUTOFF && vModSize23 >= DIFF_MODSIZE_CUTOFF_PERCENT) {
+ // break, since elements are ordered by the module size deviation there cannot be
+ // any more interesting elements for the given p1.
+ break;
+ }
+
+ FinderPattern[] test = {p1, p2, p3};
+ ResultPoint.orderBestPatterns(test);
+
+ // Calculate the distances: a = topleft-bottomleft, b=topleft-topright, c = diagonal
+ FinderPatternInfo info = new FinderPatternInfo(test);
+ float dA = ResultPoint.distance(info.getTopLeft(), info.getBottomLeft());
+ float dC = ResultPoint.distance(info.getTopRight(), info.getBottomLeft());
+ float dB = ResultPoint.distance(info.getTopLeft(), info.getTopRight());
+
+ // Check the sizes
+ float estimatedModuleCount = (dA + dB) / (p1.getEstimatedModuleSize() * 2.0f);
+ if (estimatedModuleCount > MAX_MODULE_COUNT_PER_EDGE ||
+ estimatedModuleCount < MIN_MODULE_COUNT_PER_EDGE) {
+ continue;
+ }
+
+ // Calculate the difference of the edge lengths in percent
+ float vABBC = Math.abs((dA - dB) / Math.min(dA, dB));
+ if (vABBC >= 0.1f) {
+ continue;
+ }
+
+ // Calculate the diagonal length by assuming a 90° angle at topleft
+ float dCpy = (float) Math.sqrt(dA * dA + dB * dB);
+ // Compare to the real distance in %
+ float vPyC = Math.abs((dC - dCpy) / Math.min(dC, dCpy));
+
+ if (vPyC >= 0.1f) {
+ continue;
+ }
+
+ // All tests passed!
+ results.addElement(test);
+ } // end iterate p3
+ } // end iterate p2
+ } // end iterate p1
+
+ if (!results.isEmpty()) {
+ FinderPattern[][] resultArray = new FinderPattern[results.size()][];
+ for (int i = 0; i < results.size(); i++) {
+ resultArray[i] = (FinderPattern[]) results.elementAt(i);
+ }
+ return resultArray;
+ }
+
+ // Nothing found!
+ throw NotFoundException.getNotFoundInstance();
+ }
+
+ public FinderPatternInfo[] findMulti(Hashtable hints) throws NotFoundException {
+ boolean tryHarder = hints != null && hints.containsKey(DecodeHintType.TRY_HARDER);
+ BitMatrix image = getImage();
+ int maxI = image.getHeight();
+ int maxJ = image.getWidth();
+ // We are looking for black/white/black/white/black modules in
+ // 1:1:3:1:1 ratio; this tracks the number of such modules seen so far
+
+ // Let's assume that the maximum version QR Code we support takes up 1/4 the height of the
+ // image, and then account for the center being 3 modules in size. This gives the smallest
+ // number of pixels the center could be, so skip this often. When trying harder, look for all
+ // QR versions regardless of how dense they are.
+ int iSkip = (int) (maxI / (MAX_MODULES * 4.0f) * 3);
+ if (iSkip < MIN_SKIP || tryHarder) {
+ iSkip = MIN_SKIP;
+ }
+
+ int[] stateCount = new int[5];
+ for (int i = iSkip - 1; i < maxI; i += iSkip) {
+ // Get a row of black/white values
+ stateCount[0] = 0;
+ stateCount[1] = 0;
+ stateCount[2] = 0;
+ stateCount[3] = 0;
+ stateCount[4] = 0;
+ int currentState = 0;
+ for (int j = 0; j < maxJ; j++) {
+ if (image.get(j, i)) {
+ // Black pixel
+ if ((currentState & 1) == 1) { // Counting white pixels
+ currentState++;
+ }
+ stateCount[currentState]++;
+ } else { // White pixel
+ if ((currentState & 1) == 0) { // Counting black pixels
+ if (currentState == 4) { // A winner?
+ if (foundPatternCross(stateCount)) { // Yes
+ boolean confirmed = handlePossibleCenter(stateCount, i, j);
+ if (!confirmed) {
+ do { // Advance to next black pixel
+ j++;
+ } while (j < maxJ && !image.get(j, i));
+ j--; // back up to that last white pixel
+ }
+ // Clear state to start looking again
+ currentState = 0;
+ stateCount[0] = 0;
+ stateCount[1] = 0;
+ stateCount[2] = 0;
+ stateCount[3] = 0;
+ stateCount[4] = 0;
+ } else { // No, shift counts back by two
+ stateCount[0] = stateCount[2];
+ stateCount[1] = stateCount[3];
+ stateCount[2] = stateCount[4];
+ stateCount[3] = 1;
+ stateCount[4] = 0;
+ currentState = 3;
+ }
+ } else {
+ stateCount[++currentState]++;
+ }
+ } else { // Counting white pixels
+ stateCount[currentState]++;
+ }
+ }
+ } // for j=...
+
+ if (foundPatternCross(stateCount)) {
+ handlePossibleCenter(stateCount, i, maxJ);
+ } // end if foundPatternCross
+ } // for i=iSkip-1 ...
+ FinderPattern[][] patternInfo = selectBestPatterns();
+ Vector result = new Vector();
+ for (int i = 0; i < patternInfo.length; i++) {
+ FinderPattern[] pattern = patternInfo[i];
+ ResultPoint.orderBestPatterns(pattern);
+ result.addElement(new FinderPatternInfo(pattern));
+ }
+
+ if (result.isEmpty()) {
+ return EMPTY_RESULT_ARRAY;
+ } else {
+ FinderPatternInfo[] resultArray = new FinderPatternInfo[result.size()];
+ for (int i = 0; i < result.size(); i++) {
+ resultArray[i] = (FinderPatternInfo) result.elementAt(i);
+ }
+ return resultArray;
+ }
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/QRCodeWriter.java b/libraries/zxing/src/com/google/zxing/qrcode/QRCodeWriter.java
new file mode 100644
index 000000000..fff4f5d1e
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/QRCodeWriter.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 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.qrcode;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.EncodeHintType;
+import com.google.zxing.Writer;
+import com.google.zxing.WriterException;
+import com.google.zxing.common.BitMatrix;
+import com.google.zxing.qrcode.encoder.ByteMatrix;
+import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
+import com.google.zxing.qrcode.encoder.Encoder;
+import com.google.zxing.qrcode.encoder.QRCode;
+
+import java.util.Hashtable;
+
+/**
+ * This object renders a QR Code as a BitMatrix 2D array of greyscale values.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+public final class QRCodeWriter implements Writer {
+
+ private static final int QUIET_ZONE_SIZE = 0; // patched for Bitcoin Wallet
+
+ public BitMatrix encode(String contents, BarcodeFormat format, int width, int height)
+ throws WriterException {
+
+ return encode(contents, format, width, height, null);
+ }
+
+ public BitMatrix encode(String contents, BarcodeFormat format, int width, int height,
+ Hashtable hints) throws WriterException {
+
+ if (contents == null || contents.length() == 0) {
+ throw new IllegalArgumentException("Found empty contents");
+ }
+
+ if (format != BarcodeFormat.QR_CODE) {
+ throw new IllegalArgumentException("Can only encode QR_CODE, but got " + format);
+ }
+
+ if (width < 0 || height < 0) {
+ throw new IllegalArgumentException("Requested dimensions are too small: " + width + 'x' +
+ height);
+ }
+
+ ErrorCorrectionLevel errorCorrectionLevel = ErrorCorrectionLevel.L;
+ if (hints != null) {
+ ErrorCorrectionLevel requestedECLevel = (ErrorCorrectionLevel) hints.get(EncodeHintType.ERROR_CORRECTION);
+ if (requestedECLevel != null) {
+ errorCorrectionLevel = requestedECLevel;
+ }
+ }
+
+ QRCode code = new QRCode();
+ Encoder.encode(contents, errorCorrectionLevel, hints, code);
+ return renderResult(code, width, height);
+ }
+
+ // Note that the input matrix uses 0 == white, 1 == black, while the output matrix uses
+ // 0 == black, 255 == white (i.e. an 8 bit greyscale bitmap).
+ private static BitMatrix renderResult(QRCode code, int width, int height) {
+ ByteMatrix input = code.getMatrix();
+ int inputWidth = input.getWidth();
+ int inputHeight = input.getHeight();
+ int qrWidth = inputWidth + (QUIET_ZONE_SIZE << 1);
+ int qrHeight = inputHeight + (QUIET_ZONE_SIZE << 1);
+ int outputWidth = Math.max(width, qrWidth);
+ int outputHeight = Math.max(height, qrHeight);
+
+ int multiple = Math.min(outputWidth / qrWidth, outputHeight / qrHeight);
+ // Padding includes both the quiet zone and the extra white pixels to accommodate the requested
+ // dimensions. For example, if input is 25x25 the QR will be 33x33 including the quiet zone.
+ // If the requested size is 200x160, the multiple will be 4, for a QR of 132x132. These will
+ // handle all the padding from 100x100 (the actual QR) up to 200x160.
+ int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
+ int topPadding = (outputHeight - (inputHeight * multiple)) / 2;
+
+ BitMatrix output = new BitMatrix(outputWidth, outputHeight);
+
+ for (int inputY = 0, outputY = topPadding; inputY < inputHeight; inputY++, outputY += multiple) {
+ // Write the contents of this row of the barcode
+ for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) {
+ if (input.get(inputX, inputY) == 1) {
+ output.setRegion(outputX, outputY, multiple, multiple);
+ }
+ }
+ }
+
+ return output;
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/decoder/BitMatrixParser.java b/libraries/zxing/src/com/google/zxing/qrcode/decoder/BitMatrixParser.java
new file mode 100644
index 000000000..9d131a554
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/decoder/BitMatrixParser.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2007 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.qrcode.decoder;
+
+import com.google.zxing.FormatException;
+import com.google.zxing.common.BitMatrix;
+
+/**
+ * @author Sean Owen
+ */
+final class BitMatrixParser {
+
+ private final BitMatrix bitMatrix;
+ private Version parsedVersion;
+ private FormatInformation parsedFormatInfo;
+
+ /**
+ * @param bitMatrix {@link BitMatrix} to parse
+ * @throws FormatException if dimension is not >= 21 and 1 mod 4
+ */
+ BitMatrixParser(BitMatrix bitMatrix) throws FormatException {
+ int dimension = bitMatrix.getHeight();
+ if (dimension < 21 || (dimension & 0x03) != 1) {
+ throw FormatException.getFormatInstance();
+ }
+ this.bitMatrix = bitMatrix;
+ }
+
+ /**
+ *
Reads format information from one of its two locations within the QR Code.
+ *
+ * @return {@link FormatInformation} encapsulating the QR Code's format info
+ * @throws FormatException if both format information locations cannot be parsed as
+ * the valid encoding of format information
+ */
+ FormatInformation readFormatInformation() throws FormatException {
+
+ if (parsedFormatInfo != null) {
+ return parsedFormatInfo;
+ }
+
+ // Read top-left format info bits
+ int formatInfoBits1 = 0;
+ for (int i = 0; i < 6; i++) {
+ formatInfoBits1 = copyBit(i, 8, formatInfoBits1);
+ }
+ // .. and skip a bit in the timing pattern ...
+ formatInfoBits1 = copyBit(7, 8, formatInfoBits1);
+ formatInfoBits1 = copyBit(8, 8, formatInfoBits1);
+ formatInfoBits1 = copyBit(8, 7, formatInfoBits1);
+ // .. and skip a bit in the timing pattern ...
+ for (int j = 5; j >= 0; j--) {
+ formatInfoBits1 = copyBit(8, j, formatInfoBits1);
+ }
+
+ // Read the top-right/bottom-left pattern too
+ int dimension = bitMatrix.getHeight();
+ int formatInfoBits2 = 0;
+ int jMin = dimension - 7;
+ for (int j = dimension - 1; j >= jMin; j--) {
+ formatInfoBits2 = copyBit(8, j, formatInfoBits2);
+ }
+ for (int i = dimension - 8; i < dimension; i++) {
+ formatInfoBits2 = copyBit(i, 8, formatInfoBits2);
+ }
+
+ parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits1, formatInfoBits2);
+ if (parsedFormatInfo != null) {
+ return parsedFormatInfo;
+ }
+ throw FormatException.getFormatInstance();
+ }
+
+ /**
+ *
Reads version information from one of its two locations within the QR Code.
+ *
+ * @return {@link Version} encapsulating the QR Code's version
+ * @throws FormatException if both version information locations cannot be parsed as
+ * the valid encoding of version information
+ */
+ Version readVersion() throws FormatException {
+
+ if (parsedVersion != null) {
+ return parsedVersion;
+ }
+
+ int dimension = bitMatrix.getHeight();
+
+ int provisionalVersion = (dimension - 17) >> 2;
+ if (provisionalVersion <= 6) {
+ return Version.getVersionForNumber(provisionalVersion);
+ }
+
+ // Read top-right version info: 3 wide by 6 tall
+ int versionBits = 0;
+ int ijMin = dimension - 11;
+ for (int j = 5; j >= 0; j--) {
+ for (int i = dimension - 9; i >= ijMin; i--) {
+ versionBits = copyBit(i, j, versionBits);
+ }
+ }
+
+ parsedVersion = Version.decodeVersionInformation(versionBits);
+ if (parsedVersion != null && parsedVersion.getDimensionForVersion() == dimension) {
+ return parsedVersion;
+ }
+
+ // Hmm, failed. Try bottom left: 6 wide by 3 tall
+ versionBits = 0;
+ for (int i = 5; i >= 0; i--) {
+ for (int j = dimension - 9; j >= ijMin; j--) {
+ versionBits = copyBit(i, j, versionBits);
+ }
+ }
+
+ parsedVersion = Version.decodeVersionInformation(versionBits);
+ if (parsedVersion != null && parsedVersion.getDimensionForVersion() == dimension) {
+ return parsedVersion;
+ }
+ throw FormatException.getFormatInstance();
+ }
+
+ private int copyBit(int i, int j, int versionBits) {
+ return bitMatrix.get(i, j) ? (versionBits << 1) | 0x1 : versionBits << 1;
+ }
+
+ /**
+ *
Reads the bits in the {@link BitMatrix} representing the finder pattern in the
+ * correct order in order to reconstitute the codewords bytes contained within the
+ * QR Code.
+ *
+ * @return bytes encoded within the QR Code
+ * @throws FormatException if the exact number of bytes expected is not read
+ */
+ byte[] readCodewords() throws FormatException {
+
+ FormatInformation formatInfo = readFormatInformation();
+ Version version = readVersion();
+
+ // Get the data mask for the format used in this QR Code. This will exclude
+ // some bits from reading as we wind through the bit matrix.
+ DataMask dataMask = DataMask.forReference((int) formatInfo.getDataMask());
+ int dimension = bitMatrix.getHeight();
+ dataMask.unmaskBitMatrix(bitMatrix, dimension);
+
+ BitMatrix functionPattern = version.buildFunctionPattern();
+
+ boolean readingUp = true;
+ byte[] result = new byte[version.getTotalCodewords()];
+ int resultOffset = 0;
+ int currentByte = 0;
+ int bitsRead = 0;
+ // Read columns in pairs, from right to left
+ for (int j = dimension - 1; j > 0; j -= 2) {
+ if (j == 6) {
+ // Skip whole column with vertical alignment pattern;
+ // saves time and makes the other code proceed more cleanly
+ j--;
+ }
+ // Read alternatingly from bottom to top then top to bottom
+ for (int count = 0; count < dimension; count++) {
+ int i = readingUp ? dimension - 1 - count : count;
+ for (int col = 0; col < 2; col++) {
+ // Ignore bits covered by the function pattern
+ if (!functionPattern.get(j - col, i)) {
+ // Read a bit
+ bitsRead++;
+ currentByte <<= 1;
+ if (bitMatrix.get(j - col, i)) {
+ currentByte |= 1;
+ }
+ // If we've made a whole byte, save it off
+ if (bitsRead == 8) {
+ result[resultOffset++] = (byte) currentByte;
+ bitsRead = 0;
+ currentByte = 0;
+ }
+ }
+ }
+ }
+ readingUp ^= true; // readingUp = !readingUp; // switch directions
+ }
+ if (resultOffset != version.getTotalCodewords()) {
+ throw FormatException.getFormatInstance();
+ }
+ return result;
+ }
+
+}
\ No newline at end of file
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/decoder/DataBlock.java b/libraries/zxing/src/com/google/zxing/qrcode/decoder/DataBlock.java
new file mode 100644
index 000000000..12959d9c1
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/decoder/DataBlock.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2007 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.qrcode.decoder;
+
+/**
+ *
Encapsulates a block of data within a QR Code. QR Codes may split their data into
+ * multiple blocks, each of which is a unit of data and error-correction codewords. Each
+ * is represented by an instance of this class.
+ *
+ * @author Sean Owen
+ */
+final class DataBlock {
+
+ private final int numDataCodewords;
+ private final byte[] codewords;
+
+ private DataBlock(int numDataCodewords, byte[] codewords) {
+ this.numDataCodewords = numDataCodewords;
+ this.codewords = codewords;
+ }
+
+ /**
+ *
When QR Codes use multiple data blocks, they are actually interleaved.
+ * That is, the first byte of data block 1 to n is written, then the second bytes, and so on. This
+ * method will separate the data into original blocks.
+ *
+ * @param rawCodewords bytes as read directly from the QR Code
+ * @param version version of the QR Code
+ * @param ecLevel error-correction level of the QR Code
+ * @return DataBlocks containing original bytes, "de-interleaved" from representation in the
+ * QR Code
+ */
+ static DataBlock[] getDataBlocks(byte[] rawCodewords,
+ Version version,
+ ErrorCorrectionLevel ecLevel) {
+
+ if (rawCodewords.length != version.getTotalCodewords()) {
+ throw new IllegalArgumentException();
+ }
+
+ // Figure out the number and size of data blocks used by this version and
+ // error correction level
+ Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel);
+
+ // First count the total number of data blocks
+ int totalBlocks = 0;
+ Version.ECB[] ecBlockArray = ecBlocks.getECBlocks();
+ for (int i = 0; i < ecBlockArray.length; i++) {
+ totalBlocks += ecBlockArray[i].getCount();
+ }
+
+ // Now establish DataBlocks of the appropriate size and number of data codewords
+ DataBlock[] result = new DataBlock[totalBlocks];
+ int numResultBlocks = 0;
+ for (int j = 0; j < ecBlockArray.length; j++) {
+ Version.ECB ecBlock = ecBlockArray[j];
+ for (int i = 0; i < ecBlock.getCount(); i++) {
+ int numDataCodewords = ecBlock.getDataCodewords();
+ int numBlockCodewords = ecBlocks.getECCodewordsPerBlock() + numDataCodewords;
+ result[numResultBlocks++] = new DataBlock(numDataCodewords, new byte[numBlockCodewords]);
+ }
+ }
+
+ // All blocks have the same amount of data, except that the last n
+ // (where n may be 0) have 1 more byte. Figure out where these start.
+ int shorterBlocksTotalCodewords = result[0].codewords.length;
+ int longerBlocksStartAt = result.length - 1;
+ while (longerBlocksStartAt >= 0) {
+ int numCodewords = result[longerBlocksStartAt].codewords.length;
+ if (numCodewords == shorterBlocksTotalCodewords) {
+ break;
+ }
+ longerBlocksStartAt--;
+ }
+ longerBlocksStartAt++;
+
+ int shorterBlocksNumDataCodewords = shorterBlocksTotalCodewords - ecBlocks.getECCodewordsPerBlock();
+ // The last elements of result may be 1 element longer;
+ // first fill out as many elements as all of them have
+ int rawCodewordsOffset = 0;
+ for (int i = 0; i < shorterBlocksNumDataCodewords; i++) {
+ for (int j = 0; j < numResultBlocks; j++) {
+ result[j].codewords[i] = rawCodewords[rawCodewordsOffset++];
+ }
+ }
+ // Fill out the last data block in the longer ones
+ for (int j = longerBlocksStartAt; j < numResultBlocks; j++) {
+ result[j].codewords[shorterBlocksNumDataCodewords] = rawCodewords[rawCodewordsOffset++];
+ }
+ // Now add in error correction blocks
+ int max = result[0].codewords.length;
+ for (int i = shorterBlocksNumDataCodewords; i < max; i++) {
+ for (int j = 0; j < numResultBlocks; j++) {
+ int iOffset = j < longerBlocksStartAt ? i : i + 1;
+ result[j].codewords[iOffset] = rawCodewords[rawCodewordsOffset++];
+ }
+ }
+ return result;
+ }
+
+ int getNumDataCodewords() {
+ return numDataCodewords;
+ }
+
+ byte[] getCodewords() {
+ return codewords;
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/decoder/DataMask.java b/libraries/zxing/src/com/google/zxing/qrcode/decoder/DataMask.java
new file mode 100644
index 000000000..d29dbd47f
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/decoder/DataMask.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2007 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.qrcode.decoder;
+
+import com.google.zxing.common.BitMatrix;
+
+/**
+ *
Encapsulates data masks for the data bits in a QR code, per ISO 18004:2006 6.8. Implementations
+ * of this class can un-mask a raw BitMatrix. For simplicity, they will unmask the entire BitMatrix,
+ * including areas used for finder patterns, timing patterns, etc. These areas should be unused
+ * after the point they are unmasked anyway.
+ *
+ *
Note that the diagram in section 6.8.1 is misleading since it indicates that i is column position
+ * and j is row position. In fact, as the text says, i is row position and j is column position.
+ *
+ * @author Sean Owen
+ */
+abstract class DataMask {
+
+ /**
+ * See ISO 18004:2006 6.8.1
+ */
+ private static final DataMask[] DATA_MASKS = {
+ new DataMask000(),
+ new DataMask001(),
+ new DataMask010(),
+ new DataMask011(),
+ new DataMask100(),
+ new DataMask101(),
+ new DataMask110(),
+ new DataMask111(),
+ };
+
+ private DataMask() {
+ }
+
+ /**
+ *
Implementations of this method reverse the data masking process applied to a QR Code and
+ * make its bits ready to read.
+ *
+ * @param bits representation of QR Code bits
+ * @param dimension dimension of QR Code, represented by bits, being unmasked
+ */
+ final void unmaskBitMatrix(BitMatrix bits, int dimension) {
+ for (int i = 0; i < dimension; i++) {
+ for (int j = 0; j < dimension; j++) {
+ if (isMasked(i, j)) {
+ bits.flip(j, i);
+ }
+ }
+ }
+ }
+
+ abstract boolean isMasked(int i, int j);
+
+ /**
+ * @param reference a value between 0 and 7 indicating one of the eight possible
+ * data mask patterns a QR Code may use
+ * @return DataMask encapsulating the data mask pattern
+ */
+ static DataMask forReference(int reference) {
+ if (reference < 0 || reference > 7) {
+ throw new IllegalArgumentException();
+ }
+ return DATA_MASKS[reference];
+ }
+
+ /**
+ * 000: mask bits for which (x + y) mod 2 == 0
+ */
+ private static class DataMask000 extends DataMask {
+ boolean isMasked(int i, int j) {
+ return ((i + j) & 0x01) == 0;
+ }
+ }
+
+ /**
+ * 001: mask bits for which x mod 2 == 0
+ */
+ private static class DataMask001 extends DataMask {
+ boolean isMasked(int i, int j) {
+ return (i & 0x01) == 0;
+ }
+ }
+
+ /**
+ * 010: mask bits for which y mod 3 == 0
+ */
+ private static class DataMask010 extends DataMask {
+ boolean isMasked(int i, int j) {
+ return j % 3 == 0;
+ }
+ }
+
+ /**
+ * 011: mask bits for which (x + y) mod 3 == 0
+ */
+ private static class DataMask011 extends DataMask {
+ boolean isMasked(int i, int j) {
+ return (i + j) % 3 == 0;
+ }
+ }
+
+ /**
+ * 100: mask bits for which (x/2 + y/3) mod 2 == 0
+ */
+ private static class DataMask100 extends DataMask {
+ boolean isMasked(int i, int j) {
+ return (((i >>> 1) + (j /3)) & 0x01) == 0;
+ }
+ }
+
+ /**
+ * 101: mask bits for which xy mod 2 + xy mod 3 == 0
+ */
+ private static class DataMask101 extends DataMask {
+ boolean isMasked(int i, int j) {
+ int temp = i * j;
+ return (temp & 0x01) + (temp % 3) == 0;
+ }
+ }
+
+ /**
+ * 110: mask bits for which (xy mod 2 + xy mod 3) mod 2 == 0
+ */
+ private static class DataMask110 extends DataMask {
+ boolean isMasked(int i, int j) {
+ int temp = i * j;
+ return (((temp & 0x01) + (temp % 3)) & 0x01) == 0;
+ }
+ }
+
+ /**
+ * 111: mask bits for which ((x+y)mod 2 + xy mod 3) mod 2 == 0
+ */
+ private static class DataMask111 extends DataMask {
+ boolean isMasked(int i, int j) {
+ return ((((i + j) & 0x01) + ((i * j) % 3)) & 0x01) == 0;
+ }
+ }
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java b/libraries/zxing/src/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java
new file mode 100644
index 000000000..ff374ac50
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright 2007 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.qrcode.decoder;
+
+import com.google.zxing.FormatException;
+import com.google.zxing.common.BitSource;
+import com.google.zxing.common.CharacterSetECI;
+import com.google.zxing.common.DecoderResult;
+import com.google.zxing.common.StringUtils;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Hashtable;
+import java.util.Vector;
+
+/**
+ *
QR Codes can encode text as bits in one of several modes, and can use multiple modes
+ * in one QR Code. This class decodes the bits back into text.
+ *
+ *
See ISO 18004:2006, 6.4.3 - 6.4.7
+ *
+ * @author Sean Owen
+ */
+final class DecodedBitStreamParser {
+
+ /**
+ * See ISO 18004:2006, 6.4.4 Table 5
+ */
+ private static final char[] ALPHANUMERIC_CHARS = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',
+ 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
+ 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+ ' ', '$', '%', '*', '+', '-', '.', '/', ':'
+ };
+ private static final int GB2312_SUBSET = 1;
+
+ private DecodedBitStreamParser() {
+ }
+
+ static DecoderResult decode(byte[] bytes, Version version, ErrorCorrectionLevel ecLevel, Hashtable hints)
+ throws FormatException {
+ BitSource bits = new BitSource(bytes);
+ StringBuffer result = new StringBuffer(50);
+ CharacterSetECI currentCharacterSetECI = null;
+ boolean fc1InEffect = false;
+ Vector byteSegments = new Vector(1);
+ Mode mode;
+ do {
+ // While still another segment to read...
+ if (bits.available() < 4) {
+ // OK, assume we're done. Really, a TERMINATOR mode should have been recorded here
+ mode = Mode.TERMINATOR;
+ } else {
+ try {
+ mode = Mode.forBits(bits.readBits(4)); // mode is encoded by 4 bits
+ } catch (IllegalArgumentException iae) {
+ throw FormatException.getFormatInstance();
+ }
+ }
+ if (!mode.equals(Mode.TERMINATOR)) {
+ if (mode.equals(Mode.FNC1_FIRST_POSITION) || mode.equals(Mode.FNC1_SECOND_POSITION)) {
+ // We do little with FNC1 except alter the parsed result a bit according to the spec
+ fc1InEffect = true;
+ } else if (mode.equals(Mode.STRUCTURED_APPEND)) {
+ // not really supported; all we do is ignore it
+ // Read next 8 bits (symbol sequence #) and 8 bits (parity data), then continue
+ bits.readBits(16);
+ } else if (mode.equals(Mode.ECI)) {
+ // Count doesn't apply to ECI
+ int value = parseECIValue(bits);
+ currentCharacterSetECI = CharacterSetECI.getCharacterSetECIByValue(value);
+ if (currentCharacterSetECI == null) {
+ throw FormatException.getFormatInstance();
+ }
+ } else {
+ // First handle Hanzi mode which does not start with character count
+ if (mode.equals(Mode.HANZI)) {
+ //chinese mode contains a sub set indicator right after mode indicator
+ int subset = bits.readBits(4);
+ int countHanzi = bits.readBits(mode.getCharacterCountBits(version));
+ if (subset == GB2312_SUBSET) {
+ decodeHanziSegment(bits, result, countHanzi);
+ }
+ } else {
+ // "Normal" QR code modes:
+ // How many characters will follow, encoded in this mode?
+ int count = bits.readBits(mode.getCharacterCountBits(version));
+ if (mode.equals(Mode.NUMERIC)) {
+ decodeNumericSegment(bits, result, count);
+ } else if (mode.equals(Mode.ALPHANUMERIC)) {
+ decodeAlphanumericSegment(bits, result, count, fc1InEffect);
+ } else if (mode.equals(Mode.BYTE)) {
+ decodeByteSegment(bits, result, count, currentCharacterSetECI, byteSegments, hints);
+ } else if (mode.equals(Mode.KANJI)) {
+ decodeKanjiSegment(bits, result, count);
+ } else {
+ throw FormatException.getFormatInstance();
+ }
+ }
+ }
+ }
+ } while (!mode.equals(Mode.TERMINATOR));
+
+ return new DecoderResult(bytes,
+ result.toString(),
+ byteSegments.isEmpty() ? null : byteSegments,
+ ecLevel == null ? null : ecLevel.toString());
+ }
+
+ /**
+ * See specification GBT 18284-2000
+ */
+ private static void decodeHanziSegment(BitSource bits,
+ StringBuffer result,
+ int count) throws FormatException {
+ // Don't crash trying to read more bits than we have available.
+ if (count * 13 > bits.available()) {
+ throw FormatException.getFormatInstance();
+ }
+
+ // Each character will require 2 bytes. Read the characters as 2-byte pairs
+ // and decode as GB2312 afterwards
+ byte[] buffer = new byte[2 * count];
+ int offset = 0;
+ while (count > 0) {
+ // Each 13 bits encodes a 2-byte character
+ int twoBytes = bits.readBits(13);
+ int assembledTwoBytes = ((twoBytes / 0x060) << 8) | (twoBytes % 0x060);
+ if (assembledTwoBytes < 0x003BF) {
+ // In the 0xA1A1 to 0xAAFE range
+ assembledTwoBytes += 0x0A1A1;
+ } else {
+ // In the 0xB0A1 to 0xFAFE range
+ assembledTwoBytes += 0x0A6A1;
+ }
+ buffer[offset] = (byte) ((assembledTwoBytes >> 8) & 0xFF);
+ buffer[offset + 1] = (byte) (assembledTwoBytes & 0xFF);
+ offset += 2;
+ count--;
+ }
+
+ try {
+ result.append(new String(buffer, StringUtils.GB2312));
+ } catch (UnsupportedEncodingException uee) {
+ throw FormatException.getFormatInstance();
+ }
+ }
+
+ private static void decodeKanjiSegment(BitSource bits,
+ StringBuffer result,
+ int count) throws FormatException {
+ // Don't crash trying to read more bits than we have available.
+ if (count * 13 > bits.available()) {
+ throw FormatException.getFormatInstance();
+ }
+
+ // Each character will require 2 bytes. Read the characters as 2-byte pairs
+ // and decode as Shift_JIS afterwards
+ byte[] buffer = new byte[2 * count];
+ int offset = 0;
+ while (count > 0) {
+ // Each 13 bits encodes a 2-byte character
+ int twoBytes = bits.readBits(13);
+ int assembledTwoBytes = ((twoBytes / 0x0C0) << 8) | (twoBytes % 0x0C0);
+ if (assembledTwoBytes < 0x01F00) {
+ // In the 0x8140 to 0x9FFC range
+ assembledTwoBytes += 0x08140;
+ } else {
+ // In the 0xE040 to 0xEBBF range
+ assembledTwoBytes += 0x0C140;
+ }
+ buffer[offset] = (byte) (assembledTwoBytes >> 8);
+ buffer[offset + 1] = (byte) assembledTwoBytes;
+ offset += 2;
+ count--;
+ }
+ // Shift_JIS may not be supported in some environments:
+ try {
+ result.append(new String(buffer, StringUtils.SHIFT_JIS));
+ } catch (UnsupportedEncodingException uee) {
+ throw FormatException.getFormatInstance();
+ }
+ }
+
+ private static void decodeByteSegment(BitSource bits,
+ StringBuffer result,
+ int count,
+ CharacterSetECI currentCharacterSetECI,
+ Vector byteSegments,
+ Hashtable hints) throws FormatException {
+ // Don't crash trying to read more bits than we have available.
+ if (count << 3 > bits.available()) {
+ throw FormatException.getFormatInstance();
+ }
+
+ byte[] readBytes = new byte[count];
+ for (int i = 0; i < count; i++) {
+ readBytes[i] = (byte) bits.readBits(8);
+ }
+ String encoding;
+ if (currentCharacterSetECI == null) {
+ // The spec isn't clear on this mode; see
+ // section 6.4.5: t does not say which encoding to assuming
+ // upon decoding. I have seen ISO-8859-1 used as well as
+ // Shift_JIS -- without anything like an ECI designator to
+ // give a hint.
+ encoding = StringUtils.guessEncoding(readBytes, hints);
+ } else {
+ encoding = currentCharacterSetECI.getEncodingName();
+ }
+ try {
+ result.append(new String(readBytes, encoding));
+ } catch (UnsupportedEncodingException uce) {
+ throw FormatException.getFormatInstance();
+ }
+ byteSegments.addElement(readBytes);
+ }
+
+ private static char toAlphaNumericChar(int value) throws FormatException {
+ if (value >= ALPHANUMERIC_CHARS.length) {
+ throw FormatException.getFormatInstance();
+ }
+ return ALPHANUMERIC_CHARS[value];
+ }
+
+ private static void decodeAlphanumericSegment(BitSource bits,
+ StringBuffer result,
+ int count,
+ boolean fc1InEffect) throws FormatException {
+ // Read two characters at a time
+ int start = result.length();
+ while (count > 1) {
+ int nextTwoCharsBits = bits.readBits(11);
+ result.append(toAlphaNumericChar(nextTwoCharsBits / 45));
+ result.append(toAlphaNumericChar(nextTwoCharsBits % 45));
+ count -= 2;
+ }
+ if (count == 1) {
+ // special case: one character left
+ result.append(toAlphaNumericChar(bits.readBits(6)));
+ }
+ // See section 6.4.8.1, 6.4.8.2
+ if (fc1InEffect) {
+ // We need to massage the result a bit if in an FNC1 mode:
+ for (int i = start; i < result.length(); i++) {
+ if (result.charAt(i) == '%') {
+ if (i < result.length() - 1 && result.charAt(i + 1) == '%') {
+ // %% is rendered as %
+ result.deleteCharAt(i + 1);
+ } else {
+ // In alpha mode, % should be converted to FNC1 separator 0x1D
+ result.setCharAt(i, (char) 0x1D);
+ }
+ }
+ }
+ }
+ }
+
+ private static void decodeNumericSegment(BitSource bits,
+ StringBuffer result,
+ int count) throws FormatException {
+ // Read three digits at a time
+ while (count >= 3) {
+ // Each 10 bits encodes three digits
+ int threeDigitsBits = bits.readBits(10);
+ if (threeDigitsBits >= 1000) {
+ throw FormatException.getFormatInstance();
+ }
+ result.append(toAlphaNumericChar(threeDigitsBits / 100));
+ result.append(toAlphaNumericChar((threeDigitsBits / 10) % 10));
+ result.append(toAlphaNumericChar(threeDigitsBits % 10));
+ count -= 3;
+ }
+ if (count == 2) {
+ // Two digits left over to read, encoded in 7 bits
+ int twoDigitsBits = bits.readBits(7);
+ if (twoDigitsBits >= 100) {
+ throw FormatException.getFormatInstance();
+ }
+ result.append(toAlphaNumericChar(twoDigitsBits / 10));
+ result.append(toAlphaNumericChar(twoDigitsBits % 10));
+ } else if (count == 1) {
+ // One digit left over to read
+ int digitBits = bits.readBits(4);
+ if (digitBits >= 10) {
+ throw FormatException.getFormatInstance();
+ }
+ result.append(toAlphaNumericChar(digitBits));
+ }
+ }
+
+ private static int parseECIValue(BitSource bits) {
+ int firstByte = bits.readBits(8);
+ if ((firstByte & 0x80) == 0) {
+ // just one byte
+ return firstByte & 0x7F;
+ } else if ((firstByte & 0xC0) == 0x80) {
+ // two bytes
+ int secondByte = bits.readBits(8);
+ return ((firstByte & 0x3F) << 8) | secondByte;
+ } else if ((firstByte & 0xE0) == 0xC0) {
+ // three bytes
+ int secondThirdBytes = bits.readBits(16);
+ return ((firstByte & 0x1F) << 16) | secondThirdBytes;
+ }
+ throw new IllegalArgumentException("Bad ECI bits starting with byte " + firstByte);
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/decoder/ErrorCorrectionLevel.java b/libraries/zxing/src/com/google/zxing/qrcode/decoder/ErrorCorrectionLevel.java
new file mode 100644
index 000000000..e8d6c2589
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/decoder/ErrorCorrectionLevel.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2007 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.qrcode.decoder;
+
+/**
+ *
See ISO 18004:2006, 6.5.1. This enum encapsulates the four error correction levels
+ * defined by the QR code standard.
+ *
+ * @author Sean Owen
+ */
+public final class ErrorCorrectionLevel {
+
+ // No, we can't use an enum here. J2ME doesn't support it.
+
+ /**
+ * L = ~7% correction
+ */
+ public static final ErrorCorrectionLevel L = new ErrorCorrectionLevel(0, 0x01, "L");
+ /**
+ * M = ~15% correction
+ */
+ public static final ErrorCorrectionLevel M = new ErrorCorrectionLevel(1, 0x00, "M");
+ /**
+ * Q = ~25% correction
+ */
+ public static final ErrorCorrectionLevel Q = new ErrorCorrectionLevel(2, 0x03, "Q");
+ /**
+ * H = ~30% correction
+ */
+ public static final ErrorCorrectionLevel H = new ErrorCorrectionLevel(3, 0x02, "H");
+
+ private static final ErrorCorrectionLevel[] FOR_BITS = {M, L, H, Q};
+
+ private final int ordinal;
+ private final int bits;
+ private final String name;
+
+ private ErrorCorrectionLevel(int ordinal, int bits, String name) {
+ this.ordinal = ordinal;
+ this.bits = bits;
+ this.name = name;
+ }
+
+ public int ordinal() {
+ return ordinal;
+ }
+
+ public int getBits() {
+ return bits;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String toString() {
+ return name;
+ }
+
+ /**
+ * @param bits int containing the two bits encoding a QR Code's error correction level
+ * @return ErrorCorrectionLevel representing the encoded error correction level
+ */
+ public static ErrorCorrectionLevel forBits(int bits) {
+ if (bits < 0 || bits >= FOR_BITS.length) {
+ throw new IllegalArgumentException();
+ }
+ return FOR_BITS[bits];
+ }
+
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/decoder/FormatInformation.java b/libraries/zxing/src/com/google/zxing/qrcode/decoder/FormatInformation.java
new file mode 100644
index 000000000..1b76b0de5
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/decoder/FormatInformation.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2007 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.qrcode.decoder;
+
+/**
+ *
Encapsulates a QR Code's format information, including the data mask used and
+ * error correction level.
+ *
+ * @author Sean Owen
+ * @see DataMask
+ * @see ErrorCorrectionLevel
+ */
+final class FormatInformation {
+
+ private static final int FORMAT_INFO_MASK_QR = 0x5412;
+
+ /**
+ * See ISO 18004:2006, Annex C, Table C.1
+ */
+ private static final int[][] FORMAT_INFO_DECODE_LOOKUP = {
+ {0x5412, 0x00},
+ {0x5125, 0x01},
+ {0x5E7C, 0x02},
+ {0x5B4B, 0x03},
+ {0x45F9, 0x04},
+ {0x40CE, 0x05},
+ {0x4F97, 0x06},
+ {0x4AA0, 0x07},
+ {0x77C4, 0x08},
+ {0x72F3, 0x09},
+ {0x7DAA, 0x0A},
+ {0x789D, 0x0B},
+ {0x662F, 0x0C},
+ {0x6318, 0x0D},
+ {0x6C41, 0x0E},
+ {0x6976, 0x0F},
+ {0x1689, 0x10},
+ {0x13BE, 0x11},
+ {0x1CE7, 0x12},
+ {0x19D0, 0x13},
+ {0x0762, 0x14},
+ {0x0255, 0x15},
+ {0x0D0C, 0x16},
+ {0x083B, 0x17},
+ {0x355F, 0x18},
+ {0x3068, 0x19},
+ {0x3F31, 0x1A},
+ {0x3A06, 0x1B},
+ {0x24B4, 0x1C},
+ {0x2183, 0x1D},
+ {0x2EDA, 0x1E},
+ {0x2BED, 0x1F},
+ };
+
+ /**
+ * Offset i holds the number of 1 bits in the binary representation of i
+ */
+ private static final int[] BITS_SET_IN_HALF_BYTE =
+ {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4};
+
+ private final ErrorCorrectionLevel errorCorrectionLevel;
+ private final byte dataMask;
+
+ private FormatInformation(int formatInfo) {
+ // Bits 3,4
+ errorCorrectionLevel = ErrorCorrectionLevel.forBits((formatInfo >> 3) & 0x03);
+ // Bottom 3 bits
+ dataMask = (byte) (formatInfo & 0x07);
+ }
+
+ static int numBitsDiffering(int a, int b) {
+ a ^= b; // a now has a 1 bit exactly where its bit differs with b's
+ // Count bits set quickly with a series of lookups:
+ return BITS_SET_IN_HALF_BYTE[a & 0x0F] +
+ BITS_SET_IN_HALF_BYTE[(a >>> 4 & 0x0F)] +
+ BITS_SET_IN_HALF_BYTE[(a >>> 8 & 0x0F)] +
+ BITS_SET_IN_HALF_BYTE[(a >>> 12 & 0x0F)] +
+ BITS_SET_IN_HALF_BYTE[(a >>> 16 & 0x0F)] +
+ BITS_SET_IN_HALF_BYTE[(a >>> 20 & 0x0F)] +
+ BITS_SET_IN_HALF_BYTE[(a >>> 24 & 0x0F)] +
+ BITS_SET_IN_HALF_BYTE[(a >>> 28 & 0x0F)];
+ }
+
+ /**
+ * @param maskedFormatInfo1 format info indicator, with mask still applied
+ * @param maskedFormatInfo2 second copy of same info; both are checked at the same time
+ * to establish best match
+ * @return information about the format it specifies, or null
+ * if doesn't seem to match any known pattern
+ */
+ static FormatInformation decodeFormatInformation(int maskedFormatInfo1, int maskedFormatInfo2) {
+ FormatInformation formatInfo = doDecodeFormatInformation(maskedFormatInfo1, maskedFormatInfo2);
+ if (formatInfo != null) {
+ return formatInfo;
+ }
+ // Should return null, but, some QR codes apparently
+ // do not mask this info. Try again by actually masking the pattern
+ // first
+ return doDecodeFormatInformation(maskedFormatInfo1 ^ FORMAT_INFO_MASK_QR,
+ maskedFormatInfo2 ^ FORMAT_INFO_MASK_QR);
+ }
+
+ private static FormatInformation doDecodeFormatInformation(int maskedFormatInfo1, int maskedFormatInfo2) {
+ // Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing
+ int bestDifference = Integer.MAX_VALUE;
+ int bestFormatInfo = 0;
+ for (int i = 0; i < FORMAT_INFO_DECODE_LOOKUP.length; i++) {
+ int[] decodeInfo = FORMAT_INFO_DECODE_LOOKUP[i];
+ int targetInfo = decodeInfo[0];
+ if (targetInfo == maskedFormatInfo1 || targetInfo == maskedFormatInfo2) {
+ // Found an exact match
+ return new FormatInformation(decodeInfo[1]);
+ }
+ int bitsDifference = numBitsDiffering(maskedFormatInfo1, targetInfo);
+ if (bitsDifference < bestDifference) {
+ bestFormatInfo = decodeInfo[1];
+ bestDifference = bitsDifference;
+ }
+ if (maskedFormatInfo1 != maskedFormatInfo2) {
+ // also try the other option
+ bitsDifference = numBitsDiffering(maskedFormatInfo2, targetInfo);
+ if (bitsDifference < bestDifference) {
+ bestFormatInfo = decodeInfo[1];
+ bestDifference = bitsDifference;
+ }
+ }
+ }
+ // Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits
+ // differing means we found a match
+ if (bestDifference <= 3) {
+ return new FormatInformation(bestFormatInfo);
+ }
+ return null;
+ }
+
+ ErrorCorrectionLevel getErrorCorrectionLevel() {
+ return errorCorrectionLevel;
+ }
+
+ byte getDataMask() {
+ return dataMask;
+ }
+
+ public int hashCode() {
+ return (errorCorrectionLevel.ordinal() << 3) | (int) dataMask;
+ }
+
+ public boolean equals(Object o) {
+ if (!(o instanceof FormatInformation)) {
+ return false;
+ }
+ FormatInformation other = (FormatInformation) o;
+ return this.errorCorrectionLevel == other.errorCorrectionLevel &&
+ this.dataMask == other.dataMask;
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/decoder/Mode.java b/libraries/zxing/src/com/google/zxing/qrcode/decoder/Mode.java
new file mode 100644
index 000000000..3c66217d3
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/decoder/Mode.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2007 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.qrcode.decoder;
+
+/**
+ *
See ISO 18004:2006, 6.4.1, Tables 2 and 3. This enum encapsulates the various modes in which
+ * data can be encoded to bits in the QR code standard.
+ *
+ * @author Sean Owen
+ */
+public final class Mode {
+
+ // No, we can't use an enum here. J2ME doesn't support it.
+
+ public static final Mode TERMINATOR = new Mode(new int[]{0, 0, 0}, 0x00, "TERMINATOR"); // Not really a mode...
+ public static final Mode NUMERIC = new Mode(new int[]{10, 12, 14}, 0x01, "NUMERIC");
+ public static final Mode ALPHANUMERIC = new Mode(new int[]{9, 11, 13}, 0x02, "ALPHANUMERIC");
+ public static final Mode STRUCTURED_APPEND = new Mode(new int[]{0, 0, 0}, 0x03, "STRUCTURED_APPEND"); // Not supported
+ public static final Mode BYTE = new Mode(new int[]{8, 16, 16}, 0x04, "BYTE");
+ public static final Mode ECI = new Mode(null, 0x07, "ECI"); // character counts don't apply
+ public static final Mode KANJI = new Mode(new int[]{8, 10, 12}, 0x08, "KANJI");
+ public static final Mode FNC1_FIRST_POSITION = new Mode(null, 0x05, "FNC1_FIRST_POSITION");
+ public static final Mode FNC1_SECOND_POSITION = new Mode(null, 0x09, "FNC1_SECOND_POSITION");
+ /** See GBT 18284-2000; "Hanzi" is a transliteration of this mode name. */
+ public static final Mode HANZI = new Mode(new int[]{8, 10, 12}, 0x0D, "HANZI");
+
+ private final int[] characterCountBitsForVersions;
+ private final int bits;
+ private final String name;
+
+ private Mode(int[] characterCountBitsForVersions, int bits, String name) {
+ this.characterCountBitsForVersions = characterCountBitsForVersions;
+ this.bits = bits;
+ this.name = name;
+ }
+
+ /**
+ * @param bits four bits encoding a QR Code data mode
+ * @return Mode encoded by these bits
+ * @throws IllegalArgumentException if bits do not correspond to a known mode
+ */
+ public static Mode forBits(int bits) {
+ switch (bits) {
+ case 0x0:
+ return TERMINATOR;
+ case 0x1:
+ return NUMERIC;
+ case 0x2:
+ return ALPHANUMERIC;
+ case 0x3:
+ return STRUCTURED_APPEND;
+ case 0x4:
+ return BYTE;
+ case 0x5:
+ return FNC1_FIRST_POSITION;
+ case 0x7:
+ return ECI;
+ case 0x8:
+ return KANJI;
+ case 0x9:
+ return FNC1_SECOND_POSITION;
+ case 0xD:
+ // 0xD is defined in GBT 18284-2000, may not be supported in foreign country
+ return HANZI;
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+
+ /**
+ * @param version version in question
+ * @return number of bits used, in this QR Code symbol {@link Version}, to encode the
+ * count of characters that will follow encoded in this Mode
+ */
+ public int getCharacterCountBits(Version version) {
+ if (characterCountBitsForVersions == null) {
+ throw new IllegalArgumentException("Character count doesn't apply to this mode");
+ }
+ int number = version.getVersionNumber();
+ int offset;
+ if (number <= 9) {
+ offset = 0;
+ } else if (number <= 26) {
+ offset = 1;
+ } else {
+ offset = 2;
+ }
+ return characterCountBitsForVersions[offset];
+ }
+
+ public int getBits() {
+ return bits;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String toString() {
+ return name;
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/decoder/Version.java b/libraries/zxing/src/com/google/zxing/qrcode/decoder/Version.java
new file mode 100644
index 000000000..ba795de42
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/decoder/Version.java
@@ -0,0 +1,586 @@
+/*
+ * Copyright 2007 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.qrcode.decoder;
+
+import com.google.zxing.FormatException;
+import com.google.zxing.common.BitMatrix;
+
+/**
+ * See ISO 18004:2006 Annex D
+ *
+ * @author Sean Owen
+ */
+public final class Version {
+
+ /**
+ * See ISO 18004:2006 Annex D.
+ * Element i represents the raw version bits that specify version i + 7
+ */
+ private static final int[] VERSION_DECODE_INFO = {
+ 0x07C94, 0x085BC, 0x09A99, 0x0A4D3, 0x0BBF6,
+ 0x0C762, 0x0D847, 0x0E60D, 0x0F928, 0x10B78,
+ 0x1145D, 0x12A17, 0x13532, 0x149A6, 0x15683,
+ 0x168C9, 0x177EC, 0x18EC4, 0x191E1, 0x1AFAB,
+ 0x1B08E, 0x1CC1A, 0x1D33F, 0x1ED75, 0x1F250,
+ 0x209D5, 0x216F0, 0x228BA, 0x2379F, 0x24B0B,
+ 0x2542E, 0x26A64, 0x27541, 0x28C69
+ };
+
+ private static final Version[] VERSIONS = buildVersions();
+
+ private final int versionNumber;
+ private final int[] alignmentPatternCenters;
+ private final ECBlocks[] ecBlocks;
+ private final int totalCodewords;
+
+ private Version(int versionNumber,
+ int[] alignmentPatternCenters,
+ ECBlocks ecBlocks1,
+ ECBlocks ecBlocks2,
+ ECBlocks ecBlocks3,
+ ECBlocks ecBlocks4) {
+ this.versionNumber = versionNumber;
+ this.alignmentPatternCenters = alignmentPatternCenters;
+ this.ecBlocks = new ECBlocks[]{ecBlocks1, ecBlocks2, ecBlocks3, ecBlocks4};
+ int total = 0;
+ int ecCodewords = ecBlocks1.getECCodewordsPerBlock();
+ ECB[] ecbArray = ecBlocks1.getECBlocks();
+ for (int i = 0; i < ecbArray.length; i++) {
+ ECB ecBlock = ecbArray[i];
+ total += ecBlock.getCount() * (ecBlock.getDataCodewords() + ecCodewords);
+ }
+ this.totalCodewords = total;
+ }
+
+ public int getVersionNumber() {
+ return versionNumber;
+ }
+
+ public int[] getAlignmentPatternCenters() {
+ return alignmentPatternCenters;
+ }
+
+ public int getTotalCodewords() {
+ return totalCodewords;
+ }
+
+ public int getDimensionForVersion() {
+ return 17 + 4 * versionNumber;
+ }
+
+ public ECBlocks getECBlocksForLevel(ErrorCorrectionLevel ecLevel) {
+ return ecBlocks[ecLevel.ordinal()];
+ }
+
+ /**
+ *
Deduces version information purely from QR Code dimensions.
+ *
+ * @param dimension dimension in modules
+ * @return Version for a QR Code of that dimension
+ * @throws FormatException if dimension is not 1 mod 4
+ */
+ public static Version getProvisionalVersionForDimension(int dimension) throws FormatException {
+ if (dimension % 4 != 1) {
+ throw FormatException.getFormatInstance();
+ }
+ try {
+ return getVersionForNumber((dimension - 17) >> 2);
+ } catch (IllegalArgumentException iae) {
+ throw FormatException.getFormatInstance();
+ }
+ }
+
+ public static Version getVersionForNumber(int versionNumber) {
+ if (versionNumber < 1 || versionNumber > 40) {
+ throw new IllegalArgumentException();
+ }
+ return VERSIONS[versionNumber - 1];
+ }
+
+ static Version decodeVersionInformation(int versionBits) {
+ int bestDifference = Integer.MAX_VALUE;
+ int bestVersion = 0;
+ for (int i = 0; i < VERSION_DECODE_INFO.length; i++) {
+ int targetVersion = VERSION_DECODE_INFO[i];
+ // Do the version info bits match exactly? done.
+ if (targetVersion == versionBits) {
+ return getVersionForNumber(i + 7);
+ }
+ // Otherwise see if this is the closest to a real version info bit string
+ // we have seen so far
+ int bitsDifference = FormatInformation.numBitsDiffering(versionBits, targetVersion);
+ if (bitsDifference < bestDifference) {
+ bestVersion = i + 7;
+ bestDifference = bitsDifference;
+ }
+ }
+ // We can tolerate up to 3 bits of error since no two version info codewords will
+ // differ in less than 8 bits.
+ if (bestDifference <= 3) {
+ return getVersionForNumber(bestVersion);
+ }
+ // If we didn't find a close enough match, fail
+ return null;
+ }
+
+ /**
+ * See ISO 18004:2006 Annex E
+ */
+ BitMatrix buildFunctionPattern() {
+ int dimension = getDimensionForVersion();
+ BitMatrix bitMatrix = new BitMatrix(dimension);
+
+ // Top left finder pattern + separator + format
+ bitMatrix.setRegion(0, 0, 9, 9);
+ // Top right finder pattern + separator + format
+ bitMatrix.setRegion(dimension - 8, 0, 8, 9);
+ // Bottom left finder pattern + separator + format
+ bitMatrix.setRegion(0, dimension - 8, 9, 8);
+
+ // Alignment patterns
+ int max = alignmentPatternCenters.length;
+ for (int x = 0; x < max; x++) {
+ int i = alignmentPatternCenters[x] - 2;
+ for (int y = 0; y < max; y++) {
+ if ((x == 0 && (y == 0 || y == max - 1)) || (x == max - 1 && y == 0)) {
+ // No alignment patterns near the three finder paterns
+ continue;
+ }
+ bitMatrix.setRegion(alignmentPatternCenters[y] - 2, i, 5, 5);
+ }
+ }
+
+ // Vertical timing pattern
+ bitMatrix.setRegion(6, 9, 1, dimension - 17);
+ // Horizontal timing pattern
+ bitMatrix.setRegion(9, 6, dimension - 17, 1);
+
+ if (versionNumber > 6) {
+ // Version info, top right
+ bitMatrix.setRegion(dimension - 11, 0, 3, 6);
+ // Version info, bottom left
+ bitMatrix.setRegion(0, dimension - 11, 6, 3);
+ }
+
+ return bitMatrix;
+ }
+
+ /**
+ *
Encapsulates a set of error-correction blocks in one symbol version. Most versions will
+ * use blocks of differing sizes within one version, so, this encapsulates the parameters for
+ * each set of blocks. It also holds the number of error-correction codewords per block since it
+ * will be the same across all blocks within one version.
+ */
+ public static final class ECBlocks {
+ private final int ecCodewordsPerBlock;
+ private final ECB[] ecBlocks;
+
+ ECBlocks(int ecCodewordsPerBlock, ECB ecBlocks) {
+ this.ecCodewordsPerBlock = ecCodewordsPerBlock;
+ this.ecBlocks = new ECB[]{ecBlocks};
+ }
+
+ ECBlocks(int ecCodewordsPerBlock, ECB ecBlocks1, ECB ecBlocks2) {
+ this.ecCodewordsPerBlock = ecCodewordsPerBlock;
+ this.ecBlocks = new ECB[]{ecBlocks1, ecBlocks2};
+ }
+
+ public int getECCodewordsPerBlock() {
+ return ecCodewordsPerBlock;
+ }
+
+ public int getNumBlocks() {
+ int total = 0;
+ for (int i = 0; i < ecBlocks.length; i++) {
+ total += ecBlocks[i].getCount();
+ }
+ return total;
+ }
+
+ public int getTotalECCodewords() {
+ return ecCodewordsPerBlock * getNumBlocks();
+ }
+
+ public ECB[] getECBlocks() {
+ return ecBlocks;
+ }
+ }
+
+ /**
+ *
Encapsualtes the parameters for one error-correction block in one symbol version.
+ * This includes the number of data codewords, and the number of times a block with these
+ * parameters is used consecutively in the QR code version's format.
+ */
+ public static final class ECB {
+ private final int count;
+ private final int dataCodewords;
+
+ ECB(int count, int dataCodewords) {
+ this.count = count;
+ this.dataCodewords = dataCodewords;
+ }
+
+ public int getCount() {
+ return count;
+ }
+
+ public int getDataCodewords() {
+ return dataCodewords;
+ }
+ }
+
+ public String toString() {
+ return String.valueOf(versionNumber);
+ }
+
+ /**
+ * See ISO 18004:2006 6.5.1 Table 9
+ */
+ private static Version[] buildVersions() {
+ return new Version[]{
+ new Version(1, new int[]{},
+ new ECBlocks(7, new ECB(1, 19)),
+ new ECBlocks(10, new ECB(1, 16)),
+ new ECBlocks(13, new ECB(1, 13)),
+ new ECBlocks(17, new ECB(1, 9))),
+ new Version(2, new int[]{6, 18},
+ new ECBlocks(10, new ECB(1, 34)),
+ new ECBlocks(16, new ECB(1, 28)),
+ new ECBlocks(22, new ECB(1, 22)),
+ new ECBlocks(28, new ECB(1, 16))),
+ new Version(3, new int[]{6, 22},
+ new ECBlocks(15, new ECB(1, 55)),
+ new ECBlocks(26, new ECB(1, 44)),
+ new ECBlocks(18, new ECB(2, 17)),
+ new ECBlocks(22, new ECB(2, 13))),
+ new Version(4, new int[]{6, 26},
+ new ECBlocks(20, new ECB(1, 80)),
+ new ECBlocks(18, new ECB(2, 32)),
+ new ECBlocks(26, new ECB(2, 24)),
+ new ECBlocks(16, new ECB(4, 9))),
+ new Version(5, new int[]{6, 30},
+ new ECBlocks(26, new ECB(1, 108)),
+ new ECBlocks(24, new ECB(2, 43)),
+ new ECBlocks(18, new ECB(2, 15),
+ new ECB(2, 16)),
+ new ECBlocks(22, new ECB(2, 11),
+ new ECB(2, 12))),
+ new Version(6, new int[]{6, 34},
+ new ECBlocks(18, new ECB(2, 68)),
+ new ECBlocks(16, new ECB(4, 27)),
+ new ECBlocks(24, new ECB(4, 19)),
+ new ECBlocks(28, new ECB(4, 15))),
+ new Version(7, new int[]{6, 22, 38},
+ new ECBlocks(20, new ECB(2, 78)),
+ new ECBlocks(18, new ECB(4, 31)),
+ new ECBlocks(18, new ECB(2, 14),
+ new ECB(4, 15)),
+ new ECBlocks(26, new ECB(4, 13),
+ new ECB(1, 14))),
+ new Version(8, new int[]{6, 24, 42},
+ new ECBlocks(24, new ECB(2, 97)),
+ new ECBlocks(22, new ECB(2, 38),
+ new ECB(2, 39)),
+ new ECBlocks(22, new ECB(4, 18),
+ new ECB(2, 19)),
+ new ECBlocks(26, new ECB(4, 14),
+ new ECB(2, 15))),
+ new Version(9, new int[]{6, 26, 46},
+ new ECBlocks(30, new ECB(2, 116)),
+ new ECBlocks(22, new ECB(3, 36),
+ new ECB(2, 37)),
+ new ECBlocks(20, new ECB(4, 16),
+ new ECB(4, 17)),
+ new ECBlocks(24, new ECB(4, 12),
+ new ECB(4, 13))),
+ new Version(10, new int[]{6, 28, 50},
+ new ECBlocks(18, new ECB(2, 68),
+ new ECB(2, 69)),
+ new ECBlocks(26, new ECB(4, 43),
+ new ECB(1, 44)),
+ new ECBlocks(24, new ECB(6, 19),
+ new ECB(2, 20)),
+ new ECBlocks(28, new ECB(6, 15),
+ new ECB(2, 16))),
+ new Version(11, new int[]{6, 30, 54},
+ new ECBlocks(20, new ECB(4, 81)),
+ new ECBlocks(30, new ECB(1, 50),
+ new ECB(4, 51)),
+ new ECBlocks(28, new ECB(4, 22),
+ new ECB(4, 23)),
+ new ECBlocks(24, new ECB(3, 12),
+ new ECB(8, 13))),
+ new Version(12, new int[]{6, 32, 58},
+ new ECBlocks(24, new ECB(2, 92),
+ new ECB(2, 93)),
+ new ECBlocks(22, new ECB(6, 36),
+ new ECB(2, 37)),
+ new ECBlocks(26, new ECB(4, 20),
+ new ECB(6, 21)),
+ new ECBlocks(28, new ECB(7, 14),
+ new ECB(4, 15))),
+ new Version(13, new int[]{6, 34, 62},
+ new ECBlocks(26, new ECB(4, 107)),
+ new ECBlocks(22, new ECB(8, 37),
+ new ECB(1, 38)),
+ new ECBlocks(24, new ECB(8, 20),
+ new ECB(4, 21)),
+ new ECBlocks(22, new ECB(12, 11),
+ new ECB(4, 12))),
+ new Version(14, new int[]{6, 26, 46, 66},
+ new ECBlocks(30, new ECB(3, 115),
+ new ECB(1, 116)),
+ new ECBlocks(24, new ECB(4, 40),
+ new ECB(5, 41)),
+ new ECBlocks(20, new ECB(11, 16),
+ new ECB(5, 17)),
+ new ECBlocks(24, new ECB(11, 12),
+ new ECB(5, 13))),
+ new Version(15, new int[]{6, 26, 48, 70},
+ new ECBlocks(22, new ECB(5, 87),
+ new ECB(1, 88)),
+ new ECBlocks(24, new ECB(5, 41),
+ new ECB(5, 42)),
+ new ECBlocks(30, new ECB(5, 24),
+ new ECB(7, 25)),
+ new ECBlocks(24, new ECB(11, 12),
+ new ECB(7, 13))),
+ new Version(16, new int[]{6, 26, 50, 74},
+ new ECBlocks(24, new ECB(5, 98),
+ new ECB(1, 99)),
+ new ECBlocks(28, new ECB(7, 45),
+ new ECB(3, 46)),
+ new ECBlocks(24, new ECB(15, 19),
+ new ECB(2, 20)),
+ new ECBlocks(30, new ECB(3, 15),
+ new ECB(13, 16))),
+ new Version(17, new int[]{6, 30, 54, 78},
+ new ECBlocks(28, new ECB(1, 107),
+ new ECB(5, 108)),
+ new ECBlocks(28, new ECB(10, 46),
+ new ECB(1, 47)),
+ new ECBlocks(28, new ECB(1, 22),
+ new ECB(15, 23)),
+ new ECBlocks(28, new ECB(2, 14),
+ new ECB(17, 15))),
+ new Version(18, new int[]{6, 30, 56, 82},
+ new ECBlocks(30, new ECB(5, 120),
+ new ECB(1, 121)),
+ new ECBlocks(26, new ECB(9, 43),
+ new ECB(4, 44)),
+ new ECBlocks(28, new ECB(17, 22),
+ new ECB(1, 23)),
+ new ECBlocks(28, new ECB(2, 14),
+ new ECB(19, 15))),
+ new Version(19, new int[]{6, 30, 58, 86},
+ new ECBlocks(28, new ECB(3, 113),
+ new ECB(4, 114)),
+ new ECBlocks(26, new ECB(3, 44),
+ new ECB(11, 45)),
+ new ECBlocks(26, new ECB(17, 21),
+ new ECB(4, 22)),
+ new ECBlocks(26, new ECB(9, 13),
+ new ECB(16, 14))),
+ new Version(20, new int[]{6, 34, 62, 90},
+ new ECBlocks(28, new ECB(3, 107),
+ new ECB(5, 108)),
+ new ECBlocks(26, new ECB(3, 41),
+ new ECB(13, 42)),
+ new ECBlocks(30, new ECB(15, 24),
+ new ECB(5, 25)),
+ new ECBlocks(28, new ECB(15, 15),
+ new ECB(10, 16))),
+ new Version(21, new int[]{6, 28, 50, 72, 94},
+ new ECBlocks(28, new ECB(4, 116),
+ new ECB(4, 117)),
+ new ECBlocks(26, new ECB(17, 42)),
+ new ECBlocks(28, new ECB(17, 22),
+ new ECB(6, 23)),
+ new ECBlocks(30, new ECB(19, 16),
+ new ECB(6, 17))),
+ new Version(22, new int[]{6, 26, 50, 74, 98},
+ new ECBlocks(28, new ECB(2, 111),
+ new ECB(7, 112)),
+ new ECBlocks(28, new ECB(17, 46)),
+ new ECBlocks(30, new ECB(7, 24),
+ new ECB(16, 25)),
+ new ECBlocks(24, new ECB(34, 13))),
+ new Version(23, new int[]{6, 30, 54, 78, 102},
+ new ECBlocks(30, new ECB(4, 121),
+ new ECB(5, 122)),
+ new ECBlocks(28, new ECB(4, 47),
+ new ECB(14, 48)),
+ new ECBlocks(30, new ECB(11, 24),
+ new ECB(14, 25)),
+ new ECBlocks(30, new ECB(16, 15),
+ new ECB(14, 16))),
+ new Version(24, new int[]{6, 28, 54, 80, 106},
+ new ECBlocks(30, new ECB(6, 117),
+ new ECB(4, 118)),
+ new ECBlocks(28, new ECB(6, 45),
+ new ECB(14, 46)),
+ new ECBlocks(30, new ECB(11, 24),
+ new ECB(16, 25)),
+ new ECBlocks(30, new ECB(30, 16),
+ new ECB(2, 17))),
+ new Version(25, new int[]{6, 32, 58, 84, 110},
+ new ECBlocks(26, new ECB(8, 106),
+ new ECB(4, 107)),
+ new ECBlocks(28, new ECB(8, 47),
+ new ECB(13, 48)),
+ new ECBlocks(30, new ECB(7, 24),
+ new ECB(22, 25)),
+ new ECBlocks(30, new ECB(22, 15),
+ new ECB(13, 16))),
+ new Version(26, new int[]{6, 30, 58, 86, 114},
+ new ECBlocks(28, new ECB(10, 114),
+ new ECB(2, 115)),
+ new ECBlocks(28, new ECB(19, 46),
+ new ECB(4, 47)),
+ new ECBlocks(28, new ECB(28, 22),
+ new ECB(6, 23)),
+ new ECBlocks(30, new ECB(33, 16),
+ new ECB(4, 17))),
+ new Version(27, new int[]{6, 34, 62, 90, 118},
+ new ECBlocks(30, new ECB(8, 122),
+ new ECB(4, 123)),
+ new ECBlocks(28, new ECB(22, 45),
+ new ECB(3, 46)),
+ new ECBlocks(30, new ECB(8, 23),
+ new ECB(26, 24)),
+ new ECBlocks(30, new ECB(12, 15),
+ new ECB(28, 16))),
+ new Version(28, new int[]{6, 26, 50, 74, 98, 122},
+ new ECBlocks(30, new ECB(3, 117),
+ new ECB(10, 118)),
+ new ECBlocks(28, new ECB(3, 45),
+ new ECB(23, 46)),
+ new ECBlocks(30, new ECB(4, 24),
+ new ECB(31, 25)),
+ new ECBlocks(30, new ECB(11, 15),
+ new ECB(31, 16))),
+ new Version(29, new int[]{6, 30, 54, 78, 102, 126},
+ new ECBlocks(30, new ECB(7, 116),
+ new ECB(7, 117)),
+ new ECBlocks(28, new ECB(21, 45),
+ new ECB(7, 46)),
+ new ECBlocks(30, new ECB(1, 23),
+ new ECB(37, 24)),
+ new ECBlocks(30, new ECB(19, 15),
+ new ECB(26, 16))),
+ new Version(30, new int[]{6, 26, 52, 78, 104, 130},
+ new ECBlocks(30, new ECB(5, 115),
+ new ECB(10, 116)),
+ new ECBlocks(28, new ECB(19, 47),
+ new ECB(10, 48)),
+ new ECBlocks(30, new ECB(15, 24),
+ new ECB(25, 25)),
+ new ECBlocks(30, new ECB(23, 15),
+ new ECB(25, 16))),
+ new Version(31, new int[]{6, 30, 56, 82, 108, 134},
+ new ECBlocks(30, new ECB(13, 115),
+ new ECB(3, 116)),
+ new ECBlocks(28, new ECB(2, 46),
+ new ECB(29, 47)),
+ new ECBlocks(30, new ECB(42, 24),
+ new ECB(1, 25)),
+ new ECBlocks(30, new ECB(23, 15),
+ new ECB(28, 16))),
+ new Version(32, new int[]{6, 34, 60, 86, 112, 138},
+ new ECBlocks(30, new ECB(17, 115)),
+ new ECBlocks(28, new ECB(10, 46),
+ new ECB(23, 47)),
+ new ECBlocks(30, new ECB(10, 24),
+ new ECB(35, 25)),
+ new ECBlocks(30, new ECB(19, 15),
+ new ECB(35, 16))),
+ new Version(33, new int[]{6, 30, 58, 86, 114, 142},
+ new ECBlocks(30, new ECB(17, 115),
+ new ECB(1, 116)),
+ new ECBlocks(28, new ECB(14, 46),
+ new ECB(21, 47)),
+ new ECBlocks(30, new ECB(29, 24),
+ new ECB(19, 25)),
+ new ECBlocks(30, new ECB(11, 15),
+ new ECB(46, 16))),
+ new Version(34, new int[]{6, 34, 62, 90, 118, 146},
+ new ECBlocks(30, new ECB(13, 115),
+ new ECB(6, 116)),
+ new ECBlocks(28, new ECB(14, 46),
+ new ECB(23, 47)),
+ new ECBlocks(30, new ECB(44, 24),
+ new ECB(7, 25)),
+ new ECBlocks(30, new ECB(59, 16),
+ new ECB(1, 17))),
+ new Version(35, new int[]{6, 30, 54, 78, 102, 126, 150},
+ new ECBlocks(30, new ECB(12, 121),
+ new ECB(7, 122)),
+ new ECBlocks(28, new ECB(12, 47),
+ new ECB(26, 48)),
+ new ECBlocks(30, new ECB(39, 24),
+ new ECB(14, 25)),
+ new ECBlocks(30, new ECB(22, 15),
+ new ECB(41, 16))),
+ new Version(36, new int[]{6, 24, 50, 76, 102, 128, 154},
+ new ECBlocks(30, new ECB(6, 121),
+ new ECB(14, 122)),
+ new ECBlocks(28, new ECB(6, 47),
+ new ECB(34, 48)),
+ new ECBlocks(30, new ECB(46, 24),
+ new ECB(10, 25)),
+ new ECBlocks(30, new ECB(2, 15),
+ new ECB(64, 16))),
+ new Version(37, new int[]{6, 28, 54, 80, 106, 132, 158},
+ new ECBlocks(30, new ECB(17, 122),
+ new ECB(4, 123)),
+ new ECBlocks(28, new ECB(29, 46),
+ new ECB(14, 47)),
+ new ECBlocks(30, new ECB(49, 24),
+ new ECB(10, 25)),
+ new ECBlocks(30, new ECB(24, 15),
+ new ECB(46, 16))),
+ new Version(38, new int[]{6, 32, 58, 84, 110, 136, 162},
+ new ECBlocks(30, new ECB(4, 122),
+ new ECB(18, 123)),
+ new ECBlocks(28, new ECB(13, 46),
+ new ECB(32, 47)),
+ new ECBlocks(30, new ECB(48, 24),
+ new ECB(14, 25)),
+ new ECBlocks(30, new ECB(42, 15),
+ new ECB(32, 16))),
+ new Version(39, new int[]{6, 26, 54, 82, 110, 138, 166},
+ new ECBlocks(30, new ECB(20, 117),
+ new ECB(4, 118)),
+ new ECBlocks(28, new ECB(40, 47),
+ new ECB(7, 48)),
+ new ECBlocks(30, new ECB(43, 24),
+ new ECB(22, 25)),
+ new ECBlocks(30, new ECB(10, 15),
+ new ECB(67, 16))),
+ new Version(40, new int[]{6, 30, 58, 86, 114, 142, 170},
+ new ECBlocks(30, new ECB(19, 118),
+ new ECB(6, 119)),
+ new ECBlocks(28, new ECB(18, 47),
+ new ECB(31, 48)),
+ new ECBlocks(30, new ECB(34, 24),
+ new ECB(34, 25)),
+ new ECBlocks(30, new ECB(20, 15),
+ new ECB(61, 16)))
+ };
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/detector/AlignmentPattern.java b/libraries/zxing/src/com/google/zxing/qrcode/detector/AlignmentPattern.java
new file mode 100644
index 000000000..6fc1a2c88
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/detector/AlignmentPattern.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2007 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.qrcode.detector;
+
+import com.google.zxing.ResultPoint;
+
+/**
+ *
Encapsulates an alignment pattern, which are the smaller square patterns found in
+ * all but the simplest QR Codes.
Determines if this alignment pattern "about equals" an alignment pattern at the stated
+ * position and size -- meaning, it is at nearly the same center with nearly the same size.
+ */
+ boolean aboutEquals(float moduleSize, float i, float j) {
+ if (Math.abs(i - getY()) <= moduleSize && Math.abs(j - getX()) <= moduleSize) {
+ float moduleSizeDiff = Math.abs(moduleSize - estimatedModuleSize);
+ return moduleSizeDiff <= 1.0f || moduleSizeDiff / estimatedModuleSize <= 1.0f;
+ }
+ return false;
+ }
+
+}
\ No newline at end of file
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/detector/AlignmentPatternFinder.java b/libraries/zxing/src/com/google/zxing/qrcode/detector/AlignmentPatternFinder.java
new file mode 100644
index 000000000..3aadf284f
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/detector/AlignmentPatternFinder.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright 2007 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.qrcode.detector;
+
+import com.google.zxing.NotFoundException;
+import com.google.zxing.ResultPoint;
+import com.google.zxing.ResultPointCallback;
+import com.google.zxing.common.BitMatrix;
+
+import java.util.Vector;
+
+/**
+ *
This class attempts to find alignment patterns in a QR Code. Alignment patterns look like finder
+ * patterns but are smaller and appear at regular intervals throughout the image.
+ *
+ *
At the moment this only looks for the bottom-right alignment pattern.
+ *
+ *
This is mostly a simplified copy of {@link FinderPatternFinder}. It is copied,
+ * pasted and stripped down here for maximum performance but does unfortunately duplicate
+ * some code.
+ *
+ *
This class is thread-safe but not reentrant. Each thread must allocate its own object.
+ *
+ * @author Sean Owen
+ */
+final class AlignmentPatternFinder {
+
+ private final BitMatrix image;
+ private final Vector possibleCenters;
+ private final int startX;
+ private final int startY;
+ private final int width;
+ private final int height;
+ private final float moduleSize;
+ private final int[] crossCheckStateCount;
+ private final ResultPointCallback resultPointCallback;
+
+ /**
+ *
Creates a finder that will look in a portion of the whole image.
+ *
+ * @param image image to search
+ * @param startX left column from which to start searching
+ * @param startY top row from which to start searching
+ * @param width width of region to search
+ * @param height height of region to search
+ * @param moduleSize estimated module size so far
+ */
+ AlignmentPatternFinder(BitMatrix image,
+ int startX,
+ int startY,
+ int width,
+ int height,
+ float moduleSize,
+ ResultPointCallback resultPointCallback) {
+ this.image = image;
+ this.possibleCenters = new Vector(5);
+ this.startX = startX;
+ this.startY = startY;
+ this.width = width;
+ this.height = height;
+ this.moduleSize = moduleSize;
+ this.crossCheckStateCount = new int[3];
+ this.resultPointCallback = resultPointCallback;
+ }
+
+ /**
+ *
This method attempts to find the bottom-right alignment pattern in the image. It is a bit messy since
+ * it's pretty performance-critical and so is written to be fast foremost.
+ *
+ * @return {@link AlignmentPattern} if found
+ * @throws NotFoundException if not found
+ */
+ AlignmentPattern find() throws NotFoundException {
+ int startX = this.startX;
+ int height = this.height;
+ int maxJ = startX + width;
+ int middleI = startY + (height >> 1);
+ // We are looking for black/white/black modules in 1:1:1 ratio;
+ // this tracks the number of black/white/black modules seen so far
+ int[] stateCount = new int[3];
+ for (int iGen = 0; iGen < height; iGen++) {
+ // Search from middle outwards
+ int i = middleI + ((iGen & 0x01) == 0 ? (iGen + 1) >> 1 : -((iGen + 1) >> 1));
+ stateCount[0] = 0;
+ stateCount[1] = 0;
+ stateCount[2] = 0;
+ int j = startX;
+ // Burn off leading white pixels before anything else; if we start in the middle of
+ // a white run, it doesn't make sense to count its length, since we don't know if the
+ // white run continued to the left of the start point
+ while (j < maxJ && !image.get(j, i)) {
+ j++;
+ }
+ int currentState = 0;
+ while (j < maxJ) {
+ if (image.get(j, i)) {
+ // Black pixel
+ if (currentState == 1) { // Counting black pixels
+ stateCount[currentState]++;
+ } else { // Counting white pixels
+ if (currentState == 2) { // A winner?
+ if (foundPatternCross(stateCount)) { // Yes
+ AlignmentPattern confirmed = handlePossibleCenter(stateCount, i, j);
+ if (confirmed != null) {
+ return confirmed;
+ }
+ }
+ stateCount[0] = stateCount[2];
+ stateCount[1] = 1;
+ stateCount[2] = 0;
+ currentState = 1;
+ } else {
+ stateCount[++currentState]++;
+ }
+ }
+ } else { // White pixel
+ if (currentState == 1) { // Counting black pixels
+ currentState++;
+ }
+ stateCount[currentState]++;
+ }
+ j++;
+ }
+ if (foundPatternCross(stateCount)) {
+ AlignmentPattern confirmed = handlePossibleCenter(stateCount, i, maxJ);
+ if (confirmed != null) {
+ return confirmed;
+ }
+ }
+
+ }
+
+ // Hmm, nothing we saw was observed and confirmed twice. If we had
+ // any guess at all, return it.
+ if (!possibleCenters.isEmpty()) {
+ return (AlignmentPattern) possibleCenters.elementAt(0);
+ }
+
+ throw NotFoundException.getNotFoundInstance();
+ }
+
+ /**
+ * Given a count of black/white/black pixels just seen and an end position,
+ * figures the location of the center of this black/white/black run.
+ */
+ private static float centerFromEnd(int[] stateCount, int end) {
+ return (float) (end - stateCount[2]) - stateCount[1] / 2.0f;
+ }
+
+ /**
+ * @param stateCount count of black/white/black pixels just read
+ * @return true iff the proportions of the counts is close enough to the 1/1/1 ratios
+ * used by alignment patterns to be considered a match
+ */
+ private boolean foundPatternCross(int[] stateCount) {
+ float moduleSize = this.moduleSize;
+ float maxVariance = moduleSize / 2.0f;
+ for (int i = 0; i < 3; i++) {
+ if (Math.abs(moduleSize - stateCount[i]) >= maxVariance) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ *
After a horizontal scan finds a potential alignment pattern, this method
+ * "cross-checks" by scanning down vertically through the center of the possible
+ * alignment pattern to see if the same proportion is detected.
+ *
+ * @param startI row where an alignment pattern was detected
+ * @param centerJ center of the section that appears to cross an alignment pattern
+ * @param maxCount maximum reasonable number of modules that should be
+ * observed in any reading state, based on the results of the horizontal scan
+ * @return vertical center of alignment pattern, or {@link Float#NaN} if not found
+ */
+ private float crossCheckVertical(int startI, int centerJ, int maxCount,
+ int originalStateCountTotal) {
+ BitMatrix image = this.image;
+
+ int maxI = image.getHeight();
+ int[] stateCount = crossCheckStateCount;
+ stateCount[0] = 0;
+ stateCount[1] = 0;
+ stateCount[2] = 0;
+
+ // Start counting up from center
+ int i = startI;
+ while (i >= 0 && image.get(centerJ, i) && stateCount[1] <= maxCount) {
+ stateCount[1]++;
+ i--;
+ }
+ // If already too many modules in this state or ran off the edge:
+ if (i < 0 || stateCount[1] > maxCount) {
+ return Float.NaN;
+ }
+ while (i >= 0 && !image.get(centerJ, i) && stateCount[0] <= maxCount) {
+ stateCount[0]++;
+ i--;
+ }
+ if (stateCount[0] > maxCount) {
+ return Float.NaN;
+ }
+
+ // Now also count down from center
+ i = startI + 1;
+ while (i < maxI && image.get(centerJ, i) && stateCount[1] <= maxCount) {
+ stateCount[1]++;
+ i++;
+ }
+ if (i == maxI || stateCount[1] > maxCount) {
+ return Float.NaN;
+ }
+ while (i < maxI && !image.get(centerJ, i) && stateCount[2] <= maxCount) {
+ stateCount[2]++;
+ i++;
+ }
+ if (stateCount[2] > maxCount) {
+ return Float.NaN;
+ }
+
+ int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2];
+ if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal) {
+ return Float.NaN;
+ }
+
+ return foundPatternCross(stateCount) ? centerFromEnd(stateCount, i) : Float.NaN;
+ }
+
+ /**
+ *
This is called when a horizontal scan finds a possible alignment pattern. It will
+ * cross check with a vertical scan, and if successful, will see if this pattern had been
+ * found on a previous horizontal scan. If so, we consider it confirmed and conclude we have
+ * found the alignment pattern.
+ *
+ * @param stateCount reading state module counts from horizontal scan
+ * @param i row where alignment pattern may be found
+ * @param j end of possible alignment pattern in row
+ * @return {@link AlignmentPattern} if we have found the same pattern twice, or null if not
+ */
+ private AlignmentPattern handlePossibleCenter(int[] stateCount, int i, int j) {
+ int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2];
+ float centerJ = centerFromEnd(stateCount, j);
+ float centerI = crossCheckVertical(i, (int) centerJ, 2 * stateCount[1], stateCountTotal);
+ if (!Float.isNaN(centerI)) {
+ float estimatedModuleSize = (float) (stateCount[0] + stateCount[1] + stateCount[2]) / 3.0f;
+ int max = possibleCenters.size();
+ for (int index = 0; index < max; index++) {
+ AlignmentPattern center = (AlignmentPattern) possibleCenters.elementAt(index);
+ // Look for about the same center and module size:
+ if (center.aboutEquals(estimatedModuleSize, centerI, centerJ)) {
+ return new AlignmentPattern(centerJ, centerI, estimatedModuleSize);
+ }
+ }
+ // Hadn't found this before; save it
+ ResultPoint point = new AlignmentPattern(centerJ, centerI, estimatedModuleSize);
+ possibleCenters.addElement(point);
+ if (resultPointCallback != null) {
+ resultPointCallback.foundPossibleResultPoint(point);
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/detector/Detector.java b/libraries/zxing/src/com/google/zxing/qrcode/detector/Detector.java
new file mode 100644
index 000000000..724d39d59
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/detector/Detector.java
@@ -0,0 +1,406 @@
+/*
+ * Copyright 2007 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.qrcode.detector;
+
+import com.google.zxing.DecodeHintType;
+import com.google.zxing.FormatException;
+import com.google.zxing.NotFoundException;
+import com.google.zxing.ResultPoint;
+import com.google.zxing.ResultPointCallback;
+import com.google.zxing.common.BitMatrix;
+import com.google.zxing.common.DetectorResult;
+import com.google.zxing.common.GridSampler;
+import com.google.zxing.common.PerspectiveTransform;
+import com.google.zxing.qrcode.decoder.Version;
+
+import java.util.Hashtable;
+
+/**
+ *
Encapsulates logic that can detect a QR Code in an image, even if the QR Code
+ * is rotated or skewed, or partially obscured.
+ *
+ * @return {@link DetectorResult} encapsulating results of detecting a QR Code
+ * @throws NotFoundException if no QR Code can be found
+ */
+ public DetectorResult detect() throws NotFoundException, FormatException {
+ return detect(null);
+ }
+
+ /**
+ *
Detects a QR Code in an image, simply.
+ *
+ * @param hints optional hints to detector
+ * @return {@link NotFoundException} encapsulating results of detecting a QR Code
+ * @throws NotFoundException if QR Code cannot be found
+ * @throws FormatException if a QR Code cannot be decoded
+ */
+ public DetectorResult detect(Hashtable hints) throws NotFoundException, FormatException {
+
+ resultPointCallback = hints == null ? null :
+ (ResultPointCallback) hints.get(DecodeHintType.NEED_RESULT_POINT_CALLBACK);
+
+ FinderPatternFinder finder = new FinderPatternFinder(image, resultPointCallback);
+ FinderPatternInfo info = finder.find(hints);
+
+ return processFinderPatternInfo(info);
+ }
+
+ protected DetectorResult processFinderPatternInfo(FinderPatternInfo info)
+ throws NotFoundException, FormatException {
+
+ FinderPattern topLeft = info.getTopLeft();
+ FinderPattern topRight = info.getTopRight();
+ FinderPattern bottomLeft = info.getBottomLeft();
+
+ float moduleSize = calculateModuleSize(topLeft, topRight, bottomLeft);
+ if (moduleSize < 1.0f) {
+ throw NotFoundException.getNotFoundInstance();
+ }
+ int dimension = computeDimension(topLeft, topRight, bottomLeft, moduleSize);
+ Version provisionalVersion = Version.getProvisionalVersionForDimension(dimension);
+ int modulesBetweenFPCenters = provisionalVersion.getDimensionForVersion() - 7;
+
+ AlignmentPattern alignmentPattern = null;
+ // Anything above version 1 has an alignment pattern
+ if (provisionalVersion.getAlignmentPatternCenters().length > 0) {
+
+ // Guess where a "bottom right" finder pattern would have been
+ float bottomRightX = topRight.getX() - topLeft.getX() + bottomLeft.getX();
+ float bottomRightY = topRight.getY() - topLeft.getY() + bottomLeft.getY();
+
+ // Estimate that alignment pattern is closer by 3 modules
+ // from "bottom right" to known top left location
+ float correctionToTopLeft = 1.0f - 3.0f / (float) modulesBetweenFPCenters;
+ int estAlignmentX = (int) (topLeft.getX() + correctionToTopLeft * (bottomRightX - topLeft.getX()));
+ int estAlignmentY = (int) (topLeft.getY() + correctionToTopLeft * (bottomRightY - topLeft.getY()));
+
+ // Kind of arbitrary -- expand search radius before giving up
+ for (int i = 4; i <= 16; i <<= 1) {
+ try {
+ alignmentPattern = findAlignmentInRegion(moduleSize,
+ estAlignmentX,
+ estAlignmentY,
+ (float) i);
+ break;
+ } catch (NotFoundException re) {
+ // try next round
+ }
+ }
+ // If we didn't find alignment pattern... well try anyway without it
+ }
+
+ PerspectiveTransform transform =
+ createTransform(topLeft, topRight, bottomLeft, alignmentPattern, dimension);
+
+ BitMatrix bits = sampleGrid(image, transform, dimension);
+
+ ResultPoint[] points;
+ if (alignmentPattern == null) {
+ points = new ResultPoint[]{bottomLeft, topLeft, topRight};
+ } else {
+ points = new ResultPoint[]{bottomLeft, topLeft, topRight, alignmentPattern};
+ }
+ return new DetectorResult(bits, points);
+ }
+
+ public static PerspectiveTransform createTransform(ResultPoint topLeft,
+ ResultPoint topRight,
+ ResultPoint bottomLeft,
+ ResultPoint alignmentPattern,
+ int dimension) {
+ float dimMinusThree = (float) dimension - 3.5f;
+ float bottomRightX;
+ float bottomRightY;
+ float sourceBottomRightX;
+ float sourceBottomRightY;
+ if (alignmentPattern != null) {
+ bottomRightX = alignmentPattern.getX();
+ bottomRightY = alignmentPattern.getY();
+ sourceBottomRightX = sourceBottomRightY = dimMinusThree - 3.0f;
+ } else {
+ // Don't have an alignment pattern, just make up the bottom-right point
+ bottomRightX = (topRight.getX() - topLeft.getX()) + bottomLeft.getX();
+ bottomRightY = (topRight.getY() - topLeft.getY()) + bottomLeft.getY();
+ sourceBottomRightX = sourceBottomRightY = dimMinusThree;
+ }
+
+ return PerspectiveTransform.quadrilateralToQuadrilateral(
+ 3.5f,
+ 3.5f,
+ dimMinusThree,
+ 3.5f,
+ sourceBottomRightX,
+ sourceBottomRightY,
+ 3.5f,
+ dimMinusThree,
+ topLeft.getX(),
+ topLeft.getY(),
+ topRight.getX(),
+ topRight.getY(),
+ bottomRightX,
+ bottomRightY,
+ bottomLeft.getX(),
+ bottomLeft.getY());
+ }
+
+ private static BitMatrix sampleGrid(BitMatrix image,
+ PerspectiveTransform transform,
+ int dimension) throws NotFoundException {
+
+ GridSampler sampler = GridSampler.getInstance();
+ return sampler.sampleGrid(image, dimension, dimension, transform);
+ }
+
+ /**
+ *
Computes the dimension (number of modules on a size) of the QR Code based on the position
+ * of the finder patterns and estimated module size.
Estimates module size based on two finder patterns -- it uses
+ * {@link #sizeOfBlackWhiteBlackRunBothWays(int, int, int, int)} to figure the
+ * width of each, measuring along the axis between their centers.
+ */
+ private float calculateModuleSizeOneWay(ResultPoint pattern, ResultPoint otherPattern) {
+ float moduleSizeEst1 = sizeOfBlackWhiteBlackRunBothWays((int) pattern.getX(),
+ (int) pattern.getY(),
+ (int) otherPattern.getX(),
+ (int) otherPattern.getY());
+ float moduleSizeEst2 = sizeOfBlackWhiteBlackRunBothWays((int) otherPattern.getX(),
+ (int) otherPattern.getY(),
+ (int) pattern.getX(),
+ (int) pattern.getY());
+ if (Float.isNaN(moduleSizeEst1)) {
+ return moduleSizeEst2 / 7.0f;
+ }
+ if (Float.isNaN(moduleSizeEst2)) {
+ return moduleSizeEst1 / 7.0f;
+ }
+ // Average them, and divide by 7 since we've counted the width of 3 black modules,
+ // and 1 white and 1 black module on either side. Ergo, divide sum by 14.
+ return (moduleSizeEst1 + moduleSizeEst2) / 14.0f;
+ }
+
+ /**
+ * See {@link #sizeOfBlackWhiteBlackRun(int, int, int, int)}; computes the total width of
+ * a finder pattern by looking for a black-white-black run from the center in the direction
+ * of another point (another finder pattern center), and in the opposite direction too.
+ */
+ private float sizeOfBlackWhiteBlackRunBothWays(int fromX, int fromY, int toX, int toY) {
+
+ float result = sizeOfBlackWhiteBlackRun(fromX, fromY, toX, toY);
+
+ // Now count other way -- don't run off image though of course
+ float scale = 1.0f;
+ int otherToX = fromX - (toX - fromX);
+ if (otherToX < 0) {
+ scale = (float) fromX / (float) (fromX - otherToX);
+ otherToX = 0;
+ } else if (otherToX > image.getWidth()) {
+ scale = (float) (image.getWidth() - fromX) / (float) (otherToX - fromX);
+ otherToX = image.getWidth();
+ }
+ int otherToY = (int) (fromY - (toY - fromY) * scale);
+
+ scale = 1.0f;
+ if (otherToY < 0) {
+ scale = (float) fromY / (float) (fromY - otherToY);
+ otherToY = 0;
+ } else if (otherToY > image.getHeight()) {
+ scale = (float) (image.getHeight() - fromY) / (float) (otherToY - fromY);
+ otherToY = image.getHeight();
+ }
+ otherToX = (int) (fromX + (otherToX - fromX) * scale);
+
+ result += sizeOfBlackWhiteBlackRun(fromX, fromY, otherToX, otherToY);
+ return result;
+ }
+
+ /**
+ *
This method traces a line from a point in the image, in the direction towards another point.
+ * It begins in a black region, and keeps going until it finds white, then black, then white again.
+ * It reports the distance from the start to this point.
+ *
+ *
This is used when figuring out how wide a finder pattern is, when the finder pattern
+ * may be skewed or rotated.
+ */
+ private float sizeOfBlackWhiteBlackRun(int fromX, int fromY, int toX, int toY) {
+ // Mild variant of Bresenham's algorithm;
+ // see http://en.wikipedia.org/wiki/Bresenham's_line_algorithm
+ boolean steep = Math.abs(toY - fromY) > Math.abs(toX - fromX);
+ if (steep) {
+ int temp = fromX;
+ fromX = fromY;
+ fromY = temp;
+ temp = toX;
+ toX = toY;
+ toY = temp;
+ }
+
+ int dx = Math.abs(toX - fromX);
+ int dy = Math.abs(toY - fromY);
+ int error = -dx >> 1;
+ int xstep = fromX < toX ? 1 : -1;
+ int ystep = fromY < toY ? 1 : -1;
+
+ // In black pixels, looking for white, first or second time.
+ int state = 0;
+ for (int x = fromX, y = fromY; x != toX; x += xstep) {
+ int realX = steep ? y : x;
+ int realY = steep ? x : y;
+
+ // In white pixels, looking for black.
+ // FIXME(dswitkin): This method seems to assume square images, which can cause these calls to
+ // BitMatrix.get() to throw ArrayIndexOutOfBoundsException.
+ if (state == 1) {
+ if (image.get(realX, realY)) {
+ state++;
+ }
+ } else {
+ if (!image.get(realX, realY)) {
+ state++;
+ }
+ }
+
+ // Found black, white, black, and stumbled back onto white, so we're done.
+ if (state == 3) {
+ int diffX = x - fromX;
+ int diffY = y - fromY;
+ if (xstep < 0) {
+ diffX++;
+ }
+ return (float) Math.sqrt((double) (diffX * diffX + diffY * diffY));
+ }
+ error += dy;
+ if (error > 0) {
+ if (y == toY) {
+ break;
+ }
+ y += ystep;
+ error -= dx;
+ }
+ }
+ int diffX = toX - fromX;
+ int diffY = toY - fromY;
+ return (float) Math.sqrt((double) (diffX * diffX + diffY * diffY));
+ }
+
+ /**
+ *
Attempts to locate an alignment pattern in a limited region of the image, which is
+ * guessed to contain it. This method uses {@link AlignmentPattern}.
+ *
+ * @param overallEstModuleSize estimated module size so far
+ * @param estAlignmentX x coordinate of center of area probably containing alignment pattern
+ * @param estAlignmentY y coordinate of above
+ * @param allowanceFactor number of pixels in all directions to search from the center
+ * @return {@link AlignmentPattern} if found, or null otherwise
+ * @throws NotFoundException if an unexpected error occurs during detection
+ */
+ protected AlignmentPattern findAlignmentInRegion(float overallEstModuleSize,
+ int estAlignmentX,
+ int estAlignmentY,
+ float allowanceFactor)
+ throws NotFoundException {
+ // Look for an alignment pattern (3 modules in size) around where it
+ // should be
+ int allowance = (int) (allowanceFactor * overallEstModuleSize);
+ int alignmentAreaLeftX = Math.max(0, estAlignmentX - allowance);
+ int alignmentAreaRightX = Math.min(image.getWidth() - 1, estAlignmentX + allowance);
+ if (alignmentAreaRightX - alignmentAreaLeftX < overallEstModuleSize * 3) {
+ throw NotFoundException.getNotFoundInstance();
+ }
+
+ int alignmentAreaTopY = Math.max(0, estAlignmentY - allowance);
+ int alignmentAreaBottomY = Math.min(image.getHeight() - 1, estAlignmentY + allowance);
+ if (alignmentAreaBottomY - alignmentAreaTopY < overallEstModuleSize * 3) {
+ throw NotFoundException.getNotFoundInstance();
+ }
+
+ AlignmentPatternFinder alignmentFinder =
+ new AlignmentPatternFinder(
+ image,
+ alignmentAreaLeftX,
+ alignmentAreaTopY,
+ alignmentAreaRightX - alignmentAreaLeftX,
+ alignmentAreaBottomY - alignmentAreaTopY,
+ overallEstModuleSize,
+ resultPointCallback);
+ return alignmentFinder.find();
+ }
+
+ /**
+ * Ends up being a bit faster than Math.round(). This merely rounds its argument to the nearest int,
+ * where x.5 rounds up.
+ */
+ private static int round(float d) {
+ return (int) (d + 0.5f);
+ }
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/detector/FinderPattern.java b/libraries/zxing/src/com/google/zxing/qrcode/detector/FinderPattern.java
new file mode 100644
index 000000000..7a9914d76
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/detector/FinderPattern.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2007 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.qrcode.detector;
+
+import com.google.zxing.ResultPoint;
+
+/**
+ *
Encapsulates a finder pattern, which are the three square patterns found in
+ * the corners of QR Codes. It also encapsulates a count of similar finder patterns,
+ * as a convenience to the finder's bookkeeping.
Determines if this finder pattern "about equals" a finder pattern at the stated
+ * position and size -- meaning, it is at nearly the same center with nearly the same size.
+ */
+ boolean aboutEquals(float moduleSize, float i, float j) {
+ if (Math.abs(i - getY()) <= moduleSize && Math.abs(j - getX()) <= moduleSize) {
+ float moduleSizeDiff = Math.abs(moduleSize - estimatedModuleSize);
+ return moduleSizeDiff <= 1.0f || moduleSizeDiff / estimatedModuleSize <= 1.0f;
+ }
+ return false;
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/detector/FinderPatternFinder.java b/libraries/zxing/src/com/google/zxing/qrcode/detector/FinderPatternFinder.java
new file mode 100644
index 000000000..01b3bde2a
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/detector/FinderPatternFinder.java
@@ -0,0 +1,585 @@
+/*
+ * Copyright 2007 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.qrcode.detector;
+
+import com.google.zxing.DecodeHintType;
+import com.google.zxing.NotFoundException;
+import com.google.zxing.ResultPoint;
+import com.google.zxing.ResultPointCallback;
+import com.google.zxing.common.BitMatrix;
+import com.google.zxing.common.Collections;
+import com.google.zxing.common.Comparator;
+
+import java.util.Hashtable;
+import java.util.Vector;
+
+/**
+ *
This class attempts to find finder patterns in a QR Code. Finder patterns are the square
+ * markers at three corners of a QR Code.
+ *
+ *
This class is thread-safe but not reentrant. Each thread must allocate its own object.
+ *
+ * @author Sean Owen
+ */
+public class FinderPatternFinder {
+
+ private static final int CENTER_QUORUM = 2;
+ protected static final int MIN_SKIP = 3; // 1 pixel/module times 3 modules/center
+ protected static final int MAX_MODULES = 57; // support up to version 10 for mobile clients
+ private static final int INTEGER_MATH_SHIFT = 8;
+
+ private final BitMatrix image;
+ private final Vector possibleCenters;
+ private boolean hasSkipped;
+ private final int[] crossCheckStateCount;
+ private final ResultPointCallback resultPointCallback;
+
+ /**
+ *
Creates a finder that will search the image for three finder patterns.
+ *
+ * @param image image to search
+ */
+ public FinderPatternFinder(BitMatrix image) {
+ this(image, null);
+ }
+
+ public FinderPatternFinder(BitMatrix image, ResultPointCallback resultPointCallback) {
+ this.image = image;
+ this.possibleCenters = new Vector();
+ this.crossCheckStateCount = new int[5];
+ this.resultPointCallback = resultPointCallback;
+ }
+
+ protected BitMatrix getImage() {
+ return image;
+ }
+
+ protected Vector getPossibleCenters() {
+ return possibleCenters;
+ }
+
+ FinderPatternInfo find(Hashtable hints) throws NotFoundException {
+ boolean tryHarder = hints != null && hints.containsKey(DecodeHintType.TRY_HARDER);
+ int maxI = image.getHeight();
+ int maxJ = image.getWidth();
+ // We are looking for black/white/black/white/black modules in
+ // 1:1:3:1:1 ratio; this tracks the number of such modules seen so far
+
+ // Let's assume that the maximum version QR Code we support takes up 1/4 the height of the
+ // image, and then account for the center being 3 modules in size. This gives the smallest
+ // number of pixels the center could be, so skip this often. When trying harder, look for all
+ // QR versions regardless of how dense they are.
+ int iSkip = (3 * maxI) / (4 * MAX_MODULES);
+ if (iSkip < MIN_SKIP || tryHarder) {
+ iSkip = MIN_SKIP;
+ }
+
+ boolean done = false;
+ int[] stateCount = new int[5];
+ for (int i = iSkip - 1; i < maxI && !done; i += iSkip) {
+ // Get a row of black/white values
+ stateCount[0] = 0;
+ stateCount[1] = 0;
+ stateCount[2] = 0;
+ stateCount[3] = 0;
+ stateCount[4] = 0;
+ int currentState = 0;
+ for (int j = 0; j < maxJ; j++) {
+ if (image.get(j, i)) {
+ // Black pixel
+ if ((currentState & 1) == 1) { // Counting white pixels
+ currentState++;
+ }
+ stateCount[currentState]++;
+ } else { // White pixel
+ if ((currentState & 1) == 0) { // Counting black pixels
+ if (currentState == 4) { // A winner?
+ if (foundPatternCross(stateCount)) { // Yes
+ boolean confirmed = handlePossibleCenter(stateCount, i, j);
+ if (confirmed) {
+ // Start examining every other line. Checking each line turned out to be too
+ // expensive and didn't improve performance.
+ iSkip = 2;
+ if (hasSkipped) {
+ done = haveMultiplyConfirmedCenters();
+ } else {
+ int rowSkip = findRowSkip();
+ if (rowSkip > stateCount[2]) {
+ // Skip rows between row of lower confirmed center
+ // and top of presumed third confirmed center
+ // but back up a bit to get a full chance of detecting
+ // it, entire width of center of finder pattern
+
+ // Skip by rowSkip, but back off by stateCount[2] (size of last center
+ // of pattern we saw) to be conservative, and also back off by iSkip which
+ // is about to be re-added
+ i += rowSkip - stateCount[2] - iSkip;
+ j = maxJ - 1;
+ }
+ }
+ } else {
+ stateCount[0] = stateCount[2];
+ stateCount[1] = stateCount[3];
+ stateCount[2] = stateCount[4];
+ stateCount[3] = 1;
+ stateCount[4] = 0;
+ currentState = 3;
+ continue;
+ }
+ // Clear state to start looking again
+ currentState = 0;
+ stateCount[0] = 0;
+ stateCount[1] = 0;
+ stateCount[2] = 0;
+ stateCount[3] = 0;
+ stateCount[4] = 0;
+ } else { // No, shift counts back by two
+ stateCount[0] = stateCount[2];
+ stateCount[1] = stateCount[3];
+ stateCount[2] = stateCount[4];
+ stateCount[3] = 1;
+ stateCount[4] = 0;
+ currentState = 3;
+ }
+ } else {
+ stateCount[++currentState]++;
+ }
+ } else { // Counting white pixels
+ stateCount[currentState]++;
+ }
+ }
+ }
+ if (foundPatternCross(stateCount)) {
+ boolean confirmed = handlePossibleCenter(stateCount, i, maxJ);
+ if (confirmed) {
+ iSkip = stateCount[0];
+ if (hasSkipped) {
+ // Found a third one
+ done = haveMultiplyConfirmedCenters();
+ }
+ }
+ }
+ }
+
+ FinderPattern[] patternInfo = selectBestPatterns();
+ ResultPoint.orderBestPatterns(patternInfo);
+
+ return new FinderPatternInfo(patternInfo);
+ }
+
+ /**
+ * Given a count of black/white/black/white/black pixels just seen and an end position,
+ * figures the location of the center of this run.
+ */
+ private static float centerFromEnd(int[] stateCount, int end) {
+ return (float) (end - stateCount[4] - stateCount[3]) - stateCount[2] / 2.0f;
+ }
+
+ /**
+ * @param stateCount count of black/white/black/white/black pixels just read
+ * @return true iff the proportions of the counts is close enough to the 1/1/3/1/1 ratios
+ * used by finder patterns to be considered a match
+ */
+ protected static boolean foundPatternCross(int[] stateCount) {
+ int totalModuleSize = 0;
+ for (int i = 0; i < 5; i++) {
+ int count = stateCount[i];
+ if (count == 0) {
+ return false;
+ }
+ totalModuleSize += count;
+ }
+ if (totalModuleSize < 7) {
+ return false;
+ }
+ int moduleSize = (totalModuleSize << INTEGER_MATH_SHIFT) / 7;
+ int maxVariance = moduleSize / 2;
+ // Allow less than 50% variance from 1-1-3-1-1 proportions
+ return Math.abs(moduleSize - (stateCount[0] << INTEGER_MATH_SHIFT)) < maxVariance &&
+ Math.abs(moduleSize - (stateCount[1] << INTEGER_MATH_SHIFT)) < maxVariance &&
+ Math.abs(3 * moduleSize - (stateCount[2] << INTEGER_MATH_SHIFT)) < 3 * maxVariance &&
+ Math.abs(moduleSize - (stateCount[3] << INTEGER_MATH_SHIFT)) < maxVariance &&
+ Math.abs(moduleSize - (stateCount[4] << INTEGER_MATH_SHIFT)) < maxVariance;
+ }
+
+ private int[] getCrossCheckStateCount() {
+ crossCheckStateCount[0] = 0;
+ crossCheckStateCount[1] = 0;
+ crossCheckStateCount[2] = 0;
+ crossCheckStateCount[3] = 0;
+ crossCheckStateCount[4] = 0;
+ return crossCheckStateCount;
+ }
+
+ /**
+ *
After a horizontal scan finds a potential finder pattern, this method
+ * "cross-checks" by scanning down vertically through the center of the possible
+ * finder pattern to see if the same proportion is detected.
+ *
+ * @param startI row where a finder pattern was detected
+ * @param centerJ center of the section that appears to cross a finder pattern
+ * @param maxCount maximum reasonable number of modules that should be
+ * observed in any reading state, based on the results of the horizontal scan
+ * @return vertical center of finder pattern, or {@link Float#NaN} if not found
+ */
+ private float crossCheckVertical(int startI, int centerJ, int maxCount,
+ int originalStateCountTotal) {
+ BitMatrix image = this.image;
+
+ int maxI = image.getHeight();
+ int[] stateCount = getCrossCheckStateCount();
+
+ // Start counting up from center
+ int i = startI;
+ while (i >= 0 && image.get(centerJ, i)) {
+ stateCount[2]++;
+ i--;
+ }
+ if (i < 0) {
+ return Float.NaN;
+ }
+ while (i >= 0 && !image.get(centerJ, i) && stateCount[1] <= maxCount) {
+ stateCount[1]++;
+ i--;
+ }
+ // If already too many modules in this state or ran off the edge:
+ if (i < 0 || stateCount[1] > maxCount) {
+ return Float.NaN;
+ }
+ while (i >= 0 && image.get(centerJ, i) && stateCount[0] <= maxCount) {
+ stateCount[0]++;
+ i--;
+ }
+ if (stateCount[0] > maxCount) {
+ return Float.NaN;
+ }
+
+ // Now also count down from center
+ i = startI + 1;
+ while (i < maxI && image.get(centerJ, i)) {
+ stateCount[2]++;
+ i++;
+ }
+ if (i == maxI) {
+ return Float.NaN;
+ }
+ while (i < maxI && !image.get(centerJ, i) && stateCount[3] < maxCount) {
+ stateCount[3]++;
+ i++;
+ }
+ if (i == maxI || stateCount[3] >= maxCount) {
+ return Float.NaN;
+ }
+ while (i < maxI && image.get(centerJ, i) && stateCount[4] < maxCount) {
+ stateCount[4]++;
+ i++;
+ }
+ if (stateCount[4] >= maxCount) {
+ return Float.NaN;
+ }
+
+ // If we found a finder-pattern-like section, but its size is more than 40% different than
+ // the original, assume it's a false positive
+ int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] +
+ stateCount[4];
+ if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal) {
+ return Float.NaN;
+ }
+
+ return foundPatternCross(stateCount) ? centerFromEnd(stateCount, i) : Float.NaN;
+ }
+
+ /**
+ *
Like {@link #crossCheckVertical(int, int, int, int)}, and in fact is basically identical,
+ * except it reads horizontally instead of vertically. This is used to cross-cross
+ * check a vertical cross check and locate the real center of the alignment pattern.
+ */
+ private float crossCheckHorizontal(int startJ, int centerI, int maxCount,
+ int originalStateCountTotal) {
+ BitMatrix image = this.image;
+
+ int maxJ = image.getWidth();
+ int[] stateCount = getCrossCheckStateCount();
+
+ int j = startJ;
+ while (j >= 0 && image.get(j, centerI)) {
+ stateCount[2]++;
+ j--;
+ }
+ if (j < 0) {
+ return Float.NaN;
+ }
+ while (j >= 0 && !image.get(j, centerI) && stateCount[1] <= maxCount) {
+ stateCount[1]++;
+ j--;
+ }
+ if (j < 0 || stateCount[1] > maxCount) {
+ return Float.NaN;
+ }
+ while (j >= 0 && image.get(j, centerI) && stateCount[0] <= maxCount) {
+ stateCount[0]++;
+ j--;
+ }
+ if (stateCount[0] > maxCount) {
+ return Float.NaN;
+ }
+
+ j = startJ + 1;
+ while (j < maxJ && image.get(j, centerI)) {
+ stateCount[2]++;
+ j++;
+ }
+ if (j == maxJ) {
+ return Float.NaN;
+ }
+ while (j < maxJ && !image.get(j, centerI) && stateCount[3] < maxCount) {
+ stateCount[3]++;
+ j++;
+ }
+ if (j == maxJ || stateCount[3] >= maxCount) {
+ return Float.NaN;
+ }
+ while (j < maxJ && image.get(j, centerI) && stateCount[4] < maxCount) {
+ stateCount[4]++;
+ j++;
+ }
+ if (stateCount[4] >= maxCount) {
+ return Float.NaN;
+ }
+
+ // If we found a finder-pattern-like section, but its size is significantly different than
+ // the original, assume it's a false positive
+ int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] +
+ stateCount[4];
+ if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= originalStateCountTotal) {
+ return Float.NaN;
+ }
+
+ return foundPatternCross(stateCount) ? centerFromEnd(stateCount, j) : Float.NaN;
+ }
+
+ /**
+ *
This is called when a horizontal scan finds a possible alignment pattern. It will
+ * cross check with a vertical scan, and if successful, will, ah, cross-cross-check
+ * with another horizontal scan. This is needed primarily to locate the real horizontal
+ * center of the pattern in cases of extreme skew.
+ *
+ *
If that succeeds the finder pattern location is added to a list that tracks
+ * the number of times each location has been nearly-matched as a finder pattern.
+ * Each additional find is more evidence that the location is in fact a finder
+ * pattern center
+ *
+ * @param stateCount reading state module counts from horizontal scan
+ * @param i row where finder pattern may be found
+ * @param j end of possible finder pattern in row
+ * @return true if a finder pattern candidate was found this time
+ */
+ protected boolean handlePossibleCenter(int[] stateCount, int i, int j) {
+ int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] +
+ stateCount[4];
+ float centerJ = centerFromEnd(stateCount, j);
+ float centerI = crossCheckVertical(i, (int) centerJ, stateCount[2], stateCountTotal);
+ if (!Float.isNaN(centerI)) {
+ // Re-cross check
+ centerJ = crossCheckHorizontal((int) centerJ, (int) centerI, stateCount[2], stateCountTotal);
+ if (!Float.isNaN(centerJ)) {
+ float estimatedModuleSize = (float) stateCountTotal / 7.0f;
+ boolean found = false;
+ int max = possibleCenters.size();
+ for (int index = 0; index < max; index++) {
+ FinderPattern center = (FinderPattern) possibleCenters.elementAt(index);
+ // Look for about the same center and module size:
+ if (center.aboutEquals(estimatedModuleSize, centerI, centerJ)) {
+ center.incrementCount();
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ ResultPoint point = new FinderPattern(centerJ, centerI, estimatedModuleSize);
+ possibleCenters.addElement(point);
+ if (resultPointCallback != null) {
+ resultPointCallback.foundPossibleResultPoint(point);
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @return number of rows we could safely skip during scanning, based on the first
+ * two finder patterns that have been located. In some cases their position will
+ * allow us to infer that the third pattern must lie below a certain point farther
+ * down in the image.
+ */
+ private int findRowSkip() {
+ int max = possibleCenters.size();
+ if (max <= 1) {
+ return 0;
+ }
+ FinderPattern firstConfirmedCenter = null;
+ for (int i = 0; i < max; i++) {
+ FinderPattern center = (FinderPattern) possibleCenters.elementAt(i);
+ if (center.getCount() >= CENTER_QUORUM) {
+ if (firstConfirmedCenter == null) {
+ firstConfirmedCenter = center;
+ } else {
+ // We have two confirmed centers
+ // How far down can we skip before resuming looking for the next
+ // pattern? In the worst case, only the difference between the
+ // difference in the x / y coordinates of the two centers.
+ // This is the case where you find top left last.
+ hasSkipped = true;
+ return (int) (Math.abs(firstConfirmedCenter.getX() - center.getX()) -
+ Math.abs(firstConfirmedCenter.getY() - center.getY())) / 2;
+ }
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * @return true iff we have found at least 3 finder patterns that have been detected
+ * at least {@link #CENTER_QUORUM} times each, and, the estimated module size of the
+ * candidates is "pretty similar"
+ */
+ private boolean haveMultiplyConfirmedCenters() {
+ int confirmedCount = 0;
+ float totalModuleSize = 0.0f;
+ int max = possibleCenters.size();
+ for (int i = 0; i < max; i++) {
+ FinderPattern pattern = (FinderPattern) possibleCenters.elementAt(i);
+ if (pattern.getCount() >= CENTER_QUORUM) {
+ confirmedCount++;
+ totalModuleSize += pattern.getEstimatedModuleSize();
+ }
+ }
+ if (confirmedCount < 3) {
+ return false;
+ }
+ // OK, we have at least 3 confirmed centers, but, it's possible that one is a "false positive"
+ // and that we need to keep looking. We detect this by asking if the estimated module sizes
+ // vary too much. We arbitrarily say that when the total deviation from average exceeds
+ // 5% of the total module size estimates, it's too much.
+ float average = totalModuleSize / (float) max;
+ float totalDeviation = 0.0f;
+ for (int i = 0; i < max; i++) {
+ FinderPattern pattern = (FinderPattern) possibleCenters.elementAt(i);
+ totalDeviation += Math.abs(pattern.getEstimatedModuleSize() - average);
+ }
+ return totalDeviation <= 0.05f * totalModuleSize;
+ }
+
+ /**
+ * @return the 3 best {@link FinderPattern}s from our list of candidates. The "best" are
+ * those that have been detected at least {@link #CENTER_QUORUM} times, and whose module
+ * size differs from the average among those patterns the least
+ * @throws NotFoundException if 3 such finder patterns do not exist
+ */
+ private FinderPattern[] selectBestPatterns() throws NotFoundException {
+
+ int startSize = possibleCenters.size();
+ if (startSize < 3) {
+ // Couldn't find enough finder patterns
+ throw NotFoundException.getNotFoundInstance();
+ }
+
+ // Filter outlier possibilities whose module size is too different
+ if (startSize > 3) {
+ // But we can only afford to do so if we have at least 4 possibilities to choose from
+ float totalModuleSize = 0.0f;
+ float square = 0.0f;
+ for (int i = 0; i < startSize; i++) {
+ float size = ((FinderPattern) possibleCenters.elementAt(i)).getEstimatedModuleSize();
+ totalModuleSize += size;
+ square += size * size;
+ }
+ float average = totalModuleSize / (float) startSize;
+ float stdDev = (float) Math.sqrt(square / startSize - average * average);
+
+ Collections.insertionSort(possibleCenters, new FurthestFromAverageComparator(average));
+
+ float limit = Math.max(0.2f * average, stdDev);
+
+ for (int i = 0; i < possibleCenters.size() && possibleCenters.size() > 3; i++) {
+ FinderPattern pattern = (FinderPattern) possibleCenters.elementAt(i);
+ if (Math.abs(pattern.getEstimatedModuleSize() - average) > limit) {
+ possibleCenters.removeElementAt(i);
+ i--;
+ }
+ }
+ }
+
+ if (possibleCenters.size() > 3) {
+ // Throw away all but those first size candidate points we found.
+
+ float totalModuleSize = 0.0f;
+ for (int i = 0; i < possibleCenters.size(); i++) {
+ totalModuleSize += ((FinderPattern) possibleCenters.elementAt(i)).getEstimatedModuleSize();
+ }
+
+ float average = totalModuleSize / (float) possibleCenters.size();
+
+ Collections.insertionSort(possibleCenters, new CenterComparator(average));
+
+ possibleCenters.setSize(3);
+ }
+
+ return new FinderPattern[]{
+ (FinderPattern) possibleCenters.elementAt(0),
+ (FinderPattern) possibleCenters.elementAt(1),
+ (FinderPattern) possibleCenters.elementAt(2)
+ };
+ }
+
+ /**
+ *
Orders by furthest from average
+ */
+ private static class FurthestFromAverageComparator implements Comparator {
+ private final float average;
+ private FurthestFromAverageComparator(float f) {
+ average = f;
+ }
+ public int compare(Object center1, Object center2) {
+ float dA = Math.abs(((FinderPattern) center2).getEstimatedModuleSize() - average);
+ float dB = Math.abs(((FinderPattern) center1).getEstimatedModuleSize() - average);
+ return dA < dB ? -1 : dA == dB ? 0 : 1;
+ }
+ }
+
+ /**
+ *
Orders by {@link FinderPattern#getCount()}, descending.
+ */
+ private static class CenterComparator implements Comparator {
+ private final float average;
+ private CenterComparator(float f) {
+ average = f;
+ }
+ public int compare(Object center1, Object center2) {
+ if (((FinderPattern) center2).getCount() == ((FinderPattern) center1).getCount()) {
+ float dA = Math.abs(((FinderPattern) center2).getEstimatedModuleSize() - average);
+ float dB = Math.abs(((FinderPattern) center1).getEstimatedModuleSize() - average);
+ return dA < dB ? 1 : dA == dB ? 0 : -1;
+ } else {
+ return ((FinderPattern) center2).getCount() - ((FinderPattern) center1).getCount();
+ }
+ }
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/detector/FinderPatternInfo.java b/libraries/zxing/src/com/google/zxing/qrcode/detector/FinderPatternInfo.java
new file mode 100644
index 000000000..3c3401085
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/detector/FinderPatternInfo.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2007 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.qrcode.detector;
+
+/**
+ *
Encapsulates information about finder patterns in an image, including the location of
+ * the three finder patterns, and their estimated module size.
+ *
+ * @author Sean Owen
+ */
+public final class FinderPatternInfo {
+
+ private final FinderPattern bottomLeft;
+ private final FinderPattern topLeft;
+ private final FinderPattern topRight;
+
+ public FinderPatternInfo(FinderPattern[] patternCenters) {
+ this.bottomLeft = patternCenters[0];
+ this.topLeft = patternCenters[1];
+ this.topRight = patternCenters[2];
+ }
+
+ public FinderPattern getBottomLeft() {
+ return bottomLeft;
+ }
+
+ public FinderPattern getTopLeft() {
+ return topLeft;
+ }
+
+ public FinderPattern getTopRight() {
+ return topRight;
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/encoder/BlockPair.java b/libraries/zxing/src/com/google/zxing/qrcode/encoder/BlockPair.java
new file mode 100644
index 000000000..5714d9c3a
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/encoder/BlockPair.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 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.qrcode.encoder;
+
+final class BlockPair {
+
+ private final byte[] dataBytes;
+ private final byte[] errorCorrectionBytes;
+
+ BlockPair(byte[] data, byte[] errorCorrection) {
+ dataBytes = data;
+ errorCorrectionBytes = errorCorrection;
+ }
+
+ public byte[] getDataBytes() {
+ return dataBytes;
+ }
+
+ public byte[] getErrorCorrectionBytes() {
+ return errorCorrectionBytes;
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/encoder/ByteMatrix.java b/libraries/zxing/src/com/google/zxing/qrcode/encoder/ByteMatrix.java
new file mode 100644
index 000000000..eb248a26c
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/encoder/ByteMatrix.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 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.qrcode.encoder;
+
+/**
+ * A class which wraps a 2D array of bytes. The default usage is signed. If you want to use it as a
+ * unsigned container, it's up to you to do byteValue & 0xff at each location.
+ *
+ * JAVAPORT: The original code was a 2D array of ints, but since it only ever gets assigned
+ * -1, 0, and 1, I'm going to use less memory and go with bytes.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+public final class ByteMatrix {
+
+ private final byte[][] bytes;
+ private final int width;
+ private final int height;
+
+ public ByteMatrix(int width, int height) {
+ bytes = new byte[height][width];
+ this.width = width;
+ this.height = height;
+ }
+
+ public int getHeight() {
+ return height;
+ }
+
+ public int getWidth() {
+ return width;
+ }
+
+ public byte get(int x, int y) {
+ return bytes[y][x];
+ }
+
+ public byte[][] getArray() {
+ return bytes;
+ }
+
+ public void set(int x, int y, byte value) {
+ bytes[y][x] = value;
+ }
+
+ public void set(int x, int y, int value) {
+ bytes[y][x] = (byte) value;
+ }
+
+ public void set(int x, int y, boolean value) {
+ bytes[y][x] = (byte) (value ? 1 : 0);
+ }
+
+ public void clear(byte value) {
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ bytes[y][x] = value;
+ }
+ }
+ }
+
+ public String toString() {
+ StringBuffer result = new StringBuffer(2 * width * height + 2);
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ switch (bytes[y][x]) {
+ case 0:
+ result.append(" 0");
+ break;
+ case 1:
+ result.append(" 1");
+ break;
+ default:
+ result.append(" ");
+ break;
+ }
+ }
+ result.append('\n');
+ }
+ return result.toString();
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/encoder/Encoder.java b/libraries/zxing/src/com/google/zxing/qrcode/encoder/Encoder.java
new file mode 100644
index 000000000..8796511ab
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/encoder/Encoder.java
@@ -0,0 +1,557 @@
+/*
+ * Copyright 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.qrcode.encoder;
+
+import com.google.zxing.EncodeHintType;
+import com.google.zxing.WriterException;
+import com.google.zxing.common.BitArray;
+import com.google.zxing.common.CharacterSetECI;
+import com.google.zxing.common.ECI;
+import com.google.zxing.common.reedsolomon.GenericGF;
+import com.google.zxing.common.reedsolomon.ReedSolomonEncoder;
+import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
+import com.google.zxing.qrcode.decoder.Mode;
+import com.google.zxing.qrcode.decoder.Version;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Hashtable;
+import java.util.Vector;
+
+/**
+ * @author satorux@google.com (Satoru Takabayashi) - creator
+ * @author dswitkin@google.com (Daniel Switkin) - ported from C++
+ */
+public final class Encoder {
+
+ // The original table is defined in the table 5 of JISX0510:2004 (p.19).
+ private static final int[] ALPHANUMERIC_TABLE = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x00-0x0f
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x10-0x1f
+ 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, // 0x20-0x2f
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, // 0x30-0x3f
+ -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 0x40-0x4f
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, // 0x50-0x5f
+ };
+
+ static final String DEFAULT_BYTE_MODE_ENCODING = "ISO-8859-1";
+
+ private Encoder() {
+ }
+
+ // The mask penalty calculation is complicated. See Table 21 of JISX0510:2004 (p.45) for details.
+ // Basically it applies four rules and summate all penalties.
+ private static int calculateMaskPenalty(ByteMatrix matrix) {
+ int penalty = 0;
+ penalty += MaskUtil.applyMaskPenaltyRule1(matrix);
+ penalty += MaskUtil.applyMaskPenaltyRule2(matrix);
+ penalty += MaskUtil.applyMaskPenaltyRule3(matrix);
+ penalty += MaskUtil.applyMaskPenaltyRule4(matrix);
+ return penalty;
+ }
+
+ /**
+ * Encode "bytes" with the error correction level "ecLevel". The encoding mode will be chosen
+ * internally by chooseMode(). On success, store the result in "qrCode".
+ *
+ * We recommend you to use QRCode.EC_LEVEL_L (the lowest level) for
+ * "getECLevel" since our primary use is to show QR code on desktop screens. We don't need very
+ * strong error correction for this purpose.
+ *
+ * Note that there is no way to encode bytes in MODE_KANJI. We might want to add EncodeWithMode()
+ * with which clients can specify the encoding mode. For now, we don't need the functionality.
+ */
+ public static void encode(String content, ErrorCorrectionLevel ecLevel, QRCode qrCode)
+ throws WriterException {
+ encode(content, ecLevel, null, qrCode);
+ }
+
+ public static void encode(String content, ErrorCorrectionLevel ecLevel, Hashtable hints,
+ QRCode qrCode) throws WriterException {
+
+ String encoding = hints == null ? null : (String) hints.get(EncodeHintType.CHARACTER_SET);
+ if (encoding == null) {
+ encoding = DEFAULT_BYTE_MODE_ENCODING;
+ }
+
+ // Step 1: Choose the mode (encoding).
+ Mode mode = chooseMode(content, encoding);
+
+ // Step 2: Append "bytes" into "dataBits" in appropriate encoding.
+ BitArray dataBits = new BitArray();
+ appendBytes(content, mode, dataBits, encoding);
+ // Step 3: Initialize QR code that can contain "dataBits".
+ int numInputBytes = dataBits.getSizeInBytes();
+ initQRCode(numInputBytes, ecLevel, mode, qrCode);
+
+ // Step 4: Build another bit vector that contains header and data.
+ BitArray headerAndDataBits = new BitArray();
+
+ // Step 4.5: Append ECI message if applicable
+ if (mode == Mode.BYTE && !DEFAULT_BYTE_MODE_ENCODING.equals(encoding)) {
+ CharacterSetECI eci = CharacterSetECI.getCharacterSetECIByName(encoding);
+ if (eci != null) {
+ appendECI(eci, headerAndDataBits);
+ }
+ }
+
+ appendModeInfo(mode, headerAndDataBits);
+
+ int numLetters = mode.equals(Mode.BYTE) ? dataBits.getSizeInBytes() : content.length();
+ appendLengthInfo(numLetters, qrCode.getVersion(), mode, headerAndDataBits);
+ headerAndDataBits.appendBitArray(dataBits);
+
+ // Step 5: Terminate the bits properly.
+ terminateBits(qrCode.getNumDataBytes(), headerAndDataBits);
+
+ // Step 6: Interleave data bits with error correction code.
+ BitArray finalBits = new BitArray();
+ interleaveWithECBytes(headerAndDataBits, qrCode.getNumTotalBytes(), qrCode.getNumDataBytes(),
+ qrCode.getNumRSBlocks(), finalBits);
+
+ // Step 7: Choose the mask pattern and set to "qrCode".
+ ByteMatrix matrix = new ByteMatrix(qrCode.getMatrixWidth(), qrCode.getMatrixWidth());
+ qrCode.setMaskPattern(chooseMaskPattern(finalBits, qrCode.getECLevel(), qrCode.getVersion(),
+ matrix));
+
+ // Step 8. Build the matrix and set it to "qrCode".
+ MatrixUtil.buildMatrix(finalBits, qrCode.getECLevel(), qrCode.getVersion(),
+ qrCode.getMaskPattern(), matrix);
+ qrCode.setMatrix(matrix);
+ // Step 9. Make sure we have a valid QR Code.
+ if (!qrCode.isValid()) {
+ throw new WriterException("Invalid QR code: " + qrCode.toString());
+ }
+ }
+
+ /**
+ * @return the code point of the table used in alphanumeric mode or
+ * -1 if there is no corresponding code in the table.
+ */
+ static int getAlphanumericCode(int code) {
+ if (code < ALPHANUMERIC_TABLE.length) {
+ return ALPHANUMERIC_TABLE[code];
+ }
+ return -1;
+ }
+
+ public static Mode chooseMode(String content) {
+ return chooseMode(content, null);
+ }
+
+ /**
+ * Choose the best mode by examining the content. Note that 'encoding' is used as a hint;
+ * if it is Shift_JIS, and the input is only double-byte Kanji, then we return {@link Mode#KANJI}.
+ */
+ public static Mode chooseMode(String content, String encoding) {
+ if ("Shift_JIS".equals(encoding)) {
+ // Choose Kanji mode if all input are double-byte characters
+ return isOnlyDoubleByteKanji(content) ? Mode.KANJI : Mode.BYTE;
+ }
+ boolean hasNumeric = false;
+ boolean hasAlphanumeric = false;
+ for (int i = 0; i < content.length(); ++i) {
+ char c = content.charAt(i);
+ if (c >= '0' && c <= '9') {
+ hasNumeric = true;
+ } else if (getAlphanumericCode(c) != -1) {
+ hasAlphanumeric = true;
+ } else {
+ return Mode.BYTE;
+ }
+ }
+ if (hasAlphanumeric) {
+ return Mode.ALPHANUMERIC;
+ } else if (hasNumeric) {
+ return Mode.NUMERIC;
+ }
+ return Mode.BYTE;
+ }
+
+ private static boolean isOnlyDoubleByteKanji(String content) {
+ byte[] bytes;
+ try {
+ bytes = content.getBytes("Shift_JIS");
+ } catch (UnsupportedEncodingException uee) {
+ return false;
+ }
+ int length = bytes.length;
+ if (length % 2 != 0) {
+ return false;
+ }
+ for (int i = 0; i < length; i += 2) {
+ int byte1 = bytes[i] & 0xFF;
+ if ((byte1 < 0x81 || byte1 > 0x9F) && (byte1 < 0xE0 || byte1 > 0xEB)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static int chooseMaskPattern(BitArray bits, ErrorCorrectionLevel ecLevel, int version,
+ ByteMatrix matrix) throws WriterException {
+
+ int minPenalty = Integer.MAX_VALUE; // Lower penalty is better.
+ int bestMaskPattern = -1;
+ // We try all mask patterns to choose the best one.
+ for (int maskPattern = 0; maskPattern < QRCode.NUM_MASK_PATTERNS; maskPattern++) {
+ MatrixUtil.buildMatrix(bits, ecLevel, version, maskPattern, matrix);
+ int penalty = calculateMaskPenalty(matrix);
+ if (penalty < minPenalty) {
+ minPenalty = penalty;
+ bestMaskPattern = maskPattern;
+ }
+ }
+ return bestMaskPattern;
+ }
+
+ /**
+ * Initialize "qrCode" according to "numInputBytes", "ecLevel", and "mode". On success,
+ * modify "qrCode".
+ */
+ private static void initQRCode(int numInputBytes, ErrorCorrectionLevel ecLevel, Mode mode,
+ QRCode qrCode) throws WriterException {
+ qrCode.setECLevel(ecLevel);
+ qrCode.setMode(mode);
+
+ // In the following comments, we use numbers of Version 7-H.
+ for (int versionNum = 1; versionNum <= 40; versionNum++) {
+ Version version = Version.getVersionForNumber(versionNum);
+ // numBytes = 196
+ int numBytes = version.getTotalCodewords();
+ // getNumECBytes = 130
+ Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel);
+ int numEcBytes = ecBlocks.getTotalECCodewords();
+ // getNumRSBlocks = 5
+ int numRSBlocks = ecBlocks.getNumBlocks();
+ // getNumDataBytes = 196 - 130 = 66
+ int numDataBytes = numBytes - numEcBytes;
+ // We want to choose the smallest version which can contain data of "numInputBytes" + some
+ // extra bits for the header (mode info and length info). The header can be three bytes
+ // (precisely 4 + 16 bits) at most. Hence we do +3 here.
+ if (numDataBytes >= numInputBytes + 3) {
+ // Yay, we found the proper rs block info!
+ qrCode.setVersion(versionNum);
+ qrCode.setNumTotalBytes(numBytes);
+ qrCode.setNumDataBytes(numDataBytes);
+ qrCode.setNumRSBlocks(numRSBlocks);
+ // getNumECBytes = 196 - 66 = 130
+ qrCode.setNumECBytes(numEcBytes);
+ // matrix width = 21 + 6 * 4 = 45
+ qrCode.setMatrixWidth(version.getDimensionForVersion());
+ return;
+ }
+ }
+ throw new WriterException("Cannot find proper rs block info (input data too big?)");
+ }
+
+ /**
+ * Terminate bits as described in 8.4.8 and 8.4.9 of JISX0510:2004 (p.24).
+ */
+ static void terminateBits(int numDataBytes, BitArray bits) throws WriterException {
+ int capacity = numDataBytes << 3;
+ if (bits.getSize() > capacity) {
+ throw new WriterException("data bits cannot fit in the QR Code" + bits.getSize() + " > " +
+ capacity);
+ }
+ for (int i = 0; i < 4 && bits.getSize() < capacity; ++i) {
+ bits.appendBit(false);
+ }
+ // Append termination bits. See 8.4.8 of JISX0510:2004 (p.24) for details.
+ // If the last byte isn't 8-bit aligned, we'll add padding bits.
+ int numBitsInLastByte = bits.getSize() & 0x07;
+ if (numBitsInLastByte > 0) {
+ for (int i = numBitsInLastByte; i < 8; i++) {
+ bits.appendBit(false);
+ }
+ }
+ // If we have more space, we'll fill the space with padding patterns defined in 8.4.9 (p.24).
+ int numPaddingBytes = numDataBytes - bits.getSizeInBytes();
+ for (int i = 0; i < numPaddingBytes; ++i) {
+ bits.appendBits((i & 0x01) == 0 ? 0xEC : 0x11, 8);
+ }
+ if (bits.getSize() != capacity) {
+ throw new WriterException("Bits size does not equal capacity");
+ }
+ }
+
+ /**
+ * Get number of data bytes and number of error correction bytes for block id "blockID". Store
+ * the result in "numDataBytesInBlock", and "numECBytesInBlock". See table 12 in 8.5.1 of
+ * JISX0510:2004 (p.30)
+ */
+ static void getNumDataBytesAndNumECBytesForBlockID(int numTotalBytes, int numDataBytes,
+ int numRSBlocks, int blockID, int[] numDataBytesInBlock,
+ int[] numECBytesInBlock) throws WriterException {
+ if (blockID >= numRSBlocks) {
+ throw new WriterException("Block ID too large");
+ }
+ // numRsBlocksInGroup2 = 196 % 5 = 1
+ int numRsBlocksInGroup2 = numTotalBytes % numRSBlocks;
+ // numRsBlocksInGroup1 = 5 - 1 = 4
+ int numRsBlocksInGroup1 = numRSBlocks - numRsBlocksInGroup2;
+ // numTotalBytesInGroup1 = 196 / 5 = 39
+ int numTotalBytesInGroup1 = numTotalBytes / numRSBlocks;
+ // numTotalBytesInGroup2 = 39 + 1 = 40
+ int numTotalBytesInGroup2 = numTotalBytesInGroup1 + 1;
+ // numDataBytesInGroup1 = 66 / 5 = 13
+ int numDataBytesInGroup1 = numDataBytes / numRSBlocks;
+ // numDataBytesInGroup2 = 13 + 1 = 14
+ int numDataBytesInGroup2 = numDataBytesInGroup1 + 1;
+ // numEcBytesInGroup1 = 39 - 13 = 26
+ int numEcBytesInGroup1 = numTotalBytesInGroup1 - numDataBytesInGroup1;
+ // numEcBytesInGroup2 = 40 - 14 = 26
+ int numEcBytesInGroup2 = numTotalBytesInGroup2 - numDataBytesInGroup2;
+ // Sanity checks.
+ // 26 = 26
+ if (numEcBytesInGroup1 != numEcBytesInGroup2) {
+ throw new WriterException("EC bytes mismatch");
+ }
+ // 5 = 4 + 1.
+ if (numRSBlocks != numRsBlocksInGroup1 + numRsBlocksInGroup2) {
+ throw new WriterException("RS blocks mismatch");
+ }
+ // 196 = (13 + 26) * 4 + (14 + 26) * 1
+ if (numTotalBytes !=
+ ((numDataBytesInGroup1 + numEcBytesInGroup1) *
+ numRsBlocksInGroup1) +
+ ((numDataBytesInGroup2 + numEcBytesInGroup2) *
+ numRsBlocksInGroup2)) {
+ throw new WriterException("Total bytes mismatch");
+ }
+
+ if (blockID < numRsBlocksInGroup1) {
+ numDataBytesInBlock[0] = numDataBytesInGroup1;
+ numECBytesInBlock[0] = numEcBytesInGroup1;
+ } else {
+ numDataBytesInBlock[0] = numDataBytesInGroup2;
+ numECBytesInBlock[0] = numEcBytesInGroup2;
+ }
+ }
+
+ /**
+ * Interleave "bits" with corresponding error correction bytes. On success, store the result in
+ * "result". The interleave rule is complicated. See 8.6 of JISX0510:2004 (p.37) for details.
+ */
+ static void interleaveWithECBytes(BitArray bits, int numTotalBytes,
+ int numDataBytes, int numRSBlocks, BitArray result) throws WriterException {
+
+ // "bits" must have "getNumDataBytes" bytes of data.
+ if (bits.getSizeInBytes() != numDataBytes) {
+ throw new WriterException("Number of bits and data bytes does not match");
+ }
+
+ // Step 1. Divide data bytes into blocks and generate error correction bytes for them. We'll
+ // store the divided data bytes blocks and error correction bytes blocks into "blocks".
+ int dataBytesOffset = 0;
+ int maxNumDataBytes = 0;
+ int maxNumEcBytes = 0;
+
+ // Since, we know the number of reedsolmon blocks, we can initialize the vector with the number.
+ Vector blocks = new Vector(numRSBlocks);
+
+ for (int i = 0; i < numRSBlocks; ++i) {
+ int[] numDataBytesInBlock = new int[1];
+ int[] numEcBytesInBlock = new int[1];
+ getNumDataBytesAndNumECBytesForBlockID(
+ numTotalBytes, numDataBytes, numRSBlocks, i,
+ numDataBytesInBlock, numEcBytesInBlock);
+
+ int size = numDataBytesInBlock[0];
+ byte[] dataBytes = new byte[size];
+ bits.toBytes(8*dataBytesOffset, dataBytes, 0, size);
+ byte[] ecBytes = generateECBytes(dataBytes, numEcBytesInBlock[0]);
+ blocks.addElement(new BlockPair(dataBytes, ecBytes));
+
+ maxNumDataBytes = Math.max(maxNumDataBytes, size);
+ maxNumEcBytes = Math.max(maxNumEcBytes, ecBytes.length);
+ dataBytesOffset += numDataBytesInBlock[0];
+ }
+ if (numDataBytes != dataBytesOffset) {
+ throw new WriterException("Data bytes does not match offset");
+ }
+
+ // First, place data blocks.
+ for (int i = 0; i < maxNumDataBytes; ++i) {
+ for (int j = 0; j < blocks.size(); ++j) {
+ byte[] dataBytes = ((BlockPair) blocks.elementAt(j)).getDataBytes();
+ if (i < dataBytes.length) {
+ result.appendBits(dataBytes[i], 8);
+ }
+ }
+ }
+ // Then, place error correction blocks.
+ for (int i = 0; i < maxNumEcBytes; ++i) {
+ for (int j = 0; j < blocks.size(); ++j) {
+ byte[] ecBytes = ((BlockPair) blocks.elementAt(j)).getErrorCorrectionBytes();
+ if (i < ecBytes.length) {
+ result.appendBits(ecBytes[i], 8);
+ }
+ }
+ }
+ if (numTotalBytes != result.getSizeInBytes()) { // Should be same.
+ throw new WriterException("Interleaving error: " + numTotalBytes + " and " +
+ result.getSizeInBytes() + " differ.");
+ }
+ }
+
+ static byte[] generateECBytes(byte[] dataBytes, int numEcBytesInBlock) {
+ int numDataBytes = dataBytes.length;
+ int[] toEncode = new int[numDataBytes + numEcBytesInBlock];
+ for (int i = 0; i < numDataBytes; i++) {
+ toEncode[i] = dataBytes[i] & 0xFF;
+ }
+ new ReedSolomonEncoder(GenericGF.QR_CODE_FIELD_256).encode(toEncode, numEcBytesInBlock);
+
+ byte[] ecBytes = new byte[numEcBytesInBlock];
+ for (int i = 0; i < numEcBytesInBlock; i++) {
+ ecBytes[i] = (byte) toEncode[numDataBytes + i];
+ }
+ return ecBytes;
+ }
+
+ /**
+ * Append mode info. On success, store the result in "bits".
+ */
+ static void appendModeInfo(Mode mode, BitArray bits) {
+ bits.appendBits(mode.getBits(), 4);
+ }
+
+
+ /**
+ * Append length info. On success, store the result in "bits".
+ */
+ static void appendLengthInfo(int numLetters, int version, Mode mode, BitArray bits)
+ throws WriterException {
+ int numBits = mode.getCharacterCountBits(Version.getVersionForNumber(version));
+ if (numLetters > ((1 << numBits) - 1)) {
+ throw new WriterException(numLetters + "is bigger than" + ((1 << numBits) - 1));
+ }
+ bits.appendBits(numLetters, numBits);
+ }
+
+ /**
+ * Append "bytes" in "mode" mode (encoding) into "bits". On success, store the result in "bits".
+ */
+ static void appendBytes(String content, Mode mode, BitArray bits, String encoding)
+ throws WriterException {
+ if (mode.equals(Mode.NUMERIC)) {
+ appendNumericBytes(content, bits);
+ } else if (mode.equals(Mode.ALPHANUMERIC)) {
+ appendAlphanumericBytes(content, bits);
+ } else if (mode.equals(Mode.BYTE)) {
+ append8BitBytes(content, bits, encoding);
+ } else if (mode.equals(Mode.KANJI)) {
+ appendKanjiBytes(content, bits);
+ } else {
+ throw new WriterException("Invalid mode: " + mode);
+ }
+ }
+
+ static void appendNumericBytes(String content, BitArray bits) {
+ int length = content.length();
+ int i = 0;
+ while (i < length) {
+ int num1 = content.charAt(i) - '0';
+ if (i + 2 < length) {
+ // Encode three numeric letters in ten bits.
+ int num2 = content.charAt(i + 1) - '0';
+ int num3 = content.charAt(i + 2) - '0';
+ bits.appendBits(num1 * 100 + num2 * 10 + num3, 10);
+ i += 3;
+ } else if (i + 1 < length) {
+ // Encode two numeric letters in seven bits.
+ int num2 = content.charAt(i + 1) - '0';
+ bits.appendBits(num1 * 10 + num2, 7);
+ i += 2;
+ } else {
+ // Encode one numeric letter in four bits.
+ bits.appendBits(num1, 4);
+ i++;
+ }
+ }
+ }
+
+ static void appendAlphanumericBytes(String content, BitArray bits) throws WriterException {
+ int length = content.length();
+ int i = 0;
+ while (i < length) {
+ int code1 = getAlphanumericCode(content.charAt(i));
+ if (code1 == -1) {
+ throw new WriterException();
+ }
+ if (i + 1 < length) {
+ int code2 = getAlphanumericCode(content.charAt(i + 1));
+ if (code2 == -1) {
+ throw new WriterException();
+ }
+ // Encode two alphanumeric letters in 11 bits.
+ bits.appendBits(code1 * 45 + code2, 11);
+ i += 2;
+ } else {
+ // Encode one alphanumeric letter in six bits.
+ bits.appendBits(code1, 6);
+ i++;
+ }
+ }
+ }
+
+ static void append8BitBytes(String content, BitArray bits, String encoding)
+ throws WriterException {
+ byte[] bytes;
+ try {
+ bytes = content.getBytes(encoding);
+ } catch (UnsupportedEncodingException uee) {
+ throw new WriterException(uee.toString());
+ }
+ for (int i = 0; i < bytes.length; ++i) {
+ bits.appendBits(bytes[i], 8);
+ }
+ }
+
+ static void appendKanjiBytes(String content, BitArray bits) throws WriterException {
+ byte[] bytes;
+ try {
+ bytes = content.getBytes("Shift_JIS");
+ } catch (UnsupportedEncodingException uee) {
+ throw new WriterException(uee.toString());
+ }
+ int length = bytes.length;
+ for (int i = 0; i < length; i += 2) {
+ int byte1 = bytes[i] & 0xFF;
+ int byte2 = bytes[i + 1] & 0xFF;
+ int code = (byte1 << 8) | byte2;
+ int subtracted = -1;
+ if (code >= 0x8140 && code <= 0x9ffc) {
+ subtracted = code - 0x8140;
+ } else if (code >= 0xe040 && code <= 0xebbf) {
+ subtracted = code - 0xc140;
+ }
+ if (subtracted == -1) {
+ throw new WriterException("Invalid byte sequence");
+ }
+ int encoded = ((subtracted >> 8) * 0xc0) + (subtracted & 0xff);
+ bits.appendBits(encoded, 13);
+ }
+ }
+
+ private static void appendECI(ECI eci, BitArray bits) {
+ bits.appendBits(Mode.ECI.getBits(), 4);
+ // This is correct for values up to 127, which is all we need now.
+ bits.appendBits(eci.getValue(), 8);
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/encoder/MaskUtil.java b/libraries/zxing/src/com/google/zxing/qrcode/encoder/MaskUtil.java
new file mode 100644
index 000000000..61ccf48c1
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/encoder/MaskUtil.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright 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.qrcode.encoder;
+
+/**
+ * @author satorux@google.com (Satoru Takabayashi) - creator
+ * @author dswitkin@google.com (Daniel Switkin) - ported from C++
+ */
+public final class MaskUtil {
+
+ private MaskUtil() {
+ // do nothing
+ }
+
+ // Apply mask penalty rule 1 and return the penalty. Find repetitive cells with the same color and
+ // give penalty to them. Example: 00000 or 11111.
+ public static int applyMaskPenaltyRule1(ByteMatrix matrix) {
+ return applyMaskPenaltyRule1Internal(matrix, true) + applyMaskPenaltyRule1Internal(matrix, false);
+ }
+
+ // Apply mask penalty rule 2 and return the penalty. Find 2x2 blocks with the same color and give
+ // penalty to them.
+ public static int applyMaskPenaltyRule2(ByteMatrix matrix) {
+ int penalty = 0;
+ byte[][] array = matrix.getArray();
+ int width = matrix.getWidth();
+ int height = matrix.getHeight();
+ for (int y = 0; y < height - 1; ++y) {
+ for (int x = 0; x < width - 1; ++x) {
+ int value = array[y][x];
+ if (value == array[y][x + 1] && value == array[y + 1][x] && value == array[y + 1][x + 1]) {
+ penalty += 3;
+ }
+ }
+ }
+ return penalty;
+ }
+
+ // Apply mask penalty rule 3 and return the penalty. Find consecutive cells of 00001011101 or
+ // 10111010000, and give penalty to them. If we find patterns like 000010111010000, we give
+ // penalties twice (i.e. 40 * 2).
+ public static int applyMaskPenaltyRule3(ByteMatrix matrix) {
+ int penalty = 0;
+ byte[][] array = matrix.getArray();
+ int width = matrix.getWidth();
+ int height = matrix.getHeight();
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ // Tried to simplify following conditions but failed.
+ if (x + 6 < width &&
+ array[y][x] == 1 &&
+ array[y][x + 1] == 0 &&
+ array[y][x + 2] == 1 &&
+ array[y][x + 3] == 1 &&
+ array[y][x + 4] == 1 &&
+ array[y][x + 5] == 0 &&
+ array[y][x + 6] == 1 &&
+ ((x + 10 < width &&
+ array[y][x + 7] == 0 &&
+ array[y][x + 8] == 0 &&
+ array[y][x + 9] == 0 &&
+ array[y][x + 10] == 0) ||
+ (x - 4 >= 0 &&
+ array[y][x - 1] == 0 &&
+ array[y][x - 2] == 0 &&
+ array[y][x - 3] == 0 &&
+ array[y][x - 4] == 0))) {
+ penalty += 40;
+ }
+ if (y + 6 < height &&
+ array[y][x] == 1 &&
+ array[y + 1][x] == 0 &&
+ array[y + 2][x] == 1 &&
+ array[y + 3][x] == 1 &&
+ array[y + 4][x] == 1 &&
+ array[y + 5][x] == 0 &&
+ array[y + 6][x] == 1 &&
+ ((y + 10 < height &&
+ array[y + 7][x] == 0 &&
+ array[y + 8][x] == 0 &&
+ array[y + 9][x] == 0 &&
+ array[y + 10][x] == 0) ||
+ (y - 4 >= 0 &&
+ array[y - 1][x] == 0 &&
+ array[y - 2][x] == 0 &&
+ array[y - 3][x] == 0 &&
+ array[y - 4][x] == 0))) {
+ penalty += 40;
+ }
+ }
+ }
+ return penalty;
+ }
+
+ // Apply mask penalty rule 4 and return the penalty. Calculate the ratio of dark cells and give
+ // penalty if the ratio is far from 50%. It gives 10 penalty for 5% distance. Examples:
+ // - 0% => 100
+ // - 40% => 20
+ // - 45% => 10
+ // - 50% => 0
+ // - 55% => 10
+ // - 55% => 20
+ // - 100% => 100
+ public static int applyMaskPenaltyRule4(ByteMatrix matrix) {
+ int numDarkCells = 0;
+ byte[][] array = matrix.getArray();
+ int width = matrix.getWidth();
+ int height = matrix.getHeight();
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ if (array[y][x] == 1) {
+ numDarkCells += 1;
+ }
+ }
+ }
+ int numTotalCells = matrix.getHeight() * matrix.getWidth();
+ double darkRatio = (double) numDarkCells / numTotalCells;
+ return Math.abs((int) (darkRatio * 100 - 50)) / 5 * 10;
+ }
+
+ // Return the mask bit for "getMaskPattern" at "x" and "y". See 8.8 of JISX0510:2004 for mask
+ // pattern conditions.
+ public static boolean getDataMaskBit(int maskPattern, int x, int y) {
+ if (!QRCode.isValidMaskPattern(maskPattern)) {
+ throw new IllegalArgumentException("Invalid mask pattern");
+ }
+ int intermediate;
+ int temp;
+ switch (maskPattern) {
+ case 0:
+ intermediate = (y + x) & 0x1;
+ break;
+ case 1:
+ intermediate = y & 0x1;
+ break;
+ case 2:
+ intermediate = x % 3;
+ break;
+ case 3:
+ intermediate = (y + x) % 3;
+ break;
+ case 4:
+ intermediate = ((y >>> 1) + (x / 3)) & 0x1;
+ break;
+ case 5:
+ temp = y * x;
+ intermediate = (temp & 0x1) + (temp % 3);
+ break;
+ case 6:
+ temp = y * x;
+ intermediate = ((temp & 0x1) + (temp % 3)) & 0x1;
+ break;
+ case 7:
+ temp = y * x;
+ intermediate = ((temp % 3) + ((y + x) & 0x1)) & 0x1;
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid mask pattern: " + maskPattern);
+ }
+ return intermediate == 0;
+ }
+
+ // Helper function for applyMaskPenaltyRule1. We need this for doing this calculation in both
+ // vertical and horizontal orders respectively.
+ private static int applyMaskPenaltyRule1Internal(ByteMatrix matrix, boolean isHorizontal) {
+ int penalty = 0;
+ int numSameBitCells = 0;
+ int prevBit = -1;
+ // Horizontal mode:
+ // for (int i = 0; i < matrix.height(); ++i) {
+ // for (int j = 0; j < matrix.width(); ++j) {
+ // int bit = matrix.get(i, j);
+ // Vertical mode:
+ // for (int i = 0; i < matrix.width(); ++i) {
+ // for (int j = 0; j < matrix.height(); ++j) {
+ // int bit = matrix.get(j, i);
+ int iLimit = isHorizontal ? matrix.getHeight() : matrix.getWidth();
+ int jLimit = isHorizontal ? matrix.getWidth() : matrix.getHeight();
+ byte[][] array = matrix.getArray();
+ for (int i = 0; i < iLimit; ++i) {
+ for (int j = 0; j < jLimit; ++j) {
+ int bit = isHorizontal ? array[i][j] : array[j][i];
+ if (bit == prevBit) {
+ numSameBitCells += 1;
+ // Found five repetitive cells with the same color (bit).
+ // We'll give penalty of 3.
+ if (numSameBitCells == 5) {
+ penalty += 3;
+ } else if (numSameBitCells > 5) {
+ // After five repetitive cells, we'll add the penalty one
+ // by one.
+ penalty += 1;
+ }
+ } else {
+ numSameBitCells = 1; // Include the cell itself.
+ prevBit = bit;
+ }
+ }
+ numSameBitCells = 0; // Clear at each row/column.
+ }
+ return penalty;
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/encoder/MatrixUtil.java b/libraries/zxing/src/com/google/zxing/qrcode/encoder/MatrixUtil.java
new file mode 100644
index 000000000..3d434e675
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/encoder/MatrixUtil.java
@@ -0,0 +1,524 @@
+/*
+ * Copyright 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.qrcode.encoder;
+
+import com.google.zxing.WriterException;
+import com.google.zxing.common.BitArray;
+import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
+
+/**
+ * @author satorux@google.com (Satoru Takabayashi) - creator
+ * @author dswitkin@google.com (Daniel Switkin) - ported from C++
+ */
+public final class MatrixUtil {
+
+ private MatrixUtil() {
+ // do nothing
+ }
+
+ private static final int[][] POSITION_DETECTION_PATTERN = {
+ {1, 1, 1, 1, 1, 1, 1},
+ {1, 0, 0, 0, 0, 0, 1},
+ {1, 0, 1, 1, 1, 0, 1},
+ {1, 0, 1, 1, 1, 0, 1},
+ {1, 0, 1, 1, 1, 0, 1},
+ {1, 0, 0, 0, 0, 0, 1},
+ {1, 1, 1, 1, 1, 1, 1},
+ };
+
+ private static final int[][] HORIZONTAL_SEPARATION_PATTERN = {
+ {0, 0, 0, 0, 0, 0, 0, 0},
+ };
+
+ private static final int[][] VERTICAL_SEPARATION_PATTERN = {
+ {0}, {0}, {0}, {0}, {0}, {0}, {0},
+ };
+
+ private static final int[][] POSITION_ADJUSTMENT_PATTERN = {
+ {1, 1, 1, 1, 1},
+ {1, 0, 0, 0, 1},
+ {1, 0, 1, 0, 1},
+ {1, 0, 0, 0, 1},
+ {1, 1, 1, 1, 1},
+ };
+
+ // From Appendix E. Table 1, JIS0510X:2004 (p 71). The table was double-checked by komatsu.
+ private static final int[][] POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE = {
+ {-1, -1, -1, -1, -1, -1, -1}, // Version 1
+ { 6, 18, -1, -1, -1, -1, -1}, // Version 2
+ { 6, 22, -1, -1, -1, -1, -1}, // Version 3
+ { 6, 26, -1, -1, -1, -1, -1}, // Version 4
+ { 6, 30, -1, -1, -1, -1, -1}, // Version 5
+ { 6, 34, -1, -1, -1, -1, -1}, // Version 6
+ { 6, 22, 38, -1, -1, -1, -1}, // Version 7
+ { 6, 24, 42, -1, -1, -1, -1}, // Version 8
+ { 6, 26, 46, -1, -1, -1, -1}, // Version 9
+ { 6, 28, 50, -1, -1, -1, -1}, // Version 10
+ { 6, 30, 54, -1, -1, -1, -1}, // Version 11
+ { 6, 32, 58, -1, -1, -1, -1}, // Version 12
+ { 6, 34, 62, -1, -1, -1, -1}, // Version 13
+ { 6, 26, 46, 66, -1, -1, -1}, // Version 14
+ { 6, 26, 48, 70, -1, -1, -1}, // Version 15
+ { 6, 26, 50, 74, -1, -1, -1}, // Version 16
+ { 6, 30, 54, 78, -1, -1, -1}, // Version 17
+ { 6, 30, 56, 82, -1, -1, -1}, // Version 18
+ { 6, 30, 58, 86, -1, -1, -1}, // Version 19
+ { 6, 34, 62, 90, -1, -1, -1}, // Version 20
+ { 6, 28, 50, 72, 94, -1, -1}, // Version 21
+ { 6, 26, 50, 74, 98, -1, -1}, // Version 22
+ { 6, 30, 54, 78, 102, -1, -1}, // Version 23
+ { 6, 28, 54, 80, 106, -1, -1}, // Version 24
+ { 6, 32, 58, 84, 110, -1, -1}, // Version 25
+ { 6, 30, 58, 86, 114, -1, -1}, // Version 26
+ { 6, 34, 62, 90, 118, -1, -1}, // Version 27
+ { 6, 26, 50, 74, 98, 122, -1}, // Version 28
+ { 6, 30, 54, 78, 102, 126, -1}, // Version 29
+ { 6, 26, 52, 78, 104, 130, -1}, // Version 30
+ { 6, 30, 56, 82, 108, 134, -1}, // Version 31
+ { 6, 34, 60, 86, 112, 138, -1}, // Version 32
+ { 6, 30, 58, 86, 114, 142, -1}, // Version 33
+ { 6, 34, 62, 90, 118, 146, -1}, // Version 34
+ { 6, 30, 54, 78, 102, 126, 150}, // Version 35
+ { 6, 24, 50, 76, 102, 128, 154}, // Version 36
+ { 6, 28, 54, 80, 106, 132, 158}, // Version 37
+ { 6, 32, 58, 84, 110, 136, 162}, // Version 38
+ { 6, 26, 54, 82, 110, 138, 166}, // Version 39
+ { 6, 30, 58, 86, 114, 142, 170}, // Version 40
+ };
+
+ // Type info cells at the left top corner.
+ private static final int[][] TYPE_INFO_COORDINATES = {
+ {8, 0},
+ {8, 1},
+ {8, 2},
+ {8, 3},
+ {8, 4},
+ {8, 5},
+ {8, 7},
+ {8, 8},
+ {7, 8},
+ {5, 8},
+ {4, 8},
+ {3, 8},
+ {2, 8},
+ {1, 8},
+ {0, 8},
+ };
+
+ // From Appendix D in JISX0510:2004 (p. 67)
+ private static final int VERSION_INFO_POLY = 0x1f25; // 1 1111 0010 0101
+
+ // From Appendix C in JISX0510:2004 (p.65).
+ private static final int TYPE_INFO_POLY = 0x537;
+ private static final int TYPE_INFO_MASK_PATTERN = 0x5412;
+
+ // Set all cells to -1. -1 means that the cell is empty (not set yet).
+ //
+ // JAVAPORT: We shouldn't need to do this at all. The code should be rewritten to begin encoding
+ // with the ByteMatrix initialized all to zero.
+ public static void clearMatrix(ByteMatrix matrix) {
+ matrix.clear((byte) -1);
+ }
+
+ // Build 2D matrix of QR Code from "dataBits" with "ecLevel", "version" and "getMaskPattern". On
+ // success, store the result in "matrix" and return true.
+ public static void buildMatrix(BitArray dataBits, ErrorCorrectionLevel ecLevel, int version,
+ int maskPattern, ByteMatrix matrix) throws WriterException {
+ clearMatrix(matrix);
+ embedBasicPatterns(version, matrix);
+ // Type information appear with any version.
+ embedTypeInfo(ecLevel, maskPattern, matrix);
+ // Version info appear if version >= 7.
+ maybeEmbedVersionInfo(version, matrix);
+ // Data should be embedded at end.
+ embedDataBits(dataBits, maskPattern, matrix);
+ }
+
+ // Embed basic patterns. On success, modify the matrix and return true.
+ // The basic patterns are:
+ // - Position detection patterns
+ // - Timing patterns
+ // - Dark dot at the left bottom corner
+ // - Position adjustment patterns, if need be
+ public static void embedBasicPatterns(int version, ByteMatrix matrix) throws WriterException {
+ // Let's get started with embedding big squares at corners.
+ embedPositionDetectionPatternsAndSeparators(matrix);
+ // Then, embed the dark dot at the left bottom corner.
+ embedDarkDotAtLeftBottomCorner(matrix);
+
+ // Position adjustment patterns appear if version >= 2.
+ maybeEmbedPositionAdjustmentPatterns(version, matrix);
+ // Timing patterns should be embedded after position adj. patterns.
+ embedTimingPatterns(matrix);
+ }
+
+ // Embed type information. On success, modify the matrix.
+ public static void embedTypeInfo(ErrorCorrectionLevel ecLevel, int maskPattern, ByteMatrix matrix)
+ throws WriterException {
+ BitArray typeInfoBits = new BitArray();
+ makeTypeInfoBits(ecLevel, maskPattern, typeInfoBits);
+
+ for (int i = 0; i < typeInfoBits.getSize(); ++i) {
+ // Place bits in LSB to MSB order. LSB (least significant bit) is the last value in
+ // "typeInfoBits".
+ boolean bit = typeInfoBits.get(typeInfoBits.getSize() - 1 - i);
+
+ // Type info bits at the left top corner. See 8.9 of JISX0510:2004 (p.46).
+ int x1 = TYPE_INFO_COORDINATES[i][0];
+ int y1 = TYPE_INFO_COORDINATES[i][1];
+ matrix.set(x1, y1, bit);
+
+ if (i < 8) {
+ // Right top corner.
+ int x2 = matrix.getWidth() - i - 1;
+ int y2 = 8;
+ matrix.set(x2, y2, bit);
+ } else {
+ // Left bottom corner.
+ int x2 = 8;
+ int y2 = matrix.getHeight() - 7 + (i - 8);
+ matrix.set(x2, y2, bit);
+ }
+ }
+ }
+
+ // Embed version information if need be. On success, modify the matrix and return true.
+ // See 8.10 of JISX0510:2004 (p.47) for how to embed version information.
+ public static void maybeEmbedVersionInfo(int version, ByteMatrix matrix) throws WriterException {
+ if (version < 7) { // Version info is necessary if version >= 7.
+ return; // Don't need version info.
+ }
+ BitArray versionInfoBits = new BitArray();
+ makeVersionInfoBits(version, versionInfoBits);
+
+ int bitIndex = 6 * 3 - 1; // It will decrease from 17 to 0.
+ for (int i = 0; i < 6; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ // Place bits in LSB (least significant bit) to MSB order.
+ boolean bit = versionInfoBits.get(bitIndex);
+ bitIndex--;
+ // Left bottom corner.
+ matrix.set(i, matrix.getHeight() - 11 + j, bit);
+ // Right bottom corner.
+ matrix.set(matrix.getHeight() - 11 + j, i, bit);
+ }
+ }
+ }
+
+ // Embed "dataBits" using "getMaskPattern". On success, modify the matrix and return true.
+ // For debugging purposes, it skips masking process if "getMaskPattern" is -1.
+ // See 8.7 of JISX0510:2004 (p.38) for how to embed data bits.
+ public static void embedDataBits(BitArray dataBits, int maskPattern, ByteMatrix matrix)
+ throws WriterException {
+ int bitIndex = 0;
+ int direction = -1;
+ // Start from the right bottom cell.
+ int x = matrix.getWidth() - 1;
+ int y = matrix.getHeight() - 1;
+ while (x > 0) {
+ // Skip the vertical timing pattern.
+ if (x == 6) {
+ x -= 1;
+ }
+ while (y >= 0 && y < matrix.getHeight()) {
+ for (int i = 0; i < 2; ++i) {
+ int xx = x - i;
+ // Skip the cell if it's not empty.
+ if (!isEmpty(matrix.get(xx, y))) {
+ continue;
+ }
+ boolean bit;
+ if (bitIndex < dataBits.getSize()) {
+ bit = dataBits.get(bitIndex);
+ ++bitIndex;
+ } else {
+ // Padding bit. If there is no bit left, we'll fill the left cells with 0, as described
+ // in 8.4.9 of JISX0510:2004 (p. 24).
+ bit = false;
+ }
+
+ // Skip masking if mask_pattern is -1.
+ if (maskPattern != -1) {
+ if (MaskUtil.getDataMaskBit(maskPattern, xx, y)) {
+ bit = !bit;
+ }
+ }
+ matrix.set(xx, y, bit);
+ }
+ y += direction;
+ }
+ direction = -direction; // Reverse the direction.
+ y += direction;
+ x -= 2; // Move to the left.
+ }
+ // All bits should be consumed.
+ if (bitIndex != dataBits.getSize()) {
+ throw new WriterException("Not all bits consumed: " + bitIndex + '/' + dataBits.getSize());
+ }
+ }
+
+ // Return the position of the most significant bit set (to one) in the "value". The most
+ // significant bit is position 32. If there is no bit set, return 0. Examples:
+ // - findMSBSet(0) => 0
+ // - findMSBSet(1) => 1
+ // - findMSBSet(255) => 8
+ public static int findMSBSet(int value) {
+ int numDigits = 0;
+ while (value != 0) {
+ value >>>= 1;
+ ++numDigits;
+ }
+ return numDigits;
+ }
+
+ // Calculate BCH (Bose-Chaudhuri-Hocquenghem) code for "value" using polynomial "poly". The BCH
+ // code is used for encoding type information and version information.
+ // Example: Calculation of version information of 7.
+ // f(x) is created from 7.
+ // - 7 = 000111 in 6 bits
+ // - f(x) = x^2 + x^1 + x^0
+ // g(x) is given by the standard (p. 67)
+ // - g(x) = x^12 + x^11 + x^10 + x^9 + x^8 + x^5 + x^2 + 1
+ // Multiply f(x) by x^(18 - 6)
+ // - f'(x) = f(x) * x^(18 - 6)
+ // - f'(x) = x^14 + x^13 + x^12
+ // Calculate the remainder of f'(x) / g(x)
+ // x^2
+ // __________________________________________________
+ // g(x) )x^14 + x^13 + x^12
+ // x^14 + x^13 + x^12 + x^11 + x^10 + x^7 + x^4 + x^2
+ // --------------------------------------------------
+ // x^11 + x^10 + x^7 + x^4 + x^2
+ //
+ // The remainder is x^11 + x^10 + x^7 + x^4 + x^2
+ // Encode it in binary: 110010010100
+ // The return value is 0xc94 (1100 1001 0100)
+ //
+ // Since all coefficients in the polynomials are 1 or 0, we can do the calculation by bit
+ // operations. We don't care if cofficients are positive or negative.
+ public static int calculateBCHCode(int value, int poly) {
+ // If poly is "1 1111 0010 0101" (version info poly), msbSetInPoly is 13. We'll subtract 1
+ // from 13 to make it 12.
+ int msbSetInPoly = findMSBSet(poly);
+ value <<= msbSetInPoly - 1;
+ // Do the division business using exclusive-or operations.
+ while (findMSBSet(value) >= msbSetInPoly) {
+ value ^= poly << (findMSBSet(value) - msbSetInPoly);
+ }
+ // Now the "value" is the remainder (i.e. the BCH code)
+ return value;
+ }
+
+ // Make bit vector of type information. On success, store the result in "bits" and return true.
+ // Encode error correction level and mask pattern. See 8.9 of
+ // JISX0510:2004 (p.45) for details.
+ public static void makeTypeInfoBits(ErrorCorrectionLevel ecLevel, int maskPattern, BitArray bits)
+ throws WriterException {
+ if (!QRCode.isValidMaskPattern(maskPattern)) {
+ throw new WriterException("Invalid mask pattern");
+ }
+ int typeInfo = (ecLevel.getBits() << 3) | maskPattern;
+ bits.appendBits(typeInfo, 5);
+
+ int bchCode = calculateBCHCode(typeInfo, TYPE_INFO_POLY);
+ bits.appendBits(bchCode, 10);
+
+ BitArray maskBits = new BitArray();
+ maskBits.appendBits(TYPE_INFO_MASK_PATTERN, 15);
+ bits.xor(maskBits);
+
+ if (bits.getSize() != 15) { // Just in case.
+ throw new WriterException("should not happen but we got: " + bits.getSize());
+ }
+ }
+
+ // Make bit vector of version information. On success, store the result in "bits" and return true.
+ // See 8.10 of JISX0510:2004 (p.45) for details.
+ public static void makeVersionInfoBits(int version, BitArray bits) throws WriterException {
+ bits.appendBits(version, 6);
+ int bchCode = calculateBCHCode(version, VERSION_INFO_POLY);
+ bits.appendBits(bchCode, 12);
+
+ if (bits.getSize() != 18) { // Just in case.
+ throw new WriterException("should not happen but we got: " + bits.getSize());
+ }
+ }
+
+ // Check if "value" is empty.
+ private static boolean isEmpty(int value) {
+ return value == -1;
+ }
+
+ // Check if "value" is valid.
+ private static boolean isValidValue(int value) {
+ return value == -1 || // Empty.
+ value == 0 || // Light (white).
+ value == 1; // Dark (black).
+ }
+
+ private static void embedTimingPatterns(ByteMatrix matrix) throws WriterException {
+ // -8 is for skipping position detection patterns (size 7), and two horizontal/vertical
+ // separation patterns (size 1). Thus, 8 = 7 + 1.
+ for (int i = 8; i < matrix.getWidth() - 8; ++i) {
+ int bit = (i + 1) % 2;
+ // Horizontal line.
+ if (!isValidValue(matrix.get(i, 6))) {
+ throw new WriterException();
+ }
+ if (isEmpty(matrix.get(i, 6))) {
+ matrix.set(i, 6, bit);
+ }
+ // Vertical line.
+ if (!isValidValue(matrix.get(6, i))) {
+ throw new WriterException();
+ }
+ if (isEmpty(matrix.get(6, i))) {
+ matrix.set(6, i, bit);
+ }
+ }
+ }
+
+ // Embed the lonely dark dot at left bottom corner. JISX0510:2004 (p.46)
+ private static void embedDarkDotAtLeftBottomCorner(ByteMatrix matrix) throws WriterException {
+ if (matrix.get(8, matrix.getHeight() - 8) == 0) {
+ throw new WriterException();
+ }
+ matrix.set(8, matrix.getHeight() - 8, 1);
+ }
+
+ private static void embedHorizontalSeparationPattern(int xStart, int yStart,
+ ByteMatrix matrix) throws WriterException {
+ // We know the width and height.
+ if (HORIZONTAL_SEPARATION_PATTERN[0].length != 8 || HORIZONTAL_SEPARATION_PATTERN.length != 1) {
+ throw new WriterException("Bad horizontal separation pattern");
+ }
+ for (int x = 0; x < 8; ++x) {
+ if (!isEmpty(matrix.get(xStart + x, yStart))) {
+ throw new WriterException();
+ }
+ matrix.set(xStart + x, yStart, HORIZONTAL_SEPARATION_PATTERN[0][x]);
+ }
+ }
+
+ private static void embedVerticalSeparationPattern(int xStart, int yStart,
+ ByteMatrix matrix) throws WriterException {
+ // We know the width and height.
+ if (VERTICAL_SEPARATION_PATTERN[0].length != 1 || VERTICAL_SEPARATION_PATTERN.length != 7) {
+ throw new WriterException("Bad vertical separation pattern");
+ }
+ for (int y = 0; y < 7; ++y) {
+ if (!isEmpty(matrix.get(xStart, yStart + y))) {
+ throw new WriterException();
+ }
+ matrix.set(xStart, yStart + y, VERTICAL_SEPARATION_PATTERN[y][0]);
+ }
+ }
+
+ // Note that we cannot unify the function with embedPositionDetectionPattern() despite they are
+ // almost identical, since we cannot write a function that takes 2D arrays in different sizes in
+ // C/C++. We should live with the fact.
+ private static void embedPositionAdjustmentPattern(int xStart, int yStart,
+ ByteMatrix matrix) throws WriterException {
+ // We know the width and height.
+ if (POSITION_ADJUSTMENT_PATTERN[0].length != 5 || POSITION_ADJUSTMENT_PATTERN.length != 5) {
+ throw new WriterException("Bad position adjustment");
+ }
+ for (int y = 0; y < 5; ++y) {
+ for (int x = 0; x < 5; ++x) {
+ if (!isEmpty(matrix.get(xStart + x, yStart + y))) {
+ throw new WriterException();
+ }
+ matrix.set(xStart + x, yStart + y, POSITION_ADJUSTMENT_PATTERN[y][x]);
+ }
+ }
+ }
+
+ private static void embedPositionDetectionPattern(int xStart, int yStart,
+ ByteMatrix matrix) throws WriterException {
+ // We know the width and height.
+ if (POSITION_DETECTION_PATTERN[0].length != 7 || POSITION_DETECTION_PATTERN.length != 7) {
+ throw new WriterException("Bad position detection pattern");
+ }
+ for (int y = 0; y < 7; ++y) {
+ for (int x = 0; x < 7; ++x) {
+ if (!isEmpty(matrix.get(xStart + x, yStart + y))) {
+ throw new WriterException();
+ }
+ matrix.set(xStart + x, yStart + y, POSITION_DETECTION_PATTERN[y][x]);
+ }
+ }
+ }
+
+ // Embed position detection patterns and surrounding vertical/horizontal separators.
+ private static void embedPositionDetectionPatternsAndSeparators(ByteMatrix matrix) throws WriterException {
+ // Embed three big squares at corners.
+ int pdpWidth = POSITION_DETECTION_PATTERN[0].length;
+ // Left top corner.
+ embedPositionDetectionPattern(0, 0, matrix);
+ // Right top corner.
+ embedPositionDetectionPattern(matrix.getWidth() - pdpWidth, 0, matrix);
+ // Left bottom corner.
+ embedPositionDetectionPattern(0, matrix.getWidth() - pdpWidth, matrix);
+
+ // Embed horizontal separation patterns around the squares.
+ int hspWidth = HORIZONTAL_SEPARATION_PATTERN[0].length;
+ // Left top corner.
+ embedHorizontalSeparationPattern(0, hspWidth - 1, matrix);
+ // Right top corner.
+ embedHorizontalSeparationPattern(matrix.getWidth() - hspWidth,
+ hspWidth - 1, matrix);
+ // Left bottom corner.
+ embedHorizontalSeparationPattern(0, matrix.getWidth() - hspWidth, matrix);
+
+ // Embed vertical separation patterns around the squares.
+ int vspSize = VERTICAL_SEPARATION_PATTERN.length;
+ // Left top corner.
+ embedVerticalSeparationPattern(vspSize, 0, matrix);
+ // Right top corner.
+ embedVerticalSeparationPattern(matrix.getHeight() - vspSize - 1, 0, matrix);
+ // Left bottom corner.
+ embedVerticalSeparationPattern(vspSize, matrix.getHeight() - vspSize,
+ matrix);
+ }
+
+ // Embed position adjustment patterns if need be.
+ private static void maybeEmbedPositionAdjustmentPatterns(int version, ByteMatrix matrix)
+ throws WriterException {
+ if (version < 2) { // The patterns appear if version >= 2
+ return;
+ }
+ int index = version - 1;
+ int[] coordinates = POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[index];
+ int numCoordinates = POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[index].length;
+ for (int i = 0; i < numCoordinates; ++i) {
+ for (int j = 0; j < numCoordinates; ++j) {
+ int y = coordinates[i];
+ int x = coordinates[j];
+ if (x == -1 || y == -1) {
+ continue;
+ }
+ // If the cell is unset, we embed the position adjustment pattern here.
+ if (isEmpty(matrix.get(x, y))) {
+ // -2 is necessary since the x/y coordinates point to the center of the pattern, not the
+ // left top corner.
+ embedPositionAdjustmentPattern(x - 2, y - 2, matrix);
+ }
+ }
+ }
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/encoder/QRCode.java b/libraries/zxing/src/com/google/zxing/qrcode/encoder/QRCode.java
new file mode 100644
index 000000000..05c818513
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/encoder/QRCode.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright 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.qrcode.encoder;
+
+import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
+import com.google.zxing.qrcode.decoder.Mode;
+
+/**
+ * @author satorux@google.com (Satoru Takabayashi) - creator
+ * @author dswitkin@google.com (Daniel Switkin) - ported from C++
+ */
+public final class QRCode {
+
+ public static final int NUM_MASK_PATTERNS = 8;
+
+ private Mode mode;
+ private ErrorCorrectionLevel ecLevel;
+ private int version;
+ private int matrixWidth;
+ private int maskPattern;
+ private int numTotalBytes;
+ private int numDataBytes;
+ private int numECBytes;
+ private int numRSBlocks;
+ private ByteMatrix matrix;
+
+ public QRCode() {
+ mode = null;
+ ecLevel = null;
+ version = -1;
+ matrixWidth = -1;
+ maskPattern = -1;
+ numTotalBytes = -1;
+ numDataBytes = -1;
+ numECBytes = -1;
+ numRSBlocks = -1;
+ matrix = null;
+ }
+
+ // Mode of the QR Code.
+ public Mode getMode() {
+ return mode;
+ }
+
+ // Error correction level of the QR Code.
+ public ErrorCorrectionLevel getECLevel() {
+ return ecLevel;
+ }
+
+ // Version of the QR Code. The bigger size, the bigger version.
+ public int getVersion() {
+ return version;
+ }
+
+ // ByteMatrix width of the QR Code.
+ public int getMatrixWidth() {
+ return matrixWidth;
+ }
+
+ // Mask pattern of the QR Code.
+ public int getMaskPattern() {
+ return maskPattern;
+ }
+
+ // Number of total bytes in the QR Code.
+ public int getNumTotalBytes() {
+ return numTotalBytes;
+ }
+
+ // Number of data bytes in the QR Code.
+ public int getNumDataBytes() {
+ return numDataBytes;
+ }
+
+ // Number of error correction bytes in the QR Code.
+ public int getNumECBytes() {
+ return numECBytes;
+ }
+
+ // Number of Reedsolomon blocks in the QR Code.
+ public int getNumRSBlocks() {
+ return numRSBlocks;
+ }
+
+ // ByteMatrix data of the QR Code.
+ public ByteMatrix getMatrix() {
+ return matrix;
+ }
+
+
+ // Return the value of the module (cell) pointed by "x" and "y" in the matrix of the QR Code. They
+ // call cells in the matrix "modules". 1 represents a black cell, and 0 represents a white cell.
+ public int at(int x, int y) {
+ // The value must be zero or one.
+ int value = matrix.get(x, y);
+ if (!(value == 0 || value == 1)) {
+ // this is really like an assert... not sure what better exception to use?
+ throw new RuntimeException("Bad value");
+ }
+ return value;
+ }
+
+ // Checks all the member variables are set properly. Returns true on success. Otherwise, returns
+ // false.
+ public boolean isValid() {
+ return
+ // First check if all version are not uninitialized.
+ mode != null &&
+ ecLevel != null &&
+ version != -1 &&
+ matrixWidth != -1 &&
+ maskPattern != -1 &&
+ numTotalBytes != -1 &&
+ numDataBytes != -1 &&
+ numECBytes != -1 &&
+ numRSBlocks != -1 &&
+ // Then check them in other ways..
+ isValidMaskPattern(maskPattern) &&
+ numTotalBytes == numDataBytes + numECBytes &&
+ // ByteMatrix stuff.
+ matrix != null &&
+ matrixWidth == matrix.getWidth() &&
+ // See 7.3.1 of JISX0510:2004 (p.5).
+ matrix.getWidth() == matrix.getHeight(); // Must be square.
+ }
+
+ // Return debug String.
+ public String toString() {
+ StringBuffer result = new StringBuffer(200);
+ result.append("<<\n");
+ result.append(" mode: ");
+ result.append(mode);
+ result.append("\n ecLevel: ");
+ result.append(ecLevel);
+ result.append("\n version: ");
+ result.append(version);
+ result.append("\n matrixWidth: ");
+ result.append(matrixWidth);
+ result.append("\n maskPattern: ");
+ result.append(maskPattern);
+ result.append("\n numTotalBytes: ");
+ result.append(numTotalBytes);
+ result.append("\n numDataBytes: ");
+ result.append(numDataBytes);
+ result.append("\n numECBytes: ");
+ result.append(numECBytes);
+ result.append("\n numRSBlocks: ");
+ result.append(numRSBlocks);
+ if (matrix == null) {
+ result.append("\n matrix: null\n");
+ } else {
+ result.append("\n matrix:\n");
+ result.append(matrix.toString());
+ }
+ result.append(">>\n");
+ return result.toString();
+ }
+
+ public void setMode(Mode value) {
+ mode = value;
+ }
+
+ public void setECLevel(ErrorCorrectionLevel value) {
+ ecLevel = value;
+ }
+
+ public void setVersion(int value) {
+ version = value;
+ }
+
+ public void setMatrixWidth(int value) {
+ matrixWidth = value;
+ }
+
+ public void setMaskPattern(int value) {
+ maskPattern = value;
+ }
+
+ public void setNumTotalBytes(int value) {
+ numTotalBytes = value;
+ }
+
+ public void setNumDataBytes(int value) {
+ numDataBytes = value;
+ }
+
+ public void setNumECBytes(int value) {
+ numECBytes = value;
+ }
+
+ public void setNumRSBlocks(int value) {
+ numRSBlocks = value;
+ }
+
+ // This takes ownership of the 2D array.
+ public void setMatrix(ByteMatrix value) {
+ matrix = value;
+ }
+
+ // Check if "mask_pattern" is valid.
+ public static boolean isValidMaskPattern(int maskPattern) {
+ return maskPattern >= 0 && maskPattern < NUM_MASK_PATTERNS;
+ }
+
+ // Return true if the all values in the matrix are binary numbers.
+ //
+ // JAVAPORT: This is going to be super expensive and unnecessary, we should not call this in
+ // production. I'm leaving it because it may be useful for testing. It should be removed entirely
+ // if ByteMatrix is changed never to contain a -1.
+ /*
+ private static boolean EverythingIsBinary(final ByteMatrix matrix) {
+ for (int y = 0; y < matrix.height(); ++y) {
+ for (int x = 0; x < matrix.width(); ++x) {
+ int value = matrix.get(y, x);
+ if (!(value == 0 || value == 1)) {
+ // Found non zero/one value.
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+ */
+
+}
diff --git a/settings.gradle b/settings.gradle
index 3cb8012d3..4dc9b38ab 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -2,3 +2,4 @@ include ':OpenPGP-Keychain'
include ':libraries:ActionBarSherlock'
include ':libraries:HtmlTextView'
include ':libraries:StickyListHeaders:library'
+include ':libraries:zxing'
--
cgit v1.2.3
From 4ea64968afde98fc8df444dbc076549437967a55 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Mon, 6 Jan 2014 22:58:04 +0100
Subject: integrate UnderlineTextView, add zxing info
---
OpenPGP-Keychain/res/layout/stickylist_header.xml | 2 +-
.../keychain/ui/widget/UnderlineTextView.java | 69 ++++++++++++++++++++++
.../stickylistheaders/views/UnderlineTextView.java | 50 ----------------
README.md | 4 ++
4 files changed, 74 insertions(+), 51 deletions(-)
create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/UnderlineTextView.java
delete mode 100644 OpenPGP-Keychain/src/se/emilsjolander/stickylistheaders/views/UnderlineTextView.java
diff --git a/OpenPGP-Keychain/res/layout/stickylist_header.xml b/OpenPGP-Keychain/res/layout/stickylist_header.xml
index cbd65bb72..5768e4153 100644
--- a/OpenPGP-Keychain/res/layout/stickylist_header.xml
+++ b/OpenPGP-Keychain/res/layout/stickylist_header.xml
@@ -3,7 +3,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content" >
-
Date: Tue, 7 Jan 2014 21:02:31 +0100
Subject: key edit fixes
---
OpenPGP-Keychain/AndroidManifest.xml | 1 -
OpenPGP-Keychain/res/menu/key_edit.xml | 17 ++
OpenPGP-Keychain/res/values/strings.xml | 3 +-
.../keychain/provider/ProviderHelper.java | 10 +-
.../keychain/ui/EditKeyActivity.java | 157 +++++++++------
.../keychain/ui/KeyActivity.java | 217 +++++++++++++++++++++
.../keychain/ui/KeyListPublicFragment.java | 22 +--
.../keychain/ui/KeyListSecretActivity.java | 12 +-
.../keychain/ui/KeyListSecretFragment.java | 31 ++-
.../keychain/ui/KeyViewActivity.java | 132 +++++--------
README.md | 2 +-
11 files changed, 412 insertions(+), 192 deletions(-)
create mode 100644 OpenPGP-Keychain/res/menu/key_edit.xml
create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyActivity.java
diff --git a/OpenPGP-Keychain/AndroidManifest.xml b/OpenPGP-Keychain/AndroidManifest.xml
index 8a340708d..fd7edff2a 100644
--- a/OpenPGP-Keychain/AndroidManifest.xml
+++ b/OpenPGP-Keychain/AndroidManifest.xml
@@ -118,7 +118,6 @@
android:name=".ui.EditKeyActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_edit_key"
- android:uiOptions="splitActionBarWhenNarrow"
android:windowSoftInputMode="stateHidden" />
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/values/strings.xml b/OpenPGP-Keychain/res/values/strings.xml
index e837e01dc..0e4f42c59 100644
--- a/OpenPGP-Keychain/res/values/strings.xml
+++ b/OpenPGP-Keychain/res/values/strings.xml
@@ -100,7 +100,8 @@
Copy to clipboardSign keyBeam settings
-
+ Cancel
+
SignMessage
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java
index b07ac53f8..1683c7c0e 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java
@@ -493,7 +493,7 @@ public class ProviderHelper {
*/
public static long getPublicMasterKeyId(Context context, long keyRingRowId) {
Uri queryUri = KeyRings.buildPublicKeyRingsUri(String.valueOf(keyRingRowId));
- return getMasterKeyId(context, queryUri, keyRingRowId);
+ return getMasterKeyId(context, queryUri);
}
/**
@@ -551,7 +551,7 @@ public class ProviderHelper {
*/
public static long getSecretMasterKeyId(Context context, long keyRingRowId) {
Uri queryUri = KeyRings.buildSecretKeyRingsUri(String.valueOf(keyRingRowId));
- return getMasterKeyId(context, queryUri, keyRingRowId);
+ return getMasterKeyId(context, queryUri);
}
/**
@@ -562,7 +562,7 @@ public class ProviderHelper {
* @param keyRingRowId
* @return
*/
- private static long getMasterKeyId(Context context, Uri queryUri, long keyRingRowId) {
+ public static long getMasterKeyId(Context context, Uri queryUri) {
String[] projection = new String[] { KeyRings.MASTER_KEY_ID };
ContentResolver cr = context.getContentResolver();
@@ -592,7 +592,7 @@ public class ProviderHelper {
return getKeyRingsAsArmoredString(context, KeyRings.buildSecretKeyRingsUri(), masterKeyIds);
}
- private static ArrayList getKeyRingsAsArmoredString(Context context, Uri uri,
+ public static ArrayList getKeyRingsAsArmoredString(Context context, Uri uri,
long[] masterKeyIds) {
ArrayList output = new ArrayList();
@@ -661,7 +661,7 @@ public class ProviderHelper {
return getKeyRingsAsByteArray(context, KeyRings.buildSecretKeyRingsUri(), masterKeyIds);
}
- private static byte[] getKeyRingsAsByteArray(Context context, Uri uri, long[] masterKeyIds) {
+ public static byte[] getKeyRingsAsByteArray(Context context, Uri uri, long[] masterKeyIds) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
if (masterKeyIds != null && masterKeyIds.length > 0) {
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
index 4dcaa6d73..c0c81b969 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
@@ -34,6 +34,7 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
+import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment;
import org.sufficientlysecure.keychain.ui.widget.KeyEditor;
@@ -45,6 +46,7 @@ import org.sufficientlysecure.keychain.util.Log;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
+import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -60,9 +62,10 @@ import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.LinearLayout;
import android.widget.Toast;
-import com.actionbarsherlock.app.SherlockFragmentActivity;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
-public class EditKeyActivity extends SherlockFragmentActivity {
+public class EditKeyActivity extends KeyActivity {
// Actions for internal use only:
public static final String ACTION_CREATE_KEY = Constants.INTENT_PREFIX + "CREATE_KEY";
@@ -72,13 +75,14 @@ public class EditKeyActivity extends SherlockFragmentActivity {
public static final String EXTRA_USER_IDS = "user_ids";
public static final String EXTRA_NO_PASSPHRASE = "no_passphrase";
public static final String EXTRA_GENERATE_DEFAULT_KEYS = "generate_default_keys";
- public static final String EXTRA_MASTER_KEY_ID = "master_key_id";
- public static final String EXTRA_MASTER_CAN_SIGN = "master_can_sign";
// results when saving key
public static final String RESULT_EXTRA_MASTER_KEY_ID = "master_key_id";
public static final String RESULT_EXTRA_USER_ID = "user_id";
+ // EDIT
+ private Uri mDataUri;
+
private PGPSecretKeyRing mKeyRing = null;
private SectionView mUserIdsView;
@@ -96,29 +100,10 @@ public class EditKeyActivity extends SherlockFragmentActivity {
Vector mKeysUsages;
boolean masterCanSign = true;
- // will be set to false to build layout later in handler
- private boolean mBuildLayout = true;
-
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- // Inflate a "Done"/"Cancel" custom action bar
- ActionBarHelper.setDoneCancelView(getSupportActionBar(), R.string.btn_save,
- new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // save
- saveClicked();
- }
- }, R.string.btn_do_not_save, new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // cancel
- cancelClicked();
- }
- });
-
mUserIds = new Vector();
mKeys = new Vector();
mKeysUsages = new Vector();
@@ -131,10 +116,6 @@ public class EditKeyActivity extends SherlockFragmentActivity {
} else if (ACTION_EDIT_KEY.equals(action)) {
handleActionEditKey(intent);
}
-
- if (mBuildLayout) {
- buildLayout();
- }
}
/**
@@ -143,6 +124,20 @@ public class EditKeyActivity extends SherlockFragmentActivity {
* @param intent
*/
private void handleActionCreateKey(Intent intent) {
+ // Inflate a "Done"/"Cancel" custom action bar
+ ActionBarHelper.setDoneCancelView(getSupportActionBar(), R.string.btn_save,
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ saveClicked();
+ }
+ }, R.string.btn_do_not_save, new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ cancelClicked();
+ }
+ });
+
Bundle extras = intent.getExtras();
mCurrentPassPhrase = "";
@@ -169,9 +164,6 @@ public class EditKeyActivity extends SherlockFragmentActivity {
boolean generateDefaultKeys = extras.getBoolean(EXTRA_GENERATE_DEFAULT_KEYS);
if (generateDefaultKeys) {
- // build layout in handler after generating keys not directly in onCreate
- mBuildLayout = false;
-
// Send all information needed to service generate keys in other thread
Intent serviceIntent = new Intent(this, KeychainIntentService.class);
serviceIntent.setAction(KeychainIntentService.ACTION_GENERATE_DEFAULT_RSA_KEYS);
@@ -228,12 +220,55 @@ public class EditKeyActivity extends SherlockFragmentActivity {
startService(serviceIntent);
}
}
+ } else {
+ buildLayout();
+ }
+ }
+
+ /**
+ * Handle intent action to edit existing key
+ *
+ * @param intent
+ */
+ private void handleActionEditKey(Intent intent) {
+ // Inflate a "Done"/"Cancel" custom action bar
+ ActionBarHelper.setDoneView(getSupportActionBar(), R.string.btn_save,
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ saveClicked();
+ }
+ });
+
+ mDataUri = intent.getData();
+ if (mDataUri == null) {
+ Log.e(Constants.TAG, "Intent data missing. Should be Uri of key!");
+ finish();
+ return;
+ } else {
+ Log.d(Constants.TAG, "uri: " + mDataUri);
+
+ long keyRingRowId = Long.valueOf(mDataUri.getLastPathSegment());
+
+ // get master key id using row id
+ long masterKeyId = ProviderHelper.getSecretMasterKeyId(this, keyRingRowId);
+
+ boolean masterCanSign = ProviderHelper.getSecretMasterKeyCanSign(this, keyRingRowId);
+
+ String passphrase = PassphraseCacheService.getCachedPassphrase(this, masterKeyId);
+ if (passphrase == null) {
+ showPassphraseDialog(masterKeyId, masterCanSign);
+ } else {
+ // PgpMain.setEditPassPhrase(passPhrase);
+ mCurrentPassPhrase = passphrase;
+
+ finallyEdit(masterKeyId, masterCanSign);
+ }
}
}
private void showPassphraseDialog(final long masterKeyId, final boolean masterCanSign) {
// Message is received after passphrase is cached
- final boolean mCanSign = masterCanSign;
Handler returnHandler = new Handler() {
@Override
public void handleMessage(Message message) {
@@ -263,37 +298,41 @@ public class EditKeyActivity extends SherlockFragmentActivity {
}
}
- /**
- * Handle intent action to edit existing key
- *
- * @param intent
- */
- private void handleActionEditKey(Intent intent) {
- Bundle extras = intent.getExtras();
-
- if (extras != null) {
- if (extras.containsKey(EXTRA_MASTER_CAN_SIGN)) {
- masterCanSign = extras.getBoolean(EXTRA_MASTER_CAN_SIGN);
- }
- if (extras.containsKey(EXTRA_MASTER_KEY_ID)) {
- long masterKeyId = extras.getLong(EXTRA_MASTER_KEY_ID);
-
- // build layout in edit()
- mBuildLayout = false;
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ // show menu only on edit
+ if (mDataUri != null) {
+ return super.onPrepareOptionsMenu(menu);
+ } else {
+ return false;
+ }
+ }
- String passPhrase = PassphraseCacheService.getCachedPassphrase(this, masterKeyId);
- if (passPhrase == null) {
- showPassphraseDialog(masterKeyId, masterCanSign);
- } else {
- // PgpMain.setEditPassPhrase(passPhrase);
- mCurrentPassPhrase = passPhrase;
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ getSupportMenuInflater().inflate(R.menu.key_edit, menu);
+ return true;
+ }
- finallyEdit(masterKeyId, masterCanSign);
- }
- }
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_key_edit_cancel:
+ cancelClicked();
+ return true;
+ case R.id.menu_key_edit_export_file:
+ showExportKeysDialog(mDataUri, Id.type.secret_key, Constants.path.APP_DIR
+ + "/secexport.asc");
+ return true;
+ case R.id.menu_key_edit_delete:
+ deleteKey(mDataUri, Id.type.secret_key);
+ return true;
}
+ return super.onOptionsItemSelected(item);
}
+ @SuppressWarnings("unchecked")
private void finallyEdit(final long masterKeyId, final boolean masterCanSign) {
if (masterKeyId != 0) {
PGPSecretKey masterKey = null;
@@ -316,13 +355,13 @@ public class EditKeyActivity extends SherlockFragmentActivity {
}
}
- buildLayout();
-
// TODO: ???
if (mCurrentPassPhrase == null) {
mCurrentPassPhrase = "";
}
+ buildLayout();
+
if (mCurrentPassPhrase.equals("")) {
// check "no passphrase" checkbox and remove button
mNoPassphrase.setChecked(true);
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyActivity.java
new file mode 100644
index 000000000..0415087dc
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyActivity.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2013 Dominik Schürmann
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.Id;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.KeychainIntentService;
+import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
+import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
+import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;
+import org.sufficientlysecure.keychain.util.Log;
+
+import android.app.ProgressDialog;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
+import android.widget.Toast;
+
+import com.actionbarsherlock.app.SherlockFragmentActivity;
+
+/**
+ * TODO: get key type by looking at dataUri!
+ *
+ */
+public class KeyActivity extends SherlockFragmentActivity {
+
+ protected FileDialogFragment mFileDialog;
+ protected String mExportFilename;
+
+ protected void deleteKey(Uri dataUri, final int keyType) {
+ long keyRingRowId = Long.valueOf(dataUri.getLastPathSegment());
+
+ // Message is received after key is deleted
+ Handler returnHandler = new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) {
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+ }
+ };
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(returnHandler);
+
+ DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger,
+ new long[] { keyRingRowId }, keyType);
+
+ deleteKeyDialog.show(getSupportFragmentManager(), "deleteKeyDialog");
+ }
+
+ /**
+ * Show dialog where to export keys
+ *
+ * @param keyRingMasterKeyId
+ * if -1 export all keys
+ */
+ public void showExportKeysDialog(Uri dataUri, final int keyType, final String exportFilename) {
+ long keyRingRowId = Long.valueOf(dataUri.getLastPathSegment());
+
+ // TODO?
+ final long keyRingMasterKeyId = ProviderHelper.getSecretMasterKeyId(this, keyRingRowId);
+ mExportFilename = exportFilename;
+
+ // Message is received after file is selected
+ Handler returnHandler = new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == FileDialogFragment.MESSAGE_OKAY) {
+ Bundle data = message.getData();
+ mExportFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME);
+
+ exportKeys(keyRingMasterKeyId, keyType);
+ }
+ }
+ };
+
+ // Create a new Messenger for the communication back
+ final Messenger messenger = new Messenger(returnHandler);
+
+ DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
+ public void run() {
+ String title = null;
+ if (keyRingMasterKeyId != -1) {
+ // single key export
+ title = getString(R.string.title_export_key);
+ } else {
+ title = getString(R.string.title_export_keys);
+ }
+
+ String message = null;
+ if (keyType == Id.type.public_key) {
+ message = getString(R.string.specify_file_to_export_to);
+ } else {
+ message = getString(R.string.specify_file_to_export_secret_keys_to);
+ }
+
+ mFileDialog = FileDialogFragment.newInstance(messenger, title, message,
+ exportFilename, null, Id.request.filename);
+
+ mFileDialog.show(getSupportFragmentManager(), "fileDialog");
+ }
+ });
+ }
+
+ /**
+ * Export keys
+ *
+ * @param keyRingMasterKeyId
+ * if -1 export all keys
+ */
+ public void exportKeys(long keyRingMasterKeyId, int keyType) {
+ Log.d(Constants.TAG, "exportKeys started");
+
+ // Send all information needed to service to export key in other thread
+ Intent intent = new Intent(this, KeychainIntentService.class);
+
+ intent.setAction(KeychainIntentService.ACTION_EXPORT_KEYRING);
+
+ // fill values for this action
+ Bundle data = new Bundle();
+
+ data.putString(KeychainIntentService.EXPORT_FILENAME, mExportFilename);
+ data.putInt(KeychainIntentService.EXPORT_KEY_TYPE, keyType);
+
+ if (keyRingMasterKeyId == -1) {
+ data.putBoolean(KeychainIntentService.EXPORT_ALL, true);
+ } else {
+ data.putLong(KeychainIntentService.EXPORT_KEY_RING_MASTER_KEY_ID, keyRingMasterKeyId);
+ }
+
+ intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
+
+ // Message is received after exporting is done in ApgService
+ KeychainIntentServiceHandler exportHandler = new KeychainIntentServiceHandler(this,
+ R.string.progress_exporting, ProgressDialog.STYLE_HORIZONTAL) {
+ public void handleMessage(Message message) {
+ // handle messages by standard ApgHandler first
+ super.handleMessage(message);
+
+ if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
+ // get returned data bundle
+ Bundle returnData = message.getData();
+
+ int exported = returnData.getInt(KeychainIntentService.RESULT_EXPORT);
+ String toastMessage;
+ if (exported == 1) {
+ toastMessage = getString(R.string.key_exported);
+ } else if (exported > 0) {
+ toastMessage = getString(R.string.keys_exported, exported);
+ } else {
+ toastMessage = getString(R.string.no_keys_exported);
+ }
+ Toast.makeText(KeyActivity.this, toastMessage, Toast.LENGTH_SHORT).show();
+
+ }
+ };
+ };
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(exportHandler);
+ intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+
+ // show progress dialog
+ exportHandler.showProgressDialog(this);
+
+ // start service with intent
+ startService(intent);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case Id.request.filename: {
+ if (resultCode == RESULT_OK && data != null) {
+ try {
+ String path = data.getData().getPath();
+ Log.d(Constants.TAG, "path=" + path);
+
+ // set filename used in export/import dialogs
+ mFileDialog.setFilename(path);
+ } catch (NullPointerException e) {
+ Log.e(Constants.TAG, "Nullpointer while retrieving path!", e);
+ }
+ }
+ return;
+ }
+
+ default: {
+ break;
+ }
+ }
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
index f0a009482..fde90bb32 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
@@ -35,9 +35,6 @@ import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Messenger;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
@@ -122,7 +119,7 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte
Set positions = mAdapter.getCurrentCheckedPosition();
switch (item.getItemId()) {
case R.id.delete_entry:
-
+
// get IDs for checked positions as long array
long[] ids = new long[positions.size()];
int i = 0;
@@ -233,25 +230,10 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte
/**
* Show dialog to delete key
*
- * TODO: no messenger needed etc!
- *
* @param keyRingRowIds
*/
public void showDeleteKeyDialog(long[] keyRingRowIds) {
- // Message is received after key is deleted
- Handler returnHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) {
- // no further actions needed
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(returnHandler);
-
- DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger,
+ DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(null,
keyRingRowIds, Id.type.public_key);
deleteKeyDialog.show(getActivity().getSupportFragmentManager(), "deleteKeyDialog");
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java
index d98b5ab37..c19cdeea5 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java
@@ -45,8 +45,8 @@ public class KeyListSecretActivity extends KeyListActivity {
super.onCreateOptionsMenu(menu);
menu.add(1, Id.menu.option.create, 1, R.string.menu_create_key).setShowAsAction(
MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
- menu.add(1, Id.menu.option.createExpert, 2, R.string.menu_create_key_expert).setShowAsAction(
- MenuItem.SHOW_AS_ACTION_NEVER);
+ menu.add(1, Id.menu.option.createExpert, 2, R.string.menu_create_key_expert)
+ .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
return true;
}
@@ -84,12 +84,4 @@ public class KeyListSecretActivity extends KeyListActivity {
startActivityForResult(intent, 0);
}
- void editKey(long masterKeyId, boolean masterCanSign) {
- Intent intent = new Intent(this, EditKeyActivity.class);
- intent.setAction(EditKeyActivity.ACTION_EDIT_KEY);
- intent.putExtra(EditKeyActivity.EXTRA_MASTER_KEY_ID, masterKeyId);
- intent.putExtra(EditKeyActivity.EXTRA_MASTER_CAN_SIGN, masterCanSign);
- startActivityForResult(intent, 0);
- }
-
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java
index e8a306063..40579a3c2 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java
@@ -19,11 +19,12 @@ package org.sufficientlysecure.keychain.ui;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.ui.adapter.KeyListSecretAdapter;
+import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
@@ -37,7 +38,7 @@ import android.widget.AdapterView.OnItemClickListener;
import com.actionbarsherlock.app.SherlockListFragment;
public class KeyListSecretFragment extends SherlockListFragment implements
- LoaderManager.LoaderCallbacks {
+ LoaderManager.LoaderCallbacks, OnItemClickListener {
private KeyListSecretActivity mKeyListSecretActivity;
@@ -52,20 +53,7 @@ public class KeyListSecretFragment extends SherlockListFragment implements
mKeyListSecretActivity = (KeyListSecretActivity) getActivity();
- getListView().setOnItemClickListener(new OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView> adapterView, View view, int position, long id) {
- // TODO: currently this is EDIT directly, later first show VIEW
-
- // get master key id using row id
- long masterKeyId = ProviderHelper.getSecretMasterKeyId(mKeyListSecretActivity, id);
-
- boolean masterCanSign = ProviderHelper.getSecretMasterKeyCanSign(
- mKeyListSecretActivity, id);
-
- mKeyListSecretActivity.editKey(masterKeyId, masterCanSign);
- }
- });
+ getListView().setOnItemClickListener(this);
// Give some text to display if there is no data. In a real
// application this would come from a resource.
@@ -124,4 +112,15 @@ public class KeyListSecretFragment extends SherlockListFragment implements
// longer using it.
mAdapter.swapCursor(null);
}
+
+ /**
+ * On click on item, start key view activity
+ */
+ @Override
+ public void onItemClick(AdapterView> adapterView, View view, int position, long id) {
+ Intent editIntent = new Intent(mKeyListSecretActivity, EditKeyActivity.class);
+ editIntent.setData(KeychainContract.KeyRings.buildSecretKeyRingsUri(Long.toString(id)));
+ editIntent.setAction(EditKeyActivity.ACTION_EDIT_KEY);
+ startActivityForResult(editIntent, 0);
+ }
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java
index 13926d172..c32a797be 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java
@@ -52,12 +52,11 @@ import android.text.format.DateFormat;
import android.widget.TextView;
import android.widget.Toast;
-import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
@SuppressLint("NewApi")
-public class KeyViewActivity extends SherlockFragmentActivity implements CreateNdefMessageCallback,
+public class KeyViewActivity extends KeyActivity implements CreateNdefMessageCallback,
OnNdefPushCompleteCallback {
private Uri mDataUri;
@@ -96,7 +95,7 @@ public class KeyViewActivity extends SherlockFragmentActivity implements CreateN
} else {
Log.d(Constants.TAG, "uri: " + mDataUri);
loadData(mDataUri);
- initNfc();
+ initNfc(mDataUri);
}
}
@@ -111,31 +110,32 @@ public class KeyViewActivity extends SherlockFragmentActivity implements CreateN
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_key_view_update:
- updateFromKeyserver();
+ updateFromKeyserver(mDataUri);
return true;
case R.id.menu_key_view_sign:
- signKey();
+ signKey(mDataUri);
return true;
case R.id.menu_key_view_export_keyserver:
- uploadToKeyserver();
+ uploadToKeyserver(mDataUri);
return true;
case R.id.menu_key_view_export_file:
- exportToFile();
+ showExportKeysDialog(mDataUri, Id.type.public_key, Constants.path.APP_DIR
+ + "/pubexport.asc");
return true;
case R.id.menu_key_view_share_default:
- shareKey();
+ shareKey(mDataUri);
return true;
case R.id.menu_key_view_share_qr_code:
- shareKeyQrCode();
+ shareKeyQrCode(mDataUri);
return true;
case R.id.menu_key_view_share_nfc:
shareNfc();
return true;
case R.id.menu_key_view_share_clipboard:
- copyToClipboard();
+ copyToClipboard(mDataUri);
return true;
case R.id.menu_key_view_delete:
- deleteKey();
+ deleteKey(mDataUri, Id.type.public_key);
return true;
}
@@ -188,24 +188,8 @@ public class KeyViewActivity extends SherlockFragmentActivity implements CreateN
return result;
}
- private void exportToFile() {
- // long masterKeyId = ProviderHelper.getPublicMasterKeyId(mKeyListActivity,
- // keyRingRowId);
- // if (masterKeyId == -1) {
- // masterKeyId = ProviderHelper.getSecretMasterKeyId(mKeyListActivity, keyRingRowId);
- // }
- //
- // mKeyListActivity.showExportKeysDialog(masterKeyId);
- }
-
- private void deleteKey() {
- long keyRingRowId = Long.valueOf(mDataUri.getLastPathSegment());
-
- // mKeyListActivity.showDeleteKeyDialog(keyRingRowId);
- }
-
- private void uploadToKeyserver() {
- long keyRingRowId = Long.valueOf(mDataUri.getLastPathSegment());
+ private void uploadToKeyserver(Uri dataUri) {
+ long keyRingRowId = Long.valueOf(dataUri.getLastPathSegment());
Intent uploadIntent = new Intent(this, KeyServerUploadActivity.class);
uploadIntent.setAction(KeyServerUploadActivity.ACTION_EXPORT_KEY_TO_SERVER);
@@ -213,20 +197,22 @@ public class KeyViewActivity extends SherlockFragmentActivity implements CreateN
startActivityForResult(uploadIntent, Id.request.export_to_server);
}
- private void updateFromKeyserver() {
- // TODO: use data uri!
- long keyRingRowId = Long.valueOf(mDataUri.getLastPathSegment());
-
+ private void updateFromKeyserver(Uri dataUri) {
long updateKeyId = 0;
- PGPPublicKeyRing updateKeyRing = ProviderHelper.getPGPPublicKeyRingByRowId(this,
- keyRingRowId);
- if (updateKeyRing != null) {
- updateKeyId = PgpKeyHelper.getMasterKey(updateKeyRing).getKeyID();
+ PGPPublicKeyRing updateRing = (PGPPublicKeyRing) ProviderHelper
+ .getPGPKeyRing(this, dataUri);
+
+ if (updateRing != null) {
+ updateKeyId = PgpKeyHelper.getMasterKey(updateRing).getKeyID();
+ }
+ if (updateKeyId == 0) {
+ Log.e(Constants.TAG, "this shouldn't happen. KeyId == 0!");
+ return;
}
- // if (updateKeyId == 0) {
- // // this shouldn't happen
- // return true;
- // }
+
+ Intent signIntent = new Intent(this, SignKeyActivity.class);
+ signIntent.putExtra(SignKeyActivity.EXTRA_KEY_ID, updateKeyId);
+ startActivity(signIntent);
Intent queryIntent = new Intent(this, KeyServerQueryActivity.class);
queryIntent.setAction(KeyServerQueryActivity.ACTION_LOOK_UP_KEY_ID_AND_RETURN);
@@ -234,35 +220,29 @@ public class KeyViewActivity extends SherlockFragmentActivity implements CreateN
// TODO: lookup??
startActivityForResult(queryIntent, Id.request.look_up_key_id);
-
}
- private void signKey() {
- // TODO: use data uri!
- long keyRingRowId = Long.valueOf(mDataUri.getLastPathSegment());
-
+ private void signKey(Uri dataUri) {
long keyId = 0;
- PGPPublicKeyRing signKeyRing = ProviderHelper
- .getPGPPublicKeyRingByRowId(this, keyRingRowId);
- if (signKeyRing != null) {
- keyId = PgpKeyHelper.getMasterKey(signKeyRing).getKeyID();
+ PGPPublicKeyRing signKey = (PGPPublicKeyRing) ProviderHelper.getPGPKeyRing(this, dataUri);
+
+ if (signKey != null) {
+ keyId = PgpKeyHelper.getMasterKey(signKey).getKeyID();
+ }
+ if (keyId == 0) {
+ Log.e(Constants.TAG, "this shouldn't happen. KeyId == 0!");
+ return;
}
- // if (keyId == 0) {
- // // this shouldn't happen
- // return true;
- // }
Intent signIntent = new Intent(this, SignKeyActivity.class);
signIntent.putExtra(SignKeyActivity.EXTRA_KEY_ID, keyId);
startActivity(signIntent);
}
- private void shareKey() {
- // TODO: use data uri!
- long keyRingRowId = Long.valueOf(mDataUri.getLastPathSegment());
- long masterKeyId = ProviderHelper.getPublicMasterKeyId(this, keyRingRowId);
+ private void shareKey(Uri dataUri) {
// get public keyring as ascii armored string
- ArrayList keyringArmored = ProviderHelper.getPublicKeyRingsAsArmoredString(this,
+ long masterKeyId = ProviderHelper.getMasterKeyId(this, dataUri);
+ ArrayList keyringArmored = ProviderHelper.getKeyRingsAsArmoredString(this, dataUri,
new long[] { masterKeyId });
// let user choose application
@@ -273,12 +253,10 @@ public class KeyViewActivity extends SherlockFragmentActivity implements CreateN
getResources().getText(R.string.action_share_key_with)));
}
- private void shareKeyQrCode() {
- // TODO: use data uri!
- long keyRingRowId = Long.valueOf(mDataUri.getLastPathSegment());
- long masterKeyId = ProviderHelper.getPublicMasterKeyId(this, keyRingRowId);
+ private void shareKeyQrCode(Uri dataUri) {
// get public keyring as ascii armored string
- ArrayList keyringArmored = ProviderHelper.getPublicKeyRingsAsArmoredString(this,
+ long masterKeyId = ProviderHelper.getMasterKeyId(this, dataUri);
+ ArrayList keyringArmored = ProviderHelper.getKeyRingsAsArmoredString(this, dataUri,
new long[] { masterKeyId });
ShareQrCodeDialogFragment dialog = ShareQrCodeDialogFragment.newInstance(keyringArmored
@@ -286,38 +264,34 @@ public class KeyViewActivity extends SherlockFragmentActivity implements CreateN
dialog.show(getSupportFragmentManager(), "shareQrCodeDialog");
}
- private void shareNfc() {
- ShareNfcDialogFragment dialog = ShareNfcDialogFragment.newInstance();
- dialog.show(getSupportFragmentManager(), "shareNfcDialog");
- }
-
- private void copyToClipboard() {
- // TODO: use data uri!
- long keyRingRowId = Long.valueOf(mDataUri.getLastPathSegment());
- long masterKeyId = ProviderHelper.getPublicMasterKeyId(this, keyRingRowId);
+ private void copyToClipboard(Uri dataUri) {
// get public keyring as ascii armored string
- ArrayList keyringArmored = ProviderHelper.getPublicKeyRingsAsArmoredString(this,
+ long masterKeyId = ProviderHelper.getMasterKeyId(this, dataUri);
+ ArrayList keyringArmored = ProviderHelper.getKeyRingsAsArmoredString(this, dataUri,
new long[] { masterKeyId });
ClipboardReflection.copyToClipboard(this, keyringArmored.get(0));
}
+ private void shareNfc() {
+ ShareNfcDialogFragment dialog = ShareNfcDialogFragment.newInstance();
+ dialog.show(getSupportFragmentManager(), "shareNfcDialog");
+ }
+
/**
* NFC: Initialize NFC sharing if OS and device supports it
*/
- private void initNfc() {
+ private void initNfc(Uri dataUri) {
// check if NFC Beam is supported (>= Android 4.1)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
// Check for available NFC Adapter
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
if (mNfcAdapter != null) {
// init nfc
- // TODO: use data uri!
- long keyRingRowId = Long.valueOf(mDataUri.getLastPathSegment());
- long masterKeyId = ProviderHelper.getPublicMasterKeyId(this, keyRingRowId);
// get public keyring as byte array
- mSharedKeyringBytes = ProviderHelper.getPublicKeyRingsAsByteArray(this,
+ long masterKeyId = ProviderHelper.getMasterKeyId(this, dataUri);
+ mSharedKeyringBytes = ProviderHelper.getKeyRingsAsByteArray(this, dataUri,
new long[] { masterKeyId });
// Register callback to set NDEF message
diff --git a/README.md b/README.md
index 59a6c1660..78bb79ebd 100644
--- a/README.md
+++ b/README.md
@@ -120,7 +120,7 @@ On error see: http://code.google.com/p/zxing/issues/detail?id=1207
## ZXing
-XZing classes were extracted from the ZXing library (http://code.google.com/p/zxing/).
+ZXing classes were extracted from the ZXing library (http://code.google.com/p/zxing/).
Only classes related to QR Code generation are utilized.
## Bouncy Castle
--
cgit v1.2.3
From caf3b02042f94b7b2a9b019b01d45c41bf035140 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Tue, 7 Jan 2014 22:40:18 +0100
Subject: reorder menus, cleanup code, add experimental tab activity
---
OpenPGP-Keychain/res/layout/lists_activity.xml | 13 +
OpenPGP-Keychain/res/menu/key_list_public.xml | 13 +
OpenPGP-Keychain/res/menu/key_list_secret.xml | 21 ++
OpenPGP-Keychain/res/menu/key_view.xml | 5 +-
OpenPGP-Keychain/res/values/strings.xml | 3 +-
.../keychain/ui/EditKeyActivity.java | 16 +-
.../keychain/ui/EncryptDecryptActivity.java | 175 ++++++++++++
.../keychain/ui/KeyActivity.java | 32 +--
.../keychain/ui/KeyListActivity.java | 314 ---------------------
.../keychain/ui/KeyListPublicActivity.java | 54 ++--
.../keychain/ui/KeyListPublicFragment.java | 8 +-
.../keychain/ui/KeyListSecretActivity.java | 42 +--
.../keychain/ui/KeyViewActivity.java | 18 +-
13 files changed, 313 insertions(+), 401 deletions(-)
create mode 100644 OpenPGP-Keychain/res/layout/lists_activity.xml
create mode 100644 OpenPGP-Keychain/res/menu/key_list_public.xml
create mode 100644 OpenPGP-Keychain/res/menu/key_list_secret.xml
create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptDecryptActivity.java
delete mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListActivity.java
diff --git a/OpenPGP-Keychain/res/layout/lists_activity.xml b/OpenPGP-Keychain/res/layout/lists_activity.xml
new file mode 100644
index 000000000..58b0906d3
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/lists_activity.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/menu/key_list_public.xml b/OpenPGP-Keychain/res/menu/key_list_public.xml
new file mode 100644
index 000000000..7b6261558
--- /dev/null
+++ b/OpenPGP-Keychain/res/menu/key_list_public.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/menu/key_list_secret.xml b/OpenPGP-Keychain/res/menu/key_list_secret.xml
new file mode 100644
index 000000000..c610eda35
--- /dev/null
+++ b/OpenPGP-Keychain/res/menu/key_list_secret.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/menu/key_view.xml b/OpenPGP-Keychain/res/menu/key_view.xml
index 3798cb935..1078cd937 100644
--- a/OpenPGP-Keychain/res/menu/key_view.xml
+++ b/OpenPGP-Keychain/res/menu/key_view.xml
@@ -52,9 +52,6 @@
-
-
-
+ android:title="@string/menu_delete_key"/>
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/values/strings.xml b/OpenPGP-Keychain/res/values/strings.xml
index 0e4f42c59..d870c1289 100644
--- a/OpenPGP-Keychain/res/values/strings.xml
+++ b/OpenPGP-Keychain/res/values/strings.xml
@@ -83,6 +83,7 @@
Registered AppsImport from fileImport from QR Code
+ ImportImport from NFCExport all keysExport to file
@@ -101,7 +102,7 @@
Sign keyBeam settingsCancel
-
+
SignMessage
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
index c0c81b969..d141fbe3a 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
@@ -325,10 +325,22 @@ public class EditKeyActivity extends KeyActivity {
showExportKeysDialog(mDataUri, Id.type.secret_key, Constants.path.APP_DIR
+ "/secexport.asc");
return true;
- case R.id.menu_key_edit_delete:
- deleteKey(mDataUri, Id.type.secret_key);
+ case R.id.menu_key_edit_delete: {
+ // Message is received after key is deleted
+ Handler returnHandler = new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) {
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+ }
+ };
+
+ deleteKey(mDataUri, Id.type.secret_key, returnHandler);
return true;
}
+ }
return super.onOptionsItemSelected(item);
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptDecryptActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptDecryptActivity.java
new file mode 100644
index 000000000..7f0184381
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptDecryptActivity.java
@@ -0,0 +1,175 @@
+package org.sufficientlysecure.keychain.ui;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.util.Log;
+
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.app.ActionBar.Tab;
+import com.actionbarsherlock.app.SherlockFragmentActivity;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentTransaction;
+
+public class EncryptDecryptActivity extends SherlockFragmentActivity {
+ private FragmentActivity mActivity;
+ private ActionBar mActionBar;
+ private ActionBar.Tab mTab1;
+ private ActionBar.Tab mTab2;
+ private ActionBar.Tab mTab3;
+
+ // @Override
+ // public boolean onCreateOptionsMenu(Menu menu) {
+ // MenuInflater inflater = getSupportMenuInflater();
+ // inflater.inflate(R.menu.lists_activity, menu);
+ // return true;
+ // }
+
+ /**
+ * Menu item to go back home in ActionBar, other menu items are defined in Fragments
+ */
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ // app icon in Action Bar clicked; go home
+ Intent intent = new Intent(mActivity, MainActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(intent);
+ return true;
+
+ // case R.id.menu_import:
+ // ImportExportHelper.openFileStream(mActivity);
+ // return true;
+ //
+ // case R.id.menu_export:
+ // ImportExportHelper.exportLists(mActivity);
+ // return true;
+
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ /**
+ * Set up Tabs on create
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mActivity = this;
+
+ setContentView(R.layout.lists_activity);
+
+ mActionBar = getSupportActionBar();
+ mActionBar.setDisplayShowTitleEnabled(true);
+ mActionBar.setDisplayHomeAsUpEnabled(true);
+
+ mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
+
+ mTab1 = getSupportActionBar().newTab();
+ mTab2 = getSupportActionBar().newTab();
+ mTab3 = getSupportActionBar().newTab();
+
+ mTab1.setTabListener(new TabListener(this, "publicList",
+ KeyListPublicFragment.class));
+ mTab2.setTabListener(new TabListener(this, "import",
+ KeyListPublicFragment.class));
+
+ setTabTextBasedOnOrientation(getResources().getConfiguration());
+
+ mActionBar.addTab(mTab1);
+ mActionBar.addTab(mTab2);
+ // mActionBar.addTab(mTab3);
+ }
+
+ private void setTabTextBasedOnOrientation(Configuration config) {
+ // longer names for landscape mode or tablets
+ // if (config.orientation == Configuration.ORIENTATION_LANDSCAPE
+ // || config.screenLayout == Configuration.SCREENLAYOUT_SIZE_XLARGE) {
+ mTab1.setText(getString(R.string.dashboard_manage_keys));
+ mTab2.setText(getString(R.string.dashboard_manage_keys));
+
+ // } else {
+ // mTab1.setText(getString(R.string.lists_tab_blacklist_short));
+ // mTab2.setText(getString(R.string.lists_tab_whitelist_short));
+ // mTab3.setText(getString(R.string.lists_tab_redirection_list_short));
+ // }
+ }
+
+ /**
+ * Change text on orientation change
+ */
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+
+ setTabTextBasedOnOrientation(newConfig);
+ }
+
+ public static class TabListener implements ActionBar.TabListener {
+ private Fragment mFragment;
+ private final Activity mActivity;
+ private final String mTag;
+ private final Class mClass;
+
+ /**
+ * Constructor used each time a new tab is created.
+ *
+ * @param activity
+ * The host Activity, used to instantiate the fragment
+ * @param tag
+ * The identifier tag for the fragment
+ * @param clz
+ * The fragment's Class, used to instantiate the fragment
+ */
+ public TabListener(Activity activity, String tag, Class clz) {
+ mActivity = activity;
+ mTag = tag;
+ mClass = clz;
+ }
+
+ @Override
+ public void onTabReselected(Tab tab, FragmentTransaction ft) {
+ }
+
+ /**
+ * Open Fragment based on selected Tab
+ */
+ @Override
+ public void onTabSelected(Tab tab, FragmentTransaction ignoredFt) {
+ // bug in compatibility lib:
+ // http://stackoverflow.com/questions/8645549/null-fragmenttransaction-being-passed-to-tablistener-ontabselected
+ FragmentManager fragMgr = ((FragmentActivity) mActivity).getSupportFragmentManager();
+ FragmentTransaction ft = fragMgr.beginTransaction();
+
+ mFragment = Fragment.instantiate(mActivity, mClass.getName());
+ ft.replace(R.id.lists_tabs_container, mFragment, mTag);
+ ft.commit();
+ }
+
+ @Override
+ public void onTabUnselected(Tab tab, FragmentTransaction ignoredFt) {
+ FragmentManager fragMgr = ((FragmentActivity) mActivity).getSupportFragmentManager();
+ FragmentTransaction ft = fragMgr.beginTransaction();
+
+ if (mFragment != null) {
+ // Remove the fragment
+ ft.remove(mFragment);
+ }
+
+ ft.commit();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyActivity.java
index 0415087dc..2760df97e 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyActivity.java
@@ -40,6 +40,8 @@ import android.widget.Toast;
import com.actionbarsherlock.app.SherlockFragmentActivity;
/**
+ * This implements export key method and delete key method. Used in lists and key view and key edit.
+ *
* TODO: get key type by looking at dataUri!
*
*/
@@ -48,22 +50,11 @@ public class KeyActivity extends SherlockFragmentActivity {
protected FileDialogFragment mFileDialog;
protected String mExportFilename;
- protected void deleteKey(Uri dataUri, final int keyType) {
+ protected void deleteKey(Uri dataUri, final int keyType, Handler deleteHandler) {
long keyRingRowId = Long.valueOf(dataUri.getLastPathSegment());
- // Message is received after key is deleted
- Handler returnHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) {
- setResult(RESULT_CANCELED);
- finish();
- }
- }
- };
-
// Create a new Messenger for the communication back
- Messenger messenger = new Messenger(returnHandler);
+ Messenger messenger = new Messenger(deleteHandler);
DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger,
new long[] { keyRingRowId }, keyType);
@@ -77,11 +68,8 @@ public class KeyActivity extends SherlockFragmentActivity {
* @param keyRingMasterKeyId
* if -1 export all keys
*/
- public void showExportKeysDialog(Uri dataUri, final int keyType, final String exportFilename) {
- long keyRingRowId = Long.valueOf(dataUri.getLastPathSegment());
-
- // TODO?
- final long keyRingMasterKeyId = ProviderHelper.getSecretMasterKeyId(this, keyRingRowId);
+ public void showExportKeysDialog(final Uri dataUri, final int keyType,
+ final String exportFilename) {
mExportFilename = exportFilename;
// Message is received after file is selected
@@ -92,6 +80,12 @@ public class KeyActivity extends SherlockFragmentActivity {
Bundle data = message.getData();
mExportFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME);
+ long keyRingRowId = Long.valueOf(dataUri.getLastPathSegment());
+
+ // TODO?
+ long keyRingMasterKeyId = ProviderHelper.getSecretMasterKeyId(KeyActivity.this,
+ keyRingRowId);
+
exportKeys(keyRingMasterKeyId, keyType);
}
}
@@ -103,7 +97,7 @@ public class KeyActivity extends SherlockFragmentActivity {
DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
public void run() {
String title = null;
- if (keyRingMasterKeyId != -1) {
+ if (dataUri != null) {
// single key export
title = getString(R.string.title_export_key);
} else {
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListActivity.java
deleted file mode 100644
index 5b69f9218..000000000
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListActivity.java
+++ /dev/null
@@ -1,314 +0,0 @@
-/*
- * Copyright (C) 2013 Dominik Schürmann
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package org.sufficientlysecure.keychain.ui;
-
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.Id;
-import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
-import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
-import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;
-import org.sufficientlysecure.keychain.util.Log;
-import org.sufficientlysecure.keychain.R;
-
-import android.app.ProgressDialog;
-import android.app.SearchManager;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Messenger;
-import android.widget.Toast;
-
-import com.actionbarsherlock.app.SherlockFragmentActivity;
-import com.actionbarsherlock.view.Menu;
-import com.actionbarsherlock.view.MenuItem;
-
-public class KeyListActivity extends SherlockFragmentActivity {
-
- protected String mExportFilename = Constants.path.APP_DIR + "/";
-
- protected String mImportData;
- protected boolean mDeleteAfterImport = false;
-
- protected int mKeyType;
-
- FileDialogFragment mFileDialog;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- getSupportActionBar().setHomeButtonEnabled(true);
-
- handleActions(getIntent());
- }
-
- // TODO: needed?
- // @Override
- // protected void onNewIntent(Intent intent) {
- // super.onNewIntent(intent);
- // handleActions(intent);
- // }
-
- protected void handleActions(Intent intent) {
- String action = intent.getAction();
- Bundle extras = intent.getExtras();
-
- if (extras == null) {
- extras = new Bundle();
- }
-
- /**
- * Android Standard Actions
- */
- String searchString = null;
- if (Intent.ACTION_SEARCH.equals(action)) {
- searchString = extras.getString(SearchManager.QUERY);
- if (searchString != null && searchString.trim().length() == 0) {
- searchString = null;
- }
- }
-
- // if (searchString == null) {
- // mFilterLayout.setVisibility(View.GONE);
- // } else {
- // mFilterLayout.setVisibility(View.VISIBLE);
- // mFilterInfo.setText(getString(R.string.filterInfo, searchString));
- // }
- //
- // if (mListAdapter != null) {
- // mListAdapter.cleanup();
- // }
- // mListAdapter = new KeyListAdapter(this, searchString);
- // mList.setAdapter(mListAdapter);
-
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- switch (requestCode) {
- case Id.request.filename: {
- if (resultCode == RESULT_OK && data != null) {
- try {
- String path = data.getData().getPath();
- Log.d(Constants.TAG, "path=" + path);
-
- // set filename used in export/import dialogs
- mFileDialog.setFilename(path);
- } catch (NullPointerException e) {
- Log.e(Constants.TAG, "Nullpointer while retrieving path!", e);
- }
- }
- return;
- }
-
- default: {
- break;
- }
- }
- super.onActivityResult(requestCode, resultCode, data);
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- super.onCreateOptionsMenu(menu);
- // TODO: reimplement!
- // menu.add(3, Id.menu.option.search, 0, R.string.menu_search)
- // .setIcon(R.drawable.ic_menu_search).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
- menu.add(0, Id.menu.option.import_from_file, 5, R.string.menu_import_from_file)
- .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
- menu.add(0, Id.menu.option.export_keys, 6, R.string.menu_export_keys).setShowAsAction(
- MenuItem.SHOW_AS_ACTION_NEVER | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
-
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
-
- case android.R.id.home:
- // app icon in Action Bar clicked; go home
- Intent intent = new Intent(this, MainActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(intent);
- return true;
-
- case Id.menu.option.import_from_file: {
- Intent intentImportFromFile = new Intent(this, ImportKeysActivity.class);
- intentImportFromFile.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE);
- startActivityForResult(intentImportFromFile, 0);
- return true;
- }
-
- case Id.menu.option.export_keys: {
- showExportKeysDialog(-1);
- return true;
- }
-
- // case Id.menu.option.search:
- // startSearch("", false, null, false);
- // return true;
-
- default: {
- return super.onOptionsItemSelected(item);
- }
- }
- }
-
- /**
- * Show dialog where to export keys
- *
- * @param keyRingMasterKeyId
- * if -1 export all keys
- */
- public void showExportKeysDialog(final long keyRingMasterKeyId) {
- // Message is received after file is selected
- Handler returnHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.what == FileDialogFragment.MESSAGE_OKAY) {
- Bundle data = message.getData();
- mExportFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME);
-
- exportKeys(keyRingMasterKeyId);
- }
- }
- };
-
- // Create a new Messenger for the communication back
- final Messenger messenger = new Messenger(returnHandler);
-
- DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
- public void run() {
- String title = null;
- if (keyRingMasterKeyId != -1) {
- // single key export
- title = getString(R.string.title_export_key);
- } else {
- title = getString(R.string.title_export_keys);
- }
-
- String message = null;
- if (mKeyType == Id.type.public_key) {
- message = getString(R.string.specify_file_to_export_to);
- } else {
- message = getString(R.string.specify_file_to_export_secret_keys_to);
- }
-
- mFileDialog = FileDialogFragment.newInstance(messenger, title, message,
- mExportFilename, null, Id.request.filename);
-
- mFileDialog.show(getSupportFragmentManager(), "fileDialog");
- }
- });
- }
-
- /**
- * Show dialog to delete key
- *
- * @param keyRingIds
- */
- public void showDeleteKeyDialog(long[] keyRingIds) {
- // Message is received after key is deleted
- Handler returnHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) {
- // no further actions needed
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(returnHandler);
-
- DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger,
- keyRingIds, mKeyType);
-
- deleteKeyDialog.show(getSupportFragmentManager(), "deleteKeyDialog");
- }
-
- /**
- * Export keys
- *
- * @param keyRingMasterKeyId
- * if -1 export all keys
- */
- public void exportKeys(long keyRingMasterKeyId) {
- Log.d(Constants.TAG, "exportKeys started");
-
- // Send all information needed to service to export key in other thread
- Intent intent = new Intent(this, KeychainIntentService.class);
-
- intent.setAction(KeychainIntentService.ACTION_EXPORT_KEYRING);
-
- // fill values for this action
- Bundle data = new Bundle();
-
- data.putString(KeychainIntentService.EXPORT_FILENAME, mExportFilename);
- data.putInt(KeychainIntentService.EXPORT_KEY_TYPE, mKeyType);
-
- if (keyRingMasterKeyId == -1) {
- data.putBoolean(KeychainIntentService.EXPORT_ALL, true);
- } else {
- data.putLong(KeychainIntentService.EXPORT_KEY_RING_MASTER_KEY_ID, keyRingMasterKeyId);
- }
-
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
-
- // Message is received after exporting is done in ApgService
- KeychainIntentServiceHandler exportHandler = new KeychainIntentServiceHandler(this,
- R.string.progress_exporting, ProgressDialog.STYLE_HORIZONTAL) {
- public void handleMessage(Message message) {
- // handle messages by standard ApgHandler first
- super.handleMessage(message);
-
- if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
- // get returned data bundle
- Bundle returnData = message.getData();
-
- int exported = returnData.getInt(KeychainIntentService.RESULT_EXPORT);
- String toastMessage;
- if (exported == 1) {
- toastMessage = getString(R.string.key_exported);
- } else if (exported > 0) {
- toastMessage = getString(R.string.keys_exported, exported);
- } else {
- toastMessage = getString(R.string.no_keys_exported);
- }
- Toast.makeText(KeyListActivity.this, toastMessage, Toast.LENGTH_SHORT).show();
-
- }
- };
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(exportHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
-
- // show progress dialog
- exportHandler.showProgressDialog(this);
-
- // start service with intent
- startService(intent);
- }
-}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java
index 95a3dd3b1..d281d1559 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java
@@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.ui;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.ActionBarHelper;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
@@ -27,72 +28,49 @@ import com.actionbarsherlock.view.MenuItem;
import android.content.Intent;
import android.os.Bundle;
-public class KeyListPublicActivity extends KeyListActivity {
+public class KeyListPublicActivity extends KeyActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mKeyType = Id.type.public_key;
-
setContentView(R.layout.key_list_public_activity);
- mExportFilename = Constants.path.APP_DIR + "/pubexport.asc";
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setHomeButtonEnabled(true);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
- menu.add(1, Id.menu.option.key_server, 1, R.string.menu_key_server)
- .setIcon(R.drawable.ic_menu_search_list)
- .setShowAsAction(
- MenuItem.SHOW_AS_ACTION_ALWAYS);
- menu.add(1, Id.menu.option.import_from_qr_code, 2, R.string.menu_import_from_qr_code)
- .setShowAsAction(
- MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
- menu.add(1, Id.menu.option.import_from_nfc, 3, R.string.menu_import_from_nfc)
- .setShowAsAction(
- MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
-
+ getSupportMenuInflater().inflate(R.menu.key_list_public, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
- case Id.menu.option.key_server: {
- startActivityForResult(new Intent(this, KeyServerQueryActivity.class), 0);
+ case android.R.id.home:
+ // app icon in Action Bar clicked; go home
+ Intent intent = new Intent(this, MainActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(intent);
return true;
- }
- case Id.menu.option.import_from_file: {
- Intent intentImportFromFile = new Intent(this, ImportKeysActivity.class);
- intentImportFromFile.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE);
- startActivityForResult(intentImportFromFile, 0);
-
- return true;
- }
-
- case Id.menu.option.import_from_qr_code: {
+ case R.id.menu_key_list_public_import:
Intent intentImportFromFile = new Intent(this, ImportKeysActivity.class);
- intentImportFromFile.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_QR_CODE);
+ intentImportFromFile.setAction(ImportKeysActivity.ACTION_IMPORT_KEY);
startActivityForResult(intentImportFromFile, Id.request.import_from_qr_code);
return true;
- }
-
- case Id.menu.option.import_from_nfc: {
- Intent intentImportFromFile = new Intent(this, ImportKeysActivity.class);
- intentImportFromFile.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_NFC);
- startActivityForResult(intentImportFromFile, 0);
+ case R.id.menu_key_list_public_export:
+ showExportKeysDialog(null, Id.type.public_key, Constants.path.APP_DIR
+ + "/pubexport.asc");
return true;
- }
-
- default: {
+ default:
return super.onOptionsItemSelected(item);
}
- }
}
// @Override
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
index fde90bb32..a21fa7ebd 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
@@ -56,7 +56,7 @@ import android.widget.ListView;
public class KeyListPublicFragment extends Fragment implements AdapterView.OnItemClickListener,
LoaderManager.LoaderCallbacks {
- private KeyListPublicActivity mKeyListPublicActivity;
+// private KeyListPublicActivity mKeyListPublicActivity;
private KeyListPublicAdapter mAdapter;
private StickyListHeadersListView mStickyList;
@@ -77,7 +77,7 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
- mKeyListPublicActivity = (KeyListPublicActivity) getActivity();
+// mKeyListPublicActivity = (KeyListPublicActivity) getActivity();
mStickyList = (StickyListHeadersListView) getActivity().findViewById(R.id.list);
mStickyList.setOnItemClickListener(this);
@@ -164,7 +164,7 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte
// setListShown(false);
// Create an empty adapter we will use to display the loaded data.
- mAdapter = new KeyListPublicAdapter(mKeyListPublicActivity, null, Id.type.public_key,
+ mAdapter = new KeyListPublicAdapter(getActivity(), null, Id.type.public_key,
USER_ID_INDEX);
mStickyList.setAdapter(mAdapter);
@@ -222,7 +222,7 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte
*/
@Override
public void onItemClick(AdapterView> adapterView, View view, int position, long id) {
- Intent detailsIntent = new Intent(mKeyListPublicActivity, KeyViewActivity.class);
+ Intent detailsIntent = new Intent(getActivity(), KeyViewActivity.class);
detailsIntent.setData(KeychainContract.KeyRings.buildPublicKeyRingsUri(Long.toString(id)));
startActivity(detailsIntent);
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java
index c19cdeea5..4db20e6b7 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java
@@ -27,47 +27,57 @@ import android.os.Bundle;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
-public class KeyListSecretActivity extends KeyListActivity {
+public class KeyListSecretActivity extends KeyActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mKeyType = Id.type.secret_key;
-
setContentView(R.layout.key_list_secret_activity);
- mExportFilename = Constants.path.APP_DIR + "/secexport.asc";
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setHomeButtonEnabled(true);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
- menu.add(1, Id.menu.option.create, 1, R.string.menu_create_key).setShowAsAction(
- MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
- menu.add(1, Id.menu.option.createExpert, 2, R.string.menu_create_key_expert)
- .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
-
+ getSupportMenuInflater().inflate(R.menu.key_list_secret, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
- case Id.menu.option.create: {
- createKey();
+ case android.R.id.home:
+ // app icon in Action Bar clicked; go home
+ Intent intent = new Intent(this, MainActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(intent);
+
return true;
- }
+ case R.id.menu_key_list_secret_create:
+ createKey();
- case Id.menu.option.createExpert: {
+ return true;
+ case R.id.menu_key_list_secret_create_expert:
createKeyExpert();
+
return true;
- }
+ case R.id.menu_key_list_secret_export:
+ showExportKeysDialog(null, Id.type.secret_key, Constants.path.APP_DIR
+ + "/secexport.asc");
+
+ return true;
+ case R.id.menu_key_list_secret_import:
+ Intent intentImportFromFile = new Intent(this, ImportKeysActivity.class);
+ intentImportFromFile.setAction(ImportKeysActivity.ACTION_IMPORT_KEY);
+ startActivityForResult(intentImportFromFile, Id.request.import_from_qr_code);
- default: {
+ return true;
+ default:
return super.onOptionsItemSelected(item);
}
- }
}
private void createKey() {
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java
index c32a797be..d7e2b8063 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java
@@ -31,6 +31,7 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.ShareNfcDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.ShareQrCodeDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
@@ -134,10 +135,21 @@ public class KeyViewActivity extends KeyActivity implements CreateNdefMessageCal
case R.id.menu_key_view_share_clipboard:
copyToClipboard(mDataUri);
return true;
- case R.id.menu_key_view_delete:
- deleteKey(mDataUri, Id.type.public_key);
+ case R.id.menu_key_view_delete: {
+ // Message is received after key is deleted
+ Handler returnHandler = new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) {
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+ }
+ };
+
+ deleteKey(mDataUri, Id.type.public_key, returnHandler);
return true;
-
+ }
}
return super.onOptionsItemSelected(item);
}
--
cgit v1.2.3
From a2f0667593be2f7ecd5b0e2445321a22a90eea9e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Tue, 7 Jan 2014 22:59:39 +0100
Subject: action encrypt to contact in view and multi select
---
OpenPGP-Keychain/res/layout/key_view_activity.xml | 13 ++++++
.../res/menu/key_list_multi_selection.xml | 9 ----
.../res/menu/key_list_public_multi.xml | 12 ++++++
OpenPGP-Keychain/res/values/strings.xml | 7 +++-
.../keychain/ui/KeyListPublicFragment.java | 49 +++++++++++++++-------
.../keychain/ui/KeyViewActivity.java | 20 ++++++++-
6 files changed, 85 insertions(+), 25 deletions(-)
delete mode 100644 OpenPGP-Keychain/res/menu/key_list_multi_selection.xml
create mode 100644 OpenPGP-Keychain/res/menu/key_list_public_multi.xml
diff --git a/OpenPGP-Keychain/res/layout/key_view_activity.xml b/OpenPGP-Keychain/res/layout/key_view_activity.xml
index 326959e97..8c6281d5b 100644
--- a/OpenPGP-Keychain/res/layout/key_view_activity.xml
+++ b/OpenPGP-Keychain/res/layout/key_view_activity.xml
@@ -113,4 +113,17 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
+
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/menu/key_list_multi_selection.xml b/OpenPGP-Keychain/res/menu/key_list_multi_selection.xml
deleted file mode 100644
index 64f8564c1..000000000
--- a/OpenPGP-Keychain/res/menu/key_list_multi_selection.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/menu/key_list_public_multi.xml b/OpenPGP-Keychain/res/menu/key_list_public_multi.xml
new file mode 100644
index 000000000..92481e9cb
--- /dev/null
+++ b/OpenPGP-Keychain/res/menu/key_list_public_multi.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/values/strings.xml b/OpenPGP-Keychain/res/values/strings.xml
index d870c1289..62beb64f7 100644
--- a/OpenPGP-Keychain/res/values/strings.xml
+++ b/OpenPGP-Keychain/res/values/strings.xml
@@ -55,6 +55,7 @@
DefaultsAdvancedMaster Key
+ ActionsSign (Clipboard)
@@ -102,7 +103,8 @@
Sign keyBeam settingsCancel
-
+ Encrypt to…
+
SignMessage
@@ -357,4 +359,7 @@
%d keys selected.
+
+ Encrypt to this contact
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
index a21fa7ebd..d9fa0948a 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
@@ -22,6 +22,7 @@ import java.util.Set;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.ui.adapter.KeyListPublicAdapter;
@@ -56,7 +57,7 @@ import android.widget.ListView;
public class KeyListPublicFragment extends Fragment implements AdapterView.OnItemClickListener,
LoaderManager.LoaderCallbacks {
-// private KeyListPublicActivity mKeyListPublicActivity;
+ // private KeyListPublicActivity mKeyListPublicActivity;
private KeyListPublicAdapter mAdapter;
private StickyListHeadersListView mStickyList;
@@ -77,7 +78,7 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
-// mKeyListPublicActivity = (KeyListPublicActivity) getActivity();
+ // mKeyListPublicActivity = (KeyListPublicActivity) getActivity();
mStickyList = (StickyListHeadersListView) getActivity().findViewById(R.id.list);
mStickyList.setOnItemClickListener(this);
@@ -105,7 +106,7 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
android.view.MenuInflater inflater = getActivity().getMenuInflater();
- inflater.inflate(R.menu.key_list_multi_selection, menu);
+ inflater.inflate(R.menu.key_list_public_multi, menu);
return true;
}
@@ -117,20 +118,27 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
Set positions = mAdapter.getCurrentCheckedPosition();
+
+ // get IDs for checked positions as long array
+ long[] ids = new long[positions.size()];
+ int i = 0;
+ for (int pos : positions) {
+ ids[i] = mAdapter.getItemId(pos);
+ i++;
+ }
+
switch (item.getItemId()) {
- case R.id.delete_entry:
-
- // get IDs for checked positions as long array
- long[] ids = new long[positions.size()];
- int i = 0;
- for (int pos : positions) {
- ids[i] = mAdapter.getItemId(pos);
- i++;
- }
+ case R.id.menu_key_list_public_multi_encrypt: {
+ encrypt(ids);
+
+ break;
+ }
+ case R.id.menu_key_list_public_multi_delete: {
showDeleteKeyDialog(ids);
break;
}
+ }
return false;
}
@@ -164,8 +172,7 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte
// setListShown(false);
// Create an empty adapter we will use to display the loaded data.
- mAdapter = new KeyListPublicAdapter(getActivity(), null, Id.type.public_key,
- USER_ID_INDEX);
+ mAdapter = new KeyListPublicAdapter(getActivity(), null, Id.type.public_key, USER_ID_INDEX);
mStickyList.setAdapter(mAdapter);
// Prepare the loader. Either re-connect with an existing one,
@@ -227,6 +234,20 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte
startActivity(detailsIntent);
}
+ public void encrypt(long[] keyRingRowIds) {
+ // get master key ids from row ids
+ long[] keyRingIds = new long[keyRingRowIds.length];
+ for (int i = 0; i < keyRingRowIds.length; i++) {
+ keyRingIds[i] = ProviderHelper.getPublicMasterKeyId(getActivity(), keyRingRowIds[i]);
+ }
+
+ Intent intent = new Intent(getActivity(), EncryptActivity.class);
+ intent.setAction(EncryptActivity.ACTION_ENCRYPT);
+ intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, keyRingIds);
+ // used instead of startActivity set actionbar based on callingPackage
+ startActivityForResult(intent, 0);
+ }
+
/**
* Show dialog to delete key
*
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java
index d7e2b8063..7d51c2ff4 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java
@@ -50,6 +50,9 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.format.DateFormat;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
@@ -67,6 +70,7 @@ public class KeyViewActivity extends KeyActivity implements CreateNdefMessageCal
private TextView mFingerint;
private TextView mExpiry;
private TextView mCreation;
+ private Button mActionEncrypt;
// NFC
private NfcAdapter mNfcAdapter;
@@ -86,6 +90,7 @@ public class KeyViewActivity extends KeyActivity implements CreateNdefMessageCal
mExpiry = (TextView) this.findViewById(R.id.expiry);
mCreation = (TextView) this.findViewById(R.id.creation);
mAlgorithm = (TextView) this.findViewById(R.id.algorithm);
+ mActionEncrypt = (Button) this.findViewById(R.id.action_encrypt);
Intent intent = getIntent();
mDataUri = intent.getData();
@@ -146,7 +151,7 @@ public class KeyViewActivity extends KeyActivity implements CreateNdefMessageCal
}
}
};
-
+
deleteKey(mDataUri, Id.type.public_key, returnHandler);
return true;
}
@@ -172,6 +177,19 @@ public class KeyViewActivity extends KeyActivity implements CreateNdefMessageCal
mCreation.setText(DateFormat.getDateFormat(getApplicationContext()).format(
PgpKeyHelper.getCreationDate(mPublicKey)));
mAlgorithm.setText(PgpKeyHelper.getAlgorithmInfo(mPublicKey));
+
+ mActionEncrypt.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ long[] encryptionKeyIds = new long[] { mPublicKey.getKeyID() };
+ Intent intent = new Intent(KeyViewActivity.this, EncryptActivity.class);
+ intent.setAction(EncryptActivity.ACTION_ENCRYPT);
+ intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, encryptionKeyIds);
+ // used instead of startActivity set actionbar based on callingPackage
+ startActivityForResult(intent, 0);
+ }
+ });
}
/**
--
cgit v1.2.3
From f76d33d2306d3377ca4fd8a15168176fa121966c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Tue, 7 Jan 2014 23:02:23 +0100
Subject: remove unused ids
---
.../src/org/sufficientlysecure/keychain/Id.java | 43 ----------------------
1 file changed, 43 deletions(-)
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/Id.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/Id.java
index e9b0b67d4..fb7851774 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/Id.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/Id.java
@@ -27,18 +27,7 @@ import org.spongycastle.bcpg.CompressionAlgorithmTags;
*/
public final class Id {
- public static final String TAG = "APG";
-
public static final class menu {
- public static final int export = 0x21070001;
- public static final int delete = 0x21070002;
- public static final int edit = 0x21070003;
- public static final int update = 0x21070004;
- public static final int exportToServer = 0x21070005;
- public static final int share = 0x21070006;
- public static final int share_qr_code = 0x21070007;
- public static final int share_nfc = 0x21070008;
- public static final int signKey = 0x21070009;
public static final class option {
public static final int new_pass_phrase = 0x21070001;
@@ -82,20 +71,6 @@ public final class Id {
public static final int unknown_signature_key = 0x00006011;
}
- // public static final class message {
- // public static final int progress_update = 0x21070001;
- // public static final int done = 0x21070002;
- // public static final int import_keys = 0x21070003;
- // public static final int export_keys = 0x21070004;
- // public static final int import_done = 0x21070005;
- // public static final int export_done = 0x21070006;
- // public static final int create_key = 0x21070007;
- // public static final int edit_key = 0x21070008;
- // public static final int delete_done = 0x21070009;
- // public static final int query_done = 0x21070010;
- // public static final int unknown_signature_key = 0x21070011;
- // }
-
// use only lower 16 bits due to compatibility lib
public static final class request {
public static final int public_keys = 0x00007001;
@@ -109,18 +84,6 @@ public final class Id {
public static final int sign_key = 0x00007009;
}
- // public static final class request {
- // public static final int public_keys = 0x21070001;
- // public static final int secret_keys = 0x21070002;
- // public static final int filename = 0x21070003;
- // public static final int output_filename = 0x21070004;
- // public static final int key_server_preference = 0x21070005;
- // public static final int look_up_key_id = 0x21070006;
- // public static final int export_to_server = 0x21070007;
- // public static final int import_from_qr_code = 0x21070008;
- // public static final int sign_key = 0x21070009;
- // }
-
public static final class dialog {
public static final int pass_phrase = 0x21070001;
public static final int encrypting = 0x21070002;
@@ -136,7 +99,6 @@ public final class Id {
public static final int export_keys = 0x2107000c;
public static final int exporting = 0x2107000d;
public static final int new_account = 0x2107000e;
- // public static final int about = 0x2107000f;
public static final int change_log = 0x21070010;
public static final int output_filename = 0x21070011;
public static final int delete_file = 0x21070012;
@@ -152,11 +114,6 @@ public final class Id {
public static final int export_keys = 0x21070002;
}
- // public static final class database {
- // public static final int type_public = 0;
- // public static final int type_secret = 1;
- // }
-
public static final class type {
public static final int public_key = 0x21070001;
public static final int secret_key = 0x21070002;
--
cgit v1.2.3
From 2ccfc09f440c3805674f34a599b42e16b430dcc6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Tue, 7 Jan 2014 23:48:16 +0100
Subject: instructions on empty public key list for first start
---
.../res/layout/key_list_public_fragment.xml | 54 +++++++++++++++++++---
OpenPGP-Keychain/res/values/strings.xml | 9 +++-
.../keychain/ui/KeyListPublicFragment.java | 32 ++++++++++++-
3 files changed, 87 insertions(+), 8 deletions(-)
diff --git a/OpenPGP-Keychain/res/layout/key_list_public_fragment.xml b/OpenPGP-Keychain/res/layout/key_list_public_fragment.xml
index 2aca81cd6..06719749c 100644
--- a/OpenPGP-Keychain/res/layout/key_list_public_fragment.xml
+++ b/OpenPGP-Keychain/res/layout/key_list_public_fragment.xml
@@ -1,5 +1,5 @@
-
@@ -16,13 +16,55 @@
android:paddingRight="32dp"
android:scrollbarStyle="outsideOverlay" />
-
+ android:orientation="vertical"
+ android:visibility="gone" >
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/values/strings.xml b/OpenPGP-Keychain/res/values/strings.xml
index 62beb64f7..792f2cc85 100644
--- a/OpenPGP-Keychain/res/values/strings.xml
+++ b/OpenPGP-Keychain/res/values/strings.xml
@@ -104,7 +104,7 @@
Beam settingsCancelEncrypt to…
-
+
SignMessage
@@ -354,11 +354,18 @@
QR Code %1$d of %2$dShare with NFC
+
1 key selected.%d keys selected.
+ No keys available yet…
+ You can start by
+ or
+ creating your own key pair
+ importing public keys.
+
Encrypt to this contact
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
index d9fa0948a..b5ca0269d 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
@@ -46,8 +46,10 @@ import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+import android.view.View.OnClickListener;
import android.widget.AbsListView.MultiChoiceModeListener;
import android.widget.AdapterView;
+import android.widget.Button;
import android.widget.ListView;
/**
@@ -57,16 +59,44 @@ import android.widget.ListView;
public class KeyListPublicFragment extends Fragment implements AdapterView.OnItemClickListener,
LoaderManager.LoaderCallbacks {
- // private KeyListPublicActivity mKeyListPublicActivity;
private KeyListPublicAdapter mAdapter;
private StickyListHeadersListView mStickyList;
+ // empty layout
+ private Button mButtonEmptyCreate;
+ private Button mButtonEmptyImport;
+
/**
* Load custom layout with StickyListView from library
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.key_list_public_fragment, container, false);
+
+ mButtonEmptyCreate = (Button) view.findViewById(R.id.key_list_empty_button_create);
+ mButtonEmptyCreate.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(getActivity(), EditKeyActivity.class);
+ intent.setAction(EditKeyActivity.ACTION_CREATE_KEY);
+ intent.putExtra(EditKeyActivity.EXTRA_GENERATE_DEFAULT_KEYS, true);
+ intent.putExtra(EditKeyActivity.EXTRA_USER_IDS, ""); // show user id view
+ startActivityForResult(intent, 0);
+ }
+ });
+
+ mButtonEmptyImport = (Button) view.findViewById(R.id.key_list_empty_button_import);
+ mButtonEmptyImport.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ Intent intentImportFromFile = new Intent(getActivity(), ImportKeysActivity.class);
+ intentImportFromFile.setAction(ImportKeysActivity.ACTION_IMPORT_KEY);
+ startActivityForResult(intentImportFromFile, Id.request.import_from_qr_code);
+ }
+ });
+
return view;
}
--
cgit v1.2.3
From 1970d4be6eb9d4bd09d3e4a95a344454e497be24 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Tue, 7 Jan 2014 23:54:12 +0100
Subject: multi selection for secret key list
---
OpenPGP-Keychain/res/values/strings.xml | 2 +-
.../keychain/ui/KeyListSecretFragment.java | 92 ++++++++++++++++++++++
.../keychain/ui/adapter/KeyListSecretAdapter.java | 51 +++++++++++-
3 files changed, 143 insertions(+), 2 deletions(-)
diff --git a/OpenPGP-Keychain/res/values/strings.xml b/OpenPGP-Keychain/res/values/strings.xml
index 792f2cc85..b02d72bb8 100644
--- a/OpenPGP-Keychain/res/values/strings.xml
+++ b/OpenPGP-Keychain/res/values/strings.xml
@@ -364,7 +364,7 @@
You can start byorcreating your own key pair
- importing public keys.
+ importing keys.Encrypt to this contact
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java
index 40579a3c2..c0b67719e 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java
@@ -17,22 +17,32 @@
package org.sufficientlysecure.keychain.ui;
+import java.util.Set;
+
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.ui.adapter.KeyListSecretAdapter;
+import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
+import android.annotation.SuppressLint;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
+import android.view.ActionMode;
+import android.view.Menu;
+import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
+import android.widget.ListView;
+import android.widget.AbsListView.MultiChoiceModeListener;
import android.widget.AdapterView.OnItemClickListener;
import com.actionbarsherlock.app.SherlockListFragment;
@@ -47,6 +57,7 @@ public class KeyListSecretFragment extends SherlockListFragment implements
/**
* Define Adapter and Loader on create of Activity
*/
+ @SuppressLint("NewApi")
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
@@ -59,6 +70,75 @@ public class KeyListSecretFragment extends SherlockListFragment implements
// application this would come from a resource.
setEmptyText(getString(R.string.list_empty));
+ /*
+ * ActionBarSherlock does not support MultiChoiceModeListener. Thus multi-selection is only
+ * available for Android >= 3.0
+ */
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
+ getListView().setMultiChoiceModeListener(new MultiChoiceModeListener() {
+
+ private int count = 0;
+
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ android.view.MenuInflater inflater = getActivity().getMenuInflater();
+ inflater.inflate(R.menu.key_list_secret_multi, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ return false;
+ }
+
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ Set positions = mAdapter.getCurrentCheckedPosition();
+
+ // get IDs for checked positions as long array
+ long[] ids = new long[positions.size()];
+ int i = 0;
+ for (int pos : positions) {
+ ids[i] = mAdapter.getItemId(pos);
+ i++;
+ }
+
+ switch (item.getItemId()) {
+ case R.id.menu_key_list_public_multi_delete: {
+ showDeleteKeyDialog(ids);
+
+ break;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {
+ count = 0;
+ mAdapter.clearSelection();
+ }
+
+ @Override
+ public void onItemCheckedStateChanged(ActionMode mode, int position, long id,
+ boolean checked) {
+ if (checked) {
+ count++;
+ mAdapter.setNewSelection(position, checked);
+ } else {
+ count--;
+ mAdapter.removeSelection(position);
+ }
+
+ String keysSelected = getResources().getQuantityString(
+ R.plurals.key_list_selected_keys, count, count);
+ mode.setTitle(keysSelected);
+ }
+
+ });
+ }
+
// We have a menu item to show in action bar.
setHasOptionsMenu(true);
@@ -123,4 +203,16 @@ public class KeyListSecretFragment extends SherlockListFragment implements
editIntent.setAction(EditKeyActivity.ACTION_EDIT_KEY);
startActivityForResult(editIntent, 0);
}
+
+ /**
+ * Show dialog to delete key
+ *
+ * @param keyRingRowIds
+ */
+ public void showDeleteKeyDialog(long[] keyRingRowIds) {
+ DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(null,
+ keyRingRowIds, Id.type.secret_key);
+
+ deleteKeyDialog.show(getActivity().getSupportFragmentManager(), "deleteKeyDialog");
+ }
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java
index 2fda8a9f7..f78eaf627 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java
@@ -17,12 +17,17 @@
package org.sufficientlysecure.keychain.ui.adapter;
+import java.util.HashMap;
+import java.util.Set;
+
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.OtherHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.database.Cursor;
+import android.graphics.Color;
import android.support.v4.widget.CursorAdapter;
import android.view.LayoutInflater;
import android.view.View;
@@ -30,9 +35,11 @@ import android.view.ViewGroup;
import android.widget.TextView;
public class KeyListSecretAdapter extends CursorAdapter {
-
private LayoutInflater mInflater;
+ @SuppressLint("UseSparseArrays")
+ private HashMap mSelection = new HashMap();
+
public KeyListSecretAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
@@ -74,4 +81,46 @@ public class KeyListSecretAdapter extends CursorAdapter {
return mInflater.inflate(R.layout.key_list_item, null);
}
+ /** -------------------------- MULTI-SELECTION METHODS -------------- */
+ public void setNewSelection(int position, boolean value) {
+ mSelection.put(position, value);
+ notifyDataSetChanged();
+ }
+
+ public boolean isPositionChecked(int position) {
+ Boolean result = mSelection.get(position);
+ return result == null ? false : result;
+ }
+
+ public Set getCurrentCheckedPosition() {
+ return mSelection.keySet();
+ }
+
+ public void removeSelection(int position) {
+ mSelection.remove(position);
+ notifyDataSetChanged();
+ }
+
+ public void clearSelection() {
+ mSelection.clear();
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ // let the adapter handle setting up the row views
+ View v = super.getView(position, convertView, parent);
+
+ /**
+ * Change color for multi-selection
+ */
+ // default color
+ v.setBackgroundColor(Color.TRANSPARENT);
+ if (mSelection.get(position) != null) {
+ // this is a selected position, change color!
+ v.setBackgroundColor(parent.getResources().getColor(R.color.emphasis));
+ }
+ return v;
+ }
+
}
--
cgit v1.2.3
From 14a8738933bd2bb323ee05a47421881337f6e9d7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Wed, 8 Jan 2014 00:52:09 +0100
Subject: fix import, add missing multi select menu
---
OpenPGP-Keychain/res/menu/key_list_secret_multi.xml | 9 +++++++++
.../sufficientlysecure/keychain/ui/KeyListPublicActivity.java | 8 +++-----
.../sufficientlysecure/keychain/ui/KeyListPublicFragment.java | 1 -
.../sufficientlysecure/keychain/ui/KeyListSecretActivity.java | 1 -
4 files changed, 12 insertions(+), 7 deletions(-)
create mode 100644 OpenPGP-Keychain/res/menu/key_list_secret_multi.xml
diff --git a/OpenPGP-Keychain/res/menu/key_list_secret_multi.xml b/OpenPGP-Keychain/res/menu/key_list_secret_multi.xml
new file mode 100644
index 000000000..a10d1e2fa
--- /dev/null
+++ b/OpenPGP-Keychain/res/menu/key_list_secret_multi.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java
index d281d1559..023c8fd9d 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java
@@ -20,14 +20,13 @@ package org.sufficientlysecure.keychain.ui;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.helper.ActionBarHelper;
-
-import com.actionbarsherlock.view.Menu;
-import com.actionbarsherlock.view.MenuItem;
import android.content.Intent;
import android.os.Bundle;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
+
public class KeyListPublicActivity extends KeyActivity {
@Override
@@ -59,7 +58,6 @@ public class KeyListPublicActivity extends KeyActivity {
return true;
case R.id.menu_key_list_public_import:
Intent intentImportFromFile = new Intent(this, ImportKeysActivity.class);
- intentImportFromFile.setAction(ImportKeysActivity.ACTION_IMPORT_KEY);
startActivityForResult(intentImportFromFile, Id.request.import_from_qr_code);
return true;
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
index b5ca0269d..3ce1d08d0 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
@@ -92,7 +92,6 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte
@Override
public void onClick(View v) {
Intent intentImportFromFile = new Intent(getActivity(), ImportKeysActivity.class);
- intentImportFromFile.setAction(ImportKeysActivity.ACTION_IMPORT_KEY);
startActivityForResult(intentImportFromFile, Id.request.import_from_qr_code);
}
});
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java
index 4db20e6b7..d95f03ae1 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java
@@ -71,7 +71,6 @@ public class KeyListSecretActivity extends KeyActivity {
return true;
case R.id.menu_key_list_secret_import:
Intent intentImportFromFile = new Intent(this, ImportKeysActivity.class);
- intentImportFromFile.setAction(ImportKeysActivity.ACTION_IMPORT_KEY);
startActivityForResult(intentImportFromFile, Id.request.import_from_qr_code);
return true;
--
cgit v1.2.3
From 11ed8d7e16abfb98cd96d8a4447382c83435dac8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Thu, 9 Jan 2014 12:31:45 +0100
Subject: add AndroidBootstrap lib
---
OpenPGP-Keychain/build.gradle | 1 +
OpenPGP-Keychain/project.properties | 1 +
OpenPGP-Keychain/res/raw/help_about.html | 6 +-
README.md | 13 +-
libraries/AndroidBootstrap/AndroidManifest.xml | 26 ++
libraries/AndroidBootstrap/build.gradle | 17 +
.../AndroidBootstrap/libs/android-support-v4.jar | Bin 0 -> 271754 bytes
libraries/AndroidBootstrap/proguard-project.txt | 20 +
libraries/AndroidBootstrap/project.properties | 15 +
.../res/drawable-hdpi/ic_launcher.png | Bin 0 -> 9397 bytes
.../res/drawable-mdpi/ic_launcher.png | Bin 0 -> 5237 bytes
.../res/drawable-xhdpi/ic_launcher.png | Bin 0 -> 14383 bytes
.../res/drawable/bbuton_danger.xml | 20 +
.../res/drawable/bbuton_danger_rounded.xml | 25 ++
.../res/drawable/bbuton_default.xml | 20 +
.../res/drawable/bbuton_default_rounded.xml | 23 ++
.../AndroidBootstrap/res/drawable/bbuton_info.xml | 20 +
.../res/drawable/bbuton_info_rounded.xml | 23 ++
.../res/drawable/bbuton_inverse.xml | 20 +
.../res/drawable/bbuton_inverse_rounded.xml | 23 ++
.../res/drawable/bbuton_primary.xml | 20 +
.../res/drawable/bbuton_primary_rounded.xml | 23 ++
.../res/drawable/bbuton_success.xml | 20 +
.../res/drawable/bbuton_success_rounded.xml | 23 ++
.../res/drawable/bbuton_warning.xml | 20 +
.../res/drawable/bbuton_warning_rounded.xml | 23 ++
.../res/drawable/bthumbnail_container_rounded.xml | 10 +
.../res/drawable/bthumbnail_container_square.xml | 9 +
.../drawable/bthumbnail_placeholder_default.xml | 9 +
.../res/drawable/edittext_background.xml | 18 +
.../res/drawable/edittext_background_danger.xml | 12 +
.../res/drawable/edittext_background_rounded.xml | 21 +
.../edittext_background_rounded_danger.xml | 17 +
.../edittext_background_rounded_success.xml | 14 +
.../edittext_background_rounded_warning.xml | 14 +
.../res/drawable/edittext_background_success.xml | 12 +
.../res/drawable/edittext_background_warning.xml | 12 +
.../res/drawable/thumbnail_circle.xml | 6 +
.../res/drawable/thumbnail_circle_container.xml | 12 +
.../res/drawable/thumbnail_circle_minimal.xml | 11 +
.../res/layout/bootstrap_button.xml | 40 ++
.../res/layout/bootstrap_button_fill.xml | 40 ++
.../res/layout/bootstrap_edit_text.xml | 14 +
.../res/layout/bootstrap_thumbnail.xml | 33 ++
.../res/layout/bootstrap_thumbnail_circle.xml | 32 ++
.../res/layout/font_awesome_text.xml | 14 +
.../AndroidBootstrap/res/layout/row_title.xml | 16 +
.../res/layout/row_title_and_subtitle.xml | 26 ++
.../res/layout/row_two_columns.xml | 28 ++
libraries/AndroidBootstrap/res/menu/main.xml | 9 +
.../AndroidBootstrap/res/values-sw600dp/dimens.xml | 8 +
.../res/values-sw720dp-land/dimens.xml | 9 +
.../AndroidBootstrap/res/values-v11/styles.xml | 11 +
.../AndroidBootstrap/res/values-v14/styles.xml | 12 +
libraries/AndroidBootstrap/res/values/attrs.xml | 50 +++
libraries/AndroidBootstrap/res/values/colors.xml | 65 +++
libraries/AndroidBootstrap/res/values/dimens.xml | 14 +
libraries/AndroidBootstrap/res/values/strings.xml | 8 +
libraries/AndroidBootstrap/res/values/styles.xml | 16 +
.../androidbootstrap/BootstrapButton.java | 445 +++++++++++++++++++++
.../androidbootstrap/BootstrapCircleThumbnail.java | 215 ++++++++++
.../androidbootstrap/BootstrapEditText.java | 188 +++++++++
.../androidbootstrap/BootstrapThumbnail.java | 209 ++++++++++
.../beardedhen/androidbootstrap/FontAwesome.java | 390 ++++++++++++++++++
.../androidbootstrap/FontAwesomeText.java | 274 +++++++++++++
.../androidbootstrap/utils/AutoResizeTextView.java | 303 ++++++++++++++
.../androidbootstrap/utils/ImageUtils.java | 77 ++++
settings.gradle | 1 +
68 files changed, 3121 insertions(+), 5 deletions(-)
create mode 100644 libraries/AndroidBootstrap/AndroidManifest.xml
create mode 100644 libraries/AndroidBootstrap/build.gradle
create mode 100644 libraries/AndroidBootstrap/libs/android-support-v4.jar
create mode 100644 libraries/AndroidBootstrap/proguard-project.txt
create mode 100644 libraries/AndroidBootstrap/project.properties
create mode 100644 libraries/AndroidBootstrap/res/drawable-hdpi/ic_launcher.png
create mode 100644 libraries/AndroidBootstrap/res/drawable-mdpi/ic_launcher.png
create mode 100644 libraries/AndroidBootstrap/res/drawable-xhdpi/ic_launcher.png
create mode 100644 libraries/AndroidBootstrap/res/drawable/bbuton_danger.xml
create mode 100644 libraries/AndroidBootstrap/res/drawable/bbuton_danger_rounded.xml
create mode 100644 libraries/AndroidBootstrap/res/drawable/bbuton_default.xml
create mode 100644 libraries/AndroidBootstrap/res/drawable/bbuton_default_rounded.xml
create mode 100644 libraries/AndroidBootstrap/res/drawable/bbuton_info.xml
create mode 100644 libraries/AndroidBootstrap/res/drawable/bbuton_info_rounded.xml
create mode 100644 libraries/AndroidBootstrap/res/drawable/bbuton_inverse.xml
create mode 100644 libraries/AndroidBootstrap/res/drawable/bbuton_inverse_rounded.xml
create mode 100644 libraries/AndroidBootstrap/res/drawable/bbuton_primary.xml
create mode 100644 libraries/AndroidBootstrap/res/drawable/bbuton_primary_rounded.xml
create mode 100644 libraries/AndroidBootstrap/res/drawable/bbuton_success.xml
create mode 100644 libraries/AndroidBootstrap/res/drawable/bbuton_success_rounded.xml
create mode 100644 libraries/AndroidBootstrap/res/drawable/bbuton_warning.xml
create mode 100644 libraries/AndroidBootstrap/res/drawable/bbuton_warning_rounded.xml
create mode 100644 libraries/AndroidBootstrap/res/drawable/bthumbnail_container_rounded.xml
create mode 100644 libraries/AndroidBootstrap/res/drawable/bthumbnail_container_square.xml
create mode 100644 libraries/AndroidBootstrap/res/drawable/bthumbnail_placeholder_default.xml
create mode 100644 libraries/AndroidBootstrap/res/drawable/edittext_background.xml
create mode 100644 libraries/AndroidBootstrap/res/drawable/edittext_background_danger.xml
create mode 100644 libraries/AndroidBootstrap/res/drawable/edittext_background_rounded.xml
create mode 100644 libraries/AndroidBootstrap/res/drawable/edittext_background_rounded_danger.xml
create mode 100644 libraries/AndroidBootstrap/res/drawable/edittext_background_rounded_success.xml
create mode 100644 libraries/AndroidBootstrap/res/drawable/edittext_background_rounded_warning.xml
create mode 100644 libraries/AndroidBootstrap/res/drawable/edittext_background_success.xml
create mode 100644 libraries/AndroidBootstrap/res/drawable/edittext_background_warning.xml
create mode 100644 libraries/AndroidBootstrap/res/drawable/thumbnail_circle.xml
create mode 100644 libraries/AndroidBootstrap/res/drawable/thumbnail_circle_container.xml
create mode 100644 libraries/AndroidBootstrap/res/drawable/thumbnail_circle_minimal.xml
create mode 100644 libraries/AndroidBootstrap/res/layout/bootstrap_button.xml
create mode 100644 libraries/AndroidBootstrap/res/layout/bootstrap_button_fill.xml
create mode 100644 libraries/AndroidBootstrap/res/layout/bootstrap_edit_text.xml
create mode 100644 libraries/AndroidBootstrap/res/layout/bootstrap_thumbnail.xml
create mode 100644 libraries/AndroidBootstrap/res/layout/bootstrap_thumbnail_circle.xml
create mode 100644 libraries/AndroidBootstrap/res/layout/font_awesome_text.xml
create mode 100644 libraries/AndroidBootstrap/res/layout/row_title.xml
create mode 100644 libraries/AndroidBootstrap/res/layout/row_title_and_subtitle.xml
create mode 100644 libraries/AndroidBootstrap/res/layout/row_two_columns.xml
create mode 100644 libraries/AndroidBootstrap/res/menu/main.xml
create mode 100644 libraries/AndroidBootstrap/res/values-sw600dp/dimens.xml
create mode 100644 libraries/AndroidBootstrap/res/values-sw720dp-land/dimens.xml
create mode 100644 libraries/AndroidBootstrap/res/values-v11/styles.xml
create mode 100644 libraries/AndroidBootstrap/res/values-v14/styles.xml
create mode 100644 libraries/AndroidBootstrap/res/values/attrs.xml
create mode 100644 libraries/AndroidBootstrap/res/values/colors.xml
create mode 100644 libraries/AndroidBootstrap/res/values/dimens.xml
create mode 100644 libraries/AndroidBootstrap/res/values/strings.xml
create mode 100644 libraries/AndroidBootstrap/res/values/styles.xml
create mode 100644 libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/BootstrapButton.java
create mode 100644 libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/BootstrapCircleThumbnail.java
create mode 100644 libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/BootstrapEditText.java
create mode 100644 libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/BootstrapThumbnail.java
create mode 100644 libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/FontAwesome.java
create mode 100644 libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/FontAwesomeText.java
create mode 100644 libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/utils/AutoResizeTextView.java
create mode 100644 libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/utils/ImageUtils.java
diff --git a/OpenPGP-Keychain/build.gradle b/OpenPGP-Keychain/build.gradle
index afc95fee8..143005caa 100644
--- a/OpenPGP-Keychain/build.gradle
+++ b/OpenPGP-Keychain/build.gradle
@@ -25,6 +25,7 @@ dependencies {
compile project(':libraries:HtmlTextView')
compile project(':libraries:StickyListHeaders:library')
compile project(':libraries:zxing')
+ compile project(':libraries:AndroidBootstrap')
}
android {
diff --git a/OpenPGP-Keychain/project.properties b/OpenPGP-Keychain/project.properties
index 73fc44378..76caac668 100644
--- a/OpenPGP-Keychain/project.properties
+++ b/OpenPGP-Keychain/project.properties
@@ -13,3 +13,4 @@ android.library.reference.1=../libraries/ActionBarSherlock
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
diff --git a/OpenPGP-Keychain/res/raw/help_about.html b/OpenPGP-Keychain/res/raw/help_about.html
index 0ed0d3b05..8b412ded0 100644
--- a/OpenPGP-Keychain/res/raw/help_about.html
+++ b/OpenPGP-Keychain/res/raw/help_about.html
@@ -14,6 +14,8 @@ And don't add newlines before or after p tags because of transifex -->
Dominik Schürmann (Lead developer)
Ash Hughes
Brian C. Barnes
+
Bahtiar 'kalkin' Gadimov
+
Developers APG 1.x
@@ -26,7 +28,9 @@ And don't add newlines before or after p tags because of transifex -->
diff --git a/OpenPGP-Keychain/res/values/styles.xml b/OpenPGP-Keychain/res/values/styles.xml
index ef3ff63f6..15214fa62 100644
--- a/OpenPGP-Keychain/res/values/styles.xml
+++ b/OpenPGP-Keychain/res/values/styles.xml
@@ -18,16 +18,6 @@
-
-
-
-
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DecryptActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DecryptActivity.java
index 78ad4c9be..f7fc49083 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DecryptActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DecryptActivity.java
@@ -60,7 +60,6 @@ import android.view.View.OnClickListener;
import android.view.animation.AnimationUtils;
import android.widget.CheckBox;
import android.widget.EditText;
-import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -70,6 +69,7 @@ import android.widget.ViewFlipper;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
+import com.beardedhen.androidbootstrap.BootstrapButton;
@SuppressLint("NewApi")
public class DecryptActivity extends SherlockFragmentActivity {
@@ -107,7 +107,7 @@ public class DecryptActivity extends SherlockFragmentActivity {
private EditText mFilename = null;
private CheckBox mDeleteAfter = null;
- private ImageButton mBrowse = null;
+ private BootstrapButton mBrowse = null;
private String mInputFilename = null;
private String mOutputFilename = null;
@@ -216,7 +216,7 @@ public class DecryptActivity extends SherlockFragmentActivity {
mMessage.setMinimumHeight(height);
mFilename = (EditText) findViewById(R.id.filename);
- mBrowse = (ImageButton) findViewById(R.id.btn_browse);
+ mBrowse = (BootstrapButton) findViewById(R.id.btn_browse);
mBrowse.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
FileHelper.openFile(DecryptActivity.this, mFilename.getText().toString(), "*/*",
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
index d141fbe3a..569a16c41 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
@@ -55,7 +55,6 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
-import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
@@ -64,6 +63,7 @@ import android.widget.Toast;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
+import com.beardedhen.androidbootstrap.BootstrapButton;
public class EditKeyActivity extends KeyActivity {
@@ -91,7 +91,7 @@ public class EditKeyActivity extends KeyActivity {
private String mCurrentPassPhrase = null;
private String mNewPassPhrase = null;
- private Button mChangePassPhrase;
+ private BootstrapButton mChangePassPhrase;
private CheckBox mNoPassphrase;
@@ -336,7 +336,7 @@ public class EditKeyActivity extends KeyActivity {
}
}
};
-
+
deleteKey(mDataUri, Id.type.secret_key, returnHandler);
return true;
}
@@ -426,7 +426,7 @@ public class EditKeyActivity extends KeyActivity {
setContentView(R.layout.edit_key);
// find views
- mChangePassPhrase = (Button) findViewById(R.id.edit_key_btn_change_pass_phrase);
+ mChangePassPhrase = (BootstrapButton) findViewById(R.id.edit_key_btn_change_pass_phrase);
mNoPassphrase = (CheckBox) findViewById(R.id.edit_key_no_passphrase);
// Build layout based on given userIds and keys
@@ -653,7 +653,7 @@ public class EditKeyActivity extends KeyActivity {
}
private void updatePassPhraseButtonText() {
- mChangePassPhrase.setText(isPassphraseSet() ? R.string.btn_change_passphrase
- : R.string.btn_set_passphrase);
+ mChangePassPhrase.setText(isPassphraseSet() ? getString(R.string.btn_change_passphrase)
+ : getString(R.string.btn_set_passphrase));
}
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptActivity.java
index 2dfdf254e..681548c9b 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptActivity.java
@@ -55,10 +55,8 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.view.animation.AnimationUtils;
import android.widget.ArrayAdapter;
-import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
-import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.Spinner;
import android.widget.TextView;
@@ -68,6 +66,7 @@ import android.widget.ViewFlipper;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
+import com.beardedhen.androidbootstrap.BootstrapButton;
public class EncryptActivity extends SherlockFragmentActivity {
@@ -87,7 +86,7 @@ public class EncryptActivity extends SherlockFragmentActivity {
private long mEncryptionKeyIds[] = null;
private EditText mMessage = null;
- private Button mSelectKeysButton = null;
+ private BootstrapButton mSelectKeysButton = null;
private boolean mEncryptEnabled = false;
private String mEncryptString = "";
@@ -117,7 +116,7 @@ public class EncryptActivity extends SherlockFragmentActivity {
private EditText mFilename = null;
private CheckBox mDeleteAfter = null;
- private ImageButton mBrowse = null;
+ private BootstrapButton mBrowse = null;
private String mInputFilename = null;
private String mOutputFilename = null;
@@ -825,7 +824,7 @@ public class EncryptActivity extends SherlockFragmentActivity {
mModeLabel.setOnClickListener(nextModeClickListener);
mMessage = (EditText) findViewById(R.id.message);
- mSelectKeysButton = (Button) findViewById(R.id.btn_selectEncryptKeys);
+ mSelectKeysButton = (BootstrapButton) findViewById(R.id.btn_selectEncryptKeys);
mSign = (CheckBox) findViewById(R.id.sign);
mMainUserId = (TextView) findViewById(R.id.mainUserId);
mMainUserIdRest = (TextView) findViewById(R.id.mainUserIdRest);
@@ -841,7 +840,7 @@ public class EncryptActivity extends SherlockFragmentActivity {
mMessage.setMinimumHeight(height);
mFilename = (EditText) findViewById(R.id.filename);
- mBrowse = (ImageButton) findViewById(R.id.btn_browse);
+ mBrowse = (BootstrapButton) findViewById(R.id.btn_browse);
mBrowse.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
FileHelper.openFile(EncryptActivity.this, mFilename.getText().toString(), "*/*",
@@ -896,9 +895,9 @@ public class EncryptActivity extends SherlockFragmentActivity {
private void updateView() {
if (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0) {
- mSelectKeysButton.setText(R.string.no_keys_selected);
+ mSelectKeysButton.setText(getString(R.string.no_keys_selected));
} else if (mEncryptionKeyIds.length == 1) {
- mSelectKeysButton.setText(R.string.one_key_selected);
+ mSelectKeysButton.setText(getString(R.string.one_key_selected));
} else {
mSelectKeysButton.setText("" + mEncryptionKeyIds.length + " "
+ getResources().getString(R.string.n_keys_selected));
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
index f83d469ac..94b5304a1 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
@@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.ui;
import java.util.ArrayList;
import java.util.List;
+
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.ActionBarHelper;
@@ -28,6 +29,7 @@ import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
+
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.ProgressDialog;
@@ -43,12 +45,15 @@ import android.os.Parcelable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.view.View;
+import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Toast;
+
import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.app.ActionBar.OnNavigationListener;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.MenuItem;
+import com.beardedhen.androidbootstrap.BootstrapButton;
public class ImportKeysActivity extends SherlockFragmentActivity implements OnNavigationListener {
public static final String ACTION_IMPORT_KEY = Constants.INTENT_PREFIX + "IMPORT_KEY";
@@ -74,12 +79,30 @@ public class ImportKeysActivity extends SherlockFragmentActivity implements OnNa
OnNavigationListener mOnNavigationListener;
String[] mNavigationStrings;
+ BootstrapButton mImportButton;
+ BootstrapButton mImportSignUploadButton;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.import_keys);
+ mImportButton = (BootstrapButton) findViewById(R.id.import_import);
+ mImportButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ importKeys();
+ }
+ });
+ mImportSignUploadButton = (BootstrapButton) findViewById(R.id.import_sign_and_upload);
+ mImportSignUploadButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ signAndUploadOnClick();
+ }
+ });
+
// set actionbar without home button if called from another app
ActionBarHelper.setBackButton(this);
@@ -398,11 +421,11 @@ public class ImportKeysActivity extends SherlockFragmentActivity implements OnNa
}
}
- public void importOnClick(View view) {
+ public void importOnClick() {
importKeys();
}
- public void signAndUploadOnClick(View view) {
+ public void signAndUploadOnClick() {
// first, import!
// importOnClick(view);
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java
index dcb7dbcc6..31f758395 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java
@@ -26,12 +26,13 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
-import android.widget.Button;
+
+import com.beardedhen.androidbootstrap.BootstrapButton;
public class ImportKeysClipboardFragment extends Fragment {
private ImportKeysActivity mImportActivity;
- private Button mButton;
+ private BootstrapButton mButton;
/**
* Creates new instance of this fragment
@@ -52,7 +53,7 @@ public class ImportKeysClipboardFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.import_keys_clipboard_fragment, container, false);
- mButton = (Button) view.findViewById(R.id.import_clipboard_button);
+ mButton = (BootstrapButton) view.findViewById(R.id.import_clipboard_button);
mButton.setOnClickListener(new OnClickListener() {
@Override
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java
index fbca9013b..ea76d2898 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java
@@ -31,14 +31,15 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
-import android.widget.ImageButton;
+
+import com.beardedhen.androidbootstrap.BootstrapButton;
public class ImportKeysFileFragment extends Fragment {
public static final String ARG_PATH = "path";
private ImportKeysActivity mImportActivity;
private EditText mFilename;
- private ImageButton mBrowse;
+ private BootstrapButton mBrowse;
/**
* Creates new instance of this fragment
@@ -61,7 +62,7 @@ public class ImportKeysFileFragment extends Fragment {
View view = inflater.inflate(R.layout.import_keys_file_fragment, container, false);
mFilename = (EditText) view.findViewById(R.id.import_keys_file_input);
- mBrowse = (ImageButton) view.findViewById(R.id.import_keys_file_browse);
+ mBrowse = (BootstrapButton) view.findViewById(R.id.import_keys_file_browse);
mBrowse.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java
index 2d756dde6..83af8cf48 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java
@@ -26,11 +26,12 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
-import android.widget.Button;
+
+import com.beardedhen.androidbootstrap.BootstrapButton;
public class ImportKeysNFCFragment extends Fragment {
- private Button mButton;
+ private BootstrapButton mButton;
/**
* Creates new instance of this fragment
@@ -51,7 +52,7 @@ public class ImportKeysNFCFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.import_keys_nfc_fragment, container, false);
- mButton = (Button) view.findViewById(R.id.import_nfc_button);
+ mButton = (BootstrapButton) view.findViewById(R.id.import_nfc_button);
mButton.setOnClickListener(new OnClickListener() {
@Override
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java
index 62b59b4f7..f9ead3a94 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java
@@ -30,18 +30,18 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
-import android.widget.Button;
import android.widget.ProgressBar;
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.IntentResult;
public class ImportKeysQrCodeFragment extends Fragment {
private ImportKeysActivity mImportActivity;
- private Button mButton;
+ private BootstrapButton mButton;
private TextView mText;
private ProgressBar mProgress;
@@ -66,7 +66,7 @@ public class ImportKeysQrCodeFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.import_keys_qr_code_fragment, container, false);
- mButton = (Button) view.findViewById(R.id.import_qrcode_button);
+ mButton = (BootstrapButton) view.findViewById(R.id.import_qrcode_button);
mText = (TextView) view.findViewById(R.id.import_qrcode_text);
mProgress = (ProgressBar) view.findViewById(R.id.import_qrcode_progress);
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java
index 106c8ebef..c985f1f60 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java
@@ -24,12 +24,13 @@ import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
import android.view.View.OnClickListener;
-import android.widget.Button;
+import android.view.ViewGroup;
+
+import com.beardedhen.androidbootstrap.BootstrapButton;
public class ImportKeysServerFragment extends Fragment {
- private Button mButton;
+ private BootstrapButton mButton;
/**
* Creates new instance of this fragment
@@ -50,7 +51,7 @@ public class ImportKeysServerFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.import_keys_keyserver_fragment, container, false);
- mButton = (Button) view.findViewById(R.id.import_keyserver_button);
+ mButton = (BootstrapButton) view.findViewById(R.id.import_keyserver_button);
mButton.setOnClickListener(new OnClickListener() {
@Override
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
index 3ce1d08d0..88503a5c4 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
@@ -22,9 +22,9 @@ import java.util.Set;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.provider.KeychainContract;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.ui.adapter.KeyListPublicAdapter;
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
@@ -45,13 +45,14 @@ import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
-import android.view.ViewGroup;
import android.view.View.OnClickListener;
+import android.view.ViewGroup;
import android.widget.AbsListView.MultiChoiceModeListener;
import android.widget.AdapterView;
-import android.widget.Button;
import android.widget.ListView;
+import com.beardedhen.androidbootstrap.BootstrapButton;
+
/**
* Public key list with sticky list headers. It does _not_ extend ListFragment because it uses
* StickyListHeaders library which does not extend upon ListView.
@@ -63,8 +64,8 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte
private StickyListHeadersListView mStickyList;
// empty layout
- private Button mButtonEmptyCreate;
- private Button mButtonEmptyImport;
+ private BootstrapButton mButtonEmptyCreate;
+ private BootstrapButton mButtonEmptyImport;
/**
* Load custom layout with StickyListView from library
@@ -73,7 +74,7 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.key_list_public_fragment, container, false);
- mButtonEmptyCreate = (Button) view.findViewById(R.id.key_list_empty_button_create);
+ mButtonEmptyCreate = (BootstrapButton) view.findViewById(R.id.key_list_empty_button_create);
mButtonEmptyCreate.setOnClickListener(new OnClickListener() {
@Override
@@ -86,7 +87,7 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte
}
});
- mButtonEmptyImport = (Button) view.findViewById(R.id.key_list_empty_button_import);
+ mButtonEmptyImport = (BootstrapButton) view.findViewById(R.id.key_list_empty_button_import);
mButtonEmptyImport.setOnClickListener(new OnClickListener() {
@Override
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java
index 7d51c2ff4..fb04bb1f8 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java
@@ -52,12 +52,12 @@ import android.os.Message;
import android.text.format.DateFormat;
import android.view.View;
import android.view.View.OnClickListener;
-import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
+import com.beardedhen.androidbootstrap.BootstrapButton;
@SuppressLint("NewApi")
public class KeyViewActivity extends KeyActivity implements CreateNdefMessageCallback,
@@ -70,7 +70,7 @@ public class KeyViewActivity extends KeyActivity implements CreateNdefMessageCal
private TextView mFingerint;
private TextView mExpiry;
private TextView mCreation;
- private Button mActionEncrypt;
+ private BootstrapButton mActionEncrypt;
// NFC
private NfcAdapter mNfcAdapter;
@@ -90,7 +90,7 @@ public class KeyViewActivity extends KeyActivity implements CreateNdefMessageCal
mExpiry = (TextView) this.findViewById(R.id.expiry);
mCreation = (TextView) this.findViewById(R.id.creation);
mAlgorithm = (TextView) this.findViewById(R.id.algorithm);
- mActionEncrypt = (Button) this.findViewById(R.id.action_encrypt);
+ mActionEncrypt = (BootstrapButton) this.findViewById(R.id.action_encrypt);
Intent intent = getIntent();
mDataUri = intent.getData();
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java
index 730fcc520..e65da2aa8 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java
@@ -18,9 +18,9 @@
package org.sufficientlysecure.keychain.ui.dialog;
import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.FileHelper;
import org.sufficientlysecure.keychain.util.Log;
-import org.sufficientlysecure.keychain.R;
import android.app.Activity;
import android.app.AlertDialog;
@@ -36,7 +36,8 @@ import android.view.LayoutInflater;
import android.view.View;
import android.widget.CheckBox;
import android.widget.EditText;
-import android.widget.ImageButton;
+
+import com.beardedhen.androidbootstrap.BootstrapButton;
// TODO: return result from file manager activity to this dialog! not the activity!
// do it like in ImportFileFragment!
@@ -55,6 +56,10 @@ public class FileDialogFragment extends DialogFragment {
private Messenger mMessenger;
+ private EditText mFilename;
+ private BootstrapButton mBrowse;
+ private CheckBox mCheckBox;
+
/**
* Creates new instance of this file dialog fragment
*/
@@ -90,10 +95,6 @@ public class FileDialogFragment extends DialogFragment {
String checkboxText = getArguments().getString(ARG_CHECKBOX_TEXT);
final int requestCode = getArguments().getInt(ARG_REQUEST_CODE);
- final EditText mFilename;
- final ImageButton mBrowse;
- final CheckBox mCheckBox;
-
LayoutInflater inflater = (LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
AlertDialog.Builder alert = new AlertDialog.Builder(activity);
@@ -105,13 +106,13 @@ public class FileDialogFragment extends DialogFragment {
mFilename = (EditText) view.findViewById(R.id.input);
mFilename.setText(defaultFile);
- mBrowse = (ImageButton) view.findViewById(R.id.btn_browse);
+ mBrowse = (BootstrapButton) view.findViewById(R.id.btn_browse);
mBrowse.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// only .asc or .gpg files
- // setting it to text/plain prevents Cynaogenmod's file manager from selecting asc or gpg types!
- FileHelper.openFile(activity, mFilename.getText().toString(), "*/*",
- requestCode);
+ // setting it to text/plain prevents Cynaogenmod's file manager from selecting asc
+ // or gpg types!
+ FileHelper.openFile(activity, mFilename.getText().toString(), "*/*", requestCode);
}
});
@@ -196,4 +197,3 @@ public class FileDialogFragment extends DialogFragment {
}
}
}
-
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java
index 5748839bc..0f5d26644 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java
@@ -16,12 +16,18 @@
package org.sufficientlysecure.keychain.ui.widget;
+import java.text.DateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Vector;
+
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPSecretKey;
import org.sufficientlysecure.keychain.Id;
+import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.util.Choice;
-import org.sufficientlysecure.keychain.R;
import android.app.DatePickerDialog;
import android.app.Dialog;
@@ -32,18 +38,12 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
-import android.widget.Button;
import android.widget.DatePicker;
-import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
-import java.text.DateFormat;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.Vector;
+import com.beardedhen.androidbootstrap.BootstrapButton;
public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
private PGPSecretKey mKey;
@@ -51,12 +51,12 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
private EditorListener mEditorListener = null;
private boolean mIsMasterKey;
- ImageButton mDeleteButton;
+ BootstrapButton mDeleteButton;
TextView mAlgorithm;
TextView mKeyId;
Spinner mUsage;
TextView mCreationDate;
- Button mExpiryDateButton;
+ BootstrapButton mExpiryDateButton;
GregorianCalendar mExpiryDate;
private int mDatePickerResultCount = 0;
@@ -87,7 +87,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
mAlgorithm = (TextView) findViewById(R.id.algorithm);
mKeyId = (TextView) findViewById(R.id.keyId);
mCreationDate = (TextView) findViewById(R.id.creation);
- mExpiryDateButton = (Button) findViewById(R.id.expiry);
+ mExpiryDateButton = (BootstrapButton) findViewById(R.id.expiry);
mUsage = (Spinner) findViewById(R.id.usage);
Choice choices[] = {
new Choice(Id.choice.usage.sign_only, getResources().getString(
@@ -101,7 +101,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mUsage.setAdapter(adapter);
- mDeleteButton = (ImageButton) findViewById(R.id.delete);
+ mDeleteButton = (BootstrapButton) findViewById(R.id.delete);
mDeleteButton.setOnClickListener(this);
setExpiryDate(null);
@@ -118,16 +118,18 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
date.get(Calendar.DAY_OF_MONTH));
mDatePickerResultCount = 0;
dialog.setCancelable(true);
- dialog.setButton(Dialog.BUTTON_NEGATIVE, getContext()
- .getString(R.string.btn_no_date), new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- if (mDatePickerResultCount++ == 0) // Note: Ignore results after the first
- // one - android sends multiples.
- {
- setExpiryDate(null);
- }
- }
- });
+ dialog.setButton(Dialog.BUTTON_NEGATIVE,
+ getContext().getString(R.string.btn_no_date),
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ if (mDatePickerResultCount++ == 0) // Note: Ignore results after the
+ // first
+ // one - android sends multiples.
+ {
+ setExpiryDate(null);
+ }
+ }
+ });
dialog.show();
}
});
@@ -237,7 +239,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
private void setExpiryDate(GregorianCalendar date) {
mExpiryDate = date;
if (date == null) {
- mExpiryDateButton.setText(R.string.none);
+ mExpiryDateButton.setText(getContext().getString(R.string.none));
} else {
mExpiryDateButton.setText(DateFormat.getDateInstance().format(date.getTime()));
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java
index 5fdb2c9c8..01259ccd1 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java
@@ -23,14 +23,15 @@ import android.util.AttributeSet;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
-import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
+import com.beardedhen.androidbootstrap.BootstrapButton;
+
public class KeyServerEditor extends LinearLayout implements Editor, OnClickListener {
private EditorListener mEditorListener = null;
- ImageButton mDeleteButton;
+ BootstrapButton mDeleteButton;
TextView mServer;
public KeyServerEditor(Context context) {
@@ -48,7 +49,7 @@ public class KeyServerEditor extends LinearLayout implements Editor, OnClickList
mServer = (TextView) findViewById(R.id.server);
- mDeleteButton = (ImageButton) findViewById(R.id.delete);
+ mDeleteButton = (BootstrapButton) findViewById(R.id.delete);
mDeleteButton.setOnClickListener(this);
super.onFinishInflate();
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 99190e9c7..91e3831b7 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SectionView.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SectionView.java
@@ -45,17 +45,16 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
-import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
import com.actionbarsherlock.app.SherlockFragmentActivity;
+import com.beardedhen.androidbootstrap.BootstrapButton;
public class SectionView extends LinearLayout implements OnClickListener, EditorListener {
private LayoutInflater mInflater;
- private View mAdd;
- private ImageView mPlusButton;
+ private BootstrapButton mPlusButton;
private ViewGroup mEditors;
private TextView mTitle;
private int mType = 0;
@@ -103,7 +102,6 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
public void setCanEdit(boolean bCanEdit) {
canEdit = bCanEdit;
- mPlusButton = (ImageView) findViewById(R.id.plusbutton);
if (!canEdit) {
mPlusButton.setVisibility(View.INVISIBLE);
}
@@ -117,8 +115,8 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
setDrawingCacheEnabled(true);
setAlwaysDrawnWithCacheEnabled(true);
- mAdd = findViewById(R.id.header);
- mAdd.setOnClickListener(this);
+ mPlusButton = (BootstrapButton) findViewById(R.id.plusbutton);
+ mPlusButton.setOnClickListener(this);
mEditors = (ViewGroup) findViewById(R.id.editors);
mTitle = (TextView) findViewById(R.id.title);
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java
index 6c65e840c..5428b626e 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java
@@ -27,14 +27,15 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.EditText;
-import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.RadioButton;
+import com.beardedhen.androidbootstrap.BootstrapButton;
+
public class UserIdEditor extends LinearLayout implements Editor, OnClickListener {
private EditorListener mEditorListener = null;
- private ImageButton mDeleteButton;
+ private BootstrapButton mDeleteButton;
private RadioButton mIsMainUserId;
private EditText mName;
private EditText mEmail;
@@ -95,7 +96,7 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
setDrawingCacheEnabled(true);
setAlwaysDrawnWithCacheEnabled(true);
- mDeleteButton = (ImageButton) findViewById(R.id.delete);
+ mDeleteButton = (BootstrapButton) findViewById(R.id.delete);
mDeleteButton.setOnClickListener(this);
mIsMainUserId = (RadioButton) findViewById(R.id.isMainUserId);
mIsMainUserId.setOnClickListener(this);
--
cgit v1.2.3
From 10715f7acee9620e8a27b62f1600ee4bcbae1ccd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Thu, 9 Jan 2014 17:02:49 +0100
Subject: add experimental drawer navigation
---
OpenPGP-Keychain/libs/android-support-v4.jar | Bin 271754 -> 621451 bytes
.../res/drawable-hdpi/drawer_shadow.9.png | Bin 0 -> 171 bytes
OpenPGP-Keychain/res/drawable-hdpi/ic_drawer.png | Bin 0 -> 2842 bytes
.../res/drawable-hdpi/ic_menu_filebrowser.png | Bin 1443 -> 0 bytes
.../res/drawable-mdpi/drawer_shadow.9.png | Bin 0 -> 158 bytes
OpenPGP-Keychain/res/drawable-mdpi/ic_drawer.png | Bin 0 -> 2837 bytes
.../res/drawable-mdpi/ic_menu_filebrowser.png | Bin 1307 -> 0 bytes
.../res/drawable-xhdpi/drawer_shadow.9.png | Bin 0 -> 182 bytes
OpenPGP-Keychain/res/drawable-xhdpi/ic_drawer.png | Bin 0 -> 1056 bytes
.../res/drawable-xhdpi/ic_menu_filebrowser.png | Bin 1604 -> 0 bytes
.../res/layout/api_app_settings_fragment.xml | 18 +-
OpenPGP-Keychain/res/layout/drawer_list_item.xml | 36 ++
OpenPGP-Keychain/res/layout/fragment_planet.xml | 23 ++
.../res/layout/key_list_public_activity.xml | 42 +-
OpenPGP-Keychain/res/values-sw600dp/dimens.xml | 8 +
.../res/values-sw720dp-land/dimens.xml | 9 +
OpenPGP-Keychain/res/values/dimens.xml | 7 +
OpenPGP-Keychain/res/values/strings.xml | 23 +-
.../service/remote/AppSettingsFragment.java | 19 +-
.../keychain/ui/DrawerActivity.java | 444 +++++++++++++++++++++
.../keychain/ui/KeyActivity.java | 3 +-
.../keychain/ui/KeyListPublicActivity.java | 11 +-
.../ActionBarSherlock/libs/android-support-v4.jar | Bin 271754 -> 621451 bytes
.../AndroidBootstrap/libs/android-support-v4.jar | Bin 271754 -> 621451 bytes
24 files changed, 608 insertions(+), 35 deletions(-)
create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/drawer_shadow.9.png
create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/ic_drawer.png
delete mode 100644 OpenPGP-Keychain/res/drawable-hdpi/ic_menu_filebrowser.png
create mode 100644 OpenPGP-Keychain/res/drawable-mdpi/drawer_shadow.9.png
create mode 100644 OpenPGP-Keychain/res/drawable-mdpi/ic_drawer.png
delete mode 100644 OpenPGP-Keychain/res/drawable-mdpi/ic_menu_filebrowser.png
create mode 100644 OpenPGP-Keychain/res/drawable-xhdpi/drawer_shadow.9.png
create mode 100644 OpenPGP-Keychain/res/drawable-xhdpi/ic_drawer.png
delete mode 100644 OpenPGP-Keychain/res/drawable-xhdpi/ic_menu_filebrowser.png
create mode 100644 OpenPGP-Keychain/res/layout/drawer_list_item.xml
create mode 100644 OpenPGP-Keychain/res/layout/fragment_planet.xml
create mode 100644 OpenPGP-Keychain/res/values-sw600dp/dimens.xml
create mode 100644 OpenPGP-Keychain/res/values-sw720dp-land/dimens.xml
create mode 100644 OpenPGP-Keychain/res/values/dimens.xml
create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DrawerActivity.java
diff --git a/OpenPGP-Keychain/libs/android-support-v4.jar b/OpenPGP-Keychain/libs/android-support-v4.jar
index 99e063b33..9056828a0 100644
Binary files a/OpenPGP-Keychain/libs/android-support-v4.jar and b/OpenPGP-Keychain/libs/android-support-v4.jar differ
diff --git a/OpenPGP-Keychain/res/drawable-hdpi/drawer_shadow.9.png b/OpenPGP-Keychain/res/drawable-hdpi/drawer_shadow.9.png
new file mode 100644
index 000000000..224cc4ff4
Binary files /dev/null and b/OpenPGP-Keychain/res/drawable-hdpi/drawer_shadow.9.png differ
diff --git a/OpenPGP-Keychain/res/drawable-hdpi/ic_drawer.png b/OpenPGP-Keychain/res/drawable-hdpi/ic_drawer.png
new file mode 100644
index 000000000..ff7b1def9
Binary files /dev/null and b/OpenPGP-Keychain/res/drawable-hdpi/ic_drawer.png differ
diff --git a/OpenPGP-Keychain/res/drawable-hdpi/ic_menu_filebrowser.png b/OpenPGP-Keychain/res/drawable-hdpi/ic_menu_filebrowser.png
deleted file mode 100644
index 3db304fa8..000000000
Binary files a/OpenPGP-Keychain/res/drawable-hdpi/ic_menu_filebrowser.png and /dev/null differ
diff --git a/OpenPGP-Keychain/res/drawable-mdpi/drawer_shadow.9.png b/OpenPGP-Keychain/res/drawable-mdpi/drawer_shadow.9.png
new file mode 100644
index 000000000..3797f99c0
Binary files /dev/null and b/OpenPGP-Keychain/res/drawable-mdpi/drawer_shadow.9.png differ
diff --git a/OpenPGP-Keychain/res/drawable-mdpi/ic_drawer.png b/OpenPGP-Keychain/res/drawable-mdpi/ic_drawer.png
new file mode 100644
index 000000000..fb681ba26
Binary files /dev/null and b/OpenPGP-Keychain/res/drawable-mdpi/ic_drawer.png differ
diff --git a/OpenPGP-Keychain/res/drawable-mdpi/ic_menu_filebrowser.png b/OpenPGP-Keychain/res/drawable-mdpi/ic_menu_filebrowser.png
deleted file mode 100644
index fda13f1be..000000000
Binary files a/OpenPGP-Keychain/res/drawable-mdpi/ic_menu_filebrowser.png and /dev/null differ
diff --git a/OpenPGP-Keychain/res/drawable-xhdpi/drawer_shadow.9.png b/OpenPGP-Keychain/res/drawable-xhdpi/drawer_shadow.9.png
new file mode 100644
index 000000000..fa3d853e9
Binary files /dev/null and b/OpenPGP-Keychain/res/drawable-xhdpi/drawer_shadow.9.png differ
diff --git a/OpenPGP-Keychain/res/drawable-xhdpi/ic_drawer.png b/OpenPGP-Keychain/res/drawable-xhdpi/ic_drawer.png
new file mode 100644
index 000000000..b9bc3d70f
Binary files /dev/null and b/OpenPGP-Keychain/res/drawable-xhdpi/ic_drawer.png differ
diff --git a/OpenPGP-Keychain/res/drawable-xhdpi/ic_menu_filebrowser.png b/OpenPGP-Keychain/res/drawable-xhdpi/ic_menu_filebrowser.png
deleted file mode 100644
index d1324014d..000000000
Binary files a/OpenPGP-Keychain/res/drawable-xhdpi/ic_menu_filebrowser.png and /dev/null differ
diff --git a/OpenPGP-Keychain/res/layout/api_app_settings_fragment.xml b/OpenPGP-Keychain/res/layout/api_app_settings_fragment.xml
index a40444e0f..a88d7afd2 100644
--- a/OpenPGP-Keychain/res/layout/api_app_settings_fragment.xml
+++ b/OpenPGP-Keychain/res/layout/api_app_settings_fragment.xml
@@ -1,5 +1,6 @@
@@ -41,12 +42,16 @@
android:layout_height="wrap_content"
android:orientation="horizontal" >
-
+ android:layout_margin="4dp"
+ android:text="@string/api_settings_select_key"
+ bootstrapbutton:bb_icon_left="fa-key"
+ bootstrapbutton:bb_size="default"
+ bootstrapbutton:bb_type="default" />
-
+ android:layout_gravity="center_vertical"
+ android:layout_margin="4dp"
+ android:text="@string/api_settings_show_advanced"
+ bootstrapbutton:bb_icon_left="fa-caret-up"
+ bootstrapbutton:bb_size="default"
+ bootstrapbutton:bb_type="default" />
+
+
+
diff --git a/OpenPGP-Keychain/res/layout/fragment_planet.xml b/OpenPGP-Keychain/res/layout/fragment_planet.xml
new file mode 100644
index 000000000..4fe5bbe61
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/fragment_planet.xml
@@ -0,0 +1,23 @@
+
+
+
diff --git a/OpenPGP-Keychain/res/layout/key_list_public_activity.xml b/OpenPGP-Keychain/res/layout/key_list_public_activity.xml
index 07ec253cc..704b3c8c9 100644
--- a/OpenPGP-Keychain/res/layout/key_list_public_activity.xml
+++ b/OpenPGP-Keychain/res/layout/key_list_public_activity.xml
@@ -1,14 +1,38 @@
-
+
+ android:layout_height="match_parent" >
-
+ android:layout_height="match_parent" >
-
\ No newline at end of file
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/values-sw600dp/dimens.xml b/OpenPGP-Keychain/res/values-sw600dp/dimens.xml
new file mode 100644
index 000000000..1ba777d65
--- /dev/null
+++ b/OpenPGP-Keychain/res/values-sw600dp/dimens.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/values-sw720dp-land/dimens.xml b/OpenPGP-Keychain/res/values-sw720dp-land/dimens.xml
new file mode 100644
index 000000000..eee741a51
--- /dev/null
+++ b/OpenPGP-Keychain/res/values-sw720dp-land/dimens.xml
@@ -0,0 +1,9 @@
+
+
+
+ 128dp
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/values/dimens.xml b/OpenPGP-Keychain/res/values/dimens.xml
new file mode 100644
index 000000000..a6dd14032
--- /dev/null
+++ b/OpenPGP-Keychain/res/values/dimens.xml
@@ -0,0 +1,7 @@
+
+
+
+ 16dp
+ 16dp
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/values/strings.xml b/OpenPGP-Keychain/res/values/strings.xml
index b02d72bb8..e9ce05b77 100644
--- a/OpenPGP-Keychain/res/values/strings.xml
+++ b/OpenPGP-Keychain/res/values/strings.xml
@@ -19,7 +19,7 @@
- Public Keys
+ ContactsSecret KeysSelect Public KeySelect Secret Key
@@ -301,7 +301,7 @@
very slow
- Manage Public Keys
+ ContactsMy Secret KeysEncryptDecrypt
@@ -331,8 +331,8 @@
No registered applications!
- Show advanced settings…
- Hide advanced settings…
+ Show advanced settings
+ Hide advanced settingsNo key selectedSelect keySave
@@ -369,4 +369,19 @@
Encrypt to this contact
+
+
+ Contacts
+ Encrypt
+ Decrypt
+ Import Keys
+ My Keys
+ Settings
+ Registered Apps
+ Help
+
+
+ Open navigation drawer
+ Close navigation drawer
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java
index e592f5d57..bb6e427a4 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java
@@ -49,12 +49,13 @@ import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
-import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
+import com.beardedhen.androidbootstrap.BootstrapButton;
+
public class AppSettingsFragment extends Fragment {
// model
@@ -62,12 +63,12 @@ public class AppSettingsFragment extends Fragment {
// view
private LinearLayout mAdvancedSettingsContainer;
- private Button mAdvancedSettingsButton;
+ private BootstrapButton mAdvancedSettingsButton;
private TextView mAppNameView;
private ImageView mAppIconView;
private TextView mKeyUserId;
private TextView mKeyUserIdRest;
- private Button mSelectKeyButton;
+ private BootstrapButton mSelectKeyButton;
private Spinner mEncryptionAlgorithm;
private Spinner mHashAlgorithm;
private Spinner mCompression;
@@ -116,7 +117,8 @@ public class AppSettingsFragment extends Fragment {
}
private void initView(View view) {
- mAdvancedSettingsButton = (Button) view.findViewById(R.id.api_app_settings_advanced_button);
+ mAdvancedSettingsButton = (BootstrapButton) view
+ .findViewById(R.id.api_app_settings_advanced_button);
mAdvancedSettingsContainer = (LinearLayout) view
.findViewById(R.id.api_app_settings_advanced);
@@ -124,7 +126,8 @@ public class AppSettingsFragment extends Fragment {
mAppIconView = (ImageView) view.findViewById(R.id.api_app_settings_app_icon);
mKeyUserId = (TextView) view.findViewById(R.id.api_app_settings_user_id);
mKeyUserIdRest = (TextView) view.findViewById(R.id.api_app_settings_user_id_rest);
- mSelectKeyButton = (Button) view.findViewById(R.id.api_app_settings_select_key_button);
+ mSelectKeyButton = (BootstrapButton) view
+ .findViewById(R.id.api_app_settings_select_key_button);
mEncryptionAlgorithm = (Spinner) view
.findViewById(R.id.api_app_settings_encryption_algorithm);
mHashAlgorithm = (Spinner) view.findViewById(R.id.api_app_settings_hash_algorithm);
@@ -204,11 +207,13 @@ public class AppSettingsFragment extends Fragment {
if (mAdvancedSettingsContainer.getVisibility() == View.VISIBLE) {
mAdvancedSettingsContainer.startAnimation(invisibleAnimation);
mAdvancedSettingsContainer.setVisibility(View.GONE);
- mAdvancedSettingsButton.setText(R.string.api_settings_show_advanced);
+ mAdvancedSettingsButton.setText(getString(R.string.api_settings_show_advanced));
+ mAdvancedSettingsButton.setLeftIcon("fa-caret-up");
} else {
mAdvancedSettingsContainer.startAnimation(visibleAnimation);
mAdvancedSettingsContainer.setVisibility(View.VISIBLE);
- mAdvancedSettingsButton.setText(R.string.api_settings_hide_advanced);
+ mAdvancedSettingsButton.setText(getString(R.string.api_settings_hide_advanced));
+ mAdvancedSettingsButton.setLeftIcon("fa-caret-down");
}
}
});
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DrawerActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DrawerActivity.java
new file mode 100644
index 000000000..e278af5e3
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DrawerActivity.java
@@ -0,0 +1,444 @@
+/*
+ *
+ * from https://github.com/tobykurien/SherlockNavigationDrawer
+ *
+ * Copyright 2013 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;
+
+import org.sufficientlysecure.keychain.R;
+
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.support.v4.app.ActionBarDrawerToggle;
+import android.support.v4.view.GravityCompat;
+import android.support.v4.widget.DrawerLayout;
+import android.view.ActionProvider;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.SubMenu;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+import com.actionbarsherlock.app.SherlockFragmentActivity;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
+
+/**
+ * This example illustrates a common usage of the DrawerLayout widget in the Android support
+ * library.
+ *
+ *
+ * When a navigation (left) drawer is present, the host activity should detect presses of the action
+ * bar's Up affordance as a signal to open and close the navigation drawer. The
+ * ActionBarDrawerToggle facilitates this behavior. Items within the drawer should fall into one of
+ * two categories:
+ *
+ *
+ *
+ *
View switches. A view switch follows the same basic policies as list or tab
+ * navigation in that a view switch does not create navigation history. This pattern should only be
+ * used at the root activity of a task, leaving some form of Up navigation active for activities
+ * further down the navigation hierarchy.
+ *
Selective Up. The drawer allows the user to choose an alternate parent for
+ * Up navigation. This allows a user to jump across an app's navigation hierarchy at will. The
+ * application should treat this as it treats Up navigation from a different task, replacing the
+ * current task stack using TaskStackBuilder or similar. This is the only form of navigation drawer
+ * that should be used outside of the root activity of a task.
+ *
+ *
+ *
+ * Right side drawers should be used for actions, not navigation. This follows the pattern
+ * established by the Action Bar that navigation should be to the left and actions to the right. An
+ * action should be an operation performed on the current contents of the window, for example
+ * enabling or disabling a data overlay on top of the current content.
+ *
+ */
+public class DrawerActivity extends SherlockFragmentActivity {
+ private DrawerLayout mDrawerLayout;
+ private ListView mDrawerList;
+ private ActionBarDrawerToggle mDrawerToggle;
+
+ private CharSequence mDrawerTitle;
+ private CharSequence mTitle;
+ private String[] mDrawerTitles;
+
+ protected void setupDrawerNavigation(Bundle savedInstanceState) {
+ // mTitle = mDrawerTitle = getTitle();
+ mDrawerTitles = getResources().getStringArray(R.array.drawer_array);
+ mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
+ mDrawerList = (ListView) findViewById(R.id.left_drawer);
+
+ // set a custom shadow that overlays the main content when the drawer
+ // opens
+ mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
+ // set up the drawer's list view with items and click listener
+ mDrawerList.setAdapter(new ArrayAdapter(this, R.layout.drawer_list_item,
+ mDrawerTitles));
+ mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
+
+ // enable ActionBar app icon to behave as action to toggle nav drawer
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setHomeButtonEnabled(true);
+
+ // ActionBarDrawerToggle ties together the the proper interactions
+ // between the sliding drawer and the action bar app icon
+ mDrawerToggle = new ActionBarDrawerToggle(this, /* host Activity */
+ mDrawerLayout, /* DrawerLayout object */
+ R.drawable.ic_drawer, /* nav drawer image to replace 'Up' caret */
+ R.string.drawer_open, /* "open drawer" description for accessibility */
+ R.string.drawer_close /* "close drawer" description for accessibility */
+ ) {
+ public void onDrawerClosed(View view) {
+ getSupportActionBar().setTitle(mTitle);
+ supportInvalidateOptionsMenu(); // creates call to
+ // onPrepareOptionsMenu()
+ }
+
+ public void onDrawerOpened(View drawerView) {
+ getSupportActionBar().setTitle(mDrawerTitle);
+ supportInvalidateOptionsMenu(); // creates call to
+ // onPrepareOptionsMenu()
+ }
+ };
+ mDrawerLayout.setDrawerListener(mDrawerToggle);
+
+ if (savedInstanceState == null) {
+ selectItem(0);
+ }
+ }
+
+ /* Called whenever we call invalidateOptionsMenu() */
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ // If the nav drawer is open, hide action items related to the content
+ // view
+ boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
+ // menu.findItem(R.id.action_websearch).setVisible(!drawerOpen);
+ return super.onPrepareOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ // The action bar home/up action should open or close the drawer.
+ // ActionBarDrawerToggle will take care of this.
+ if (mDrawerToggle.onOptionsItemSelected(getMenuItem(item))) {
+ return true;
+ }
+
+ // Handle action buttons
+ switch (item.getItemId()) {
+ // case R.id.action_websearch:
+ // // create intent to perform web search for this planet
+ // Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
+ // intent.putExtra(SearchManager.QUERY, getSupportActionBar().getTitle());
+ // // catch event that there's no activity to handle intent
+ // if (intent.resolveActivity(getPackageManager()) != null) {
+ // startActivity(intent);
+ // } else {
+ // Toast.makeText(this, R.string.app_not_available, Toast.LENGTH_LONG).show();
+ // }
+ // return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private android.view.MenuItem getMenuItem(final MenuItem item) {
+ return new android.view.MenuItem() {
+ @Override
+ public int getItemId() {
+ return item.getItemId();
+ }
+
+ public boolean isEnabled() {
+ return true;
+ }
+
+ @Override
+ public boolean collapseActionView() {
+ return false;
+ }
+
+ @Override
+ public boolean expandActionView() {
+ return false;
+ }
+
+ @Override
+ public ActionProvider getActionProvider() {
+ return null;
+ }
+
+ @Override
+ public View getActionView() {
+ return null;
+ }
+
+ @Override
+ public char getAlphabeticShortcut() {
+ return 0;
+ }
+
+ @Override
+ public int getGroupId() {
+ return 0;
+ }
+
+ @Override
+ public Drawable getIcon() {
+ return null;
+ }
+
+ @Override
+ public Intent getIntent() {
+ return null;
+ }
+
+ @Override
+ public ContextMenuInfo getMenuInfo() {
+ return null;
+ }
+
+ @Override
+ public char getNumericShortcut() {
+ return 0;
+ }
+
+ @Override
+ public int getOrder() {
+ return 0;
+ }
+
+ @Override
+ public SubMenu getSubMenu() {
+ return null;
+ }
+
+ @Override
+ public CharSequence getTitle() {
+ return null;
+ }
+
+ @Override
+ public CharSequence getTitleCondensed() {
+ return null;
+ }
+
+ @Override
+ public boolean hasSubMenu() {
+ return false;
+ }
+
+ @Override
+ public boolean isActionViewExpanded() {
+ return false;
+ }
+
+ @Override
+ public boolean isCheckable() {
+ return false;
+ }
+
+ @Override
+ public boolean isChecked() {
+ return false;
+ }
+
+ @Override
+ public boolean isVisible() {
+ return false;
+ }
+
+ @Override
+ public android.view.MenuItem setActionProvider(ActionProvider actionProvider) {
+ return null;
+ }
+
+ @Override
+ public android.view.MenuItem setActionView(View view) {
+ return null;
+ }
+
+ @Override
+ public android.view.MenuItem setActionView(int resId) {
+ return null;
+ }
+
+ @Override
+ public android.view.MenuItem setAlphabeticShortcut(char alphaChar) {
+ return null;
+ }
+
+ @Override
+ public android.view.MenuItem setCheckable(boolean checkable) {
+ return null;
+ }
+
+ @Override
+ public android.view.MenuItem setChecked(boolean checked) {
+ return null;
+ }
+
+ @Override
+ public android.view.MenuItem setEnabled(boolean enabled) {
+ return null;
+ }
+
+ @Override
+ public android.view.MenuItem setIcon(Drawable icon) {
+ return null;
+ }
+
+ @Override
+ public android.view.MenuItem setIcon(int iconRes) {
+ return null;
+ }
+
+ @Override
+ public android.view.MenuItem setIntent(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public android.view.MenuItem setNumericShortcut(char numericChar) {
+ return null;
+ }
+
+ @Override
+ public android.view.MenuItem setOnActionExpandListener(OnActionExpandListener listener) {
+ return null;
+ }
+
+ @Override
+ public android.view.MenuItem setOnMenuItemClickListener(
+ OnMenuItemClickListener menuItemClickListener) {
+ return null;
+ }
+
+ @Override
+ public android.view.MenuItem setShortcut(char numericChar, char alphaChar) {
+ return null;
+ }
+
+ @Override
+ public void setShowAsAction(int actionEnum) {
+ }
+
+ @Override
+ public android.view.MenuItem setShowAsActionFlags(int actionEnum) {
+ return null;
+ }
+
+ @Override
+ public android.view.MenuItem setTitle(CharSequence title) {
+ return null;
+ }
+
+ @Override
+ public android.view.MenuItem setTitle(int title) {
+ return null;
+ }
+
+ @Override
+ public android.view.MenuItem setTitleCondensed(CharSequence title) {
+ return null;
+ }
+
+ @Override
+ public android.view.MenuItem setVisible(boolean visible) {
+ return null;
+ }
+ };
+ }
+
+ /* The click listner for ListView in the navigation drawer */
+ private class DrawerItemClickListener implements ListView.OnItemClickListener {
+ @Override
+ public void onItemClick(AdapterView> parent, View view, int position, long id) {
+ selectItem(position);
+ }
+ }
+
+ private void selectItem(int position) {
+ // update the main content by replacing fragments
+ // Fragment fragment = new PlanetFragment();
+ // Bundle args = new Bundle();
+ // args.putInt(PlanetFragment.ARG_PLANET_NUMBER, position);
+ // fragment.setArguments(args);
+
+ // FragmentManager fragmentManager = getSupportFragmentManager();
+ // fragmentManager.beginTransaction().replace(R.id.content_frame, fragment).commit();
+
+ // update selected item and title, then close the drawer
+ mDrawerList.setItemChecked(position, true);
+ // setTitle(mDrawerTitles[position]);
+ mDrawerLayout.closeDrawer(mDrawerList);
+ }
+
+ // @Override
+ // public void setTitle(CharSequence title) {
+ // mTitle = title;
+ // getSupportActionBar().setTitle(mTitle);
+ // }
+
+ /**
+ * When using the ActionBarDrawerToggle, you must call it during onPostCreate() and
+ * onConfigurationChanged()...
+ */
+
+ @Override
+ protected void onPostCreate(Bundle savedInstanceState) {
+ super.onPostCreate(savedInstanceState);
+ // Sync the toggle state after onRestoreInstanceState has occurred.
+ mDrawerToggle.syncState();
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ // Pass any configuration change to the drawer toggles
+ mDrawerToggle.onConfigurationChanged(newConfig);
+ }
+
+ /**
+ * Fragment that appears in the "content_frame", shows a planet
+ */
+ // public static class PlanetFragment extends SherlockFragment {
+ // public static final String ARG_PLANET_NUMBER = "planet_number";
+ //
+ // public PlanetFragment() {
+ // // Empty constructor required for fragment subclasses
+ // }
+ //
+ // @Override
+ // public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ // Bundle savedInstanceState) {
+ // View rootView = inflater.inflate(R.layout.fragment_planet, container, false);
+ // int i = getArguments().getInt(ARG_PLANET_NUMBER);
+ // String planet = getResources().getStringArray(R.array.drawer_array)[i];
+ //
+ // int imageId = getResources().getIdentifier(planet.toLowerCase(Locale.getDefault()),
+ // "drawable", getActivity().getPackageName());
+ // ((ImageView) rootView.findViewById(R.id.image)).setImageResource(imageId);
+ // getActivity().setTitle(planet);
+ // return rootView;
+ // }
+ // }
+}
\ No newline at end of file
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyActivity.java
index 2760df97e..3766b1c42 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyActivity.java
@@ -45,8 +45,7 @@ import com.actionbarsherlock.app.SherlockFragmentActivity;
* TODO: get key type by looking at dataUri!
*
*/
-public class KeyActivity extends SherlockFragmentActivity {
-
+public class KeyActivity extends DrawerActivity {
protected FileDialogFragment mFileDialog;
protected String mExportFilename;
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java
index 023c8fd9d..476d3c96c 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java
@@ -35,8 +35,8 @@ public class KeyListPublicActivity extends KeyActivity {
setContentView(R.layout.key_list_public_activity);
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- getSupportActionBar().setHomeButtonEnabled(true);
+ // now setup navigation drawer in DrawerActivity...
+ setupDrawerNavigation(savedInstanceState);
}
@Override
@@ -49,13 +49,6 @@ public class KeyListPublicActivity extends KeyActivity {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
- case android.R.id.home:
- // app icon in Action Bar clicked; go home
- Intent intent = new Intent(this, MainActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(intent);
-
- return true;
case R.id.menu_key_list_public_import:
Intent intentImportFromFile = new Intent(this, ImportKeysActivity.class);
startActivityForResult(intentImportFromFile, Id.request.import_from_qr_code);
diff --git a/libraries/ActionBarSherlock/libs/android-support-v4.jar b/libraries/ActionBarSherlock/libs/android-support-v4.jar
index 99e063b33..9056828a0 100644
Binary files a/libraries/ActionBarSherlock/libs/android-support-v4.jar and b/libraries/ActionBarSherlock/libs/android-support-v4.jar differ
diff --git a/libraries/AndroidBootstrap/libs/android-support-v4.jar b/libraries/AndroidBootstrap/libs/android-support-v4.jar
index 99e063b33..9056828a0 100644
Binary files a/libraries/AndroidBootstrap/libs/android-support-v4.jar and b/libraries/AndroidBootstrap/libs/android-support-v4.jar differ
--
cgit v1.2.3
From bb161d5fa9d56c5fc7369c979d6fd4eeff187987 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Thu, 9 Jan 2014 22:58:52 +0100
Subject: implement navigation drawer
---
OpenPGP-Keychain/AndroidManifest.xml | 11 +-
.../drawable-hdpi/dashboard_decrypt_default.png | Bin 4996 -> 0 bytes
.../drawable-hdpi/dashboard_decrypt_pressed.png | Bin 5032 -> 0 bytes
.../drawable-hdpi/dashboard_encrypt_default.png | Bin 4926 -> 0 bytes
.../drawable-hdpi/dashboard_encrypt_pressed.png | Bin 4967 -> 0 bytes
.../res/drawable-hdpi/dashboard_help_default.png | Bin 8607 -> 0 bytes
.../res/drawable-hdpi/dashboard_help_pressed.png | Bin 8343 -> 0 bytes
.../res/drawable-hdpi/dashboard_import_default.png | Bin 5195 -> 0 bytes
.../res/drawable-hdpi/dashboard_import_pressed.png | Bin 4973 -> 0 bytes
.../dashboard_manage_keys_default.png | Bin 7306 -> 0 bytes
.../dashboard_manage_keys_pressed.png | Bin 7147 -> 0 bytes
.../drawable-hdpi/dashboard_my_keys_default.png | Bin 5438 -> 0 bytes
.../drawable-hdpi/dashboard_my_keys_pressed.png | Bin 5414 -> 0 bytes
.../res/drawable/dashboard_decrypt.xml | 28 --
.../res/drawable/dashboard_encrypt.xml | 28 --
OpenPGP-Keychain/res/drawable/dashboard_help.xml | 28 --
OpenPGP-Keychain/res/drawable/dashboard_import.xml | 28 --
.../res/drawable/dashboard_manage_keys.xml | 28 --
.../res/drawable/dashboard_my_keys.xml | 28 --
.../res/layout/api_apps_list_activity.xml | 22 +-
OpenPGP-Keychain/res/layout/decrypt.xml | 270 ++++++------
OpenPGP-Keychain/res/layout/drawer_list.xml | 18 +
OpenPGP-Keychain/res/layout/drawer_list_item.xml | 50 ++-
OpenPGP-Keychain/res/layout/encrypt.xml | 480 ++++++++++-----------
OpenPGP-Keychain/res/layout/help_activity.xml | 27 +-
OpenPGP-Keychain/res/layout/import_keys.xml | 104 ++---
.../res/layout/key_list_public_activity.xml | 20 +-
.../res/layout/key_list_secret_activity.xml | 24 +-
OpenPGP-Keychain/res/layout/main.xml | 70 ---
OpenPGP-Keychain/res/values/strings.xml | 19 +-
.../service/remote/RegisteredAppsListActivity.java | 31 +-
.../keychain/ui/DecryptActivity.java | 27 +-
.../keychain/ui/DrawerActivity.java | 224 +++++-----
.../keychain/ui/EncryptActivity.java | 34 +-
.../keychain/ui/EncryptDecryptActivity.java | 175 --------
.../keychain/ui/HelpActivity.java | 44 +-
.../keychain/ui/ImportKeysActivity.java | 29 +-
.../keychain/ui/KeyActivity.java | 4 +-
.../keychain/ui/KeyListPublicActivity.java | 4 +-
.../keychain/ui/KeyListSecretActivity.java | 15 +-
.../keychain/ui/MainActivity.java | 109 -----
.../keychain/ui/PreferencesActivity.java | 26 +-
.../keychain/ui/SelectSecretKeyActivity.java | 17 -
.../keychain/ui/widget/DashboardLayout.java | 186 --------
44 files changed, 713 insertions(+), 1495 deletions(-)
delete mode 100644 OpenPGP-Keychain/res/drawable-hdpi/dashboard_decrypt_default.png
delete mode 100644 OpenPGP-Keychain/res/drawable-hdpi/dashboard_decrypt_pressed.png
delete mode 100644 OpenPGP-Keychain/res/drawable-hdpi/dashboard_encrypt_default.png
delete mode 100644 OpenPGP-Keychain/res/drawable-hdpi/dashboard_encrypt_pressed.png
delete mode 100644 OpenPGP-Keychain/res/drawable-hdpi/dashboard_help_default.png
delete mode 100644 OpenPGP-Keychain/res/drawable-hdpi/dashboard_help_pressed.png
delete mode 100644 OpenPGP-Keychain/res/drawable-hdpi/dashboard_import_default.png
delete mode 100644 OpenPGP-Keychain/res/drawable-hdpi/dashboard_import_pressed.png
delete mode 100644 OpenPGP-Keychain/res/drawable-hdpi/dashboard_manage_keys_default.png
delete mode 100644 OpenPGP-Keychain/res/drawable-hdpi/dashboard_manage_keys_pressed.png
delete mode 100644 OpenPGP-Keychain/res/drawable-hdpi/dashboard_my_keys_default.png
delete mode 100644 OpenPGP-Keychain/res/drawable-hdpi/dashboard_my_keys_pressed.png
delete mode 100644 OpenPGP-Keychain/res/drawable/dashboard_decrypt.xml
delete mode 100644 OpenPGP-Keychain/res/drawable/dashboard_encrypt.xml
delete mode 100644 OpenPGP-Keychain/res/drawable/dashboard_help.xml
delete mode 100644 OpenPGP-Keychain/res/drawable/dashboard_import.xml
delete mode 100644 OpenPGP-Keychain/res/drawable/dashboard_manage_keys.xml
delete mode 100644 OpenPGP-Keychain/res/drawable/dashboard_my_keys.xml
create mode 100644 OpenPGP-Keychain/res/layout/drawer_list.xml
delete mode 100644 OpenPGP-Keychain/res/layout/main.xml
delete mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptDecryptActivity.java
delete mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/MainActivity.java
delete mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/DashboardLayout.java
diff --git a/OpenPGP-Keychain/AndroidManifest.xml b/OpenPGP-Keychain/AndroidManifest.xml
index fd7edff2a..dcd496789 100644
--- a/OpenPGP-Keychain/AndroidManifest.xml
+++ b/OpenPGP-Keychain/AndroidManifest.xml
@@ -75,20 +75,15 @@
android:label="@string/app_name"
android:theme="@style/Theme.Sherlock.Light" >
+ android:label="@string/app_name"
+ android:launchMode="singleTop" >
-
-
diff --git a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_decrypt_default.png b/OpenPGP-Keychain/res/drawable-hdpi/dashboard_decrypt_default.png
deleted file mode 100644
index 0d51bcb68..000000000
Binary files a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_decrypt_default.png and /dev/null differ
diff --git a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_decrypt_pressed.png b/OpenPGP-Keychain/res/drawable-hdpi/dashboard_decrypt_pressed.png
deleted file mode 100644
index d4cc0f8ea..000000000
Binary files a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_decrypt_pressed.png and /dev/null differ
diff --git a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_encrypt_default.png b/OpenPGP-Keychain/res/drawable-hdpi/dashboard_encrypt_default.png
deleted file mode 100644
index 07617bb9d..000000000
Binary files a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_encrypt_default.png and /dev/null differ
diff --git a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_encrypt_pressed.png b/OpenPGP-Keychain/res/drawable-hdpi/dashboard_encrypt_pressed.png
deleted file mode 100644
index b8fe6e1d6..000000000
Binary files a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_encrypt_pressed.png and /dev/null differ
diff --git a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_help_default.png b/OpenPGP-Keychain/res/drawable-hdpi/dashboard_help_default.png
deleted file mode 100644
index 233fddffc..000000000
Binary files a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_help_default.png and /dev/null differ
diff --git a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_help_pressed.png b/OpenPGP-Keychain/res/drawable-hdpi/dashboard_help_pressed.png
deleted file mode 100644
index dad8694f8..000000000
Binary files a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_help_pressed.png and /dev/null differ
diff --git a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_import_default.png b/OpenPGP-Keychain/res/drawable-hdpi/dashboard_import_default.png
deleted file mode 100644
index 7be4837a0..000000000
Binary files a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_import_default.png and /dev/null differ
diff --git a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_import_pressed.png b/OpenPGP-Keychain/res/drawable-hdpi/dashboard_import_pressed.png
deleted file mode 100644
index a4fe3c903..000000000
Binary files a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_import_pressed.png and /dev/null differ
diff --git a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_manage_keys_default.png b/OpenPGP-Keychain/res/drawable-hdpi/dashboard_manage_keys_default.png
deleted file mode 100644
index de83398c2..000000000
Binary files a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_manage_keys_default.png and /dev/null differ
diff --git a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_manage_keys_pressed.png b/OpenPGP-Keychain/res/drawable-hdpi/dashboard_manage_keys_pressed.png
deleted file mode 100644
index a86bc1bf9..000000000
Binary files a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_manage_keys_pressed.png and /dev/null differ
diff --git a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_my_keys_default.png b/OpenPGP-Keychain/res/drawable-hdpi/dashboard_my_keys_default.png
deleted file mode 100644
index f8b54961e..000000000
Binary files a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_my_keys_default.png and /dev/null differ
diff --git a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_my_keys_pressed.png b/OpenPGP-Keychain/res/drawable-hdpi/dashboard_my_keys_pressed.png
deleted file mode 100644
index 6a5c92138..000000000
Binary files a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_my_keys_pressed.png and /dev/null differ
diff --git a/OpenPGP-Keychain/res/drawable/dashboard_decrypt.xml b/OpenPGP-Keychain/res/drawable/dashboard_decrypt.xml
deleted file mode 100644
index 981e38a0b..000000000
--- a/OpenPGP-Keychain/res/drawable/dashboard_decrypt.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
diff --git a/OpenPGP-Keychain/res/drawable/dashboard_encrypt.xml b/OpenPGP-Keychain/res/drawable/dashboard_encrypt.xml
deleted file mode 100644
index af812dc51..000000000
--- a/OpenPGP-Keychain/res/drawable/dashboard_encrypt.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
diff --git a/OpenPGP-Keychain/res/drawable/dashboard_help.xml b/OpenPGP-Keychain/res/drawable/dashboard_help.xml
deleted file mode 100644
index e121ea0d1..000000000
--- a/OpenPGP-Keychain/res/drawable/dashboard_help.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
diff --git a/OpenPGP-Keychain/res/drawable/dashboard_import.xml b/OpenPGP-Keychain/res/drawable/dashboard_import.xml
deleted file mode 100644
index e5857dc6c..000000000
--- a/OpenPGP-Keychain/res/drawable/dashboard_import.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
diff --git a/OpenPGP-Keychain/res/drawable/dashboard_manage_keys.xml b/OpenPGP-Keychain/res/drawable/dashboard_manage_keys.xml
deleted file mode 100644
index ebc519253..000000000
--- a/OpenPGP-Keychain/res/drawable/dashboard_manage_keys.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
diff --git a/OpenPGP-Keychain/res/drawable/dashboard_my_keys.xml b/OpenPGP-Keychain/res/drawable/dashboard_my_keys.xml
deleted file mode 100644
index d4045db45..000000000
--- a/OpenPGP-Keychain/res/drawable/dashboard_my_keys.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
diff --git a/OpenPGP-Keychain/res/layout/api_apps_list_activity.xml b/OpenPGP-Keychain/res/layout/api_apps_list_activity.xml
index 11f663f7f..71fbcfb12 100644
--- a/OpenPGP-Keychain/res/layout/api_apps_list_activity.xml
+++ b/OpenPGP-Keychain/res/layout/api_apps_list_activity.xml
@@ -1,12 +1,20 @@
-
+ android:layout_height="match_parent" >
-
+ android:layout_height="match_parent" >
-
\ No newline at end of file
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/decrypt.xml b/OpenPGP-Keychain/res/layout/decrypt.xml
index c52d873f1..9d9e1a1e8 100644
--- a/OpenPGP-Keychain/res/layout/decrypt.xml
+++ b/OpenPGP-Keychain/res/layout/decrypt.xml
@@ -1,185 +1,177 @@
-
-
-
+ android:layout_height="match_parent" >
-
+ android:fillViewport="true"
+ android:orientation="vertical" >
-
+ android:layout_height="match_parent"
+ android:fillViewport="true" >
-
+
-
+ android:layout_height="wrap_content" >
-
+
+
+
+
+
-
+ android:orientation="vertical"
+ android:paddingLeft="5dip" >
+
+
+
+
+
+
+ android:orientation="horizontal" >
-
+ android:src="@drawable/ic_previous" />
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ android:src="@drawable/ic_next" />
-
+ android:layout_height="0dip"
+ android:layout_weight="1" >
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="4dp" >
-
-
+ android:id="@+id/message"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="top"
+ android:inputType="text|textCapSentences|textMultiLine|textLongMessage"
+ android:scrollHorizontally="true" />
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="4dp" >
-
+ android:orientation="horizontal" >
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/drawer_list.xml b/OpenPGP-Keychain/res/layout/drawer_list.xml
new file mode 100644
index 000000000..18210afc5
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/drawer_list.xml
@@ -0,0 +1,18 @@
+
+
+
diff --git a/OpenPGP-Keychain/res/layout/drawer_list_item.xml b/OpenPGP-Keychain/res/layout/drawer_list_item.xml
index d8e39399c..0d4b9537e 100644
--- a/OpenPGP-Keychain/res/layout/drawer_list_item.xml
+++ b/OpenPGP-Keychain/res/layout/drawer_list_item.xml
@@ -1,28 +1,31 @@
-
+
-
-
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"/>
+-->
diff --git a/OpenPGP-Keychain/res/layout/encrypt.xml b/OpenPGP-Keychain/res/layout/encrypt.xml
index 1fb3bc589..41b01c7c3 100644
--- a/OpenPGP-Keychain/res/layout/encrypt.xml
+++ b/OpenPGP-Keychain/res/layout/encrypt.xml
@@ -1,315 +1,307 @@
-
-
-
+ android:layout_height="match_parent" >
-
+ android:layout_height="match_parent"
+ android:fillViewport="true" >
-
-
-
-
-
-
-
-
-
+ android:orientation="vertical"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp" >
+
+
+
+
+
+
+
+
+
+ android:orientation="vertical"
+ android:padding="4dp" >
-
+ android:orientation="horizontal" >
+
+
+
+
+
+
+
+
+
+
+ android:orientation="horizontal"
+ android:paddingBottom="3dip" >
-
+ android:layout_gravity="center_vertical"
+ android:layout_margin="4dp"
+ android:text="@string/btn_select_encrypt_keys"
+ bootstrapbutton:bb_icon_left="fa-users"
+ bootstrapbutton:bb_size="default"
+ bootstrapbutton:bb_type="default" />
-
+ android:padding="4dp"
+ android:stretchColumns="1" >
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
+
-
-
-
-
+
+
-
+
-
-
-
-
-
-
+
-
+
+
+
+
-
+
+
+ android:layout_weight="1"
+ android:gravity="center_horizontal|center_vertical"
+ android:text="@string/label_message"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+
-
+ android:layout_height="0dip"
+ android:layout_weight="1" >
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="4dp" >
-
-
+ android:id="@+id/message"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="top"
+ android:inputType="text|textCapSentences|textMultiLine|textLongMessage" />
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="4dp" >
-
-
-
+
+
+
+
+
+
+
-
+ android:orientation="horizontal" >
-
+
-
+
+
+
-
+ android:orientation="horizontal" >
-
+
+
-
+ android:orientation="horizontal" >
+
+
+
-
-
-
+
+
+
+
+
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/help_activity.xml b/OpenPGP-Keychain/res/layout/help_activity.xml
index 89aac6476..77c62e03f 100644
--- a/OpenPGP-Keychain/res/layout/help_activity.xml
+++ b/OpenPGP-Keychain/res/layout/help_activity.xml
@@ -1,13 +1,20 @@
-
-
-
+
+
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
+
+
+
+
+
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/import_keys.xml b/OpenPGP-Keychain/res/layout/import_keys.xml
index 1b40bad58..c2217d2ec 100644
--- a/OpenPGP-Keychain/res/layout/import_keys.xml
+++ b/OpenPGP-Keychain/res/layout/import_keys.xml
@@ -1,55 +1,63 @@
-
+ android:id="@+id/drawer_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
-
-
-
+ android:layout_centerHorizontal="true" >
+
+
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
\ No newline at end of file
+ android:layout_height="match_parent"
+ android:layout_above="@+id/import_footer"
+ android:layout_alignParentLeft="true"
+ android:layout_below="@+id/import_navigation_fragment"
+ android:orientation="vertical"
+ android:paddingLeft="4dp"
+ android:paddingRight="4dp" />
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/key_list_public_activity.xml b/OpenPGP-Keychain/res/layout/key_list_public_activity.xml
index 704b3c8c9..f0e843e56 100644
--- a/OpenPGP-Keychain/res/layout/key_list_public_activity.xml
+++ b/OpenPGP-Keychain/res/layout/key_list_public_activity.xml
@@ -1,5 +1,4 @@
-
-
-
-
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/key_list_secret_activity.xml b/OpenPGP-Keychain/res/layout/key_list_secret_activity.xml
index b8df9faa7..13370f2e5 100644
--- a/OpenPGP-Keychain/res/layout/key_list_secret_activity.xml
+++ b/OpenPGP-Keychain/res/layout/key_list_secret_activity.xml
@@ -1,14 +1,20 @@
-
+ android:layout_height="match_parent" >
-
+ android:layout_height="match_parent" >
-
\ No newline at end of file
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/main.xml b/OpenPGP-Keychain/res/layout/main.xml
deleted file mode 100644
index 71967271b..000000000
--- a/OpenPGP-Keychain/res/layout/main.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/values/strings.xml b/OpenPGP-Keychain/res/values/strings.xml
index e9ce05b77..e84a3e2f2 100644
--- a/OpenPGP-Keychain/res/values/strings.xml
+++ b/OpenPGP-Keychain/res/values/strings.xml
@@ -370,17 +370,14 @@
Encrypt to this contact
-
- Contacts
- Encrypt
- Decrypt
- Import Keys
- My Keys
- Settings
- Registered Apps
- Help
-
-
+ Contacts
+ Encrypt
+ Decrypt
+ Import Keys
+ My Keys
+ Settings
+ Registered Apps
+ HelpOpen navigation drawerClose navigation drawer
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListActivity.java
index 4530ac2fc..3c553fff5 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListActivity.java
@@ -18,44 +18,19 @@
package org.sufficientlysecure.keychain.service.remote;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.ui.MainActivity;
+import org.sufficientlysecure.keychain.ui.DrawerActivity;
-import com.actionbarsherlock.app.ActionBar;
-import com.actionbarsherlock.app.SherlockFragmentActivity;
-import com.actionbarsherlock.view.MenuItem;
-
-import android.content.Intent;
import android.os.Bundle;
-public class RegisteredAppsListActivity extends SherlockFragmentActivity {
- private ActionBar mActionBar;
+public class RegisteredAppsListActivity extends DrawerActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mActionBar = getSupportActionBar();
-
setContentView(R.layout.api_apps_list_activity);
- mActionBar.setDisplayShowTitleEnabled(true);
- mActionBar.setDisplayHomeAsUpEnabled(true);
+ setupDrawerNavigation(savedInstanceState);
}
- /**
- * Menu Options
- */
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- // app icon in Action Bar clicked; go home
- Intent intent = new Intent(this, MainActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(intent);
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DecryptActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DecryptActivity.java
index f7fc49083..87229f6c3 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DecryptActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DecryptActivity.java
@@ -66,13 +66,12 @@ import android.widget.TextView;
import android.widget.Toast;
import android.widget.ViewFlipper;
-import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
import com.beardedhen.androidbootstrap.BootstrapButton;
@SuppressLint("NewApi")
-public class DecryptActivity extends SherlockFragmentActivity {
+public class DecryptActivity extends DrawerActivity {
/* Intents */
// without permission
@@ -144,13 +143,6 @@ public class DecryptActivity extends SherlockFragmentActivity {
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
- case android.R.id.home:
- // app icon in Action Bar clicked; go home
- Intent intent = new Intent(this, MainActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(intent);
- return true;
-
case Id.menu.option.decrypt: {
decryptClicked();
@@ -245,6 +237,8 @@ public class DecryptActivity extends SherlockFragmentActivity {
initView();
+ setupDrawerNavigation(savedInstanceState);
+
// Handle intent actions
handleActions(getIntent());
@@ -262,7 +256,8 @@ public class DecryptActivity extends SherlockFragmentActivity {
if (matcher.matches()) {
data = matcher.group(1);
mMessage.setText(data);
- Toast.makeText(this, R.string.using_clipboard_content, Toast.LENGTH_SHORT).show();
+ Toast.makeText(this, R.string.using_clipboard_content, Toast.LENGTH_SHORT)
+ .show();
}
}
}
@@ -472,8 +467,9 @@ public class DecryptActivity extends SherlockFragmentActivity {
if (!file.exists() || !file.isFile()) {
Toast.makeText(
this,
- getString(R.string.error_message, getString(R.string.error_file_not_found)),
- Toast.LENGTH_SHORT).show();
+ getString(R.string.error_message,
+ getString(R.string.error_file_not_found)), Toast.LENGTH_SHORT)
+ .show();
return;
}
}
@@ -592,7 +588,8 @@ public class DecryptActivity extends SherlockFragmentActivity {
}
mSecretKeyId = Id.key.symmetric;
if (!PgpOperation.hasSymmetricEncryption(this, inStream)) {
- throw new PgpGeneralException(getString(R.string.error_no_known_encryption_found));
+ throw new PgpGeneralException(
+ getString(R.string.error_no_known_encryption_found));
}
mAssumeSymmetricEncryption = true;
}
@@ -790,8 +787,8 @@ public class DecryptActivity extends SherlockFragmentActivity {
.getBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN)) {
mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
Toast.makeText(DecryptActivity.this,
- R.string.unknown_signature_key_touch_to_look_up, Toast.LENGTH_LONG)
- .show();
+ R.string.unknown_signature_key_touch_to_look_up,
+ Toast.LENGTH_LONG).show();
} else {
mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DrawerActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DrawerActivity.java
index e278af5e3..db6a3a155 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DrawerActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DrawerActivity.java
@@ -1,26 +1,27 @@
/*
- *
- * from https://github.com/tobykurien/SherlockNavigationDrawer
- *
- * Copyright 2013 The Android Open Source Project
+ * Copyright (C) 2014 Dominik Schürmann
*
- * 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
*/
package org.sufficientlysecure.keychain.ui;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.service.remote.RegisteredAppsListActivity;
+import android.app.Activity;
+import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.drawable.Drawable;
@@ -30,45 +31,24 @@ import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.view.ActionProvider;
import android.view.ContextMenu.ContextMenuInfo;
+import android.view.LayoutInflater;
import android.view.SubMenu;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
+import android.widget.TextView;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
+import com.beardedhen.androidbootstrap.FontAwesomeText;
/**
- * This example illustrates a common usage of the DrawerLayout widget in the Android support
- * library.
- *
- *
- * When a navigation (left) drawer is present, the host activity should detect presses of the action
- * bar's Up affordance as a signal to open and close the navigation drawer. The
- * ActionBarDrawerToggle facilitates this behavior. Items within the drawer should fall into one of
- * two categories:
- *
- *
- *
- *
View switches. A view switch follows the same basic policies as list or tab
- * navigation in that a view switch does not create navigation history. This pattern should only be
- * used at the root activity of a task, leaving some form of Up navigation active for activities
- * further down the navigation hierarchy.
- *
Selective Up. The drawer allows the user to choose an alternate parent for
- * Up navigation. This allows a user to jump across an app's navigation hierarchy at will. The
- * application should treat this as it treats Up navigation from a different task, replacing the
- * current task stack using TaskStackBuilder or similar. This is the only form of navigation drawer
- * that should be used outside of the root activity of a task.
- *
- *
- *
- * Right side drawers should be used for actions, not navigation. This follows the pattern
- * established by the Action Bar that navigation should be to the left and actions to the right. An
- * action should be an operation performed on the current contents of the window, for example
- * enabling or disabling a data overlay on top of the current content.
- *
+ * some fundamental ideas from https://github.com/tobykurien/SherlockNavigationDrawer
+ *
+ *
*/
public class DrawerActivity extends SherlockFragmentActivity {
private DrawerLayout mDrawerLayout;
@@ -77,11 +57,14 @@ public class DrawerActivity extends SherlockFragmentActivity {
private CharSequence mDrawerTitle;
private CharSequence mTitle;
- private String[] mDrawerTitles;
+
+ private static Class[] mItemsClass = new Class[] { KeyListPublicActivity.class,
+ EncryptActivity.class, DecryptActivity.class, ImportKeysActivity.class,
+ KeyListSecretActivity.class, PreferencesActivity.class,
+ RegisteredAppsListActivity.class, HelpActivity.class };
protected void setupDrawerNavigation(Bundle savedInstanceState) {
- // mTitle = mDrawerTitle = getTitle();
- mDrawerTitles = getResources().getStringArray(R.array.drawer_array);
+ mDrawerTitle = getString(R.string.app_name);
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerList = (ListView) findViewById(R.id.left_drawer);
@@ -89,10 +72,31 @@ public class DrawerActivity extends SherlockFragmentActivity {
// opens
mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
// set up the drawer's list view with items and click listener
- mDrawerList.setAdapter(new ArrayAdapter(this, R.layout.drawer_list_item,
- mDrawerTitles));
+ // mDrawerList
+ // .setAdapter(new ArrayAdapter(this, R.layout.drawer_list_item, mItemsText));
+
+ NavItem mItemIconTexts[] = new NavItem[] {
+ new NavItem("fa-user", getString(R.string.nav_contacts)),
+ new NavItem("fa-lock", getString(R.string.nav_encrypt)),
+ new NavItem("fa-unlock", getString(R.string.nav_decrypt)),
+ new NavItem("fa-download", getString(R.string.nav_import)),
+ new NavItem("fa-key", getString(R.string.nav_secret_keys)),
+ new NavItem("fa-wrench", getString(R.string.nav_settings)),
+ new NavItem("fa-android", getString(R.string.nav_apps)),
+ new NavItem("fa-question", getString(R.string.nav_help)), };
+
+ mDrawerList.setAdapter(new NavigationDrawerAdapter(this, R.layout.drawer_list_item,
+ mItemIconTexts));
+
mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
+ //
+
// enable ActionBar app icon to behave as action to toggle nav drawer
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
@@ -107,21 +111,22 @@ public class DrawerActivity extends SherlockFragmentActivity {
) {
public void onDrawerClosed(View view) {
getSupportActionBar().setTitle(mTitle);
- supportInvalidateOptionsMenu(); // creates call to
- // onPrepareOptionsMenu()
+ // creates call to onPrepareOptionsMenu()
+ supportInvalidateOptionsMenu();
}
public void onDrawerOpened(View drawerView) {
+ mTitle = getSupportActionBar().getTitle();
getSupportActionBar().setTitle(mDrawerTitle);
- supportInvalidateOptionsMenu(); // creates call to
- // onPrepareOptionsMenu()
+ // creates call to onPrepareOptionsMenu()
+ supportInvalidateOptionsMenu();
}
};
mDrawerLayout.setDrawerListener(mDrawerToggle);
- if (savedInstanceState == null) {
- selectItem(0);
- }
+ // if (savedInstanceState == null) {
+ // selectItem(0);
+ // }
}
/* Called whenever we call invalidateOptionsMenu() */
@@ -142,8 +147,10 @@ public class DrawerActivity extends SherlockFragmentActivity {
return true;
}
+ return super.onOptionsItemSelected(item);
+
// Handle action buttons
- switch (item.getItemId()) {
+ // switch (item.getItemId()) {
// case R.id.action_websearch:
// // create intent to perform web search for this planet
// Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
@@ -155,9 +162,9 @@ public class DrawerActivity extends SherlockFragmentActivity {
// Toast.makeText(this, R.string.app_not_available, Toast.LENGTH_LONG).show();
// }
// return true;
- default:
- return super.onOptionsItemSelected(item);
- }
+ // default:
+ // return super.onOptionsItemSelected(item);
+ // }
}
private android.view.MenuItem getMenuItem(final MenuItem item) {
@@ -377,32 +384,24 @@ public class DrawerActivity extends SherlockFragmentActivity {
}
private void selectItem(int position) {
- // update the main content by replacing fragments
- // Fragment fragment = new PlanetFragment();
- // Bundle args = new Bundle();
- // args.putInt(PlanetFragment.ARG_PLANET_NUMBER, position);
- // fragment.setArguments(args);
-
- // FragmentManager fragmentManager = getSupportFragmentManager();
- // fragmentManager.beginTransaction().replace(R.id.content_frame, fragment).commit();
-
// update selected item and title, then close the drawer
mDrawerList.setItemChecked(position, true);
// setTitle(mDrawerTitles[position]);
mDrawerLayout.closeDrawer(mDrawerList);
- }
- // @Override
- // public void setTitle(CharSequence title) {
- // mTitle = title;
- // getSupportActionBar().setTitle(mTitle);
- // }
+ finish();
+ overridePendingTransition(0, 0);
+
+ Intent intent = new Intent(this, mItemsClass[position]);
+ startActivity(intent);
+ // disable animation of activity start
+ overridePendingTransition(0, 0);
+ }
/**
* When using the ActionBarDrawerToggle, you must call it during onPostCreate() and
* onConfigurationChanged()...
*/
-
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
@@ -417,28 +416,59 @@ public class DrawerActivity extends SherlockFragmentActivity {
mDrawerToggle.onConfigurationChanged(newConfig);
}
- /**
- * Fragment that appears in the "content_frame", shows a planet
- */
- // public static class PlanetFragment extends SherlockFragment {
- // public static final String ARG_PLANET_NUMBER = "planet_number";
- //
- // public PlanetFragment() {
- // // Empty constructor required for fragment subclasses
- // }
- //
- // @Override
- // public View onCreateView(LayoutInflater inflater, ViewGroup container,
- // Bundle savedInstanceState) {
- // View rootView = inflater.inflate(R.layout.fragment_planet, container, false);
- // int i = getArguments().getInt(ARG_PLANET_NUMBER);
- // String planet = getResources().getStringArray(R.array.drawer_array)[i];
- //
- // int imageId = getResources().getIdentifier(planet.toLowerCase(Locale.getDefault()),
- // "drawable", getActivity().getPackageName());
- // ((ImageView) rootView.findViewById(R.id.image)).setImageResource(imageId);
- // getActivity().setTitle(planet);
- // return rootView;
- // }
- // }
+ private class NavItem {
+ public String icon;
+ public String title;
+
+ public NavItem(String icon, String title) {
+ super();
+ this.icon = icon;
+ this.title = title;
+ }
+ }
+
+ private class NavigationDrawerAdapter extends ArrayAdapter {
+ Context context;
+ int layoutResourceId;
+ NavItem data[] = null;
+
+ public NavigationDrawerAdapter(Context context, int layoutResourceId, NavItem[] data) {
+ super(context, layoutResourceId, data);
+ this.layoutResourceId = layoutResourceId;
+ this.context = context;
+ this.data = data;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View row = convertView;
+ NavItemHolder holder = null;
+
+ if (row == null) {
+ LayoutInflater inflater = ((Activity) context).getLayoutInflater();
+ row = inflater.inflate(layoutResourceId, parent, false);
+
+ holder = new NavItemHolder();
+ holder.img = (FontAwesomeText) row.findViewById(R.id.drawer_item_icon);
+ holder.txtTitle = (TextView) row.findViewById(R.id.drawer_item_text);
+
+ row.setTag(holder);
+ } else {
+ holder = (NavItemHolder) row.getTag();
+ }
+
+ NavItem item = data[position];
+ holder.txtTitle.setText(item.title);
+ holder.img.setIcon(item.icon);
+
+ return row;
+ }
+
+ }
+
+ static class NavItemHolder {
+ FontAwesomeText img;
+ TextView txtTitle;
+ }
+
}
\ No newline at end of file
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptActivity.java
index 681548c9b..24caebb3a 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptActivity.java
@@ -63,12 +63,11 @@ import android.widget.TextView;
import android.widget.Toast;
import android.widget.ViewFlipper;
-import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
import com.beardedhen.androidbootstrap.BootstrapButton;
-public class EncryptActivity extends SherlockFragmentActivity {
+public class EncryptActivity extends DrawerActivity {
/* Intents */
public static final String ACTION_ENCRYPT = Constants.INTENT_PREFIX + "ENCRYPT";
@@ -153,13 +152,6 @@ public class EncryptActivity extends SherlockFragmentActivity {
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
- case android.R.id.home:
- // app icon in Action Bar clicked; go home
- Intent intent = new Intent(this, MainActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(intent);
- return true;
-
case Id.menu.option.encrypt_to_clipboard:
encryptToClipboardClicked();
@@ -187,6 +179,8 @@ public class EncryptActivity extends SherlockFragmentActivity {
initView();
+ setupDrawerNavigation(savedInstanceState);
+
// Handle intent actions
handleActions(getIntent());
@@ -491,8 +485,9 @@ public class EncryptActivity extends SherlockFragmentActivity {
if (!file.exists() || !file.isFile()) {
Toast.makeText(
this,
- getString(R.string.error_message, getString(R.string.error_file_not_found)),
- Toast.LENGTH_SHORT).show();
+ getString(R.string.error_message,
+ getString(R.string.error_file_not_found)), Toast.LENGTH_SHORT)
+ .show();
return;
}
}
@@ -510,7 +505,8 @@ public class EncryptActivity extends SherlockFragmentActivity {
gotPassPhrase = (passPhrase.length() != 0);
if (!gotPassPhrase) {
- Toast.makeText(this, R.string.passphrase_must_not_be_empty, Toast.LENGTH_SHORT).show();
+ Toast.makeText(this, R.string.passphrase_must_not_be_empty, Toast.LENGTH_SHORT)
+ .show();
return;
}
} else {
@@ -522,8 +518,8 @@ public class EncryptActivity extends SherlockFragmentActivity {
}
if (!encryptIt && mSecretKeyId == 0) {
- Toast.makeText(this, R.string.select_encryption_or_signature_key, Toast.LENGTH_SHORT)
- .show();
+ Toast.makeText(this, R.string.select_encryption_or_signature_key,
+ Toast.LENGTH_SHORT).show();
return;
}
@@ -852,10 +848,12 @@ public class EncryptActivity extends SherlockFragmentActivity {
Choice[] choices = new Choice[] {
new Choice(Id.choice.compression.none, getString(R.string.choice_none) + " ("
+ getString(R.string.compression_fast) + ")"),
- new Choice(Id.choice.compression.zip, "ZIP (" + getString(R.string.compression_fast) + ")"),
- new Choice(Id.choice.compression.zlib, "ZLIB (" + getString(R.string.compression_fast) + ")"),
- new Choice(Id.choice.compression.bzip2, "BZIP2 (" + getString(R.string.compression_very_slow)
- + ")"), };
+ new Choice(Id.choice.compression.zip, "ZIP ("
+ + getString(R.string.compression_fast) + ")"),
+ new Choice(Id.choice.compression.zlib, "ZLIB ("
+ + getString(R.string.compression_fast) + ")"),
+ new Choice(Id.choice.compression.bzip2, "BZIP2 ("
+ + getString(R.string.compression_very_slow) + ")"), };
ArrayAdapter adapter = new ArrayAdapter(this,
android.R.layout.simple_spinner_item, choices);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptDecryptActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptDecryptActivity.java
deleted file mode 100644
index 7f0184381..000000000
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptDecryptActivity.java
+++ /dev/null
@@ -1,175 +0,0 @@
-package org.sufficientlysecure.keychain.ui;
-
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.util.Log;
-
-import com.actionbarsherlock.app.ActionBar;
-import com.actionbarsherlock.app.ActionBar.Tab;
-import com.actionbarsherlock.app.SherlockFragmentActivity;
-import com.actionbarsherlock.view.Menu;
-import com.actionbarsherlock.view.MenuInflater;
-import com.actionbarsherlock.view.MenuItem;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentActivity;
-import android.support.v4.app.FragmentManager;
-import android.support.v4.app.FragmentTransaction;
-
-public class EncryptDecryptActivity extends SherlockFragmentActivity {
- private FragmentActivity mActivity;
- private ActionBar mActionBar;
- private ActionBar.Tab mTab1;
- private ActionBar.Tab mTab2;
- private ActionBar.Tab mTab3;
-
- // @Override
- // public boolean onCreateOptionsMenu(Menu menu) {
- // MenuInflater inflater = getSupportMenuInflater();
- // inflater.inflate(R.menu.lists_activity, menu);
- // return true;
- // }
-
- /**
- * Menu item to go back home in ActionBar, other menu items are defined in Fragments
- */
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- // app icon in Action Bar clicked; go home
- Intent intent = new Intent(mActivity, MainActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(intent);
- return true;
-
- // case R.id.menu_import:
- // ImportExportHelper.openFileStream(mActivity);
- // return true;
- //
- // case R.id.menu_export:
- // ImportExportHelper.exportLists(mActivity);
- // return true;
-
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-
- /**
- * Set up Tabs on create
- */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mActivity = this;
-
- setContentView(R.layout.lists_activity);
-
- mActionBar = getSupportActionBar();
- mActionBar.setDisplayShowTitleEnabled(true);
- mActionBar.setDisplayHomeAsUpEnabled(true);
-
- mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
-
- mTab1 = getSupportActionBar().newTab();
- mTab2 = getSupportActionBar().newTab();
- mTab3 = getSupportActionBar().newTab();
-
- mTab1.setTabListener(new TabListener(this, "publicList",
- KeyListPublicFragment.class));
- mTab2.setTabListener(new TabListener(this, "import",
- KeyListPublicFragment.class));
-
- setTabTextBasedOnOrientation(getResources().getConfiguration());
-
- mActionBar.addTab(mTab1);
- mActionBar.addTab(mTab2);
- // mActionBar.addTab(mTab3);
- }
-
- private void setTabTextBasedOnOrientation(Configuration config) {
- // longer names for landscape mode or tablets
- // if (config.orientation == Configuration.ORIENTATION_LANDSCAPE
- // || config.screenLayout == Configuration.SCREENLAYOUT_SIZE_XLARGE) {
- mTab1.setText(getString(R.string.dashboard_manage_keys));
- mTab2.setText(getString(R.string.dashboard_manage_keys));
-
- // } else {
- // mTab1.setText(getString(R.string.lists_tab_blacklist_short));
- // mTab2.setText(getString(R.string.lists_tab_whitelist_short));
- // mTab3.setText(getString(R.string.lists_tab_redirection_list_short));
- // }
- }
-
- /**
- * Change text on orientation change
- */
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
-
- setTabTextBasedOnOrientation(newConfig);
- }
-
- public static class TabListener implements ActionBar.TabListener {
- private Fragment mFragment;
- private final Activity mActivity;
- private final String mTag;
- private final Class mClass;
-
- /**
- * Constructor used each time a new tab is created.
- *
- * @param activity
- * The host Activity, used to instantiate the fragment
- * @param tag
- * The identifier tag for the fragment
- * @param clz
- * The fragment's Class, used to instantiate the fragment
- */
- public TabListener(Activity activity, String tag, Class clz) {
- mActivity = activity;
- mTag = tag;
- mClass = clz;
- }
-
- @Override
- public void onTabReselected(Tab tab, FragmentTransaction ft) {
- }
-
- /**
- * Open Fragment based on selected Tab
- */
- @Override
- public void onTabSelected(Tab tab, FragmentTransaction ignoredFt) {
- // bug in compatibility lib:
- // http://stackoverflow.com/questions/8645549/null-fragmenttransaction-being-passed-to-tablistener-ontabselected
- FragmentManager fragMgr = ((FragmentActivity) mActivity).getSupportFragmentManager();
- FragmentTransaction ft = fragMgr.beginTransaction();
-
- mFragment = Fragment.instantiate(mActivity, mClass.getName());
- ft.replace(R.id.lists_tabs_container, mFragment, mTag);
- ft.commit();
- }
-
- @Override
- public void onTabUnselected(Tab tab, FragmentTransaction ignoredFt) {
- FragmentManager fragMgr = ((FragmentActivity) mActivity).getSupportFragmentManager();
- FragmentTransaction ft = fragMgr.beginTransaction();
-
- if (mFragment != null) {
- // Remove the fragment
- ft.remove(mFragment);
- }
-
- ft.commit();
- }
- }
-
-}
\ No newline at end of file
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/HelpActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/HelpActivity.java
index 13350b6c6..7407564a9 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/HelpActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/HelpActivity.java
@@ -17,27 +17,24 @@
package org.sufficientlysecure.keychain.ui;
+import java.util.ArrayList;
+
import org.sufficientlysecure.keychain.R;
+import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.FragmentTransaction;
+import android.support.v4.view.ViewPager;
import android.widget.TextView;
import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.app.ActionBar.Tab;
-import com.actionbarsherlock.view.MenuItem;
-
-import java.util.ArrayList;
-
-import android.content.Context;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentPagerAdapter;
-import android.support.v4.view.ViewPager;
-
import com.actionbarsherlock.app.SherlockFragmentActivity;
-public class HelpActivity extends SherlockFragmentActivity {
+public class HelpActivity extends DrawerActivity {
public static final String EXTRA_SELECTED_TAB = "selectedTab";
ViewPager mViewPager;
@@ -45,37 +42,18 @@ public class HelpActivity extends SherlockFragmentActivity {
TextView tabCenter;
TextView tabText;
- /**
- * Menu Items
- */
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- // app icon in Action Bar clicked; go home
- Intent intent = new Intent(this, MainActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(intent);
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
+
setContentView(R.layout.help_activity);
- mViewPager = new ViewPager(this);
- mViewPager.setId(R.id.pager);
+ mViewPager = (ViewPager) findViewById(R.id.pager);
+
+ setupDrawerNavigation(savedInstanceState);
- setContentView(mViewPager);
ActionBar bar = getSupportActionBar();
bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
- bar.setDisplayShowTitleEnabled(true);
- bar.setDisplayHomeAsUpEnabled(true);
mTabsAdapter = new TabsAdapter(this, mViewPager);
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
index 94b5304a1..1842364e7 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
@@ -22,7 +22,6 @@ import java.util.List;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.helper.ActionBarHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
@@ -51,11 +50,9 @@ import android.widget.Toast;
import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.app.ActionBar.OnNavigationListener;
-import com.actionbarsherlock.app.SherlockFragmentActivity;
-import com.actionbarsherlock.view.MenuItem;
import com.beardedhen.androidbootstrap.BootstrapButton;
-public class ImportKeysActivity extends SherlockFragmentActivity implements OnNavigationListener {
+public class ImportKeysActivity extends DrawerActivity implements OnNavigationListener {
public static final String ACTION_IMPORT_KEY = Constants.INTENT_PREFIX + "IMPORT_KEY";
public static final String ACTION_IMPORT_KEY_FROM_QR_CODE = Constants.INTENT_PREFIX
+ "IMPORT_KEY_FROM_QR_CODE";
@@ -103,8 +100,12 @@ public class ImportKeysActivity extends SherlockFragmentActivity implements OnNa
}
});
+ getSupportActionBar().setDisplayShowTitleEnabled(false);
+
+ setupDrawerNavigation(savedInstanceState);
+
// set actionbar without home button if called from another app
- ActionBarHelper.setBackButton(this);
+ // ActionBarHelper.setBackButton(this);
// set drop down navigation
mNavigationStrings = getResources().getStringArray(R.array.import_action_list);
@@ -114,7 +115,6 @@ public class ImportKeysActivity extends SherlockFragmentActivity implements OnNa
list.setDropDownViewResource(R.layout.sherlock_spinner_dropdown_item);
getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
getSupportActionBar().setListNavigationCallbacks(list, this);
- getSupportActionBar().setDisplayShowTitleEnabled(false);
handleActions(savedInstanceState, getIntent());
}
@@ -240,23 +240,6 @@ public class ImportKeysActivity extends SherlockFragmentActivity implements OnNa
mListFragment.loadNew(importData, importFilename);
}
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
-
- case android.R.id.home:
- // app icon in Action Bar clicked; go home
- Intent intent = new Intent(this, MainActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(intent);
- return true;
-
- default:
- return super.onOptionsItemSelected(item);
-
- }
- }
-
// private void importAndSignOld(final long keyId, final String expectedFingerprint) {
// if (expectedFingerprint != null && expectedFingerprint.length() > 0) {
//
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyActivity.java
index 3766b1c42..91481d2b4 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Dominik Schürmann
+ * Copyright (C) 2013-2014 Dominik Schürmann
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -37,8 +37,6 @@ import android.os.Message;
import android.os.Messenger;
import android.widget.Toast;
-import com.actionbarsherlock.app.SherlockFragmentActivity;
-
/**
* This implements export key method and delete key method. Used in lists and key view and key edit.
*
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java
index 476d3c96c..cb04ddf0d 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java
@@ -50,8 +50,8 @@ public class KeyListPublicActivity extends KeyActivity {
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_key_list_public_import:
- Intent intentImportFromFile = new Intent(this, ImportKeysActivity.class);
- startActivityForResult(intentImportFromFile, Id.request.import_from_qr_code);
+ Intent intentImport = new Intent(this, ImportKeysActivity.class);
+ startActivityForResult(intentImport, Id.request.import_from_qr_code);
return true;
case R.id.menu_key_list_public_export:
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java
index d95f03ae1..747055a47 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java
@@ -35,8 +35,8 @@ public class KeyListSecretActivity extends KeyActivity {
setContentView(R.layout.key_list_secret_activity);
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- getSupportActionBar().setHomeButtonEnabled(true);
+ // now setup navigation drawer in DrawerActivity...
+ setupDrawerNavigation(savedInstanceState);
}
@Override
@@ -49,13 +49,6 @@ public class KeyListSecretActivity extends KeyActivity {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
- case android.R.id.home:
- // app icon in Action Bar clicked; go home
- Intent intent = new Intent(this, MainActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(intent);
-
- return true;
case R.id.menu_key_list_secret_create:
createKey();
@@ -70,8 +63,8 @@ public class KeyListSecretActivity extends KeyActivity {
return true;
case R.id.menu_key_list_secret_import:
- Intent intentImportFromFile = new Intent(this, ImportKeysActivity.class);
- startActivityForResult(intentImportFromFile, Id.request.import_from_qr_code);
+ Intent intentImport = new Intent(this, ImportKeysActivity.class);
+ startActivityForResult(intentImport, Id.request.import_from_qr_code);
return true;
default:
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/MainActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/MainActivity.java
deleted file mode 100644
index 9a270e60b..000000000
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/MainActivity.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2012-2013 Dominik Schürmann
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package org.sufficientlysecure.keychain.ui;
-
-import org.sufficientlysecure.keychain.Id;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.service.remote.RegisteredAppsListActivity;
-
-import com.actionbarsherlock.app.ActionBar;
-import com.actionbarsherlock.app.SherlockActivity;
-import com.actionbarsherlock.view.Menu;
-import com.actionbarsherlock.view.MenuItem;
-
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.View;
-
-public class MainActivity extends SherlockActivity {
-
- public void manageKeysOnClick(View view) {
- // used instead of startActivity set actionbar based on callingPackage
- startActivityForResult(new Intent(this, KeyListPublicActivity.class), 0);
- }
-
- public void myKeysOnClick(View view) {
- // used instead of startActivity set actionbar based on callingPackage
- startActivityForResult(new Intent(this, KeyListSecretActivity.class), 0);
- }
-
- public void encryptOnClick(View view) {
- Intent intent = new Intent(MainActivity.this, EncryptActivity.class);
- intent.setAction(EncryptActivity.ACTION_ENCRYPT);
- // used instead of startActivity set actionbar based on callingPackage
- startActivityForResult(intent, 0);
- }
-
- public void decryptOnClick(View view) {
- Intent intent = new Intent(MainActivity.this, DecryptActivity.class);
- intent.setAction(DecryptActivity.ACTION_DECRYPT);
- // used instead of startActivity set actionbar based on callingPackage
- startActivityForResult(intent, 0);
- }
-
- public void scanQrcodeOnClick(View view) {
- Intent intent = new Intent(this, ImportKeysActivity.class);
- startActivityForResult(intent, 0);
- }
-
- public void helpOnClick(View view) {
- startActivity(new Intent(this, HelpActivity.class));
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
-
- final ActionBar actionBar = getSupportActionBar();
- actionBar.setDisplayShowTitleEnabled(true);
- actionBar.setDisplayHomeAsUpEnabled(false);
- actionBar.setHomeButtonEnabled(false);
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- menu.add(0, Id.menu.option.preferences, 0, R.string.menu_preferences)
- .setIcon(R.drawable.ic_menu_settings)
- .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
- menu.add(0, Id.menu.option.crypto_consumers, 0, R.string.menu_api_app_settings)
- .setIcon(R.drawable.ic_menu_settings)
- .setShowAsAction(MenuItem.SHOW_AS_ACTION_WITH_TEXT);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
-
- case Id.menu.option.preferences:
- startActivity(new Intent(this, PreferencesActivity.class));
- return true;
-
- case Id.menu.option.crypto_consumers:
- startActivity(new Intent(this, RegisteredAppsListActivity.class));
- return true;
-
- default:
- break;
-
- }
- return false;
- }
-
-}
\ No newline at end of file
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/PreferencesActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/PreferencesActivity.java
index 6607ab4d5..43ad0c93d 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/PreferencesActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/PreferencesActivity.java
@@ -20,13 +20,9 @@ import org.spongycastle.bcpg.HashAlgorithmTags;
import org.spongycastle.openpgp.PGPEncryptedData;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
+import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.ui.widget.IntegerListPreference;
-import org.sufficientlysecure.keychain.R;
-
-import com.actionbarsherlock.app.ActionBar;
-import com.actionbarsherlock.app.SherlockPreferenceActivity;
-import com.actionbarsherlock.view.MenuItem;
import android.content.Intent;
import android.os.Bundle;
@@ -34,6 +30,9 @@ import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.preference.PreferenceScreen;
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.app.SherlockPreferenceActivity;
+
public class PreferencesActivity extends SherlockPreferenceActivity {
private IntegerListPreference mPassPhraseCacheTtl = null;
private IntegerListPreference mEncryptionAlgorithm = null;
@@ -220,21 +219,4 @@ public class PreferencesActivity extends SherlockPreferenceActivity {
}
}
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
-
- case android.R.id.home:
- // app icon in Action Bar clicked; go home
- Intent intent = new Intent(this, MainActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(intent);
- return true;
-
- default:
- break;
-
- }
- return false;
- }
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java
index b0711ed31..83669a523 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java
@@ -137,21 +137,4 @@ public class SelectSecretKeyActivity extends SherlockFragmentActivity {
return true;
}
- /**
- * Menu Options
- */
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- // app icon in Action Bar clicked; go home
- Intent intent = new Intent(this, MainActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(intent);
- return true;
-
- default:
- return super.onOptionsItemSelected(item);
- }
- }
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/DashboardLayout.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/DashboardLayout.java
deleted file mode 100644
index 158a271bc..000000000
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/DashboardLayout.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright 2011 Google Inc.
- *
- * 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.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * Custom layout that arranges children in a grid-like manner, optimizing for even horizontal and
- * vertical whitespace.
- */
-public class DashboardLayout extends ViewGroup {
- private static final int UNEVEN_GRID_PENALTY_MULTIPLIER = 10;
-
- private int mMaxChildWidth = 0;
- private int mMaxChildHeight = 0;
-
- public DashboardLayout(Context context) {
- super(context, null);
- }
-
- public DashboardLayout(Context context, AttributeSet attrs) {
- super(context, attrs, 0);
- }
-
- public DashboardLayout(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- mMaxChildWidth = 0;
- mMaxChildHeight = 0;
-
- // Measure once to find the maximum child size.
-
- int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
- MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.AT_MOST);
- int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
- MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.AT_MOST);
-
- final int count = getChildCount();
- for (int i = 0; i < count; i++) {
- final View child = getChildAt(i);
- if (child.getVisibility() == GONE) {
- continue;
- }
-
- child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
-
- mMaxChildWidth = Math.max(mMaxChildWidth, child.getMeasuredWidth());
- mMaxChildHeight = Math.max(mMaxChildHeight, child.getMeasuredHeight());
- }
-
- // Measure again for each child to be exactly the same size.
-
- childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxChildWidth, MeasureSpec.EXACTLY);
- childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxChildHeight, MeasureSpec.EXACTLY);
-
- for (int i = 0; i < count; i++) {
- final View child = getChildAt(i);
- if (child.getVisibility() == GONE) {
- continue;
- }
-
- child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
- }
-
- setMeasuredDimension(resolveSize(mMaxChildWidth, widthMeasureSpec),
- resolveSize(mMaxChildHeight, heightMeasureSpec));
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- int width = r - l;
- int height = b - t;
-
- final int count = getChildCount();
-
- // Calculate the number of visible children.
- int visibleCount = 0;
- for (int i = 0; i < count; i++) {
- final View child = getChildAt(i);
- if (child.getVisibility() == GONE) {
- continue;
- }
- ++visibleCount;
- }
-
- if (visibleCount == 0) {
- return;
- }
-
- // Calculate what number of rows and columns will optimize for even horizontal and
- // vertical whitespace between items. Start with a 1 x N grid, then try 2 x N, and so on.
- int bestSpaceDifference = Integer.MAX_VALUE;
- int spaceDifference;
-
- // Horizontal and vertical space between items
- int hSpace = 0;
- int vSpace = 0;
-
- int cols = 1;
- int rows;
-
- while (true) {
- rows = (visibleCount - 1) / cols + 1;
-
- hSpace = ((width - mMaxChildWidth * cols) / (cols + 1));
- vSpace = ((height - mMaxChildHeight * rows) / (rows + 1));
-
- spaceDifference = Math.abs(vSpace - hSpace);
- if (rows * cols != visibleCount) {
- spaceDifference *= UNEVEN_GRID_PENALTY_MULTIPLIER;
- } else if (rows * mMaxChildHeight > height || cols * mMaxChildWidth > width) {
- spaceDifference *= UNEVEN_GRID_PENALTY_MULTIPLIER;
- }
-
- if (spaceDifference < bestSpaceDifference) {
- // Found a better whitespace squareness/ratio
- bestSpaceDifference = spaceDifference;
-
- // If we found a better whitespace squareness and there's only 1 row, this is
- // the best we can do.
- if (rows == 1) {
- break;
- }
- } else {
- // This is a worse whitespace ratio, use the previous value of cols and exit.
- --cols;
- rows = (visibleCount - 1) / cols + 1;
- hSpace = ((width - mMaxChildWidth * cols) / (cols + 1));
- vSpace = ((height - mMaxChildHeight * rows) / (rows + 1));
- break;
- }
-
- ++cols;
- }
-
- // Lay out children based on calculated best-fit number of rows and cols.
-
- // If we chose a layout that has negative horizontal or vertical space, force it to zero.
- hSpace = Math.max(0, hSpace);
- vSpace = Math.max(0, vSpace);
-
- // Re-use width/height variables to be child width/height.
- width = (width - hSpace * (cols + 1)) / cols;
- height = (height - vSpace * (rows + 1)) / rows;
-
- int left, top;
- int col, row;
- int visibleIndex = 0;
- for (int i = 0; i < count; i++) {
- final View child = getChildAt(i);
- if (child.getVisibility() == GONE) {
- continue;
- }
-
- row = visibleIndex / cols;
- col = visibleIndex % cols;
-
- left = hSpace * (col + 1) + width * col;
- top = vSpace * (row + 1) + height * row;
-
- child.layout(left, top, (hSpace == 0 && col == cols - 1) ? r : (left + width),
- (vSpace == 0 && row == rows - 1) ? b : (top + height));
- ++visibleIndex;
- }
- }
-}
\ No newline at end of file
--
cgit v1.2.3
From 37fd7dbbc70d6fc033db4101eaf89bad627fe499 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Thu, 9 Jan 2014 23:13:23 +0100
Subject: fix bad crashes
---
.../keychain/ui/DrawerActivity.java | 10 -
.../keychain/ui/EditKeyActivity.java | 19 +-
.../keychain/ui/KeyActivity.java | 208 ---------------------
.../keychain/ui/KeyListPublicActivity.java | 58 +++---
.../keychain/ui/KeyListSecretActivity.java | 16 +-
.../keychain/ui/KeyViewActivity.java | 20 +-
6 files changed, 80 insertions(+), 251 deletions(-)
delete mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyActivity.java
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DrawerActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DrawerActivity.java
index db6a3a155..2190388b0 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DrawerActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DrawerActivity.java
@@ -71,9 +71,6 @@ public class DrawerActivity extends SherlockFragmentActivity {
// set a custom shadow that overlays the main content when the drawer
// opens
mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
- // set up the drawer's list view with items and click listener
- // mDrawerList
- // .setAdapter(new ArrayAdapter(this, R.layout.drawer_list_item, mItemsText));
NavItem mItemIconTexts[] = new NavItem[] {
new NavItem("fa-user", getString(R.string.nav_contacts)),
@@ -90,13 +87,6 @@ public class DrawerActivity extends SherlockFragmentActivity {
mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
- //
-
// enable ActionBar app icon to behave as action to toggle nav drawer
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
index 569a16c41..b79200ba1 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
@@ -27,6 +27,7 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.ActionBarHelper;
+import org.sufficientlysecure.keychain.helper.ExportHelper;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
@@ -61,11 +62,12 @@ import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.LinearLayout;
import android.widget.Toast;
+import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
import com.beardedhen.androidbootstrap.BootstrapButton;
-public class EditKeyActivity extends KeyActivity {
+public class EditKeyActivity extends SherlockFragmentActivity {
// Actions for internal use only:
public static final String ACTION_CREATE_KEY = Constants.INTENT_PREFIX + "CREATE_KEY";
@@ -100,10 +102,14 @@ public class EditKeyActivity extends KeyActivity {
Vector mKeysUsages;
boolean masterCanSign = true;
+ ExportHelper mExportHelper;
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ mExportHelper = new ExportHelper(this);
+
mUserIds = new Vector();
mKeys = new Vector();
mKeysUsages = new Vector();
@@ -322,7 +328,7 @@ public class EditKeyActivity extends KeyActivity {
cancelClicked();
return true;
case R.id.menu_key_edit_export_file:
- showExportKeysDialog(mDataUri, Id.type.secret_key, Constants.path.APP_DIR
+ mExportHelper.showExportKeysDialog(mDataUri, Id.type.secret_key, Constants.path.APP_DIR
+ "/secexport.asc");
return true;
case R.id.menu_key_edit_delete: {
@@ -337,7 +343,7 @@ public class EditKeyActivity extends KeyActivity {
}
};
- deleteKey(mDataUri, Id.type.secret_key, returnHandler);
+ mExportHelper.deleteKey(mDataUri, Id.type.secret_key, returnHandler);
return true;
}
}
@@ -656,4 +662,11 @@ public class EditKeyActivity extends KeyActivity {
mChangePassPhrase.setText(isPassphraseSet() ? getString(R.string.btn_change_passphrase)
: getString(R.string.btn_set_passphrase));
}
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (!mExportHelper.handleActivityResult(requestCode, resultCode, data)) {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ }
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyActivity.java
deleted file mode 100644
index 91481d2b4..000000000
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyActivity.java
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * Copyright (C) 2013-2014 Dominik Schürmann
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package org.sufficientlysecure.keychain.ui;
-
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.Id;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
-import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
-import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;
-import org.sufficientlysecure.keychain.util.Log;
-
-import android.app.ProgressDialog;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Messenger;
-import android.widget.Toast;
-
-/**
- * This implements export key method and delete key method. Used in lists and key view and key edit.
- *
- * TODO: get key type by looking at dataUri!
- *
- */
-public class KeyActivity extends DrawerActivity {
- protected FileDialogFragment mFileDialog;
- protected String mExportFilename;
-
- protected void deleteKey(Uri dataUri, final int keyType, Handler deleteHandler) {
- long keyRingRowId = Long.valueOf(dataUri.getLastPathSegment());
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(deleteHandler);
-
- DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger,
- new long[] { keyRingRowId }, keyType);
-
- deleteKeyDialog.show(getSupportFragmentManager(), "deleteKeyDialog");
- }
-
- /**
- * Show dialog where to export keys
- *
- * @param keyRingMasterKeyId
- * if -1 export all keys
- */
- public void showExportKeysDialog(final Uri dataUri, final int keyType,
- final String exportFilename) {
- mExportFilename = exportFilename;
-
- // Message is received after file is selected
- Handler returnHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.what == FileDialogFragment.MESSAGE_OKAY) {
- Bundle data = message.getData();
- mExportFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME);
-
- long keyRingRowId = Long.valueOf(dataUri.getLastPathSegment());
-
- // TODO?
- long keyRingMasterKeyId = ProviderHelper.getSecretMasterKeyId(KeyActivity.this,
- keyRingRowId);
-
- exportKeys(keyRingMasterKeyId, keyType);
- }
- }
- };
-
- // Create a new Messenger for the communication back
- final Messenger messenger = new Messenger(returnHandler);
-
- DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
- public void run() {
- String title = null;
- if (dataUri != null) {
- // single key export
- title = getString(R.string.title_export_key);
- } else {
- title = getString(R.string.title_export_keys);
- }
-
- String message = null;
- if (keyType == Id.type.public_key) {
- message = getString(R.string.specify_file_to_export_to);
- } else {
- message = getString(R.string.specify_file_to_export_secret_keys_to);
- }
-
- mFileDialog = FileDialogFragment.newInstance(messenger, title, message,
- exportFilename, null, Id.request.filename);
-
- mFileDialog.show(getSupportFragmentManager(), "fileDialog");
- }
- });
- }
-
- /**
- * Export keys
- *
- * @param keyRingMasterKeyId
- * if -1 export all keys
- */
- public void exportKeys(long keyRingMasterKeyId, int keyType) {
- Log.d(Constants.TAG, "exportKeys started");
-
- // Send all information needed to service to export key in other thread
- Intent intent = new Intent(this, KeychainIntentService.class);
-
- intent.setAction(KeychainIntentService.ACTION_EXPORT_KEYRING);
-
- // fill values for this action
- Bundle data = new Bundle();
-
- data.putString(KeychainIntentService.EXPORT_FILENAME, mExportFilename);
- data.putInt(KeychainIntentService.EXPORT_KEY_TYPE, keyType);
-
- if (keyRingMasterKeyId == -1) {
- data.putBoolean(KeychainIntentService.EXPORT_ALL, true);
- } else {
- data.putLong(KeychainIntentService.EXPORT_KEY_RING_MASTER_KEY_ID, keyRingMasterKeyId);
- }
-
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
-
- // Message is received after exporting is done in ApgService
- KeychainIntentServiceHandler exportHandler = new KeychainIntentServiceHandler(this,
- R.string.progress_exporting, ProgressDialog.STYLE_HORIZONTAL) {
- public void handleMessage(Message message) {
- // handle messages by standard ApgHandler first
- super.handleMessage(message);
-
- if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
- // get returned data bundle
- Bundle returnData = message.getData();
-
- int exported = returnData.getInt(KeychainIntentService.RESULT_EXPORT);
- String toastMessage;
- if (exported == 1) {
- toastMessage = getString(R.string.key_exported);
- } else if (exported > 0) {
- toastMessage = getString(R.string.keys_exported, exported);
- } else {
- toastMessage = getString(R.string.no_keys_exported);
- }
- Toast.makeText(KeyActivity.this, toastMessage, Toast.LENGTH_SHORT).show();
-
- }
- };
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(exportHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
-
- // show progress dialog
- exportHandler.showProgressDialog(this);
-
- // start service with intent
- startService(intent);
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- switch (requestCode) {
- case Id.request.filename: {
- if (resultCode == RESULT_OK && data != null) {
- try {
- String path = data.getData().getPath();
- Log.d(Constants.TAG, "path=" + path);
-
- // set filename used in export/import dialogs
- mFileDialog.setFilename(path);
- } catch (NullPointerException e) {
- Log.e(Constants.TAG, "Nullpointer while retrieving path!", e);
- }
- }
- return;
- }
-
- default: {
- break;
- }
- }
- super.onActivityResult(requestCode, resultCode, data);
- }
-}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java
index cb04ddf0d..204939610 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java
@@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.ui;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.ExportHelper;
import android.content.Intent;
import android.os.Bundle;
@@ -27,12 +28,16 @@ import android.os.Bundle;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
-public class KeyListPublicActivity extends KeyActivity {
+public class KeyListPublicActivity extends DrawerActivity {
+
+ ExportHelper mExportHelper;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ mExportHelper = new ExportHelper(this);
+
setContentView(R.layout.key_list_public_activity);
// now setup navigation drawer in DrawerActivity...
@@ -55,7 +60,7 @@ public class KeyListPublicActivity extends KeyActivity {
return true;
case R.id.menu_key_list_public_export:
- showExportKeysDialog(null, Id.type.public_key, Constants.path.APP_DIR
+ mExportHelper.showExportKeysDialog(null, Id.type.public_key, Constants.path.APP_DIR
+ "/pubexport.asc");
return true;
@@ -64,27 +69,30 @@ public class KeyListPublicActivity extends KeyActivity {
}
}
- // @Override
- // protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- // switch (requestCode) {
- // case Id.request.look_up_key_id: {
- // if (resultCode == RESULT_CANCELED || data == null
- // || data.getStringExtra(KeyServerQueryActivity.RESULT_EXTRA_TEXT) == null) {
- // return;
- // }
- //
- // Intent intent = new Intent(this, KeyListPublicActivity.class);
- // intent.setAction(KeyListPublicActivity.ACTION_IMPORT);
- // intent.putExtra(KeyListPublicActivity.EXTRA_TEXT,
- // data.getStringExtra(KeyListActivity.EXTRA_TEXT));
- // handleActions(intent);
- // break;
- // }
- //
- // default: {
- // super.onActivityResult(requestCode, resultCode, data);
- // break;
- // }
- // }
- // }
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (!mExportHelper.handleActivityResult(requestCode, resultCode, data)) {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ // switch (requestCode) {
+ // case Id.request.look_up_key_id: {
+ // if (resultCode == RESULT_CANCELED || data == null
+ // || data.getStringExtra(KeyServerQueryActivity.RESULT_EXTRA_TEXT) == null) {
+ // return;
+ // }
+ //
+ // Intent intent = new Intent(this, KeyListPublicActivity.class);
+ // intent.setAction(KeyListPublicActivity.ACTION_IMPORT);
+ // intent.putExtra(KeyListPublicActivity.EXTRA_TEXT,
+ // data.getStringExtra(KeyListActivity.EXTRA_TEXT));
+ // handleActions(intent);
+ // break;
+ // }
+ //
+ // default: {
+ // super.onActivityResult(requestCode, resultCode, data);
+ // break;
+ // }
+ // }
+ }
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java
index 747055a47..34a053d25 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java
@@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.ui;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.ExportHelper;
import android.content.Intent;
import android.os.Bundle;
@@ -27,12 +28,16 @@ import android.os.Bundle;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
-public class KeyListSecretActivity extends KeyActivity {
+public class KeyListSecretActivity extends DrawerActivity {
+
+ ExportHelper mExportHelper;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ mExportHelper = new ExportHelper(this);
+
setContentView(R.layout.key_list_secret_activity);
// now setup navigation drawer in DrawerActivity...
@@ -58,7 +63,7 @@ public class KeyListSecretActivity extends KeyActivity {
return true;
case R.id.menu_key_list_secret_export:
- showExportKeysDialog(null, Id.type.secret_key, Constants.path.APP_DIR
+ mExportHelper.showExportKeysDialog(null, Id.type.secret_key, Constants.path.APP_DIR
+ "/secexport.asc");
return true;
@@ -86,4 +91,11 @@ public class KeyListSecretActivity extends KeyActivity {
startActivityForResult(intent, 0);
}
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (!mExportHelper.handleActivityResult(requestCode, resultCode, data)) {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java
index fb04bb1f8..0e1e20cce 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java
@@ -29,6 +29,7 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
+import org.sufficientlysecure.keychain.helper.ExportHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
@@ -55,13 +56,17 @@ import android.view.View.OnClickListener;
import android.widget.TextView;
import android.widget.Toast;
+import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
import com.beardedhen.androidbootstrap.BootstrapButton;
@SuppressLint("NewApi")
-public class KeyViewActivity extends KeyActivity implements CreateNdefMessageCallback,
+public class KeyViewActivity extends SherlockFragmentActivity implements CreateNdefMessageCallback,
OnNdefPushCompleteCallback {
+
+ ExportHelper mExportHelper;
+
private Uri mDataUri;
private PGPPublicKey mPublicKey;
@@ -81,6 +86,8 @@ public class KeyViewActivity extends KeyActivity implements CreateNdefMessageCal
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ mExportHelper = new ExportHelper(this);
+
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
@@ -125,7 +132,7 @@ public class KeyViewActivity extends KeyActivity implements CreateNdefMessageCal
uploadToKeyserver(mDataUri);
return true;
case R.id.menu_key_view_export_file:
- showExportKeysDialog(mDataUri, Id.type.public_key, Constants.path.APP_DIR
+ mExportHelper.showExportKeysDialog(mDataUri, Id.type.public_key, Constants.path.APP_DIR
+ "/pubexport.asc");
return true;
case R.id.menu_key_view_share_default:
@@ -152,7 +159,7 @@ public class KeyViewActivity extends KeyActivity implements CreateNdefMessageCal
}
};
- deleteKey(mDataUri, Id.type.public_key, returnHandler);
+ mExportHelper.deleteKey(mDataUri, Id.type.public_key, returnHandler);
return true;
}
}
@@ -373,4 +380,11 @@ public class KeyViewActivity extends KeyActivity implements CreateNdefMessageCal
}
};
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (!mExportHelper.handleActivityResult(requestCode, resultCode, data)) {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+
}
--
cgit v1.2.3
From 3abad09cb0f755a53b47a239c4738b4d1718abfd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Thu, 9 Jan 2014 23:51:23 +0100
Subject: preferences and help into menu not drawer according to guidelines and
google apps
---
OpenPGP-Keychain/res/layout/encrypt.xml | 2 +-
OpenPGP-Keychain/res/values/strings.xml | 3 +-
.../keychain/helper/ExportHelper.java | 205 +++++++++++++++++++++
.../keychain/ui/DrawerActivity.java | 35 +++-
.../keychain/ui/HelpActivity.java | 19 +-
.../keychain/ui/PreferencesActivity.java | 5 +-
6 files changed, 247 insertions(+), 22 deletions(-)
create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/ExportHelper.java
diff --git a/OpenPGP-Keychain/res/layout/encrypt.xml b/OpenPGP-Keychain/res/layout/encrypt.xml
index 41b01c7c3..eea484df2 100644
--- a/OpenPGP-Keychain/res/layout/encrypt.xml
+++ b/OpenPGP-Keychain/res/layout/encrypt.xml
@@ -119,7 +119,7 @@
android:layout_gravity="center_vertical"
android:layout_margin="4dp"
android:text="@string/btn_select_encrypt_keys"
- bootstrapbutton:bb_icon_left="fa-users"
+ bootstrapbutton:bb_icon_left="fa-user"
bootstrapbutton:bb_size="default"
bootstrapbutton:bb_type="default" />
diff --git a/OpenPGP-Keychain/res/values/strings.xml b/OpenPGP-Keychain/res/values/strings.xml
index e84a3e2f2..a84cd52a1 100644
--- a/OpenPGP-Keychain/res/values/strings.xml
+++ b/OpenPGP-Keychain/res/values/strings.xml
@@ -81,6 +81,7 @@
Settings
+ HelpRegistered AppsImport from fileImport from QR Code
@@ -375,9 +376,7 @@
DecryptImport KeysMy Keys
- SettingsRegistered Apps
- HelpOpen navigation drawerClose navigation drawer
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/ExportHelper.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/ExportHelper.java
new file mode 100644
index 000000000..261e26be6
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/ExportHelper.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann
+ *
+ * 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.helper;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.Id;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.KeychainIntentService;
+import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
+import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
+import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;
+import org.sufficientlysecure.keychain.util.Log;
+
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
+import android.widget.Toast;
+
+import com.actionbarsherlock.app.SherlockFragmentActivity;
+
+public class ExportHelper {
+ protected FileDialogFragment mFileDialog;
+ protected String mExportFilename;
+
+ SherlockFragmentActivity activity;
+
+ public ExportHelper(SherlockFragmentActivity activity) {
+ super();
+ this.activity = activity;
+ }
+
+ public void deleteKey(Uri dataUri, final int keyType, Handler deleteHandler) {
+ long keyRingRowId = Long.valueOf(dataUri.getLastPathSegment());
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(deleteHandler);
+
+ DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger,
+ new long[] { keyRingRowId }, keyType);
+
+ deleteKeyDialog.show(activity.getSupportFragmentManager(), "deleteKeyDialog");
+ }
+
+ /**
+ * Show dialog where to export keys
+ *
+ * @param keyRingMasterKeyId
+ * if -1 export all keys
+ */
+ public void showExportKeysDialog(final Uri dataUri, final int keyType,
+ final String exportFilename) {
+ mExportFilename = exportFilename;
+
+ // Message is received after file is selected
+ Handler returnHandler = new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == FileDialogFragment.MESSAGE_OKAY) {
+ 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);
+ }
+ }
+ };
+
+ // Create a new Messenger for the communication back
+ final Messenger messenger = new Messenger(returnHandler);
+
+ 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 {
+ title = activity.getString(R.string.title_export_keys);
+ }
+
+ String message = null;
+ if (keyType == Id.type.public_key) {
+ message = activity.getString(R.string.specify_file_to_export_to);
+ } else {
+ message = activity.getString(R.string.specify_file_to_export_secret_keys_to);
+ }
+
+ mFileDialog = FileDialogFragment.newInstance(messenger, title, message,
+ exportFilename, null, Id.request.filename);
+
+ mFileDialog.show(activity.getSupportFragmentManager(), "fileDialog");
+ }
+ });
+ }
+
+ /**
+ * Export keys
+ *
+ * @param keyRingMasterKeyId
+ * if -1 export all keys
+ */
+ public void exportKeys(long keyRingMasterKeyId, int keyType) {
+ Log.d(Constants.TAG, "exportKeys started");
+
+ // Send all information needed to service to export key in other thread
+ Intent intent = new Intent(activity, KeychainIntentService.class);
+
+ intent.setAction(KeychainIntentService.ACTION_EXPORT_KEYRING);
+
+ // fill values for this action
+ Bundle data = new Bundle();
+
+ data.putString(KeychainIntentService.EXPORT_FILENAME, mExportFilename);
+ data.putInt(KeychainIntentService.EXPORT_KEY_TYPE, keyType);
+
+ if (keyRingMasterKeyId == -1) {
+ data.putBoolean(KeychainIntentService.EXPORT_ALL, true);
+ } else {
+ data.putLong(KeychainIntentService.EXPORT_KEY_RING_MASTER_KEY_ID, keyRingMasterKeyId);
+ }
+
+ intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
+
+ // Message is received after exporting is done in ApgService
+ KeychainIntentServiceHandler exportHandler = new KeychainIntentServiceHandler(activity,
+ R.string.progress_exporting, ProgressDialog.STYLE_HORIZONTAL) {
+ public void handleMessage(Message message) {
+ // handle messages by standard ApgHandler first
+ super.handleMessage(message);
+
+ if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
+ // get returned data bundle
+ Bundle returnData = message.getData();
+
+ int exported = returnData.getInt(KeychainIntentService.RESULT_EXPORT);
+ String toastMessage;
+ if (exported == 1) {
+ toastMessage = activity.getString(R.string.key_exported);
+ } else if (exported > 0) {
+ toastMessage = activity.getString(R.string.keys_exported, exported);
+ } else {
+ toastMessage = activity.getString(R.string.no_keys_exported);
+ }
+ Toast.makeText(activity, toastMessage, Toast.LENGTH_SHORT).show();
+
+ }
+ };
+ };
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(exportHandler);
+ intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+
+ // show progress dialog
+ exportHandler.showProgressDialog(activity);
+
+ // start service with intent
+ activity.startService(intent);
+ }
+
+ public boolean handleActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == Id.request.filename) {
+ if (resultCode == Activity.RESULT_OK && data != null) {
+ try {
+ String path = data.getData().getPath();
+ Log.d(Constants.TAG, "path=" + path);
+
+ // set filename used in export/import dialogs
+ mFileDialog.setFilename(path);
+ } catch (NullPointerException e) {
+ Log.e(Constants.TAG, "Nullpointer while retrieving path!", e);
+ }
+ }
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DrawerActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DrawerActivity.java
index 2190388b0..ee8a01432 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DrawerActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DrawerActivity.java
@@ -60,8 +60,10 @@ public class DrawerActivity extends SherlockFragmentActivity {
private static Class[] mItemsClass = new Class[] { KeyListPublicActivity.class,
EncryptActivity.class, DecryptActivity.class, ImportKeysActivity.class,
- KeyListSecretActivity.class, PreferencesActivity.class,
- RegisteredAppsListActivity.class, HelpActivity.class };
+ KeyListSecretActivity.class, RegisteredAppsListActivity.class };
+
+ private static final int MENU_ID_PREFERENCE = 222;
+ private static final int MENU_ID_HELP = 223;
protected void setupDrawerNavigation(Bundle savedInstanceState) {
mDrawerTitle = getString(R.string.app_name);
@@ -78,9 +80,7 @@ public class DrawerActivity extends SherlockFragmentActivity {
new NavItem("fa-unlock", getString(R.string.nav_decrypt)),
new NavItem("fa-download", getString(R.string.nav_import)),
new NavItem("fa-key", getString(R.string.nav_secret_keys)),
- new NavItem("fa-wrench", getString(R.string.nav_settings)),
- new NavItem("fa-android", getString(R.string.nav_apps)),
- new NavItem("fa-question", getString(R.string.nav_help)), };
+ new NavItem("fa-android", getString(R.string.nav_apps)) };
mDrawerList.setAdapter(new NavigationDrawerAdapter(this, R.layout.drawer_list_item,
mItemIconTexts));
@@ -119,6 +119,14 @@ public class DrawerActivity extends SherlockFragmentActivity {
// }
}
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ menu.add(42, MENU_ID_PREFERENCE, 100, R.string.menu_preferences);
+ menu.add(42, MENU_ID_HELP, 101, R.string.menu_help);
+
+ return super.onCreateOptionsMenu(menu);
+ }
+
/* Called whenever we call invalidateOptionsMenu() */
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
@@ -137,7 +145,20 @@ public class DrawerActivity extends SherlockFragmentActivity {
return true;
}
- return super.onOptionsItemSelected(item);
+ switch (item.getItemId()) {
+ case MENU_ID_PREFERENCE: {
+ Intent intent = new Intent(this, PreferencesActivity.class);
+ startActivity(intent);
+ return true;
+ }
+ case MENU_ID_HELP: {
+ Intent intent = new Intent(this, HelpActivity.class);
+ startActivity(intent);
+ return true;
+ }
+ default:
+ return super.onOptionsItemSelected(item);
+ }
// Handle action buttons
// switch (item.getItemId()) {
@@ -365,7 +386,7 @@ public class DrawerActivity extends SherlockFragmentActivity {
};
}
- /* The click listner for ListView in the navigation drawer */
+ /* The click listener for ListView in the navigation drawer */
private class DrawerItemClickListener implements ListView.OnItemClickListener {
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/HelpActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/HelpActivity.java
index 7407564a9..d604c1c86 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/HelpActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/HelpActivity.java
@@ -34,7 +34,7 @@ import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.app.ActionBar.Tab;
import com.actionbarsherlock.app.SherlockFragmentActivity;
-public class HelpActivity extends DrawerActivity {
+public class HelpActivity extends SherlockFragmentActivity {
public static final String EXTRA_SELECTED_TAB = "selectedTab";
ViewPager mViewPager;
@@ -50,10 +50,11 @@ public class HelpActivity extends DrawerActivity {
mViewPager = (ViewPager) findViewById(R.id.pager);
- setupDrawerNavigation(savedInstanceState);
-
- ActionBar bar = getSupportActionBar();
- bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
+ final ActionBar actionBar = getSupportActionBar();
+ actionBar.setDisplayShowTitleEnabled(true);
+ actionBar.setDisplayHomeAsUpEnabled(false);
+ actionBar.setHomeButtonEnabled(false);
+ actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
mTabsAdapter = new TabsAdapter(this, mViewPager);
@@ -65,20 +66,20 @@ public class HelpActivity extends DrawerActivity {
Bundle startBundle = new Bundle();
startBundle.putInt(HelpFragmentHtml.ARG_HTML_FILE, R.raw.help_start);
- mTabsAdapter.addTab(bar.newTab().setText(getString(R.string.help_tab_start)),
+ mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_start)),
HelpFragmentHtml.class, startBundle, (selectedTab == 0 ? true : false));
Bundle nfcBundle = new Bundle();
nfcBundle.putInt(HelpFragmentHtml.ARG_HTML_FILE, R.raw.help_nfc_beam);
- mTabsAdapter.addTab(bar.newTab().setText(getString(R.string.help_tab_nfc_beam)),
+ mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_nfc_beam)),
HelpFragmentHtml.class, nfcBundle, (selectedTab == 1 ? true : false));
Bundle changelogBundle = new Bundle();
changelogBundle.putInt(HelpFragmentHtml.ARG_HTML_FILE, R.raw.help_changelog);
- mTabsAdapter.addTab(bar.newTab().setText(getString(R.string.help_tab_changelog)),
+ mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_changelog)),
HelpFragmentHtml.class, changelogBundle, (selectedTab == 2 ? true : false));
- mTabsAdapter.addTab(bar.newTab().setText(getString(R.string.help_tab_about)),
+ mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_about)),
HelpFragmentAbout.class, null, (selectedTab == 3 ? true : false));
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/PreferencesActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/PreferencesActivity.java
index 43ad0c93d..46bbd05c9 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/PreferencesActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/PreferencesActivity.java
@@ -51,8 +51,8 @@ public class PreferencesActivity extends SherlockPreferenceActivity {
final ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayShowTitleEnabled(true);
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setHomeButtonEnabled(true);
+ actionBar.setDisplayHomeAsUpEnabled(false);
+ actionBar.setHomeButtonEnabled(false);
addPreferencesFromResource(R.xml.preferences);
@@ -218,5 +218,4 @@ public class PreferencesActivity extends SherlockPreferenceActivity {
}
}
}
-
}
--
cgit v1.2.3
From d7463a3c3b1822cc6a228b6a765f0054ef303d22 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Fri, 10 Jan 2014 09:02:32 +0100
Subject: bump to version code 21104 for beta testers
---
OpenPGP-Keychain/AndroidManifest.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/OpenPGP-Keychain/AndroidManifest.xml b/OpenPGP-Keychain/AndroidManifest.xml
index dcd496789..49074107c 100644
--- a/OpenPGP-Keychain/AndroidManifest.xml
+++ b/OpenPGP-Keychain/AndroidManifest.xml
@@ -18,7 +18,7 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/create_key_dialog.xml b/OpenPGP-Keychain/res/layout/create_key_dialog.xml
new file mode 100644
index 000000000..a2e908433
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/create_key_dialog.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/decrypt.xml b/OpenPGP-Keychain/res/layout/decrypt.xml
deleted file mode 100644
index 9d9e1a1e8..000000000
--- a/OpenPGP-Keychain/res/layout/decrypt.xml
+++ /dev/null
@@ -1,177 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/decrypt_activity.xml b/OpenPGP-Keychain/res/layout/decrypt_activity.xml
new file mode 100644
index 000000000..9d9e1a1e8
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/decrypt_activity.xml
@@ -0,0 +1,177 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/drawer_list_item.xml b/OpenPGP-Keychain/res/layout/drawer_list_item.xml
index 0d4b9537e..14760e79d 100644
--- a/OpenPGP-Keychain/res/layout/drawer_list_item.xml
+++ b/OpenPGP-Keychain/res/layout/drawer_list_item.xml
@@ -25,16 +25,4 @@
android:textAppearance="@android:style/TextAppearance.Medium"
android:textColor="#111" />
-
+
diff --git a/OpenPGP-Keychain/res/layout/edit_key.xml b/OpenPGP-Keychain/res/layout/edit_key.xml
deleted file mode 100644
index f8597b0df..000000000
--- a/OpenPGP-Keychain/res/layout/edit_key.xml
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/edit_key_activity.xml b/OpenPGP-Keychain/res/layout/edit_key_activity.xml
new file mode 100644
index 000000000..f8597b0df
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/edit_key_activity.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/edit_key_key_item.xml b/OpenPGP-Keychain/res/layout/edit_key_key_item.xml
index 06bff2f9e..11bce64ee 100644
--- a/OpenPGP-Keychain/res/layout/edit_key_key_item.xml
+++ b/OpenPGP-Keychain/res/layout/edit_key_key_item.xml
@@ -1,20 +1,4 @@
-
-
@@ -12,7 +12,7 @@
android:text="@string/label_main_user_id" />
@@ -33,7 +33,7 @@
@@ -51,7 +51,7 @@
@@ -69,7 +69,7 @@
@@ -91,7 +91,7 @@
diff --git a/OpenPGP-Keychain/res/layout/encrypt.xml b/OpenPGP-Keychain/res/layout/encrypt.xml
deleted file mode 100644
index eea484df2..000000000
--- a/OpenPGP-Keychain/res/layout/encrypt.xml
+++ /dev/null
@@ -1,307 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/encrypt_activity.xml b/OpenPGP-Keychain/res/layout/encrypt_activity.xml
new file mode 100644
index 000000000..eea484df2
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/encrypt_activity.xml
@@ -0,0 +1,307 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/fragment_planet.xml b/OpenPGP-Keychain/res/layout/fragment_planet.xml
deleted file mode 100644
index 4fe5bbe61..000000000
--- a/OpenPGP-Keychain/res/layout/fragment_planet.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
diff --git a/OpenPGP-Keychain/res/layout/help_about_fragment.xml b/OpenPGP-Keychain/res/layout/help_about_fragment.xml
new file mode 100644
index 000000000..71788e720
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/help_about_fragment.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/help_fragment_about.xml b/OpenPGP-Keychain/res/layout/help_fragment_about.xml
deleted file mode 100644
index 71788e720..000000000
--- a/OpenPGP-Keychain/res/layout/help_fragment_about.xml
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/import_keys.xml b/OpenPGP-Keychain/res/layout/import_keys.xml
deleted file mode 100644
index c2217d2ec..000000000
--- a/OpenPGP-Keychain/res/layout/import_keys.xml
+++ /dev/null
@@ -1,63 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/import_keys_activity.xml b/OpenPGP-Keychain/res/layout/import_keys_activity.xml
new file mode 100644
index 000000000..c2217d2ec
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/import_keys_activity.xml
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/key_list_child_item.xml b/OpenPGP-Keychain/res/layout/key_list_child_item.xml
deleted file mode 100644
index 81d843529..000000000
--- a/OpenPGP-Keychain/res/layout/key_list_child_item.xml
+++ /dev/null
@@ -1,102 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/OpenPGP-Keychain/res/layout/key_list_public_header.xml b/OpenPGP-Keychain/res/layout/key_list_public_header.xml
new file mode 100644
index 000000000..5768e4153
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/key_list_public_header.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/key_server_export.xml b/OpenPGP-Keychain/res/layout/key_server_export.xml
new file mode 100644
index 000000000..30e4e0644
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/key_server_export.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/key_server_export_layout.xml b/OpenPGP-Keychain/res/layout/key_server_export_layout.xml
deleted file mode 100644
index 257f087ee..000000000
--- a/OpenPGP-Keychain/res/layout/key_server_export_layout.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/key_server_query.xml b/OpenPGP-Keychain/res/layout/key_server_query.xml
new file mode 100644
index 000000000..20f260a95
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/key_server_query.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/key_server_query_layout.xml b/OpenPGP-Keychain/res/layout/key_server_query_layout.xml
deleted file mode 100644
index 5eca10c45..000000000
--- a/OpenPGP-Keychain/res/layout/key_server_query_layout.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/key_server_query_result_item.xml b/OpenPGP-Keychain/res/layout/key_server_query_result_item.xml
index 29c8b88f4..6d883d26d 100644
--- a/OpenPGP-Keychain/res/layout/key_server_query_result_item.xml
+++ b/OpenPGP-Keychain/res/layout/key_server_query_result_item.xml
@@ -1,97 +1,77 @@
-
-
-
+ android:singleLine="true" >
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:paddingLeft="5dip"
+ android:paddingRight="5dip" >
+ android:layout_height="wrap_content"
+ android:text="Main User ID"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
-
+ android:layout_height="wrap_content"
+ android:text="<user@somewhere.com>"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ android:layout_height="wrap_content"
+ android:gravity="right"
+ android:minWidth="90dip"
+ android:orientation="vertical"
+ android:paddingLeft="3dip" >
+ android:typeface="monospace" />
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
-
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="#e00" />
-
-
+ android:layout_marginLeft="30dip"
+ android:orientation="vertical" >
-
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/key_server_query_result_user_id.xml b/OpenPGP-Keychain/res/layout/key_server_query_result_user_id.xml
index 9d3a4a1ab..c69735e67 100644
--- a/OpenPGP-Keychain/res/layout/key_server_query_result_user_id.xml
+++ b/OpenPGP-Keychain/res/layout/key_server_query_result_user_id.xml
@@ -1,26 +1,10 @@
-
-
-
+ android:layout_marginRight="?android:attr/scrollbarSize"
+ android:paddingRight="3dip"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceSmall" >
-
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/key_view_activity.xml b/OpenPGP-Keychain/res/layout/key_view_activity.xml
deleted file mode 100644
index ffb2400f1..000000000
--- a/OpenPGP-Keychain/res/layout/key_view_activity.xml
+++ /dev/null
@@ -1,133 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/lists_activity.xml b/OpenPGP-Keychain/res/layout/lists_activity.xml
deleted file mode 100644
index 58b0906d3..000000000
--- a/OpenPGP-Keychain/res/layout/lists_activity.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/passphrase.xml b/OpenPGP-Keychain/res/layout/passphrase.xml
deleted file mode 100644
index 778ae6b80..000000000
--- a/OpenPGP-Keychain/res/layout/passphrase.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/passphrase_dialog.xml b/OpenPGP-Keychain/res/layout/passphrase_dialog.xml
new file mode 100644
index 000000000..4b331f0f2
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/passphrase_dialog.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/passphrase_repeat.xml b/OpenPGP-Keychain/res/layout/passphrase_repeat.xml
deleted file mode 100644
index 1eef4eb68..000000000
--- a/OpenPGP-Keychain/res/layout/passphrase_repeat.xml
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/passphrase_repeat_dialog.xml b/OpenPGP-Keychain/res/layout/passphrase_repeat_dialog.xml
new file mode 100644
index 000000000..2bdd231ee
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/passphrase_repeat_dialog.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/share_nfc_beam.xml b/OpenPGP-Keychain/res/layout/share_nfc_beam.xml
deleted file mode 100644
index 692f53e4e..000000000
--- a/OpenPGP-Keychain/res/layout/share_nfc_beam.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/sign_key_activity.xml b/OpenPGP-Keychain/res/layout/sign_key_activity.xml
new file mode 100644
index 000000000..b9f66db7f
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/sign_key_activity.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/sign_key_layout.xml b/OpenPGP-Keychain/res/layout/sign_key_layout.xml
deleted file mode 100644
index 4530831ee..000000000
--- a/OpenPGP-Keychain/res/layout/sign_key_layout.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/OpenPGP-Keychain/res/layout/stickylist_header.xml b/OpenPGP-Keychain/res/layout/stickylist_header.xml
deleted file mode 100644
index 5768e4153..000000000
--- a/OpenPGP-Keychain/res/layout/stickylist_header.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/view_key_activity.xml b/OpenPGP-Keychain/res/layout/view_key_activity.xml
new file mode 100644
index 000000000..ffb2400f1
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/view_key_activity.xml
@@ -0,0 +1,133 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/view_key_keys_item.xml b/OpenPGP-Keychain/res/layout/view_key_keys_item.xml
new file mode 100644
index 000000000..b50253980
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/view_key_keys_item.xml
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/view_key_userids_item.xml b/OpenPGP-Keychain/res/layout/view_key_userids_item.xml
new file mode 100644
index 000000000..cbc85189f
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/view_key_userids_item.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DecryptActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DecryptActivity.java
index 87229f6c3..6cc0b3b5a 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DecryptActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DecryptActivity.java
@@ -230,7 +230,7 @@ public class DecryptActivity extends DrawerActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.decrypt);
+ setContentView(R.layout.decrypt_activity);
// set actionbar without home button if called from another app
ActionBarHelper.setBackButton(this);
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
index b79200ba1..be2e4115b 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
@@ -429,7 +429,7 @@ public class EditKeyActivity extends SherlockFragmentActivity {
* id and key.
*/
private void buildLayout() {
- setContentView(R.layout.edit_key);
+ setContentView(R.layout.edit_key_activity);
// find views
mChangePassPhrase = (BootstrapButton) findViewById(R.id.edit_key_btn_change_pass_phrase);
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptActivity.java
index 24caebb3a..c974dfd46 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptActivity.java
@@ -172,7 +172,7 @@ public class EncryptActivity extends DrawerActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.encrypt);
+ setContentView(R.layout.encrypt_activity);
// set actionbar without home button if called from another app
ActionBarHelper.setBackButton(this);
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/HelpFragmentAbout.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/HelpFragmentAbout.java
index e7a977707..840ebb650 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/HelpFragmentAbout.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/HelpFragmentAbout.java
@@ -48,7 +48,7 @@ public class HelpFragmentAbout extends SherlockFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.help_fragment_about, container, false);
+ View view = inflater.inflate(R.layout.help_about_fragment, container, false);
TextView versionText = (TextView) view.findViewById(R.id.help_about_version);
versionText.setText(getString(R.string.help_about_version) + " " + getVersion());
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
index 1842364e7..7d8f4154f 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
@@ -83,7 +83,7 @@ public class ImportKeysActivity extends DrawerActivity implements OnNavigationLi
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.import_keys);
+ setContentView(R.layout.import_keys_activity);
mImportButton = (BootstrapButton) findViewById(R.id.import_import);
mImportButton.setOnClickListener(new OnClickListener() {
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
index 88503a5c4..ea088efca 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
@@ -259,7 +259,7 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte
*/
@Override
public void onItemClick(AdapterView> adapterView, View view, int position, long id) {
- Intent detailsIntent = new Intent(getActivity(), KeyViewActivity.class);
+ Intent detailsIntent = new Intent(getActivity(), ViewKeyActivity.class);
detailsIntent.setData(KeychainContract.KeyRings.buildPublicKeyRingsUri(Long.toString(id)));
startActivity(detailsIntent);
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyServerQueryActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyServerQueryActivity.java
index b4679f9d5..6073e6b80 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyServerQueryActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyServerQueryActivity.java
@@ -111,7 +111,7 @@ public class KeyServerQueryActivity extends SherlockFragmentActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.key_server_query_layout);
+ setContentView(R.layout.key_server_query);
mQuery = (EditText)findViewById(R.id.query);
mSearch = (Button)findViewById(R.id.btn_search);
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyServerUploadActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyServerUploadActivity.java
index 996637c7a..8a32ea513 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyServerUploadActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyServerUploadActivity.java
@@ -76,7 +76,7 @@ public class KeyServerUploadActivity extends SherlockFragmentActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.key_server_export_layout);
+ setContentView(R.layout.key_server_export);
export = (Button) findViewById(R.id.btn_export_to_server);
keyServer = (Spinner) findViewById(R.id.keyServer);
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java
deleted file mode 100644
index 0e1e20cce..000000000
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyViewActivity.java
+++ /dev/null
@@ -1,390 +0,0 @@
-/*
- * Copyright (C) 2013 Dominik Schürmann
- * Copyright (C) 2013 Bahtiar 'kalkin' Gadimov
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package org.sufficientlysecure.keychain.ui;
-
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.spongycastle.openpgp.PGPPublicKey;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.Id;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
-import org.sufficientlysecure.keychain.helper.ExportHelper;
-import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
-import org.sufficientlysecure.keychain.ui.dialog.ShareNfcDialogFragment;
-import org.sufficientlysecure.keychain.ui.dialog.ShareQrCodeDialogFragment;
-import org.sufficientlysecure.keychain.util.Log;
-
-import android.annotation.SuppressLint;
-import android.content.Intent;
-import android.net.Uri;
-import android.nfc.NdefMessage;
-import android.nfc.NdefRecord;
-import android.nfc.NfcAdapter;
-import android.nfc.NfcAdapter.CreateNdefMessageCallback;
-import android.nfc.NfcAdapter.OnNdefPushCompleteCallback;
-import android.nfc.NfcEvent;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.text.format.DateFormat;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.actionbarsherlock.app.SherlockFragmentActivity;
-import com.actionbarsherlock.view.Menu;
-import com.actionbarsherlock.view.MenuItem;
-import com.beardedhen.androidbootstrap.BootstrapButton;
-
-@SuppressLint("NewApi")
-public class KeyViewActivity extends SherlockFragmentActivity implements CreateNdefMessageCallback,
- OnNdefPushCompleteCallback {
-
- ExportHelper mExportHelper;
-
- private Uri mDataUri;
-
- private PGPPublicKey mPublicKey;
-
- private TextView mAlgorithm;
- private TextView mFingerint;
- private TextView mExpiry;
- private TextView mCreation;
- private BootstrapButton mActionEncrypt;
-
- // NFC
- private NfcAdapter mNfcAdapter;
- private byte[] mSharedKeyringBytes;
- private static final int NFC_SENT = 1;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mExportHelper = new ExportHelper(this);
-
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- getSupportActionBar().setHomeButtonEnabled(true);
-
- setContentView(R.layout.key_view_activity);
-
- mFingerint = (TextView) this.findViewById(R.id.fingerprint);
- mExpiry = (TextView) this.findViewById(R.id.expiry);
- mCreation = (TextView) this.findViewById(R.id.creation);
- mAlgorithm = (TextView) this.findViewById(R.id.algorithm);
- mActionEncrypt = (BootstrapButton) this.findViewById(R.id.action_encrypt);
-
- Intent intent = getIntent();
- mDataUri = intent.getData();
- if (mDataUri == null) {
- Log.e(Constants.TAG, "Intent data missing. Should be Uri of key!");
- finish();
- return;
- } else {
- Log.d(Constants.TAG, "uri: " + mDataUri);
- loadData(mDataUri);
- initNfc(mDataUri);
- }
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- super.onCreateOptionsMenu(menu);
- getSupportMenuInflater().inflate(R.menu.key_view, menu);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.menu_key_view_update:
- updateFromKeyserver(mDataUri);
- return true;
- case R.id.menu_key_view_sign:
- signKey(mDataUri);
- return true;
- case R.id.menu_key_view_export_keyserver:
- uploadToKeyserver(mDataUri);
- return true;
- case R.id.menu_key_view_export_file:
- mExportHelper.showExportKeysDialog(mDataUri, Id.type.public_key, Constants.path.APP_DIR
- + "/pubexport.asc");
- return true;
- case R.id.menu_key_view_share_default:
- shareKey(mDataUri);
- return true;
- case R.id.menu_key_view_share_qr_code:
- shareKeyQrCode(mDataUri);
- return true;
- case R.id.menu_key_view_share_nfc:
- shareNfc();
- return true;
- case R.id.menu_key_view_share_clipboard:
- copyToClipboard(mDataUri);
- return true;
- case R.id.menu_key_view_delete: {
- // Message is received after key is deleted
- Handler returnHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) {
- setResult(RESULT_CANCELED);
- finish();
- }
- }
- };
-
- mExportHelper.deleteKey(mDataUri, Id.type.public_key, returnHandler);
- return true;
- }
- }
- return super.onOptionsItemSelected(item);
- }
-
- private void loadData(Uri dataUri) {
- PGPPublicKeyRing ring = (PGPPublicKeyRing) ProviderHelper.getPGPKeyRing(this, dataUri);
- mPublicKey = ring.getPublicKey();
-
- mFingerint.setText(PgpKeyHelper.shortifyFingerprint(PgpKeyHelper
- .convertFingerprintToHex(mPublicKey.getFingerprint())));
- String[] mainUserId = splitUserId("");
-
- Date expiryDate = PgpKeyHelper.getExpiryDate(mPublicKey);
- if (expiryDate == null) {
- mExpiry.setText("");
- } else {
- mExpiry.setText(DateFormat.getDateFormat(getApplicationContext()).format(expiryDate));
- }
-
- mCreation.setText(DateFormat.getDateFormat(getApplicationContext()).format(
- PgpKeyHelper.getCreationDate(mPublicKey)));
- mAlgorithm.setText(PgpKeyHelper.getAlgorithmInfo(mPublicKey));
-
- mActionEncrypt.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- long[] encryptionKeyIds = new long[] { mPublicKey.getKeyID() };
- Intent intent = new Intent(KeyViewActivity.this, EncryptActivity.class);
- intent.setAction(EncryptActivity.ACTION_ENCRYPT);
- intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, encryptionKeyIds);
- // used instead of startActivity set actionbar based on callingPackage
- startActivityForResult(intent, 0);
- }
- });
- }
-
- /**
- * TODO: does this duplicate functionality from elsewhere? put in helper!
- */
- private String[] splitUserId(String userId) {
- String[] result = new String[] { "", "", "" };
- Log.v("UserID", userId);
-
- Pattern withComment = Pattern.compile("^(.*) [(](.*)[)] <(.*)>$");
- Matcher matcher = withComment.matcher(userId);
- if (matcher.matches()) {
- result[0] = matcher.group(1);
- result[1] = matcher.group(2);
- result[2] = matcher.group(3);
- return result;
- }
-
- Pattern withoutComment = Pattern.compile("^(.*) <(.*)>$");
- matcher = withoutComment.matcher(userId);
- if (matcher.matches()) {
- result[0] = matcher.group(1);
- result[1] = matcher.group(2);
- return result;
- }
- return result;
- }
-
- private void uploadToKeyserver(Uri dataUri) {
- long keyRingRowId = Long.valueOf(dataUri.getLastPathSegment());
-
- Intent uploadIntent = new Intent(this, KeyServerUploadActivity.class);
- uploadIntent.setAction(KeyServerUploadActivity.ACTION_EXPORT_KEY_TO_SERVER);
- uploadIntent.putExtra(KeyServerUploadActivity.EXTRA_KEYRING_ROW_ID, (int) keyRingRowId);
- startActivityForResult(uploadIntent, Id.request.export_to_server);
- }
-
- private void updateFromKeyserver(Uri dataUri) {
- long updateKeyId = 0;
- PGPPublicKeyRing updateRing = (PGPPublicKeyRing) ProviderHelper
- .getPGPKeyRing(this, dataUri);
-
- if (updateRing != null) {
- updateKeyId = PgpKeyHelper.getMasterKey(updateRing).getKeyID();
- }
- if (updateKeyId == 0) {
- Log.e(Constants.TAG, "this shouldn't happen. KeyId == 0!");
- return;
- }
-
- Intent signIntent = new Intent(this, SignKeyActivity.class);
- signIntent.putExtra(SignKeyActivity.EXTRA_KEY_ID, updateKeyId);
- startActivity(signIntent);
-
- Intent queryIntent = new Intent(this, KeyServerQueryActivity.class);
- queryIntent.setAction(KeyServerQueryActivity.ACTION_LOOK_UP_KEY_ID_AND_RETURN);
- queryIntent.putExtra(KeyServerQueryActivity.EXTRA_KEY_ID, updateKeyId);
-
- // TODO: lookup??
- startActivityForResult(queryIntent, Id.request.look_up_key_id);
- }
-
- private void signKey(Uri dataUri) {
- long keyId = 0;
- PGPPublicKeyRing signKey = (PGPPublicKeyRing) ProviderHelper.getPGPKeyRing(this, dataUri);
-
- if (signKey != null) {
- keyId = PgpKeyHelper.getMasterKey(signKey).getKeyID();
- }
- if (keyId == 0) {
- Log.e(Constants.TAG, "this shouldn't happen. KeyId == 0!");
- return;
- }
-
- Intent signIntent = new Intent(this, SignKeyActivity.class);
- signIntent.putExtra(SignKeyActivity.EXTRA_KEY_ID, keyId);
- startActivity(signIntent);
- }
-
- private void shareKey(Uri dataUri) {
- // get public keyring as ascii armored string
- long masterKeyId = ProviderHelper.getMasterKeyId(this, dataUri);
- ArrayList keyringArmored = ProviderHelper.getKeyRingsAsArmoredString(this, dataUri,
- new long[] { masterKeyId });
-
- // let user choose application
- Intent sendIntent = new Intent(Intent.ACTION_SEND);
- sendIntent.putExtra(Intent.EXTRA_TEXT, keyringArmored.get(0));
- sendIntent.setType("text/plain");
- startActivity(Intent.createChooser(sendIntent,
- getResources().getText(R.string.action_share_key_with)));
- }
-
- private void shareKeyQrCode(Uri dataUri) {
- // get public keyring as ascii armored string
- long masterKeyId = ProviderHelper.getMasterKeyId(this, dataUri);
- ArrayList keyringArmored = ProviderHelper.getKeyRingsAsArmoredString(this, dataUri,
- new long[] { masterKeyId });
-
- ShareQrCodeDialogFragment dialog = ShareQrCodeDialogFragment.newInstance(keyringArmored
- .get(0));
- dialog.show(getSupportFragmentManager(), "shareQrCodeDialog");
- }
-
- private void copyToClipboard(Uri dataUri) {
- // get public keyring as ascii armored string
- long masterKeyId = ProviderHelper.getMasterKeyId(this, dataUri);
- ArrayList keyringArmored = ProviderHelper.getKeyRingsAsArmoredString(this, dataUri,
- new long[] { masterKeyId });
-
- ClipboardReflection.copyToClipboard(this, keyringArmored.get(0));
- }
-
- private void shareNfc() {
- ShareNfcDialogFragment dialog = ShareNfcDialogFragment.newInstance();
- dialog.show(getSupportFragmentManager(), "shareNfcDialog");
- }
-
- /**
- * NFC: Initialize NFC sharing if OS and device supports it
- */
- private void initNfc(Uri dataUri) {
- // check if NFC Beam is supported (>= Android 4.1)
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
- // Check for available NFC Adapter
- mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
- if (mNfcAdapter != null) {
- // init nfc
-
- // get public keyring as byte array
- long masterKeyId = ProviderHelper.getMasterKeyId(this, dataUri);
- mSharedKeyringBytes = ProviderHelper.getKeyRingsAsByteArray(this, dataUri,
- new long[] { masterKeyId });
-
- // Register callback to set NDEF message
- mNfcAdapter.setNdefPushMessageCallback(this, this);
- // Register callback to listen for message-sent success
- mNfcAdapter.setOnNdefPushCompleteCallback(this, this);
- }
- }
- }
-
- /**
- * NFC: Implementation for the CreateNdefMessageCallback interface
- */
- @Override
- public NdefMessage createNdefMessage(NfcEvent event) {
- /**
- * When a device receives a push with an AAR in it, the application specified in the AAR is
- * guaranteed to run. The AAR overrides the tag dispatch system. You can add it back in to
- * guarantee that this activity starts when receiving a beamed message. For now, this code
- * uses the tag dispatch system.
- */
- NdefMessage msg = new NdefMessage(NdefRecord.createMime(Constants.NFC_MIME,
- mSharedKeyringBytes), NdefRecord.createApplicationRecord(Constants.PACKAGE_NAME));
- return msg;
- }
-
- /**
- * NFC: Implementation for the OnNdefPushCompleteCallback interface
- */
- @Override
- public void onNdefPushComplete(NfcEvent arg0) {
- // A handler is needed to send messages to the activity when this
- // callback occurs, because it happens from a binder thread
- mNfcHandler.obtainMessage(NFC_SENT).sendToTarget();
- }
-
- /**
- * NFC: This handler receives a message from onNdefPushComplete
- */
- private final Handler mNfcHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case NFC_SENT:
- Toast.makeText(getApplicationContext(), R.string.nfc_successfull, Toast.LENGTH_LONG)
- .show();
- break;
- }
- }
- };
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (!mExportHelper.handleActivityResult(requestCode, resultCode, data)) {
- super.onActivityResult(requestCode, resultCode, data);
- }
- }
-
-}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SignKeyActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SignKeyActivity.java
index c2fe1315b..6abdddee6 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SignKeyActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SignKeyActivity.java
@@ -68,7 +68,7 @@ public class SignKeyActivity extends SherlockFragmentActivity {
super.onCreate(savedInstanceState);
// check we havent already signed it
- setContentView(R.layout.sign_key_layout);
+ setContentView(R.layout.sign_key_activity);
final ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayShowTitleEnabled(true);
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
new file mode 100644
index 000000000..5bc30ccc5
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2013 Dominik Schürmann
+ * Copyright (C) 2013 Bahtiar 'kalkin' Gadimov
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.Id;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
+import org.sufficientlysecure.keychain.helper.ExportHelper;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
+import org.sufficientlysecure.keychain.ui.dialog.ShareNfcDialogFragment;
+import org.sufficientlysecure.keychain.ui.dialog.ShareQrCodeDialogFragment;
+import org.sufficientlysecure.keychain.util.Log;
+
+import android.annotation.SuppressLint;
+import android.content.Intent;
+import android.net.Uri;
+import android.nfc.NdefMessage;
+import android.nfc.NdefRecord;
+import android.nfc.NfcAdapter;
+import android.nfc.NfcAdapter.CreateNdefMessageCallback;
+import android.nfc.NfcAdapter.OnNdefPushCompleteCallback;
+import android.nfc.NfcEvent;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.text.format.DateFormat;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.actionbarsherlock.app.SherlockFragmentActivity;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
+import com.beardedhen.androidbootstrap.BootstrapButton;
+
+@SuppressLint("NewApi")
+public class ViewKeyActivity extends SherlockFragmentActivity implements CreateNdefMessageCallback,
+ OnNdefPushCompleteCallback {
+
+ ExportHelper mExportHelper;
+
+ private Uri mDataUri;
+
+ private PGPPublicKey mPublicKey;
+
+ private TextView mAlgorithm;
+ private TextView mFingerint;
+ private TextView mExpiry;
+ private TextView mCreation;
+ private BootstrapButton mActionEncrypt;
+
+ private ListView mUserIds;
+ private ListView mKeys;
+
+ // NFC
+ private NfcAdapter mNfcAdapter;
+ private byte[] mSharedKeyringBytes;
+ private static final int NFC_SENT = 1;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mExportHelper = new ExportHelper(this);
+
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setHomeButtonEnabled(true);
+
+ setContentView(R.layout.view_key_activity);
+
+ mFingerint = (TextView) findViewById(R.id.fingerprint);
+ mExpiry = (TextView) findViewById(R.id.expiry);
+ mCreation = (TextView) findViewById(R.id.creation);
+ mAlgorithm = (TextView) findViewById(R.id.algorithm);
+ mActionEncrypt = (BootstrapButton) findViewById(R.id.action_encrypt);
+ mUserIds = (ListView) findViewById(R.id.user_ids);
+ mKeys = (ListView) findViewById(R.id.keys);
+
+ Intent intent = getIntent();
+ mDataUri = intent.getData();
+ if (mDataUri == null) {
+ Log.e(Constants.TAG, "Intent data missing. Should be Uri of key!");
+ finish();
+ return;
+ } else {
+ Log.d(Constants.TAG, "uri: " + mDataUri);
+ loadData(mDataUri);
+ initNfc(mDataUri);
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ getSupportMenuInflater().inflate(R.menu.key_view, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_key_view_update:
+ updateFromKeyserver(mDataUri);
+ return true;
+ case R.id.menu_key_view_sign:
+ signKey(mDataUri);
+ return true;
+ case R.id.menu_key_view_export_keyserver:
+ uploadToKeyserver(mDataUri);
+ return true;
+ case R.id.menu_key_view_export_file:
+ mExportHelper.showExportKeysDialog(mDataUri, Id.type.public_key, Constants.path.APP_DIR
+ + "/pubexport.asc");
+ return true;
+ case R.id.menu_key_view_share_default:
+ shareKey(mDataUri);
+ return true;
+ case R.id.menu_key_view_share_qr_code:
+ shareKeyQrCode(mDataUri);
+ return true;
+ case R.id.menu_key_view_share_nfc:
+ shareNfc();
+ return true;
+ case R.id.menu_key_view_share_clipboard:
+ copyToClipboard(mDataUri);
+ return true;
+ case R.id.menu_key_view_delete: {
+ // Message is received after key is deleted
+ Handler returnHandler = new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) {
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+ }
+ };
+
+ mExportHelper.deleteKey(mDataUri, Id.type.public_key, returnHandler);
+ return true;
+ }
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void loadData(Uri dataUri) {
+ PGPPublicKeyRing ring = (PGPPublicKeyRing) ProviderHelper.getPGPKeyRing(this, dataUri);
+ mPublicKey = ring.getPublicKey();
+
+ mFingerint.setText(PgpKeyHelper.shortifyFingerprint(PgpKeyHelper
+ .convertFingerprintToHex(mPublicKey.getFingerprint())));
+ String[] mainUserId = splitUserId("");
+
+ Date expiryDate = PgpKeyHelper.getExpiryDate(mPublicKey);
+ if (expiryDate == null) {
+ mExpiry.setText("");
+ } else {
+ mExpiry.setText(DateFormat.getDateFormat(getApplicationContext()).format(expiryDate));
+ }
+
+ mCreation.setText(DateFormat.getDateFormat(getApplicationContext()).format(
+ PgpKeyHelper.getCreationDate(mPublicKey)));
+ mAlgorithm.setText(PgpKeyHelper.getAlgorithmInfo(mPublicKey));
+
+ mActionEncrypt.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ long[] encryptionKeyIds = new long[] { mPublicKey.getKeyID() };
+ Intent intent = new Intent(ViewKeyActivity.this, EncryptActivity.class);
+ intent.setAction(EncryptActivity.ACTION_ENCRYPT);
+ intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, encryptionKeyIds);
+ // used instead of startActivity set actionbar based on callingPackage
+ startActivityForResult(intent, 0);
+ }
+ });
+ }
+
+ /**
+ * TODO: does this duplicate functionality from elsewhere? put in helper!
+ */
+ private String[] splitUserId(String userId) {
+ String[] result = new String[] { "", "", "" };
+ Log.v("UserID", userId);
+
+ Pattern withComment = Pattern.compile("^(.*) [(](.*)[)] <(.*)>$");
+ Matcher matcher = withComment.matcher(userId);
+ if (matcher.matches()) {
+ result[0] = matcher.group(1);
+ result[1] = matcher.group(2);
+ result[2] = matcher.group(3);
+ return result;
+ }
+
+ Pattern withoutComment = Pattern.compile("^(.*) <(.*)>$");
+ matcher = withoutComment.matcher(userId);
+ if (matcher.matches()) {
+ result[0] = matcher.group(1);
+ result[1] = matcher.group(2);
+ return result;
+ }
+ return result;
+ }
+
+ private void uploadToKeyserver(Uri dataUri) {
+ long keyRingRowId = Long.valueOf(dataUri.getLastPathSegment());
+
+ Intent uploadIntent = new Intent(this, KeyServerUploadActivity.class);
+ uploadIntent.setAction(KeyServerUploadActivity.ACTION_EXPORT_KEY_TO_SERVER);
+ uploadIntent.putExtra(KeyServerUploadActivity.EXTRA_KEYRING_ROW_ID, (int) keyRingRowId);
+ startActivityForResult(uploadIntent, Id.request.export_to_server);
+ }
+
+ private void updateFromKeyserver(Uri dataUri) {
+ long updateKeyId = 0;
+ PGPPublicKeyRing updateRing = (PGPPublicKeyRing) ProviderHelper
+ .getPGPKeyRing(this, dataUri);
+
+ if (updateRing != null) {
+ updateKeyId = PgpKeyHelper.getMasterKey(updateRing).getKeyID();
+ }
+ if (updateKeyId == 0) {
+ Log.e(Constants.TAG, "this shouldn't happen. KeyId == 0!");
+ return;
+ }
+
+ Intent signIntent = new Intent(this, SignKeyActivity.class);
+ signIntent.putExtra(SignKeyActivity.EXTRA_KEY_ID, updateKeyId);
+ startActivity(signIntent);
+
+ Intent queryIntent = new Intent(this, KeyServerQueryActivity.class);
+ queryIntent.setAction(KeyServerQueryActivity.ACTION_LOOK_UP_KEY_ID_AND_RETURN);
+ queryIntent.putExtra(KeyServerQueryActivity.EXTRA_KEY_ID, updateKeyId);
+
+ // TODO: lookup??
+ startActivityForResult(queryIntent, Id.request.look_up_key_id);
+ }
+
+ private void signKey(Uri dataUri) {
+ long keyId = 0;
+ PGPPublicKeyRing signKey = (PGPPublicKeyRing) ProviderHelper.getPGPKeyRing(this, dataUri);
+
+ if (signKey != null) {
+ keyId = PgpKeyHelper.getMasterKey(signKey).getKeyID();
+ }
+ if (keyId == 0) {
+ Log.e(Constants.TAG, "this shouldn't happen. KeyId == 0!");
+ return;
+ }
+
+ Intent signIntent = new Intent(this, SignKeyActivity.class);
+ signIntent.putExtra(SignKeyActivity.EXTRA_KEY_ID, keyId);
+ startActivity(signIntent);
+ }
+
+ private void shareKey(Uri dataUri) {
+ // get public keyring as ascii armored string
+ long masterKeyId = ProviderHelper.getMasterKeyId(this, dataUri);
+ ArrayList keyringArmored = ProviderHelper.getKeyRingsAsArmoredString(this, dataUri,
+ new long[] { masterKeyId });
+
+ // let user choose application
+ Intent sendIntent = new Intent(Intent.ACTION_SEND);
+ sendIntent.putExtra(Intent.EXTRA_TEXT, keyringArmored.get(0));
+ sendIntent.setType("text/plain");
+ startActivity(Intent.createChooser(sendIntent,
+ getResources().getText(R.string.action_share_key_with)));
+ }
+
+ private void shareKeyQrCode(Uri dataUri) {
+ // get public keyring as ascii armored string
+ long masterKeyId = ProviderHelper.getMasterKeyId(this, dataUri);
+ ArrayList keyringArmored = ProviderHelper.getKeyRingsAsArmoredString(this, dataUri,
+ new long[] { masterKeyId });
+
+ ShareQrCodeDialogFragment dialog = ShareQrCodeDialogFragment.newInstance(keyringArmored
+ .get(0));
+ dialog.show(getSupportFragmentManager(), "shareQrCodeDialog");
+ }
+
+ private void copyToClipboard(Uri dataUri) {
+ // get public keyring as ascii armored string
+ long masterKeyId = ProviderHelper.getMasterKeyId(this, dataUri);
+ ArrayList keyringArmored = ProviderHelper.getKeyRingsAsArmoredString(this, dataUri,
+ new long[] { masterKeyId });
+
+ ClipboardReflection.copyToClipboard(this, keyringArmored.get(0));
+ }
+
+ private void shareNfc() {
+ ShareNfcDialogFragment dialog = ShareNfcDialogFragment.newInstance();
+ dialog.show(getSupportFragmentManager(), "shareNfcDialog");
+ }
+
+ /**
+ * NFC: Initialize NFC sharing if OS and device supports it
+ */
+ private void initNfc(Uri dataUri) {
+ // check if NFC Beam is supported (>= Android 4.1)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ // Check for available NFC Adapter
+ mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
+ if (mNfcAdapter != null) {
+ // init nfc
+
+ // get public keyring as byte array
+ long masterKeyId = ProviderHelper.getMasterKeyId(this, dataUri);
+ mSharedKeyringBytes = ProviderHelper.getKeyRingsAsByteArray(this, dataUri,
+ new long[] { masterKeyId });
+
+ // Register callback to set NDEF message
+ mNfcAdapter.setNdefPushMessageCallback(this, this);
+ // Register callback to listen for message-sent success
+ mNfcAdapter.setOnNdefPushCompleteCallback(this, this);
+ }
+ }
+ }
+
+ /**
+ * NFC: Implementation for the CreateNdefMessageCallback interface
+ */
+ @Override
+ public NdefMessage createNdefMessage(NfcEvent event) {
+ /**
+ * When a device receives a push with an AAR in it, the application specified in the AAR is
+ * guaranteed to run. The AAR overrides the tag dispatch system. You can add it back in to
+ * guarantee that this activity starts when receiving a beamed message. For now, this code
+ * uses the tag dispatch system.
+ */
+ NdefMessage msg = new NdefMessage(NdefRecord.createMime(Constants.NFC_MIME,
+ mSharedKeyringBytes), NdefRecord.createApplicationRecord(Constants.PACKAGE_NAME));
+ return msg;
+ }
+
+ /**
+ * NFC: Implementation for the OnNdefPushCompleteCallback interface
+ */
+ @Override
+ public void onNdefPushComplete(NfcEvent arg0) {
+ // A handler is needed to send messages to the activity when this
+ // callback occurs, because it happens from a binder thread
+ mNfcHandler.obtainMessage(NFC_SENT).sendToTarget();
+ }
+
+ /**
+ * NFC: This handler receives a message from onNdefPushComplete
+ */
+ private final Handler mNfcHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case NFC_SENT:
+ Toast.makeText(getApplicationContext(), R.string.nfc_successfull, Toast.LENGTH_LONG)
+ .show();
+ break;
+ }
+ }
+ };
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (!mExportHelper.handleActivityResult(requestCode, resultCode, data)) {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java
index 5f71c9805..140781b4f 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java
@@ -105,7 +105,7 @@ public class KeyListPublicAdapter extends CursorAdapter implements StickyListHea
HeaderViewHolder holder;
if (convertView == null) {
holder = new HeaderViewHolder();
- convertView = mInflater.inflate(R.layout.stickylist_header, parent, false);
+ convertView = mInflater.inflate(R.layout.key_list_public_header, parent, false);
holder.text = (TextView) convertView.findViewById(R.id.stickylist_header_text);
convertView.setTag(holder);
} else {
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java
new file mode 100644
index 000000000..55821b11e
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2013 Dominik Schürmann
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package org.sufficientlysecure.keychain.ui.adapter;
+
+import java.util.HashMap;
+import java.util.Set;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.OtherHelper;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.database.Cursor;
+import android.graphics.Color;
+import android.support.v4.widget.CursorAdapter;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+public class ViewKeyUserIdsAdapter extends CursorAdapter {
+ private LayoutInflater mInflater;
+
+ public ViewKeyUserIdsAdapter(Context context, Cursor c, int flags) {
+ super(context, c, flags);
+
+ mInflater = LayoutInflater.from(context);
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ int userIdIndex = cursor.getColumnIndex(UserIds.USER_ID);
+
+ TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
+ mainUserId.setText(R.string.unknown_user_id);
+ TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
+ mainUserIdRest.setText("");
+
+ String userId = cursor.getString(userIdIndex);
+ if (userId != null) {
+ String[] userIdSplit = OtherHelper.splitUserId(userId);
+
+ if (userIdSplit[1] != null) {
+ mainUserIdRest.setText(userIdSplit[1]);
+ }
+ mainUserId.setText(userIdSplit[0]);
+ }
+
+ if (mainUserId.getText().length() == 0) {
+ mainUserId.setText(R.string.unknown_user_id);
+ }
+
+ if (mainUserIdRest.getText().length() == 0) {
+ mainUserIdRest.setVisibility(View.GONE);
+ } else {
+ mainUserIdRest.setVisibility(View.VISIBLE);
+ }
+ }
+
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return mInflater.inflate(R.layout.key_list_item, null);
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java
index aba7e974e..e88271240 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java
@@ -141,7 +141,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
}
LayoutInflater inflater = activity.getLayoutInflater();
- View view = inflater.inflate(R.layout.passphrase, null);
+ View view = inflater.inflate(R.layout.passphrase_dialog, null);
alert.setView(view);
mPassphraseEditText = (EditText) view.findViewById(R.id.passphrase_passphrase);
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java
index 797b28829..79d577c58 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java
@@ -90,7 +90,7 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
alert.setMessage(R.string.enter_passphrase_twice);
LayoutInflater inflater = activity.getLayoutInflater();
- View view = inflater.inflate(R.layout.passphrase_repeat, null);
+ View view = inflater.inflate(R.layout.passphrase_repeat_dialogf, null);
alert.setView(view);
mPassphraseEditText = (EditText) view.findViewById(R.id.passphrase_passphrase);
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 91e3831b7..b33dbe4c5 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SectionView.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SectionView.java
@@ -153,7 +153,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
case Id.type.key: {
AlertDialog.Builder dialog = new AlertDialog.Builder(getContext());
- View view = mInflater.inflate(R.layout.create_key, null);
+ View view = mInflater.inflate(R.layout.create_key_dialog, null);
dialog.setView(view);
dialog.setTitle(R.string.title_create_key);
--
cgit v1.2.3
From c740d409c4f8977da22738f98f16f6b3f624ea73 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Sun, 12 Jan 2014 22:26:55 +0100
Subject: fix compile
---
OpenPGP-Keychain/AndroidManifest.xml | 2 +-
.../keychain/ui/adapter/KeyListAdapterOLD.java | 40 +++++++++++++---------
.../ui/dialog/SetPassphraseDialogFragment.java | 2 +-
3 files changed, 25 insertions(+), 19 deletions(-)
diff --git a/OpenPGP-Keychain/AndroidManifest.xml b/OpenPGP-Keychain/AndroidManifest.xml
index 3b2e423df..9e4f3ca49 100644
--- a/OpenPGP-Keychain/AndroidManifest.xml
+++ b/OpenPGP-Keychain/AndroidManifest.xml
@@ -115,7 +115,7 @@
android:label="@string/title_edit_key"
android:windowSoftInputMode="stateHidden" />
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListAdapterOLD.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListAdapterOLD.java
index 0c709d25a..8f745bdd3 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListAdapterOLD.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListAdapterOLD.java
@@ -18,12 +18,12 @@ package org.sufficientlysecure.keychain.ui.adapter;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
+import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.OtherHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.util.Log;
-import org.sufficientlysecure.keychain.R;
import android.content.Context;
import android.database.Cursor;
@@ -36,7 +36,6 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorTreeAdapter;
import android.widget.ImageView;
-import android.widget.LinearLayout;
import android.widget.TextView;
public class KeyListAdapterOLD extends CursorTreeAdapter {
@@ -97,27 +96,27 @@ public class KeyListAdapterOLD extends CursorTreeAdapter {
}
}
- /**
- * Inflate new view for child items
- */
- @Override
- public View newChildView(Context context, Cursor cursor, boolean isLastChild, ViewGroup parent) {
- return mInflater.inflate(R.layout.key_list_child_item, null);
- }
+// /**
+// * Inflate new view for child items
+// */
+// @Override
+// public View newChildView(Context context, Cursor cursor, boolean isLastChild, ViewGroup parent) {
+// return mInflater.inflate(R.layout.key_list_child_item, null);
+// }
/**
* Bind TextViews from view of childs based on query results
*/
@Override
protected void bindChildView(View view, Context context, Cursor cursor, boolean isLastChild) {
- LinearLayout keyLayout = (LinearLayout) view.findViewById(R.id.keyLayout);
- LinearLayout userIdLayout = (LinearLayout) view.findViewById(R.id.userIdLayout);
+// LinearLayout keyLayout = (LinearLayout) view.findViewById(R.id.keyLayout);
+// LinearLayout userIdLayout = (LinearLayout) view.findViewById(R.id.userIdLayout);
// first entry is fingerprint
if (cursor.getPosition() == 0) {
// show only userId layout
- keyLayout.setVisibility(View.GONE);
- userIdLayout.setVisibility(View.VISIBLE);
+// keyLayout.setVisibility(View.GONE);
+// userIdLayout.setVisibility(View.VISIBLE);
String fingerprint = PgpKeyHelper.getFingerPrint(context,
cursor.getLong(cursor.getColumnIndex(Keys.KEY_ID)));
@@ -131,8 +130,8 @@ public class KeyListAdapterOLD extends CursorTreeAdapter {
} else {
// differentiate between keys and userIds in MergeCursor
if (cursor.getColumnIndex(Keys.KEY_ID) != -1) {
- keyLayout.setVisibility(View.VISIBLE);
- userIdLayout.setVisibility(View.GONE);
+// keyLayout.setVisibility(View.VISIBLE);
+// userIdLayout.setVisibility(View.GONE);
String keyIdStr = PgpKeyHelper.convertKeyIdToHex(cursor.getLong(cursor
.getColumnIndex(Keys.KEY_ID)));
@@ -174,8 +173,8 @@ public class KeyListAdapterOLD extends CursorTreeAdapter {
signIcon.setVisibility(View.VISIBLE);
}
} else {
- keyLayout.setVisibility(View.GONE);
- userIdLayout.setVisibility(View.VISIBLE);
+// keyLayout.setVisibility(View.GONE);
+// userIdLayout.setVisibility(View.VISIBLE);
String userIdStr = cursor.getString(cursor.getColumnIndex(UserIds.USER_ID));
@@ -270,4 +269,11 @@ public class KeyListAdapterOLD extends CursorTreeAdapter {
return mContext.getContentResolver().query(uri, projection, selection, null, sortOrder);
}
+ @Override
+ protected View newChildView(Context context, Cursor cursor, boolean isLastChild,
+ ViewGroup parent) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java
index 79d577c58..d5c366bed 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java
@@ -90,7 +90,7 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
alert.setMessage(R.string.enter_passphrase_twice);
LayoutInflater inflater = activity.getLayoutInflater();
- View view = inflater.inflate(R.layout.passphrase_repeat_dialogf, null);
+ View view = inflater.inflate(R.layout.passphrase_repeat_dialog, null);
alert.setView(view);
mPassphraseEditText = (EditText) view.findViewById(R.id.passphrase_passphrase);
--
cgit v1.2.3
From face67d64b06f14913fff9ce61ae4a23421cec28 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Mon, 13 Jan 2014 00:39:51 +0100
Subject: key view is working
---
OpenPGP-Keychain/res/layout/select_key_item.xml | 20 +-
.../res/layout/share_qr_code_dialog.xml | 16 -
OpenPGP-Keychain/res/layout/view_key_activity.xml | 343 +++++++++++++--------
.../res/layout/view_key_userids_item.xml | 1 -
OpenPGP-Keychain/res/values/strings.xml | 2 +
.../keychain/helper/OtherHelper.java | 19 --
.../keychain/pgp/PgpKeyHelper.java | 30 ++
.../keychain/provider/KeychainContract.java | 20 +-
.../keychain/ui/KeyListSecretFragment.java | 9 +-
.../keychain/ui/ViewKeyActivity.java | 183 +++++++++--
.../keychain/ui/adapter/KeyListAdapterOLD.java | 279 -----------------
.../keychain/ui/adapter/KeyListPublicAdapter.java | 6 +-
.../keychain/ui/adapter/KeyListSecretAdapter.java | 4 +-
.../ui/adapter/SelectKeyCursorAdapter.java | 5 +-
.../keychain/ui/adapter/ViewKeyKeysAdapter.java | 90 ++++++
.../keychain/ui/adapter/ViewKeyUserIdsAdapter.java | 36 +--
.../keychain/ui/widget/FixedListView.java | 55 ++++
17 files changed, 577 insertions(+), 541 deletions(-)
delete mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListAdapterOLD.java
create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java
create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/FixedListView.java
diff --git a/OpenPGP-Keychain/res/layout/select_key_item.xml b/OpenPGP-Keychain/res/layout/select_key_item.xml
index d5b1655ed..bbfe17c44 100644
--- a/OpenPGP-Keychain/res/layout/select_key_item.xml
+++ b/OpenPGP-Keychain/res/layout/select_key_item.xml
@@ -1,22 +1,6 @@
-
-
diff --git a/OpenPGP-Keychain/res/layout/share_qr_code_dialog.xml b/OpenPGP-Keychain/res/layout/share_qr_code_dialog.xml
index 88b06b698..66102d3e5 100644
--- a/OpenPGP-Keychain/res/layout/share_qr_code_dialog.xml
+++ b/OpenPGP-Keychain/res/layout/share_qr_code_dialog.xml
@@ -1,20 +1,4 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
-
-
-
-
-
-
-
\ No newline at end of file
+ android:layout_height="wrap_content"
+ android:layout_marginRight="?android:attr/scrollbarSize"
+ android:orientation="vertical"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp" >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/view_key_userids_item.xml b/OpenPGP-Keychain/res/layout/view_key_userids_item.xml
index cbc85189f..2d022ba13 100644
--- a/OpenPGP-Keychain/res/layout/view_key_userids_item.xml
+++ b/OpenPGP-Keychain/res/layout/view_key_userids_item.xml
@@ -4,7 +4,6 @@
android:layout_height="?android:attr/listPreferredItemHeight"
android:layout_marginRight="?android:attr/scrollbarSize"
android:orientation="vertical"
- android:paddingLeft="36dip"
android:singleLine="true" >
DefaultsAdvancedMaster Key
+ Master User IDActions
@@ -137,6 +138,7 @@
CommentEmailSend Key to Server?
+ FingerprintSelect1 SelectedSelected
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/OtherHelper.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/OtherHelper.java
index 8fa2df1f5..9f3cd8e88 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/OtherHelper.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/OtherHelper.java
@@ -79,23 +79,4 @@ public class OtherHelper {
}
}
- /**
- * Splits userId string into naming part and email part
- *
- * @param userId
- * @return array with naming (0) and email (1)
- */
- public static String[] splitUserId(String userId) {
- String[] output = new String[2];
-
- String chunks[] = userId.split(" <", 2);
- userId = chunks[0];
- if (chunks.length > 1) {
- output[1] = "<" + chunks[1];
- }
- output[0] = userId;
-
- return output;
- }
-
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
index daba94b99..edb30496a 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
@@ -22,6 +22,8 @@ import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.Vector;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.openpgp.PGPPublicKey;
@@ -524,4 +526,32 @@ public class PgpKeyHelper {
return (Long.parseLong(s1, 16) << 32) | Long.parseLong(s2, 16);
}
+ /**
+ * Splits userId string into naming part, email part, and comment part
+ *
+ * @param userId
+ * @return array with naming (0), email (1), comment (2)
+ */
+ public static String[] splitUserId(String userId) {
+ String[] result = new String[] { "", "", "" };
+
+ Pattern withComment = Pattern.compile("^(.*) \\((.*)\\) <(.*)>$");
+ Matcher matcher = withComment.matcher(userId);
+ if (matcher.matches()) {
+ result[0] = matcher.group(1);
+ result[1] = matcher.group(3);
+ result[2] = matcher.group(2);
+ return result;
+ }
+
+ Pattern withoutComment = Pattern.compile("^(.*) <(.*)>$");
+ matcher = withoutComment.matcher(userId);
+ if (matcher.matches()) {
+ result[0] = matcher.group(1);
+ result[1] = matcher.group(2);
+ return result;
+ }
+ return result;
+ }
+
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainContract.java
index 82bb473f6..d2381f6f0 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainContract.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainContract.java
@@ -69,8 +69,8 @@ public class KeychainContract {
public static final String CONTENT_AUTHORITY = Constants.PACKAGE_NAME + ".provider";
- private static final Uri BASE_CONTENT_URI_INTERNAL = Uri.parse("content://"
- + CONTENT_AUTHORITY);
+ private static final Uri BASE_CONTENT_URI_INTERNAL = Uri
+ .parse("content://" + CONTENT_AUTHORITY);
public static final String BASE_KEY_RINGS = "key_rings";
public static final String BASE_DATA = "data";
@@ -185,6 +185,14 @@ public class KeychainContract {
return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId)
.appendPath(PATH_KEYS).appendPath(keyRowId).build();
}
+
+ public static Uri buildKeysUri(Uri keyRingUri) {
+ return keyRingUri.buildUpon().appendPath(PATH_KEYS).build();
+ }
+
+ public static Uri buildKeysUri(Uri keyRingUri, String keyRowId) {
+ return keyRingUri.buildUpon().appendPath(PATH_KEYS).appendPath(keyRowId).build();
+ }
}
public static class UserIds implements UserIdsColumns, BaseColumns {
@@ -216,6 +224,14 @@ public class KeychainContract {
return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId)
.appendPath(PATH_USER_IDS).appendPath(userIdRowId).build();
}
+
+ public static Uri buildUserIdsUri(Uri keyRingUri) {
+ return keyRingUri.buildUpon().appendPath(PATH_USER_IDS).build();
+ }
+
+ public static Uri buildUserIdsUri(Uri keyRingUri, String userIdRowId) {
+ return keyRingUri.buildUpon().appendPath(PATH_USER_IDS).appendPath(userIdRowId).build();
+ }
}
public static class ApiApps implements ApiAppsColumns, BaseColumns {
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java
index c0b67719e..0e2d22e1e 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java
@@ -51,7 +51,6 @@ public class KeyListSecretFragment extends SherlockListFragment implements
LoaderManager.LoaderCallbacks, OnItemClickListener {
private KeyListSecretActivity mKeyListSecretActivity;
-
private KeyListSecretAdapter mAdapter;
/**
@@ -146,7 +145,7 @@ public class KeyListSecretFragment extends SherlockListFragment implements
setListShown(false);
// Create an empty adapter we will use to display the loaded data.
- mAdapter = new KeyListSecretAdapter(mKeyListSecretActivity, null, Id.type.secret_key);
+ mAdapter = new KeyListSecretAdapter(mKeyListSecretActivity, null, 0);
setListAdapter(mAdapter);
// Prepare the loader. Either re-connect with an existing one,
@@ -157,8 +156,7 @@ public class KeyListSecretFragment extends SherlockListFragment implements
// These are the rows that we will retrieve.
static final String[] PROJECTION = new String[] { KeyRings._ID, KeyRings.MASTER_KEY_ID,
UserIds.USER_ID };
-
- static final String SORT_ORDER = UserIds.USER_ID + " ASC";
+ static final String SORT_ORDER = UserIds.USER_ID + " COLLATE LOCALIZED ASC";
public Loader onCreateLoader(int id, Bundle args) {
// This is called when a new Loader needs to be created. This
@@ -169,8 +167,7 @@ public class KeyListSecretFragment extends SherlockListFragment implements
// Now create and return a CursorLoader that will take care of
// creating a Cursor for the data being displayed.
- return new CursorLoader(getActivity(), baseUri, PROJECTION, null, null, UserIds.USER_ID
- + " COLLATE LOCALIZED ASC");
+ return new CursorLoader(getActivity(), baseUri, PROJECTION, null, null, SORT_ORDER);
}
public void onLoadFinished(Loader loader, Cursor data) {
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
index 5bc30ccc5..e2f90e87c 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Dominik Schürmann
+ * Copyright (C) 2013-2014 Dominik Schürmann
* Copyright (C) 2013 Bahtiar 'kalkin' Gadimov
*
* This program is free software: you can redistribute it and/or modify
@@ -20,8 +20,6 @@ package org.sufficientlysecure.keychain.ui;
import java.util.ArrayList;
import java.util.Date;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing;
@@ -31,7 +29,12 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
import org.sufficientlysecure.keychain.helper.ExportHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
+import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.ui.adapter.ViewKeyKeysAdapter;
+import org.sufficientlysecure.keychain.ui.adapter.ViewKeyUserIdsAdapter;
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.ShareNfcDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.ShareQrCodeDialogFragment;
@@ -39,6 +42,7 @@ import org.sufficientlysecure.keychain.util.Log;
import android.annotation.SuppressLint;
import android.content.Intent;
+import android.database.Cursor;
import android.net.Uri;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
@@ -50,6 +54,9 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.content.CursorLoader;
+import android.support.v4.content.Loader;
import android.text.format.DateFormat;
import android.view.View;
import android.view.View.OnClickListener;
@@ -64,7 +71,7 @@ import com.beardedhen.androidbootstrap.BootstrapButton;
@SuppressLint("NewApi")
public class ViewKeyActivity extends SherlockFragmentActivity implements CreateNdefMessageCallback,
- OnNdefPushCompleteCallback {
+ OnNdefPushCompleteCallback, LoaderManager.LoaderCallbacks {
ExportHelper mExportHelper;
@@ -72,10 +79,14 @@ public class ViewKeyActivity extends SherlockFragmentActivity implements CreateN
private PGPPublicKey mPublicKey;
+ private TextView mName;
+ private TextView mEmail;
+ private TextView mComment;
private TextView mAlgorithm;
- private TextView mFingerint;
+ private TextView mKeyId;
private TextView mExpiry;
private TextView mCreation;
+ private TextView mFingerprint;
private BootstrapButton mActionEncrypt;
private ListView mUserIds;
@@ -86,6 +97,12 @@ public class ViewKeyActivity extends SherlockFragmentActivity implements CreateN
private byte[] mSharedKeyringBytes;
private static final int NFC_SENT = 1;
+ private static final int LOADER_ID_KEYRING = 0;
+ private static final int LOADER_ID_USER_IDS = 1;
+ private static final int LOADER_ID_KEYS = 2;
+ private ViewKeyUserIdsAdapter mUserIdsAdapter;
+ private ViewKeyKeysAdapter mKeysAdapter;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -93,14 +110,19 @@ public class ViewKeyActivity extends SherlockFragmentActivity implements CreateN
mExportHelper = new ExportHelper(this);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setIcon(android.R.color.transparent);
getSupportActionBar().setHomeButtonEnabled(true);
setContentView(R.layout.view_key_activity);
- mFingerint = (TextView) findViewById(R.id.fingerprint);
- mExpiry = (TextView) findViewById(R.id.expiry);
- mCreation = (TextView) findViewById(R.id.creation);
+ mName = (TextView) findViewById(R.id.name);
+ mEmail = (TextView) findViewById(R.id.email);
+ mComment = (TextView) findViewById(R.id.comment);
+ mKeyId = (TextView) findViewById(R.id.key_id);
mAlgorithm = (TextView) findViewById(R.id.algorithm);
+ mCreation = (TextView) findViewById(R.id.creation);
+ mExpiry = (TextView) findViewById(R.id.expiry);
+ mFingerprint = (TextView) findViewById(R.id.fingerprint);
mActionEncrypt = (BootstrapButton) findViewById(R.id.action_encrypt);
mUserIds = (ListView) findViewById(R.id.user_ids);
mKeys = (ListView) findViewById(R.id.keys);
@@ -128,6 +150,11 @@ public class ViewKeyActivity extends SherlockFragmentActivity implements CreateN
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
+ case android.R.id.home:
+ Intent homeIntent = new Intent(this, KeyListPublicActivity.class);
+ homeIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(homeIntent);
+ return true;
case R.id.menu_key_view_update:
updateFromKeyserver(mDataUri);
return true;
@@ -173,16 +200,23 @@ public class ViewKeyActivity extends SherlockFragmentActivity implements CreateN
}
private void loadData(Uri dataUri) {
+ // TODO: don't use pubkey object, use database!!!
+
PGPPublicKeyRing ring = (PGPPublicKeyRing) ProviderHelper.getPGPKeyRing(this, dataUri);
mPublicKey = ring.getPublicKey();
- mFingerint.setText(PgpKeyHelper.shortifyFingerprint(PgpKeyHelper
+ mKeyId.setText(PgpKeyHelper.shortifyFingerprint(PgpKeyHelper
.convertFingerprintToHex(mPublicKey.getFingerprint())));
- String[] mainUserId = splitUserId("");
+
+ String fingerprint = PgpKeyHelper.convertFingerprintToHex(mPublicKey.getFingerprint());
+ fingerprint = fingerprint.replace(" ", "\n");
+ mFingerprint.setText(fingerprint);
+
+ // TODO: get image with getUserAttributes() on key and then PGPUserAttributeSubpacketVector
Date expiryDate = PgpKeyHelper.getExpiryDate(mPublicKey);
if (expiryDate == null) {
- mExpiry.setText("");
+ mExpiry.setText(R.string.none);
} else {
mExpiry.setText(DateFormat.getDateFormat(getApplicationContext()).format(expiryDate));
}
@@ -203,32 +237,117 @@ public class ViewKeyActivity extends SherlockFragmentActivity implements CreateN
startActivityForResult(intent, 0);
}
});
+
+ mUserIdsAdapter = new ViewKeyUserIdsAdapter(this, null, 0);
+
+ mUserIds.setAdapter(mUserIdsAdapter);
+ // mUserIds.setEmptyView(findViewById(android.R.id.empty));
+ // mUserIds.setClickable(true);
+ // mUserIds.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ // @Override
+ // public void onItemClick(AdapterView> arg0, View arg1, int position, long id) {
+ // }
+ // });
+
+ mKeysAdapter = new ViewKeyKeysAdapter(this, null, 0);
+
+ mKeys.setAdapter(mKeysAdapter);
+
+ // Prepare the loader. Either re-connect with an existing one,
+ // or start a new one.
+ getSupportLoaderManager().initLoader(LOADER_ID_KEYRING, null, this);
+ getSupportLoaderManager().initLoader(LOADER_ID_USER_IDS, null, this);
+ getSupportLoaderManager().initLoader(LOADER_ID_KEYS, null, this);
}
- /**
- * TODO: does this duplicate functionality from elsewhere? put in helper!
- */
- private String[] splitUserId(String userId) {
- String[] result = new String[] { "", "", "" };
- Log.v("UserID", userId);
-
- Pattern withComment = Pattern.compile("^(.*) [(](.*)[)] <(.*)>$");
- Matcher matcher = withComment.matcher(userId);
- if (matcher.matches()) {
- result[0] = matcher.group(1);
- result[1] = matcher.group(2);
- result[2] = matcher.group(3);
- return result;
+ static final String[] KEYRING_PROJECTION = new String[] { KeyRings._ID, KeyRings.MASTER_KEY_ID,
+ UserIds.USER_ID };
+
+ static final String[] USER_IDS_PROJECTION = new String[] { UserIds._ID, UserIds.USER_ID,
+ UserIds.RANK, };
+ // not the main user id
+ static final String USER_IDS_SELECTION = UserIds.RANK + " > 0 ";
+ static final String USER_IDS_SORT_ORDER = UserIds.USER_ID + " COLLATE LOCALIZED ASC";
+
+ static final String[] KEYS_PROJECTION = new String[] { Keys._ID, Keys.KEY_ID,
+ Keys.IS_MASTER_KEY, Keys.ALGORITHM, Keys.KEY_SIZE, Keys.CAN_CERTIFY, Keys.CAN_SIGN,
+ Keys.CAN_ENCRYPT, };
+ static final String KEYS_SORT_ORDER = Keys.RANK + " ASC";
+
+ public Loader onCreateLoader(int id, Bundle args) {
+ switch (id) {
+ case LOADER_ID_KEYRING: {
+ Uri baseUri = mDataUri;
+
+ // Now create and return a CursorLoader that will take care of
+ // creating a Cursor for the data being displayed.
+ return new CursorLoader(this, baseUri, KEYRING_PROJECTION, null, null, null);
}
+ case LOADER_ID_USER_IDS: {
+ Uri baseUri = UserIds.buildUserIdsUri(mDataUri);
+
+ // Now create and return a CursorLoader that will take care of
+ // creating a Cursor for the data being displayed.
+ return new CursorLoader(this, baseUri, USER_IDS_PROJECTION, USER_IDS_SELECTION, null,
+ USER_IDS_SORT_ORDER);
+ }
+ case LOADER_ID_KEYS: {
+ Uri baseUri = Keys.buildKeysUri(mDataUri);
+
+ // Now create and return a CursorLoader that will take care of
+ // creating a Cursor for the data being displayed.
+ return new CursorLoader(this, baseUri, KEYS_PROJECTION, null, null, KEYS_SORT_ORDER);
+ }
+
+ default:
+ return null;
+ }
+ }
- Pattern withoutComment = Pattern.compile("^(.*) <(.*)>$");
- matcher = withoutComment.matcher(userId);
- if (matcher.matches()) {
- result[0] = matcher.group(1);
- result[1] = matcher.group(2);
- return result;
+ public void onLoadFinished(Loader loader, Cursor data) {
+ // Swap the new cursor in. (The framework will take care of closing the
+ // old cursor once we return.)
+ switch (loader.getId()) {
+ case LOADER_ID_KEYRING:
+ if (data.moveToFirst()) {
+ String[] mainUserId = PgpKeyHelper.splitUserId(data.getString(2));
+ setTitle(mainUserId[0]);
+ mName.setText(mainUserId[0]);
+ mEmail.setText(mainUserId[1]);
+ mComment.setText(mainUserId[2]);
+ }
+
+ break;
+ case LOADER_ID_USER_IDS:
+ mUserIdsAdapter.swapCursor(data);
+ break;
+ case LOADER_ID_KEYS:
+ mKeysAdapter.swapCursor(data);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /**
+ * This is called when the last Cursor provided to onLoadFinished() above is about to be closed.
+ * We need to make sure we are no longer using it.
+ */
+ public void onLoaderReset(Loader loader) {
+ switch (loader.getId()) {
+ case LOADER_ID_KEYRING:
+ // TODO?
+ break;
+ case LOADER_ID_USER_IDS:
+ mUserIdsAdapter.swapCursor(null);
+ break;
+ case LOADER_ID_KEYS:
+ mKeysAdapter.swapCursor(null);
+ break;
+ default:
+ break;
}
- return result;
}
private void uploadToKeyserver(Uri dataUri) {
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListAdapterOLD.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListAdapterOLD.java
deleted file mode 100644
index 8f745bdd3..000000000
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListAdapterOLD.java
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- * Copyright (C) 2012-2013 Dominik Schürmann
- *
- * 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.adapter;
-
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.Id;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.helper.OtherHelper;
-import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
-import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
-import org.sufficientlysecure.keychain.util.Log;
-
-import android.content.Context;
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.database.MergeCursor;
-import android.net.Uri;
-import android.provider.BaseColumns;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.CursorTreeAdapter;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-public class KeyListAdapterOLD extends CursorTreeAdapter {
- private Context mContext;
- private LayoutInflater mInflater;
-
- protected int mKeyType;
-
- private static final int CHILD_KEY = 0;
- private static final int CHILD_USER_ID = 1;
- private static final int CHILD_FINGERPRINT = 2;
-
- public KeyListAdapterOLD(Context context, Cursor groupCursor, int keyType) {
- super(groupCursor, context);
- mContext = context;
- mInflater = LayoutInflater.from(context);
- mKeyType = keyType;
- }
-
- /**
- * Inflate new view for group items
- */
- @Override
- public View newGroupView(Context context, Cursor cursor, boolean isExpanded, ViewGroup parent) {
- return mInflater.inflate(R.layout.key_list_item, null);
- }
-
- /**
- * Binds TextViews from group view to results from database group cursor.
- */
- @Override
- protected void bindGroupView(View view, Context context, Cursor cursor, boolean isExpanded) {
- int userIdIndex = cursor.getColumnIndex(UserIds.USER_ID);
-
- TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
- mainUserId.setText(R.string.unknown_user_id);
- TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
- mainUserIdRest.setText("");
-
- String userId = cursor.getString(userIdIndex);
- if (userId != null) {
- String[] userIdSplit = OtherHelper.splitUserId(userId);
-
- if (userIdSplit[1] != null) {
- mainUserIdRest.setText(userIdSplit[1]);
- }
- mainUserId.setText(userIdSplit[0]);
- }
-
- if (mainUserId.getText().length() == 0) {
- mainUserId.setText(R.string.unknown_user_id);
- }
-
- if (mainUserIdRest.getText().length() == 0) {
- mainUserIdRest.setVisibility(View.GONE);
- } else {
- mainUserIdRest.setVisibility(View.VISIBLE);
- }
- }
-
-// /**
-// * Inflate new view for child items
-// */
-// @Override
-// public View newChildView(Context context, Cursor cursor, boolean isLastChild, ViewGroup parent) {
-// return mInflater.inflate(R.layout.key_list_child_item, null);
-// }
-
- /**
- * Bind TextViews from view of childs based on query results
- */
- @Override
- protected void bindChildView(View view, Context context, Cursor cursor, boolean isLastChild) {
-// LinearLayout keyLayout = (LinearLayout) view.findViewById(R.id.keyLayout);
-// LinearLayout userIdLayout = (LinearLayout) view.findViewById(R.id.userIdLayout);
-
- // first entry is fingerprint
- if (cursor.getPosition() == 0) {
- // show only userId layout
-// keyLayout.setVisibility(View.GONE);
-// userIdLayout.setVisibility(View.VISIBLE);
-
- String fingerprint = PgpKeyHelper.getFingerPrint(context,
- cursor.getLong(cursor.getColumnIndex(Keys.KEY_ID)));
- fingerprint = fingerprint.replace(" ", "\n");
-
- TextView userId = (TextView) view.findViewById(R.id.userId);
- if (userId == null) {
- Log.d(Constants.TAG, "userId is null!");
- }
- userId.setText(context.getString(R.string.fingerprint) + "\n" + fingerprint);
- } else {
- // differentiate between keys and userIds in MergeCursor
- if (cursor.getColumnIndex(Keys.KEY_ID) != -1) {
-// keyLayout.setVisibility(View.VISIBLE);
-// userIdLayout.setVisibility(View.GONE);
-
- String keyIdStr = PgpKeyHelper.convertKeyIdToHex(cursor.getLong(cursor
- .getColumnIndex(Keys.KEY_ID)));
- String algorithmStr = PgpKeyHelper.getAlgorithmInfo(
- cursor.getInt(cursor.getColumnIndex(Keys.ALGORITHM)),
- cursor.getInt(cursor.getColumnIndex(Keys.KEY_SIZE)));
-
- TextView keyId = (TextView) view.findViewById(R.id.keyId);
- keyId.setText(keyIdStr);
-
- TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails);
- keyDetails.setText("(" + algorithmStr + ")");
-
- ImageView masterKeyIcon = (ImageView) view.findViewById(R.id.ic_masterKey);
- if (cursor.getInt(cursor.getColumnIndex(Keys.IS_MASTER_KEY)) != 1) {
- masterKeyIcon.setVisibility(View.INVISIBLE);
- } else {
- masterKeyIcon.setVisibility(View.VISIBLE);
- }
-
- ImageView certifyIcon = (ImageView) view.findViewById(R.id.ic_certifyKey);
- if (cursor.getInt(cursor.getColumnIndex(Keys.CAN_CERTIFY)) != 1) {
- certifyIcon.setVisibility(View.GONE);
- } else {
- certifyIcon.setVisibility(View.VISIBLE);
- }
-
- ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey);
- if (cursor.getInt(cursor.getColumnIndex(Keys.CAN_ENCRYPT)) != 1) {
- encryptIcon.setVisibility(View.GONE);
- } else {
- encryptIcon.setVisibility(View.VISIBLE);
- }
-
- ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey);
- if (cursor.getInt(cursor.getColumnIndex(Keys.CAN_SIGN)) != 1) {
- signIcon.setVisibility(View.GONE);
- } else {
- signIcon.setVisibility(View.VISIBLE);
- }
- } else {
-// keyLayout.setVisibility(View.GONE);
-// userIdLayout.setVisibility(View.VISIBLE);
-
- String userIdStr = cursor.getString(cursor.getColumnIndex(UserIds.USER_ID));
-
- TextView userId = (TextView) view.findViewById(R.id.userId);
- userId.setText(userIdStr);
- }
- }
- }
-
- /**
- * Given the group cursor, we start cursors for a fingerprint, keys, and userIds, which are
- * merged together and build the child cursor
- */
- @Override
- protected Cursor getChildrenCursor(Cursor groupCursor) {
- final long keyRingRowId = groupCursor.getLong(groupCursor.getColumnIndex(BaseColumns._ID));
-
- Cursor fingerprintCursor = getChildCursor(keyRingRowId, CHILD_FINGERPRINT);
- Cursor keyCursor = getChildCursor(keyRingRowId, CHILD_KEY);
- Cursor userIdCursor = getChildCursor(keyRingRowId, CHILD_USER_ID);
-
- MergeCursor mergeCursor = new MergeCursor(new Cursor[] { fingerprintCursor, keyCursor,
- userIdCursor });
- Log.d(Constants.TAG, "mergeCursor:" + DatabaseUtils.dumpCursorToString(mergeCursor));
-
- return mergeCursor;
- }
-
- /**
- * This builds a cursor for a specific type of children
- *
- * @param keyRingRowId
- * foreign row id of the keyRing
- * @param type
- * @return
- */
- private Cursor getChildCursor(long keyRingRowId, int type) {
- Uri uri = null;
- String[] projection = null;
- String sortOrder = null;
- String selection = null;
-
- switch (type) {
- case CHILD_FINGERPRINT:
- projection = new String[] { Keys._ID, Keys.KEY_ID, Keys.IS_MASTER_KEY, Keys.ALGORITHM,
- Keys.KEY_SIZE, Keys.CAN_CERTIFY, Keys.CAN_SIGN, Keys.CAN_ENCRYPT, };
- sortOrder = Keys.RANK + " ASC";
-
- // use only master key for fingerprint
- selection = Keys.IS_MASTER_KEY + " = 1 ";
-
- if (mKeyType == Id.type.public_key) {
- uri = Keys.buildPublicKeysUri(String.valueOf(keyRingRowId));
- } else {
- uri = Keys.buildSecretKeysUri(String.valueOf(keyRingRowId));
- }
- break;
-
- case CHILD_KEY:
- projection = new String[] { Keys._ID, Keys.KEY_ID, Keys.IS_MASTER_KEY, Keys.ALGORITHM,
- Keys.KEY_SIZE, Keys.CAN_CERTIFY, Keys.CAN_SIGN, Keys.CAN_ENCRYPT, };
- sortOrder = Keys.RANK + " ASC";
-
- if (mKeyType == Id.type.public_key) {
- uri = Keys.buildPublicKeysUri(String.valueOf(keyRingRowId));
- } else {
- uri = Keys.buildSecretKeysUri(String.valueOf(keyRingRowId));
- }
-
- break;
-
- case CHILD_USER_ID:
- projection = new String[] { UserIds._ID, UserIds.USER_ID, UserIds.RANK, };
- sortOrder = UserIds.RANK + " ASC";
-
- // not the main user id
- selection = UserIds.RANK + " > 0 ";
-
- if (mKeyType == Id.type.public_key) {
- uri = UserIds.buildPublicUserIdsUri(String.valueOf(keyRingRowId));
- } else {
- uri = UserIds.buildSecretUserIdsUri(String.valueOf(keyRingRowId));
- }
-
- break;
-
- default:
- return null;
-
- }
-
- return mContext.getContentResolver().query(uri, projection, selection, null, sortOrder);
- }
-
- @Override
- protected View newChildView(Context context, Cursor cursor, boolean isLastChild,
- ViewGroup parent) {
- // TODO Auto-generated method stub
- return null;
- }
-
-}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java
index 140781b4f..f1e58a5d3 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java
@@ -19,11 +19,13 @@ package org.sufficientlysecure.keychain.ui.adapter;
import java.util.HashMap;
import java.util.Set;
+
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.helper.OtherHelper;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.util.Log;
+
import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
import android.annotation.SuppressLint;
import android.content.Context;
@@ -69,7 +71,7 @@ public class KeyListPublicAdapter extends CursorAdapter implements StickyListHea
String userId = cursor.getString(userIdIndex);
if (userId != null) {
- String[] userIdSplit = OtherHelper.splitUserId(userId);
+ String[] userIdSplit = PgpKeyHelper.splitUserId(userId);
if (userIdSplit[1] != null) {
mainUserIdRest.setText(userIdSplit[1]);
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java
index f78eaf627..d06c0287c 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java
@@ -21,7 +21,7 @@ import java.util.HashMap;
import java.util.Set;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.helper.OtherHelper;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import android.annotation.SuppressLint;
@@ -57,7 +57,7 @@ public class KeyListSecretAdapter extends CursorAdapter {
String userId = cursor.getString(userIdIndex);
if (userId != null) {
- String[] userIdSplit = OtherHelper.splitUserId(userId);
+ String[] userIdSplit = PgpKeyHelper.splitUserId(userId);
if (userIdSplit[1] != null) {
mainUserIdRest.setText(userIdSplit[1]);
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java
index ebb7261be..c6eca0296 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java
@@ -18,11 +18,10 @@
package org.sufficientlysecure.keychain.ui.adapter;
import org.sufficientlysecure.keychain.Id;
-import org.sufficientlysecure.keychain.helper.OtherHelper;
+import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
-import org.sufficientlysecure.keychain.R;
import android.content.Context;
import android.database.Cursor;
@@ -78,7 +77,7 @@ public class SelectKeyCursorAdapter extends CursorAdapter {
String userId = cursor.getString(cursor.getColumnIndex(UserIds.USER_ID));
if (userId != null) {
- String[] userIdSplit = OtherHelper.splitUserId(userId);
+ String[] userIdSplit = PgpKeyHelper.splitUserId(userId);
if (userIdSplit[1] != null) {
mainUserIdRest.setText(userIdSplit[1]);
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java
new file mode 100644
index 000000000..51286af66
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package org.sufficientlysecure.keychain.ui.adapter;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.support.v4.widget.CursorAdapter;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+public class ViewKeyKeysAdapter extends CursorAdapter {
+ private LayoutInflater mInflater;
+
+ public ViewKeyKeysAdapter(Context context, Cursor c, int flags) {
+ super(context, c, flags);
+
+ mInflater = LayoutInflater.from(context);
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ String keyIdStr = PgpKeyHelper.convertKeyIdToHex(cursor.getLong(cursor
+ .getColumnIndex(Keys.KEY_ID)));
+ String algorithmStr = PgpKeyHelper.getAlgorithmInfo(
+ cursor.getInt(cursor.getColumnIndex(Keys.ALGORITHM)),
+ cursor.getInt(cursor.getColumnIndex(Keys.KEY_SIZE)));
+
+ TextView keyId = (TextView) view.findViewById(R.id.keyId);
+ keyId.setText(keyIdStr);
+
+ TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails);
+ keyDetails.setText("(" + algorithmStr + ")");
+
+ ImageView masterKeyIcon = (ImageView) view.findViewById(R.id.ic_masterKey);
+ if (cursor.getInt(cursor.getColumnIndex(Keys.IS_MASTER_KEY)) != 1) {
+ masterKeyIcon.setVisibility(View.INVISIBLE);
+ } else {
+ masterKeyIcon.setVisibility(View.VISIBLE);
+ }
+
+ ImageView certifyIcon = (ImageView) view.findViewById(R.id.ic_certifyKey);
+ if (cursor.getInt(cursor.getColumnIndex(Keys.CAN_CERTIFY)) != 1) {
+ certifyIcon.setVisibility(View.GONE);
+ } else {
+ certifyIcon.setVisibility(View.VISIBLE);
+ }
+
+ ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey);
+ if (cursor.getInt(cursor.getColumnIndex(Keys.CAN_ENCRYPT)) != 1) {
+ encryptIcon.setVisibility(View.GONE);
+ } else {
+ encryptIcon.setVisibility(View.VISIBLE);
+ }
+
+ ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey);
+ if (cursor.getInt(cursor.getColumnIndex(Keys.CAN_SIGN)) != 1) {
+ signIcon.setVisibility(View.GONE);
+ } else {
+ signIcon.setVisibility(View.VISIBLE);
+ }
+ }
+
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return mInflater.inflate(R.layout.view_key_keys_item, null);
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java
index 55821b11e..2e2606fd0 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Dominik Schürmann
+ * Copyright (C) 2014 Dominik Schürmann
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,17 +17,11 @@
package org.sufficientlysecure.keychain.ui.adapter;
-import java.util.HashMap;
-import java.util.Set;
-
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.helper.OtherHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
-import android.annotation.SuppressLint;
import android.content.Context;
import android.database.Cursor;
-import android.graphics.Color;
import android.support.v4.widget.CursorAdapter;
import android.view.LayoutInflater;
import android.view.View;
@@ -47,35 +41,15 @@ public class ViewKeyUserIdsAdapter extends CursorAdapter {
public void bindView(View view, Context context, Cursor cursor) {
int userIdIndex = cursor.getColumnIndex(UserIds.USER_ID);
- TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
- mainUserId.setText(R.string.unknown_user_id);
- TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
- mainUserIdRest.setText("");
-
- String userId = cursor.getString(userIdIndex);
- if (userId != null) {
- String[] userIdSplit = OtherHelper.splitUserId(userId);
-
- if (userIdSplit[1] != null) {
- mainUserIdRest.setText(userIdSplit[1]);
- }
- mainUserId.setText(userIdSplit[0]);
- }
-
- if (mainUserId.getText().length() == 0) {
- mainUserId.setText(R.string.unknown_user_id);
- }
+ String userIdStr = cursor.getString(userIdIndex);
- if (mainUserIdRest.getText().length() == 0) {
- mainUserIdRest.setVisibility(View.GONE);
- } else {
- mainUserIdRest.setVisibility(View.VISIBLE);
- }
+ TextView userId = (TextView) view.findViewById(R.id.userId);
+ userId.setText(userIdStr);
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
- return mInflater.inflate(R.layout.key_list_item, null);
+ return mInflater.inflate(R.layout.view_key_userids_item, null);
}
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/FixedListView.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/FixedListView.java
new file mode 100644
index 000000000..277f14b6f
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/FixedListView.java
@@ -0,0 +1,55 @@
+package org.sufficientlysecure.keychain.ui.widget;
+
+/*
+ * Copyright (C) 2014 Dominik Schürmann
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.ListView;
+
+/**
+ * Automatically calculate height of listview based on contained items. This enables to put this
+ * ListView into a ScrollView without messing up.
+ *
+ * from
+ * http://stackoverflow.com/questions/2419246/how-do-i-create-a-listview-thats-not-in-a-scrollview-
+ * or-has-the-scrollview-dis
+ */
+public class FixedListView extends ListView {
+
+ public FixedListView(Context context) {
+ super(context);
+ }
+
+ public FixedListView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ public FixedListView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // Calculate height of the entire list by providing a very large
+ // height hint. But do not use the highest 2 bits of this integer;
+ // those are reserved for the MeasureSpec mode.
+ int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
+ super.onMeasure(widthMeasureSpec, expandSpec);
+ }
+
+}
\ No newline at end of file
--
cgit v1.2.3
From e3bcf64d9e63e293c51db921572a9b87533d26ad Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?=
Date: Mon, 13 Jan 2014 00:48:20 +0100
Subject: bump version code to 21106 for beta testers
---
OpenPGP-Keychain/AndroidManifest.xml | 18 +-----------------
1 file changed, 1 insertion(+), 17 deletions(-)
diff --git a/OpenPGP-Keychain/AndroidManifest.xml b/OpenPGP-Keychain/AndroidManifest.xml
index 9e4f3ca49..da655ba02 100644
--- a/OpenPGP-Keychain/AndroidManifest.xml
+++ b/OpenPGP-Keychain/AndroidManifest.xml
@@ -1,24 +1,8 @@
-