Copying Message Array - android

I am trying to copy messages from the Inbox folder:
messages = folder.getMessages();
Message[] source = messages;
I am doing this because, when I view messages in my inbox, all recent messages become "seen messages". And the reason for that is the getContent() method. I want to figure out how to copy the messages to another array, and process them all in the source message array.
But when I try the copying process like above, whatever changes I make to the source array also changes in the messages array. I mean if I call getContent() on the source message array, the messages array is effected also.
How can I copy all messages and rupture them completely from the folder?
public Message[] ConnectionToServer(String email, String password)
throws Exception
{
Properties props = System.getProperties();
props.setProperty("mail.imaps.partialfetch", "false");
URLName server = new URLName("imaps://" + email + ":" + password + "#imap.gmail.com/INBOX");
Session session = Session.getDefaultInstance(props, null);
folder = session.getFolder(server);
if (folder == null)
{
System.exit(0);
}
folder.open(Folder.READ_WRITE);
messages = folder.getMessages();
for (int i = messages.length - 1; i >= 23; i--)
{
Part p = messages[i];
subject = messages[i].getSubject();
if (messages[i].isSet(Flags.Flag.RECENT)) {
isSet = true;
System.out.println("Recent");
isSetlist.add(String.valueOf(isSet));
}
if (messages[i].isSet(Flags.Flag.SEEN))
{
isSet = false;
System.out.println("Read");
isSetlist.add(String.valueOf(isSet));
}
else
{
isSet = true;
System.out.println("Recent");
isSetlist.add(String.valueOf(isSet));
}
body = getText(p);
list.add(body);
}
return (Message[]) messages;
}
and the getContent method is in my getText() method:
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));
if (s != null)
return String.valueOf(s);
}
}
return null;
}

You're copying the pointer to the array, you're not copying the message content. You're two levels of indirection away from what you want to do.
But, copying the message content isn't going to solve your problem. The process of copying the message content is going to set the SEEN flag. What you need to do is make a copy of all the SEEN flags before you access the message content.
Or, you can use the com.sun.mail.imap.IMAPMessage.setPeek() method to cause accesses of the message content to NOT set the SEEN flag. Cast the Message object to IMAPMessage to use this method.

Related

in email mutipart message body (with attachment) is comming as plain text instead html format

I am writing an email client android app. In it, I am using JavaMail. While reading emails from server everything is coming properly except for a few mails which have attachments. In those cases the email body is coming as plain text instead of HTML format.
My code is as follows:
String str; //=bodyPart.toString();//(bodyPart.getContent());
if (bodyPart instanceof Part) {
if (bodyPart.getContent() instanceof String) {
str = bodyPart.getContent().toString();
}
else {
MimeMultipart mimeMultipart = (MimeMultipart) bodyPart.getContent();
str = getTextFromMimeMultipart(mimeMultipart);
}
}
else
str = bodyPart.getContent().toString();
//
//*****************************
//
private String getTextFromMimeMultipart(MimeMultipart mimeMultipart) throws MessagingException, IOException {
String result = "";
int count = mimeMultipart.getCount();
for (int i = 0; i < count; i++) {
BodyPart bodyPart = mimeMultipart.getBodyPart(i);
if (bodyPart.isMimeType("text/plain")) {
result = result + "\n" + bodyPart.getContent();
break; // without break same text appears twice in my tests
}
else if (bodyPart.isMimeType("text/html")) {
String html = (String) bodyPart.getContent();
result = result + "\n" + Jsoup.parse(html).text();//html;// + org.Jsoup.parse(html).text();
}
else if (bodyPart.getContent() instanceof MimeMultipart) {
result = result + getTextFromMimeMultipart((MimeMultipart)bodyPart.getContent());
}
}
return result;
}

Apply a filter on FirebaseRecyclerAdapter

