AndroidTV: Add profile edition from main screen

User can now update his displayed name and his picture

Tuleap-Id: #1780

Change-Id: I8b917031ed8d8fd1ac4e02fbb6a85b79cdb1b518
diff --git a/ring-android/app/src/main/AndroidManifest.xml b/ring-android/app/src/main/AndroidManifest.xml
index 2e1e235..c0b59e1 100644
--- a/ring-android/app/src/main/AndroidManifest.xml
+++ b/ring-android/app/src/main/AndroidManifest.xml
@@ -271,6 +271,9 @@
             android:name="cx.ring.tv.about.AboutActivity"
             android:theme="@style/Theme.Leanback" />
 
+        <activity android:name="cx.ring.tv.account.TVProfileEditingActivity"
+            android:theme="@style/Theme.Leanback"/>
+
         <activity
             android:name="cx.ring.tv.call.TVCallActivity"
             android:theme="@style/AppThemeBase">
diff --git a/ring-android/app/src/main/java/cx/ring/dependencyinjection/RingInjectionComponent.java b/ring-android/app/src/main/java/cx/ring/dependencyinjection/RingInjectionComponent.java
index c92f923..f37ba49 100755
--- a/ring-android/app/src/main/java/cx/ring/dependencyinjection/RingInjectionComponent.java
+++ b/ring-android/app/src/main/java/cx/ring/dependencyinjection/RingInjectionComponent.java
@@ -66,6 +66,7 @@
 import cx.ring.tv.account.TVAccountWizard;
 import cx.ring.tv.account.TVHomeAccountCreationFragment;
 import cx.ring.tv.account.TVProfileCreationFragment;
+import cx.ring.tv.account.TVProfileEditingFragment;
 import cx.ring.tv.account.TVRingAccountCreationFragment;
 import cx.ring.tv.account.TVRingLinkAccountFragment;
 import cx.ring.tv.call.TVCallActivity;
@@ -186,6 +187,8 @@
 
     void inject(TVLaunchActivity activity);
 
+    void inject(TVProfileEditingFragment activity);
+
     void inject(TVContactRequestFragment fragment);
 
 }
diff --git a/ring-android/app/src/main/java/cx/ring/navigation/RingNavigationFragment.java b/ring-android/app/src/main/java/cx/ring/navigation/RingNavigationFragment.java
index 0974ee6..cff7d45 100644
--- a/ring-android/app/src/main/java/cx/ring/navigation/RingNavigationFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/navigation/RingNavigationFragment.java
@@ -347,10 +347,11 @@
                     mSourcePhoto.compress(Bitmap.CompressFormat.PNG, 100, stream);
                     Photo photo = new Photo(stream.toByteArray(), ImageType.PNG);
 
-                    presenter.saveVCard(editText.getText().toString().trim(), photo);
+                    presenter.saveVCardFormattedName(editText.getText().toString().trim());
+                    presenter.saveVCardPhoto(photo);
                     mSourcePhoto = null;
                 } else {
-                    presenter.saveVCard(editText.getText().toString().trim());
+                    presenter.saveVCardFormattedName(editText.getText().toString().trim());
                 }
             }
         });
