From 2041ab9ff50390c939a41af1c2b3703f105b2e98 Mon Sep 17 00:00:00 2001 From: Aline Bonnet <aline.bonnet@savoirfairelinux.com> Date: Wed, 5 Oct 2016 11:51:58 -0400 Subject: [PATCH] profile : edit photo and name This commit allows the user to edit his name and photo. An AlertDialog pops up when you click on the name or photo in the left menu. - VCardUtils implements the methods to load and save the vcard for the local profile - MenuHeaderView implements the methods to change the name or photo If a VCard does not exist, loadFromDisk saves a default profile. Change-Id: I810607d02fbec115329a9e159fe44ebec0736618 Tuleap: #1074 --- .../cx/ring/adapters/ContactDetailsTask.java | 85 ++++++- .../java/cx/ring/client/HomeActivity.java | 33 +++ .../java/cx/ring/fragments/CallFragment.java | 3 +- .../main/java/cx/ring/model/CallContact.java | 40 +--- .../cx/ring/service/CallManagerCallBack.java | 2 +- .../main/java/cx/ring/utils/VCardUtils.java | 92 ++++++-- .../java/cx/ring/views/MenuHeaderView.java | 207 ++++++++++++++---- .../res/drawable-hdpi/ic_insert_photo.png | Bin 0 -> 425 bytes .../res/drawable-hdpi/ic_photo_camera.png | Bin 0 -> 750 bytes .../res/drawable-mdpi/ic_insert_photo.png | Bin 0 -> 298 bytes .../res/drawable-mdpi/ic_photo_camera.png | Bin 0 -> 479 bytes .../res/drawable-xhdpi/ic_insert_photo.png | Bin 0 -> 613 bytes .../res/drawable-xhdpi/ic_photo_camera.png | Bin 0 -> 1013 bytes .../res/drawable-xxhdpi/ic_insert_photo.png | Bin 0 -> 797 bytes .../res/drawable-xxhdpi/ic_photo_camera.png | Bin 0 -> 1699 bytes .../src/main/res/layout/dialog_profile.xml | 76 +++++++ .../src/main/res/layout/frag_menu_header.xml | 72 ++++-- .../app/src/main/res/values/strings.xml | 8 + 18 files changed, 500 insertions(+), 118 deletions(-) create mode 100644 ring-android/app/src/main/res/drawable-hdpi/ic_insert_photo.png create mode 100644 ring-android/app/src/main/res/drawable-hdpi/ic_photo_camera.png create mode 100644 ring-android/app/src/main/res/drawable-mdpi/ic_insert_photo.png create mode 100644 ring-android/app/src/main/res/drawable-mdpi/ic_photo_camera.png create mode 100644 ring-android/app/src/main/res/drawable-xhdpi/ic_insert_photo.png create mode 100644 ring-android/app/src/main/res/drawable-xhdpi/ic_photo_camera.png create mode 100644 ring-android/app/src/main/res/drawable-xxhdpi/ic_insert_photo.png create mode 100644 ring-android/app/src/main/res/drawable-xxhdpi/ic_photo_camera.png create mode 100644 ring-android/app/src/main/res/layout/dialog_profile.xml diff --git a/ring-android/app/src/main/java/cx/ring/adapters/ContactDetailsTask.java b/ring-android/app/src/main/java/cx/ring/adapters/ContactDetailsTask.java index 8246a98d0..392ec8707 100644 --- a/ring-android/app/src/main/java/cx/ring/adapters/ContactDetailsTask.java +++ b/ring-android/app/src/main/java/cx/ring/adapters/ContactDetailsTask.java @@ -25,15 +25,19 @@ import android.content.ContentResolver; import android.content.ContentUris; import android.content.Context; import android.content.res.Resources; +import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.graphics.Matrix; import android.net.Uri; import android.provider.ContactsContract; +import android.provider.MediaStore; import android.text.TextUtils; import android.util.Log; import android.widget.ImageView; import android.widget.TextView; +import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -58,6 +62,13 @@ public class ContactDetailsTask implements Runnable { private final int mViewWidth, mViewHeight; + private final static String MIME_TYPE_JPG = "image/jpg"; + private final static String MIME_TYPE_JPEG = "image/jpeg"; + private final static String MIME_TYPE_PNG = "image/png"; + private final static int ORIENTATION_LEFT = 270; + private final static int ORIENTATION_RIGHT = 90; + private final static int MAX_IMAGE_DIMENSION = 70; + public void addCallback(DetailsLoadedCallback cb) { synchronized (mCallbacks) { if (cb == null) { @@ -127,7 +138,8 @@ public class ContactDetailsTask implements Runnable { if (!mContact.getPhones().isEmpty()) { String username = mContact.getPhones().get(0).getNumber().host; - vcard = VCardUtils.loadFromDisk(username + ".vcf", mContext); + Log.d(TAG, "getPhones not empty. Username : " + username); + vcard = VCardUtils.loadPeerProfileFromDisk(username + ".vcf", mContext); if (vcard != null && vcard.getFormattedName() != null) { if (!TextUtils.isEmpty(vcard.getFormattedName().getValue())) { @@ -216,4 +228,75 @@ public class ContactDetailsTask implements Runnable { return inSampleSize; } + + public static Bitmap loadProfilePhotoFromUri(Context context, Uri uriImage) { + try { + InputStream is = context.getContentResolver().openInputStream(uriImage); + BitmapFactory.Options dbo = new BitmapFactory.Options(); + dbo.inJustDecodeBounds = true; + BitmapFactory.decodeStream(is, null, dbo); + is.close(); + + int rotatedWidth, rotatedHeight; + int orientation = getOrientation(context, uriImage); + + if (orientation == ORIENTATION_LEFT || orientation == ORIENTATION_RIGHT) { + rotatedWidth = dbo.outHeight; + rotatedHeight = dbo.outWidth; + } else { + rotatedWidth = dbo.outWidth; + rotatedHeight = dbo.outHeight; + } + + Bitmap srcBitmap; + is = context.getContentResolver().openInputStream(uriImage); + if (rotatedWidth > MAX_IMAGE_DIMENSION || rotatedHeight > MAX_IMAGE_DIMENSION) { + float widthRatio = ((float) rotatedWidth) / ((float) MAX_IMAGE_DIMENSION); + float heightRatio = ((float) rotatedHeight) / ((float) MAX_IMAGE_DIMENSION); + float maxRatio = Math.max(widthRatio, heightRatio); + + // Create the bitmap from file + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inSampleSize = (int) maxRatio; + srcBitmap = BitmapFactory.decodeStream(is, null, options); + } else { + srcBitmap = BitmapFactory.decodeStream(is); + } + is.close(); + + if (orientation > 0) { + Matrix matrix = new Matrix(); + matrix.postRotate(orientation); + + srcBitmap = Bitmap.createBitmap(srcBitmap, 0, 0, srcBitmap.getWidth(), + srcBitmap.getHeight(), matrix, true); + } + + String type = context.getContentResolver().getType(uriImage); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + if (type.equals(MIME_TYPE_PNG)) { + srcBitmap.compress(Bitmap.CompressFormat.PNG, 100, baos); + } else if (type.equals(MIME_TYPE_JPG) || type.equals(MIME_TYPE_JPEG)) { + srcBitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos); + } + byte[] bMapArray = baos.toByteArray(); + baos.close(); + return BitmapFactory.decodeByteArray(bMapArray, 0, bMapArray.length); + } catch (Exception e) { + Log.e(TAG, "Error while loading photo from URI", e); + return null; + } + } + + public static int getOrientation(Context context, Uri photoUri) { + Cursor cursor = context.getContentResolver().query(photoUri, + new String[]{MediaStore.Images.ImageColumns.ORIENTATION}, null, null, null); + + if (cursor.getCount() != 1) { + return -1; + } + + cursor.moveToFirst(); + return cursor.getInt(0); + } } diff --git a/ring-android/app/src/main/java/cx/ring/client/HomeActivity.java b/ring-android/app/src/main/java/cx/ring/client/HomeActivity.java index c24b15e02..904fafaa3 100644 --- a/ring-android/app/src/main/java/cx/ring/client/HomeActivity.java +++ b/ring-android/app/src/main/java/cx/ring/client/HomeActivity.java @@ -20,6 +20,7 @@ package cx.ring.client; import android.Manifest; +import android.app.Activity; import android.app.AlertDialog; import android.app.FragmentManager; import android.app.FragmentTransaction; @@ -40,6 +41,7 @@ import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.preference.PreferenceManager; +import android.provider.MediaStore; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.design.widget.FloatingActionButton; @@ -96,6 +98,10 @@ public class HomeActivity extends AppCompatActivity implements LocalService.Call public static final int REQUEST_CODE_PREFERENCES = 1; public static final int REQUEST_CODE_CALL = 3; public static final int REQUEST_CODE_CONVERSATION = 4; + public static final int REQUEST_CODE_PHOTO = 5; + public static final int REQUEST_CODE_GALLERY = 6; + public static final int REQUEST_PERMISSION_CAMERA = 113; + public static final int REQUEST_PERMISSION_READ_STORAGE = 114; private static final String HOME_TAG = "Home"; private static final String ACCOUNTS_TAG = "Accounts"; @@ -371,6 +377,22 @@ public class HomeActivity extends AppCompatActivity implements LocalService.Call break; } + case REQUEST_PERMISSION_READ_STORAGE: + if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); + startActivityForResult(intent, REQUEST_CODE_GALLERY); + } else { + return; + } + break; + case REQUEST_PERMISSION_CAMERA: + if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) { + Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + startActivityForResult(intent, REQUEST_CODE_PHOTO); + } else { + return; + } + break; } } @@ -555,6 +577,17 @@ public class HomeActivity extends AppCompatActivity implements LocalService.Call Log.w(TAG, "Call Failed"); } break; + case REQUEST_CODE_PHOTO: + if(resultCode == RESULT_OK && data != null){ + fMenuHead.updatePhoto((Bitmap) data.getExtras().get("data")); + + } + break; + case REQUEST_CODE_GALLERY: + if(resultCode == RESULT_OK && data != null) { + fMenuHead.updatePhoto(data.getData()); + } + break; } } diff --git a/ring-android/app/src/main/java/cx/ring/fragments/CallFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/CallFragment.java index ed152fd44..f7de4e6e6 100644 --- a/ring-android/app/src/main/java/cx/ring/fragments/CallFragment.java +++ b/ring-android/app/src/main/java/cx/ring/fragments/CallFragment.java @@ -937,7 +937,8 @@ public class CallFragment extends Fragment implements CallInterface, ContactDeta VCard vcard; String username = participant.getNumberUri().username; - vcard = VCardUtils.loadFromDisk(username + ".vcf", context); + Log.d(TAG, "username " + username); + vcard = VCardUtils.loadPeerProfileFromDisk(username + ".vcf", context); if (vcard == null) { Log.d(TAG, "No vcard."); setDefaultPhoto(); diff --git a/ring-android/app/src/main/java/cx/ring/model/CallContact.java b/ring-android/app/src/main/java/cx/ring/model/CallContact.java index 004bc09ea..a3de0fad5 100644 --- a/ring-android/app/src/main/java/cx/ring/model/CallContact.java +++ b/ring-android/app/src/main/java/cx/ring/model/CallContact.java @@ -44,6 +44,8 @@ import java.lang.ref.WeakReference; import java.util.ArrayList; import cx.ring.R; +import cx.ring.utils.VCardUtils; +import ezvcard.VCard; public class CallContact implements Parcelable { static final String TAG = CallContact.class.getSimpleName(); @@ -100,44 +102,6 @@ public class CallContact implements Parcelable { return new CallContact(UNKNOWN_ID, null, to, 0, phones, "", false); } - public static CallContact buildUserContact(Context c) { - CallContact result = null; - try { - if (null != c) { - //~ Checking the state of the READ_CONTACTS permission - boolean hasReadContactsPermission = ContextCompat.checkSelfPermission(c, - Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED; - if (hasReadContactsPermission) { - Cursor mProfileCursor = c.getContentResolver().query(Profile.CONTENT_URI, PROFILE_PROJECTION, null, null, null); - if (mProfileCursor != null) { - if (mProfileCursor.moveToFirst()) { - String key = mProfileCursor.getString(mProfileCursor.getColumnIndex(Profile.LOOKUP_KEY)); - String displayName = mProfileCursor.getString(mProfileCursor.getColumnIndex(Profile.DISPLAY_NAME_PRIMARY)); - - if (displayName == null || displayName.isEmpty()) - displayName = c.getResources().getString(R.string.me); - result = new CallContact(mProfileCursor.getLong(mProfileCursor.getColumnIndex(Profile._ID)), key, displayName, - mProfileCursor.getLong(mProfileCursor.getColumnIndex(Profile.PHOTO_ID)), new ArrayList<Phone>(), "", true); - } - mProfileCursor.close(); - } - } else { - Log.d(TAG, "READ_CONTACTS permission is not granted."); - } - } - } catch (Exception e) { - Log.w(TAG, e); - } - - //~ Returns the contact if not null - if (null != result) { - return result; - } - //~ Or returning a default one - String displayName = (null != c) ? c.getResources().getString(R.string.me) : "Me"; - return new CallContact(UNKNOWN_ID, null, displayName, 0, new ArrayList<Phone>(), "", true); - } - public void setContactInfos(String k, String displayName, long photo_id) { key = k; mDisplayName = displayName; diff --git a/ring-android/app/src/main/java/cx/ring/service/CallManagerCallBack.java b/ring-android/app/src/main/java/cx/ring/service/CallManagerCallBack.java index 2845ee815..53761489c 100644 --- a/ring-android/app/src/main/java/cx/ring/service/CallManagerCallBack.java +++ b/ring-android/app/src/main/java/cx/ring/service/CallManagerCallBack.java @@ -99,7 +99,7 @@ public class CallManagerCallBack extends Callback { String splitFrom[] = from.split("@"); if (splitFrom.length == 2) { String filename = splitFrom[0] + ".vcf"; - VCardUtils.saveToDisk(mProfileChunk.getCompleteProfile(), + VCardUtils.savePeerProfileToDisk(mProfileChunk.getCompleteProfile(), filename, this.mService.getApplicationContext()); diff --git a/ring-android/app/src/main/java/cx/ring/utils/VCardUtils.java b/ring-android/app/src/main/java/cx/ring/utils/VCardUtils.java index 8182b2805..066670abe 100644 --- a/ring-android/app/src/main/java/cx/ring/utils/VCardUtils.java +++ b/ring-android/app/src/main/java/cx/ring/utils/VCardUtils.java @@ -23,15 +23,19 @@ package cx.ring.utils; import android.content.Context; import android.support.annotation.Nullable; import android.text.TextUtils; +import android.util.Log; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.HashMap; +import cx.ring.R; import cx.ring.service.StringMap; +import ezvcard.Ezvcard; import ezvcard.VCard; -import ezvcard.io.text.VCardReader; +import ezvcard.VCardVersion; +import ezvcard.property.FormattedName; public class VCardUtils { public static final String TAG = VCardUtils.class.getSimpleName(); @@ -70,56 +74,101 @@ public class VCardUtils { return messageKeyValue; } + public static void savePeerProfileToDisk(String vcard, String filename, Context context) { + String path = peerProfilePath(context); + saveToDisk(vcard, filename, path, context); + } + + public static void saveLocalProfileToDisk(String vcard, Context context) { + String path = localProfilePath(context); + File vcardPath = new File(path); + String filename; + if (vcardPath.exists() && vcardPath.listFiles().length > 0) { + filename = vcardPath.listFiles()[0].getName(); + } else { + filename = String.valueOf(System.currentTimeMillis()) + ".vcf"; + } + saveToDisk(vcard, filename, path, context); + } + /** * Saves a vcard string to an internal new vcf file. * * @param vcard the string to save * @param filename the filename of the vcf + * @param path the path of the vcf * @param context the context used to open streams. */ - public static void saveToDisk(String vcard, String filename, Context context) { + private static void saveToDisk(String vcard, String filename, String path, Context context) { if (TextUtils.isEmpty(vcard) || TextUtils.isEmpty(filename) || context == null) { return; } - String path = peerProfilePath(context); File peerProfilesFile = new File(path); - if (!peerProfilesFile.exists()) + if (!peerProfilesFile.exists()) { peerProfilesFile.mkdirs(); + } FileOutputStream outputStream; try { - outputStream = new FileOutputStream(path + "/" + filename); + File outputFile = new File(path + File.separator + filename); + outputStream = new FileOutputStream(outputFile); outputStream.write(vcard.getBytes()); outputStream.close(); + Log.d(TAG, "vcard in saveToDisk " + vcard); } catch (Exception e) { - e.printStackTrace(); + Log.e(TAG, "Error while saving VCard to disk", e); + } + } + + public static VCard loadPeerProfileFromDisk(@Nullable String filename, @Nullable Context context) { + String path = peerProfilePath(context) + File.separator + filename; + return loadFromDisk(path, context); + } + + public static VCard loadLocalProfileFromDisk(@Nullable Context context) { + VCard vcard = null; + String path = localProfilePath(context); + File vcardPath = new File(path); + if (vcardPath.exists()) { + File[] listvCard = vcardPath.listFiles(); + if (listvCard.length > 0) { + vcard = loadFromDisk(listvCard[0].toString(), context); + } } + + if (vcard == null) { + Log.d(TAG, "load default profile"); + vcard = setupDefaultProfile(context); + } + + return vcard; } /** * Loads the vcard file from the disk * - * @param filename the filename of the vcard - * @param context the contact used to open a fileinputstream + * @param path the filename of the vcard + * @param context the contact used to open a fileinputstream * @return the VCard or null */ @Nullable - public static VCard loadFromDisk(@Nullable String filename,@Nullable Context context) { + private static VCard loadFromDisk(@Nullable String path, @Nullable Context context) { try { - if (TextUtils.isEmpty(filename) || context == null) { + if (TextUtils.isEmpty(path) || context == null) { + Log.d(TAG, "Empty file or error with the context"); return null; } - String path = peerProfilePath(context); - File vcardPath = new File(path + "/" + filename); + File vcardPath = new File(path); if (!vcardPath.exists()) { + Log.d(TAG, "vcardPath not exist " + vcardPath); return null; } - VCardReader reader = new VCardReader(new File(path + "/" + filename)); - VCard vcard = reader.readNext(); - reader.close(); + VCard vcard = Ezvcard.parse(vcardPath).first(); + + Log.d(TAG, "vcard in loadFromDisk " + Ezvcard.write(vcard).go()); return vcard; } catch (IOException e) { - e.printStackTrace(); + Log.e(TAG, "Error while loading VCard from disk", e); return null; } } @@ -127,4 +176,15 @@ public class VCardUtils { private static String peerProfilePath(Context context) { return context.getFilesDir().getAbsolutePath() + File.separator + "peer_profiles"; } + + private static String localProfilePath(Context context) { + return context.getFilesDir().getAbsolutePath() + File.separator + "profiles"; + } + + private static VCard setupDefaultProfile(Context context) { + VCard vcard = new VCard(); + vcard.setFormattedName(new FormattedName(context.getString(R.string.unknown))); + saveLocalProfileToDisk(Ezvcard.write(vcard).version(VCardVersion.V2_1).go(), context); + return vcard; + } } diff --git a/ring-android/app/src/main/java/cx/ring/views/MenuHeaderView.java b/ring-android/app/src/main/java/cx/ring/views/MenuHeaderView.java index 67c0ad0ac..9bb916c42 100644 --- a/ring-android/app/src/main/java/cx/ring/views/MenuHeaderView.java +++ b/ring-android/app/src/main/java/cx/ring/views/MenuHeaderView.java @@ -19,45 +19,83 @@ */ package cx.ring.views; +import android.Manifest; +import android.app.Activity; +import android.app.AlertDialog; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; +import android.content.pm.PackageManager; import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.net.Uri; +import android.provider.MediaStore; +import android.support.annotation.NonNull; +import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat; +import android.support.v4.content.res.ResourcesCompat; import android.util.AttributeSet; import android.util.Log; import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemSelectedListener; import android.widget.Button; +import android.widget.EditText; import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.Spinner; import android.widget.TextView; +import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.List; +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; import cx.ring.R; import cx.ring.adapters.AccountSelectionAdapter; import cx.ring.adapters.ContactDetailsTask; import cx.ring.client.AccountWizard; import cx.ring.client.HomeActivity; -import cx.ring.model.CallContact; import cx.ring.model.account.Account; import cx.ring.service.LocalService; +import cx.ring.utils.CropImageUtils; +import cx.ring.utils.VCardUtils; +import ezvcard.Ezvcard; +import ezvcard.VCard; +import ezvcard.VCardVersion; +import ezvcard.parameter.ImageType; +import ezvcard.property.FormattedName; +import ezvcard.property.Photo; +import ezvcard.property.RawProperty; public class MenuHeaderView extends FrameLayout { private static final String TAG = MenuHeaderView.class.getSimpleName(); private AccountSelectionAdapter mAccountAdapter; - private Spinner mSpinnerAccounts; - private ImageButton mShareBtn; - private Button mNewAccountBtn; - private ImageButton mQrImage; - private ImageView mUserImage; - private TextView mUserName; - private CallContact mCurrentlyDisplayedUser; + @BindView(R.id.account_selection) + Spinner mSpinnerAccounts; + + @BindView(R.id.share_btn) + ImageButton mShareBtn; + + @BindView(R.id.addaccount_btn) + Button mNewAccountBtn; + + @BindView(R.id.qr_image) + ImageButton mQrImage; + + @BindView(R.id.user_photo) + ImageView mUserImage; + + @BindView(R.id.user_name) + TextView mUserName; + private ImageView mProfilePhoto; + private VCard mVCardProfile; public MenuHeaderView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); @@ -87,8 +125,9 @@ public class MenuHeaderView extends FrameLayout { if (!shareUri.isEmpty()) { Bitmap qrBitmap = HomeActivity.QRCodeFragment.encodeStringAsQrBitmap(shareUri, mQrImage.getWidth()); mQrImage.setImageBitmap(qrBitmap); - } else + } else { mQrImage.setImageBitmap(null); + } } @Override @@ -102,27 +141,36 @@ public class MenuHeaderView extends FrameLayout { public void updateUserView() { LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); + Log.d(TAG, "updateUserView"); if (null != inflater) { - boolean shouldUpdate = true; - CallContact user = CallContact.buildUserContact(inflater.getContext()); - if (null != this.mCurrentlyDisplayedUser && this.mCurrentlyDisplayedUser.equals(user)) { - shouldUpdate = false; - Log.d(TAG, "User did not change, not updating user view."); - } - if (shouldUpdate) { - this.mCurrentlyDisplayedUser = user; - new ContactDetailsTask(inflater.getContext(), mUserImage, user).run(); - mUserName.setText(user.getDisplayName()); - Log.d(TAG, "User did change, updating user view."); + mVCardProfile = VCardUtils.loadLocalProfileFromDisk(getContext()); + if (!mVCardProfile.getPhotos().isEmpty()) { + Photo tmp = mVCardProfile.getPhotos().get(0); + mUserImage.setImageBitmap(CropImageUtils.cropImageToCircle(tmp.getData())); + } else { + mUserImage.setImageDrawable(ResourcesCompat.getDrawable(getResources(), R.drawable.ic_contact_picture, null)); } + mUserName.setText(mVCardProfile.getFormattedName().getValue()); + Log.d(TAG, "User did change, updating user view."); } } + public void updatePhoto(Uri uriImage) { + Bitmap imageProfile = ContactDetailsTask.loadProfilePhotoFromUri(getContext(), uriImage); + updatePhoto(imageProfile); + } + + public void updatePhoto(Bitmap image) { + image = CropImageUtils.cropImageToCircle(image); + mProfilePhoto.setImageBitmap(image); + } + private void initViews() { - LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); + final LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); View inflatedView = inflater.inflate(R.layout.frag_menu_header, this); - mNewAccountBtn = (Button) inflatedView.findViewById(R.id.addaccount_btn); + ButterKnife.bind(this, inflatedView); + mNewAccountBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -132,27 +180,94 @@ public class MenuHeaderView extends FrameLayout { mAccountAdapter = new AccountSelectionAdapter(inflater.getContext(), new ArrayList<Account>()); - mShareBtn = (ImageButton) inflatedView.findViewById(R.id.share_btn); - mShareBtn.setOnClickListener(new View.OnClickListener() { + mSpinnerAccounts.setAdapter(mAccountAdapter); + + mVCardProfile = VCardUtils.loadLocalProfileFromDisk(getContext()); + + updateUserView(); + } + + @OnClick(R.id.profile_container) + public void profileContainerClicked() { + Log.d(TAG, "Click on the edit profile"); + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + builder.setTitle(R.string.profile); + + LayoutInflater inflater = ((Activity) getContext()).getLayoutInflater(); + ViewGroup view = (ViewGroup) inflater.inflate(R.layout.dialog_profile, null); + + final EditText editText = (EditText) view.findViewById(R.id.user_name); + editText.setText(mUserName.getText()); + mProfilePhoto = (ImageView) view.findViewById(R.id.profile_photo); + mProfilePhoto.setImageDrawable(mUserImage.getDrawable()); + + ImageButton cameraView = (ImageButton) view.findViewById(R.id.camera); + cameraView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - Account account = mAccountAdapter.getSelectedAccount(); - String shareUri = account.getShareURI(); - Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND); - sharingIntent.setType("text/plain"); - String shareBody = getContext().getString(R.string.account_share_body, shareUri); - sharingIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, getContext().getString(R.string.account_contact_me)); - sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, shareBody); - getContext().startActivity(Intent.createChooser(sharingIntent, getContext().getText(R.string.share_via))); + boolean hasPermission = ContextCompat.checkSelfPermission(getContext(), Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED && + ContextCompat.checkSelfPermission(getContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; + if (hasPermission) { + Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + ((Activity) getContext()).startActivityForResult(intent, HomeActivity.REQUEST_CODE_PHOTO); + } else { + ActivityCompat.requestPermissions((Activity) getContext(), + new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE}, + HomeActivity.REQUEST_PERMISSION_CAMERA); + } } }); - mQrImage = (ImageButton) inflatedView.findViewById(R.id.qr_image); - mSpinnerAccounts = (Spinner) inflatedView.findViewById(R.id.account_selection); - mSpinnerAccounts.setAdapter(mAccountAdapter); - mUserImage = (ImageView) inflatedView.findViewById(R.id.user_photo); - mUserName = (TextView) inflatedView.findViewById(R.id.user_name); - this.updateUserView(); + ImageButton gallery = (ImageButton) view.findViewById(R.id.gallery); + gallery.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + boolean hasPermission = ContextCompat.checkSelfPermission(getContext(), Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; + if (hasPermission) { + Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); + ((Activity) getContext()).startActivityForResult(intent, HomeActivity.REQUEST_CODE_GALLERY); + } else { + ActivityCompat.requestPermissions((Activity) getContext(), + new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, + HomeActivity.REQUEST_PERMISSION_READ_STORAGE); + } + } + }); + + builder.setView(view); + + builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.cancel(); + } + }); + + builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + String username = editText.getText().toString().trim(); + if (username.isEmpty()) { + username = getContext().getString(R.string.unknown); + } + mVCardProfile.setFormattedName(new FormattedName(username)); + + if (mProfilePhoto.getDrawable() != ResourcesCompat.getDrawable(getResources(), R.drawable.ic_contact_picture, null)) { + Bitmap bmp = ((BitmapDrawable) mProfilePhoto.getDrawable()).getBitmap(); + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + bmp.compress(Bitmap.CompressFormat.PNG, 100, stream); + Photo photo = new Photo(stream.toByteArray(), ImageType.PNG); + mVCardProfile.removeProperties(Photo.class); + mVCardProfile.removeProperties(RawProperty.class); + mVCardProfile.addPhoto(photo); + } + + VCardUtils.saveLocalProfileToDisk(Ezvcard.write(mVCardProfile).version(VCardVersion.V2_1).go(), getContext()); + updateUserView(); + } + }); + + builder.show(); } public Account getSelectedAccount() { @@ -179,4 +294,20 @@ public class MenuHeaderView extends FrameLayout { mQrImage.setOnClickListener(l); } + /** + * Click listeners + */ + + @OnClick(R.id.share_btn) + public void shareButtonClicked() { + Account account = mAccountAdapter.getSelectedAccount(); + String shareUri = account.getShareURI(); + Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND); + sharingIntent.setType("text/plain"); + String shareBody = getContext().getString(R.string.account_share_body, shareUri); + sharingIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, getContext().getString(R.string.account_contact_me)); + sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, shareBody); + getContext().startActivity(Intent.createChooser(sharingIntent, getContext().getText(R.string.share_via))); + } + } diff --git a/ring-android/app/src/main/res/drawable-hdpi/ic_insert_photo.png b/ring-android/app/src/main/res/drawable-hdpi/ic_insert_photo.png new file mode 100644 index 0000000000000000000000000000000000000000..7976c2cc5d0b9512be401f528532331dc25fbe1d GIT binary patch literal 425 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4mJh`hH$2z?F<Zz1)eUBAr-gY&h+*^>>$#* zTA@i#v__|7a&LgsJ;tN0_6zI}_!iz4W8J=sQ!i%Kk*06=tMno`CV4J9Jj3UK%#J1f z7pnK2+3TB@X0k}5t0=5cX1<E&rUM&~6kbjB@H=ATdpSAzb4UNW-s?wp3%0~mvY%P+ zo}6dIwA7hl=}C??F<eXn4Gb&_42&EO3`_zCynGgG?2^_xU_9?_xy||8M_(reFzk4h zZ&1JE-(LeB|BpA9b29`V$?g7`FLS)Un8mT+rZz*5t*57m>&D*i{i{A3XwF~p>BYC^ zQU=XVozr|;#V>4+c%QBL#u%T!5+okV5bF7)vQFi+g6QY<``*9b{`AsI_SFo!s)dY? z<1O+zlb`SUdF$6fDF&ar=2MU4Ti#`OQgwNb@81MghwUmpSFiA!F-&8y`2GHCM5z4@ zh8?f60(Y*H|6yQm(Qr`z-Kk02oEbLvGNuGCtO|cy-0b}BgWG>LCPn3okHs#ZxnbiP d7^41*ahZO&sQFdLBnAcs22WQ%mvv4FO#r|3xK027 literal 0 HcmV?d00001 diff --git a/ring-android/app/src/main/res/drawable-hdpi/ic_photo_camera.png b/ring-android/app/src/main/res/drawable-hdpi/ic_photo_camera.png new file mode 100644 index 0000000000000000000000000000000000000000..b2666709880229a24596ca6fe58d8e91ce82eed6 GIT binary patch literal 750 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4mJh`hH$2z?F<Y|TRmMILn>~)jfnP+b`-c4 z6`iy4);00lUhzrHa%cZuy5W%=y1`D%QJ!g$Nbb}s$M`)7%ubmbzg}V&{-3*_zbuhM z>fF&C?l&40XK%LN{rT;gb8m8@XRCTo(}~<9GU3zDnsTeTc4tzI!t-V%YYKBc?LPXb zSmVptwV#-dgrAZMmGiP`G&;R9WYs%ShSUSztxgM9r*gR|O*|fvBf(vJDsA)RULJ<* z2eZPTwu)wLxmQ}OKKUf)I$v3)ooqi2o#$guU3YTpPS1TYyuxSLZ{EF~wDfS3{QrO1 zQI(0k!S-S++BqEV8aFhg{b&Ez+*&{R=Ckbz#rHTFW=RPy5$ZoKE4L?T<(CO(%=Rzb zf5>nl^Ma&bTu%2_7wx=LczMnn<_Xs)M1Fm>NSB%6`i>N*q@c;4`X1YKxh+0_BF9|K zLA6+tjX`>2#G94VR)?+T7wSBsq04kH{LHuNZ!Zg{G`?9D$Pn;=CGT_C=_f@yr~lYj z7yB<DvYa=(UA)RBweo;3Qv^%-t8DgL@w0wzdlh|6&o%N~;-%I}N#7s){MB>#oFydu zTru?$ldaQ4SN_1&i=Gz5RrNAXP!#^Z-In3TrJ^&!#~4n0;NY(LE^z(2^tv}`3<@Iq z1Rtm0QBj}zW!Y94qwfud3_3OjTjoBVnV8s`EdKnN#GUoiGLLW9oZMc~J~K?ad9G}> zSm%ZEzWe-ACaD`acdId8S#ZF8X6wgX7Kb{I@}R3%1-)Hn_I}*|WLrko)6hraMz1DJ zd1}g78~CMn^37@2d7l>Xr8*Z%w<_5So@bwZ*Cq9a%cW}fnH#P$rx#3()VOwiiO<`^ zJ1qNnQymxo`Ps8xvS9W3J&luYhWQ&juk*fYt*aS*@cXV6%TxbGJWz01FZ8?aUhAhi z?hn!TYWMj+wP8*;v`|6qhVe(I<yt#zRZgw(sRN~|l#`Q|{NMOz66YDmJKGr;7#KWV L{an^LB{Ts5%XnV0 literal 0 HcmV?d00001 diff --git a/ring-android/app/src/main/res/drawable-mdpi/ic_insert_photo.png b/ring-android/app/src/main/res/drawable-mdpi/ic_insert_photo.png new file mode 100644 index 0000000000000000000000000000000000000000..fb22f9f1cbf5448dbba0d61d9c0a6deb5494561f GIT binary patch literal 298 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANL}A3a?hLn>}1CrGd^X5gJM zd-i8ZY3b)oA5DaXC(pccAYgNmLz-|2!-=A-$R5_`0*@Oqw-hm)$Z6tRu<-rCiYttZ z`6NmYtYFS~#*m<SJuM|Ah4F|n+abna9$wz33x73dK4O@_-#Bds!^@WuPu3>>a&~r} z*|;#kEtlPgF-nJhYPrMYZfEnu$BwxPyy&pYvN|BO_E=9-S2**{H8;*MS~>7XS1BB4 zl(5Um$Vgz4n#Z;!rlleBpok?yhNq~Wyb$w-rR<Aj8M>T}jg1q%W>>s;nfO86rFo&} zG-qvghQ&&=SEN~<5WJE%tyxEXRiqu4n+yZP<a?>dv%I-Lp~2wk>gTe~DWM4fAkA%N literal 0 HcmV?d00001 diff --git a/ring-android/app/src/main/res/drawable-mdpi/ic_photo_camera.png b/ring-android/app/src/main/res/drawable-mdpi/ic_photo_camera.png new file mode 100644 index 0000000000000000000000000000000000000000..111db39b1f62a327000f7a3c762abb2ee61c8169 GIT binary patch literal 479 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANNf%RF5iLn>~)4L$93#6jRr zs7OsrO*j7+Igc}W^JdImq;AV$soBA9tI@GRT~t-)NU)38lo$7-ScN52)C%WvI7x9$ zdO2_1ta<ZhhOORs?B8U`wvD&mmI<HUWcSHoe&#IgtrKo<+so7Rul&iRj(x9pc{>aL z+-KjYQmtU~zQjsg(%@T&o1Wsd=S*j|p1-q{NuYvl&;N!sLNg}s^k!A~5o)?~i*0@= zONj5Zg8i(MR(%fP=xBVaz>#*yKt}ZkuVCYctLh9Ywh5C814Vi#%y?ja{6(@)EK^d+ zN1bGw^S4Ekgc{|}{b#G~dwfPTG%15o;fCP%gH{~#KUX<j|7t3@d36i}$Ft4JQ3A!M zBsyXP^c-$IKhDI+A6c&<-tft<lk1KM$Bwj~Wto>xu`bxj^RV1u%CiZ}HTJ4jC9Lj~ zOSs;@anGLXA8jYpFz=K9lKAzptNTZDrl2)%7!-4leLT9kHL+Uk=ohmj{?A%V-l!dU z)n9pS|GCE!Tju;Z8tStrVd`{&VwVU1yOO=`HCfF|`sukYsPVTH>z%1w59aoLy0>Z# mOR-`4w>Xn)5y_isnT$4Y)w9lfyoiB;fx*+&&t;ucLK6V;Bh*v? literal 0 HcmV?d00001 diff --git a/ring-android/app/src/main/res/drawable-xhdpi/ic_insert_photo.png b/ring-android/app/src/main/res/drawable-xhdpi/ic_insert_photo.png new file mode 100644 index 0000000000000000000000000000000000000000..0d166021d0469db1eab14a5f5002a18b31b30126 GIT binary patch literal 613 zcmeAS@N?(olHy`uVBq!ia0y~yU~m9o4mJh`hE<t`_ZS$MbUa-gLn>~)o#pL)*g>Q< zRKzh>qsu$ob&AJjwOq|8lShx`ITk5xlNMj3(4{S2s{Cj%t7Fdpwa=Zn7H%=jR5MII z*SOG7_3hsKXJ*FDu{LuH6mbm{dAj~%(8?{17dPmvZoPQ0)ZEuOsVI5%>}8oAXS61- zzp?G_ylqR`d5`=GYn$RQVdizsN`01cRTs7k7j!H8bze<n>{xHYb|Hvy!D7RPJ}&N( zQaKg}2JC17GsEp|_J(D;r>joLFsS_NExeLrrv71<T75c~!^Q|3_e;im-yi?Fa7~8~ zgTtcpTcdIh{{GvRc;?|=@3jmkdJ@&P7uHIv{q$P8$x5TG#?HR``}{2o7d~&8bNXQ^ z>-SrZ^R%a*w(fiE^J|X(jm0t-Rrwa&KRhE;_<MY9L#^V@%MI;Y{BAVQa$)58Wbn?H z@zlKYXYMm!N}3RH=lm`|RR(+h-SdleHsAezhWl91jEW8NI_)d@g_sp)&zs-Q-FnD| zY5yl1<?6%NH2!iWzG}X~cw$Y`9F{q@H_yt)%FBCfG|q}~eBG?VaEJBlh80n3*B$<z zzB4Az$_peK!PwD$SY>^b<Ev&1#w`zDpWXlN-mkTG9jpx%5~nNdyV^y|H}J0IPhw<v zWFBa{CaCT!<9|gbhMY;K@@C6?YvUJXcqO&Iq5A~GlDi?d{ETmFY46{BsbY;V%hI!# xY&o2Q-%NN`R{3#{PW9EdpFmOS`q%$2LyJIl`PI+SH4F?444$rjF6*2UngABf3<3ZE literal 0 HcmV?d00001 diff --git a/ring-android/app/src/main/res/drawable-xhdpi/ic_photo_camera.png b/ring-android/app/src/main/res/drawable-xhdpi/ic_photo_camera.png new file mode 100644 index 0000000000000000000000000000000000000000..0e75cb7b513ae004534754afa8227f1286171332 GIT binary patch literal 1013 zcmeAS@N?(olHy`uVBq!ia0y~yU~m9o4mJh`hE<t`_ZS$M_jtNEhE&{o8}&O|*ij(w zidI<717X(rA&XfqtK?2{%j{gSqPAfh+v<NxTkN!svU_At%@X;$(C+2okpI#9*ZU+! zp0C<_w`hAIdz<&Svgc|0=FXmbbMG`U|9Li*pPrnYtnNQ=O>la8x^%^w)vI5Ji#&@s zRdDG?j@j<D0UCEYvi2|?Dm_(n>WA0TO`@m1H}0R!q+s+*<Fe@XeAak|`mG6ny2NV^ zZuPoZcysC_9*4%}r9m$%Hb&TdG;dop{bK&TX&<>bKL4!w_bBW9vb-ySkK9hp`dINn zQKL{eru~Ebw%*MgmxO-E?r@vCwt*q!yTcEcNxb|FO}qR8%99xcPO>X(c-Zxki-Y%7 z9&-c#I?44P+@%BeKGb69;XKA*Ve|X+91Dgci^X=XX?))DeCgVYQy3Kb{!B?p6RP38 z_%lRr`t8uQVdfKmO25s#=X~Gu+tV3oj19)WSzZ^Z&h^XxfBo|f_J%o!XDpX_ZNgym zStsq=_19l{6X&1YC)51VdD%Pxb%ySk_S?5_Z)ZuJe&%&$zmDZU`AF&aj>i^ssvluu zm=gEk4Qoh&=E<w4Uzj(&PB!{=@6dmt_b(khQZ0RE`Y||6XSLU!ewzKu%_R>XOn1yQ zy?r29BdcrI^#HM7HvONhjxZiDk2$~ky$x^D)Q;-SipS0~HG9wWN!u(XeBqw*rpdew zX9{Mo+H>a5bJG()UHk(apR5x|@!8Gbuz#A@`=__|g;{R+-x_z;aK$bGmdmdyBqnuF zdT6M2;K3TL)w_59?%|f(5G-w<vq^E)sYyMCHyI4JUcK{epQE1Ps#A+{#BCOYO50~m z@Vs}~JVuzmL2ictGaIkX0)PG$dKCvEeP3P<42n#vTO+mgy5{RcS_X@jZ9eg+%;6(@ z_Q^*}I;1YIIxh0=ZkeKRjd%D}5&y++eI*2r{E&3{Gg)hb#`5ol(f3-z6ZqU>R$k4L zzWSo~8`D8f!#cL>dv?tV5IYvPG)KKlF(y9oSl=se2_6fj^@p7<oaCSG!*cepR>dR+ zS?BrJ{Y0F*r0o~oW}7QpwAuMpbKOIu-)wXLdj77s(mU~nnM)Xx<g1@|)s_cMN!+9@ z@_U*@>^w%_^4C{8)^ksH=&612Y1)jcgrD;&YiDe>S}E6_awh2V9BvK)8*Yi`ZSL;w z?rn|}&)m<MTwZ&y?O~^_#LxHsZF86pEVDlLac%=I!!lX6gi{g>DoT3;zduPfn>RDH z-;g2b?A_RY{`uD~SA3XjkUnvt-6SQBh-FnTgE;?tWc@5?HP$~C{X6IkC__PV)^fGQ a_tW0XY|-8zsKLO%z~JfX=d#Wzp$P!|CFMc@ literal 0 HcmV?d00001 diff --git a/ring-android/app/src/main/res/drawable-xxhdpi/ic_insert_photo.png b/ring-android/app/src/main/res/drawable-xxhdpi/ic_insert_photo.png new file mode 100644 index 0000000000000000000000000000000000000000..ad92e792149c367b90603dc485462df98a857a97 GIT binary patch literal 797 zcmeAS@N?(olHy`uVBq!ia0y~yU`POA4mJh`hDS5XEf^S>o_M-AhE&{oJJ&J)vVlY! zbCS#rK^2D$g>J5yv#+f^;-F=nU%tvK^gjcKg!&E^wZasSoTNX|cHWG>k-|dig>Rlz zF7;o%&2;1b<KME)ONCfC6k7zGAcW|Ype+5aMORadET`_6;kSJMZkb(uLh+?@4;$3n ziF&kDz&dC_>2=|G;!F>XGHa}_P1gUo@%%~uuU*2c1%BQ8WL$Ol)U6r*hqm*(huJtz zR(m*e;}*kz^C!LV(f^Uee1P@jxp|9j86-@aE6hM9$gsH<D3Z%?!LR1p?RK{2gSnNm z%m?1BoBDq9%{T9+uD@nK<^ErmhNZgBpFT(K`m^}r3rhx_mY`P_>9eo0F`RuDQmO5_ zcVcL}+TR1=3{|&ZZ(8=Gp8IgY_5)$7-ToY~W|-9C-+lB^n9OnWr(buks@`(!@yE%F zN;41f?_t`II<08=DLtRc_uS37+h@lyUD!P3VS$B*e7)WL`_uQ&pK3dA%b%^DwXeQ0 z&u5T%y5-dJOS9^y#^!|HsEVE~U%}`Q^?LT6NUup#!dd&v)&5tn&)WOy6Z3OMk-1A% zY!g#A-7pH3t$SeAP|Ua?{!2_%n#%ji`OFV?oZEHvA^SRJ3yo^KyV2Xu_Wav^BkMGO z4BG}X+vP#88daXPpEO9j%|D0bL;1$gr+cbStx9)G6_4Kb_kbnC%cEgOm&kbS;n6v9 zg?|pygsnF|xvJGYkZM?bpm^$6=5z*;$ef>xjOre+G%QQk+!lI_KZYS7YvWZ@`vS%T zju!$&KQhO!@A!CB2OL=+7#Pg+a(^CM`|BaAinVc)^lGQPdvo{xs=sT&P}}|ONsxl) z{(lD_D@V`oy}*5IN9kWt`3wC9mWnU`pZV^5mD|rvpv3>h=YlC$YBOzrX8K=fy}+;k q;&k$wFFEeg>$V!oBT`|hQ$544%|`Q<dmh&SS?%fS=d#Wzp$Pzo$yrbU literal 0 HcmV?d00001 diff --git a/ring-android/app/src/main/res/drawable-xxhdpi/ic_photo_camera.png b/ring-android/app/src/main/res/drawable-xxhdpi/ic_photo_camera.png new file mode 100644 index 0000000000000000000000000000000000000000..89b4678d36afa961aebf8d0eed0fc81bd78bfc8e GIT binary patch literal 1699 zcmeAS@N?(olHy`uVBq!ia0y~yU`POA4mJh`hDS5XEf^TsvOHZJLn>~)ozv|v6Do47 z`<nQ^#cm%RYZq*BO`Lw5sjGBte?+#hyrXdQ)iABCy<uB|0<tyjHe3j3+<N8zwfEEK zXyv|tbI8c}b<ufGzcQoE*5%)7zR#<kw>i0eGIkJr?#lJ+-<SR9|M&0T{>v%vPVS98 zbh0U5cdD1|-o1OpOKL2c8T5+tk0t0#|6%p&)vH~nTmIMHYT{=ooV9V+?%nT8POhK* z^Oubt!@3k+hU5<QBX=*ZXF2;Py8Gy(IL>FEb*}iCmM%HJm?^QpFTXSB$L7tO&0n71 z?#*#rrN=AoQ2g<!jg5`TL9YF;3!@e>I~f00<a|)l+)^JZyEaRxMqBiU!{&1j%s4-m z{+&44YM$`<**muj#)<r&+J0~SHu(=cR&h6u6rX=xzW>!{?K4rnOWY^At~(?zC%12L zfX1E^$2uQ;W!HOIymsQD9EO7Wu6n`xmBspslkcS29lE|?vQ&*FTjTW`7bYH5QFyXR zXi1UWMV=qWTI+c^w9OQ68?ieu@9lMe*|gtL_u)bLGlvUj86JuKa6K~Q*B?cGhG{no zVqYBWtyJY_ShTV16{BLm?Sa<VR#^sih1pAU&&kdER{1f~m~B~a`Wu<+&wl1DzVc)n z(}S!P4C{lB$3OV>`p@SdN-dL4KmFu4Bl%q6*?q=|$DH0R?8{|lNRvIdD{147_4A%^ zddl5hp5r;Mi+RJzHKI-OGBPzYB)o0)_OWtDfAnK?vU$poqZ4haws3ENhR8c_<!we* zR#nE|m6(?q@};kxG)ss<_>WLkT}H->lYM=Cw=dP@y^GYEI&V|Slb|pM6Kw|XUn<K# z`1C%E-tzL^xgIxL!R~g;XEN8bQn#CgvH#?jzRYN#{XAP|MpTo$q~y+c?UeqOf3sMA zEd0@7c>H(i{JS;F#ROJZeAM(l=EBcl_Q%8Y;p9Ej8}1ug@1J#HLs?qqji^m~m!9&f znE4>`3PXh7!(J8R$uo1NavM+EzgDW<Xn*3_ebsHEiK`VemPLu)^E$&Ib3Hx%#4SVN zB7x5ro%YW5oAiMB#B>RctN&-r+!bA;<i#*!<)pKAX6GjqsO+)xuE}@xUp&dLt6On` zWc2wDUb7y4O^agOpIDT%oIS06_K#pE0nMIb&foIhnopDyW@XK}pUm=L#lIiT3w_1! zt`RNJJ9@QD?6l?<&CfiKK1wiZl$Vv=lf6;FxvqBlrp}0k6BL%TRj~Qoc)>8?U9HuV zkKew0DGAJ<+_=+Pz4UX;3Fl*sJ$wvi2PXVGu5)&S<KwFrx(-)%^D)f+t2o!j>t+%g zgO1nLJ>r{od=Op4+%R)G!-3<Ex+NJ30vT$i2t2zXDa+6z?!+K){UirN*j08#hKSya zj0(H|%xkfI;2<`&{0(DBQF{$z?C$Gp7ySRl_HCEw6lMk&WzLRPrh>>Hj3VdT7>@HR z$Oy_Z%zD`_Y!&>Wg<q)3Pp~19@n2ZR`G=AvZ#PRc7d~e_v~EKAsmkm-(SC_CUQvA! z`ZsvhG?F7Hd)8bG`1|u=fJIa9#O&x$*Kg6UzOg$yBy&V8KOeQ`XG3Ap7R9R$liGYF zUoDms(7F24c&;|*-vhcr|2AJ+Dw*rlw&di$C2K^Vck*5pmQ-c(GShu|?}~I}UZrUi zgHOQaGnbas7_4GWj7&ebQz|r1boN(vfjukdPFpg^V5Oh`scg{;nI2UeqLSu4knO!Y zYwO<z#mj{}Um1&Y7{0#z!FhU<s@jv4YZOFhx-R$M9Cl1MqG{RQuvmd#bJX(pbu*{l z$$K7^eU)|QRpWWC2iC9De%$fjV6x_xQ;+`g-U<GFGFj^VWSu16+sn+RG)Qii($?s` z74R-mOjJAdO1884vB@v0YFe_SGj&Uk=I?BO7k+5@hgUDo9?#d%)NfC6+sbipGUH;0 z)4!Qtg}aMZuRJ~{(kfL)!pmpf`w0(auCLKdJ`*QbbW!M2xJ<*^=8Xw;2d0Q#o3|n8 zGpDhp|I3O~`!+wYO}eN0k|D>NVUPE|gL6Oi)}6lkKk`S;k4lCQ!M6mD<bK&UMgDVu z<NI_`_smbz%@XzY&$eWkGUf7*2df|4>R4ww_5CRshC9`(@6Ra{_{Y0^{=#P(+qbI= z-BFtD;=cQ8%(`P+45Q<XYcsmf)V#iM=DX>M1-_r0o8>p<9rof;Si?Q>%!~i>i*6}> j|K4V0i4~ZN{$-w0@n1Q-#v+1&fq}u()z4*}Q$iB})LJg$ literal 0 HcmV?d00001 diff --git a/ring-android/app/src/main/res/layout/dialog_profile.xml b/ring-android/app/src/main/res/layout/dialog_profile.xml new file mode 100644 index 000000000..5104ea29b --- /dev/null +++ b/ring-android/app/src/main/res/layout/dialog_profile.xml @@ -0,0 +1,76 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_gravity="center" + android:orientation="vertical" + android:padding="20dp"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:text="@string/profile_message_warning" /> + + <RelativeLayout + android:id="@+id/profile_container" + android:layout_width="150dp" + android:layout_height="180dp" + android:layout_gravity="center"> + + <ImageView + android:id="@+id/profile_photo" + android:layout_width="120dp" + android:layout_height="120dp" + android:layout_alignParentTop="true" + android:layout_centerHorizontal="true" + android:layout_margin="10dp" + android:scaleType="fitCenter" + tools:src="@drawable/ic_contact_picture" /> + + <android.support.design.widget.FloatingActionButton + android:id="@+id/camera" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignTop="@+id/anchor" + android:layout_toRightOf="@+id/anchor" + android:contentDescription="@string/take_a_photo" + android:src="@drawable/ic_photo_camera" + android:text="@string/take_a_photo" + app:backgroundTint="@color/light" + app:rippleColor="@android:color/white" /> + + <View + android:id="@+id/anchor" + android:layout_width="20dp" + android:layout_height="20dp" + android:layout_alignBottom="@+id/profile_photo" + android:layout_centerHorizontal="true" /> + + <android.support.design.widget.FloatingActionButton + android:id="@+id/gallery" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignTop="@+id/anchor" + android:layout_toLeftOf="@+id/anchor" + android:contentDescription="@string/open_the_gallery" + android:src="@drawable/ic_insert_photo" + android:text="@string/open_the_gallery" + app:backgroundTint="@color/light" + app:rippleColor="@android:color/white" /> + + </RelativeLayout> + + <EditText + android:id="@+id/user_name" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:inputType="textCapWords" + android:maxLines="1" + android:textAlignment="center" + android:hint="@string/unknowm_if_empty" + tools:text="Username" /> + +</LinearLayout> \ No newline at end of file diff --git a/ring-android/app/src/main/res/layout/frag_menu_header.xml b/ring-android/app/src/main/res/layout/frag_menu_header.xml index 1c64429b5..e52effcc1 100644 --- a/ring-android/app/src/main/res/layout/frag_menu_header.xml +++ b/ring-android/app/src/main/res/layout/frag_menu_header.xml @@ -1,40 +1,66 @@ <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/color_primary_dark" android:paddingLeft="16dp" android:paddingRight="16dp" - android:paddingTop="40dp" - android:theme="@style/MenuHeader" - tools:showIn="@layout/menuheader"> + android:paddingTop="30dp" + android:theme="@style/MenuHeader"> - <ImageView - android:id="@+id/user_photo" - android:layout_width="70dp" - android:layout_height="70dp" - android:layout_alignParentTop="true" - android:layout_centerHorizontal="true" /> + <RelativeLayout + android:id="@+id/profile_container" + android:layout_width="match_parent" + android:layout_height="wrap_content"> - <TextView - android:id="@+id/user_name" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_below="@+id/user_photo" - android:layout_centerHorizontal="true" - android:layout_marginBottom="8dp" - android:layout_marginTop="8dp" - android:singleLine="true" - android:textColor="@color/white" - android:textSize="20sp" - android:textStyle="bold" /> + <ImageView + android:id="@+id/user_photo" + android:layout_width="80dp" + android:layout_height="80dp" + android:layout_alignParentTop="true" + android:layout_centerHorizontal="true" + tools:src="@drawable/ic_contact_picture" /> + + <View + android:id="@+id/anchor" + android:layout_width="20dp" + android:layout_height="20dp" + android:layout_alignBottom="@+id/user_photo" + android:layout_centerHorizontal="true" /> + + <android.support.design.widget.FloatingActionButton + android:id="@+id/edit_profile" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignTop="@+id/anchor" + android:layout_toRightOf="@+id/anchor" + android:src="@drawable/ic_action_edit" + app:backgroundTint="@color/transparent_light" + app:fabSize="mini" /> + + <TextView + android:id="@+id/user_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@+id/edit_profile" + android:layout_centerHorizontal="true" + android:layout_marginBottom="8dp" + android:layout_marginTop="8dp" + android:maxLines="1" + android:textColor="@color/white" + android:textSize="20sp" + android:textStyle="bold" + tools:text="Username" /> + + </RelativeLayout> <ImageButton android:id="@+id/qr_image" android:layout_width="85dp" android:layout_height="85dp" - android:layout_below="@+id/user_name" + android:layout_below="@+id/profile_container" android:layout_centerHorizontal="true" android:background="@null" /> @@ -73,7 +99,7 @@ android:layout_alignParentLeft="true" android:layout_alignParentRight="true" android:layout_alignParentStart="true" - android:layout_below="@+id/user_name" + android:layout_below="@+id/profile_container" android:text="@string/action_create" android:visibility="gone" /> </RelativeLayout> \ No newline at end of file diff --git a/ring-android/app/src/main/res/values/strings.xml b/ring-android/app/src/main/res/values/strings.xml index 9149e88ef..00aa5f796 100644 --- a/ring-android/app/src/main/res/values/strings.xml +++ b/ring-android/app/src/main/res/values/strings.xml @@ -168,4 +168,12 @@ along with this program; if not, write to the Free Software <string name="prompt_new_password_repeat">Repeat new password</string> <string name="account_device_updated_message">You have successfully updated your Ring account.</string> + <!-- MenuHeaderView --> + <string name="profile">My profile</string> + <string name="profile_message_warning">This is shared with your contacts</string> + <string name="open_the_gallery">Open the gallery</string> + <string name="take_a_photo">Take a photo</string> + <string name="unknowm_if_empty">Unknown if empty</string> + <string name="unknown">Unknown</string> + </resources> -- GitLab