aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoriNPUTmice <daniel@gultsch.de>2014-08-03 20:28:13 +0200
committeriNPUTmice <daniel@gultsch.de>2014-08-03 20:28:13 +0200
commitdaab16bdef2f24f57d117efaa0144c578c883a6b (patch)
treefbea06de6eecf760161b95fe13b149c118acdef5
parent88d1bd356c9408df6f12829dcd427b92af1e3969 (diff)
activity for future avatar publications. not working yet
-rw-r--r--res/layout/activity_publish_profile_picture.xml87
-rw-r--r--res/layout/manage_accounts.xml3
-rw-r--r--res/menu/manageaccounts_context.xml4
-rw-r--r--res/values/strings.xml3
-rw-r--r--src/eu/siacs/conversations/persistance/FileBackend.java105
-rw-r--r--src/eu/siacs/conversations/services/XmppConnectionService.java13
-rw-r--r--src/eu/siacs/conversations/ui/ManageAccountActivity.java2
-rw-r--r--src/eu/siacs/conversations/ui/PublishProfilePictureActivity.java101
-rw-r--r--src/eu/siacs/conversations/utils/PhoneHelper.java2
-rw-r--r--src/eu/siacs/conversations/xmpp/pep/Avatar.java23
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;
+ }
+ }
+}