diff --git a/ring-android/app/src/main/java/cx/ring/tv/account/TVProfileEditingActivity.java b/ring-android/app/src/main/java/cx/ring/tv/account/TVProfileEditingActivity.java
new file mode 100644
index 0000000..61beb02
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/tv/account/TVProfileEditingActivity.java
@@ -0,0 +1,64 @@
+/*
+ *  Copyright (C) 2017 Savoir-faire Linux Inc.
+ *
+ *  Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package cx.ring.tv.account;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+
+import cx.ring.R;
+
+import static cx.ring.tv.account.TVProfileEditingFragment.REQUEST_CODE_GALLERY;
+import static cx.ring.tv.account.TVProfileEditingFragment.REQUEST_CODE_PHOTO;
+
+public class TVProfileEditingActivity extends Activity {
+
+    public static final String TAG = TVProfileEditingActivity.class.getSimpleName();
+    private static final String TV_PROFILE_EDITING_TAG = "tv_profile_editing";
+    private TVProfileEditingFragment fTvProfileEditing;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.tv_activity_profile_editing);
+
+        if (savedInstanceState != null) {
+            fTvProfileEditing = (TVProfileEditingFragment) getFragmentManager().findFragmentByTag(TV_PROFILE_EDITING_TAG);
+        }
+    }
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+
+        switch (requestCode) {
+            case REQUEST_CODE_PHOTO:
+                if (resultCode == RESULT_OK && data != null && data.getExtras() != null) {
+                    fTvProfileEditing.updatePhoto((Bitmap) data.getExtras().get("data"));
+                }
+                break;
+            case REQUEST_CODE_GALLERY:
+                if (resultCode == RESULT_OK && data != null) {
+                    fTvProfileEditing.updatePhoto(data.getData());
+                }
+                break;
+        }
+    }
+}
diff --git a/ring-android/app/src/main/java/cx/ring/tv/account/TVProfileEditingFragment.java b/ring-android/app/src/main/java/cx/ring/tv/account/TVProfileEditingFragment.java
new file mode 100644
index 0000000..f14f42e
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/tv/account/TVProfileEditingFragment.java
@@ -0,0 +1,226 @@
+/*
+ *  Copyright (C) 2017 Savoir-faire Linux Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package cx.ring.tv.account;
+
+import android.Manifest;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.MediaStore;
+import android.support.annotation.NonNull;
+import android.support.v17.leanback.app.GuidedStepFragment;
+import android.support.v17.leanback.widget.GuidanceStylist;
+import android.support.v17.leanback.widget.GuidedAction;
+import android.support.v4.app.ActivityCompat;
+import android.util.Log;
+import android.view.View;
+
+import java.io.ByteArrayOutputStream;
+import java.util.List;
+
+import cx.ring.R;
+import cx.ring.account.ProfileCreationFragment;
+import cx.ring.adapters.ContactDetailsTask;
+import cx.ring.application.RingApplication;
+import cx.ring.navigation.RingNavigationPresenter;
+import cx.ring.navigation.RingNavigationView;
+import cx.ring.navigation.RingNavigationViewModel;
+import cx.ring.tv.camera.CustomCameraActivity;
+import cx.ring.utils.BitmapUtils;
+import cx.ring.utils.VCardUtils;
+import ezvcard.VCard;
+import ezvcard.parameter.ImageType;
+import ezvcard.property.Photo;
+
+public class TVProfileEditingFragment extends RingGuidedStepFragment<RingNavigationPresenter>
+        implements RingNavigationView {
+
+    public static final int REQUEST_CODE_PHOTO = 1;
+    public static final int REQUEST_CODE_GALLERY = 2;
+    public static final int REQUEST_PERMISSION_CAMERA = 3;
+    public static final int REQUEST_PERMISSION_READ_STORAGE = 4;
+    private static final int USER_NAME = 1;
+    private static final int GALLERY = 2;
+    private static final int CAMERA = 3;
+
+    private List<GuidedAction> actions;
+
+    public static GuidedStepFragment newInstance() {
+        return new TVProfileEditingFragment();
+    }
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        switch (requestCode) {
+            case ProfileCreationFragment.REQUEST_CODE_PHOTO:
+                if (resultCode == Activity.RESULT_OK && data != null) {
+                    Bundle extras = data.getExtras();
+                    if (extras == null) {
+                        Log.e(TAG, "onActivityResult: Not able to get picture from extra");
+                        return;
+                    }
+                    byte[] input = extras.getByteArray("data");
+                    if (input == null) {
+                        Log.e(TAG, "onActivityResult: Not able to get byte[] from extra");
+                        return;
+                    }
+                    Bitmap original = BitmapFactory.decodeByteArray(input, 0, input.length);
+                    updatePhoto(original);
+                }
+                break;
+            case ProfileCreationFragment.REQUEST_CODE_GALLERY:
+                if (resultCode == Activity.RESULT_OK && data != null) {
+                    updatePhoto(data.getData());
+                }
+                break;
+            default:
+                break;
+        }
+    }
+
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        ((RingApplication) getActivity().getApplication()).getRingInjectionComponent().inject(this);
+        super.onViewCreated(view, savedInstanceState);
+        presenter.updateUser();
+    }
+
+    @Override
+    @NonNull
+    public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) {
+        String title = getString(R.string.profile);
+        String breadcrumb = "";
+        String description = getString(R.string.profile_message_warning);
+
+        Drawable icon = getActivity().getResources().getDrawable(R.drawable.ic_contact_picture);
+
+        return new GuidanceStylist.Guidance(title, description, breadcrumb, icon);
+    }
+
+    @Override
+    public int onProvideTheme() {
+        return R.style.Theme_Ring_Leanback_GuidedStep_First;
+    }
+
+    @Override
+    public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
+        String desc = getString(R.string.account_edit_profile);
+        String editdesc = getString(R.string.profile_name_hint);
+        addEditTextAction(actions, USER_NAME, desc, editdesc, "");
+        addAction(actions, CAMERA, getActivity().getResources().getString(R.string.take_a_photo), "");
+        addAction(actions, GALLERY, getActivity().getResources().getString(R.string.open_the_gallery), "");
+
+        this.actions = actions;
+    }
+
+    public long onGuidedActionEditedAndProceed(GuidedAction action) {
+        if (action.getId() == USER_NAME) {
+            String username = action.getEditDescription().toString();
+            presenter.saveVCardFormattedName(username);
+        } else if (action.getId() == CAMERA) {
+            presenter.cameraClicked();
+        } else if (action.getId() == GALLERY) {
+            presenter.galleryClicked();
+        }
+        return super.onGuidedActionEditedAndProceed(action);
+    }
+
+    @Override
+    public void onGuidedActionClicked(GuidedAction action) {
+        if (action.getId() == CAMERA) {
+            presenter.cameraClicked();
+        } else if (action.getId() == GALLERY) {
+            presenter.galleryClicked();
+        }
+    }
+
+    @Override
+    public void showViewModel(RingNavigationViewModel viewModel) {
+        // displays account available info
+        VCard vcard = viewModel.getVcard(getActivity().getFilesDir());
+        if (vcard == null || vcard.getPhotos().isEmpty()) {
+            getGuidanceStylist().getIconView().setImageDrawable(getActivity().getResources().getDrawable(R.drawable.ic_contact_picture));
+        } else {
+            if (!this.actions.isEmpty() && this.actions.get(0).getId() == USER_NAME) {
+                this.actions.get(0).setEditDescription(vcard.getFormattedName().getValue());
+            }
+
+            Photo tmp = vcard.getPhotos().get(0);
+            Bitmap bitmap = BitmapFactory.decodeByteArray(tmp.getData(), 0, tmp.getData().length);
+            getGuidanceStylist().getIconView().setImageBitmap(bitmap);
+        }
+    }
+
+    @Override
+    public void gotToImageCapture() {
+        Intent intent = new Intent(getActivity(), CustomCameraActivity.class);
+        startActivityForResult(intent, REQUEST_CODE_PHOTO);
+    }
+
+    @Override
+    public void askCameraPermission() {
+        ActivityCompat.requestPermissions(getActivity(),
+                new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE},
+                REQUEST_PERMISSION_CAMERA);
+    }
+
+    @Override
+    public void askGalleryPermission() {
+        ActivityCompat.requestPermissions(getActivity(),
+                new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
+                REQUEST_PERMISSION_READ_STORAGE);
+    }
+
+    @Override
+    public void goToGallery() {
+        try {
+            Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
+            startActivityForResult(intent, REQUEST_CODE_GALLERY);
+        } catch (ActivityNotFoundException e) {
+            new AlertDialog.Builder(getActivity())
+                    .setPositiveButton(android.R.string.ok, null)
+                    .setTitle(R.string.gallery_error_title)
+                    .setMessage(R.string.gallery_error_message)
+                    .show();
+        }
+    }
+
+    public void updatePhoto(Uri uriImage) {
+        updatePhoto(ContactDetailsTask.loadProfilePhotoFromUri(getActivity(), uriImage));
+    }
+
+    public void updatePhoto(Bitmap image) {
+        if (image == null) {
+            Log.w(TAG, "updatePhoto: null photo");
+            return;
+        }
+
+        ByteArrayOutputStream stream = new ByteArrayOutputStream();
+        BitmapUtils.reduceBitmap(image, VCardUtils.VCARD_PHOTO_SIZE);
+        image.compress(Bitmap.CompressFormat.PNG, 100, stream);
+        Photo photo = new Photo(stream.toByteArray(), ImageType.PNG);
+
+        presenter.saveVCardPhoto(photo);
+    }
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/tv/cards/Card.java b/ring-android/app/src/main/java/cx/ring/tv/cards/Card.java
index eaa0167..65fcc50 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/cards/Card.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/cards/Card.java
@@ -127,6 +127,7 @@
         SEARCH_RESULT,
         ABOUT_CONTRIBUTOR,
         ACCOUNT_ADD_DEVICE,
+        ACCOUNT_EDIT_PROFILE,
         ABOUT_LICENCES,
         CONTACT,
         CONTACT_ONLINE,
diff --git a/ring-android/app/src/main/java/cx/ring/tv/cards/CardPresenterSelector.java b/ring-android/app/src/main/java/cx/ring/tv/cards/CardPresenterSelector.java
index 24bde1e..1adda08 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/cards/CardPresenterSelector.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/cards/CardPresenterSelector.java
@@ -32,7 +32,7 @@
 public class CardPresenterSelector extends PresenterSelector {
 
     private final Context mContext;
-    private final HashMap<Card.Type, Presenter> presenters = new HashMap<Card.Type, Presenter>();
+    private final HashMap<Card.Type, Presenter> presenters = new HashMap<>();
 
     public CardPresenterSelector(Context context) {
         mContext = context;
@@ -51,6 +51,7 @@
                 case ABOUT_CONTRIBUTOR:
                 case ABOUT_LICENCES:
                 case ACCOUNT_ADD_DEVICE:
+                case ACCOUNT_EDIT_PROFILE:
                     presenter = new IconCardPresenter(mContext);
                     break;
                 case SEARCH_RESULT:
diff --git a/ring-android/app/src/main/java/cx/ring/tv/cards/iconcards/IconCardHelper.java b/ring-android/app/src/main/java/cx/ring/tv/cards/iconcards/IconCardHelper.java
index 9113bb1..6e3bc65 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/cards/iconcards/IconCardHelper.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/cards/iconcards/IconCardHelper.java
@@ -44,12 +44,22 @@
             case ABOUT_VERSION:
                 return getVersionCard(pContext);
             case ACCOUNT_ADD_DEVICE:
-                return getAccountAddDevice(pContext);
+                return getAccountAddDeviceCard(pContext);
+            case ACCOUNT_EDIT_PROFILE:
+                return getAccountManagementCard(pContext);
             default:
                 return null;
         }
     }
 
+    public static IconCard getAccountAddDeviceCard(Context pContext) {
+        return new IconCard(Card.Type.ACCOUNT_ADD_DEVICE, pContext.getString(R.string.account_link_export_button), "", R.drawable.ic_add_white);
+    }
+
+    public static IconCard getAccountManagementCard(Context pContext) {
+        return new IconCard(Card.Type.ACCOUNT_EDIT_PROFILE, pContext.getString(R.string.account_edit_profile), "", R.drawable.ic_account_card_details_white);
+    }
+
     public static IconCard getVersionCard(Context pContext) {
         return new IconCard(Card.Type.ABOUT_VERSION, pContext.getString(R.string.version_section) + " 1.0" + " " + BuildConfig.VERSION_NAME, "", R.drawable.ic_ring_logo_white);
     }
@@ -62,10 +72,6 @@
         return new IconCard(Card.Type.ABOUT_CONTRIBUTOR, pContext.getString(R.string.credits), formatContributors(pContext), R.drawable.ic_face);
     }
 
-    public static IconCard getAccountAddDevice(Context pContext) {
-        return new IconCard(Card.Type.ACCOUNT_ADD_DEVICE, pContext.getString(R.string.account_link_export_button), "", R.drawable.ic_add_white);
-    }
-
     private static CharSequence formatLicence(Context pContext) {
         Resources res = pContext.getResources();
 
diff --git a/ring-android/app/src/main/java/cx/ring/tv/main/MainFragment.java b/ring-android/app/src/main/java/cx/ring/tv/main/MainFragment.java
index b94f2f5..e994dc6 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/main/MainFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/main/MainFragment.java
@@ -40,6 +40,7 @@
 import cx.ring.navigation.RingNavigationViewModel;
 import cx.ring.tv.about.AboutActivity;
 import cx.ring.tv.account.TVAccountExport;
+import cx.ring.tv.account.TVProfileEditingActivity;
 import cx.ring.tv.call.TVCallActivity;
 import cx.ring.tv.cards.Card;
 import cx.ring.tv.cards.CardListRow;
@@ -55,6 +56,8 @@
 import cx.ring.tv.model.TVListViewModel;
 import cx.ring.tv.search.SearchActivity;
 import cx.ring.tv.views.CustomTitleView;
+import ezvcard.VCard;
+import ezvcard.property.FormattedName;
 import ezvcard.property.Photo;
 
 public class MainFragment extends BaseBrowseFragment<MainPresenter> implements MainView {
@@ -155,12 +158,12 @@
         }
 
         return new CardListRow(new HeaderItem(HEADER_MISC, titleSection), listRowAdapter, row);
-
     }
 
     private Row createMyAccountRow() {
         List<Card> cards = new ArrayList<>();
-        cards.add(IconCardHelper.getAccountAddDevice(getActivity()));
+        cards.add(IconCardHelper.getAccountAddDeviceCard(getActivity()));
+        cards.add(IconCardHelper.getAccountManagementCard(getActivity()));
 
         return createRow(getString(R.string.ring_account), cards, false);
     }
@@ -283,8 +286,19 @@
                     return;
                 }
 
-                List<Photo> photos = viewModel.getVcard(getActivity().getFilesDir()).getPhotos();
-                if (!photos.isEmpty()) {
+                VCard vcard = viewModel.getVcard(getActivity().getFilesDir());
+                if (vcard == null) {
+                    Log.e(TAG, "displayAccountInfos: Not able to get vcard");
+                    return;
+                }
+
+                FormattedName formattedName = vcard.getFormattedName();
+                if (formattedName != null) {
+                    titleView.setAlias(formattedName.getValue());
+                }
+
+                List<Photo> photos = vcard.getPhotos();
+                if (!photos.isEmpty() && photos.get(0) != null) {
                     titleView.setCurrentAccountPhoto(photos.get(0).getData());
                 }
             }
@@ -297,6 +311,19 @@
         GuidedStepFragment.add(getFragmentManager(), wizard, R.id.main_browse_fragment);
     }
 
+    @Override
+    public void showProfileEditing() {
+        Intent intent = new Intent(getActivity(), TVProfileEditingActivity.class);
+        startActivity(intent);
+    }
+
+    @Override
+    public void showLicence(int aboutType) {
+        Intent intent = new Intent(getActivity(), AboutActivity.class);
+        intent.putExtra("abouttype", aboutType);
+        startActivity(intent);
+    }
+
     private final class ItemViewClickedListener implements OnItemViewClickedListener {
         @Override
         public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
@@ -319,14 +346,14 @@
                 switch (card.getType()) {
                     case ABOUT_CONTRIBUTOR:
                     case ABOUT_LICENCES:
-                        Intent intent = new Intent(getActivity(),
-                                AboutActivity.class);
-                        intent.putExtra("abouttype", card.getType().ordinal());
-                        startActivity(intent);
+                        presenter.onLicenceClicked(card.getType().ordinal());
                         break;
                     case ACCOUNT_ADD_DEVICE:
                         presenter.onExportClicked();
                         break;
+                    case ACCOUNT_EDIT_PROFILE:
+                        presenter.onEditProfileClicked();
+                        break;
                     default:
                         break;
                 }
diff --git a/ring-android/app/src/main/java/cx/ring/tv/main/MainPresenter.java b/ring-android/app/src/main/java/cx/ring/tv/main/MainPresenter.java
index c63fb9c..1c63f25 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/main/MainPresenter.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/main/MainPresenter.java
@@ -40,6 +40,7 @@
 import cx.ring.services.ContactService;
 import cx.ring.services.HardwareService;
 import cx.ring.services.PresenceService;
+import cx.ring.tv.cards.iconcards.IconCard;
 import cx.ring.tv.model.TVContactRequestViewModel;
 import cx.ring.tv.model.TVListViewModel;
 import cx.ring.utils.Log;
@@ -256,6 +257,14 @@
         getView().showExportDialog(mAccountService.getCurrentAccount().getAccountID());
     }
 
+    public void onLicenceClicked(int aboutType) {
+        getView().showLicence(aboutType);
+    }
+
+    public void onEditProfileClicked() {
+        getView().showProfileEditing();
+    }
+
     private void subscribePresence() {
         if (mAccountService.getCurrentAccount() == null) {
             return;
diff --git a/ring-android/app/src/main/java/cx/ring/tv/main/MainView.java b/ring-android/app/src/main/java/cx/ring/tv/main/MainView.java
index 43f0888..f09b3b7 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/main/MainView.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/main/MainView.java
@@ -44,4 +44,8 @@
     void displayAccountInfos(String address, RingNavigationViewModel viewModel);
 
     void showExportDialog(String pAccountID);
+
+    void showProfileEditing();
+
+    void showLicence(int aboutType);
 }
diff --git a/ring-android/app/src/main/java/cx/ring/tv/views/CustomTitleView.java b/ring-android/app/src/main/java/cx/ring/tv/views/CustomTitleView.java
index 601b824..56c3d5d 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/views/CustomTitleView.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/views/CustomTitleView.java
@@ -31,6 +31,7 @@
  * Custom title view to be used in {@link android.support.v17.leanback.app.BrowseFragment}.
  */
 public class CustomTitleView extends RelativeLayout implements TitleViewAdapter.Provider {
+    private final TextView mAliasView;
     private final TextView mTitleView;
     private final ImageView mLogoView;
     private final View mSearchOrbView;
@@ -76,6 +77,7 @@
     public CustomTitleView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
         View root = LayoutInflater.from(context).inflate(R.layout.tv_titleview, this);
+        mAliasView = root.findViewById(R.id.account_alias);
         mTitleView = root.findViewById(R.id.title_text);
         mLogoView = root.findViewById(R.id.title_photo_contact);
         mSearchOrbView = root.findViewById(R.id.title_orb);
@@ -92,6 +94,13 @@
         }
     }
 
