From 9d3abf7d99c88e69b438347b6c501e796a710e73 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Adrien=20B=C3=A9raud?= <adrien.beraud@savoirfairelinux.com>
Date: Fri, 22 Jan 2021 16:56:45 -0500
Subject: [PATCH] conversation: add emoji preference

Change-Id: Ie47a58d18a81754bf5a7dca0771bd4293ee2d3fa
---
 .../ring/client/ContactDetailsActivity.java   | 27 +++++-
 .../ring/client/EmojiChooserBottomSheet.java  | 97 +++++++++++++++++++
 .../ring/fragments/ConversationFragment.java  | 11 ++-
 .../conversation/TvConversationFragment.java  |  5 +
 .../main/res/layout/item_contact_action.xml   | 88 ++++++++---------
 .../app/src/main/res/layout/item_emoji.xml    | 10 ++
 .../app/src/main/res/values/arrays.xml        | 13 +++
 .../conversation/ConversationPresenter.java   |  8 ++
 .../ring/conversation/ConversationView.java   |  1 +
 .../main/java/cx/ring/model/Conversation.java |  9 ++
 10 files changed, 221 insertions(+), 48 deletions(-)
 create mode 100644 ring-android/app/src/main/java/cx/ring/client/EmojiChooserBottomSheet.java
 create mode 100644 ring-android/app/src/main/res/layout/item_emoji.xml

diff --git a/ring-android/app/src/main/java/cx/ring/client/ContactDetailsActivity.java b/ring-android/app/src/main/java/cx/ring/client/ContactDetailsActivity.java
index ca63edba7..402cb1aec 100644
--- a/ring-android/app/src/main/java/cx/ring/client/ContactDetailsActivity.java
+++ b/ring-android/app/src/main/java/cx/ring/client/ContactDetailsActivity.java
@@ -50,7 +50,7 @@ import java.util.Map;
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
-import androidx.core.widget.ImageViewCompat;
+import androidx.core.view.ViewCompat;
 import androidx.recyclerview.widget.RecyclerView;
 
 import cx.ring.R;
