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 8246a98d034847962c3aa4a17e2b8b79884c697e..392ec8707e7a22246b8783ce25dc19bae3cd2484 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 c24b15e029aa18df333cd32f658eba248d1c0592..904fafaa3d94651a01b13afee54c4c8e42f139a5 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 ed152fd445b13e093df525fad35ec255be1ace88..f7de4e6e616a9577f0b8d158ca966037cae61c07 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 004bc09eac1728925ceb345027e23a0c26e90307..a3de0fad565931a6c423510b236d1e97b46e8be7 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 2845ee815e366edd00c4004b697291ad4d84dbb2..53761489c9b65f5863f89a7a519a0df1ebe0d9b5 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 8182b2805ae080beefb1978bea3aae4e90b72f61..066670abe756c5b21c26e86f3a76a84a003b7bea 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 67c0ad0ac9bdbcb85fbf65b4e583b8c57b985b7b..9bb916c4223b6affbd30a421e21db68c220c4d96 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 Binary files /dev/null and b/ring-android/app/src/main/res/drawable-hdpi/ic_insert_photo.png differ 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 Binary files /dev/null and b/ring-android/app/src/main/res/drawable-hdpi/ic_photo_camera.png differ 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 Binary files /dev/null and b/ring-android/app/src/main/res/drawable-mdpi/ic_insert_photo.png differ 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 Binary files /dev/null and b/ring-android/app/src/main/res/drawable-mdpi/ic_photo_camera.png differ 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 Binary files /dev/null and b/ring-android/app/src/main/res/drawable-xhdpi/ic_insert_photo.png differ 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 Binary files /dev/null and b/ring-android/app/src/main/res/drawable-xhdpi/ic_photo_camera.png differ 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 Binary files /dev/null and b/ring-android/app/src/main/res/drawable-xxhdpi/ic_insert_photo.png differ 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 Binary files /dev/null and b/ring-android/app/src/main/res/drawable-xxhdpi/ic_photo_camera.png differ 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 0000000000000000000000000000000000000000..5104ea29b570db25d59f6772fb4d96cdcab829a8 --- /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 1c64429b5cd9ed4de2b6b0e886588ae12cfb44cd..e52effcc139d887ee9916e62c295f6ad7378c4af 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 9149e88ef8518d3d474bea521f2d12e24c28560a..00aa5f796068fc6e60cb5108b2cbce8f300c1688 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>