I am writing a code in android for rpn calculations and it crashes when i have operators inside brackets. Eg(89+7) crashes but (8)+7 outputs 15.
This is the method i use for infix to postfix conversion:
private static List<String> convert(String input) {
int p = 0;
String pfb = "";
Stack<Character> chara = new Stack<>();
List<String> pfa = new ArrayList<>();
for (int i = 0; i < input.length(); i++) {
char ch = input.charAt(i);
if (ch == '+' || ch == '-' || ch == '*' || ch == '/') {
if (pfb.length() > 0) {
pfa.add(pfb);
}
pfb = "";
if (chara.empty()) {
chara.push(ch);
} else {
Character chTop = (Character) chara.peek();
if (chTop == '*' || chTop == '/')
p = 1;
else if (chTop == '+' || chTop == '-')
p = 0;
if (p == 1) {
if (ch == '+' || ch == '-') {
pfa.add(String.valueOf(chara.pop()));
i--;
Log.d("pfa", "" + input.length() + "" + i);
} else { // Same
pfa.add(String.valueOf(chara.pop()));
i--;
}
} else {
if (ch == '+' || ch == '-' && chara.peek() != '(') {
pfa.add(String.valueOf(chara.pop()));
chara.push(ch);
} else
chara.push(ch);
}
}
} else if (ch == '(') {
chara.push(ch);
// Log.d("St",""+chara.peek());
} else if (ch == ')') {
`//Code crashes here` while (chara.peek() != '(' && !chara.empty()) {
pfa.add(String.valueOf(chara.pop()));
}
if (!chara.empty() && chara.peek() != '(')
return null; // invalid expression
else
chara.pop();
} else {
pfb += ch;
}
// Log.d("pfa",""+pfa+"");
}
return pfa;
}
You code crashes because first you peek the stack element and then checking the empty condition.
it should be like
while ( !chara.empty() && chara.peek() != '(' )
I am trying to serialize an android.location.Address object using Simple 2.7.1.
I have tried creating a converter...
public class AddressConverter implements Converter<Address> {
#Override
public Address read(InputNode node) throws Exception {
...
//Not included because this part works!!!!
}
#Override
public void write(OutputNode node, Address addr) throws Exception {
node.setAttribute("Locale", addr.getLocale().toString().replace("_", "-"));
OutputNode child = null;
if (addr.getMaxAddressLineIndex() > 0) {
for (int index = 0; index <= addr.getMaxAddressLineIndex(); index++) {
child = node.getChild("AddressLine");
child.setAttribute("Index", String.valueOf(index));
child.setValue(addr.getAddressLine(index));
}
}
if (addr.getFeatureName() != null && addr.getFeatureName().length() > 0) {
child = node.getChild("FeatureName");
child.setValue(addr.getFeatureName());
child.commit();
}
if (addr.getPremises() != null && addr.getPremises().length() > 0) {
child = node.getChild("Premises");
child.setValue(addr.getPremises());
child.commit();
}
if (addr.getSubThoroughfare() != null && addr.getSubThoroughfare().length() > 0) {
child = node.getChild("SubThoroughfare");
child.setValue(addr.getSubThoroughfare());
child.commit();
}
if (addr.getThoroughfare() != null && addr.getThoroughfare().length() > 0) {
child = node.getChild("Thoroughfare");
child.setValue(addr.getThoroughfare());
child.commit();
}
if (addr.getSubLocality() != null && addr.getSubLocality().length() > 0) {
child = node.getChild("SubLocality");
child.setValue(addr.getSubLocality());
child.commit();
}
if (addr.getLocality() != null && addr.getLocality().length() > 0) {
child = node.getChild("Locality");
child.setValue(addr.getLocality());
child.commit();
}
if (addr.getSubAdminArea() != null && addr.getSubAdminArea().length() > 0) {
child = node.getChild("SubAdminArea");
child.setValue(addr.getSubAdminArea());
child.commit();
}
if (addr.getAdminArea() != null && addr.getAdminArea().length() > 0) {
child = node.getChild("AdminArea");
child.setValue(addr.getAdminArea());
child.commit();
}
if (addr.getPostalCode() != null && addr.getPostalCode().length() > 0) {
child = node.getChild("PostalCode");
child.setValue(addr.getPostalCode());
child.commit();
}
if (addr.getCountryCode() != null && addr.getCountryCode().length() > 0) {
child = node.getChild("CountryCode");
child.setValue(addr.getCountryCode());
child.commit();
}
if (addr.getCountryName() != null && addr.getCountryName().length() > 0) {
child = node.getChild("CountryName");
child.setValue(addr.getCountryName());
child.commit();
}
if (addr.getLatitude() != 0) {
child = node.getChild("Latitude");
child.setValue(String.valueOf(addr.getLatitude()));
child.commit();
}
if (addr.getLongitude() != 0) {
child = node.getChild("Longitude");
child.setValue(String.valueOf(addr.getLongitude()));
child.commit();
}
if (addr.getPhone() != null && addr.getPhone().length() > 0) {
child = node.getChild("Phone");
child.setValue(addr.getPhone());
child.commit();
}
node.commit();
}
#SuppressLint("DefaultLocale")
private Locale getLocale(String strLocale) {
String[] splits = strLocale.split("-", 3);
switch (splits.length) {
case 3:
return new Locale(splits[0].toLowerCase(), splits[1].toUpperCase(), splits[2].toUpperCase());
case 2:
return new Locale(splits[0].toLowerCase(), splits[1].toUpperCase());
default:
return new Locale(splits[0].toLowerCase());
}
}
}
I cannot get the write to write the child elements.
Can someone please help me?!
Any suggestions on how to create child nodes with an OutputNode would also be greatly appreciated, as you can see I have tried the documentation but I can't figure it out!!!
I have found that this does serialize the android.location.Address object but it changes the order of where the object is put within the XML!!!
I am sorry if anyone has tried to resolve this, and I can only apologise for wasting your time!!!
I know this is probably one of dumbest questions ever but my brain seems off.
I have this method which makes a string out of vCard:
public static String process(String vCard) {
ArrayList<ArrayList<String>> vCardData = parseData(vCard);
if (vCardData != null) {
StringBuilder readableVCard = new StringBuilder();
for (int i = 0; i < FIELD_COUNT; i++) {
ArrayList<String> vCardDataField = vCardData.get(i);
if (vCardDataField.size() > 0) {
String field = null;
if (i == FORMATTED_NAME) {
field = "Name: ";
} else if (i == PHONE_MOBILE) {
field = "Phone (mobile): ";
} else if (i == PHONE_HOME) {
field = "Phone (home): ";
} else if (i == PHONE_WORK) {
field = "Phone (work): ";
} else if (i == PHONE) {
field = "Phone: ";
} else if (i == FAX_HOME) {
field = "Fax (home): ";
} else if (i == FAX_WORK) {
field = "Fax (work): ";
} else if (i == PAGER) {
field = "Pager: ";
} else if (i == EMAIL_HOME) {
field = "Email (home): ";
} else if (i == EMAIL_WORK) {
field = "Email (work): ";
} else if (i == EMAIL) {
field = "Email: ";
} else if (i == ORGANISATION) {
field = "Company: ";
} else if (i == JOB_TITLE) {
field = "Job title: ";
} else if (i == ADDRESS_HOME) {
field = "Address (home): ";
} else if (i == ADDRESS_WORK) {
field = "Address (work): ";
} else if (i == ADDRESS) {
field = "Address: ";
} else if (i == IM_SKYPE) {
field = "Skype: ";
} else if (i == IM_GOOGLE) {
field = "Google Talk: ";
} else if (i == IM_JABBER) {
field = "Jabber: ";
} else if (i == IM_YAHOO) {
field = "Yahoo: ";
} else if (i == IM_MSN) {
field = "MSN: ";
} else if (i == IM_ICQ) {
field = "ICQ: ";
} else if (i == IM_AIM) {
field = "AIM: ";
} else if (i == TWITTER) {
field = "Twitter: ";
} else if (i == BIRTHDAY) {
field = "Birthday: ";
} else if (i == ANNIVERSARY) {
field = "Anniversary: ";
} else if (i == NOTES) {
field = "Notes: ";
} else if (i == WEBSITE) {
field = "Website: ";
} else {
continue;
}
if (readableVCard.length() != 0) {
readableVCard.append("\n");
}
readableVCard.append(field);
for (int j = 0; j < vCardDataField.size(); j++) {
if (j != 0) {
readableVCard.append("; ");
}
readableVCard.append(vCardDataField.get(j));
}
}
}
if (readableVCard.length() != 0) {
String textVCard = readableVCard.toString();
try {
textVCard = qpDecoder.decode(readableVCard.toString());
} catch (Exception e) {
Logger.e("VCard to UTF-8", e.getMessage());
}
return (textVCard);
}
}
return (null);
}
So my current output is like this:
Name: Marko
Phone(mobile):1312
Phone(fax):441231
Phone(home):543534
Email(home):dddd
Email(work):eeee
Email(other):aaaa
What I want is to add a line break between groups (name/phone/email) so I get something like this:
Name: Marko
Phone(mobile):1312
Phone(fax):441231
Phone(home):543534
Email(home):dddd
Email(work):eeee
Email(other):aaaa
Addressblablabla
.
.
.
IMsblablabla
.
.
.
Can someone help please?
Thanks.
You could iterate the string array, splitting by : and then checking if next item starts with current. If it does, then do nothing, if it doesn't you add the line break to current item and replace it in the array.
Note: splitting with : also separates all phone (xxx) types. To avoid this, you can create enum type, map type to Phone if the string starts with "phone" and use that enum as current type while iterating through the array. If current type if different than next type you add line break.
You can append line separator via
System.getProperty("line.separator");
I have an email project in Android and I can receive and download my mails and its attachments but now I want to learn if a message has an attachment or not, and how many attachments it has got.
I mean I just need to do an if clause like:
if(messages[i].hasAttachment)
{
int numberOfAttachments = messages[i].attachmentNumber;
// do smthng
}
else
{
// do smthng
}
Maybe its help: I am receiving my body part with this code sample. Meanwhile, as per my subject, if the isMimeType = "Multipart/alternative" or "Multipart/*" , does it mean it has an attachment?
public String getText(Part p) throws MessagingException, IOException {
if (p.isMimeType("text/*")) {
boolean textIsHtml = false;
String s = (String) p.getContent();
textIsHtml = p.isMimeType("text/html");
return String.valueOf(s);
}
if (p.isMimeType("multipart/alternative")) {
// prefer html text over plain text
Multipart mp = (Multipart) p.getContent();
String text = null;
for (int i = 0; i < mp.getCount(); i++) {
Part bp = mp.getBodyPart(i);
if (bp.isMimeType("text/plain")) {
if (text == null)
text = getText(bp);
continue;
} else if (bp.isMimeType("text/html")) {
String s = getText(bp);
if (s != null)
return String.valueOf(s);
} else {
return getText(bp);
}
}
return text;
} else if (p.isMimeType("multipart/*")) {
Multipart mp = (Multipart) p.getContent();
for (int i = 0; i < mp.getCount(); i++) {
String s = getText(mp.getBodyPart(i));
// fileName = bp.getFileName();
if (s != null)
return String.valueOf(s);
}
}
return null;
}
Here is the code I use to parse attachments. I used to parse with multipart/attachment or multipart/*, and changed to this:
if( mimeMessage.getContent() instanceof Multipart) {
Multipart multipartContent = (Multipart) mimeMessage.getContent();
List<BodyPart> deleteThese = new ArrayList<BodyPart>();
for( int i = 0; i < multipartContent.getCount(); i++ ) {
MimeBodyPart part = (MimeBodyPart) multipartContent.getBodyPart(i);
String disposition = part.getDisposition();
if( disposition != null
&& ( disposition.equalsIgnoreCase( Part.ATTACHMENT )
|| ( disposition.equalsIgnoreCase( Part.INLINE )
&& !part.isMimeType( PLAIN_TEXT_MIME_TYPE )
&& !part.isMimeType( HTML_MIME_TYPE ) )
) ) {
// do something with part
}
}
Essentially if an email has a Multipart there is potential for an attachment, but you have to look at the content disposition to really know. Part.ATTACHMENT would be what you are interested in, and optionally you can ignore or parse the Part.INLINE.
I've built a QR code generator and a QR code scanner for passing data about phones and their users between phones (the phones are being loaned out so there will be a master phone with the scanner app and the rest with the generator app). The QR code generated is a JSON format string containing a persons name/number/imei of their phone but for security I have tried to encrypt the string before encoding to QR, but the scanned QR code throws up a 'pad block corrupted' error.
The JSON data encodes into QR/decodes from QR fine as plain text, and I checked the encryption/decryption before encoding to QR and the data encrypts/decrypts fine, so it's something to do with when the encrypted text is encoded into QR but I've no idea where to begin with it!
Does anyone know how i can sort the issue? Or if theres any QR friendly encryption methods?!!
I took the QRCodeEncoder straight from ZXings source and placed it into my activity:
/**QR ENCODER CLASS****************************************************/
public class QRCodeEncoder
{
private final String TAG = QRCodeEncoder.class.getSimpleName();
private static final int WHITE = 0xFFFFFFFF;
private static final int BLACK = 0xFF000000;
private final Activity activity;
private String contents;
private String displayContents;
private String title;
private BarcodeFormat format;
private final int dimension;
QRCodeEncoder(Activity activity, Intent intent, int dimension) {
this.activity = activity;
if (intent == null) {
throw new IllegalArgumentException("No valid data to encode. intent is null");
}
String action = intent.getAction();
if (action.equals(Intents.Encode.ACTION)) {
if (!encodeContentsFromZXingIntent(intent)) {
throw new IllegalArgumentException("No valid data to encode. Zxing intent returned false");
}
} else if (action.equals(Intent.ACTION_SEND)) {
if (!encodeContentsFromShareIntent(intent)) {
throw new IllegalArgumentException("No valid data to encode. Share Intent returned false");
}
}
this.dimension = dimension;
}
public String getContents() {
return contents;
}
public String getDisplayContents() {
return displayContents;
}
public String getTitle() {
return title;
}
// It would be nice if the string encoding lived in the core ZXing library,
// but we use platform specific code like PhoneNumberUtils, so it can't.
private boolean encodeContentsFromZXingIntent(Intent intent) {
// Default to QR_CODE if no format given.
String formatString = intent.getStringExtra(Intents.Encode.FORMAT);
try {
format = BarcodeFormat.valueOf(formatString);
} catch (IllegalArgumentException iae) {
// Ignore it then
format = null;
}
if (format == null || BarcodeFormat.QR_CODE.equals(format)) {
String type = intent.getStringExtra(Intents.Encode.TYPE);
if (type == null || type.length() == 0) {
return false;
}
this.format = BarcodeFormat.QR_CODE;
encodeQRCodeContents(intent, type);
} else {
String data = intent.getStringExtra(Intents.Encode.DATA);
if (data != null && data.length() > 0) {
contents = data;
displayContents = data;
title = "QR Encoder";
}
}
return contents != null && contents.length() > 0;
}
// Handles send intents from multitude of Android applications
private boolean encodeContentsFromShareIntent(Intent intent) {
// Check if this is a plain text encoding, or contact
if (intent.hasExtra(Intent.EXTRA_TEXT)) {
return encodeContentsFromShareIntentPlainText(intent);
}
// Attempt default sharing.
return encodeContentsFromShareIntentDefault(intent);
}
private boolean encodeContentsFromShareIntentPlainText(Intent intent) {
// Notice: Google Maps shares both URL and details in one text, bummer!
contents = intent.getStringExtra(Intent.EXTRA_TEXT);
Toast.makeText(getApplicationContext(),"contents read = "+contents,Toast.LENGTH_SHORT).show();
// We only support non-empty and non-blank texts.
// Trim text to avoid URL breaking.
if (contents == null) {
return false;
}
contents = contents.trim();
if (contents.length() == 0) {
return false;
}
// We only do QR code.
format = BarcodeFormat.QR_CODE;
if (intent.hasExtra(Intent.EXTRA_SUBJECT)) {
displayContents = intent.getStringExtra(Intent.EXTRA_SUBJECT);
} else if (intent.hasExtra(Intent.EXTRA_TITLE)) {
displayContents = intent.getStringExtra(Intent.EXTRA_TITLE);
} else {
displayContents = contents;
}
title = "QR Encoder";
return true;
}
// Handles send intents from the Contacts app, retrieving a contact as a VCARD.
// Note: Does not work on HTC devices due to broken custom Contacts application.
private boolean encodeContentsFromShareIntentDefault(Intent intent) {
format = BarcodeFormat.QR_CODE;
try {
Uri uri = (Uri)intent.getExtras().getParcelable(Intent.EXTRA_STREAM);
InputStream stream = activity.getContentResolver().openInputStream(uri);
int length = stream.available();
if (length <= 0) {
Log.w(TAG, "Content stream is empty");
return false;
}
byte[] vcard = new byte[length];
int bytesRead = stream.read(vcard, 0, length);
if (bytesRead < length) {
Log.w(TAG, "Unable to fully read available bytes from content stream");
return false;
}
String vcardString = new String(vcard, 0, bytesRead, "UTF-8");
Log.d(TAG, "Encoding share intent content:");
Log.d(TAG, vcardString);
Result result = new Result(vcardString, vcard, null, BarcodeFormat.QR_CODE);
ParsedResult parsedResult = ResultParser.parseResult(result);
if (!(parsedResult instanceof AddressBookParsedResult)) {
Log.d(TAG, "Result was not an address");
return false;
}
if (!encodeQRCodeContents((AddressBookParsedResult) parsedResult)) {
Log.d(TAG, "Unable to encode contents");
return false;
}
} catch (IOException e) {
Log.w(TAG, e);
return false;
} catch (NullPointerException e) {
Log.w(TAG, e);
// In case the uri was not found in the Intent.
return false;
}
return contents != null && contents.length() > 0;
}
private void encodeQRCodeContents(Intent intent, String type) {
if (type.equals(Contents.Type.TEXT)) {
String data = intent.getStringExtra(Intents.Encode.DATA);
if (data != null && data.length() > 0) {
contents = data;
displayContents = data;
title = "QR Encoder";
}
} else if (type.equals(Contents.Type.EMAIL)) {
String data = trim(intent.getStringExtra(Intents.Encode.DATA));
if (data != null) {
contents = "mailto:" + data;
displayContents = data;
title = "QR Encoder";
}
} else if (type.equals(Contents.Type.PHONE)) {
String data = trim(intent.getStringExtra(Intents.Encode.DATA));
if (data != null) {
contents = "tel:" + data;
displayContents = PhoneNumberUtils.formatNumber(data);
title = "QR Encoder";
}
} else if (type.equals(Contents.Type.SMS)) {
String data = trim(intent.getStringExtra(Intents.Encode.DATA));
if (data != null) {
contents = "sms:" + data;
displayContents = PhoneNumberUtils.formatNumber(data);
title = "QR Encoder";
}
} else if (type.equals(Contents.Type.CONTACT)) {
Bundle bundle = intent.getBundleExtra(Intents.Encode.DATA);
if (bundle != null) {
StringBuilder newContents = new StringBuilder(100);
StringBuilder newDisplayContents = new StringBuilder(100);
newContents.append("MECARD:");
String name = trim(bundle.getString(Contacts.Intents.Insert.NAME));
if (name != null) {
newContents.append("N:").append(escapeMECARD(name)).append(';');
newDisplayContents.append(name);
}
String address = trim(bundle.getString(Contacts.Intents.Insert.POSTAL));
if (address != null) {
newContents.append("ADR:").append(escapeMECARD(address)).append(';');
newDisplayContents.append('\n').append(address);
}
for (int x = 0; x < Contents.PHONE_KEYS.length; x++) {
String phone = trim(bundle.getString(Contents.PHONE_KEYS[x]));
if (phone != null) {
newContents.append("TEL:").append(escapeMECARD(phone)).append(';');
newDisplayContents.append('\n').append(PhoneNumberUtils.formatNumber(phone));
}
}
for (int x = 0; x < Contents.EMAIL_KEYS.length; x++) {
String email = trim(bundle.getString(Contents.EMAIL_KEYS[x]));
if (email != null) {
newContents.append("EMAIL:").append(escapeMECARD(email)).append(';');
newDisplayContents.append('\n').append(email);
}
}
// Make sure we've encoded at least one field.
if (newDisplayContents.length() > 0) {
newContents.append(';');
contents = newContents.toString();
displayContents = newDisplayContents.toString();
title = "QR Encoder";
} else {
contents = null;
displayContents = null;
}
}
} else if (type.equals(Contents.Type.LOCATION)) {
Bundle bundle = intent.getBundleExtra(Intents.Encode.DATA);
if (bundle != null) {
// These must use Bundle.getFloat(), not getDouble(), it's part of the API.
float latitude = bundle.getFloat("LAT", Float.MAX_VALUE);
float longitude = bundle.getFloat("LONG", Float.MAX_VALUE);
if (latitude != Float.MAX_VALUE && longitude != Float.MAX_VALUE) {
contents = "geo:" + latitude + ',' + longitude;
displayContents = latitude + "," + longitude;
title = "QR Encoder";
}
}
}
}
private boolean encodeQRCodeContents(AddressBookParsedResult contact) {
StringBuilder newContents = new StringBuilder(100);
StringBuilder newDisplayContents = new StringBuilder(100);
newContents.append("MECARD:");
String[] names = contact.getNames();
if (names != null && names.length > 0) {
String name = trim(names[0]);
if (name != null) {
newContents.append("N:").append(escapeMECARD(name)).append(';');
newDisplayContents.append(name);
}
}
String[] addresses = contact.getAddresses();
if (addresses != null) {
for (String address : addresses) {
address = trim(address);
if (address != null) {
newContents.append("ADR:").append(escapeMECARD(address)).append(';');
newDisplayContents.append('\n').append(address);
}
}
}
String[] phoneNumbers = contact.getPhoneNumbers();
if (phoneNumbers != null) {
for (String phone : phoneNumbers) {
phone = trim(phone);
if (phone != null) {
newContents.append("TEL:").append(escapeMECARD(phone)).append(';');
newDisplayContents.append('\n').append(PhoneNumberUtils.formatNumber(phone));
}
}
}
String[] emails = contact.getEmails();
if (emails != null) {
for (String email : emails) {
email = trim(email);
if (email != null) {
newContents.append("EMAIL:").append(escapeMECARD(email)).append(';');
newDisplayContents.append('\n').append(email);
}
}
}
String url = trim(contact.getURL());
if (url != null) {
newContents.append("URL:").append(escapeMECARD(url)).append(';');
newDisplayContents.append('\n').append(url);
}
// Make sure we've encoded at least one field.
if (newDisplayContents.length() > 0) {
newContents.append(';');
contents = newContents.toString();
displayContents = newDisplayContents.toString();
title = "QR Encoder";
return true;
} else {
contents = null;
displayContents = null;
return false;
}
}
Bitmap encodeAsBitmap() throws WriterException {
Hashtable<EncodeHintType,Object> hints = null;
String encoding = guessAppropriateEncoding(contents);
if (encoding != null) {
hints = new Hashtable<EncodeHintType,Object>(2);
hints.put(EncodeHintType.CHARACTER_SET, encoding);
}
MultiFormatWriter writer = new MultiFormatWriter();
BitMatrix result = writer.encode(contents, format, dimension, dimension, hints);
int width = result.getWidth();
int height = result.getHeight();
int[] pixels = new int[width * height];
// All are 0, or black, by default
for (int y = 0; y < height; y++) {
int offset = y * width;
for (int x = 0; x < width; x++) {
pixels[offset + x] = result.get(x, y) ? BLACK : WHITE;
}
}
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
return bitmap;
}
private String guessAppropriateEncoding(CharSequence contents) {
// Very crude at the moment
for (int i = 0; i < contents.length(); i++) {
if (contents.charAt(i) > 0xFF) {
return "UTF-8";
}
}
return null;
}
private String trim(String s) {
if (s == null) {
return null;
}
s = s.trim();
return s.length() == 0 ? null : s;
}
private String escapeMECARD(String input) {
if (input == null || (input.indexOf(':') < 0 && input.indexOf(';') < 0)) {
return input;
}
int length = input.length();
StringBuilder result = new StringBuilder(length);
for (int i = 0; i < length; i++) {
char c = input.charAt(i);
if (c == ':' || c == ';') {
result.append('\\');
}
result.append(c);
}
return result.toString();
}
}
And the encryption/decryption class from this website (unedited)
Here's a snippet of the onCreate() method in my activity:
QRCodeEncoder myQRCodeEncoder;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.qr_view);
ImageView imageView = (ImageView)findViewById(R.id.qr_image);
extStorageDirectory = Environment.getExternalStorageDirectory().toString();
try
{
//JSON data is passed from another activity to this one
qrMessage = getIntent().getStringExtra("QR_JSON");
Intent encode = new Intent(Intents.Encode.ACTION);
encode.putExtra(Intents.Encode.TYPE, Contents.Type.TEXT);
encode.putExtra(Intents.Encode.FORMAT, "QR_CODE");
//This is the original plain text way that works:
//encode.putExtra(Intents.Encode.DATA, qrMessage);
//This is the encyption way
String encMessage = SimpleCrypto.encrypt("my s3cr3t k3y", qrMessage);
encode.putExtra(Intents.Encode.DATA,encMessage);
myQRCodeEncoder = new QRCodeEncoder(this, encode, 200);
}
catch(Exception e)
{
Toast.makeText(getApplicationContext(),"Could not encode:"+e.getMessage(),Toast.LENGTH_SHORT).show();
}
catch(Error e)
{
Toast.makeText(getApplicationContext(),"Could not encode:"+e.getMessage(),Toast.LENGTH_SHORT).show();
}
try {
Bitmap qrBitmap = myQRCodeEncoder.encodeAsBitmap();
imageView.setImageBitmap(qrBitmap);
} catch (Exception e) {
Toast.makeText(getApplicationContext(),"Could not set image:"+e.getMessage(),Toast.LENGTH_SHORT).show();
}
}
And here's the onActivityResult method from the scanner (I use ZXing's barcode scanner to retrieve the data)
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
if (requestCode == 0) {
if (resultCode == RESULT_OK) {
String contents = intent.getStringExtra("SCAN_RESULT");//contents of the scan
String format = intent.getStringExtra("SCAN_RESULT_FORMAT");
// Handle successful scan
/* display the scanned persons info*/
try {
String decryptedcontents = SimpleCrypto.decrypt("my s3cr3t k3y",contents);
String result = getJSONFromScanData(decryptedcontents);
} catch (Exception e) {
// TODO Auto-generated catch block
Toast.makeText(this, "Scanned data could not be decrypted:"+e.getMessage(), Toast.LENGTH_SHORT).show();//says 'pad block corrupted' as the message
}
} else if (resultCode == RESULT_CANCELED) {
// Handle cancel
Toast.makeText(this, "Scan cancelled", Toast.LENGTH_SHORT).show();
}
}
}
EDIT: after some further investigation it seems that the encyption/decyption process seems to 'shave off' part of the data:
JSONObject example = new JSONObject("{\"user_firstname\":\"Ben\",\"user_lastname\":\" Ten\",\"user_login\":\"benten\",\"user_pass\":\"password\",\"user_email\":\"benten#domain.com\"}");
String mess = SimpleCrypto.encrypt("my s3cr3t k3y",example.toString());
String decrmess = SimpleCrypto.decrypt("my s3cr3t k3y",mess));
//decypts as:{"user_pass":"password","user_email":"benten#domain.com","user_login":"benten","user_lastname":"
as you can see only 96 characters are decrypted, theres no user_firstname or the users actual last name, the data is missing, but this number is inconsistent, I changed the user_email to "benbenten#domain.com" and the user_firstname to "benben" and 112 characters were decrypted...I am completely stumped
EDIT 2: Yngve Ã…dlandsvik has kindly pointed me in the right direction (many thanks again!) that the string length needed to be a multiple of 16, so I set the Cipher.getInstance in both the encrypt and decrypt methods to:
Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding","BC");
and in my main activity set a loop to add 0's on the end of my string as custom padding before encrypting:
boolean carryOn = true;
while(carryOn)
{
int paddedLength = qrMessage.getBytes().length;
int checkMultiple16 = paddedLength%16;
if(checkMultiple16==0)
{
carryOn = false;
}
else
qrMessage+="0";
}
EDIT 3: It looks like QR encoding still screws with the encryption, I can't decrypt the scanned in data properly, looks like QR encoding does something with strings before it encodes to QR which seems to break the thing, guess I'll have to stick to unencrypted text in the QR...
I haven't read the code closely, but I assume this happens because AES only operates on blocks of 16 bytes at once. So my guess is you need to manually apply some form of reversible padding to your string before encryption so it becomes a multiple of 16, and then reverse the padding after decryption.
You could also change the Cipher.getInstance() strings in the crypto code so the encryption will support padding natively, though I don't know which padding types and cipher modes are available on Android.