+    public void setAlias(CharSequence alias) {
+        if (alias != null) {
+            mAliasView.setText(alias);
+            mAliasView.setVisibility(VISIBLE);
+        }
+    }
+
     public void setCurrentAccountPhoto(byte[] photo) {
         if (photo != null && photo.length > 0) {
             Glide.with(getContext())
diff --git a/ring-android/app/src/main/res/drawable/ic_account_card_details_white.xml b/ring-android/app/src/main/res/drawable/ic_account_card_details_white.xml
new file mode 100644
index 0000000..1519e77
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable/ic_account_card_details_white.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24">
+    <path
+        android:fillColor="#ffffff"
+        android:pathData="M2,3H22C23.05,3 24,3.95 24,5V19C24,20.05 23.05,21 22,21H2C0.95,21 0,20.05 0,19V5C0,3.95 0.95,3 2,3M14,6V7H22V6H14M14,8V9H21.5L22,9V8H14M14,10V11H21V10H14M8,13.91C6,13.91 2,15 2,17V18H14V17C14,15 10,13.91 8,13.91M8,6A3,3 0 0,0 5,9A3,3 0 0,0 8,12A3,3 0 0,0 11,9A3,3 0 0,0 8,6Z" />
+</vector>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/drawable/ic_group_white.xml b/ring-android/app/src/main/res/drawable/ic_group_white.xml
new file mode 100644
index 0000000..45d435c
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable/ic_group_white.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M16,11c1.66,0 2.99,-1.34 2.99,-3S17.66,5 16,5c-1.66,0 -3,1.34 -3,3s1.34,3 3,3zM8,11c1.66,0 2.99,-1.34 2.99,-3S9.66,5 8,5C6.34,5 5,6.34 5,8s1.34,3 3,3zM8,13c-2.33,0 -7,1.17 -7,3.5L1,19h14v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5zM16,13c-0.29,0 -0.62,0.02 -0.97,0.05 1.16,0.84 1.97,1.97 1.97,3.45L17,19h6v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5z"
+        android:fillColor="#FFFFFF"/>
+</vector>
diff --git a/ring-android/app/src/main/res/layout-w960dp-land/tv_activity_profile_editing.xml b/ring-android/app/src/main/res/layout-w960dp-land/tv_activity_profile_editing.xml
new file mode 100644
index 0000000..d914fce
--- /dev/null
+++ b/ring-android/app/src/main/res/layout-w960dp-land/tv_activity_profile_editing.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+Copyright (C) 2004-2018 Savoir-faire Linux Inc.
+
+Author: Adrien Beraud <adrien.beraud@savoirfairelinux.com>
+Pierre Duchemin <pierre.duchemin@savoirfairelinux.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+-->
+
+<fragment xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/details_fragment"
+    android:name="cx.ring.tv.account.TVProfileEditingFragment"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:tag="tv_profile_editing" />
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/layout/tv_titleview.xml b/ring-android/app/src/main/res/layout/tv_titleview.xml
index 940a288..10ea79e 100644
--- a/ring-android/app/src/main/res/layout/tv_titleview.xml
+++ b/ring-android/app/src/main/res/layout/tv_titleview.xml
@@ -1,32 +1,57 @@
 <?xml version="1.0" encoding="utf-8"?>
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
 
     <android.support.v17.leanback.widget.SearchOrbView
         android:id="@+id/title_orb"
-        android:layout_height="wrap_content"
         android:layout_width="wrap_content"
-        android:transitionGroup="true"
+        android:layout_height="wrap_content"
         android:layout_gravity="center_vertical|start"
+        android:layout_marginStart="48dp"
         android:layout_marginTop="8dp"
-        android:layout_marginStart="48dp" />
+        android:transitionGroup="true" />
 
-    <TextView
-        android:id="@+id/title_text"
-        android:textAppearance="@android:style/TextAppearance.Large"
-        android:visibility="gone"
+    <LinearLayout
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginEnd="24dp"
         android:layout_centerVertical="true"
-        android:layout_toStartOf="@id/title_photo_contact" />
+        android:layout_marginEnd="24dp"
+        android:layout_marginStart="260dp"
+        android:layout_toStartOf="@id/title_photo_contact"
+        android:gravity="end"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/account_alias"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:ellipsize="end"
+            android:maxLines="1"
+            android:textAppearance="@android:style/TextAppearance.Large"
+            android:visibility="gone"
+            tools:text="account alias"
+            tools:visibility="visible" />
+
+        <TextView
+            android:id="@+id/title_text"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:ellipsize="end"
+            android:maxLines="1"
+            android:textAppearance="@android:style/TextAppearance.DeviceDefault"
+            android:visibility="gone"
+            tools:text="account name"
+            tools:visibility="visible" />
+
+    </LinearLayout>
 
     <ImageView
         android:id="@+id/title_photo_contact"
         android:layout_width="80dp"
         android:layout_height="80dp"
-        android:padding="6dp"
-        android:src="@drawable/ic_contact_picture"
         android:layout_alignParentEnd="true"
         android:layout_gravity="center_vertical|end"
-        android:layout_marginEnd="24dp" />
+        android:layout_marginEnd="24dp"
+        android:padding="6dp"
+        android:src="@drawable/ic_contact_picture" />
 </merge>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/values-fr-rFR/strings.xml b/ring-android/app/src/main/res/values-fr-rFR/strings.xml
index d714827..9dcf2a2 100644
--- a/ring-android/app/src/main/res/values-fr-rFR/strings.xml
+++ b/ring-android/app/src/main/res/values-fr-rFR/strings.xml
@@ -6,7 +6,7 @@
   <string name="contribute_section">Contribuer</string>
   <string name="version_section">Version</string>
   <string name="no_email_app_installed">Aucun logiciel de messagerie n\'a été trouvé, merci d\'installer un logiciel de messagerie pour nous envoyer un rapport</string>
-  <string name="email_chooser_title">Envoyer un courriel en utilisant...</string>
+  <string name="email_chooser_title">Envoyer un courriel en utilisant&#8230;</string>
   <!--About dialog-->
   <!--RingActivity-->
   <string name="title_activity_sflphone_home">Ring</string>
@@ -21,9 +21,10 @@
   <!--Sections-->
   <string name="menu_item_home">Accueil</string>
   <string name="menu_item_accounts">Gérer des comptes</string>
+  <string name="menu_item_account">Gérer mon compte</string>
   <string name="menu_item_settings">Réglages</string>
   <string name="menu_item_share">Partager mes contacts</string>
-  <string name="menu_item_about">A propos de Ring</string>
+  <string name="menu_item_about">À propos de Ring</string>
   <!--Dialing Fragment-->
   <string name="dial_number">Composer un numéro </string>
   <!--History Fragment-->
diff --git a/ring-android/app/src/main/res/values/strings.xml b/ring-android/app/src/main/res/values/strings.xml
index 17b579d..b9c2571 100644
--- a/ring-android/app/src/main/res/values/strings.xml
+++ b/ring-android/app/src/main/res/values/strings.xml
@@ -48,7 +48,7 @@
     <!-- About dialog -->
     <string name="developed_by">Developed by</string>
     <string name="designed_by">Designed by</string>
-    <string name="credits_developer" translatable="false">Adrien Béraud\nAlexandr Sergheev\nAlexandre Lision\nAlexandre Viau\nAline Bonnet\nAndreas Traczyk\nAnthony Léonard\nCyrille Béraud\nEdric Milaret\nÉloi Bail\nEmmanuel Lepage-Vallée\nFrédéric Guimont\nGuillaume Roguez\nHadrien De Sousa\nJulien Grossholtz\nKateryna Kostiuk\nLoïc Siret\nMarianne Forget\nMichel Schmit\nNicolas Jäger\nNicolas Reynaud\nOlivier Gregoire\nOlivier Soldano\nPatrick Keroulas\nPhilippe Gorley\nRomain Bertozzi\nSaher Azer\nSébastien Blin\nSeva Ivanov\nSilbino Gonçalves Matado\nSimon Désaulniers\nStepan Salenikovich\nSimon Zeni\nThibault Wittemberg</string>
+    <string name="credits_developer" translatable="false">Adrien Béraud\nAlexandr Sergheev\nAlexandre Lision\nAlexandre Viau\nAline Bonnet\nAndreas Traczyk\nAnthony Léonard\nCyrille Béraud\nEdric Milaret\nÉloi Bail\nEmmanuel Lepage-Vallée\nFrédéric Guimont\nGuillaume Roguez\nHadrien De Sousa\nJulien Grossholtz\nKateryna Kostiuk\nLoïc Siret\nMarianne Forget\nMichel Schmit\nNicolas Jäger\nNicolas Reynaud\nOlivier Gregoire\nOlivier Soldano\nPatrick Keroulas\nPierre Duchemin\nPhilippe Gorley\nRomain Bertozzi\nSaher Azer\nSébastien Blin\nSeva Ivanov\nSilbino Gonçalves Matado\nSimon Désaulniers\nStepan Salenikovich\nSimon Zeni\nThibault Wittemberg</string>
 
     <!-- RingActivity -->
     <string name="title_activity_sflphone_home">Ring</string>
@@ -66,6 +66,7 @@
     <string name="menu_item_home">Home</string>
     <string name="menu_item_contact_request">Contact requests</string>
     <string name="menu_item_accounts">Manage accounts</string>
+    <string name="menu_item_account">Manage account</string>
     <string name="menu_item_settings">Settings</string>
     <string name="menu_item_share">Share my contact</string>
     <string name="menu_item_about">About Ring</string>
diff --git a/ring-android/app/src/main/res/values/strings_account.xml b/ring-android/app/src/main/res/values/strings_account.xml
index 3cac302..8a1a81a 100644
--- a/ring-android/app/src/main/res/values/strings_account.xml
+++ b/ring-android/app/src/main/res/values/strings_account.xml
@@ -214,6 +214,9 @@
     <string name="account_link_title">Link this device</string>
     <string name="account_sip_cannot_be_registered">Can\'t register account</string>
 
+    <!-- Edit profile-->
+    <string name="account_edit_profile">Edit your profile</string>
+
     <!-- Devices -->
     <string name="account_revoke_device_hint">Enter password to confirm</string>
     <string name="enter_password">Enter password</string>
diff --git a/ring-android/libringclient/src/main/java/cx/ring/navigation/RingNavigationPresenter.java b/ring-android/libringclient/src/main/java/cx/ring/navigation/RingNavigationPresenter.java
index a4f6de6..9098a37 100644
--- a/ring-android/libringclient/src/main/java/cx/ring/navigation/RingNavigationPresenter.java
+++ b/ring-android/libringclient/src/main/java/cx/ring/navigation/RingNavigationPresenter.java
@@ -83,13 +83,12 @@
         mAccountService.setCurrentAccount(selectedAccount);
     }
 
-    public void saveVCard(String username, Photo photo) {
+    public void saveVCardPhoto(Photo photo) {
         String accountId = mAccountService.getCurrentAccount().getAccountID();
         String ringId = mAccountService.getCurrentAccount().getUsername();
         File filesDir = mDeviceRuntimeService.provideFilesDir();
 
         VCard vcard = VCardUtils.loadLocalProfileFromDisk(filesDir, accountId);
-        vcard.setFormattedName(username);
         vcard.setUid(new Uid(ringId));
         vcard.removeProperties(Photo.class);
         vcard.addPhoto(photo);
@@ -99,7 +98,7 @@
         updateUser();
     }
 
-    public void saveVCard(String username) {
+    public void saveVCardFormattedName(String username) {
         String accountId = mAccountService.getCurrentAccount().getAccountID();
         File filesDir = mDeviceRuntimeService.provideFilesDir();