@@ -100,6 +100,7 @@ public class ContactDetailsActivity extends AppCompatActivity {
         final IContactAction callback;
 
         int iconTint;
+        CharSequence iconSymbol;
 
         ContactAction(@DrawableRes int i, int tint, CharSequence t, IContactAction cb) {
             icon = i;
@@ -118,6 +119,10 @@ public class ContactDetailsActivity extends AppCompatActivity {
         void setIconTint(int tint) {
             iconTint = tint;
         }
+
+        void setSymbol(CharSequence t) {
+            iconSymbol = t;
+        }
     }
 
     static class ContactActionView extends RecyclerView.ViewHolder {
@@ -152,9 +157,10 @@ public class ContactDetailsActivity extends AppCompatActivity {
         @Override
         public void onBindViewHolder(@NonNull ContactActionView holder, int position) {
             ContactAction action = actions.get(position);
-            holder.binding.actionIcon.setImageResource(action.icon);
+            holder.binding.actionIcon.setBackgroundResource(action.icon);
+            holder.binding.actionIcon.setText(action.iconSymbol);
             if (action.iconTint != Color.BLACK)
-                ImageViewCompat.setImageTintList(holder.binding.actionIcon, ColorStateList.valueOf(action.iconTint));
+                ViewCompat.setBackgroundTintList(holder.binding.actionIcon, ColorStateList.valueOf(action.iconTint));
             holder.binding.actionTitle.setText(action.title);
             holder.callback = action.callback;
         }
@@ -169,8 +175,10 @@ public class ContactDetailsActivity extends AppCompatActivity {
     private final CompositeDisposable mDisposableBag = new CompositeDisposable();
 
     private ContactAction colorAction;
+    private ContactAction symbolAction;
     private ContactAction contactAction;
     private int colorActionPosition;
+    private int symbolActionPosition;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -193,6 +201,7 @@ public class ContactDetailsActivity extends AppCompatActivity {
         fab.setOnClickListener(view -> goToConversationActivity(mConversation.getAccountId(), mConversation.getUri()));
 
         colorActionPosition = 1;
+        symbolActionPosition = 2;
 
         mDisposableBag.add(mConversationFacade
                 .startConversation(path.getAccountId(), path.getConversationUri())
@@ -240,6 +249,18 @@ public class ContactDetailsActivity extends AppCompatActivity {
                     collapsingToolbarLayout.setStatusBarScrimColor(color);
                     adapter.actions.add(colorAction);
 
+                    symbolAction = new ContactAction(0, getText(R.string.conversation_preference_emoji), () -> {
+                        EmojiChooserBottomSheet frag = new EmojiChooserBottomSheet();
+                        frag.setCallback(s -> {
+                            symbolAction.setSymbol(s);
+                            adapter.notifyItemChanged(symbolActionPosition);
+                            mPreferences.edit().putString(ConversationFragment.KEY_PREFERENCE_CONVERSATION_SYMBOL, s).apply();
+                        });
+                        frag.show(getSupportFragmentManager(), "colorChooser");
+                    });
+                    symbolAction.setSymbol(mPreferences.getString(ConversationFragment.KEY_PREFERENCE_CONVERSATION_SYMBOL, getResources().getString(R.string.conversation_default_emoji)));
+                    adapter.actions.add(symbolAction);
+
                     if (mConversation.getContacts().size() <= 2) {
                         CallContact contact = mConversation.getContact();
                         adapter.actions.add(new ContactAction(R.drawable.baseline_call_24, getText(R.string.ab_action_audio_call), () ->
diff --git a/ring-android/app/src/main/java/cx/ring/client/EmojiChooserBottomSheet.java b/ring-android/app/src/main/java/cx/ring/client/EmojiChooserBottomSheet.java
new file mode 100644
index 000000000..ee7e294cd
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/client/EmojiChooserBottomSheet.java
@@ -0,0 +1,97 @@
+/*
+ *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Adrien BΓ©raud <adrien.beraud@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.
+ */
+package cx.ring.client;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.ArrayRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
+
+import cx.ring.R;
+
+public class EmojiChooserBottomSheet extends BottomSheetDialogFragment {
+
+    interface IEmojiSelected {
+        void onEmojiSelected(String emoji);
+    }
+
+    private IEmojiSelected callback;
+
+    public void setCallback(IEmojiSelected cb) {
+        callback = cb;
+    }
+
+    private class EmojiView extends RecyclerView.ViewHolder {
+        TextView view;
+        String emoji;
+
+        EmojiView(@NonNull View itemView) {
+            super(itemView);
+            view = (TextView) itemView;
+            itemView.setOnClickListener(v -> {
+                if (callback != null)
+                    callback.onEmojiSelected(emoji);
+                dismiss();
+            });
+        }
+    }
+
+    class ColorAdapter extends RecyclerView.Adapter<EmojiView>  {
+        private final String[] emojis;
+
+        public ColorAdapter(@ArrayRes int arrayResId) {
+            emojis = getResources().getStringArray(arrayResId);
+        }
+
+        @NonNull
+        @Override
+        public EmojiView onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+            View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_emoji, parent, false);
+            return new EmojiView(v);
+        }
+
+        @Override
+        public void onBindViewHolder(@NonNull EmojiView holder, int position) {
+            holder.emoji = emojis[position];
+            holder.view.setText(holder.emoji);
+        }
+
+        @Override
+        public int getItemCount() {
+            return emojis.length;
+        }
+    }
+
+    @Nullable
+    @Override
+    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+        RecyclerView view = (RecyclerView) inflater.inflate(R.layout.frag_color_chooser, container);
+        view.setAdapter(new ColorAdapter(R.array.conversation_emojis));
+        return view;
+    }
+}
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/ConversationFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/ConversationFragment.java
index 3280919e8..826c3fb12 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/ConversationFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/fragments/ConversationFragment.java
@@ -135,6 +135,7 @@ public class ConversationFragment extends BaseSupportFragment<ConversationPresen
     public static final String KEY_PREFERENCE_PENDING_MESSAGE = "pendingMessage";
     public static final String KEY_PREFERENCE_CONVERSATION_COLOR = "color";
     public static final String KEY_PREFERENCE_CONVERSATION_LAST_READ = "lastRead";
+    public static final String KEY_PREFERENCE_CONVERSATION_SYMBOL = "symbol";
     public static final String EXTRA_SHOW_MAP = "showMap";
 
     private static final int REQUEST_CODE_FILE_PICKER = 1000;
@@ -372,6 +373,11 @@ public class ConversationFragment extends BaseSupportFragment<ConversationPresen
         mAdapter.setPrimaryColor(color);
     }
 
+    @Override
+    public void setConversationSymbol(CharSequence symbol) {
+        binding.emojiSend.setText(symbol);
+    }
+
     @Override
     public void onDestroyView() {
         if (mPreferences != null)
@@ -898,8 +904,8 @@ public class ConversationFragment extends BaseSupportFragment<ConversationPresen
             mPreferences = requireActivity().getSharedPreferences(path.getAccountId() + "_" + uri.getUri(), Context.MODE_PRIVATE);
             mPreferences.registerOnSharedPreferenceChangeListener(this);
             presenter.setConversationColor(mPreferences.getInt(KEY_PREFERENCE_CONVERSATION_COLOR, getResources().getColor(R.color.color_primary_light)));
+            presenter.setConversationSymbol(mPreferences.getString(KEY_PREFERENCE_CONVERSATION_SYMBOL, getResources().getText(R.string.conversation_default_emoji).toString()));
             mLastRead = mPreferences.getString(KEY_PREFERENCE_CONVERSATION_LAST_READ, null);
-            Log.w(TAG, "Loaded last read " + mLastRead);
         } catch (Exception e) {
             Log.e(TAG, "Can't load conversation preferences");
         }
@@ -940,6 +946,9 @@ public class ConversationFragment extends BaseSupportFragment<ConversationPresen
             case KEY_PREFERENCE_CONVERSATION_COLOR:
                 presenter.setConversationColor(prefs.getInt(KEY_PREFERENCE_CONVERSATION_COLOR, getResources().getColor(R.color.color_primary_light)));
                 break;
+            case KEY_PREFERENCE_CONVERSATION_SYMBOL:
+                presenter.setConversationSymbol(prefs.getString(KEY_PREFERENCE_CONVERSATION_SYMBOL, getResources().getText(R.string.conversation_default_emoji).toString()));
+                break;
         }
     }
 
diff --git a/ring-android/app/src/main/java/cx/ring/tv/conversation/TvConversationFragment.java b/ring-android/app/src/main/java/cx/ring/tv/conversation/TvConversationFragment.java
index ee7b2b6e8..bcdae7809 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/conversation/TvConversationFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/conversation/TvConversationFragment.java
@@ -682,6 +682,11 @@ public class TvConversationFragment extends BaseSupportFragment<ConversationPres
 
     }
 
+    @Override
+    public void setConversationSymbol(CharSequence symbol) {
+
+    }
+
     @Override
     public void startShareLocation(String accountId, String contactId) {
 
diff --git a/ring-android/app/src/main/res/layout/item_contact_action.xml b/ring-android/app/src/main/res/layout/item_contact_action.xml
index 24e369e4e..5d1899054 100644
--- a/ring-android/app/src/main/res/layout/item_contact_action.xml
+++ b/ring-android/app/src/main/res/layout/item_contact_action.xml
@@ -1,49 +1,49 @@
 <?xml version="1.0" encoding="utf-8"?>
-<layout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools">
+<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:id="@+id/call_entry"
+    android:layout_width="match_parent"
+    android:layout_height="56dp"
+    android:background="?android:attr/selectableItemBackground"
+    android:descendantFocusability="blocksDescendants"
+    android:paddingLeft="16dp"
+    android:paddingTop="8dp"
+    android:paddingRight="16dp"
+    android:paddingBottom="8dp">
 
-    <data />
+    <TextView
+        android:id="@+id/action_icon"
+        android:layout_width="32dp"
+        android:layout_height="32dp"
+        android:layout_centerVertical="true"
+        android:layout_marginStart="4dp"
+        android:layout_marginTop="4dp"
+        android:layout_marginEnd="20dp"
+        android:layout_marginBottom="4dp"
+        android:backgroundTint="@color/colorPrimary"
+        android:gravity="center"
+        android:scaleType="fitCenter"
+        android:text=""
+        android:textSize="24dp"
+        android:textColor="@color/black"
+        app:tint="@color/colorPrimary"
+        tools:background="@drawable/baseline_call_24" />
 
-    <RelativeLayout
-        android:id="@+id/call_entry"
+    <TextView
+        android:id="@+id/action_title"
         android:layout_width="match_parent"
-        android:layout_height="56dp"
-        android:background="?android:attr/selectableItemBackground"
-        android:descendantFocusability="blocksDescendants"
-        android:paddingLeft="16dp"
-        android:paddingTop="8dp"
-        android:paddingRight="16dp"
-        android:paddingBottom="8dp">
+        android:layout_height="wrap_content"
+        android:layout_centerVertical="true"
+        android:layout_toEndOf="@id/action_icon"
+        android:ellipsize="middle"
+        android:gravity="start"
+        android:marqueeRepeatLimit="marquee_forever"
+        android:scrollHorizontally="true"
+        android:singleLine="true"
+        android:textAlignment="viewStart"
+        android:textColor="@color/textColorPrimary"
+        android:textSize="16sp"
+        tools:text="Start Audio Call" />
 
-        <ImageView
-            android:id="@+id/action_icon"
-            android:layout_width="40dp"
-            android:layout_height="40dp"
-            android:layout_centerVertical="true"
-            android:layout_marginEnd="16dp"
-            android:background="@null"
-            android:padding="4dp"
-            android:scaleType="fitCenter"
-            android:tint="@color/colorPrimary"
-            tools:src="@drawable/baseline_call_24" />
-
-        <TextView
-            android:id="@+id/action_title"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-
-            android:layout_centerVertical="true"
-            android:layout_toEndOf="@id/action_icon"
-            android:ellipsize="middle"
-            android:gravity="start"
-            android:marqueeRepeatLimit="marquee_forever"
-            android:scrollHorizontally="true"
-            android:singleLine="true"
-            android:textAlignment="viewStart"
-            android:textColor="@color/textColorPrimary"
-            android:textSize="16sp"
-            tools:text="Start Audio Call" />
-
-    </RelativeLayout>
-
-</layout>
\ No newline at end of file
+</RelativeLayout>
diff --git a/ring-android/app/src/main/res/layout/item_emoji.xml b/ring-android/app/src/main/res/layout/item_emoji.xml
new file mode 100644
index 000000000..79e8e6675
--- /dev/null
+++ b/ring-android/app/src/main/res/layout/item_emoji.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center"
+    android:padding="16dp"
+    android:text="@string/conversation_default_emoji"
+    android:textAlignment="center"
+    android:textSize="24dp"
+    android:textColor="@color/text_color"/>
diff --git a/ring-android/app/src/main/res/values/arrays.xml b/ring-android/app/src/main/res/values/arrays.xml
index 4e34db27f..5f56bf7e5 100644
--- a/ring-android/app/src/main/res/values/arrays.xml
+++ b/ring-android/app/src/main/res/values/arrays.xml
@@ -56,6 +56,19 @@ along with this program; if not, write to the Free Software
         <item>1080</item>
         <item>2160</item>
     </string-array>
+
+    <string-array name="conversation_emojis">
+        <item>πŸ‘</item>
+        <item>❀️</item>
+        <item>πŸ˜‚</item>
+        <item>πŸ’©</item>
+        <item>πŸ‘»</item>
+        <item>πŸ˜ƒ</item>
+        <item>✨</item>
+        <item>😍</item>
+        <item>πŸ™</item>
+    </string-array>
+
     <string translatable="false" name="video_resolution_default">480</string>
     <string translatable="false" name="video_resolution_default_tv">720</string>
 </resources>
diff --git a/ring-android/libringclient/src/main/java/cx/ring/conversation/ConversationPresenter.java b/ring-android/libringclient/src/main/java/cx/ring/conversation/ConversationPresenter.java
index 23c14a6ac..8a94b9d2a 100644
--- a/ring-android/libringclient/src/main/java/cx/ring/conversation/ConversationPresenter.java
+++ b/ring-android/libringclient/src/main/java/cx/ring/conversation/ConversationPresenter.java
@@ -262,6 +262,9 @@ public class ConversationPresenter extends RootPresenter<ConversationView> {
         mConversationDisposable.add(c.getColor()
                 .observeOn(mUiScheduler)
                 .subscribe(view::setConversationColor, e -> Log.e(TAG, "Can't update conversation color", e)));
+        mConversationDisposable.add(c.getSymbol()
+                .observeOn(mUiScheduler)
+                .subscribe(view::setConversationSymbol, e -> Log.e(TAG, "Can't update conversation color", e)));
 
         Log.e(TAG, "getLocationUpdates subscribe");
         mConversationDisposable.add(account
@@ -440,6 +443,11 @@ public class ConversationPresenter extends RootPresenter<ConversationView> {
                 .firstElement()
                 .subscribe(conversation -> conversation.setColor(color)));
     }
+    public void setConversationSymbol(CharSequence symbol) {
+        mCompositeDisposable.add(mConversationSubject
+                .firstElement()
+                .subscribe(conversation -> conversation.setSymbol(symbol)));
+    }
 
     public void cameraPermissionChanged(boolean isGranted) {
         if (isGranted && mHardwareService.isVideoAvailable()) {
diff --git a/ring-android/libringclient/src/main/java/cx/ring/conversation/ConversationView.java b/ring-android/libringclient/src/main/java/cx/ring/conversation/ConversationView.java
index 47e8ccc50..8c881ec01 100644
--- a/ring-android/libringclient/src/main/java/cx/ring/conversation/ConversationView.java
+++ b/ring-android/libringclient/src/main/java/cx/ring/conversation/ConversationView.java
@@ -83,6 +83,7 @@ public interface ConversationView extends BaseView {
     void setLastDisplayed(Interaction interaction);
 
     void setConversationColor(int integer);
+    void setConversationSymbol(CharSequence symbol);
 
     void startSaveFile(DataTransfer currentFile, String fileAbsolutePath);
 
diff --git a/ring-android/libringclient/src/main/java/cx/ring/model/Conversation.java b/ring-android/libringclient/src/main/java/cx/ring/model/Conversation.java
index 1c37a4145..954b27598 100644
--- a/ring-android/libringclient/src/main/java/cx/ring/model/Conversation.java
+++ b/ring-android/libringclient/src/main/java/cx/ring/model/Conversation.java
@@ -60,6 +60,7 @@ public class Conversation extends ConversationHistory {
     private final Subject<List<Conference>> callsSubject = BehaviorSubject.create();
     private final Subject<Account.ComposingStatus> composingStatusSubject = BehaviorSubject.createDefault(Account.ComposingStatus.Idle);
     private final Subject<Integer> color = BehaviorSubject.create();
+    private final Subject<CharSequence> symbol = BehaviorSubject.create();
     private final Subject<List<CallContact>> mContactSubject = BehaviorSubject.create();
 
     private Single<Conversation> isLoaded = null;
@@ -648,9 +649,17 @@ public class Conversation extends ConversationHistory {
         color.onNext(c);
     }
 
+    public void setSymbol(CharSequence s) {
+        symbol.onNext(s);
+    }
+
     public Observable<Integer> getColor() {
         return color;
     }
+    public Observable<CharSequence> getSymbol() {
+        return symbol;
+    }
+
 
     public String getAccountId() {
         return mAccountId;
-- 
GitLab