diff options
Diffstat (limited to '')
-rw-r--r-- | res/layout/activity_publish_profile_picture.xml | 87 | ||||
-rw-r--r-- | res/layout/manage_accounts.xml | 3 | ||||
-rw-r--r-- | res/menu/manageaccounts_context.xml | 4 | ||||
-rw-r--r-- | res/values/strings.xml | 3 | ||||
-rw-r--r-- | src/eu/siacs/conversations/persistance/FileBackend.java | 105 | ||||
-rw-r--r-- | src/eu/siacs/conversations/services/XmppConnectionService.java | 13 | ||||
-rw-r--r-- | src/eu/siacs/conversations/ui/ManageAccountActivity.java | 2 | ||||
-rw-r--r-- | src/eu/siacs/conversations/ui/PublishProfilePictureActivity.java | 101 | ||||
-rw-r--r-- | src/eu/siacs/conversations/utils/PhoneHelper.java | 2 | ||||
-rw-r--r-- | src/eu/siacs/conversations/xmpp/pep/Avatar.java | 23 |
10 files changed, 340 insertions, 3 deletions
diff --git a/res/layout/activity_publish_profile_picture.xml b/res/layout/activity_publish_profile_picture.xml new file mode 100644 index 00000000..ff7f6fa2 --- /dev/null +++ b/res/layout/activity_publish_profile_picture.xml @@ -0,0 +1,87 @@ +<?xml version="1.0" encoding="utf-8"?> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@color/primarybackground" > + + <LinearLayout + android:id="@+id/account_image_wrapper" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentTop="true" + android:layout_centerHorizontal="true" + android:layout_marginBottom="8dp" + android:layout_marginTop="24dp" + android:background="@drawable/message_border" > + + <ImageView + android:id="@+id/account_image" + android:layout_width="194dp" + android:layout_height="194dp" /> + </LinearLayout> + + <TextView + android:id="@+id/hint" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/account_image_wrapper" + android:layout_centerHorizontal="true" + android:text="@string/touch_to_choose_picture" + android:textColor="@color/secondarytext" /> + + <LinearLayout + android:id="@+id/button_bar" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentBottom="true" + android:layout_alignParentLeft="true" + android:layout_alignParentRight="true" > + + <Button + android:id="@+id/cancel_button" + style="?android:attr/borderlessButtonStyle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="@string/cancel" + android:textColor="@color/primarytext" /> + + <View + android:layout_width="1dp" + android:layout_height="fill_parent" + android:layout_marginBottom="7dp" + android:layout_marginTop="7dp" + android:background="@color/divider" /> + + <Button + android:id="@+id/publish_button" + style="?android:attr/borderlessButtonStyle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:enabled="false" + android:text="@string/publish_avatar" + android:textColor="@color/secondarytext" /> + </LinearLayout> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="fill_parent" + android:layout_above="@+id/button_bar" + android:layout_below="@+id/hint" + android:layout_centerHorizontal="true" + android:gravity="center_vertical" + android:orientation="vertical" + android:paddingLeft="8dp" + android:paddingRight="8dp" > + + <TextView + android:id="@+id/explanation" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/publish_avatar_explanation" + android:textColor="@color/primarytext" + android:textSize="18sp" /> + </LinearLayout> + +</RelativeLayout>
\ No newline at end of file diff --git a/res/layout/manage_accounts.xml b/res/layout/manage_accounts.xml index a2a01bf1..71eb7572 100644 --- a/res/layout/manage_accounts.xml +++ b/res/layout/manage_accounts.xml @@ -2,7 +2,8 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="fill_parent" - android:layout_height="fill_parent" > + android:layout_height="fill_parent" + android:background="@color/primarybackground"> <ListView android:id="@+id/account_list" diff --git a/res/menu/manageaccounts_context.xml b/res/menu/manageaccounts_context.xml index 04ecc25f..774c0afa 100644 --- a/res/menu/manageaccounts_context.xml +++ b/res/menu/manageaccounts_context.xml @@ -25,6 +25,10 @@ android:showAsAction="never" android:title="@string/announce_pgp"/> <item + android:id="@+id/mgmt_account_publish_avatar" + android:showAsAction="never" + android:title="@string/publish_avatar"/> + <item android:id="@+id/mgmt_otr_key" android:showAsAction="never" android:title="@string/show_otr_key"/> diff --git a/res/values/strings.xml b/res/values/strings.xml index c636be79..0b999482 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -268,4 +268,7 @@ <string name="contact_added_you">Contact added you to contact list</string> <string name="add_back">Add back</string> <string name="contact_has_read_up_to_this_point">%s has read up to this point</string> + <string name="publish_avatar">Publish avatar</string> + <string name="touch_to_choose_picture">Touch avatar to select picture from gallary</string> + <string name="publish_avatar_explanation">Publish avatar for <b>%s</b>. Everyone subscribed to your presence updates will also be able to see this picture.</string> </resources> diff --git a/src/eu/siacs/conversations/persistance/FileBackend.java b/src/eu/siacs/conversations/persistance/FileBackend.java index 1ee68a27..8895a2c8 100644 --- a/src/eu/siacs/conversations/persistance/FileBackend.java +++ b/src/eu/siacs/conversations/persistance/FileBackend.java @@ -1,5 +1,7 @@ package eu.siacs.conversations.persistance; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -7,19 +9,28 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.security.DigestOutputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.graphics.Canvas; import android.graphics.Matrix; +import android.graphics.RectF; import android.media.ExifInterface; import android.net.Uri; +import android.util.Base64; +import android.util.Base64OutputStream; import android.util.Log; import android.util.LruCache; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Message; +import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.xmpp.jingle.JingleFile; +import eu.siacs.conversations.xmpp.pep.Avatar; public class FileBackend { @@ -215,10 +226,102 @@ public class FileBackend { public File getIncomingFile() { return new File(context.getFilesDir().getAbsolutePath() + "/incoming"); } - + public Uri getIncomingUri() { return Uri.parse(context.getFilesDir().getAbsolutePath() + "/incoming"); } + + public Avatar getPepAvatar(Uri image, int size, Bitmap.CompressFormat format) { + try { + Avatar avatar = new Avatar(); + Bitmap bm = cropCenterSquare(image, size); + ByteArrayOutputStream mByteArrayOutputStream = new ByteArrayOutputStream(); + Base64OutputStream mBase64OutputSttream = new Base64OutputStream(mByteArrayOutputStream, Base64.DEFAULT); + MessageDigest digest = MessageDigest.getInstance("SHA-1"); + DigestOutputStream mDigestOutputStream = new DigestOutputStream(mBase64OutputSttream, digest); + bm.compress(format, 75, mDigestOutputStream); + avatar.sha1sum = CryptoHelper.bytesToHex(digest.digest()); + avatar.image = new String(mByteArrayOutputStream.toByteArray()); + return avatar; + } catch (NoSuchAlgorithmException e) { + return null; + } + } + + public void save(Avatar avatar) { + String path = context.getFilesDir().getAbsolutePath() + "/avatars/"; + File file = new File(path+"/"+avatar.getFilename()); + file.getParentFile().mkdirs(); + Log.d("xmppService",file.getAbsolutePath()); + try { + file.createNewFile(); + FileOutputStream mFileOutputStream = new FileOutputStream(file); + MessageDigest digest = MessageDigest.getInstance("SHA-1"); + DigestOutputStream mDigestOutputStream = new DigestOutputStream(mFileOutputStream, digest); + mDigestOutputStream.write(avatar.getImageAsBytes()); + mDigestOutputStream.flush(); + mDigestOutputStream.close(); + Log.d("xmppService","sha1sum after write: "+CryptoHelper.bytesToHex(digest.digest())); + } catch (FileNotFoundException e) { + + } catch (IOException e) { + Log.d("xmppService",e.getMessage()); + } catch (NoSuchAlgorithmException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + public Bitmap cropCenterSquare(Uri image, int size) { + try { + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inSampleSize = calcSampleSize(image, size); + InputStream is = context.getContentResolver() + .openInputStream(image); + Bitmap input = BitmapFactory.decodeStream(is, null, options); + int w = input.getWidth(); + int h = input.getHeight(); + + float scale = Math.max((float) size / h, (float) size / w); + + float outWidth = scale * w; + float outHeight = scale * h; + float left = (size - outWidth) / 2; + float top = (size - outHeight) / 2; + RectF target = new RectF(left, top, left + outWidth, top + + outHeight); + + Bitmap output = Bitmap.createBitmap(size, size, input.getConfig()); + Canvas canvas = new Canvas(output); + canvas.drawBitmap(input, null, target, null); + return output; + } catch (FileNotFoundException e) { + return null; + } + } + + private int calcSampleSize(Uri image, int size) + throws FileNotFoundException { + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeStream(context.getContentResolver() + .openInputStream(image), null, options); + int height = options.outHeight; + int width = options.outWidth; + int inSampleSize = 1; + + if (height > size || width > size) { + int halfHeight = height / 2; + int halfWidth = width / 2; + + while ((halfHeight / inSampleSize) > size + && (halfWidth / inSampleSize) > size) { + inSampleSize *= 2; + } + } + return inSampleSize; + + } public class ImageCopyException extends Exception { private static final long serialVersionUID = -1010013599132881427L; diff --git a/src/eu/siacs/conversations/services/XmppConnectionService.java b/src/eu/siacs/conversations/services/XmppConnectionService.java index 3f9b3bfb..4ba0954f 100644 --- a/src/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/eu/siacs/conversations/services/XmppConnectionService.java @@ -53,6 +53,7 @@ import eu.siacs.conversations.xmpp.XmppConnection; import eu.siacs.conversations.xmpp.jingle.JingleConnectionManager; import eu.siacs.conversations.xmpp.jingle.OnJinglePacketReceived; import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket; +import eu.siacs.conversations.xmpp.pep.Avatar; import eu.siacs.conversations.xmpp.stanzas.IqPacket; import eu.siacs.conversations.xmpp.stanzas.MessagePacket; import eu.siacs.conversations.xmpp.stanzas.PresencePacket; @@ -64,6 +65,7 @@ import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.database.ContentObserver; +import android.graphics.Bitmap; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; @@ -1183,6 +1185,17 @@ public class XmppConnectionService extends Service { } } + + public void pushAvatar(Account account, Uri image) { + Avatar avatar = getFileBackend().getPepAvatar(image, 192, Bitmap.CompressFormat.WEBP); + if (avatar!=null) { + Log.d(LOGTAG,avatar.sha1sum); + Log.d(LOGTAG,avatar.image); + avatar.type = "image/webp"; + getFileBackend().save(avatar); + } + } + public void deleteContactOnServer(Contact contact) { contact.resetOption(Contact.Options.PREEMPTIVE_GRANT); contact.resetOption(Contact.Options.DIRTY_PUSH); diff --git a/src/eu/siacs/conversations/ui/ManageAccountActivity.java b/src/eu/siacs/conversations/ui/ManageAccountActivity.java index 15f904c3..105275ce 100644 --- a/src/eu/siacs/conversations/ui/ManageAccountActivity.java +++ b/src/eu/siacs/conversations/ui/ManageAccountActivity.java @@ -99,6 +99,8 @@ public class ManageAccountActivity extends XmppActivity { xmppConnectionService .updateAccount(selectedAccountForActionMode); mode.finish(); + } else if (item.getItemId() == R.id.mgmt_account_publish_avatar) { + startActivity(new Intent(getApplicationContext(), PublishProfilePictureActivity.class)); } else if (item.getItemId() == R.id.mgmt_account_delete) { AlertDialog.Builder builder = new AlertDialog.Builder(activity); builder.setTitle(getString(R.string.mgmt_account_are_you_sure)); diff --git a/src/eu/siacs/conversations/ui/PublishProfilePictureActivity.java b/src/eu/siacs/conversations/ui/PublishProfilePictureActivity.java new file mode 100644 index 00000000..0e369a97 --- /dev/null +++ b/src/eu/siacs/conversations/ui/PublishProfilePictureActivity.java @@ -0,0 +1,101 @@ +package eu.siacs.conversations.ui; + +import android.content.Intent; +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.TextView; +import eu.siacs.conversations.R; +import eu.siacs.conversations.utils.PhoneHelper; + +public class PublishProfilePictureActivity extends XmppActivity { + + private static final int REQUEST_CHOOSE_FILE = 0xac23; + + private ImageView avatar; + private TextView explanation; + private Button cancelButton; + private Button publishButton; + + private Uri avatarUri; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_publish_profile_picture); + this.avatar = (ImageView) findViewById(R.id.account_image); + this.explanation = (TextView) findViewById(R.id.explanation); + this.cancelButton = (Button) findViewById(R.id.cancel_button); + this.publishButton = (Button) findViewById(R.id.publish_button); + this.publishButton.setOnClickListener(new OnClickListener() { + + @Override + public void onClick(View v) { + if (avatarUri!=null) { + xmppConnectionService.pushAvatar(null, avatarUri); + finish(); + } + } + }); + this.cancelButton.setOnClickListener(new OnClickListener() { + + @Override + public void onClick(View v) { + finish(); + } + }); + this.avatar.setOnClickListener(new OnClickListener() { + + @Override + public void onClick(View v) { + Intent attachFileIntent = new Intent(); + attachFileIntent.setType("image/*"); + attachFileIntent.setAction(Intent.ACTION_GET_CONTENT); + Intent chooser = Intent.createChooser(attachFileIntent, + getString(R.string.attach_file)); + startActivityForResult(chooser, REQUEST_CHOOSE_FILE); + } + }); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, + final Intent data) { + super.onActivityResult(requestCode, resultCode, data); + Log.d("xmppService","on activity result"); + if (resultCode == RESULT_OK) { + if (requestCode == REQUEST_CHOOSE_FILE) { + Log.d("xmppService","bla"); + this.avatarUri = data.getData(); + } + } + } + + @Override + protected void onBackendConnected() { + Log.d("xmppService","on backend connected"); + if (this.avatarUri == null) { + avatarUri = PhoneHelper.getSefliUri(getApplicationContext()); + } + loadImageIntoPreview(avatarUri); + String explainText = getString(R.string.publish_avatar_explanation,"daniel@gultsch.de"); + this.explanation.setText(explainText); + } + + protected void loadImageIntoPreview(Uri uri) { + Bitmap bm = xmppConnectionService.getFileBackend().cropCenterSquare(uri, 384); + this.avatar.setImageBitmap(bm); + enablePublishButton(); + } + + protected void enablePublishButton() { + this.publishButton.setEnabled(true); + this.publishButton.setTextColor(getPrimaryTextColor()); + } + +} diff --git a/src/eu/siacs/conversations/utils/PhoneHelper.java b/src/eu/siacs/conversations/utils/PhoneHelper.java index 67623ea3..63c17761 100644 --- a/src/eu/siacs/conversations/utils/PhoneHelper.java +++ b/src/eu/siacs/conversations/utils/PhoneHelper.java @@ -70,7 +70,7 @@ public class PhoneHelper { public static Uri getSefliUri(Context context) { String[] mProjection = new String[] { Profile._ID, - Profile.PHOTO_THUMBNAIL_URI }; + Profile.PHOTO_URI }; Cursor mProfileCursor = context.getContentResolver().query( Profile.CONTENT_URI, mProjection, null, null, null); diff --git a/src/eu/siacs/conversations/xmpp/pep/Avatar.java b/src/eu/siacs/conversations/xmpp/pep/Avatar.java new file mode 100644 index 00000000..196c876c --- /dev/null +++ b/src/eu/siacs/conversations/xmpp/pep/Avatar.java @@ -0,0 +1,23 @@ +package eu.siacs.conversations.xmpp.pep; + +import android.util.Base64; + +public class Avatar { + public String type; + public String sha1sum; + public String image; + public byte[] getImageAsBytes() { + return Base64.decode(image, Base64.DEFAULT); + } + public String getFilename() { + if (type==null) { + return sha1sum; + } else if (type.equalsIgnoreCase("image/webp")) { + return sha1sum+".webp"; + } else if (type.equalsIgnoreCase("image/png")) { + return sha1sum+".png"; + } else { + return sha1sum; + } + } +} |