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