I am using FirebaseRecyclerAdapter to display chat messages.
private void attachRecyclerViewAdapter() {
lastFifty = mChatRef.limitToLast(50).;
mRecyclerViewAdapter = new FirebaseRecyclerAdapter<Chat, ChatHolder>(
Chat.class, R.layout.message, ChatHolder.class, lastFifty) {
#Override
public void populateViewHolder(ChatHolder chatView, Chat chat, int position) {
chatView.setName(chat.getName());
chatView.setText(chat.getText());
chatView.setTimeLocation(chat.getTime());
FirebaseUser currentUser = mAuth.getCurrentUser();
if (currentUser != null && chat.getUid().equals(currentUser.getUid())) {
chatView.setIsSender(true);
} else {
chatView.setIsSender(false);
}
}
};
I have a list that contains list of specific users. I would like to apply filter to see only messages from those specific users. What should I do ?
You can create messages with user id pair nodes. For example messages->'uid1-uid2'->...
To prevent which is first order uids alphatecially as the following messageId generator code does:
public static String getMessageId(String id1, String id2){
String messageId;
if(id1.compareTo(id2) < 0){
messageId = id1 + "-" + id2;
}else if(id1.compareTo(id2) > 0) {
messageId = id2 + "-" + id1;
}else{
messageId = id1;
}
return messageId;
}
When you want to see the chat history between a user and urself, obtain the user's id and generate messageId = getMessageId(id1, id2); or messageId = getMessageId(id2, id1); gives the same result since the order doesn't affect the result.
Then call the messages from the node messages -> messageId
P.S. you should restructure your messages node as i describe.
EDIT
You can convert messageId to md5 equivalent to save chars.
just change
return messageId;
to
return md5(messageId);
where md5 is:
public static String md5(final String s) {
try {
// Create MD5 Hash
MessageDigest digest = java.security.MessageDigest
.getInstance("MD5");
digest.update(s.getBytes());
byte messageDigest[] = digest.digest();
// Create Hex String
StringBuffer hexString = new StringBuffer();
for (int i = 0; i < messageDigest.length; i++) {
String h = Integer.toHexString(0xFF & messageDigest[i]);
while (h.length() < 2)
h = "0" + h;
hexString.append(h);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return "";
}

Why the connection to Gmail server is change my mails situation from "Unread" to "Read"?

I can receive my mails from Gmail Server and show theme on a listview on my Android Project. Yesterday, I started to change my mail's imageview for "unread" or "read" situation. But, then I realized that when my application connected to Gmail Server and receiving my mails, the RECENT mails become SEEN mails. And because of this reason I can't set my imageview's for unread mails.
I mean; I want to receive my mails from Gmail Server without changing their situations on Gmail Server. I want to receive them 3 unread and 4 read as in server.
What should I do for doing that?
My connection code sample is:
public Message[] ConnectionToServer(String email, String password)
throws Exception
{
Properties props = System.getProperties();
props.setProperty("mail.imaps.partialfetch", "false");
URLName server = new URLName("imaps://" + email + ":" + password + "#imap.gmail.com/INBOX");
Session session = Session.getDefaultInstance(props, null);
folder = session.getFolder(server);
if (folder == null)
{
System.exit(0);
}
folder.open(Folder.READ_WRITE);
messages = folder.getMessages();
for (int i = messages.length - 1; i >= 23; i--)
{
Part p = messages[i];
subject = messages[i].getSubject();
if (messages[i].isSet(Flags.Flag.RECENT)) {
isSet = true;
System.out.println("Recent");
isSetlist.add(String.valueOf(isSet));
}
if (messages[i].isSet(Flags.Flag.SEEN))
{
isSet = false;
System.out.println("Read");
isSetlist.add(String.valueOf(isSet));
}
else
{
isSet = true;
System.out.println("Recent");
isSetlist.add(String.valueOf(isSet));
}
body = getText(p);
list.add(body);
}
return (Message[]) messages;
}
I am using getContent in getText() method
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));
if (s != null)
return String.valueOf(s);
}
}
return null;
}
Access the message flags (to determine if the message is SEEN or not) before you access the content. Accessing the content normally sets the SEEN flag.
Try opening the folder in READONLY mode if you don't need to do any modifications - alternately, try preserving the Message flags across getContent calls.

part.isMimeType == "Multipart/*" Does it mean it has an attachment?

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.

Android - decryption issues with AES encrypted string in QR code

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.

Categories

Resources