diff --git a/.travis.yml b/.travis.yml
index 9ffa188f9..2e3817153 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,4 +1,5 @@
language: android
+
android:
components:
# Uncomment the lines below if you want to
diff --git a/Habitica/build.gradle b/Habitica/build.gradle
index 71cb820f0..c7e181c3b 100644
--- a/Habitica/build.gradle
+++ b/Habitica/build.gradle
@@ -26,6 +26,11 @@ repositories {
url "http://dl.bintray.com/florent37/maven"
}
+ // Markdown
+ maven {
+ url "https://s3.amazonaws.com/repo.commonsware.com"
+ }
+
maven {
url "https://jitpack.io"
}
@@ -61,6 +66,12 @@ dependencies {
// Icons
compile 'com.mikepenz:fontawesome-typeface:4.4.0.1@aar'
+ // Emojis
+ compile 'com.github.data5tream:emoji-lib:0.0.2.1'
+
+ // Markdown
+ compile 'com.commonsware.cwac:anddown:0.2.4'
+
// Changelog Fragment, minSDK 17
compile 'com.github.porokoro.paperboy:paperboy:2.1.0'
diff --git a/Habitica/res/drawable-hdpi/ic_emoticon_grey600_24dp.png b/Habitica/res/drawable-hdpi/ic_emoticon_grey600_24dp.png
new file mode 100644
index 000000000..6a1b45580
Binary files /dev/null and b/Habitica/res/drawable-hdpi/ic_emoticon_grey600_24dp.png differ
diff --git a/Habitica/res/drawable-hdpi/ic_keyboard_grey600_24dp.png b/Habitica/res/drawable-hdpi/ic_keyboard_grey600_24dp.png
new file mode 100644
index 000000000..75da33507
Binary files /dev/null and b/Habitica/res/drawable-hdpi/ic_keyboard_grey600_24dp.png differ
diff --git a/Habitica/res/drawable-mdpi/ic_emoticon_grey600_24dp.png b/Habitica/res/drawable-mdpi/ic_emoticon_grey600_24dp.png
new file mode 100644
index 000000000..a744009de
Binary files /dev/null and b/Habitica/res/drawable-mdpi/ic_emoticon_grey600_24dp.png differ
diff --git a/Habitica/res/drawable-mdpi/ic_keyboard_grey600_24dp.png b/Habitica/res/drawable-mdpi/ic_keyboard_grey600_24dp.png
new file mode 100644
index 000000000..bd8001ad7
Binary files /dev/null and b/Habitica/res/drawable-mdpi/ic_keyboard_grey600_24dp.png differ
diff --git a/Habitica/res/drawable-xhdpi/ic_emoticon_grey600_24dp.png b/Habitica/res/drawable-xhdpi/ic_emoticon_grey600_24dp.png
new file mode 100644
index 000000000..ccb6517ba
Binary files /dev/null and b/Habitica/res/drawable-xhdpi/ic_emoticon_grey600_24dp.png differ
diff --git a/Habitica/res/drawable-xhdpi/ic_keyboard_grey600_24dp.png b/Habitica/res/drawable-xhdpi/ic_keyboard_grey600_24dp.png
new file mode 100644
index 000000000..64032e826
Binary files /dev/null and b/Habitica/res/drawable-xhdpi/ic_keyboard_grey600_24dp.png differ
diff --git a/Habitica/res/drawable-xxhdpi/ic_emoticon_grey600_24dp.png b/Habitica/res/drawable-xxhdpi/ic_emoticon_grey600_24dp.png
new file mode 100644
index 000000000..0f9177662
Binary files /dev/null and b/Habitica/res/drawable-xxhdpi/ic_emoticon_grey600_24dp.png differ
diff --git a/Habitica/res/drawable-xxhdpi/ic_keyboard_grey600_24dp.png b/Habitica/res/drawable-xxhdpi/ic_keyboard_grey600_24dp.png
new file mode 100644
index 000000000..4caef1c60
Binary files /dev/null and b/Habitica/res/drawable-xxhdpi/ic_keyboard_grey600_24dp.png differ
diff --git a/Habitica/res/drawable-xxxhdpi/ic_emoticon_grey600_24dp.png b/Habitica/res/drawable-xxxhdpi/ic_emoticon_grey600_24dp.png
new file mode 100644
index 000000000..9871f25a1
Binary files /dev/null and b/Habitica/res/drawable-xxxhdpi/ic_emoticon_grey600_24dp.png differ
diff --git a/Habitica/res/drawable-xxxhdpi/ic_keyboard_grey600_24dp.png b/Habitica/res/drawable-xxxhdpi/ic_keyboard_grey600_24dp.png
new file mode 100644
index 000000000..fffa8845d
Binary files /dev/null and b/Habitica/res/drawable-xxxhdpi/ic_keyboard_grey600_24dp.png differ
diff --git a/Habitica/res/drawable/emoticon.xml b/Habitica/res/drawable/emoticon.xml
new file mode 100644
index 000000000..99f885520
--- /dev/null
+++ b/Habitica/res/drawable/emoticon.xml
@@ -0,0 +1,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/drawable/keyboard.xml b/Habitica/res/drawable/keyboard.xml
new file mode 100644
index 000000000..df6149a29
--- /dev/null
+++ b/Habitica/res/drawable/keyboard.xml
@@ -0,0 +1,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/layout/activity_task_form.xml b/Habitica/res/layout/activity_task_form.xml
index 888371687..398d87290 100644
--- a/Habitica/res/layout/activity_task_form.xml
+++ b/Habitica/res/layout/activity_task_form.xml
@@ -12,7 +12,6 @@
android:orientation="vertical"
android:padding="@dimen/abc_action_bar_content_inset_material">
-
-
+ android:orientation="horizontal" >
-
+
+
+
+
-
-
+
+ android:orientation="horizontal">
-
+
+
+
+
-
-
+
-
+
+
-
\ No newline at end of file
+
diff --git a/Habitica/res/layout/checklist_item.xml b/Habitica/res/layout/checklist_item.xml
index 0b0e72d40..31f2e265f 100644
--- a/Habitica/res/layout/checklist_item.xml
+++ b/Habitica/res/layout/checklist_item.xml
@@ -17,7 +17,7 @@
android:id="@+id/delete_item_button"
android:text="x" />
-
-
-
+
@@ -58,18 +59,18 @@
android:layout_alignParentStart="true"
android:layout_toLeftOf="@+id/checklistIndicatorWrapper"
android:layout_toStartOf="@+id/checklistIndicatorWrapper">
-
-
+
-
-
-
+ bind:parsemarkdown="@{party.description}" />
diff --git a/Habitica/res/layout/habit_item_card.xml b/Habitica/res/layout/habit_item_card.xml
index 3d30bddc1..918d17267 100644
--- a/Habitica/res/layout/habit_item_card.xml
+++ b/Habitica/res/layout/habit_item_card.xml
@@ -1,5 +1,6 @@
-
+
@@ -73,19 +74,19 @@
android:paddingEnd="16dp"
android:paddingBottom="@dimen/task_top_bottom_padding"
android:paddingTop="@dimen/task_top_bottom_padding">
-
+ bind:parsemarkdown="@{habit.text}" />
-
-
+ bind:parsemarkdown="@{reward.text}"/>
-
diff --git a/Habitica/res/layout/tavern_chat_item.xml b/Habitica/res/layout/tavern_chat_item.xml
index f89fadcf9..f1db4e57b 100644
--- a/Habitica/res/layout/tavern_chat_item.xml
+++ b/Habitica/res/layout/tavern_chat_item.xml
@@ -97,7 +97,7 @@
-
+
+
-
android:inputType="textCapSentences" />
@@ -44,4 +53,4 @@
android:drawableTint="@color/white"/>
-
\ No newline at end of file
+
diff --git a/Habitica/res/layout/todo_item_card.xml b/Habitica/res/layout/todo_item_card.xml
index 75f55d111..3e826ed1a 100644
--- a/Habitica/res/layout/todo_item_card.xml
+++ b/Habitica/res/layout/todo_item_card.xml
@@ -1,6 +1,7 @@
-
+
@@ -52,19 +53,19 @@
android:paddingEnd="16dp"
android:paddingBottom="@dimen/task_top_bottom_padding"
android:paddingTop="@dimen/task_top_bottom_padding">
-
+ bind:parsemarkdown="@{todo.text}" />
-
{
View view;
@Bind(R.id.editText)
- EditText editText;
+ EmojiEditText editText;
@Bind(R.id.btnAdd)
Button btnAdd;
diff --git a/Habitica/src/com/habitrpg/android/habitica/ui/activities/TaskFormActivity.java b/Habitica/src/com/habitrpg/android/habitica/ui/activities/TaskFormActivity.java
index 5714996a1..42c5ac37c 100644
--- a/Habitica/src/com/habitrpg/android/habitica/ui/activities/TaskFormActivity.java
+++ b/Habitica/src/com/habitrpg/android/habitica/ui/activities/TaskFormActivity.java
@@ -5,10 +5,12 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.TextInputLayout;
+import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
+import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@@ -18,15 +20,21 @@ import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.DatePicker;
-import android.widget.EditText;
+import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.NumberPicker;
+import android.widget.PopupWindow;
import android.widget.Spinner;
import android.widget.TextView;
+import com.github.data5tream.emojilib.EmojiEditText;
+import com.github.data5tream.emojilib.EmojiGridView;
+import com.github.data5tream.emojilib.EmojiPopup;
+import com.github.data5tream.emojilib.emoji.Emojicon;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.events.TaskSaveEvent;
import com.habitrpg.android.habitica.events.commands.DeleteTaskCommand;
+import com.habitrpg.android.habitica.ui.helpers.MarkdownParser;
import com.habitrpg.android.habitica.ui.WrapContentRecyclerViewLayoutManager;
import com.habitrpg.android.habitica.ui.adapter.tasks.CheckListAdapter;
import com.habitrpg.android.habitica.ui.helpers.SimpleItemTouchHelperCallback;
@@ -63,7 +71,7 @@ public class TaskFormActivity extends BaseActivity implements AdapterView.OnItem
private CheckListAdapter checklistAdapter;
@Bind(R.id.task_value_edittext)
- EditText taskValue;
+ EmojiEditText taskValue;
@Bind(R.id.task_value_layout)
TextInputLayout taskValueLayout;
@@ -81,10 +89,10 @@ public class TaskFormActivity extends BaseActivity implements AdapterView.OnItem
LinearLayout mainWrapper;
@Bind(R.id.task_text_edittext)
- EditText taskText;
+ EmojiEditText taskText;
@Bind(R.id.task_notes_edittext)
- EditText taskNotes;
+ EmojiEditText taskNotes;
@Bind(R.id.task_difficulty_spinner)
Spinner taskDifficultySpinner;
@@ -120,14 +128,23 @@ public class TaskFormActivity extends BaseActivity implements AdapterView.OnItem
RecyclerView recyclerView;
@Bind(R.id.new_checklist)
- EditText newCheckListEditText;
+ EmojiEditText newCheckListEditText;
@Bind(R.id.add_checklist_button)
Button button;
+ @Bind(R.id.emoji_toggle_btn0)
+ ImageButton emojiToggle0;
+
+ @Bind(R.id.emoji_toggle_btn1)
+ ImageButton emojiToggle1;
+
+
+ ImageButton emojiToggle2;
@Bind(R.id.task_duedate_layout)
LinearLayout dueDateLayout;
+ EmojiPopup popup;
@Bind(R.id.task_duedate_picker)
DatePicker dueDatePicker;
@@ -150,7 +167,7 @@ public class TaskFormActivity extends BaseActivity implements AdapterView.OnItem
}
btnDelete.setEnabled(false);
- ViewHelper.SetBackgroundTint(btnDelete, getResources().getColor(R.color.worse_10));
+ ViewHelper.SetBackgroundTint(btnDelete, ContextCompat.getColor(this, R.color.worse_10));
btnDelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
@@ -231,11 +248,140 @@ public class TaskFormActivity extends BaseActivity implements AdapterView.OnItem
btnDelete.setEnabled(true);
} else {
setTitle((Task) null);
+ taskText.requestFocus();
}
if (taskType.equals("todo") || taskType.equals("daily")) {
createCheckListRecyclerView();
}
+
+ // Emoji keyboard stuff
+ boolean isTodo = false;
+ if (taskType.equals("todo")) {
+ isTodo = true;
+ }
+
+ // If it's a to-do, change the emojiToggle2 to the actual emojiToggle2 (prevents NPEs when not a to-do task)
+ if (isTodo) {
+ emojiToggle2 = (ImageButton) findViewById(R.id.emoji_toggle_btn2);
+ } else {
+ emojiToggle2 = emojiToggle0;
+ }
+
+ popup = new EmojiPopup(emojiToggle0.getRootView(), this, ContextCompat.getColor(this, R.color.brand));
+
+ popup.setSizeForSoftKeyboard();
+ popup.setOnDismissListener(new PopupWindow.OnDismissListener() {
+
+ @Override
+ public void onDismiss() {
+ changeEmojiKeyboardIcon(false);
+ }
+ });
+ popup.setOnSoftKeyboardOpenCloseListener(new EmojiPopup.OnSoftKeyboardOpenCloseListener() {
+
+ @Override
+ public void onKeyboardOpen(int keyBoardHeight) {
+
+ }
+
+ @Override
+ public void onKeyboardClose() {
+ if (popup.isShowing())
+ popup.dismiss();
+ }
+ });
+
+ popup.setOnEmojiconClickedListener(new EmojiGridView.OnEmojiconClickedListener() {
+
+ @Override
+ public void onEmojiconClicked(Emojicon emojicon) {
+ EmojiEditText emojiEditText = null;
+ if (getCurrentFocus() == null || !isEmojiEditText(getCurrentFocus()) || emojicon == null) {
+ return;
+ } else {
+ emojiEditText = (EmojiEditText) getCurrentFocus();
+ }
+ int start = emojiEditText.getSelectionStart();
+ int end = emojiEditText.getSelectionEnd();
+ if (start < 0) {
+ emojiEditText.append(emojicon.getEmoji());
+ } else {
+ emojiEditText.getText().replace(Math.min(start, end),
+ Math.max(start, end), emojicon.getEmoji(), 0,
+ emojicon.getEmoji().length());
+ }
+ }
+ });
+
+ popup.setOnEmojiconBackspaceClickedListener(new EmojiPopup.OnEmojiconBackspaceClickedListener() {
+
+ @Override
+ public void onEmojiconBackspaceClicked(View v) {
+ if (isEmojiEditText(getCurrentFocus())) {
+ KeyEvent event = new KeyEvent(
+ 0, 0, 0, KeyEvent.KEYCODE_DEL, 0, 0, 0, 0, KeyEvent.KEYCODE_ENDCALL);
+ getCurrentFocus().dispatchKeyEvent(event);
+ }
+ }
+ });
+
+ emojiToggle0.setOnClickListener(new emojiClickListener(taskText));
+ emojiToggle1.setOnClickListener(new emojiClickListener(taskNotes));
+ if (isTodo) {
+ emojiToggle2.setOnClickListener(new emojiClickListener(newCheckListEditText));
+ }
+ }
+
+ private boolean isEmojiEditText(View view) {
+ return view instanceof EmojiEditText;
+ }
+
+ private void changeEmojiKeyboardIcon(Boolean keyboardOpened) {
+
+ if (keyboardOpened) {
+ emojiToggle0.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_keyboard_grey600_24dp));
+ emojiToggle1.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_keyboard_grey600_24dp));
+ emojiToggle2.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_keyboard_grey600_24dp));
+ } else {
+ emojiToggle0.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_emoticon_grey600_24dp));
+ emojiToggle1.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_emoticon_grey600_24dp));
+ emojiToggle2.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_emoticon_grey600_24dp));
+ }
+ }
+
+ private class emojiClickListener implements View.OnClickListener {
+
+ EmojiEditText view;
+
+ public emojiClickListener(EmojiEditText view) {
+ this.view = view;
+ }
+
+ @Override
+ public void onClick(View v) {
+ if(!popup.isShowing()){
+
+ if(popup.isKeyBoardOpen()){
+ popup.showAtBottom();
+ changeEmojiKeyboardIcon(true);
+ }
+
+ else{
+ view.setFocusableInTouchMode(true);
+ view.requestFocus();
+ popup.showAtBottomPending();
+ final InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ inputMethodManager.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
+ changeEmojiKeyboardIcon(true);
+ }
+ }
+
+ else{
+ popup.dismiss();
+ changeEmojiKeyboardIcon(false);
+ }
+ }
}
private void createCheckListRecyclerView() {
@@ -385,9 +531,9 @@ public class TaskFormActivity extends BaseActivity implements AdapterView.OnItem
if (task.type.equals("daily")) {
if (task.getStartDate() != null) {
- Calendar calendar = new GregorianCalendar();
- calendar.setTime(task.getStartDate());
- startDatePicker.updateDate(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH));
+ GregorianCalendar calendar = new GregorianCalendar();
+ calendar.setTime(task.getStartDate());
+ startDatePicker.updateDate(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH));
}
if (task.getFrequency().equals("weekly")) {
@@ -420,7 +566,7 @@ public class TaskFormActivity extends BaseActivity implements AdapterView.OnItem
}
private boolean saveTask(Task task) {
- task.text = taskText.getText().toString();
+ task.text = MarkdownParser.parseCompiled(taskText.getText());
if (checklistAdapter != null) {
if (checklistAdapter.getCheckListItems() != null) {
@@ -431,7 +577,7 @@ public class TaskFormActivity extends BaseActivity implements AdapterView.OnItem
if (task.text.isEmpty())
return false;
- task.notes = taskNotes.getText().toString();
+ task.notes = MarkdownParser.parseCompiled(taskNotes.getText());
if (this.taskDifficultySpinner.getSelectedItemPosition() == 0) {
task.setPriority((float) 0.1);
@@ -451,7 +597,7 @@ public class TaskFormActivity extends BaseActivity implements AdapterView.OnItem
break;
case "daily": {
- Calendar calendar = new GregorianCalendar();
+ GregorianCalendar calendar = new GregorianCalendar();
calendar.set(startDatePicker.getYear(), startDatePicker.getMonth(), startDatePicker.getDayOfMonth());
task.setStartDate(new Date(calendar.getTimeInMillis()));
diff --git a/Habitica/src/com/habitrpg/android/habitica/ui/adapter/social/ChatRecyclerViewAdapter.java b/Habitica/src/com/habitrpg/android/habitica/ui/adapter/social/ChatRecyclerViewAdapter.java
index cf7ec631a..49d8c3e73 100644
--- a/Habitica/src/com/habitrpg/android/habitica/ui/adapter/social/ChatRecyclerViewAdapter.java
+++ b/Habitica/src/com/habitrpg/android/habitica/ui/adapter/social/ChatRecyclerViewAdapter.java
@@ -3,7 +3,7 @@ package com.habitrpg.android.habitica.ui.adapter.social;
import android.content.Context;
import android.content.res.Resources;
import android.support.annotation.Nullable;
-import android.support.v7.widget.AppCompatEditText;
+import android.support.v4.content.ContextCompat;
import android.support.v7.widget.PopupMenu;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
@@ -15,6 +15,8 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
+import com.github.data5tream.emojilib.EmojiEditText;
+import com.github.data5tream.emojilib.EmojiTextView;
import com.habitrpg.android.habitica.HabiticaApplication;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.events.commands.CopyChatAsTodoCommand;
@@ -24,6 +26,7 @@ import com.habitrpg.android.habitica.events.commands.OpenNewPMActivityCommand;
import com.habitrpg.android.habitica.events.commands.SendNewGroupMessageCommand;
import com.habitrpg.android.habitica.events.commands.ToggleInnCommand;
import com.habitrpg.android.habitica.events.commands.ToggleLikeMessageCommand;
+import com.habitrpg.android.habitica.ui.helpers.EmojiKeyboard;
import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils;
import com.habitrpg.android.habitica.ui.helpers.ViewHelper;
import com.magicmicky.habitrpgwrapper.lib.models.ChatMessage;
@@ -137,7 +140,7 @@ public class ChatRecyclerViewAdapter extends RecyclerView.Adapter
public HabitViewHolder(View itemView) {
super(itemView);
+
binding = DataBindingUtil.bind(itemView);
btnPlus.setClickable(true);
@@ -340,7 +343,6 @@ public class HabitItemRecyclerViewAdapter
@Override
public void bindHolder(Task habitItem, int position) {
super.bindHolder(habitItem, position);
-
binding.setHabit(habitItem);
}
}
@@ -409,7 +411,7 @@ public class HabitItemRecyclerViewAdapter
for (ChecklistItem item : this.Item.checklist) {
LinearLayout itemView = (LinearLayout) layoutInflater.inflate(R.layout.checklist_item_row, null);
CheckBox checkbox = (CheckBox) itemView.findViewById(R.id.checkBox);
- TextView textView = (TextView) itemView.findViewById(R.id.checkedTextView);
+ EmojiTextView textView = (EmojiTextView) itemView.findViewById(R.id.checkedTextView);
// Populate the data into the template view using the data object
textView.setText(item.getText());
checkbox.setChecked(item.getCompleted());
@@ -548,7 +550,7 @@ public class HabitItemRecyclerViewAdapter
EventBus.getDefault().post(event);
}
- };
+ }
}
private MaterialDialog createGearDialog(LinearLayout contentViewForDialog) {
@@ -562,7 +564,7 @@ public class HabitItemRecyclerViewAdapter
}
})
.contentGravity(GravityEnum.CENTER)
- .positiveColor(context.getResources().getColor(R.color.brand_200))
+ .positiveColor(ContextCompat.getColor(context, R.color.brand_200))
.positiveText(R.string.reward_dialog_buy)
.title(binding.getReward().getText())
.customView(contentViewForDialog, true)
@@ -620,7 +622,7 @@ public class HabitItemRecyclerViewAdapter
priceTextView.setPadding(10, 0, 0, 0);
ImageView gold = new ImageView(context);
- gold.setImageDrawable(context.getResources().getDrawable(R.drawable.ic_header_gold));
+ gold.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_header_gold));
gold.setMinimumHeight(50);
gold.setMinimumWidth(50);
gold.setPadding(0, 0, 5, 0);
diff --git a/Habitica/src/com/habitrpg/android/habitica/ui/fragments/social/ChatListFragment.java b/Habitica/src/com/habitrpg/android/habitica/ui/fragments/social/ChatListFragment.java
index 316a0e1da..dd0a0379a 100644
--- a/Habitica/src/com/habitrpg/android/habitica/ui/fragments/social/ChatListFragment.java
+++ b/Habitica/src/com/habitrpg/android/habitica/ui/fragments/social/ChatListFragment.java
@@ -2,6 +2,7 @@ package com.habitrpg.android.habitica.ui.fragments.social;
import android.content.Context;
import android.content.DialogInterface;
+import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
@@ -12,6 +13,7 @@ import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.ImageView;
import com.habitrpg.android.habitica.APIHelper;
import com.habitrpg.android.habitica.HabiticaApplication;
@@ -26,6 +28,7 @@ import com.habitrpg.android.habitica.events.commands.ToggleInnCommand;
import com.habitrpg.android.habitica.events.commands.ToggleLikeMessageCommand;
import com.habitrpg.android.habitica.ui.UiUtils;
import com.habitrpg.android.habitica.ui.adapter.social.ChatRecyclerViewAdapter;
+import com.habitrpg.android.habitica.ui.helpers.MarkdownParser;
import com.magicmicky.habitrpgwrapper.lib.models.ChatMessage;
import com.magicmicky.habitrpgwrapper.lib.models.HabitRPGUser;
import com.magicmicky.habitrpgwrapper.lib.models.PostChatMessageResult;
@@ -121,7 +124,7 @@ public class ChatListFragment extends Fragment implements SwipeRefreshLayout.OnR
LinearLayoutManager layoutManager;
@Override
- public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+ public void onViewCreated(final View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ButterKnife.bind(this, view);
@@ -164,6 +167,7 @@ public class ChatListFragment extends Fragment implements SwipeRefreshLayout.OnR
public void success(List chatMessages, Response response) {
currentChatMessages = chatMessages;
+ //Load unparsed messages first
ChatRecyclerViewAdapter tavernAdapter = new ChatRecyclerViewAdapter(chatMessages, ctx, userId, groupId, isTavern);
if(mRecyclerView != null) {
@@ -173,6 +177,9 @@ public class ChatListFragment extends Fragment implements SwipeRefreshLayout.OnR
swipeRefreshLayout.setRefreshing(false);
}
+ //Parse chatMessages in AsyncTask
+ ParseMessages parseMessages = new ParseMessages(chatMessages);
+ parseMessages.execute();
gotNewMessages = true;
markMessagesAsSeen();
@@ -183,7 +190,7 @@ public class ChatListFragment extends Fragment implements SwipeRefreshLayout.OnR
private void markMessagesAsSeen(){
if(!isTavern && seenGroupId != null && !seenGroupId.isEmpty()
- && gotNewMessages && navigatedOnceToFragment) {
+ && gotNewMessages && navigatedOnceToFragment) {
gotNewMessages = false;
@@ -319,4 +326,34 @@ public class ChatListFragment extends Fragment implements SwipeRefreshLayout.OnR
super.onSaveInstanceState(outState);
}
+ private class ParseMessages extends AsyncTask {
+ private List chatMessages;
+
+ public ParseMessages(List chatMessages) {
+ this.chatMessages = chatMessages;
+ }
+
+ @Override
+ protected Void doInBackground(Void... params) {
+
+ for (int i = 0; i < chatMessages.size() ; i++) {
+ chatMessages.get(i).parsedText = MarkdownParser.parseMarkdown(chatMessages.get(i).text);
+ }
+
+ return null;
+ }
+
+ protected void onPostExecute(Void result) {
+ ChatRecyclerViewAdapter tavernAdapter = new ChatRecyclerViewAdapter(chatMessages, ctx, userId, groupId, isTavern);
+
+ if(mRecyclerView != null) {
+ mRecyclerView.setAdapter(tavernAdapter);
+ }
+
+ if (swipeRefreshLayout != null) {
+ swipeRefreshLayout.setRefreshing(false);
+ }
+ }
+ }
+
}
diff --git a/Habitica/src/com/habitrpg/android/habitica/ui/helpers/DataBindingUtils.java b/Habitica/src/com/habitrpg/android/habitica/ui/helpers/DataBindingUtils.java
index f859ccb10..3a473fef9 100644
--- a/Habitica/src/com/habitrpg/android/habitica/ui/helpers/DataBindingUtils.java
+++ b/Habitica/src/com/habitrpg/android/habitica/ui/helpers/DataBindingUtils.java
@@ -15,6 +15,7 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
+import com.github.data5tream.emojilib.EmojiTextView;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.databinding.ValueBarBinding;
import com.squareup.picasso.Picasso;
@@ -115,6 +116,13 @@ public class DataBindingUtils {
}
}
+ @BindingAdapter("parsemarkdown")
+ public static void bindEmojiconTextView(EmojiTextView textView, CharSequence value) {
+ if (value != null) {
+ textView.setText(MarkdownParser.parseMarkdown(value.toString()));
+ }
+ }
+
public static class LayoutWeightAnimation extends Animation {
float targetWeight;
float initializeWeight;
diff --git a/Habitica/src/com/habitrpg/android/habitica/ui/helpers/EmojiKeyboard.java b/Habitica/src/com/habitrpg/android/habitica/ui/helpers/EmojiKeyboard.java
new file mode 100644
index 000000000..f818666f2
--- /dev/null
+++ b/Habitica/src/com/habitrpg/android/habitica/ui/helpers/EmojiKeyboard.java
@@ -0,0 +1,127 @@
+package com.habitrpg.android.habitica.ui.helpers;
+
+import android.content.Context;
+import android.support.v4.content.ContextCompat;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.ImageButton;
+import android.widget.PopupWindow;
+
+import com.github.data5tream.emojilib.EmojiEditText;
+import com.github.data5tream.emojilib.EmojiGridView;
+import com.github.data5tream.emojilib.EmojiPopup;
+import com.github.data5tream.emojilib.emoji.Emojicon;
+import com.habitrpg.android.habitica.R;
+
+/**
+ * @author data5tream
+ */
+public class EmojiKeyboard {
+
+ /**
+ * Create a Emoji keyboard
+ *
+ * @param itemView Must contain views with the following IDs:
+ * 'emoji.toggle.btn' for the ImageButton that is used to enable/disable the emoji keyboard
+ * 'edit.new.message.text' for the EmojiEditText where the emojis are put into
+ * @param context The context from the calling Activity
+ */
+ public static void createKeyboard(View itemView, final Context context) {
+
+ final ImageButton emojiButton = (ImageButton) itemView.findViewById(R.id.emoji_toggle_btn);
+ final EmojiEditText emojiEditText = (EmojiEditText) itemView.findViewById(R.id.edit_new_message_text);
+ final EmojiPopup popup = new EmojiPopup(itemView.getRootView(), context, ContextCompat.getColor(context, R.color.brand));
+
+ popup.setSizeForSoftKeyboard();
+
+ popup.setOnDismissListener(new PopupWindow.OnDismissListener() {
+
+ @Override
+ public void onDismiss() {
+ changeEmojiKeyboardIcon(emojiButton, context, false);
+ }
+ });
+
+ popup.setOnSoftKeyboardOpenCloseListener(new EmojiPopup.OnSoftKeyboardOpenCloseListener() {
+
+ @Override
+ public void onKeyboardOpen(int keyBoardHeight) {
+
+ }
+
+ @Override
+ public void onKeyboardClose() {
+ if(popup.isShowing())
+ popup.dismiss();
+ }
+ });
+
+ popup.setOnEmojiconClickedListener(new EmojiGridView.OnEmojiconClickedListener() {
+
+ @Override
+ public void onEmojiconClicked(Emojicon emojicon) {
+ if (emojiEditText == null || emojicon == null) {
+ return;
+ }
+
+ int start = emojiEditText.getSelectionStart();
+ int end = emojiEditText.getSelectionEnd();
+ if (start < 0) {
+ emojiEditText.append(emojicon.getEmoji());
+ } else {
+ emojiEditText.getText().replace(Math.min(start, end),
+ Math.max(start, end), emojicon.getEmoji(), 0,
+ emojicon.getEmoji().length());
+ }
+ }
+ });
+
+ popup.setOnEmojiconBackspaceClickedListener(new EmojiPopup.OnEmojiconBackspaceClickedListener() {
+
+ @Override
+ public void onEmojiconBackspaceClicked(View v) {
+ KeyEvent event = new KeyEvent(
+ 0, 0, 0, KeyEvent.KEYCODE_DEL, 0, 0, 0, 0, KeyEvent.KEYCODE_ENDCALL);
+ emojiEditText.dispatchKeyEvent(event);
+ }
+ });
+
+ emojiButton.setOnClickListener(new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+
+ if(!popup.isShowing()){
+
+ if(popup.isKeyBoardOpen()){
+ popup.showAtBottom();
+ changeEmojiKeyboardIcon(emojiButton, context, true);
+ }
+
+ else{
+ emojiEditText.setFocusableInTouchMode(true);
+ emojiEditText.requestFocus();
+ popup.showAtBottomPending();
+ final InputMethodManager inputMethodManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
+ inputMethodManager.showSoftInput(emojiEditText, InputMethodManager.SHOW_IMPLICIT);
+ changeEmojiKeyboardIcon(emojiButton, context, true);
+ }
+ }
+
+ else{
+ popup.dismiss();
+ changeEmojiKeyboardIcon(emojiButton, context, false);
+ }
+ }
+ });
+ }
+ private static void changeEmojiKeyboardIcon(ImageButton view, Context context, Boolean keyboardOpened) {
+
+ if (keyboardOpened) {
+ view.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_keyboard_grey600_24dp));
+ } else {
+ view.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_emoticon_grey600_24dp));
+ }
+ }
+}
diff --git a/Habitica/src/com/habitrpg/android/habitica/ui/helpers/MarkdownParser.java b/Habitica/src/com/habitrpg/android/habitica/ui/helpers/MarkdownParser.java
new file mode 100644
index 000000000..174df89f7
--- /dev/null
+++ b/Habitica/src/com/habitrpg/android/habitica/ui/helpers/MarkdownParser.java
@@ -0,0 +1,37 @@
+package com.habitrpg.android.habitica.ui.helpers;
+
+import android.text.Html;
+
+import com.commonsware.cwac.anddown.AndDown;
+import com.github.data5tream.emojilib.EmojiParser;
+
+/**
+ * @author data5tream
+ */
+public class MarkdownParser {
+
+ static AndDown processor = new AndDown();
+
+ /**
+ * Parses formatted markdown and returns it as styled CharSequence
+ *
+ * @param input Markdown formatted String
+ * @return Stylized CharSequence
+ */
+ public static CharSequence parseMarkdown(String input) {
+ CharSequence output = Html.fromHtml(processor.markdownToHtml(EmojiParser.parseEmojis(input.trim())));
+ if (output.length() >= 2) output = output.subSequence(0, output.length() - 2);
+ return output;
+ }
+
+ /**
+ * Converts stylized CharSequence into markdown
+ *
+ * @param input Stylized CharSequence
+ * @return Markdown formatted String
+ */
+ public static String parseCompiled(CharSequence input) {
+ return EmojiParser.convertToCheatCode(input.toString());
+ }
+
+}
diff --git a/Habitica/src/com/magicmicky/habitrpgwrapper/lib/models/ChatMessage.java b/Habitica/src/com/magicmicky/habitrpgwrapper/lib/models/ChatMessage.java
index 4640c2f18..bb51432b0 100644
--- a/Habitica/src/com/magicmicky/habitrpgwrapper/lib/models/ChatMessage.java
+++ b/Habitica/src/com/magicmicky/habitrpgwrapper/lib/models/ChatMessage.java
@@ -32,6 +32,8 @@ public class ChatMessage {
public String text;
+ public CharSequence parsedText;
+
public long timestamp;
public HashMap likes;
diff --git a/Habitica/src/com/magicmicky/habitrpgwrapper/lib/models/tasks/Task.java b/Habitica/src/com/magicmicky/habitrpgwrapper/lib/models/tasks/Task.java
index 9d115256a..2216f60cd 100644
--- a/Habitica/src/com/magicmicky/habitrpgwrapper/lib/models/tasks/Task.java
+++ b/Habitica/src/com/magicmicky/habitrpgwrapper/lib/models/tasks/Task.java
@@ -35,7 +35,6 @@ public class Task extends BaseModel {
public static final String FREQUENCY_WEEKLY = "weekly";
public static final String FREQUENCY_DAILY = "daily";
-
@Column
@PrimaryKey
@NotNull