Refactor fragments to be in kotlin and not use Butterknife

This commit is contained in:
Phillip Thelen 2018-07-06 14:50:40 +02:00
parent e66c95bda0
commit 381b76a33a
103 changed files with 3216 additions and 3489 deletions

View file

@ -0,0 +1,136 @@
package com.habitrpg.android.habitica.api;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.habitrpg.android.habitica.models.ContentResult;
import com.habitrpg.android.habitica.models.FAQArticle;
import com.habitrpg.android.habitica.models.Skill;
import com.habitrpg.android.habitica.models.Tag;
import com.habitrpg.android.habitica.models.TutorialStep;
import com.habitrpg.android.habitica.models.WorldState;
import com.habitrpg.android.habitica.models.inventory.Customization;
import com.habitrpg.android.habitica.models.inventory.Egg;
import com.habitrpg.android.habitica.models.inventory.Equipment;
import com.habitrpg.android.habitica.models.inventory.Food;
import com.habitrpg.android.habitica.models.inventory.HatchingPotion;
import com.habitrpg.android.habitica.models.inventory.Mount;
import com.habitrpg.android.habitica.models.inventory.Pet;
import com.habitrpg.android.habitica.models.inventory.Quest;
import com.habitrpg.android.habitica.models.inventory.QuestCollect;
import com.habitrpg.android.habitica.models.inventory.QuestContent;
import com.habitrpg.android.habitica.models.inventory.QuestDropItem;
import com.habitrpg.android.habitica.models.responses.FeedResponse;
import com.habitrpg.android.habitica.models.social.Challenge;
import com.habitrpg.android.habitica.models.social.ChatMessage;
import com.habitrpg.android.habitica.models.social.Group;
import com.habitrpg.android.habitica.models.tasks.Task;
import com.habitrpg.android.habitica.models.tasks.TaskList;
import com.habitrpg.android.habitica.models.user.Purchases;
import com.habitrpg.android.habitica.models.user.User;
import com.habitrpg.android.habitica.utils.BooleanAsIntAdapter;
import com.habitrpg.android.habitica.utils.ChallengeDeserializer;
import com.habitrpg.android.habitica.utils.ChallengeListDeserializer;
import com.habitrpg.android.habitica.utils.ChatMessageDeserializer;
import com.habitrpg.android.habitica.utils.ChatMessageListDeserializer;
import com.habitrpg.android.habitica.utils.ContentDeserializer;
import com.habitrpg.android.habitica.utils.CustomizationDeserializer;
import com.habitrpg.android.habitica.utils.DateDeserializer;
import com.habitrpg.android.habitica.utils.EggListDeserializer;
import com.habitrpg.android.habitica.utils.EquipmentListDeserializer;
import com.habitrpg.android.habitica.utils.FAQArticleListDeserilializer;
import com.habitrpg.android.habitica.utils.FeedResponseDeserializer;
import com.habitrpg.android.habitica.utils.FoodListDeserializer;
import com.habitrpg.android.habitica.utils.GroupSerialization;
import com.habitrpg.android.habitica.utils.HatchingPotionListDeserializer;
import com.habitrpg.android.habitica.utils.MemberSerialization;
import com.habitrpg.android.habitica.utils.MountListDeserializer;
import com.habitrpg.android.habitica.utils.MountMapDeserializer;
import com.habitrpg.android.habitica.utils.PetListDeserializer;
import com.habitrpg.android.habitica.utils.PetMapDeserializer;
import com.habitrpg.android.habitica.utils.PurchasedDeserializer;
import com.habitrpg.android.habitica.utils.QuestCollectDeserializer;
import com.habitrpg.android.habitica.utils.QuestDeserializer;
import com.habitrpg.android.habitica.utils.QuestDropItemsListSerialization;
import com.habitrpg.android.habitica.utils.QuestListDeserializer;
import com.habitrpg.android.habitica.utils.SkillDeserializer;
import com.habitrpg.android.habitica.utils.TaskListDeserializer;
import com.habitrpg.android.habitica.utils.TaskSerializer;
import com.habitrpg.android.habitica.utils.TaskTagDeserializer;
import com.habitrpg.android.habitica.utils.TutorialStepListDeserializer;
import com.habitrpg.android.habitica.utils.UserDeserializer;
import com.habitrpg.android.habitica.utils.WorldStateSerialization;
import java.lang.reflect.Member;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.List;
import java.util.Map;
import io.realm.RealmList;
import retrofit2.converter.gson.GsonConverterFactory;
public class GSonFactoryCreator {
public static GsonConverterFactory create() {
Type taskTagClassListType = new TypeToken<RealmList<Tag>>() {}.getType();
Type skillListType = new TypeToken<List<Skill>>() {}.getType();
Type customizationListType = new TypeToken<RealmList<Customization>>() {}.getType();
Type tutorialStepListType = new TypeToken<RealmList<TutorialStep>>() {}.getType();
Type faqArticleListType = new TypeToken<RealmList<FAQArticle>>() {}.getType();
Type itemDataListType = new TypeToken<RealmList<Equipment>>() {}.getType();
Type eggListType = new TypeToken<RealmList<Egg>>() {}.getType();
Type foodListType = new TypeToken<RealmList<Food>>() {}.getType();
Type hatchingPotionListType = new TypeToken<RealmList<HatchingPotion>>() {}.getType();
Type questContentListType = new TypeToken<RealmList<QuestContent>>() {}.getType();
Type petMapType = new TypeToken<Map<String, Pet>>() {}.getType();
Type mountMapType = new TypeToken<Map<String, Mount>>() {}.getType();
Type petListType = new TypeToken<RealmList<Pet>>() {}.getType();
Type mountListType = new TypeToken<RealmList<Mount>>() {}.getType();
Type questCollectListType = new TypeToken<RealmList<QuestCollect>>() {}.getType();
Type chatMessageListType = new TypeToken<RealmList<ChatMessage>>() {}.getType();
Type challengeListType = new TypeToken<List<Challenge>>() {}.getType();
Type challengeRealmListType = new TypeToken<RealmList<Challenge>>() {}.getType();
Type questDropItemListType = new TypeToken<RealmList<QuestDropItem>>() {}.getType();
//Exclusion strategy needed for DBFlow https://github.com/Raizlabs/DBFlow/issues/121
Gson gson = new GsonBuilder()
.registerTypeAdapter(taskTagClassListType, new TaskTagDeserializer())
.registerTypeAdapter(Boolean.class, new BooleanAsIntAdapter())
.registerTypeAdapter(boolean.class, new BooleanAsIntAdapter())
.registerTypeAdapter(skillListType, new SkillDeserializer())
.registerTypeAdapter(TaskList.class, new TaskListDeserializer())
.registerTypeAdapter(Purchases.class, new PurchasedDeserializer())
.registerTypeAdapter(customizationListType, new CustomizationDeserializer())
.registerTypeAdapter(tutorialStepListType, new TutorialStepListDeserializer())
.registerTypeAdapter(faqArticleListType, new FAQArticleListDeserilializer())
.registerTypeAdapter(Group.class, new GroupSerialization())
.registerTypeAdapter(Date.class, new DateDeserializer())
.registerTypeAdapter(itemDataListType, new EquipmentListDeserializer())
.registerTypeAdapter(eggListType, new EggListDeserializer())
.registerTypeAdapter(foodListType, new FoodListDeserializer())
.registerTypeAdapter(hatchingPotionListType, new HatchingPotionListDeserializer())
.registerTypeAdapter(questContentListType, new QuestListDeserializer())
.registerTypeAdapter(petListType, new PetListDeserializer())
.registerTypeAdapter(mountListType, new MountListDeserializer())
.registerTypeAdapter(petMapType, new PetMapDeserializer())
.registerTypeAdapter(mountMapType, new MountMapDeserializer())
.registerTypeAdapter(ChatMessage.class, new ChatMessageDeserializer())
.registerTypeAdapter(Task.class, new TaskSerializer())
.registerTypeAdapter(ContentResult.class, new ContentDeserializer())
.registerTypeAdapter(FeedResponse.class, new FeedResponseDeserializer())
.registerTypeAdapter(Challenge.class, new ChallengeDeserializer())
.registerTypeAdapter(User.class, new UserDeserializer())
.registerTypeAdapter(questCollectListType, new QuestCollectDeserializer())
.registerTypeAdapter(chatMessageListType, new ChatMessageListDeserializer())
.registerTypeAdapter(challengeListType, new ChallengeListDeserializer())
.registerTypeAdapter(challengeRealmListType, new ChallengeListDeserializer())
.registerTypeAdapter(questDropItemListType, new QuestDropItemsListSerialization())
.registerTypeAdapter(Quest.class, new QuestDeserializer())
.registerTypeAdapter(Member.class, new MemberSerialization())
.registerTypeAdapter(WorldState.class, new WorldStateSerialization())
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
.create();
return GsonConverterFactory.create(gson);
}
}

View file

@ -11,6 +11,7 @@ import com.habitrpg.android.habitica.BuildConfig
import com.habitrpg.android.habitica.HabiticaBaseApplication
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.api.ApiService
import com.habitrpg.android.habitica.api.GSonFactoryCreator
import com.habitrpg.android.habitica.api.HostConfig
import com.habitrpg.android.habitica.api.Server
import com.habitrpg.android.habitica.data.ApiClient
@ -686,103 +687,7 @@ class ApiClientImpl//private OnHabitsAPIResult mResultListener;
private const val TAG = "ApiClientImpl"
fun createGsonFactory(): GsonConverterFactory {
val taskTagClassListType = object : TypeToken<RealmList<Tag>>() {
}.type
val skillListType = object : TypeToken<List<Skill>>() {
}.type
val customizationListType = object : TypeToken<RealmList<Customization>>() {
}.type
val tutorialStepListType = object : TypeToken<RealmList<TutorialStep>>() {
}.type
val faqArticleListType = object : TypeToken<RealmList<FAQArticle>>() {
}.type
val itemDataListType = object : TypeToken<RealmList<Equipment>>() {
}.type
val eggListType = object : TypeToken<RealmList<Egg>>() {
}.type
val foodListType = object : TypeToken<RealmList<Food>>() {
}.type
val hatchingPotionListType = object : TypeToken<RealmList<HatchingPotion>>() {
}.type
val questContentListType = object : TypeToken<RealmList<QuestContent>>() {
}.type
val petMapType = object : TypeToken<Map<String, Pet>>() {
}.type
val mountMapType = object : TypeToken<Map<String, Mount>>() {
}.type
val petListType = object : TypeToken<RealmList<Pet>>() {
}.type
val mountListType = object : TypeToken<RealmList<Mount>>() {
}.type
val questCollectListType = object : TypeToken<RealmList<QuestCollect>>() {
}.type
val chatMessageListType = object : TypeToken<RealmList<ChatMessage>>() {
}.type
val challengeListType = object : TypeToken<List<Challenge>>() {
}.type
val challengeRealmListType = object : TypeToken<RealmList<Challenge>>() {
}.type
val questDropItemListType = object : TypeToken<RealmList<QuestDropItem>>() {
}.type
//Exclusion strategy needed for DBFlow https://github.com/Raizlabs/DBFlow/issues/121
val gson = GsonBuilder()
.registerTypeAdapter(taskTagClassListType, TaskTagDeserializer())
.registerTypeAdapter(Boolean::class.java, BooleanAsIntAdapter())
.registerTypeAdapter(Boolean::class.javaPrimitiveType, BooleanAsIntAdapter())
.registerTypeAdapter(skillListType, SkillDeserializer())
.registerTypeAdapter(TaskList::class.java, TaskListDeserializer())
.registerTypeAdapter(Purchases::class.java, PurchasedDeserializer())
.registerTypeAdapter(customizationListType, CustomizationDeserializer())
.registerTypeAdapter(tutorialStepListType, TutorialStepListDeserializer())
.registerTypeAdapter(faqArticleListType, FAQArticleListDeserilializer())
.registerTypeAdapter(Group::class.java, GroupSerialization())
.registerTypeAdapter(Date::class.java, DateDeserializer())
.registerTypeAdapter(itemDataListType, EquipmentListDeserializer())
.registerTypeAdapter(eggListType, EggListDeserializer())
.registerTypeAdapter(foodListType, FoodListDeserializer())
.registerTypeAdapter(hatchingPotionListType, HatchingPotionListDeserializer())
.registerTypeAdapter(questContentListType, QuestListDeserializer())
.registerTypeAdapter(petListType, PetListDeserializer())
.registerTypeAdapter(mountListType, MountListDeserializer())
.registerTypeAdapter(petMapType, PetMapDeserializer())
.registerTypeAdapter(mountMapType, MountMapDeserializer())
.registerTypeAdapter(ChatMessage::class.java, ChatMessageDeserializer())
.registerTypeAdapter(Task::class.java, TaskSerializer())
.registerTypeAdapter(ContentResult::class.java, ContentDeserializer())
.registerTypeAdapter(FeedResponse::class.java, FeedResponseDeserializer())
.registerTypeAdapter(Challenge::class.java, ChallengeDeserializer())
.registerTypeAdapter(User::class.java, UserDeserializer())
.registerTypeAdapter(questCollectListType, QuestCollectDeserializer())
.registerTypeAdapter(chatMessageListType, ChatMessageListDeserializer())
.registerTypeAdapter(challengeListType, ChallengeListDeserializer())
.registerTypeAdapter(challengeRealmListType, ChallengeListDeserializer())
.registerTypeAdapter(questDropItemListType, QuestDropItemsListSerialization())
.registerTypeAdapter(Quest::class.java, QuestDeserializer())
.registerTypeAdapter(Member::class.java, MemberSerialization())
.registerTypeAdapter(WorldState::class.java, WorldStateSerialization())
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
.create()
return GsonConverterFactory.create(gson)
return GSonFactoryCreator.create()
}
}
}

View file

@ -1,7 +1,10 @@
package com.habitrpg.android.habitica.extensions
import android.app.AlertDialog
import android.app.Dialog
import android.content.DialogInterface
import android.support.annotation.IdRes
import android.view.View
import com.habitrpg.android.habitica.R
public fun AlertDialog.Builder.setOkButton(listener: ((DialogInterface, Int) -> Unit)? = null): AlertDialog.Builder {

View file

@ -11,11 +11,6 @@ fun View.setScaledPadding(context: Context?, left: Int, top: Int, right: Int, bo
this.setPadding(left.dpToPx(context), top.dpToPx(context), right.dpToPx(context), bottom.dpToPx(context))
}
fun <T : View> View.bindView(@IdRes res: Int) : Lazy<T> {
@Suppress("UNCHECKED_CAST")
return lazy(LazyThreadSafetyMode.NONE) { findViewById<T>(res) }
}
var <T : View> T.backgroundCompat: Drawable?
get() {
return background

View file

@ -1,22 +0,0 @@
package com.habitrpg.android.habitica.extensions
import android.content.Context
import android.support.annotation.ColorRes
import android.support.annotation.IdRes
import android.support.v4.content.ContextCompat
import android.view.View
fun <T : View> bindView(container: View, @IdRes res: Int) : Lazy<T> {
@Suppress("UNCHECKED_CAST")
return lazy(LazyThreadSafetyMode.NONE) { container.findViewById<T>(res) }
}
fun <T : View> bindOptionalView(container: View?, @IdRes res: Int) : Lazy<T?> {
@Suppress("UNCHECKED_CAST")
return lazy(LazyThreadSafetyMode.NONE) { container?.findViewById<T>(res) }
}
fun bindColor(context: Context, @ColorRes res: Int) : Lazy<Int> {
@Suppress("UNCHECKED_CAST")
return lazy(LazyThreadSafetyMode.NONE) { ContextCompat.getColor(context, res) }
}

View file

@ -9,5 +9,7 @@ public class TaskGroupPlan extends RealmObject {
@PrimaryKey
String task_id;
public boolean approvalRequested, approvalApproved, approvalRequired;
public boolean approvalRequested;
public boolean approvalApproved;
public boolean approvalRequired;
}

View file

@ -11,11 +11,11 @@ import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.events.BoughtGemsEvent
import com.habitrpg.android.habitica.events.commands.OpenGemPurchaseFragmentCommand
import com.habitrpg.android.habitica.events.commands.OpenMenuItemCommand
import com.habitrpg.android.habitica.extensions.bindView
import com.habitrpg.android.habitica.models.Avatar
import com.habitrpg.android.habitica.models.user.Stats
import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.ui.fragments.NavigationDrawerFragment
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.ui.views.CurrencyViews
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
import com.habitrpg.android.habitica.ui.views.ValueBar

View file

@ -356,7 +356,7 @@ public class ChallengeDetailActivity extends BaseActivity {
if (challengeName != null) {
challengeName.setText(EmojiParser.parseEmojis(challenge.name));
}
challengeDescription.setText(MarkdownParser.parseMarkdown(challenge.description));
challengeDescription.setText(MarkdownParser.INSTANCE.parseMarkdown(challenge.description));
memberCountTextView.setText(String.valueOf(challenge.memberCount));
@ -370,7 +370,7 @@ public class ChallengeDetailActivity extends BaseActivity {
@OnClick(R.id.btn_show_more)
void onShowMore() {
ChallengeDetailDialogHolder.showDialog(ChallengeDetailActivity.this,
ChallengeDetailDialogHolder.Companion.showDialog(ChallengeDetailActivity.this,
ChallengeDetailActivity.this.challengeRepository,
challenge,
challenge1 -> ChallengeDetailActivity.this.onBackPressed());

View file

@ -225,7 +225,7 @@ public class FullProfileActivity extends BaseActivity {
String blurbText = profile.getBlurb();
if (blurbText != null && !blurbText.isEmpty()) {
blurbTextView.setText(MarkdownParser.parseMarkdown(blurbText));
blurbTextView.setText(MarkdownParser.INSTANCE.parseMarkdown(blurbText));
}
userIdText.setText(userId);
copyUserIdButton.setVisibility(View.VISIBLE);

View file

@ -187,7 +187,7 @@ public class GroupFormActivity extends BaseActivity {
Intent resultIntent = new Intent();
Bundle bundle = new Bundle();
bundle.putString("name", this.groupNameEditText.getText().toString());
bundle.putString("description", MarkdownParser.parseCompiled(this.groupDescriptionEditText.getText()));
bundle.putString("description", MarkdownParser.INSTANCE.parseCompiled(this.groupDescriptionEditText.getText()));
bundle.putString("leader", this.groupLeader);
resultIntent.putExtras(bundle);
setResult(Activity.RESULT_OK, resultIntent);

View file

@ -55,7 +55,7 @@ public class MaintenanceActivity extends BaseActivity {
this.titleTextView.setText(data.getString("title"));
imageView.setImageURI(Uri.parse(data.getString("imageUrl")));
this.descriptionTextView.setText(MarkdownParser.parseMarkdown(data.getString("description")));
this.descriptionTextView.setText(MarkdownParser.INSTANCE.parseMarkdown(data.getString("description")));
this.descriptionTextView.setMovementMethod(LinkMovementMethod.getInstance());
isDeprecationNotice = data.getBoolean("deprecationNotice");

View file

@ -118,7 +118,7 @@ public class PartyInviteActivity extends BaseActivity {
public Fragment getItem(int position) {
PartyInviteFragment fragment = new PartyInviteFragment();
fragment.isEmailInvite = position == 0;
fragment.setEmailInvite(position == 0);
if (fragments.size() > position) {
fragments.set(position, fragment);
} else {

View file

@ -255,9 +255,9 @@ public class SetupActivity extends BaseActivity implements ViewPager.OnPageChang
switch (position) {
case 1: {
avatarSetupFragment = new AvatarSetupFragment();
avatarSetupFragment.activity = SetupActivity.this;
avatarSetupFragment.setActivity(SetupActivity.this);
avatarSetupFragment.setUser(user);
avatarSetupFragment.width = pager.getWidth();
avatarSetupFragment.setWidth(pager.getWidth());
fragment = avatarSetupFragment;
break;
}

View file

@ -1087,7 +1087,7 @@ public class TaskFormActivity extends BaseActivity implements AdapterView.OnItem
private boolean saveTask(Task task) {
String text = MarkdownParser.parseCompiled(taskText.getText());
String text = MarkdownParser.INSTANCE.parseCompiled(taskText.getText());
if (text == null || text.isEmpty()) {
return false;
}
@ -1099,7 +1099,7 @@ public class TaskFormActivity extends BaseActivity implements AdapterView.OnItem
taskRepository.executeTransaction(realm -> {
try {
task.setText(text);
task.setNotes(MarkdownParser.parseCompiled(taskNotes.getText()));
task.setNotes(MarkdownParser.INSTANCE.parseCompiled(taskNotes.getText()));
} catch (IllegalArgumentException ignored) {
}

View file

@ -12,7 +12,7 @@ import android.widget.*
import com.facebook.drawee.view.SimpleDraweeView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.events.commands.OpenMenuItemCommand
import com.habitrpg.android.habitica.extensions.bindView
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.extensions.notNull
import com.habitrpg.android.habitica.models.inventory.Customization
import com.habitrpg.android.habitica.models.inventory.CustomizationSet

View file

@ -8,8 +8,8 @@ import android.view.ViewGroup
import android.widget.TextView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.backgroundCompat
import com.habitrpg.android.habitica.extensions.bindOptionalView
import com.habitrpg.android.habitica.extensions.inflate
import com.habitrpg.android.habitica.ui.helpers.bindOptionalView
import com.habitrpg.android.habitica.ui.menu.HabiticaDrawerItem
import io.reactivex.BackpressureStrategy
import io.reactivex.Flowable

View file

@ -9,7 +9,7 @@ import android.view.ViewGroup
import android.widget.TextView
import com.facebook.drawee.view.SimpleDraweeView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.bindView
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.models.inventory.Equipment
import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils
import io.reactivex.subjects.PublishSubject

View file

@ -10,7 +10,7 @@ import android.widget.Button
import android.widget.TextView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.events.commands.OpenGemPurchaseFragmentCommand
import com.habitrpg.android.habitica.extensions.bindView
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.extensions.inflate
import com.habitrpg.android.habitica.models.inventory.Item
import com.habitrpg.android.habitica.models.shops.Shop

View file

@ -7,7 +7,7 @@ import android.view.ViewGroup
import android.widget.TextView
import com.facebook.drawee.view.SimpleDraweeView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.bindView
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.models.inventory.Animal
import com.habitrpg.android.habitica.ui.activities.MainActivity
import com.habitrpg.android.habitica.ui.fragments.inventory.stable.MountDetailRecyclerFragment

View file

@ -180,7 +180,7 @@ public class ChatRecyclerViewAdapter extends RealmRecyclerViewAdapter<ChatMessag
if (msg.getParsedText() == null) {
messageText.setText(chatMessage.getText());
Maybe.just(chatMessage.getText())
.map(MarkdownParser::parseMarkdown)
.map(MarkdownParser.INSTANCE::parseMarkdown)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(parsedText -> {

View file

@ -1,13 +1,10 @@
package com.habitrpg.android.habitica.ui.fragments
import android.os.Bundle
import android.support.annotation.CallSuper
import android.support.v4.app.DialogFragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import butterknife.ButterKnife
import butterknife.Unbinder
import com.habitrpg.android.habitica.HabiticaApplication
import com.habitrpg.android.habitica.HabiticaBaseApplication
import com.habitrpg.android.habitica.components.AppComponent
@ -31,7 +28,6 @@ abstract class BaseFragment : DialogFragment() {
var tutorialStepIdentifier: String? = null
var tutorialText: String? = null
var unbinder: Unbinder? = null
protected var tutorialCanBeDeferred = true
var tutorialTexts: MutableList<String> = ArrayList()
@ -66,15 +62,6 @@ abstract class BaseFragment : DialogFragment() {
abstract fun injectFragment(component: AppComponent)
@CallSuper
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (unbinder == null) {
unbinder = ButterKnife.bind(this, view)
}
}
override fun onResume() {
super.onResume()
showTutorialIfNeeded()
@ -115,10 +102,6 @@ abstract class BaseFragment : DialogFragment() {
if (EventBus.getDefault().isRegistered(this)) {
EventBus.getDefault().unregister(this)
}
if (unbinder != null) {
unbinder!!.unbind()
unbinder = null
}
if (!compositeSubscription.isDisposed) {
compositeSubscription.dispose()
}

View file

@ -1,57 +0,0 @@
package com.habitrpg.android.habitica.ui.fragments.faq;
import android.os.Bundle;
import android.text.method.LinkMovementMethod;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.components.AppComponent;
import com.habitrpg.android.habitica.models.FAQArticle;
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment;
import com.habitrpg.android.habitica.ui.helpers.MarkdownParser;
import butterknife.BindView;
import butterknife.ButterKnife;
public class FAQDetailFragment extends BaseMainFragment {
@BindView(R.id.questionTextView)
TextView questionTextView;
@BindView(R.id.answerTextView)
TextView answerTextView;
private FAQArticle article;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.fragment_faq_detail, container, false);
setUnbinder(ButterKnife.bind(this, view));
if (this.article != null) {
this.questionTextView.setText(this.article.getQuestion());
this.answerTextView.setText(MarkdownParser.parseMarkdown(this.article.getAnswer()));
}
this.answerTextView.setMovementMethod(LinkMovementMethod.getInstance());
return view;
}
@Override
public void injectFragment(AppComponent component) {
}
public void setArticle(FAQArticle article) {
this.article = article;
if (this.questionTextView != null) {
this.questionTextView.setText(this.article.getQuestion());
}
if (this.answerTextView != null) {
this.answerTextView.setText(MarkdownParser.parseMarkdown(this.article.getAnswer()));
}
}
}

View file

@ -0,0 +1,56 @@
package com.habitrpg.android.habitica.ui.fragments.faq
import android.os.Bundle
import android.text.method.LinkMovementMethod
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.AppComponent
import com.habitrpg.android.habitica.extensions.inflate
import com.habitrpg.android.habitica.models.FAQArticle
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
import com.habitrpg.android.habitica.ui.helpers.MarkdownParser
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.ui.helpers.resetViews
class FAQDetailFragment : BaseMainFragment() {
private val questionTextView: TextView? by bindView(R.id.questionTextView)
private val answerTextView: TextView? by bindView(R.id.answerTextView)
private var article: FAQArticle? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
super.onCreateView(inflater, container, savedInstanceState)
return container?.inflate(R.layout.fragment_faq_detail)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
resetViews()
if (this.article != null) {
this.questionTextView!!.text = this.article!!.question
//TODO: FIX
this.answerTextView?.text = MarkdownParser.parseMarkdown(article?.answer)
}
this.answerTextView!!.movementMethod = LinkMovementMethod.getInstance()
}
override fun injectFragment(component: AppComponent) {}
fun setArticle(article: FAQArticle) {
this.article = article
if (this.questionTextView != null) {
this.questionTextView!!.text = this.article!!.question
}
if (this.answerTextView != null) {
//TODO: FIX
this.answerTextView?.text = MarkdownParser.parseMarkdown(article.answer)
}
}
}

View file

@ -1,77 +0,0 @@
package com.habitrpg.android.habitica.ui.fragments.faq;
import android.os.Bundle;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.components.AppComponent;
import com.habitrpg.android.habitica.data.FAQRepository;
import com.habitrpg.android.habitica.helpers.RxErrorHandler;
import com.habitrpg.android.habitica.ui.adapter.FAQOverviewRecyclerAdapter;
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment;
import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
public class FAQOverviewFragment extends BaseMainFragment {
@Inject
FAQRepository faqRepository;
@BindView(R.id.recyclerView)
RecyclerView recyclerView;
FAQOverviewRecyclerAdapter adapter;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.fragment_recyclerview, container, false);
setUnbinder(ButterKnife.bind(this, view));
adapter = new FAQOverviewRecyclerAdapter();
adapter.getResetWalkthroughEvents().subscribe(aVoid -> this.userRepository.resetTutorial(user), RxErrorHandler.handleEmptyError());
adapter.activity = activity;
recyclerView.setLayoutManager(new LinearLayoutManager(activity));
recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL));
recyclerView.setAdapter(adapter);
recyclerView.setItemAnimator(new SafeDefaultItemAnimator());
this.loadArticles();
return view;
}
@Override
public void onDestroy() {
faqRepository.close();
super.onDestroy();
}
@Override
public void injectFragment(AppComponent component) {
component.inject(this);
}
private void loadArticles() {
if (user == null || adapter == null) {
return;
}
faqRepository.getArticles().subscribe(adapter::setArticles, RxErrorHandler.handleEmptyError());
}
@Override
public String customTitle() {
if (!isAdded()) {
return "";
}
return getString(R.string.FAQ);
}
}

View file

@ -0,0 +1,73 @@
package com.habitrpg.android.habitica.ui.fragments.faq
import android.os.Bundle
import android.support.v7.widget.DividerItemDecoration
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.AppComponent
import com.habitrpg.android.habitica.data.FAQRepository
import com.habitrpg.android.habitica.extensions.inflate
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.ui.adapter.FAQOverviewRecyclerAdapter
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.ui.helpers.resetViews
import io.reactivex.functions.Consumer
import javax.inject.Inject
class FAQOverviewFragment : BaseMainFragment() {
@Inject
lateinit var faqRepository: FAQRepository
private val recyclerView: RecyclerView? by bindView(R.id.recyclerView)
internal var adapter: FAQOverviewRecyclerAdapter? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
super.onCreateView(inflater, container, savedInstanceState)
return container?.inflate(R.layout.fragment_recyclerview)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
resetViews()
adapter = FAQOverviewRecyclerAdapter()
adapter?.resetWalkthroughEvents?.subscribe(Consumer { this.userRepository.resetTutorial(user) }, RxErrorHandler.handleEmptyError())
adapter?.activity = activity
recyclerView?.layoutManager = LinearLayoutManager(activity)
recyclerView?.addItemDecoration(DividerItemDecoration(getActivity()!!, DividerItemDecoration.VERTICAL))
recyclerView?.adapter = adapter
recyclerView?.itemAnimator = SafeDefaultItemAnimator()
this.loadArticles()
}
override fun onDestroy() {
faqRepository.close()
super.onDestroy()
}
override fun injectFragment(component: AppComponent) {
component.inject(this)
}
private fun loadArticles() {
if (user == null || adapter == null) {
return
}
faqRepository.articles.subscribe(Consumer { adapter?.setArticles(it) }, RxErrorHandler.handleEmptyError())
}
override fun customTitle(): String {
return if (!isAdded) {
""
} else getString(R.string.FAQ)
}
}

View file

@ -1,224 +0,0 @@
package com.habitrpg.android.habitica.ui.fragments.inventory.items;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.Button;
import android.widget.TextView;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.components.AppComponent;
import com.habitrpg.android.habitica.data.InventoryRepository;
import com.habitrpg.android.habitica.events.commands.OpenMenuItemCommand;
import com.habitrpg.android.habitica.helpers.RxErrorHandler;
import com.habitrpg.android.habitica.models.inventory.Egg;
import com.habitrpg.android.habitica.models.inventory.Food;
import com.habitrpg.android.habitica.models.inventory.HatchingPotion;
import com.habitrpg.android.habitica.models.inventory.Item;
import com.habitrpg.android.habitica.models.inventory.Pet;
import com.habitrpg.android.habitica.models.inventory.QuestContent;
import com.habitrpg.android.habitica.models.inventory.SpecialItem;
import com.habitrpg.android.habitica.models.user.User;
import com.habitrpg.android.habitica.ui.adapter.inventory.ItemRecyclerAdapter;
import com.habitrpg.android.habitica.ui.fragments.BaseFragment;
import com.habitrpg.android.habitica.ui.fragments.NavigationDrawerFragment;
import com.habitrpg.android.habitica.ui.helpers.RecyclerViewEmptySupport;
import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator;
import org.greenrobot.eventbus.EventBus;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import io.realm.OrderedRealmCollection;
public class ItemRecyclerFragment extends BaseFragment {
@Inject
InventoryRepository inventoryRepository;
private static final String ITEM_TYPE_KEY = "CLASS_TYPE_KEY";
@BindView(R.id.recyclerView)
public RecyclerViewEmptySupport recyclerView;
@BindView(R.id.emptyView)
public View emptyView;
@BindView(R.id.empty_text_view)
public TextView emptyTextView;
@BindView(R.id.titleTextView)
public TextView titleView;
@BindView(R.id.footerTextView)
public TextView footerView;
@BindView(R.id.openMarketButton)
public Button openMarketButton;
public ItemRecyclerAdapter adapter;
public String itemType;
public String itemTypeText;
public boolean isHatching;
public boolean isFeeding;
public Item hatchingItem;
public Pet feedingPet;
@Nullable
public User user;
LinearLayoutManager layoutManager = null;
private View view;
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
view = inflater.inflate(R.layout.fragment_items, container, false);
setUnbinder(ButterKnife.bind(this, view));
recyclerView.setEmptyView(emptyView);
emptyTextView.setText(getString(R.string.empty_items, itemTypeText));
android.support.v4.app.FragmentActivity context = getActivity();
layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
if (layoutManager == null) {
layoutManager = new LinearLayoutManager(context);
recyclerView.setLayoutManager(layoutManager);
}
adapter = (ItemRecyclerAdapter) recyclerView.getAdapter();
if (adapter == null) {
adapter = new ItemRecyclerAdapter(null, true);
adapter.context = this.getActivity();
adapter.isHatching = this.isHatching;
adapter.isFeeding = this.isFeeding;
adapter.fragment = this;
if (this.hatchingItem != null) {
adapter.hatchingItem = this.hatchingItem;
}
if (this.feedingPet != null) {
adapter.feedingPet = this.feedingPet;
}
recyclerView.setAdapter(adapter);
getCompositeSubscription().add(adapter.getSellItemEvents()
.flatMap(item -> inventoryRepository.sellItem(user, item))
.subscribe(item -> {}, RxErrorHandler.handleEmptyError()));
getCompositeSubscription().add(adapter.getQuestInvitationEvents()
.flatMap(quest -> inventoryRepository.inviteToQuest(quest))
.subscribe(group -> {
EventBus.getDefault().post(new OpenMenuItemCommand(NavigationDrawerFragment.SIDEBAR_PARTY));
}, RxErrorHandler.handleEmptyError()));
}
recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL));
recyclerView.setItemAnimator(new SafeDefaultItemAnimator());
if (savedInstanceState != null) {
this.itemType = savedInstanceState.getString(ITEM_TYPE_KEY, "");
}
if (this.isHatching) {
getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
this.titleView.setText(getString(R.string.hatch_with, this.hatchingItem.getText()));
this.titleView.setVisibility(View.VISIBLE);
this.footerView.setText(getString(R.string.hatching_market_info));
this.footerView.setVisibility(View.VISIBLE);
this.openMarketButton.setVisibility(View.VISIBLE);
} else if (this.isFeeding) {
getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
this.titleView.setText(getString(R.string.dialog_feeding, this.feedingPet.getColorText(), this.feedingPet.getAnimalText()));
this.titleView.setVisibility(View.VISIBLE);
this.footerView.setText(getString(R.string.feeding_market_info));
this.footerView.setVisibility(View.VISIBLE);
this.openMarketButton.setVisibility(View.VISIBLE);
} else {
this.titleView.setVisibility(View.GONE);
this.footerView.setVisibility(View.GONE);
this.openMarketButton.setVisibility(View.GONE);
}
return view;
}
@Override
public void onDestroy() {
inventoryRepository.close();
super.onDestroy();
}
@Override
public void injectFragment(AppComponent component) {
component.inject(this);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
this.loadItems();
}
@Override
public void onResume() {
if ((this.isHatching || this.isFeeding) && getDialog().getWindow() != null) {
android.view.WindowManager.LayoutParams params = getDialog().getWindow().getAttributes();
params.width = ViewGroup.LayoutParams.MATCH_PARENT;
params.verticalMargin = 60;
getDialog().getWindow().setAttributes(params);
}
super.onResume();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(ITEM_TYPE_KEY, this.itemType);
}
private void loadItems() {
Class<? extends Item> itemClass = null;
switch (itemType) {
case "eggs":
itemClass = Egg.class;
break;
case "hatchingPotions":
itemClass = HatchingPotion.class;
break;
case "food":
itemClass = Food.class;
break;
case "quests":
itemClass = QuestContent.class;
break;
case "special":
itemClass = SpecialItem.class;
break;
}
inventoryRepository.getOwnedItems(itemClass, user).firstElement().subscribe(items -> {
if (items.size() > 0) {
adapter.updateData((OrderedRealmCollection<Item>) items);
}
}, RxErrorHandler.handleEmptyError());
getCompositeSubscription().add(inventoryRepository.getOwnedPets().subscribe(adapter::setOwnedPets, RxErrorHandler.handleEmptyError()));
}
@OnClick(R.id.openMarketButton)
public void onOpenMarketClicked() {
dismiss();
openMarket();
}
@OnClick(R.id.openEmptyMarketButton)
public void onEmptyOpenMarketButtonClicked() {
openMarket();
}
private void openMarket() {
EventBus.getDefault().post(new OpenMenuItemCommand(NavigationDrawerFragment.SIDEBAR_SHOPS));
}
}

View file

@ -0,0 +1,192 @@
package com.habitrpg.android.habitica.ui.fragments.inventory.items
import android.os.Bundle
import android.support.v7.widget.DividerItemDecoration
import android.support.v7.widget.LinearLayoutManager
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.Window
import android.widget.Button
import android.widget.TextView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.AppComponent
import com.habitrpg.android.habitica.data.InventoryRepository
import com.habitrpg.android.habitica.events.commands.OpenMenuItemCommand
import com.habitrpg.android.habitica.extensions.inflate
import com.habitrpg.android.habitica.extensions.notNull
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.inventory.*
import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.ui.adapter.inventory.ItemRecyclerAdapter
import com.habitrpg.android.habitica.ui.fragments.BaseFragment
import com.habitrpg.android.habitica.ui.fragments.NavigationDrawerFragment
import com.habitrpg.android.habitica.ui.helpers.RecyclerViewEmptySupport
import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.ui.helpers.resetViews
import io.reactivex.functions.Consumer
import io.realm.OrderedRealmCollection
import org.greenrobot.eventbus.EventBus
import javax.inject.Inject
class ItemRecyclerFragment : BaseFragment() {
@Inject
lateinit var inventoryRepository: InventoryRepository
val recyclerView: RecyclerViewEmptySupport? by bindView(R.id.recyclerView)
val emptyView: View? by bindView(R.id.emptyView)
val emptyTextView: TextView? by bindView(R.id.empty_text_view)
val titleView: TextView? by bindView(R.id.titleTextView)
val footerView: TextView? by bindView(R.id.footerTextView)
val openMarketButton: Button? by bindView(R.id.openMarketButton)
val openEmptyMarketButton: Button? by bindView(R.id.openEmptyMarketButton)
var adapter: ItemRecyclerAdapter? = null
var itemType: String? = null
var itemTypeText: String? = null
var isHatching: Boolean = false
var isFeeding: Boolean = false
var hatchingItem: Item? = null
var feedingPet: Pet? = null
var user: User? = null
internal var layoutManager: LinearLayoutManager? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
super.onCreateView(inflater, container, savedInstanceState)
return container?.inflate(R.layout.fragment_items)
}
override fun onDestroy() {
inventoryRepository.close()
super.onDestroy()
}
override fun injectFragment(component: AppComponent) {
component.inject(this)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
resetViews()
recyclerView?.setEmptyView(emptyView)
emptyTextView?.text = getString(R.string.empty_items, itemTypeText)
val context = activity
layoutManager = recyclerView?.layoutManager as LinearLayoutManager?
if (layoutManager == null) {
layoutManager = LinearLayoutManager(context)
recyclerView?.layoutManager = layoutManager
}
adapter = recyclerView?.adapter as ItemRecyclerAdapter?
if (adapter == null) {
adapter = ItemRecyclerAdapter(null, true)
adapter?.context = this.activity
adapter?.isHatching = this.isHatching
adapter?.isFeeding = this.isFeeding
adapter?.fragment = this
if (this.hatchingItem != null) {
adapter?.hatchingItem = this.hatchingItem
}
if (this.feedingPet != null) {
adapter?.feedingPet = this.feedingPet
}
recyclerView?.adapter = adapter
adapter?.notNull {
compositeSubscription.add(it.sellItemEvents
.flatMap { item -> inventoryRepository.sellItem(user, item) }
.subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
compositeSubscription.add(it.questInvitationEvents
.flatMap { quest -> inventoryRepository.inviteToQuest(quest) }
.subscribe(Consumer { EventBus.getDefault().post(OpenMenuItemCommand(NavigationDrawerFragment.SIDEBAR_PARTY)) }, RxErrorHandler.handleEmptyError()))
}
}
activity.notNull {
recyclerView?.addItemDecoration(DividerItemDecoration(it, DividerItemDecoration.VERTICAL))
}
recyclerView?.itemAnimator = SafeDefaultItemAnimator()
if (savedInstanceState != null) {
this.itemType = savedInstanceState.getString(ITEM_TYPE_KEY, "")
}
if (this.isHatching) {
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
this.titleView?.text = getString(R.string.hatch_with, this.hatchingItem?.text)
this.titleView?.visibility = View.VISIBLE
this.footerView?.text = getString(R.string.hatching_market_info)
this.footerView?.visibility = View.VISIBLE
this.openMarketButton?.visibility = View.VISIBLE
} else if (this.isFeeding) {
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
this.titleView?.text = getString(R.string.dialog_feeding, this.feedingPet?.colorText, this.feedingPet?.animalText)
this.titleView?.visibility = View.VISIBLE
this.footerView?.text = getString(R.string.feeding_market_info)
this.footerView?.visibility = View.VISIBLE
this.openMarketButton?.visibility = View.VISIBLE
} else {
this.titleView?.visibility = View.GONE
this.footerView?.visibility = View.GONE
this.openMarketButton?.visibility = View.GONE
}
openMarketButton?.setOnClickListener {
dismiss()
openMarket()
}
openEmptyMarketButton?.setOnClickListener { openMarket() }
this.loadItems()
}
override fun onResume() {
if ((this.isHatching || this.isFeeding) && dialog.window != null) {
val params = dialog.window?.attributes
params?.width = ViewGroup.LayoutParams.MATCH_PARENT
params?.verticalMargin = 60f
dialog.window?.attributes = params
}
super.onResume()
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putString(ITEM_TYPE_KEY, this.itemType)
}
private fun loadItems() {
var itemClass: Class<out Item>? = null
when (itemType) {
"eggs" -> itemClass = Egg::class.java
"hatchingPotions" -> itemClass = HatchingPotion::class.java
"food" -> itemClass = Food::class.java
"quests" -> itemClass = QuestContent::class.java
"special" -> itemClass = SpecialItem::class.java
}
inventoryRepository.getOwnedItems(itemClass, user).firstElement().subscribe(Consumer { items ->
if (items.size > 0) {
adapter?.updateData(items as OrderedRealmCollection<Item>)
}
}, RxErrorHandler.handleEmptyError())
compositeSubscription.add(inventoryRepository?.ownedPets.subscribe(Consumer { adapter?.setOwnedPets(it) }, RxErrorHandler.handleEmptyError()))
}
private fun openMarket() {
EventBus.getDefault().post(OpenMenuItemCommand(NavigationDrawerFragment.SIDEBAR_SHOPS))
}
companion object {
private val ITEM_TYPE_KEY = "CLASS_TYPE_KEY"
}
}

View file

@ -53,29 +53,29 @@ public class ItemsFragment extends BaseMainFragment {
switch (position) {
case 0: {
fragment.itemType = "eggs";
fragment.setItemType("eggs");
break;
}
case 1: {
fragment.itemType = "hatchingPotions";
fragment.setItemType("hatchingPotions");
break;
}
case 2: {
fragment.itemType = "food";
fragment.setItemType("food");
break;
}
case 3: {
fragment.itemType = "quests";
fragment.setItemType("quests");
break;
}
case 4: {
fragment.itemType = "special";
fragment.setItemType("special");
}
}
fragment.isHatching = false;
fragment.isFeeding = false;
fragment.itemTypeText = this.getPageTitle(position).toString();
fragment.user = ItemsFragment.this.user;
fragment.setHatching(false);
fragment.setFeeding(false);
fragment.setItemTypeText(this.getPageTitle(position).toString());
fragment.setUser(ItemsFragment.this.user);
return fragment;
}
@ -116,14 +116,14 @@ public class ItemsFragment extends BaseMainFragment {
if (event.usingEgg == null || event.usingHatchingPotion == null) {
ItemRecyclerFragment fragment = new ItemRecyclerFragment();
if (event.usingEgg != null) {
fragment.itemType = "hatchingPotions";
fragment.hatchingItem = event.usingEgg;
fragment.setItemType("hatchingPotions");
fragment.setHatchingItem(event.usingEgg);
} else {
fragment.itemType = "eggs";
fragment.hatchingItem = event.usingHatchingPotion;
fragment.setItemType("eggs");
fragment.setHatchingItem(event.usingHatchingPotion);
}
fragment.isHatching = true;
fragment.isFeeding = false;
fragment.setHatching(true);
fragment.setFeeding(false);
fragment.show(getFragmentManager(), "hatchingDialog");
}
}

View file

@ -49,16 +49,16 @@ public class StableFragment extends BaseMainFragment {
switch (position) {
case 0: {
fragment.itemType = "pets";
fragment.setItemType("pets");
break;
}
case 1: {
fragment.itemType = "mounts";
fragment.setItemType("mounts");
break;
}
}
fragment.user = StableFragment.this.user;
fragment.itemTypeText = this.getPageTitle(position).toString();
fragment.setUser(StableFragment.this.user);
fragment.setItemTypeText(this.getPageTitle(position).toString());
return fragment;
}

View file

@ -1,188 +0,0 @@
package com.habitrpg.android.habitica.ui.fragments.inventory.stable;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.widget.GridLayoutManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.components.AppComponent;
import com.habitrpg.android.habitica.data.InventoryRepository;
import com.habitrpg.android.habitica.helpers.RxErrorHandler;
import com.habitrpg.android.habitica.models.inventory.Animal;
import com.habitrpg.android.habitica.models.inventory.Mount;
import com.habitrpg.android.habitica.models.inventory.Pet;
import com.habitrpg.android.habitica.models.user.User;
import com.habitrpg.android.habitica.ui.activities.MainActivity;
import com.habitrpg.android.habitica.ui.adapter.inventory.StableRecyclerAdapter;
import com.habitrpg.android.habitica.ui.fragments.BaseFragment;
import com.habitrpg.android.habitica.ui.helpers.MarginDecoration;
import com.habitrpg.android.habitica.ui.helpers.RecyclerViewEmptySupport;
import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
import io.reactivex.Flowable;
import io.reactivex.Single;
public class StableRecyclerFragment extends BaseFragment {
private static final String ITEM_TYPE_KEY = "CLASS_TYPE_KEY";
@Inject
InventoryRepository inventoryRepository;
@BindView(R.id.recyclerView)
public RecyclerViewEmptySupport recyclerView;
@BindView(R.id.emptyView)
public TextView emptyView;
public StableRecyclerAdapter adapter;
public String itemType;
public String itemTypeText;
public User user;
GridLayoutManager layoutManager = null;
private View view;
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
view = inflater.inflate(R.layout.fragment_recyclerview, container, false);
setUnbinder(ButterKnife.bind(this, view));
recyclerView.setEmptyView(emptyView);
emptyView.setText(getString(R.string.empty_items, itemTypeText));
layoutManager = new GridLayoutManager(getActivity(), 2);
layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
if (adapter.getItemViewType(position) == 0) {
return layoutManager.getSpanCount();
} else {
return 1;
}
}
});
recyclerView.setLayoutManager(layoutManager);
recyclerView.addItemDecoration(new MarginDecoration(getActivity()));
adapter = (StableRecyclerAdapter) recyclerView.getAdapter();
if (adapter == null) {
adapter = new StableRecyclerAdapter();
adapter.setActivity((MainActivity) this.getActivity());
adapter.setItemType(this.itemType);
recyclerView.setAdapter(adapter);
recyclerView.setItemAnimator(new SafeDefaultItemAnimator());
}
if (savedInstanceState != null) {
this.itemType = savedInstanceState.getString(ITEM_TYPE_KEY, "");
}
return view;
}
@Override
public void onDestroy() {
inventoryRepository.close();
super.onDestroy();
}
@Override
public void injectFragment(AppComponent component) {
component.inject(this);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
this.loadItems();
final View finalView = view;
finalView.post(() -> setGridSpanCount(finalView.getWidth()));
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(ITEM_TYPE_KEY, this.itemType);
}
private void setGridSpanCount(int width) {
int spanCount = 0;
if (getContext() != null && getContext().getResources() != null) {
float itemWidth;
itemWidth = getContext().getResources().getDimension(R.dimen.pet_width);
spanCount = (int) (width / itemWidth);
}
if (spanCount == 0) {
spanCount = 1;
}
layoutManager.setSpanCount(spanCount);
}
private void loadItems() {
Flowable<? extends Animal> observable;
if ("pets".equals(itemType)) {
observable = inventoryRepository.getPets().firstElement().toFlowable().flatMap(Flowable::fromIterable);
} else {
observable = inventoryRepository.getMounts().firstElement().toFlowable().flatMap(Flowable::fromIterable);
}
observable.toList().flatMap(unsortedAnimals -> {
List<Object> items = new ArrayList<>();
if (unsortedAnimals.size() == 0) {
return Single.just(items);
}
String lastSectionTitle = "";
Animal lastAnimal = unsortedAnimals.get(0);
for (Animal animal : unsortedAnimals) {
if (!animal.getAnimal().equals(lastAnimal.getAnimal()) || animal == unsortedAnimals.get(unsortedAnimals.size()-1)) {
if (!((lastAnimal.getAnimalGroup().equals("premiumPets") || lastAnimal.getAnimalGroup().equals("specialPets")
|| lastAnimal.getAnimalGroup().equals("specialMounts") || lastAnimal.getAnimalGroup().equals("premiumMounts"))
&& lastAnimal.getNumberOwned() == 0)) {
items.add(lastAnimal);
}
lastAnimal = animal;
}
if (!animal.getAnimalGroup().equals(lastSectionTitle)) {
if (items.size() > 0 && items.get(items.size() - 1).getClass().equals(String.class)) {
items.remove(items.size() - 1);
}
items.add(animal.getAnimalGroup());
lastSectionTitle = animal.getAnimalGroup();
}
if (user != null && user.getItems() != null) {
switch (itemType) {
case "pets":
Pet pet = (Pet) animal;
if (pet.getTrained() > 0) {
lastAnimal.setNumberOwned(lastAnimal.getNumberOwned() + 1);
}
break;
case "mounts":
Mount mount = (Mount) animal;
if (mount.getOwned()) {
lastAnimal.setNumberOwned(lastAnimal.getNumberOwned() + 1);
}
break;
}
}
}
return Single.just(items);
}).subscribe(items -> adapter.setItemList(items), RxErrorHandler.handleEmptyError());
}
}

View file

@ -0,0 +1,175 @@
package com.habitrpg.android.habitica.ui.fragments.inventory.stable
import android.os.Bundle
import android.support.v7.widget.GridLayoutManager
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.AppComponent
import com.habitrpg.android.habitica.data.InventoryRepository
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.inventory.Animal
import com.habitrpg.android.habitica.models.inventory.Mount
import com.habitrpg.android.habitica.models.inventory.Pet
import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.ui.adapter.inventory.StableRecyclerAdapter
import com.habitrpg.android.habitica.ui.fragments.BaseFragment
import java.util.ArrayList
import javax.inject.Inject
import com.habitrpg.android.habitica.extensions.inflate
import com.habitrpg.android.habitica.extensions.notNull
import com.habitrpg.android.habitica.ui.activities.MainActivity
import com.habitrpg.android.habitica.ui.helpers.*
import io.reactivex.Flowable
import io.reactivex.Single
import io.reactivex.functions.Consumer
class StableRecyclerFragment : BaseFragment() {
@Inject
lateinit var inventoryRepository: InventoryRepository
private val recyclerView: RecyclerViewEmptySupport? by bindView(R.id.recyclerView)
private val emptyView: TextView? by bindView(R.id.emptyView)
var adapter: StableRecyclerAdapter? = null
var itemType: String? = null
var itemTypeText: String? = null
var user: User? = null
internal var layoutManager: GridLayoutManager? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
super.onCreateView(inflater, container, savedInstanceState)
if (savedInstanceState != null) {
this.itemType = savedInstanceState.getString(ITEM_TYPE_KEY, "")
}
return container?.inflate(R.layout.fragment_recyclerview)
}
override fun onDestroy() {
inventoryRepository.close()
super.onDestroy()
}
override fun injectFragment(component: AppComponent) {
component.inject(this)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
resetViews()
recyclerView?.setEmptyView(emptyView)
emptyView?.text = getString(R.string.empty_items, itemTypeText)
layoutManager = GridLayoutManager(activity, 2)
layoutManager?.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
return if (adapter?.getItemViewType(position) == 0) {
layoutManager?.spanCount ?: 1
} else {
1
}
}
}
recyclerView?.layoutManager = layoutManager
activity.notNull {
recyclerView?.addItemDecoration(MarginDecoration(it))
}
adapter = recyclerView?.adapter as StableRecyclerAdapter
if (adapter == null) {
adapter = StableRecyclerAdapter()
adapter?.activity = this.activity as MainActivity?
adapter?.itemType = this.itemType
recyclerView?.adapter = adapter
recyclerView?.itemAnimator = SafeDefaultItemAnimator()
}
this.loadItems()
view.post { setGridSpanCount(view.width) }
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putString(ITEM_TYPE_KEY, this.itemType)
}
private fun setGridSpanCount(width: Int) {
var spanCount = 0
if (context != null && context?.resources != null) {
val itemWidth: Float = context?.resources?.getDimension(R.dimen.pet_width) ?: 0.toFloat()
spanCount = (width / itemWidth).toInt()
}
if (spanCount == 0) {
spanCount = 1
}
layoutManager?.spanCount = spanCount
}
private fun loadItems() {
val observable: Flowable<out Animal> = if ("pets" == itemType) {
inventoryRepository.pets.firstElement().toFlowable().flatMap { Flowable.fromIterable(it) }
} else {
inventoryRepository.mounts.firstElement().toFlowable().flatMap { Flowable.fromIterable(it) }
}
observable.toList().flatMap { unsortedAnimals ->
val items = ArrayList<Any>()
if (unsortedAnimals.size == 0) {
return@flatMap Single.just<List<Any>>(items)
}
var lastSectionTitle = ""
var lastAnimal: Animal = unsortedAnimals[0]
for (animal in unsortedAnimals) {
if (animal.animal != lastAnimal.animal || animal === unsortedAnimals[unsortedAnimals.size - 1]) {
if (!((lastAnimal.animalGroup == "premiumPets" || lastAnimal.animalGroup == "specialPets"
|| lastAnimal.animalGroup == "specialMounts" || lastAnimal.animalGroup == "premiumMounts") && lastAnimal.numberOwned == 0)) {
items.add(lastAnimal)
}
lastAnimal = animal
}
if (animal.animalGroup != lastSectionTitle) {
if (items.size > 0 && items[items.size - 1].javaClass == String::class.java) {
items.removeAt(items.size - 1)
}
items.add(animal.animalGroup)
lastSectionTitle = animal.animalGroup
}
if (user != null && user?.items != null) {
when (itemType) {
"pets" -> {
val pet = animal as Pet
if (pet.trained > 0) {
lastAnimal.numberOwned = lastAnimal.numberOwned + 1
}
}
"mounts" -> {
val mount = animal as Mount
if (mount.owned) {
lastAnimal.numberOwned = lastAnimal.numberOwned + 1
}
}
}
}
}
Single.just<List<Any>>(items)
}.subscribe(Consumer { items -> adapter?.setItemList(items) }, RxErrorHandler.handleEmptyError())
}
companion object {
private const val ITEM_TYPE_KEY = "CLASS_TYPE_KEY"
}
}

View file

@ -188,9 +188,11 @@ class AuthenticationPreferenceFragment: BasePreferencesFragment() {
}
private fun showSubscriptionStatusDialog() {
val view = SubscriptionDetailsView(context)
view.setPlan(user?.purchased?.plan)
context.notNull { context ->
val view = SubscriptionDetailsView(context)
user?.purchased?.plan?.notNull {
view.setPlan(it)
}
val dialog = AlertDialog.Builder(context)
.setView(view)
.setTitle(R.string.subscription_status)

View file

@ -1,269 +0,0 @@
package com.habitrpg.android.habitica.ui.fragments.setup;
import android.content.res.Resources;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.TabLayout;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.components.AppComponent;
import com.habitrpg.android.habitica.data.ApiClient;
import com.habitrpg.android.habitica.data.SetupCustomizationRepository;
import com.habitrpg.android.habitica.events.commands.UpdateUserCommand;
import com.habitrpg.android.habitica.models.SetupCustomization;
import com.habitrpg.android.habitica.models.user.User;
import com.habitrpg.android.habitica.ui.AvatarView;
import com.habitrpg.android.habitica.ui.SpeechBubbleView;
import com.habitrpg.android.habitica.ui.activities.SetupActivity;
import com.habitrpg.android.habitica.ui.adapter.setup.CustomizationSetupAdapter;
import com.habitrpg.android.habitica.ui.fragments.BaseFragment;
import com.habitrpg.android.habitica.ui.views.setup.AvatarCategoryView;
import org.greenrobot.eventbus.EventBus;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class AvatarSetupFragment extends BaseFragment {
public SetupActivity activity;
public int width;
View view;
@BindView(R.id.avatarView)
AvatarView avatarView;
@BindView(R.id.customization_list)
RecyclerView customizationList;
@BindView(R.id.subcategory_tabs)
TabLayout subCategoryTabs;
@BindView(R.id.body_button)
AvatarCategoryView bodyButton;
@BindView(R.id.skin_button)
AvatarCategoryView skinButton;
@BindView(R.id.hair_button)
AvatarCategoryView hairButton;
@BindView(R.id.extras_button)
AvatarCategoryView extrasButton;
@BindView(R.id.caret_view)
ImageView caretView;
@BindView(R.id.speech_bubble)
SpeechBubbleView speechBubbleView;
CustomizationSetupAdapter adapter;
LinearLayoutManager layoutManager;
@Inject
SetupCustomizationRepository customizationRepository;
@Inject
ApiClient apiClient;
private User user;
private List<String> subcategories;
private AvatarCategoryView activeButton;
private String activeCategory;
private String activeSubCategory;
private Random random;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
view = inflater.inflate(R.layout.fragment_setup_avatar, container, false);
setUnbinder(ButterKnife.bind(this, view));
this.adapter = new CustomizationSetupAdapter();
if (this.user != null) {
this.adapter.userSize = this.user.getPreferences().getSize();
} else {
this.adapter.userSize = "slim";
}
this.adapter.user = this.user;
this.layoutManager = new LinearLayoutManager(activity);
this.layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
this.customizationList.setLayoutManager(this.layoutManager);
this.customizationList.setAdapter(this.adapter);
this.subCategoryTabs.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
int position = tab.getPosition();
if (subcategories != null && position < subcategories.size()) {
activeSubCategory = subcategories.get(position);
}
loadCustomizations();
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
this.selectedBodyCategory();
this.random = new Random();
if (this.user != null) {
this.updateAvatar();
}
return view;
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser && getContext() != null) {
speechBubbleView.animateText(getContext().getString(R.string.avatar_setup_description));
}
}
@Override
public void injectFragment(AppComponent component) {
component.inject(this);
}
private void loadCustomizations() {
if (this.user == null || this.adapter == null || this.activeCategory == null) {
return;
}
this.adapter.setCustomizationList(customizationRepository.getCustomizations(activeCategory, activeSubCategory, user));
}
public void setUser(@Nullable User user) {
this.user = user;
if (avatarView != null) {
updateAvatar();
}
if (this.adapter != null) {
this.adapter.user = user;
this.adapter.notifyDataSetChanged();
}
}
private void updateAvatar() {
avatarView.setAvatar(user);
}
@OnClick(R.id.body_button)
protected void selectedBodyCategory() {
activateButton(bodyButton);
this.activeCategory = "body";
this.subCategoryTabs.removeAllTabs();
this.subcategories = Arrays.asList("size", "shirt");
this.subCategoryTabs.addTab(subCategoryTabs.newTab().setText(R.string.avatar_size));
this.subCategoryTabs.addTab(subCategoryTabs.newTab().setText(R.string.avatar_shirt));
loadCustomizations();
}
@OnClick(R.id.skin_button)
protected void selectedSkinCategory() {
activateButton(skinButton);
this.activeCategory = "skin";
this.subCategoryTabs.removeAllTabs();
this.subcategories = Collections.singletonList("color");
this.subCategoryTabs.addTab(subCategoryTabs.newTab().setText(R.string.avatar_skin_color));
loadCustomizations();
}
@OnClick(R.id.hair_button)
protected void selectedHairCategory() {
activateButton(hairButton);
this.activeCategory = "hair";
this.subCategoryTabs.removeAllTabs();
this.subcategories = Arrays.asList("bangs", "color", "ponytail");
this.subCategoryTabs.addTab(subCategoryTabs.newTab().setText(R.string.avatar_hair_bangs));
this.subCategoryTabs.addTab(subCategoryTabs.newTab().setText(R.string.avatar_hair_color));
this.subCategoryTabs.addTab(subCategoryTabs.newTab().setText(R.string.avatar_hair_ponytail));
loadCustomizations();
}
@OnClick(R.id.extras_button)
protected void selectedExtrasCategory() {
activateButton(extrasButton);
this.activeCategory = "extras";
this.subCategoryTabs.removeAllTabs();
this.subcategories = Arrays.asList("glasses", "flower", "wheelchair");
this.subCategoryTabs.addTab(subCategoryTabs.newTab().setText(R.string.avatar_glasses));
this.subCategoryTabs.addTab(subCategoryTabs.newTab().setText(R.string.avatar_flower));
this.subCategoryTabs.addTab(subCategoryTabs.newTab().setText(R.string.avatar_wheelchair));
loadCustomizations();
}
@OnClick(R.id.randomize_button)
protected void randomizeCharacter() {
if (user == null) {
return;
}
UpdateUserCommand command = new UpdateUserCommand();
Map<String, Object> updateData = new HashMap<>();
updateData.put("preferences.size", chooseRandomKey(customizationRepository.getCustomizations("body", "size", user), false));
updateData.put("preferences.shirt", chooseRandomKey(customizationRepository.getCustomizations("body", "shirt", user), false));
updateData.put("preferences.skin", chooseRandomKey(customizationRepository.getCustomizations("skin", "color", user), false));
updateData.put("preferences.hair.color", chooseRandomKey(customizationRepository.getCustomizations("hair", "color", user), false));
updateData.put("preferences.hair.base", chooseRandomKey(customizationRepository.getCustomizations("hair", "ponytail", user), false));
updateData.put("preferences.hair.bangs", chooseRandomKey(customizationRepository.getCustomizations("hair", "bangs", user), false));
updateData.put("preferences.hair.flower", chooseRandomKey(customizationRepository.getCustomizations("extras", "flower", user), true));
updateData.put("preferences.chair", chooseRandomKey(customizationRepository.getCustomizations("extras", "wheelchair", user), true));
command.updateData = updateData;
EventBus.getDefault().post(command);
}
@Nullable
private String chooseRandomKey(List<SetupCustomization> customizations, boolean weighFirstOption) {
if (customizations.size() == 0) {
return null;
}
if (weighFirstOption) {
if (random.nextInt(10) > 3) {
return customizations.get(0).getKey();
}
}
return customizations.get(random.nextInt(customizations.size())).getKey();
}
private void activateButton(AvatarCategoryView button) {
if (this.activeButton != null) {
this.activeButton.setActive(false);
}
this.activeButton = button;
this.activeButton.setActive(true);
int[] location = new int[2];
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams)this.caretView.getLayoutParams();
this.activeButton.getLocationOnScreen(location);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
Resources r = getResources();
int px = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 40, r.getDisplayMetrics());
params.setMarginStart(location[0]+px);
this.caretView.setLayoutParams(params);
} else {
caretView.setVisibility(View.GONE);
}
}
}

View file

@ -0,0 +1,246 @@
package com.habitrpg.android.habitica.ui.fragments.setup
import android.os.Build
import android.os.Bundle
import android.support.design.widget.TabLayout
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.ImageView
import android.widget.RelativeLayout
import butterknife.OnClick
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.AppComponent
import com.habitrpg.android.habitica.data.ApiClient
import com.habitrpg.android.habitica.data.SetupCustomizationRepository
import com.habitrpg.android.habitica.events.commands.UpdateUserCommand
import com.habitrpg.android.habitica.extensions.inflate
import com.habitrpg.android.habitica.extensions.notNull
import com.habitrpg.android.habitica.models.SetupCustomization
import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.ui.AvatarView
import com.habitrpg.android.habitica.ui.SpeechBubbleView
import com.habitrpg.android.habitica.ui.activities.SetupActivity
import com.habitrpg.android.habitica.ui.adapter.setup.CustomizationSetupAdapter
import com.habitrpg.android.habitica.ui.fragments.BaseFragment
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.ui.helpers.resetViews
import com.habitrpg.android.habitica.ui.views.setup.AvatarCategoryView
import org.greenrobot.eventbus.EventBus
import java.util.*
import javax.inject.Inject
class AvatarSetupFragment : BaseFragment() {
@Inject
lateinit var customizationRepository: SetupCustomizationRepository
@Inject
lateinit var apiClient: ApiClient
var activity: SetupActivity? = null
var width: Int = 0
private val avatarView: AvatarView? by bindView(R.id.avatarView)
private val customizationList: RecyclerView? by bindView(R.id.customization_list)
private val subCategoryTabs: TabLayout? by bindView(R.id.subcategory_tabs)
private val bodyButton: AvatarCategoryView? by bindView(R.id.body_button)
private val skinButton: AvatarCategoryView? by bindView(R.id.skin_button)
private val hairButton: AvatarCategoryView? by bindView(R.id.hair_button)
private val extrasButton: AvatarCategoryView? by bindView(R.id.extras_button)
private val caretView: ImageView? by bindView(R.id.caret_view)
private val speechBubbleView: SpeechBubbleView? by bindView(R.id.speech_bubble)
private val randomizeButton: Button? by bindView(R.id.randomize_button)
internal var adapter: CustomizationSetupAdapter? = null
internal var layoutManager: LinearLayoutManager = LinearLayoutManager(activity)
private var user: User? = null
private var subcategories: List<String> = emptyList()
private var activeButton: AvatarCategoryView? = null
private var activeCategory: String? = null
private var activeSubCategory: String? = null
private var random = Random()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
super.onCreateView(inflater, container, savedInstanceState)
return container?.inflate(R.layout.fragment_setup_avatar)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
resetViews()
this.adapter = CustomizationSetupAdapter()
this.adapter?.userSize = this.user?.preferences?.size ?: "slim"
this.adapter?.user = this.user
this.layoutManager = LinearLayoutManager(activity)
this.layoutManager.orientation = LinearLayoutManager.HORIZONTAL
this.customizationList?.layoutManager = this.layoutManager
this.customizationList?.adapter = this.adapter
this.subCategoryTabs?.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab) {
val position = tab.position
if (position < subcategories.size) {
activeSubCategory = subcategories[position]
}
loadCustomizations()
}
override fun onTabUnselected(tab: TabLayout.Tab) {
}
override fun onTabReselected(tab: TabLayout.Tab) {
}
})
bodyButton?.setOnClickListener { selectedBodyCategory() }
skinButton?.setOnClickListener { selectedSkinCategory() }
hairButton?.setOnClickListener { selectedHairCategory() }
extrasButton?.setOnClickListener { selectedExtrasCategory() }
randomizeButton?.setOnClickListener { randomizeCharacter() }
this.selectedBodyCategory()
if (this.user != null) {
this.updateAvatar()
}
}
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
super.setUserVisibleHint(isVisibleToUser)
if (isVisibleToUser && context != null) {
speechBubbleView?.animateText(context?.getString(R.string.avatar_setup_description))
}
}
override fun injectFragment(component: AppComponent) {
component.inject(this)
}
private fun loadCustomizations() {
if (this.user == null || this.adapter == null || this.activeCategory == null) {
return
}
this.adapter?.setCustomizationList(customizationRepository.getCustomizations(activeCategory, activeSubCategory, user))
}
fun setUser(user: User?) {
this.user = user
if (avatarView != null) {
updateAvatar()
}
if (this.adapter != null) {
this.adapter?.user = user
this.adapter?.notifyDataSetChanged()
}
}
private fun updateAvatar() {
user.notNull {
avatarView?.setAvatar(it)
}
}
fun selectedBodyCategory() {
activateButton(bodyButton)
this.activeCategory = "body"
this.subCategoryTabs?.removeAllTabs()
this.subcategories = Arrays.asList("size", "shirt")
subCategoryTabs?.newTab()?.setText(R.string.avatar_size).notNull { this.subCategoryTabs?.addTab(it) }
subCategoryTabs?.newTab()?.setText(R.string.avatar_shirt).notNull { this.subCategoryTabs?.addTab(it) }
loadCustomizations()
}
fun selectedSkinCategory() {
activateButton(skinButton)
this.activeCategory = "skin"
this.subCategoryTabs?.removeAllTabs()
this.subcategories = listOf("color")
subCategoryTabs?.newTab()?.setText(R.string.avatar_skin_color).notNull { this.subCategoryTabs?.addTab(it) }
loadCustomizations()
}
fun selectedHairCategory() {
activateButton(hairButton)
this.activeCategory = "hair"
this.subCategoryTabs?.removeAllTabs()
this.subcategories = Arrays.asList("bangs", "color", "ponytail")
subCategoryTabs?.newTab()?.setText(R.string.avatar_hair_bangs).notNull { this.subCategoryTabs?.addTab(it) }
subCategoryTabs?.newTab()?.setText(R.string.avatar_hair_color).notNull { this.subCategoryTabs?.addTab(it) }
subCategoryTabs?.newTab()?.setText(R.string.avatar_hair_ponytail).notNull { this.subCategoryTabs?.addTab(it) }
loadCustomizations()
}
fun selectedExtrasCategory() {
activateButton(extrasButton)
this.activeCategory = "extras"
this.subCategoryTabs?.removeAllTabs()
this.subcategories = Arrays.asList("glasses", "flower", "wheelchair")
subCategoryTabs?.newTab()?.setText(R.string.avatar_glasses).notNull { this.subCategoryTabs?.addTab(it) }
subCategoryTabs?.newTab()?.setText(R.string.avatar_flower).notNull { this.subCategoryTabs?.addTab(it) }
subCategoryTabs?.newTab()?.setText(R.string.avatar_wheelchair).notNull { this.subCategoryTabs?.addTab(it) }
loadCustomizations()
}
fun randomizeCharacter() {
if (user == null) {
return
}
val command = UpdateUserCommand()
val updateData = HashMap<String, Any>()
updateData["preferences.size"] = chooseRandomKey(customizationRepository.getCustomizations("body", "size", user), false)
updateData["preferences.shirt"] = chooseRandomKey(customizationRepository.getCustomizations("body", "shirt", user), false)
updateData["preferences.skin"] = chooseRandomKey(customizationRepository.getCustomizations("skin", "color", user), false)
updateData["preferences.hair.color"] = chooseRandomKey(customizationRepository.getCustomizations("hair", "color", user), false)
updateData["preferences.hair.base"] = chooseRandomKey(customizationRepository.getCustomizations("hair", "ponytail", user), false)
updateData["preferences.hair.bangs"] = chooseRandomKey(customizationRepository.getCustomizations("hair", "bangs", user), false)
updateData["preferences.hair.flower"] = chooseRandomKey(customizationRepository.getCustomizations("extras", "flower", user), true)
updateData["preferences.chair"] = chooseRandomKey(customizationRepository.getCustomizations("extras", "wheelchair", user), true)
command.updateData = updateData
EventBus.getDefault().post(command)
}
private fun chooseRandomKey(customizations: List<SetupCustomization>, weighFirstOption: Boolean): String {
if (customizations.isEmpty()) {
return ""
}
if (weighFirstOption) {
if (random.nextInt(10) > 3) {
return customizations[0].key
}
}
return customizations[random.nextInt(customizations.size)].key
}
private fun activateButton(button: AvatarCategoryView?) {
if (this.activeButton != null) {
this.activeButton?.setActive(false)
}
this.activeButton = button
this.activeButton?.setActive(true)
val location = IntArray(2)
val params = this.caretView?.layoutParams as RelativeLayout.LayoutParams
this.activeButton?.getLocationOnScreen(location)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
val r = resources
val px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 40f, r.displayMetrics).toInt()
params.marginStart = location[0] + px
this.caretView?.layoutParams = params
} else {
caretView?.visibility = View.GONE
}
}
}

View file

@ -1,128 +0,0 @@
package com.habitrpg.android.habitica.ui.fragments.setup;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.components.AppComponent;
import com.habitrpg.android.habitica.ui.fragments.BaseFragment;
import butterknife.BindView;
import butterknife.ButterKnife;
public class IntroFragment extends BaseFragment {
@BindView(R.id.subtitleTextView)
TextView subtitleTextView;
@BindView(R.id.titleTextView)
TextView titleTextView;
@BindView(R.id.titleImageView)
ImageView titleImageView;
@BindView(R.id.descriptionTextView)
TextView descriptionTextView;
@BindView(R.id.imageView)
ImageView imageView;
@BindView(R.id.container_view)
ViewGroup containerView;
Drawable image;
Drawable titleImage;
String subtitle;
String title;
String description;
Integer backgroundColor;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View v = inflater.inflate(R.layout.fragment_intro, container, false);
setUnbinder(ButterKnife.bind(this, v));
if (this.image != null) {
this.imageView.setImageDrawable(this.image);
}
if (this.titleImage != null) {
this.titleImageView.setImageDrawable(this.titleImage);
}
if (this.subtitle != null) {
this.subtitleTextView.setText(this.subtitle);
}
if (this.title != null) {
this.titleTextView.setText(this.title);
}
if (this.description != null) {
this.descriptionTextView.setText(this.description);
}
if (this.backgroundColor != null) {
this.containerView.setBackgroundColor(this.backgroundColor);
}
return v;
}
@Override
public void injectFragment(AppComponent component) {
component.inject(this);
}
public void setImage(Drawable image) {
this.image = image;
if (this.imageView != null && image != null) {
this.imageView.setImageDrawable(image);
}
}
public void setTitleImage(Drawable image) {
this.titleImage = image;
if (this.titleImageView != null && image != null) {
this.titleImageView.setImageDrawable(image);
}
}
public void setSubtitle(String text) {
this.subtitle = text;
if (this.subtitleTextView != null && text != null) {
this.subtitleTextView.setText(text);
}
}
public void setTitle(String text) {
this.title = text;
if (this.titleTextView != null && text != null) {
this.titleTextView.setText(text);
}
}
public void setDescription(String text) {
this.description = text;
if (this.descriptionTextView != null && text != null) {
this.descriptionTextView.setText(text);
}
}
public void setBackgroundColor(int color) {
this.backgroundColor = color;
if (containerView != null) {
containerView.setBackgroundColor(color);
}
}
}

View file

@ -0,0 +1,104 @@
package com.habitrpg.android.habitica.ui.fragments.setup
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.AppComponent
import com.habitrpg.android.habitica.extensions.inflate
import com.habitrpg.android.habitica.extensions.notNull
import com.habitrpg.android.habitica.ui.fragments.BaseFragment
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.ui.helpers.resetViews
class IntroFragment : BaseFragment() {
private val subtitleTextView: TextView? by bindView(R.id.subtitleTextView)
private val titleTextView: TextView? by bindView(R.id.titleTextView)
private val titleImageView: ImageView? by bindView(R.id.titleImageView)
private val descriptionTextView: TextView? by bindView(R.id.descriptionTextView)
private val imageView: ImageView? by bindView(R.id.imageView)
private val containerView: ViewGroup? by bindView(R.id.container_view)
private var image: Drawable? = null
private var titleImage: Drawable? = null
private var subtitle: String? = null
private var title: String? = null
private var description: String? = null
private var backgroundColor: Int? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
super.onCreateView(inflater, container, savedInstanceState)
return container?.inflate(R.layout.fragment_intro)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
resetViews()
if (this.image != null) {
this.imageView?.setImageDrawable(this.image)
}
if (this.titleImage != null) {
this.titleImageView?.setImageDrawable(this.titleImage)
}
if (this.subtitle != null) {
this.subtitleTextView?.text = this.subtitle
}
if (this.title != null) {
this.titleTextView?.text = this.title
}
if (this.description != null) {
this.descriptionTextView?.text = this.description
}
backgroundColor.notNull {
this.containerView?.setBackgroundColor(it)
}
}
override fun injectFragment(component: AppComponent) {
component.inject(this)
}
fun setImage(image: Drawable?) {
this.image = image
if (image != null) {
this.imageView?.setImageDrawable(image)
}
}
fun setTitleImage(image: Drawable?) {
this.titleImage = image
this.titleImageView?.setImageDrawable(image)
}
fun setSubtitle(text: String?) {
this.subtitle = text
subtitleTextView?.text = text
}
fun setTitle(text: String?) {
this.title = text
titleTextView?.text = text
}
fun setDescription(text: String?) {
descriptionTextView?.text = text
}
fun setBackgroundColor(color: Int) {
this.backgroundColor = color
containerView?.setBackgroundColor(color)
}
}

View file

@ -1,187 +0,0 @@
package com.habitrpg.android.habitica.ui.fragments.setup;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.components.AppComponent;
import com.habitrpg.android.habitica.models.tasks.Days;
import com.habitrpg.android.habitica.models.tasks.Task;
import com.habitrpg.android.habitica.models.user.User;
import com.habitrpg.android.habitica.ui.AvatarView;
import com.habitrpg.android.habitica.ui.SpeechBubbleView;
import com.habitrpg.android.habitica.ui.activities.SetupActivity;
import com.habitrpg.android.habitica.ui.adapter.setup.TaskSetupAdapter;
import com.habitrpg.android.habitica.ui.fragments.BaseFragment;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
public class TaskSetupFragment extends BaseFragment {
public SetupActivity activity;
public int width;
View view;
@BindView(R.id.recyclerView)
RecyclerView recyclerView;
@BindView(R.id.avatarView)
AvatarView avatarView;
@BindView(R.id.speech_bubble)
SpeechBubbleView speechBubbleView;
TaskSetupAdapter adapter;
private String[][] taskGroups;
private Object[][] tasks;
private User user;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
view = inflater.inflate(R.layout.fragment_setup_tasks, container, false);
this.setTasks();
setUnbinder(ButterKnife.bind(this, view));
this.adapter = new TaskSetupAdapter();
this.adapter.setTaskList(this.taskGroups);
this.recyclerView.setLayoutManager(new GridLayoutManager(activity, 2));
this.recyclerView.setAdapter(this.adapter);
if (this.user != null) {
this.updateAvatar();
}
return view;
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser && getContext() != null) {
speechBubbleView.animateText(getContext().getString(R.string.task_setup_description));
}
}
public void setUser(@Nullable User user) {
this.user = user;
if (avatarView != null) {
updateAvatar();
}
}
private void updateAvatar() {
avatarView.setAvatar(user);
}
@Override
public void injectFragment(AppComponent component) {
component.inject(this);
}
private void setTasks() {
this.taskGroups = new String[][]{
{getString(R.string.setup_group_work), "work"},
{getString(R.string.setup_group_exercise), "exercise"},
{getString(R.string.setup_group_health), "healthWellness"},
{getString(R.string.setup_group_school), "school"},
{getString(R.string.setup_group_teams), "teams"},
{getString(R.string.setup_group_chores), "chores"},
{getString(R.string.setup_group_creativity), "creativity"},
{getString(R.string.setuP_group_other), "other"}
};
this.tasks = new Object[][]{
{"work", "habit", getString(R.string.setup_task_work_1), true, false},
{"work", "daily", getString(R.string.setup_task_work_2)},
{"work", "todo", getString(R.string.setup_task_work_3)},
{"exercise", "habit", getString(R.string.setup_task_exercise_1), true, false},
{"exercise", "daily", getString(R.string.setup_task_exercise_2)},
{"exercise", "todo", getString(R.string.setup_task_exercise_3)},
{"healthWellness", "habit", getString(R.string.setup_task_healthWellness_1), true, true},
{"healthWellness", "daily", getString(R.string.setup_task_healthWellness_2)},
{"healthWellness", "todo", getString(R.string.setup_task_healthWellness_3)},
{"school", "habit", getString(R.string.setup_task_school_1), true, true},
{"school", "daily", getString(R.string.setup_task_school_2)},
{"school", "todo", getString(R.string.setup_task_school_3)},
{"teams", "habit", getString(R.string.setup_task_teams_1), true, false},
{"teams", "daily", getString(R.string.setup_task_teams_2)},
{"teams", "todo", getString(R.string.setup_task_teams_3)},
{"chores", "habit", getString(R.string.setup_task_chores_1), true, false},
{"chores", "daily", getString(R.string.setup_task_chores_2)},
{"chores", "todo", getString(R.string.setup_task_chores_3)},
{"creativity", "habit", getString(R.string.setup_task_creativity_1), true, false},
{"creativity", "daily", getString(R.string.setup_task_creativity_2)},
{"creativity", "todo", getString(R.string.setup_task_creativity_3)},
};
}
public List<Task> createSampleTasks() {
List<String> groups = new ArrayList<>();
int i = 0;
for (Boolean checked : this.adapter.checkedList) {
if (checked) {
groups.add(this.taskGroups[i][1]);
}
i++;
}
List<Task> tasks = new ArrayList<>();
for (Object[] task : this.tasks) {
String taskGroup = (String) task[0];
if (groups.contains(taskGroup)) {
Task taskObject;
if (task.length == 5) {
taskObject = this.makeTaskObject((String) task[1], (String) task[2], (Boolean) task[3], (Boolean) task[4]);
} else {
taskObject = this.makeTaskObject((String) task[1], (String) task[2], null, null);
}
tasks.add(taskObject);
}
}
return tasks;
}
private Task makeTaskObject(String type, String text, @Nullable Boolean up, @Nullable Boolean down) {
Task task = new Task();
task.setText(text);
task.setPriority(1.0f);
task.setType(type);
if (type.equals("habit")) {
task.setUp(up);
task.setDown(down);
}
if (type.equals("daily")) {
task.setFrequency("weekly");
task.setStartDate(new Date());
Days days = new Days();
days.setM(true);
days.setT(true);
days.setW(true);
days.setTh(true);
days.setF(true);
days.setS(true);
days.setSu(true);
task.setRepeat(days);
}
return task;
}
}

View file

@ -0,0 +1,147 @@
package com.habitrpg.android.habitica.ui.fragments.setup
import android.os.Bundle
import android.support.v7.widget.GridLayoutManager
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.AppComponent
import com.habitrpg.android.habitica.extensions.inflate
import com.habitrpg.android.habitica.extensions.notNull
import com.habitrpg.android.habitica.models.tasks.Days
import com.habitrpg.android.habitica.models.tasks.Task
import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.ui.AvatarView
import com.habitrpg.android.habitica.ui.SpeechBubbleView
import com.habitrpg.android.habitica.ui.activities.SetupActivity
import com.habitrpg.android.habitica.ui.adapter.setup.TaskSetupAdapter
import com.habitrpg.android.habitica.ui.fragments.BaseFragment
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.ui.helpers.resetViews
import java.util.*
class TaskSetupFragment : BaseFragment() {
var activity: SetupActivity? = null
var width: Int = 0
private val recyclerView: RecyclerView? by bindView(R.id.recyclerView)
private val avatarView: AvatarView? by bindView(R.id.avatarView)
private val speechBubbleView: SpeechBubbleView? by bindView(R.id.speech_bubble)
internal var adapter: TaskSetupAdapter = TaskSetupAdapter()
private var taskGroups: Array<Array<String>> = arrayOf()
private var tasks: Array<Array<Any>> = arrayOf()
private var user: User? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
super.onCreateView(inflater, container, savedInstanceState)
return container?.inflate(R.layout.fragment_setup_tasks)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
resetViews()
this.setTasks()
this.adapter = TaskSetupAdapter()
this.adapter.setTaskList(this.taskGroups)
this.recyclerView?.layoutManager = GridLayoutManager(activity, 2)
this.recyclerView?.adapter = this.adapter
if (this.user != null) {
this.updateAvatar()
}
}
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
super.setUserVisibleHint(isVisibleToUser)
if (isVisibleToUser && context != null) {
speechBubbleView?.animateText(context?.getString(R.string.task_setup_description))
}
}
fun setUser(user: User?) {
this.user = user
if (avatarView != null) {
updateAvatar()
}
}
private fun updateAvatar() {
user.notNull {
avatarView?.setAvatar(it)
}
}
override fun injectFragment(component: AppComponent) {
component.inject(this)
}
private fun setTasks() {
this.taskGroups = arrayOf(arrayOf(getString(R.string.setup_group_work), "work"), arrayOf(getString(R.string.setup_group_exercise), "exercise"), arrayOf(getString(R.string.setup_group_health), "healthWellness"), arrayOf(getString(R.string.setup_group_school), "school"), arrayOf(getString(R.string.setup_group_teams), "teams"), arrayOf(getString(R.string.setup_group_chores), "chores"), arrayOf(getString(R.string.setup_group_creativity), "creativity"), arrayOf(getString(R.string.setuP_group_other), "other"))
this.tasks = arrayOf(arrayOf("work", "habit", getString(R.string.setup_task_work_1), true, false), arrayOf<Any>("work", "daily", getString(R.string.setup_task_work_2)), arrayOf<Any>("work", "todo", getString(R.string.setup_task_work_3)),
arrayOf("exercise", "habit", getString(R.string.setup_task_exercise_1), true, false), arrayOf<Any>("exercise", "daily", getString(R.string.setup_task_exercise_2)), arrayOf<Any>("exercise", "todo", getString(R.string.setup_task_exercise_3)),
arrayOf("healthWellness", "habit", getString(R.string.setup_task_healthWellness_1), true, true), arrayOf<Any>("healthWellness", "daily", getString(R.string.setup_task_healthWellness_2)), arrayOf<Any>("healthWellness", "todo", getString(R.string.setup_task_healthWellness_3)),
arrayOf("school", "habit", getString(R.string.setup_task_school_1), true, true), arrayOf<Any>("school", "daily", getString(R.string.setup_task_school_2)), arrayOf<Any>("school", "todo", getString(R.string.setup_task_school_3)),
arrayOf("teams", "habit", getString(R.string.setup_task_teams_1), true, false), arrayOf<Any>("teams", "daily", getString(R.string.setup_task_teams_2)), arrayOf<Any>("teams", "todo", getString(R.string.setup_task_teams_3)),
arrayOf("chores", "habit", getString(R.string.setup_task_chores_1), true, false), arrayOf<Any>("chores", "daily", getString(R.string.setup_task_chores_2)), arrayOf<Any>("chores", "todo", getString(R.string.setup_task_chores_3)),
arrayOf("creativity", "habit", getString(R.string.setup_task_creativity_1), true, false), arrayOf<Any>("creativity", "daily", getString(R.string.setup_task_creativity_2)), arrayOf<Any>("creativity", "todo", getString(R.string.setup_task_creativity_3)))
}
fun createSampleTasks(): List<Task> {
val groups = ArrayList<String>()
for ((i, checked) in this.adapter.checkedList.withIndex()) {
if (checked) {
groups.add(this.taskGroups[i][1])
}
}
val tasks = ArrayList<Task>()
for (task in this.tasks) {
val taskGroup = task[0] as String
if (groups.contains(taskGroup)) {
val taskObject: Task = if (task.size == 5) {
this.makeTaskObject(task[1] as String, task[2] as String, task[3] as Boolean, task[4] as Boolean)
} else {
this.makeTaskObject(task[1] as String, task[2] as String, null, null)
}
tasks.add(taskObject)
}
}
return tasks
}
private fun makeTaskObject(type: String, text: String, up: Boolean?, down: Boolean?): Task {
val task = Task()
task.text = text
task.priority = 1.0f
task.type = type
if (type == "habit") {
task.up = up
task.down = down
}
if (type == "daily") {
task.frequency = "weekly"
task.startDate = Date()
val days = Days()
days.m = true
days.t = true
days.w = true
days.th = true
days.f = true
days.s = true
days.su = true
task.repeat = days
}
return task
}
}

View file

@ -1,58 +0,0 @@
package com.habitrpg.android.habitica.ui.fragments.setup;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.components.AppComponent;
import com.habitrpg.android.habitica.ui.SpeechBubbleView;
import com.habitrpg.android.habitica.ui.fragments.BaseFragment;
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper;
import butterknife.BindView;
import butterknife.ButterKnife;
public class WelcomeFragment extends BaseFragment {
@BindView(R.id.speech_bubble)
SpeechBubbleView speechBubbleView;
@BindView(R.id.heart_icon)
ImageView heartIconView;
@BindView(R.id.exp_icon)
ImageView expIconView;
@BindView(R.id.magic_icon)
ImageView magicIconView;
@BindView(R.id.gold_icon)
ImageView goldIconView;
@BindView(R.id.gem_icon)
ImageView gemIconView;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View v = inflater.inflate(R.layout.fragment_welcome, container, false);
setUnbinder(ButterKnife.bind(this, v));
speechBubbleView.animateText(getContext().getString(R.string.welcome_text));
heartIconView.setImageBitmap(HabiticaIconsHelper.imageOfHeartLightBg());
expIconView.setImageBitmap(HabiticaIconsHelper.imageOfExperience());
magicIconView.setImageBitmap(HabiticaIconsHelper.imageOfMagic());
goldIconView.setImageBitmap(HabiticaIconsHelper.imageOfGold());
gemIconView.setImageBitmap(HabiticaIconsHelper.imageOfGem());
return v;
}
@Override
public void injectFragment(AppComponent component) {
component.inject(this);
}
}

View file

@ -0,0 +1,49 @@
package com.habitrpg.android.habitica.ui.fragments.setup
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.AppComponent
import com.habitrpg.android.habitica.extensions.inflate
import com.habitrpg.android.habitica.ui.SpeechBubbleView
import com.habitrpg.android.habitica.ui.fragments.BaseFragment
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.ui.helpers.resetViews
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
class WelcomeFragment : BaseFragment() {
private val speechBubbleView: SpeechBubbleView? by bindView(R.id.speech_bubble)
private val heartIconView: ImageView? by bindView(R.id.heart_icon)
private val magicIconView: ImageView? by bindView(R.id.magic_icon)
private val expIconView: ImageView? by bindView(R.id.exp_icon)
private val goldIconView: ImageView? by bindView(R.id.gold_icon)
private val gemIconView: ImageView? by bindView(R.id.gem_icon)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
super.onCreateView(inflater, container, savedInstanceState)
return container?.inflate(R.layout.fragment_welcome)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
resetViews()
speechBubbleView?.animateText(context?.getString(R.string.welcome_text))
heartIconView?.setImageBitmap(HabiticaIconsHelper.imageOfHeartLightBg())
expIconView?.setImageBitmap(HabiticaIconsHelper.imageOfExperience())
magicIconView?.setImageBitmap(HabiticaIconsHelper.imageOfMagic())
goldIconView?.setImageBitmap(HabiticaIconsHelper.imageOfGold())
gemIconView?.setImageBitmap(HabiticaIconsHelper.imageOfGem())
}
override fun injectFragment(component: AppComponent) {
component.inject(this)
}
}

View file

@ -1,141 +0,0 @@
package com.habitrpg.android.habitica.ui.fragments.social;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.widget.SwipeRefreshLayout;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.components.AppComponent;
import com.habitrpg.android.habitica.data.ChallengeRepository;
import com.habitrpg.android.habitica.data.SocialRepository;
import com.habitrpg.android.habitica.helpers.RxErrorHandler;
import com.habitrpg.android.habitica.models.social.Group;
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
import io.realm.RealmResults;
public class GuildsOverviewFragment extends BaseMainFragment implements View.OnClickListener, SwipeRefreshLayout.OnRefreshListener {
@Inject
SocialRepository socialRepository;
@Inject
ChallengeRepository challengeRepository;
@BindView(R.id.my_guilds_listview)
LinearLayout guildsListView;
@BindView(R.id.publicGuildsButton)
Button publicGuildsButton;
@BindView(R.id.chat_refresh_layout)
SwipeRefreshLayout swipeRefreshLayout;
private List<Group> guilds;
private ArrayList<String> guildIDs;
@Override
public void onCreate(Bundle savedInstance) {
super.onCreate(savedInstance);
this.fetchGuilds();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View v = inflater.inflate(R.layout.fragment_guilds_overview, container, false);
setUnbinder(ButterKnife.bind(this, v));
swipeRefreshLayout.setOnRefreshListener(this);
this.publicGuildsButton.setOnClickListener(this);
getCompositeSubscription().add(socialRepository.getUserGroups().subscribe(this::setGuilds, RxErrorHandler.handleEmptyError()));
return v;
}
@Override
public void onDestroy() {
socialRepository.close();
challengeRepository.close();
super.onDestroy();
}
@Override
public void injectFragment(AppComponent component) {
component.inject(this);
}
@Override
public void onRefresh() {
if (swipeRefreshLayout != null) {
swipeRefreshLayout.setRefreshing(true);
}
fetchGuilds();
}
private void fetchGuilds() {
if (this.socialRepository != null) {
this.socialRepository.retrieveGroups("guilds")
.subscribe(groups -> {
if (swipeRefreshLayout != null) {
swipeRefreshLayout.setRefreshing(false);
}
}, RxErrorHandler.handleEmptyError());
}
}
private void setGuilds(RealmResults<Group> guilds) {
this.guilds = guilds;
if (this.guildsListView == null) {
return;
}
this.guildIDs = new ArrayList<>();
this.guildsListView.removeAllViewsInLayout();
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
for (Group guild : this.guilds) {
TextView entry = (TextView) inflater.inflate(R.layout.plain_list_item, this.guildsListView, false);
entry.setText(guild.getName());
entry.setOnClickListener(this);
this.guildsListView.addView(entry);
this.guildIDs.add(guild.getId());
}
}
@Override
public void onClick(View v) {
BaseMainFragment fragment;
if (v == this.publicGuildsButton) {
PublicGuildsFragment publicGuildsFragment = new PublicGuildsFragment();
publicGuildsFragment.memberGuildIDs = this.guildIDs;
fragment = publicGuildsFragment;
} else {
Integer guildIndex = ((ViewGroup) v.getParent()).indexOfChild(v);
GuildFragment guildFragment = new GuildFragment();
guildFragment.setGuildId(this.guilds.get(guildIndex).getId());
guildFragment.isMember = true;
fragment = guildFragment;
}
if (this.activity != null) {
this.activity.displayFragment(fragment);
}
}
@Override
public String customTitle() {
if (isAdded()) {
return getString(R.string.sidebar_guilds);
} else {
return "";
}
}
}

View file

@ -0,0 +1,126 @@
package com.habitrpg.android.habitica.ui.fragments.social
import android.content.Context
import android.os.Bundle
import android.support.v4.widget.SwipeRefreshLayout
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.LinearLayout
import android.widget.TextView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.AppComponent
import com.habitrpg.android.habitica.data.ChallengeRepository
import com.habitrpg.android.habitica.data.SocialRepository
import com.habitrpg.android.habitica.extensions.inflate
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.social.Group
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.ui.helpers.resetViews
import io.reactivex.functions.Consumer
import io.realm.RealmResults
import java.util.*
import javax.inject.Inject
class GuildsOverviewFragment : BaseMainFragment(), View.OnClickListener, SwipeRefreshLayout.OnRefreshListener {
@Inject
lateinit var socialRepository: SocialRepository
@Inject
lateinit var challengeRepository: ChallengeRepository
private val guildsListView: LinearLayout? by bindView(R.id.my_guilds_listview)
private val publicGuildsButton: Button? by bindView(R.id.publicGuildsButton)
private val swipeRefreshLayout: SwipeRefreshLayout? by bindView(R.id.chat_refresh_layout)
private var guilds: List<Group>? = null
private var guildIDs: ArrayList<String>? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
this.fetchGuilds()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
super.onCreateView(inflater, container, savedInstanceState)
return container?.inflate(R.layout.fragment_guilds_overview)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
resetViews()
swipeRefreshLayout?.setOnRefreshListener(this)
this.publicGuildsButton?.setOnClickListener(this)
compositeSubscription.add(socialRepository.getUserGroups().subscribe(Consumer { this.setGuilds(it) }, RxErrorHandler.handleEmptyError()))
}
override fun onDestroy() {
socialRepository.close()
challengeRepository.close()
super.onDestroy()
}
override fun injectFragment(component: AppComponent) {
component.inject(this)
}
override fun onRefresh() {
if (swipeRefreshLayout != null) {
swipeRefreshLayout?.isRefreshing = true
}
fetchGuilds()
}
private fun fetchGuilds() {
this.socialRepository.retrieveGroups("guilds")
.subscribe(Consumer {
swipeRefreshLayout?.isRefreshing = false
}, RxErrorHandler.handleEmptyError())
}
private fun setGuilds(guilds: RealmResults<Group>) {
this.guilds = guilds
if (this.guildsListView == null) {
return
}
this.guildIDs = ArrayList()
this.guildsListView?.removeAllViewsInLayout()
val inflater = context?.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
for (guild in guilds) {
val entry = inflater.inflate(R.layout.plain_list_item, this.guildsListView, false) as TextView
entry.text = guild.name
entry.setOnClickListener(this)
this.guildsListView?.addView(entry)
this.guildIDs?.add(guild.id)
}
}
override fun onClick(v: View) {
val fragment: BaseMainFragment
if (v === this.publicGuildsButton) {
val publicGuildsFragment = PublicGuildsFragment()
publicGuildsFragment.memberGuildIDs = this.guildIDs
fragment = publicGuildsFragment
} else {
val guildIndex = (v.parent as ViewGroup).indexOfChild(v)
val guildFragment = GuildFragment()
guildFragment.setGuildId(this.guilds?.get(guildIndex)?.id)
guildFragment.isMember = true
fragment = guildFragment
}
activity?.displayFragment(fragment)
}
override fun customTitle(): String {
return if (isAdded) {
getString(R.string.sidebar_guilds)
} else {
""
}
}
}

View file

@ -1,127 +0,0 @@
package com.habitrpg.android.habitica.ui.fragments.social;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.SearchView;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.components.AppComponent;
import com.habitrpg.android.habitica.data.SocialRepository;
import com.habitrpg.android.habitica.helpers.RxErrorHandler;
import com.habitrpg.android.habitica.ui.adapter.social.PublicGuildsRecyclerViewAdapter;
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment;
import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator;
import com.habitrpg.android.habitica.ui.helpers.UiUtils;
import java.util.List;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
public class PublicGuildsFragment extends BaseMainFragment implements SearchView.OnQueryTextListener {
@Inject
SocialRepository socialRepository;
List<String> memberGuildIDs;
@BindView(R.id.recyclerView)
RecyclerView recyclerView;
private View view;
private PublicGuildsRecyclerViewAdapter viewAdapter;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
view = inflater.inflate(R.layout.fragment_recyclerview, container, false);
setUnbinder(ButterKnife.bind(this, view));
recyclerView.setLayoutManager(new LinearLayoutManager(this.activity));
recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL));
viewAdapter = new PublicGuildsRecyclerViewAdapter(null, true);
viewAdapter.setMemberGuildIDs(this.memberGuildIDs);
viewAdapter.apiClient = this.apiClient;
recyclerView.setAdapter(viewAdapter);
recyclerView.setItemAnimator(new SafeDefaultItemAnimator());
this.fetchGuilds();
return view;
}
@Override
public void injectFragment(AppComponent component) {
component.inject(this);
}
@Override
public void onCreate(Bundle savedInstance) {
super.onCreate(savedInstance);
}
@Override
public void onDestroy() {
socialRepository.close();
super.onDestroy();
}
private void fetchGuilds() {
if (this.socialRepository != null) {
this.socialRepository.getPublicGuilds()
.firstElement()
.subscribe(groups -> {
if (PublicGuildsFragment.this.viewAdapter != null) {
PublicGuildsFragment.this.viewAdapter.updateData(groups);
}
}, RxErrorHandler.handleEmptyError());
this.socialRepository.retrieveGroups("publicGuilds").subscribe(groups -> {}, RxErrorHandler.handleEmptyError());
}
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_public_guild, menu);
MenuItem searchItem = menu.findItem(R.id.action_guild_search);
SearchView guildSearchView = (SearchView) searchItem.getActionView();
SearchView.SearchAutoComplete theTextArea = guildSearchView.findViewById(R.id.search_src_text);
theTextArea.setHintTextColor(ContextCompat.getColor(getContext(), R.color.white));
guildSearchView.setQueryHint(getString(R.string.guild_search_hint));
guildSearchView.setOnQueryTextListener(this);
}
@Override
public boolean onQueryTextSubmit(String s) {
viewAdapter.getFilter().filter(s);
if (this.activity != null) {
UiUtils.dismissKeyboard(this.activity);
}
return true;
}
@Override
public boolean onQueryTextChange(String s) {
viewAdapter.getFilter().filter(s);
return true;
}
@Override
public String customTitle() {
if (!isAdded()) {
return "";
}
return getString(R.string.public_guilds);
}
}

View file

@ -0,0 +1,102 @@
package com.habitrpg.android.habitica.ui.fragments.social
import android.os.Bundle
import android.support.v4.content.ContextCompat
import android.support.v7.widget.DividerItemDecoration
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.support.v7.widget.SearchView
import android.view.*
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.AppComponent
import com.habitrpg.android.habitica.data.SocialRepository
import com.habitrpg.android.habitica.extensions.inflate
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.ui.adapter.social.PublicGuildsRecyclerViewAdapter
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator
import com.habitrpg.android.habitica.ui.helpers.UiUtils
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.ui.helpers.resetViews
import io.reactivex.functions.Consumer
import javax.inject.Inject
class PublicGuildsFragment : BaseMainFragment(), SearchView.OnQueryTextListener {
@Inject
lateinit var socialRepository: SocialRepository
var memberGuildIDs: List<String>? = null
private val recyclerView: RecyclerView? by bindView(R.id.recyclerView)
private var viewAdapter = PublicGuildsRecyclerViewAdapter(null, true)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
super.onCreateView(inflater, container, savedInstanceState)
return container?.inflate(R.layout.fragment_recyclerview)
}
override fun injectFragment(component: AppComponent) {
component.inject(this)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
resetViews()
recyclerView?.layoutManager = LinearLayoutManager(this.activity)
recyclerView?.addItemDecoration(DividerItemDecoration(getActivity()!!, DividerItemDecoration.VERTICAL))
viewAdapter.setMemberGuildIDs(this.memberGuildIDs)
viewAdapter.apiClient = this.apiClient
viewAdapter = PublicGuildsRecyclerViewAdapter(null, true)
recyclerView?.adapter = viewAdapter
recyclerView?.itemAnimator = SafeDefaultItemAnimator()
this.fetchGuilds()
}
override fun onDestroy() {
socialRepository.close()
super.onDestroy()
}
private fun fetchGuilds() {
this.socialRepository.getPublicGuilds()
.firstElement()
.subscribe(Consumer { groups ->
this@PublicGuildsFragment.viewAdapter.updateData(groups)
}, RxErrorHandler.handleEmptyError())
this.socialRepository.retrieveGroups("publicGuilds").subscribe(Consumer { }, RxErrorHandler.handleEmptyError())
}
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
inflater!!.inflate(R.menu.menu_public_guild, menu)
val searchItem = menu!!.findItem(R.id.action_guild_search)
val guildSearchView = searchItem.actionView as SearchView
val theTextArea = guildSearchView.findViewById<SearchView.SearchAutoComplete>(R.id.search_src_text)
theTextArea.setHintTextColor(ContextCompat.getColor(context!!, R.color.white))
guildSearchView.queryHint = getString(R.string.guild_search_hint)
guildSearchView.setOnQueryTextListener(this)
}
override fun onQueryTextSubmit(s: String): Boolean {
viewAdapter.filter.filter(s)
if (this.activity != null) {
UiUtils.dismissKeyboard(this.activity!!)
}
return true
}
override fun onQueryTextChange(s: String): Boolean {
viewAdapter.filter.filter(s)
return true
}
override fun customTitle(): String {
return if (!isAdded) {
""
} else getString(R.string.public_guilds)
}
}

View file

@ -165,7 +165,8 @@ public class QuestDetailFragment extends BaseMainFragment {
return;
}
questTitleView.setText(questContent.getText());
questDescriptionView.setText(MarkdownParser.parseMarkdown(questContent.getNotes()));
//TODO: FIX
//questDescriptionView.setText(MarkdownParser.parseMarkdown(questContent.getNotes()));
DataBindingUtils.INSTANCE.loadImage(questScrollImageView, "inventory_quest_scroll_"+questContent.getKey());
}

View file

@ -1,337 +0,0 @@
package com.habitrpg.android.habitica.ui.fragments.social.challenges;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.data.ChallengeRepository;
import com.habitrpg.android.habitica.helpers.RxErrorHandler;
import com.habitrpg.android.habitica.models.LeaveChallengeBody;
import com.habitrpg.android.habitica.models.social.Challenge;
import com.habitrpg.android.habitica.models.tasks.Task;
import com.habitrpg.android.habitica.ui.activities.ChallengeDetailActivity;
import com.habitrpg.android.habitica.ui.activities.FullProfileActivity;
import com.habitrpg.android.habitica.ui.helpers.MarkdownParser;
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper;
import com.habitrpg.android.habitica.utils.Action1;
import net.pherth.android.emoji_library.EmojiParser;
import net.pherth.android.emoji_library.EmojiTextView;
import java.util.ArrayList;
import java.util.Map;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class ChallengeDetailDialogHolder {
@BindView(R.id.challenge_not_joined_header)
LinearLayout notJoinedHeader;
@BindView(R.id.challenge_joined_header)
LinearLayout joinedHeader;
@BindView(R.id.challenge_join_btn)
Button joinButton;
@BindView(R.id.challenge_leave_btn)
Button leaveButton;
@BindView(R.id.challenge_name)
EmojiTextView challengeName;
@BindView(R.id.challenge_description)
EmojiTextView challengeDescription;
@BindView(R.id.challenge_leader)
TextView challengeLeader;
@BindView(R.id.gem_amount)
TextView gem_amount;
@BindView(R.id.challenge_member_count)
TextView member_count;
@BindView(R.id.task_group_layout)
LinearLayout task_group_layout;
private AlertDialog dialog;
private ChallengeRepository challengeRepository;
@Nullable
private Challenge challenge;
private Action1<Challenge> challengeLeftAction;
private Activity context;
private ChallengeDetailDialogHolder(View view, Activity context) {
this.context = context;
ButterKnife.bind(this, view);
}
public static void showDialog(Activity activity, ChallengeRepository challengeRepository, Challenge challenge, Action1<Challenge> challengeLeftAction) {
if (activity == null) {
return;
}
View dialogLayout = activity.getLayoutInflater().inflate(R.layout.dialog_challenge_detail, null);
ChallengeDetailDialogHolder challengeDetailDialogHolder = new ChallengeDetailDialogHolder(dialogLayout, activity);
AlertDialog.Builder builder = new AlertDialog.Builder(activity)
.setView(dialogLayout);
challengeDetailDialogHolder.bind(builder.show(), challengeRepository, challenge, challengeLeftAction);
}
public void bind(AlertDialog dialog, ChallengeRepository challengeRepository, Challenge challenge,
Action1<Challenge> challengeLeftAction) {
this.dialog = dialog;
this.challengeRepository = challengeRepository;
this.challenge = challenge;
this.challengeLeftAction = challengeLeftAction;
changeViewsByChallenge(challenge);
}
private void changeViewsByChallenge(Challenge challenge) {
setJoined(challenge.isParticipating);
challengeName.setText(EmojiParser.parseEmojis(challenge.name));
challengeDescription.setText(MarkdownParser.parseMarkdown(challenge.description));
challengeLeader.setText(challenge.leaderName);
gem_amount.setText(String.valueOf(challenge.prize));
member_count.setText(String.valueOf(challenge.memberCount));
challengeRepository.getChallengeTasks(challenge.id)
.subscribe(taskList -> {
ArrayList<Task> todos = new ArrayList<>();
ArrayList<Task> habits = new ArrayList<>();
ArrayList<Task> dailies = new ArrayList<>();
ArrayList<Task> rewards = new ArrayList<>();
for (Map.Entry<String, Task> entry : taskList.tasks.entrySet()) {
switch (entry.getValue().getType()) {
case Task.TYPE_TODO:
todos.add(entry.getValue());
break;
case Task.TYPE_HABIT:
habits.add(entry.getValue());
break;
case Task.TYPE_DAILY:
dailies.add(entry.getValue());
break;
case Task.TYPE_REWARD:
rewards.add(entry.getValue());
break;
}
}
if (habits.size() > 0) {
addHabits(habits);
}
if (dailies.size() > 0) {
addDailys(dailies);
}
if (todos.size() > 0) {
addTodos(todos);
}
if (rewards.size() > 0) {
addRewards(rewards);
}
}, RxErrorHandler.handleEmptyError());
}
private void addHabits(ArrayList<Task> habits) {
LinearLayout taskGroup = (LinearLayout) context.getLayoutInflater().inflate(R.layout.dialog_challenge_detail_task_group, task_group_layout, false);
TextView groupName = (TextView) taskGroup.findViewById(R.id.task_group_name);
LinearLayout tasks_layout = (LinearLayout) taskGroup.findViewById(R.id.tasks_layout);
groupName.setText(habits.size() + " " + getLabelByTypeAndCount(Challenge.TASK_ORDER_HABITS, habits.size()));
int size = habits.size();
for (int i = 0; i < size; i++) {
Task task = habits.get(i);
View entry = context.getLayoutInflater().inflate(R.layout.dialog_challenge_detail_habit, tasks_layout, false);
TextView habitTitle = (TextView) entry.findViewById(R.id.habit_title);
ImageView plusImg = (ImageView) entry.findViewById(task.getUp() ? R.id.plus_img_tinted : R.id.plus_img);
ImageView minusImg = (ImageView) entry.findViewById(task.getDown() ? R.id.minus_img_tinted : R.id.minus_img);
plusImg.setVisibility(View.VISIBLE);
minusImg.setVisibility(View.VISIBLE);
habitTitle.setText(EmojiParser.parseEmojis(task.getText()));
tasks_layout.addView(entry);
}
task_group_layout.addView(taskGroup);
}
private void addDailys(ArrayList<Task> dailies) {
LinearLayout taskGroup = (LinearLayout) context.getLayoutInflater().inflate(R.layout.dialog_challenge_detail_task_group, task_group_layout, false);
TextView groupName = (TextView) taskGroup.findViewById(R.id.task_group_name);
LinearLayout tasks_layout = (LinearLayout) taskGroup.findViewById(R.id.tasks_layout);
int size = dailies.size();
groupName.setText(dailies.size() + " " + getLabelByTypeAndCount(Challenge.TASK_ORDER_DAILYS, size));
for (int i = 0; i < size; i++) {
Task task = dailies.get(i);
View entry = context.getLayoutInflater().inflate(R.layout.dialog_challenge_detail_daily, tasks_layout, false);
TextView title = (TextView) entry.findViewById(R.id.daily_title);
title.setText(EmojiParser.parseEmojis(task.getText()));
if (task.getChecklist() != null && !task.getChecklist().isEmpty()) {
View checklistIndicatorWrapper = entry.findViewById(R.id.checklistIndicatorWrapper);
checklistIndicatorWrapper.setVisibility(View.VISIBLE);
TextView checkListAllTextView = (TextView) entry.findViewById(R.id.checkListAllTextView);
checkListAllTextView.setText(String.valueOf(task.getChecklist().size()));
}
tasks_layout.addView(entry);
}
task_group_layout.addView(taskGroup);
}
private void addTodos(ArrayList<Task> todos) {
LinearLayout taskGroup = (LinearLayout) context.getLayoutInflater().inflate(R.layout.dialog_challenge_detail_task_group, task_group_layout, false);
TextView groupName = (TextView) taskGroup.findViewById(R.id.task_group_name);
LinearLayout tasks_layout = (LinearLayout) taskGroup.findViewById(R.id.tasks_layout);
int size = todos.size();
groupName.setText(todos.size() + " " + getLabelByTypeAndCount(Challenge.TASK_ORDER_TODOS, size));
for (int i = 0; i < size; i++) {
Task task = todos.get(i);
View entry = context.getLayoutInflater().inflate(R.layout.dialog_challenge_detail_todo, tasks_layout, false);
TextView title = (TextView) entry.findViewById(R.id.todo_title);
title.setText(EmojiParser.parseEmojis(task.getText()));
if (task.getChecklist() != null && !task.getChecklist().isEmpty()) {
View checklistIndicatorWrapper = entry.findViewById(R.id.checklistIndicatorWrapper);
checklistIndicatorWrapper.setVisibility(View.VISIBLE);
TextView checkListAllTextView = (TextView) entry.findViewById(R.id.checkListAllTextView);
checkListAllTextView.setText(String.valueOf(task.getChecklist().size()));
}
tasks_layout.addView(entry);
}
task_group_layout.addView(taskGroup);
}
private void addRewards(ArrayList<Task> rewards) {
LinearLayout taskGroup = (LinearLayout) context.getLayoutInflater().inflate(R.layout.dialog_challenge_detail_task_group, task_group_layout, false);
TextView groupName = (TextView) taskGroup.findViewById(R.id.task_group_name);
LinearLayout tasks_layout = (LinearLayout) taskGroup.findViewById(R.id.tasks_layout);
int size = rewards.size();
groupName.setText(rewards.size() + " " + getLabelByTypeAndCount(Challenge.TASK_ORDER_REWARDS, size));
for (int i = 0; i < size; i++) {
Task task = rewards.get(i);
View entry = context.getLayoutInflater().inflate(R.layout.dialog_challenge_detail_reward, tasks_layout, false);
((ImageView)entry.findViewById(R.id.gold_icon)).setImageBitmap(HabiticaIconsHelper.imageOfGold());
TextView title = (TextView) entry.findViewById(R.id.reward_title);
title.setText(EmojiParser.parseEmojis(task.getText()));
tasks_layout.addView(entry);
}
task_group_layout.addView(taskGroup);
}
private String getLabelByTypeAndCount(String type, int count) {
if (Challenge.TASK_ORDER_DAILYS.equals(type)) {
return context.getString(count == 1 ? R.string.daily : R.string.dailies);
} else if (Challenge.TASK_ORDER_HABITS.equals(type)) {
return context.getString(count == 1 ? R.string.habit : R.string.habits);
} else if (Challenge.TASK_ORDER_REWARDS.equals(type)) {
return context.getString(count == 1 ? R.string.reward : R.string.rewards);
} else {
return context.getString(count == 1 ? R.string.todo : R.string.todos);
}
}
private void setJoined(boolean joined) {
joinedHeader.setVisibility(joined ? View.VISIBLE : View.GONE);
leaveButton.setVisibility(joined ? View.VISIBLE : View.GONE);
notJoinedHeader.setVisibility(joined ? View.GONE : View.VISIBLE);
joinButton.setVisibility(joined ? View.GONE : View.VISIBLE);
}
@OnClick(R.id.challenge_leader)
void openLeaderProfile() {
FullProfileActivity.open(context, challenge.leaderId);
}
@OnClick(R.id.challenge_go_to_btn)
void openChallengeActivity() {
Bundle bundle = new Bundle();
bundle.putString(ChallengeDetailActivity.CHALLENGE_ID, challenge.id);
Intent intent = new Intent(context, ChallengeDetailActivity.class);
intent.putExtras(bundle);
context.startActivity(intent);
this.dialog.dismiss();
}
@OnClick(R.id.challenge_join_btn)
void joinChallenge() {
this.challengeRepository.joinChallenge(challenge).subscribe(this::changeViewsByChallenge, RxErrorHandler.handleEmptyError());
}
@OnClick(R.id.challenge_leave_btn)
void leaveChallenge() {
new AlertDialog.Builder(context)
.setTitle(context.getString(R.string.challenge_leave_title))
.setMessage(context.getString(R.string.challenge_leave_text, challenge.name))
.setPositiveButton(context.getString(R.string.yes), (dialog, which) ->
showRemoveTasksDialog(keepTasks -> this.challengeRepository.leaveChallenge(challenge, new LeaveChallengeBody(keepTasks))
.subscribe(aVoid -> {
if (challengeLeftAction != null) {
challengeLeftAction.call(challenge);
}
this.dialog.dismiss();
}, RxErrorHandler.handleEmptyError())))
.setNegativeButton(context.getString(R.string.no), (dialog, which) -> dialog.dismiss()).show();
}
// refactor as an UseCase later - see ChallengeDetailActivity
private void showRemoveTasksDialog(Action1<String> callback){
new AlertDialog.Builder(context)
.setTitle(context.getString(R.string.challenge_remove_tasks_title))
.setMessage(context.getString(R.string.challenge_remove_tasks_text))
.setPositiveButton(context.getString(R.string.remove_tasks), (dialog, which) -> {
callback.call("remove-all");
dialog.dismiss();
})
.setNegativeButton(context.getString(R.string.keep_tasks), (dialog, which) -> {
callback.call("keep-all");
dialog.dismiss();
}).show();
}
}

View file

@ -0,0 +1,307 @@
package com.habitrpg.android.habitica.ui.fragments.social.challenges
import android.app.Activity
import android.app.AlertDialog
import android.content.Intent
import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.data.ChallengeRepository
import com.habitrpg.android.habitica.extensions.notNull
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.LeaveChallengeBody
import com.habitrpg.android.habitica.models.social.Challenge
import com.habitrpg.android.habitica.models.tasks.Task
import com.habitrpg.android.habitica.ui.activities.ChallengeDetailActivity
import com.habitrpg.android.habitica.ui.activities.FullProfileActivity
import com.habitrpg.android.habitica.ui.helpers.MarkdownParser
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
import com.habitrpg.android.habitica.utils.Action1
import io.reactivex.functions.Consumer
import net.pherth.android.emoji_library.EmojiParser
import net.pherth.android.emoji_library.EmojiTextView
import java.util.*
class ChallengeDetailDialogHolder private constructor(view: View, private val context: Activity) {
private val notJoinedHeader: LinearLayout? by bindView(view, R.id.challenge_not_joined_header)
private val joinedHeader: LinearLayout? by bindView(view, R.id.challenge_joined_header)
private val joinButton: Button? by bindView(view, R.id.challenge_join_btn)
private val leaveButton: Button? by bindView(view, R.id.challenge_leave_btn)
private val challengeName: EmojiTextView? by bindView(view, R.id.challenge_name)
private val challengeDescription: EmojiTextView? by bindView(view, R.id.challenge_description)
private val challengeLeader: TextView? by bindView(view, R.id.challenge_leader)
private val gemAmountView: TextView? by bindView(view, R.id.gem_amount)
private val memberCountView: TextView? by bindView(view, R.id.challenge_member_count)
private val taskGrouplayout: LinearLayout? by bindView(view, R.id.task_group_layout)
private val openChallengeButton: Button? by bindView(view, R.id.challenge_go_to_btn)
private var dialog: AlertDialog? = null
lateinit var challengeRepository: ChallengeRepository
private var challenge: Challenge? = null
private var challengeLeftAction: Action1<Challenge>? = null
init {
joinButton?.setOnClickListener { joinChallenge() }
leaveButton?.setOnClickListener { leaveChallenge() }
challengeLeader?.setOnClickListener { openLeaderProfile() }
openChallengeButton?.setOnClickListener { openChallengeActivity() }
}
fun bind(dialog: AlertDialog, challengeRepository: ChallengeRepository, challenge: Challenge,
challengeLeftAction: Action1<Challenge>) {
this.dialog = dialog
this.challengeRepository = challengeRepository
this.challenge = challenge
this.challengeLeftAction = challengeLeftAction
changeViewsByChallenge(challenge)
}
private fun changeViewsByChallenge(challenge: Challenge) {
setJoined(challenge.isParticipating)
challengeName?.text = EmojiParser.parseEmojis(challenge.name)
challengeDescription?.text = MarkdownParser.parseMarkdown(challenge.description)
challengeLeader?.text = challenge.leaderName
gemAmountView?.text = challenge.prize.toString()
memberCountView?.text = challenge.memberCount.toString()
challengeRepository.getChallengeTasks(challenge.id)
.subscribe(Consumer { taskList ->
val todos = ArrayList<Task>()
val habits = ArrayList<Task>()
val dailies = ArrayList<Task>()
val rewards = ArrayList<Task>()
for (entry in taskList.tasks.entries) {
when (entry.value.type) {
Task.TYPE_TODO -> todos.add(entry.value)
Task.TYPE_HABIT ->
habits.add(entry.value)
Task.TYPE_DAILY ->
dailies.add(entry.value)
Task.TYPE_REWARD ->
rewards.add(entry.value)
}
}
if (habits.size > 0) {
addHabits(habits)
}
if (dailies.size > 0) {
addDailys(dailies)
}
if (todos.size > 0) {
addTodos(todos)
}
if (rewards.size > 0) {
addRewards(rewards)
}
}, RxErrorHandler.handleEmptyError())
}
private fun addHabits(habits: ArrayList<Task>) {
val taskGroup = context.layoutInflater.inflate(R.layout.dialog_challenge_detail_task_group, taskGrouplayout, false) as LinearLayout
val groupName = taskGroup.findViewById<View>(R.id.task_group_name) as TextView
val tasksLayout = taskGroup.findViewById<View>(R.id.tasks_layout) as LinearLayout
groupName.text = habits.size.toString() + " " + getLabelByTypeAndCount(Challenge.TASK_ORDER_HABITS, habits.size)
val size = habits.size
for (i in 0 until size) {
val task = habits[i]
val entry = context.layoutInflater.inflate(R.layout.dialog_challenge_detail_habit, tasksLayout, false)
val habitTitle = entry.findViewById<View>(R.id.habit_title) as TextView
val plusImg = entry.findViewById<View>(if (task.up == true) R.id.plus_img_tinted else R.id.plus_img) as ImageView
val minusImg = entry.findViewById<View>(if (task.down == true) R.id.minus_img_tinted else R.id.minus_img) as ImageView
plusImg.visibility = View.VISIBLE
minusImg.visibility = View.VISIBLE
habitTitle.text = EmojiParser.parseEmojis(task.text)
tasksLayout.addView(entry)
}
taskGrouplayout?.addView(taskGroup)
}
private fun addDailys(dailies: ArrayList<Task>) {
val taskGroup = context.layoutInflater.inflate(R.layout.dialog_challenge_detail_task_group, taskGrouplayout, false) as LinearLayout
val groupName = taskGroup.findViewById<View>(R.id.task_group_name) as TextView
val tasksLayout = taskGroup.findViewById<View>(R.id.tasks_layout) as LinearLayout
val size = dailies.size
groupName.text = dailies.size.toString() + " " + getLabelByTypeAndCount(Challenge.TASK_ORDER_DAILYS, size)
for (i in 0 until size) {
val task = dailies[i]
val entry = context.layoutInflater.inflate(R.layout.dialog_challenge_detail_daily, tasksLayout, false)
val title = entry.findViewById<View>(R.id.daily_title) as TextView
title.text = EmojiParser.parseEmojis(task.text)
if (task.checklist != null && task.checklist?.isEmpty() == false) {
val checklistIndicatorWrapper = entry.findViewById<View>(R.id.checklistIndicatorWrapper)
checklistIndicatorWrapper.visibility = View.VISIBLE
val checkListAllTextView = entry.findViewById<View>(R.id.checkListAllTextView) as TextView
checkListAllTextView.text = task.checklist?.size.toString()
}
tasksLayout.addView(entry)
}
taskGrouplayout?.addView(taskGroup)
}
private fun addTodos(todos: ArrayList<Task>) {
val taskGroup = context.layoutInflater.inflate(R.layout.dialog_challenge_detail_task_group, taskGrouplayout, false) as LinearLayout
val groupName = taskGroup.findViewById<View>(R.id.task_group_name) as TextView
val tasksLayout = taskGroup.findViewById<View>(R.id.tasks_layout) as LinearLayout
val size = todos.size
groupName.text = todos.size.toString() + " " + getLabelByTypeAndCount(Challenge.TASK_ORDER_TODOS, size)
for (i in 0 until size) {
val task = todos[i]
val entry = context.layoutInflater.inflate(R.layout.dialog_challenge_detail_todo, tasksLayout, false)
val title = entry.findViewById<View>(R.id.todo_title) as TextView
title.text = EmojiParser.parseEmojis(task.text)
if (task.checklist != null && task.checklist?.isEmpty() == false) {
val checklistIndicatorWrapper = entry.findViewById<View>(R.id.checklistIndicatorWrapper)
checklistIndicatorWrapper.visibility = View.VISIBLE
val checkListAllTextView = entry.findViewById<View>(R.id.checkListAllTextView) as TextView
checkListAllTextView.text = task.checklist?.size.toString()
}
tasksLayout.addView(entry)
}
taskGrouplayout?.addView(taskGroup)
}
private fun addRewards(rewards: ArrayList<Task>) {
val taskGroup = context.layoutInflater.inflate(R.layout.dialog_challenge_detail_task_group, taskGrouplayout, false) as LinearLayout
val groupName = taskGroup.findViewById<View>(R.id.task_group_name) as TextView
val tasksLayout = taskGroup.findViewById<View>(R.id.tasks_layout) as LinearLayout
val size = rewards.size
groupName.text = rewards.size.toString() + " " + getLabelByTypeAndCount(Challenge.TASK_ORDER_REWARDS, size)
for (i in 0 until size) {
val task = rewards[i]
val entry = context.layoutInflater.inflate(R.layout.dialog_challenge_detail_reward, tasksLayout, false)
(entry.findViewById<View>(R.id.gold_icon) as ImageView).setImageBitmap(HabiticaIconsHelper.imageOfGold())
val title = entry.findViewById<View>(R.id.reward_title) as TextView
title.text = EmojiParser.parseEmojis(task.text)
tasksLayout.addView(entry)
}
taskGrouplayout?.addView(taskGroup)
}
private fun getLabelByTypeAndCount(type: String, count: Int): String {
return when (type) {
Challenge.TASK_ORDER_DAILYS -> context.getString(if (count == 1) R.string.daily else R.string.dailies)
Challenge.TASK_ORDER_HABITS -> context.getString(if (count == 1) R.string.habit else R.string.habits)
Challenge.TASK_ORDER_REWARDS -> context.getString(if (count == 1) R.string.reward else R.string.rewards)
else -> context.getString(if (count == 1) R.string.todo else R.string.todos)
}
}
private fun setJoined(joined: Boolean) {
joinedHeader?.visibility = if (joined) View.VISIBLE else View.GONE
leaveButton?.visibility = if (joined) View.VISIBLE else View.GONE
notJoinedHeader?.visibility = if (joined) View.GONE else View.VISIBLE
joinButton?.visibility = if (joined) View.GONE else View.VISIBLE
}
private fun openLeaderProfile() {
FullProfileActivity.open(context, challenge?.leaderId)
}
private fun openChallengeActivity() {
val bundle = Bundle()
bundle.putString(ChallengeDetailActivity.CHALLENGE_ID, challenge?.id)
val intent = Intent(context, ChallengeDetailActivity::class.java)
intent.putExtras(bundle)
context.startActivity(intent)
this.dialog?.dismiss()
}
private fun joinChallenge() {
this.challengeRepository.joinChallenge(challenge).subscribe(Consumer<Challenge> { this.changeViewsByChallenge(it) }, RxErrorHandler.handleEmptyError())
}
private fun leaveChallenge() {
AlertDialog.Builder(context)
.setTitle(context.getString(R.string.challenge_leave_title))
.setMessage(context.getString(R.string.challenge_leave_text, challenge?.name))
.setPositiveButton(context.getString(R.string.yes)) { dialog, _ ->
showRemoveTasksDialog(object : Action1<String> {
override fun call(t: String) {
challengeRepository.leaveChallenge(challenge, LeaveChallengeBody(t))
.subscribe(Consumer {
challenge.notNull {
challengeLeftAction?.call(it)
}
dialog.dismiss()
}, RxErrorHandler.handleEmptyError())
}
})
}
.setNegativeButton(context.getString(R.string.no)) { dialog, _ -> dialog.dismiss() }.show()
}
// refactor as an UseCase later - see ChallengeDetailActivity
private fun showRemoveTasksDialog(callback: Action1<String>) {
AlertDialog.Builder(context)
.setTitle(context.getString(R.string.challenge_remove_tasks_title))
.setMessage(context.getString(R.string.challenge_remove_tasks_text))
.setPositiveButton(context.getString(R.string.remove_tasks)) { dialog, _ ->
callback.call("remove-all")
dialog.dismiss()
}
.setNegativeButton(context.getString(R.string.keep_tasks)) { dialog, _ ->
callback.call("keep-all")
dialog.dismiss()
}.show()
}
companion object {
fun showDialog(activity: Activity?, challengeRepository: ChallengeRepository, challenge: Challenge, challengeLeftAction: Action1<Challenge>) {
if (activity == null) {
return
}
val dialogLayout = activity.layoutInflater.inflate(R.layout.dialog_challenge_detail, null)
val challengeDetailDialogHolder = ChallengeDetailDialogHolder(dialogLayout, activity)
val builder = AlertDialog.Builder(activity)
.setView(dialogLayout)
challengeDetailDialogHolder.bind(builder.show(), challengeRepository, challenge, challengeLeftAction)
}
}
}

View file

@ -1,140 +0,0 @@
package com.habitrpg.android.habitica.ui.fragments.social.challenges;
import android.app.Activity;
import android.app.AlertDialog;
import android.support.annotation.Nullable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.models.social.Challenge;
import com.habitrpg.android.habitica.models.social.Group;
import com.habitrpg.android.habitica.ui.adapter.social.challenges.ChallengesFilterRecyclerViewAdapter;
import com.habitrpg.android.habitica.utils.Action1;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
class ChallengeFilterDialogHolder {
@BindView(R.id.challenge_filter_recycler_view)
RecyclerView groupRecyclerView;
@BindView(R.id.challenge_filter_button_all)
Button allButton;
@BindView(R.id.challenge_filter_button_none)
Button noneButton;
@BindView(R.id.challenge_filter_owned)
CheckBox checkboxOwned;
@BindView(R.id.challenge_filter_not_owned)
CheckBox checkboxNotOwned;
private AlertDialog dialog;
private List<Challenge> challengesViewed;
private ChallengeFilterOptions currentFilter;
private Action1<ChallengeFilterOptions> selectedGroupsCallback;
private Activity context;
private ChallengesFilterRecyclerViewAdapter adapter;
private ChallengeFilterDialogHolder(View view, Activity context) {
this.context = context;
ButterKnife.bind(this, view);
}
static void showDialog(Activity activity, List<Challenge> challengesViewed,
ChallengeFilterOptions currentFilter,
Action1<ChallengeFilterOptions> selectedGroupsCallback) {
View dialogLayout = activity.getLayoutInflater().inflate(R.layout.dialog_challenge_filter, null);
ChallengeFilterDialogHolder challengeFilterDialogHolder = new ChallengeFilterDialogHolder(dialogLayout, activity);
AlertDialog.Builder builder = new AlertDialog.Builder(activity)
.setTitle(R.string.filter)
.setView(dialogLayout);
challengeFilterDialogHolder.bind(builder, challengesViewed, currentFilter, selectedGroupsCallback);
}
public void bind(AlertDialog.Builder builder, List<Challenge> challengesViewed,
ChallengeFilterOptions currentFilter,
Action1<ChallengeFilterOptions> selectedGroupsCallback) {
builder = builder
.setPositiveButton(context.getString(R.string.done), (dialog1, which) -> doneClicked());
this.dialog = builder.show();
this.challengesViewed = challengesViewed;
this.currentFilter = currentFilter;
this.selectedGroupsCallback = selectedGroupsCallback;
fillChallengeGroups();
if(currentFilter != null ){
checkboxOwned.setChecked(currentFilter.showOwned);
checkboxNotOwned.setChecked(currentFilter.notOwned);
}
}
private void fillChallengeGroups() {
this.groupRecyclerView.setLayoutManager(new LinearLayoutManager(context));
adapter = new ChallengesFilterRecyclerViewAdapter(getGroups(challengesViewed));
if(currentFilter != null && currentFilter.showByGroups != null){
adapter.selectAll(currentFilter.showByGroups);
}
this.groupRecyclerView.setAdapter(adapter);
}
private Collection<Group> getGroups(@Nullable List<Challenge> challenges){
Map<String, Group> groupMap = new HashMap<>();
if (challenges != null) {
for (Challenge challenge : challenges) {
if (groupMap.containsKey(challenge.groupName)) {
continue;
}
Group group = new Group();
group.setId(challenge.groupId);
group.setName(challenge.groupName);
groupMap.put(challenge.groupName, group);
}
}
return groupMap.values();
}
private void doneClicked() {
ChallengeFilterOptions options = new ChallengeFilterOptions();
options.showByGroups = this.adapter.getCheckedEntries();
options.showOwned = checkboxOwned.isChecked();
options.notOwned = checkboxNotOwned.isChecked();
selectedGroupsCallback.call(options);
this.dialog.hide();
}
@OnClick(R.id.challenge_filter_button_all)
void allClicked() {
this.adapter.selectAll();
}
@OnClick(R.id.challenge_filter_button_none)
void noneClicked() {
this.adapter.deSelectAll();
}
}

View file

@ -0,0 +1,122 @@
package com.habitrpg.android.habitica.ui.fragments.social.challenges
import android.app.Activity
import android.app.AlertDialog
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.view.View
import android.widget.Button
import android.widget.CheckBox
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.models.social.Challenge
import com.habitrpg.android.habitica.models.social.Group
import com.habitrpg.android.habitica.ui.adapter.social.challenges.ChallengesFilterRecyclerViewAdapter
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.utils.Action1
import java.util.*
internal class ChallengeFilterDialogHolder private constructor(view: View, private val context: Activity) {
private val groupRecyclerView: RecyclerView? by bindView(view, R.id.challenge_filter_recycler_view)
private val allButton: Button? by bindView(view, R.id.challenge_filter_button_all)
private val noneButton: Button? by bindView(view, R.id.challenge_filter_button_none)
private val checkboxOwned: CheckBox? by bindView(view, R.id.challenge_filter_owned)
private val checkboxNotOwned: CheckBox? by bindView(view, R.id.challenge_filter_not_owned)
private var dialog: AlertDialog? = null
private var challengesViewed: List<Challenge>? = null
private var currentFilter: ChallengeFilterOptions? = null
private var selectedGroupsCallback: Action1<ChallengeFilterOptions>? = null
private var adapter: ChallengesFilterRecyclerViewAdapter? = null
init {
allButton?.setOnClickListener { allClicked() }
noneButton?.setOnClickListener { noneClicked() }
}
fun bind(builder: AlertDialog.Builder, challengesViewed: List<Challenge>,
currentFilter: ChallengeFilterOptions?,
selectedGroupsCallback: Action1<ChallengeFilterOptions>) {
var dialogBuilder = builder
dialogBuilder = builder
.setPositiveButton(context.getString(R.string.done)) { _, _ -> doneClicked() }
this.dialog = dialogBuilder.show()
this.challengesViewed = challengesViewed
this.currentFilter = currentFilter
this.selectedGroupsCallback = selectedGroupsCallback
fillChallengeGroups()
if (currentFilter != null) {
checkboxOwned?.isChecked = currentFilter.showOwned
checkboxNotOwned?.isChecked = currentFilter.notOwned
}
}
private fun fillChallengeGroups() {
this.groupRecyclerView?.layoutManager = LinearLayoutManager(context)
adapter = ChallengesFilterRecyclerViewAdapter(getGroups(challengesViewed))
if (currentFilter != null && currentFilter?.showByGroups != null) {
adapter?.selectAll(currentFilter?.showByGroups)
}
this.groupRecyclerView?.adapter = adapter
}
private fun getGroups(challenges: List<Challenge>?): Collection<Group> {
val groupMap = HashMap<String, Group>()
if (challenges != null) {
for (challenge in challenges) {
if (groupMap.containsKey(challenge.groupName)) {
continue
}
val group = Group()
group.id = challenge.groupId
group.name = challenge.groupName
groupMap[challenge.groupName] = group
}
}
return groupMap.values
}
private fun doneClicked() {
val options = ChallengeFilterOptions()
options.showByGroups = this.adapter?.checkedEntries
options.showOwned = checkboxOwned?.isChecked ?: false
options.notOwned = checkboxNotOwned?.isChecked ?: false
selectedGroupsCallback?.call(options)
this.dialog?.hide()
}
fun allClicked() {
this.adapter?.selectAll()
}
fun noneClicked() {
this.adapter?.deSelectAll()
}
companion object {
fun showDialog(activity: Activity, challengesViewed: List<Challenge>,
currentFilter: ChallengeFilterOptions?,
selectedGroupsCallback: Action1<ChallengeFilterOptions>) {
val dialogLayout = activity.layoutInflater.inflate(R.layout.dialog_challenge_filter, null)
val challengeFilterDialogHolder = ChallengeFilterDialogHolder(dialogLayout, activity)
val builder = AlertDialog.Builder(activity)
.setTitle(R.string.filter)
.setView(dialogLayout)
challengeFilterDialogHolder.bind(builder, challengesViewed, currentFilter, selectedGroupsCallback)
}
}
}

View file

@ -1,191 +0,0 @@
package com.habitrpg.android.habitica.ui.fragments.social.challenges;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.LinearLayoutManager;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.components.AppComponent;
import com.habitrpg.android.habitica.data.ChallengeRepository;
import com.habitrpg.android.habitica.helpers.RxErrorHandler;
import com.habitrpg.android.habitica.models.social.Challenge;
import com.habitrpg.android.habitica.modules.AppModule;
import com.habitrpg.android.habitica.ui.activities.CreateChallengeActivity;
import com.habitrpg.android.habitica.ui.adapter.social.ChallengesListViewAdapter;
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment;
import com.habitrpg.android.habitica.ui.helpers.RecyclerViewEmptySupport;
import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator;
import javax.inject.Inject;
import javax.inject.Named;
import butterknife.BindView;
import butterknife.ButterKnife;
import io.reactivex.Flowable;
import io.realm.RealmResults;
public class ChallengeListFragment extends BaseMainFragment implements SwipeRefreshLayout.OnRefreshListener {
@Inject
ChallengeRepository challengeRepository;
@Inject
@Named(AppModule.NAMED_USER_ID)
String userId;
@BindView(R.id.refreshLayout)
SwipeRefreshLayout swipeRefreshLayout;
@BindView(R.id.recyclerView)
RecyclerViewEmptySupport recyclerView;
@BindView(R.id.emptyView)
public View emptyView;
private ChallengesListViewAdapter challengeAdapter;
private boolean viewUserChallengesOnly;
public void setViewUserChallengesOnly(boolean only) {
this.viewUserChallengesOnly = only;
}
private RealmResults<Challenge> challenges;
private ChallengeFilterOptions filterOptions;
@Override
public void onDestroy() {
challengeRepository.close();
super.onDestroy();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View v = inflater.inflate(R.layout.fragment_challengeslist, container, false);
setUnbinder(ButterKnife.bind(this, v));
challengeAdapter = new ChallengesListViewAdapter(null, true, viewUserChallengesOnly, userId);
swipeRefreshLayout.setOnRefreshListener(this);
recyclerView.setLayoutManager(new LinearLayoutManager(this.activity));
recyclerView.setAdapter(challengeAdapter);
if (!viewUserChallengesOnly) {
this.recyclerView.setBackgroundResource(R.color.white);
}
recyclerView.setEmptyView(emptyView);
recyclerView.setItemAnimator(new SafeDefaultItemAnimator());
loadLocalChallenges();
return v;
}
@Override
public void injectFragment(@NonNull AppComponent component) {
component.inject(this);
}
@Override
public void onRefresh() {
fetchOnlineChallenges();
}
private void setRefreshing(boolean state) {
if (swipeRefreshLayout != null && swipeRefreshLayout.getVisibility() == View.VISIBLE) {
swipeRefreshLayout.setRefreshing(state);
}
}
private void loadLocalChallenges() {
Flowable<RealmResults<Challenge>> observable;
if (viewUserChallengesOnly && user != null) {
observable = challengeRepository.getUserChallenges(user.getId());
} else {
observable = challengeRepository.getChallenges();
}
observable.firstElement().subscribe(challenges -> {
if (challenges.size() == 0) {
fetchOnlineChallenges();
}
this.challenges = challenges;
challengeAdapter.updateUnfilteredData(challenges);
}, RxErrorHandler.handleEmptyError());
}
private void fetchOnlineChallenges() {
setRefreshing(true);
challengeRepository.retrieveChallenges(user).subscribe(challenges -> {}, RxErrorHandler.handleEmptyError(), () -> setRefreshing(false));
}
@Override
public String customTitle() {
if (!isAdded()) {
return "";
}
return getString(R.string.sidebar_challenges);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_list_challenges, menu);
RelativeLayout badgeLayout = (RelativeLayout) MenuItemCompat.getActionView(menu.findItem(R.id.action_search));
if (badgeLayout != null) {
TextView filterCountTextView = badgeLayout.findViewById(R.id.badge_textview);
filterCountTextView.setText(null);
filterCountTextView.setVisibility(View.GONE);
badgeLayout.setOnClickListener(view -> showFilterDialog());
}
}
private void showFilterDialog() {
ChallengeFilterDialogHolder.showDialog(getActivity(),
challenges,
filterOptions, this::changeFilter);
}
private void changeFilter(ChallengeFilterOptions challengeFilterOptions) {
filterOptions = challengeFilterOptions;
if (challengeAdapter != null) {
challengeAdapter.filter(filterOptions);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
switch (id) {
case R.id.action_create_challenge:
Intent intent = new Intent(getActivity(), CreateChallengeActivity.class);
startActivity(intent);
return true;
case R.id.action_reload:
fetchOnlineChallenges();
return true;
case R.id.action_search:
showFilterDialog();
return true;
}
return super.onOptionsItemSelected(item);
}
}

View file

@ -0,0 +1,183 @@
package com.habitrpg.android.habitica.ui.fragments.social.challenges
import android.content.Intent
import android.os.Bundle
import android.support.v4.view.MenuItemCompat
import android.support.v4.widget.SwipeRefreshLayout
import android.support.v7.widget.LinearLayoutManager
import android.view.*
import android.widget.RelativeLayout
import android.widget.TextView
import butterknife.ButterKnife
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.AppComponent
import com.habitrpg.android.habitica.data.ChallengeRepository
import com.habitrpg.android.habitica.extensions.inflate
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.social.Challenge
import com.habitrpg.android.habitica.modules.AppModule
import com.habitrpg.android.habitica.ui.activities.CreateChallengeActivity
import com.habitrpg.android.habitica.ui.adapter.social.ChallengesListViewAdapter
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
import com.habitrpg.android.habitica.ui.helpers.RecyclerViewEmptySupport
import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.ui.helpers.resetViews
import com.habitrpg.android.habitica.utils.Action1
import io.reactivex.Flowable
import io.reactivex.functions.Consumer
import io.realm.RealmResults
import javax.inject.Inject
import javax.inject.Named
class ChallengeListFragment : BaseMainFragment(), SwipeRefreshLayout.OnRefreshListener {
@Inject
lateinit var challengeRepository: ChallengeRepository
@field:[Inject Named(AppModule.NAMED_USER_ID)]
lateinit var userId: String
private val swipeRefreshLayout: SwipeRefreshLayout? by bindView(R.id.refreshLayout)
private val recyclerView: RecyclerViewEmptySupport? by bindView(R.id.recyclerView)
private val emptyView: View? by bindView(R.id.emptyView)
private var challengeAdapter: ChallengesListViewAdapter? = null
private var viewUserChallengesOnly: Boolean = false
private var challenges: RealmResults<Challenge>? = null
private var filterOptions: ChallengeFilterOptions? = null
fun setViewUserChallengesOnly(only: Boolean) {
this.viewUserChallengesOnly = only
}
override fun onDestroy() {
challengeRepository.close()
super.onDestroy()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
super.onCreateView(inflater, container, savedInstanceState)
return container?.inflate(R.layout.fragment_challengeslist)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
resetViews()
challengeAdapter = ChallengesListViewAdapter(null, true, viewUserChallengesOnly, userId)
swipeRefreshLayout?.setOnRefreshListener(this)
recyclerView?.layoutManager = LinearLayoutManager(this.activity)
recyclerView?.adapter = challengeAdapter
if (!viewUserChallengesOnly) {
this.recyclerView?.setBackgroundResource(R.color.white)
}
recyclerView?.setEmptyView(emptyView)
recyclerView?.itemAnimator = SafeDefaultItemAnimator()
loadLocalChallenges()
}
override fun injectFragment(component: AppComponent) {
component.inject(this)
}
override fun onRefresh() {
fetchOnlineChallenges()
}
private fun setRefreshing(state: Boolean) {
if (swipeRefreshLayout != null && swipeRefreshLayout!!.visibility == View.VISIBLE) {
swipeRefreshLayout!!.isRefreshing = state
}
}
private fun loadLocalChallenges() {
val observable: Flowable<RealmResults<Challenge>> = if (viewUserChallengesOnly && user != null) {
challengeRepository.getUserChallenges(user?.id)
} else {
challengeRepository.challenges
}
observable.firstElement().subscribe(Consumer { challenges ->
if (challenges.size == 0) {
fetchOnlineChallenges()
}
this.challenges = challenges
challengeAdapter!!.updateUnfilteredData(challenges)
}, RxErrorHandler.handleEmptyError())
}
private fun fetchOnlineChallenges() {
setRefreshing(true)
challengeRepository.retrieveChallenges(user).doOnComplete {
setRefreshing(false)
} .subscribe(Consumer { }, RxErrorHandler.handleEmptyError())
}
override fun customTitle(): String {
return if (!isAdded) {
""
} else getString(R.string.sidebar_challenges)
}
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
inflater?.inflate(R.menu.menu_list_challenges, menu)
val badgeLayout = MenuItemCompat.getActionView(menu!!.findItem(R.id.action_search)) as RelativeLayout?
if (badgeLayout != null) {
val filterCountTextView = badgeLayout.findViewById<TextView>(R.id.badge_textview)
filterCountTextView.text = null
filterCountTextView.visibility = View.GONE
badgeLayout.setOnClickListener { showFilterDialog() }
}
}
private fun showFilterDialog() {
ChallengeFilterDialogHolder.showDialog(getActivity()!!,
challenges ?: emptyList(),
filterOptions, object : Action1<ChallengeFilterOptions> {
override fun call(t: ChallengeFilterOptions) {
changeFilter(t)
}
})
}
private fun changeFilter(challengeFilterOptions: ChallengeFilterOptions) {
filterOptions = challengeFilterOptions
challengeAdapter?.filter(filterOptions)
}
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
val id = item!!.itemId
when (id) {
R.id.action_create_challenge -> {
val intent = Intent(getActivity(), CreateChallengeActivity::class.java)
startActivity(intent)
return true
}
R.id.action_reload -> {
fetchOnlineChallenges()
return true
}
R.id.action_search -> {
showFilterDialog()
return true
}
}
return super.onOptionsItemSelected(item)
}
}

View file

@ -108,7 +108,7 @@ public class ChallengesOverviewFragment extends BaseMainFragment {
@Subscribe
public void onEvent(ShowChallengeDetailDialogCommand cmd) {
challengeRepository.getChallenge(cmd.challengeId).firstElement().subscribe(challenge -> ChallengeDetailDialogHolder.showDialog(getActivity(), challengeRepository, challenge,
challengeRepository.getChallenge(cmd.challengeId).firstElement().subscribe(challenge -> ChallengeDetailDialogHolder.Companion.showDialog(getActivity(), challengeRepository, challenge,
challenge1 -> {
// challenge left
}), RxErrorHandler.handleEmptyError());

View file

@ -144,7 +144,8 @@ public class PartyDetailFragment extends BaseFragment {
return;
}
titleView.setText(party.getName());
descriptionView.setText(MarkdownParser.parseMarkdown(party.getDescription()));
//TODO: FIX
//descriptionView.setText(MarkdownParser.parseMarkdown(party.getDescription()));
if (quest != null && !quest.getKey().isEmpty()) {
newQuestButton.setVisibility(View.GONE);
@ -216,8 +217,8 @@ public class PartyDetailFragment extends BaseFragment {
@OnClick(R.id.new_quest_button)
public void inviteNewQuest() {
ItemRecyclerFragment fragment = new ItemRecyclerFragment();
fragment.itemType = "quests";
fragment.itemTypeText = getString(R.string.quest);
fragment.setItemType("quests");
fragment.setItemTypeText(getString(R.string.quest));
fragment.show(getFragmentManager(), "questDialog");
}

View file

@ -1,87 +0,0 @@
package com.habitrpg.android.habitica.ui.fragments.social.party;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.text.InputType;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.components.AppComponent;
import com.habitrpg.android.habitica.prefs.scanner.IntentIntegrator;
import com.habitrpg.android.habitica.ui.fragments.BaseFragment;
import java.util.ArrayList;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class PartyInviteFragment extends BaseFragment {
public boolean isEmailInvite;
@BindView(R.id.inviteDescription)
TextView inviteDescription;
@BindView(R.id.invitationWrapper)
LinearLayout invitationWrapper;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View v = inflater.inflate(R.layout.fragment_party_invite, container, false);
setUnbinder(ButterKnife.bind(this, v));
if (isEmailInvite) {
inviteDescription.setText(getString(R.string.invite_email_description));
} else {
inviteDescription.setText(getString(R.string.invite_id_description));
}
addInviteField();
return v;
}
@Override
public void injectFragment(AppComponent component) {
component.inject(this);
}
@OnClick(R.id.addInviteButton)
public void addInviteField() {
EditText editText = new EditText(getContext());
if (isEmailInvite) {
editText.setHint(R.string.email);
editText.setInputType(InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
} else {
editText.setHint(R.string.user_id);
}
invitationWrapper.addView(editText);
}
@OnClick(R.id.InviteByQR)
public void startQRInvite() {
IntentIntegrator scanIntegrator = new IntentIntegrator(getActivity());
scanIntegrator.initiateScan();
}
public String[] getValues() {
List<String> values = new ArrayList<>();
for (int i = 0; i < invitationWrapper.getChildCount(); i++) {
EditText valueEditText = (EditText) invitationWrapper.getChildAt(i);
if (valueEditText.getText().toString().length() > 0) {
values.add(valueEditText.getText().toString());
}
}
return values.toArray(new String[values.size()]);
}
}

View file

@ -0,0 +1,83 @@
package com.habitrpg.android.habitica.ui.fragments.social.party
import android.os.Bundle
import android.text.InputType
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.EditText
import android.widget.LinearLayout
import android.widget.TextView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.AppComponent
import com.habitrpg.android.habitica.prefs.scanner.IntentIntegrator
import com.habitrpg.android.habitica.ui.fragments.BaseFragment
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.ui.helpers.resetViews
import java.util.*
class PartyInviteFragment : BaseFragment() {
var isEmailInvite: Boolean = false
private val inviteDescription: TextView? by bindView(R.id.inviteDescription)
private val invitationWrapper: LinearLayout? by bindView(R.id.invitationWrapper)
private val addInviteButton: Button? by bindView(R.id.addInviteButton)
private val inviteQRButton: Button? by bindView(R.id.InviteByQR)
val values: Array<String>
get() {
val values = ArrayList<String>()
for (i in 0 until (invitationWrapper?.childCount ?: 0)) {
val valueEditText = invitationWrapper?.getChildAt(i) as EditText
if (valueEditText.text.toString().isNotEmpty()) {
values.add(valueEditText.text.toString())
}
}
return values.toTypedArray()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
super.onCreateView(inflater, container, savedInstanceState)
return inflater.inflate(R.layout.fragment_party_invite, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
resetViews()
if (isEmailInvite) {
inviteDescription?.text = getString(R.string.invite_email_description)
} else {
inviteDescription?.text = getString(R.string.invite_id_description)
}
addInviteField()
addInviteButton?.setOnClickListener { addInviteField() }
inviteQRButton?.setOnClickListener { startQRInvite() }
}
override fun injectFragment(component: AppComponent) {
component.inject(this)
}
fun addInviteField() {
val editText = EditText(context)
if (isEmailInvite) {
editText.setHint(R.string.email)
editText.inputType = InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
} else {
editText.setHint(R.string.user_id)
}
invitationWrapper?.addView(editText)
}
fun startQRInvite() {
val scanIntegrator = IntentIntegrator(activity)
scanIntegrator.initiateScan()
}
}

View file

@ -15,6 +15,7 @@ import com.habitrpg.android.habitica.components.AppComponent
import com.habitrpg.android.habitica.data.TagRepository
import com.habitrpg.android.habitica.events.TaskTappedEvent
import com.habitrpg.android.habitica.events.commands.AddNewTaskCommand
import com.habitrpg.android.habitica.extensions.notNull
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.helpers.TaskFilterHelper
import com.habitrpg.android.habitica.models.tasks.Task
@ -133,33 +134,37 @@ class TasksFragment : BaseMainFragment() {
}
private fun showFilterDialog() {
val dialog = TaskFilterDialog(context, HabiticaBaseApplication.getComponent())
if (user != null) {
dialog.setTags(user?.tags?.createSnapshot())
}
dialog.setActiveTags(taskFilterHelper.tags)
if (activeFragment != null) {
val taskType = activeFragment?.classType
if (taskType != null) {
dialog.setTaskType(taskType, taskFilterHelper.getActiveFilter(taskType))
context.notNull {
val dialog = TaskFilterDialog(it, HabiticaBaseApplication.getComponent())
if (user != null) {
dialog.setTags(user?.tags?.createSnapshot() ?: emptyList())
}
}
dialog.setListener { activeTaskFilter, activeTags ->
if (viewFragmentsDictionary == null) {
return@setListener
dialog.setActiveTags(taskFilterHelper.tags)
if (activeFragment != null) {
val taskType = activeFragment?.classType
if (taskType != null) {
dialog.setTaskType(taskType, taskFilterHelper.getActiveFilter(taskType))
}
}
val activePos = viewPager?.currentItem ?: 0
viewFragmentsDictionary?.get(activePos - 1)?.recyclerAdapter?.filter()
viewFragmentsDictionary?.get(activePos + 1)?.recyclerAdapter?.filter()
taskFilterHelper.tags = activeTags
if (activeTaskFilter != null) {
activeFragment?.setActiveFilter(activeTaskFilter)
}
viewFragmentsDictionary?.values?.forEach { it.recyclerAdapter?.filter() }
updateFilterIcon()
dialog.setListener(object : TaskFilterDialog.OnFilterCompletedListener {
override fun onFilterCompleted(activeTaskFilter: String?, activeTags: List<String>?) {
if (viewFragmentsDictionary == null) {
return
}
val activePos = viewPager?.currentItem ?: 0
viewFragmentsDictionary?.get(activePos - 1)?.recyclerAdapter?.filter()
viewFragmentsDictionary?.get(activePos + 1)?.recyclerAdapter?.filter()
taskFilterHelper.tags = activeTags
if (activeTaskFilter != null) {
activeFragment?.setActiveFilter(activeTaskFilter)
}
viewFragmentsDictionary?.values?.forEach { it.recyclerAdapter?.filter() }
updateFilterIcon()
}
})
dialog.show()
}
dialog.show()
}
private fun refresh() {

View file

@ -0,0 +1,133 @@
package com.habitrpg.android.habitica.ui.helpers
import android.app.Activity
import android.app.Dialog
import android.content.Context
import android.support.annotation.ColorRes
import android.support.annotation.IdRes
import android.support.v4.app.Fragment
import android.support.v4.content.ContextCompat
import android.support.v7.widget.RecyclerView.ViewHolder
import android.view.View
import java.util.*
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
object ButterKnife {
fun reset(target: Any) {
LazyRegistry.reset(target)
}
}
fun <V : View> View.bindView(@IdRes id: Int)
: ReadOnlyProperty<View, V> = required(id, viewFinder)
fun <V : View> Activity.bindView(@IdRes id: Int)
: ReadOnlyProperty<Activity, V> = required(id, viewFinder)
fun <V : View> Dialog.bindView(@IdRes id: Int)
: ReadOnlyProperty<Dialog, V> = required(id, viewFinder)
fun <V : View> Fragment.bindView(@IdRes id: Int)
: ReadOnlyProperty<Fragment, V> = required(id, viewFinder)
fun <V : View> ViewHolder.bindView(@IdRes id: Int)
: ReadOnlyProperty<ViewHolder, V> = required(id, viewFinder)
fun <V : View> View.bindOptionalView(@IdRes id: Int)
: ReadOnlyProperty<View, V?> = optional(id, viewFinder)
fun <V : View> Activity.bindOptionalView(@IdRes id: Int)
: ReadOnlyProperty<Activity, V?> = optional(id, viewFinder)
fun <V : View> Dialog.bindOptionalView(@IdRes id: Int)
: ReadOnlyProperty<Dialog, V?> = optional(id, viewFinder)
fun <V : View> Fragment.bindOptionalView(@IdRes id: Int)
: ReadOnlyProperty<Fragment, V?> = optional(id, viewFinder)
fun <V : View> ViewHolder.bindOptionalView(@IdRes id: Int)
: ReadOnlyProperty<ViewHolder, V?> = optional(id, viewFinder)
fun <V : View> bindView(container: View, @IdRes res: Int) : Lazy<V> {
@Suppress("UNCHECKED_CAST")
return lazy(LazyThreadSafetyMode.NONE) { container.findViewById<V>(res) }
}
fun <V : View> bindOptionalView(container: View?, @IdRes res: Int) : Lazy<V?> {
@Suppress("UNCHECKED_CAST")
return lazy(LazyThreadSafetyMode.NONE) { container?.findViewById<V>(res) }
}
fun bindColor(context: Context, @ColorRes res: Int) : Lazy<Int> {
@Suppress("UNCHECKED_CAST")
return lazy(LazyThreadSafetyMode.NONE) { ContextCompat.getColor(context, res) }
}
fun Fragment.resetViews() {
LazyRegistry.reset(this)
}
private val View.viewFinder: View.(Int) -> View?
get() = { findViewById(it) }
private val Activity.viewFinder: Activity.(Int) -> View?
get() = { findViewById(it) }
private val Dialog.viewFinder: Dialog.(Int) -> View?
get() = { findViewById(it) }
private val Fragment.viewFinder: Fragment.(Int) -> View?
get() = { view?.findViewById(it) }
private val ViewHolder.viewFinder: ViewHolder.(Int) -> View?
get() = { itemView.findViewById(it) }
private fun viewNotFound(id: Int, desc: KProperty<*>): Nothing =
throw IllegalStateException("View ID $id for '${desc.name}' not found.")
@Suppress("UNCHECKED_CAST")
private fun <T, V : View> required(id: Int, finder: T.(Int) -> View?)
= TargetedLazy { t: T, desc -> t.finder(id) as V? ?: viewNotFound(id, desc) }
@Suppress("UNCHECKED_CAST")
private fun <T, V : View> optional(id: Int, finder: T.(Int) -> View?)
= TargetedLazy { t: T, desc -> t.finder(id) as V? }
@Suppress("UNCHECKED_CAST")
private fun <T, V : View> required(ids: IntArray, finder: T.(Int) -> View?)
= TargetedLazy { t: T, desc -> ids.map { t.finder(it) as V? ?: viewNotFound(it, desc) } }
@Suppress("UNCHECKED_CAST")
private fun <T, V : View> optional(ids: IntArray, finder: T.(Int) -> View?)
= TargetedLazy { t: T, desc -> ids.map { t.finder(it) as V? }.filterNotNull() }
// Like Kotlin's lazy delegate but the initializer gets the target and metadata passed to it
private class TargetedLazy<T, V>(private val initializer: (T, KProperty<*>) -> V) : ReadOnlyProperty<T, V> {
private object EMPTY
private var value: Any? = EMPTY
override fun getValue(thisRef: T, property: KProperty<*>): V {
LazyRegistry.register(thisRef!!, this)
if (value == EMPTY) {
value = initializer(thisRef, property)
}
@Suppress("UNCHECKED_CAST")
return value as V
}
fun reset() {
value = EMPTY
}
}
private object LazyRegistry {
private val lazyMap = WeakHashMap<Any, MutableCollection<TargetedLazy<*, *>>>()
fun register(target: Any, lazy: TargetedLazy<*, *>) {
lazyMap.getOrPut(target) { Collections.newSetFromMap(WeakHashMap()) }.add(lazy)
}
fun reset(target: Any) {
lazyMap[target]?.forEach { it.reset() }
}
}

View file

@ -1,50 +0,0 @@
package com.habitrpg.android.habitica.ui.helpers;
import android.support.annotation.Nullable;
import android.text.Html;
import com.commonsware.cwac.anddown.AndDown;
import net.pherth.android.emoji_library.EmojiParser;
import static android.text.Html.FROM_HTML_MODE_LEGACY;
/**
* @author data5tream
*/
public class MarkdownParser {
private 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) {
if (input == null) {
return "";
}
CharSequence output;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
output = Html.fromHtml(processor.markdownToHtml(EmojiParser.parseEmojis(input.trim())), FROM_HTML_MODE_LEGACY);
} else {
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
*/
@Nullable
public static String parseCompiled(CharSequence input) {
return EmojiParser.convertToCheatCode(input.toString());
}
}

View file

@ -0,0 +1,47 @@
package com.habitrpg.android.habitica.ui.helpers
import android.text.Html
import com.commonsware.cwac.anddown.AndDown
import net.pherth.android.emoji_library.EmojiParser
import android.text.Html.FROM_HTML_MODE_LEGACY
/**
* @author data5tream
*/
object MarkdownParser {
private val processor = AndDown()
/**
* Parses formatted markdown and returns it as styled CharSequence
*
* @param input Markdown formatted String
* @return Stylized CharSequence
*/
fun parseMarkdown(input: String?): CharSequence {
if (input == null) {
return ""
}
var output: CharSequence = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
Html.fromHtml(processor.markdownToHtml(EmojiParser.parseEmojis(input.trim { it <= ' ' })), FROM_HTML_MODE_LEGACY)
} else {
Html.fromHtml(processor.markdownToHtml(EmojiParser.parseEmojis(input.trim { it <= ' ' })))
}
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
*/
fun parseCompiled(input: CharSequence): String? {
return EmojiParser.convertToCheatCode(input.toString())
}
}

View file

@ -9,7 +9,7 @@ import android.widget.Button
import android.widget.Spinner
import android.widget.TextView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.bindView
import com.habitrpg.android.habitica.ui.helpers.bindView
class SectionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

View file

@ -9,7 +9,7 @@ import android.widget.TextView
import com.facebook.drawee.view.SimpleDraweeView
import com.habitrpg.android.habitica.HabiticaBaseApplication
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.bindView
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.models.shops.ShopItem
import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils
import com.habitrpg.android.habitica.ui.views.CurrencyView

View file

@ -8,13 +8,13 @@ import android.widget.LinearLayout
import android.widget.TextView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.events.TaskTappedEvent
import com.habitrpg.android.habitica.extensions.bindColor
import com.habitrpg.android.habitica.extensions.bindOptionalView
import com.habitrpg.android.habitica.extensions.bindView
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.extensions.notNull
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.tasks.Task
import com.habitrpg.android.habitica.ui.helpers.MarkdownParser
import com.habitrpg.android.habitica.ui.helpers.bindColor
import com.habitrpg.android.habitica.ui.helpers.bindOptionalView
import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.functions.Consumer

View file

@ -27,7 +27,7 @@ import org.greenrobot.eventbus.EventBus
import butterknife.BindView
import butterknife.OnClick
import com.habitrpg.android.habitica.extensions.bindView
import com.habitrpg.android.habitica.ui.helpers.bindView
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.functions.Consumer

View file

@ -3,7 +3,7 @@ package com.habitrpg.android.habitica.ui.viewHolders.tasks
import android.view.View
import android.widget.TextView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.bindView
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.models.tasks.Task
class DailyViewHolder(itemView: View) : ChecklistedViewHolder(itemView) {

View file

@ -8,7 +8,7 @@ import android.widget.TextView
import butterknife.OnClick
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.events.HabitScoreEvent
import com.habitrpg.android.habitica.extensions.bindView
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.models.tasks.Task
import org.greenrobot.eventbus.EventBus

View file

@ -8,7 +8,7 @@ import butterknife.OnClick
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.events.TaskTappedEvent
import com.habitrpg.android.habitica.events.commands.BuyRewardCommand
import com.habitrpg.android.habitica.extensions.bindView
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.helpers.NumberAbbreviator
import com.habitrpg.android.habitica.models.tasks.Task
import com.habitrpg.android.habitica.ui.ItemDetailDialog

View file

@ -10,8 +10,8 @@ import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.bindView
import com.habitrpg.android.habitica.extensions.notNull
import com.habitrpg.android.habitica.ui.helpers.bindView
class CollapsibleSectionView(context: Context?, attrs: AttributeSet?) : LinearLayout(context, attrs) {

View file

@ -8,15 +8,15 @@ import android.view.View
import android.widget.LinearLayout
import android.widget.TextView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.bindView
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.extensions.layoutInflater
open class HabiticaAlertDialog(context: Context) : AlertDialog(context) {
private val view: LinearLayout = LayoutInflater.from(context).inflate(R.layout.dialog_habitica_base, null) as LinearLayout
private val titleTextView: TextView by bindView(view, R.id.titleTextView)
private val subtitleTextView: TextView by bindView(view, R.id.subtitleTextView)
private val messageTextView: TextView by bindView(view, R.id.messageTextView)
private val titleTextView: TextView by bindView(R.id.titleTextView)
private val subtitleTextView: TextView by bindView(R.id.subtitleTextView)
private val messageTextView: TextView by bindView(R.id.messageTextView)
private var additionalContentView: View? = null

View file

@ -6,7 +6,7 @@ import android.view.View
import android.widget.FrameLayout
import android.widget.LinearLayout
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.bindView
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils

View file

@ -10,7 +10,7 @@ import android.widget.ImageView
import com.facebook.drawee.view.SimpleDraweeView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.backgroundCompat
import com.habitrpg.android.habitica.extensions.bindView
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.extensions.layoutInflater
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils

View file

@ -11,7 +11,7 @@ import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.TextView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.bindView
import com.habitrpg.android.habitica.ui.helpers.bindView
import java.text.NumberFormat
class ValueBar(context: Context?, attrs: AttributeSet?) : FrameLayout(context, attrs) {

View file

@ -1,39 +0,0 @@
package com.habitrpg.android.habitica.ui.views.insufficientCurrency;
import android.content.Context;
import android.support.v7.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import com.habitrpg.android.habitica.R;
import butterknife.BindView;
import butterknife.ButterKnife;
/**
* Created by phillip on 27.09.17.
*/
abstract public class InsufficientCurrencyDialog extends AlertDialog {
@BindView(R.id.imageView)
ImageView imageView;
@BindView(R.id.textView)
TextView textView;
public InsufficientCurrencyDialog(Context context) {
super(context);
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.dialog_insufficient_currency, null);
ButterKnife.bind(this, view);
setView(view);
this.setButton(AlertDialog.BUTTON_NEUTRAL, context.getString(R.string.close), (dialogInterface, i) -> {
this.dismiss();
});
}
}

View file

@ -0,0 +1,29 @@
package com.habitrpg.android.habitica.ui.views.insufficientCurrency
import android.content.Context
import android.support.v7.app.AlertDialog
import android.view.LayoutInflater
import android.widget.ImageView
import android.widget.TextView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.ui.helpers.bindView
/**
* Created by phillip on 27.09.17.
*/
abstract class InsufficientCurrencyDialog(context: Context) : AlertDialog(context) {
protected val imageView: ImageView by bindView(R.id.imageView)
protected val textView: TextView by bindView(R.id.textView)
init {
val inflater = LayoutInflater.from(context)
val view = inflater.inflate(R.layout.dialog_insufficient_currency, null)
setView(view)
this.setButton(AlertDialog.BUTTON_NEUTRAL, context.getString(R.string.close)) { _, _ -> this.dismiss() }
}
}

View file

@ -1,27 +0,0 @@
package com.habitrpg.android.habitica.ui.views.insufficientCurrency;
import android.app.AlertDialog;
import android.content.Context;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.events.commands.OpenGemPurchaseFragmentCommand;
import org.greenrobot.eventbus.EventBus;
/**
* Created by phillip on 27.09.17.
*/
public class InsufficientGemsDialog extends InsufficientCurrencyDialog {
public InsufficientGemsDialog(Context context) {
super(context);
imageView.setImageResource(R.drawable.gems_84);
textView.setText(R.string.insufficientGems);
setButton(AlertDialog.BUTTON_POSITIVE, context.getString(R.string.purchase_gems), (dialogInterface, i) -> {
EventBus.getDefault().post(new OpenGemPurchaseFragmentCommand());
});
}
}

View file

@ -0,0 +1,24 @@
package com.habitrpg.android.habitica.ui.views.insufficientCurrency
import android.app.AlertDialog
import android.content.Context
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.events.commands.OpenGemPurchaseFragmentCommand
import org.greenrobot.eventbus.EventBus
/**
* Created by phillip on 27.09.17.
*/
class InsufficientGemsDialog(context: Context) : InsufficientCurrencyDialog(context) {
init {
imageView.setImageResource(R.drawable.gems_84)
textView.setText(R.string.insufficientGems)
setButton(AlertDialog.BUTTON_POSITIVE, context.getString(R.string.purchase_gems)) { _, _ -> EventBus.getDefault().post(OpenGemPurchaseFragmentCommand()) }
}
}

View file

@ -1,16 +0,0 @@
package com.habitrpg.android.habitica.ui.views.insufficientCurrency;
import android.content.Context;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper;
public class InsufficientGoldDialog extends InsufficientCurrencyDialog {
public InsufficientGoldDialog(Context context) {
super(context);
imageView.setImageBitmap(HabiticaIconsHelper.imageOfGoldReward());
textView.setText(context.getString(R.string.insufficientGold));
setTitle(R.string.insufficientSubscriberGemsTitle);
}
}

View file

@ -0,0 +1,15 @@
package com.habitrpg.android.habitica.ui.views.insufficientCurrency
import android.content.Context
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
class InsufficientGoldDialog(context: Context) : InsufficientCurrencyDialog(context) {
init {
imageView.setImageBitmap(HabiticaIconsHelper.imageOfGoldReward())
textView.text = context.getString(R.string.insufficientGold)
setTitle(R.string.insufficientSubscriberGemsTitle)
}
}

View file

@ -1,24 +0,0 @@
package com.habitrpg.android.habitica.ui.views.insufficientCurrency;
import android.content.Context;
import android.support.v7.app.AlertDialog;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.events.commands.OpenGemPurchaseFragmentCommand;
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper;
import org.greenrobot.eventbus.EventBus;
public class InsufficientHourglassesDialog extends InsufficientCurrencyDialog {
public InsufficientHourglassesDialog(Context context) {
super(context);
imageView.setImageBitmap(HabiticaIconsHelper.imageOfHourglassShop());
textView.setText(R.string.insufficientHourglasses);
setButton(AlertDialog.BUTTON_POSITIVE, context.getString(R.string.get_hourglasses), (dialogInterface, i) -> {
EventBus.getDefault().post(new OpenGemPurchaseFragmentCommand());
});
}
}

View file

@ -0,0 +1,20 @@
package com.habitrpg.android.habitica.ui.views.insufficientCurrency
import android.content.Context
import android.support.v7.app.AlertDialog
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.events.commands.OpenGemPurchaseFragmentCommand
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
import org.greenrobot.eventbus.EventBus
class InsufficientHourglassesDialog(context: Context) : InsufficientCurrencyDialog(context) {
init {
imageView.setImageBitmap(HabiticaIconsHelper.imageOfHourglassShop())
textView.setText(R.string.insufficientHourglasses)
setButton(AlertDialog.BUTTON_POSITIVE, context.getString(R.string.get_hourglasses)) { _, _ -> EventBus.getDefault().post(OpenGemPurchaseFragmentCommand()) }
}
}

View file

@ -1,151 +0,0 @@
package com.habitrpg.android.habitica.ui.views.login;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import com.habitrpg.android.habitica.R;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import butterknife.BindView;
import butterknife.ButterKnife;
public class LoginBackgroundView extends RelativeLayout {
private static float SIZE_FACTOR = 1.5f;
private static int STAR_SIZE = 30;
private static int STAR_OFFSET = 18;
private final Random random;
@BindView(R.id.left_cloud_view)
ImageView leftCloudView;
@BindView(R.id.right_cloud_view)
ImageView rightCloudView;
private List<StarView> starViews;
private int width;
private int height;
private boolean didLayoutStars = false;
private FrameLayout.LayoutParams params;
public LoginBackgroundView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
this.random = new Random();
DisplayMetrics metrics = new DisplayMetrics();
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
windowManager.getDefaultDisplay().getMetrics(metrics);
height = (int) (metrics.heightPixels*SIZE_FACTOR);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
ButterKnife.bind(this, this);
starViews = new ArrayList<>();
params = new FrameLayout.LayoutParams(0, 0);
generateStars();
animateClouds();
}
private void animateClouds() {
ValueAnimator leftAnimator = ObjectAnimator.ofFloat(leftCloudView, View.TRANSLATION_Y, 10.0f).setDuration(5000);
leftAnimator.setRepeatCount(ValueAnimator.INFINITE);
leftAnimator.setRepeatMode(ValueAnimator.REVERSE);
leftAnimator.start();
ValueAnimator rightAnimator = ObjectAnimator.ofFloat(rightCloudView, View.TRANSLATION_Y, -10.0f).setDuration(8000);
rightAnimator.setRepeatCount(ValueAnimator.INFINITE);
rightAnimator.setRepeatMode(ValueAnimator.REVERSE);
rightAnimator.start();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
this.width = MeasureSpec.getSize(widthMeasureSpec);
this.setMeasuredDimension(width, height);
params.width = width;
params.height = height;
this.setLayoutParams(params);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
updateStarLayoutParams();
}
public void generateStars() {
generateStars(1, 12, 25);
}
private void generateStars(int largeCount, int mediumCount, int smallCount) {
removeStarViews();
for (int x = 0; x < largeCount; x++) {
generateStar(2);
}
for (int x = 0; x < mediumCount; x++) {
generateStar(1);
}
for (int x = 0; x < smallCount; x++) {
generateStar(0);
}
requestLayout();
}
private void removeStarViews() {
if (starViews.size() > 0) {
starViews.forEach(this::removeView);
starViews.clear();
}
}
private void generateStar(int size) {
StarView starView = new StarView(getContext()
);
starView.setStarSize(size);
if (random.nextInt(10) > 2) {
starView.setBlinkDurations(Arrays.asList(getBlinkDuration(), getBlinkDuration(), getBlinkDuration()));
}
starViews.add(starView);
if (width > 0 && height > 0) {
this.addView(starView, 0, getStarParams());
} else {
this.addView(starView, 0);
}
}
private int getBlinkDuration() {
return random.nextInt(30)*800+4;
}
private void updateStarLayoutParams() {
if (width <= 0 || height <= 0 || didLayoutStars || starViews.size() == 0) {
return;
}
for (StarView view : starViews) {
view.setLayoutParams(getStarParams());
}
didLayoutStars = true;
}
private RelativeLayout.LayoutParams getStarParams() {
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(STAR_SIZE, STAR_SIZE);
params.leftMargin = random.nextInt(width);
params.topMargin = random.nextInt(height);
return params;
}
}

View file

@ -0,0 +1,134 @@
package com.habitrpg.android.habitica.ui.views.login
import android.animation.ObjectAnimator
import android.animation.ValueAnimator
import android.content.Context
import android.util.AttributeSet
import android.util.DisplayMetrics
import android.view.View
import android.view.WindowManager
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.RelativeLayout
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.ui.helpers.bindView
import java.util.*
class LoginBackgroundView(context: Context, attrs: AttributeSet?) : RelativeLayout(context, attrs) {
private val random: Random = Random()
private val leftCloudView: ImageView by bindView(R.id.left_cloud_view)
private val rightCloudView: ImageView by bindView(R.id.right_cloud_view)
private var starViews: MutableList<StarView>? = null
private var viewWidth: Int = 0
private val viewHeight: Int
private var didLayoutStars = false
private var params = FrameLayout.LayoutParams(0, 0)
private val blinkDuration: Int
get() = random.nextInt(30) * 800 + 4
private val starParams: RelativeLayout.LayoutParams
get() {
val params = RelativeLayout.LayoutParams(STAR_SIZE, STAR_SIZE)
params.leftMargin = random.nextInt(viewWidth)
params.topMargin = random.nextInt(viewHeight)
return params
}
init {
val metrics = DisplayMetrics()
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
windowManager.defaultDisplay.getMetrics(metrics)
viewHeight = (metrics.heightPixels * SIZE_FACTOR).toInt()
}
override fun onFinishInflate() {
super.onFinishInflate()
starViews = ArrayList()
generateStars()
animateClouds()
}
private fun animateClouds() {
val leftAnimator = ObjectAnimator.ofFloat(leftCloudView, View.TRANSLATION_Y, 10.0f).setDuration(5000)
leftAnimator.repeatCount = ValueAnimator.INFINITE
leftAnimator.repeatMode = ValueAnimator.REVERSE
leftAnimator.start()
val rightAnimator = ObjectAnimator.ofFloat(rightCloudView, View.TRANSLATION_Y, -10.0f).setDuration(8000)
rightAnimator.repeatCount = ValueAnimator.INFINITE
rightAnimator.repeatMode = ValueAnimator.REVERSE
rightAnimator.start()
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
this.viewWidth = View.MeasureSpec.getSize(widthMeasureSpec)
this.setMeasuredDimension(viewWidth, viewHeight)
params.width = viewWidth
params.height = viewHeight
this.layoutParams = params
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
}
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
super.onLayout(changed, l, t, r, b)
updateStarLayoutParams()
}
private fun generateStars() {
generateStars(1, 12, 25)
}
private fun generateStars(largeCount: Int, mediumCount: Int, smallCount: Int) {
removeStarViews()
for (x in 0 until largeCount) {
generateStar(2)
}
for (x in 0 until mediumCount) {
generateStar(1)
}
for (x in 0 until smallCount) {
generateStar(0)
}
requestLayout()
}
private fun removeStarViews() {
if (starViews?.size ?: 0 > 0) {
starViews?.forEach { this.removeView(it) }
starViews?.clear()
}
}
private fun generateStar(size: Int) {
val starView = StarView(context)
starView.setStarSize(size)
if (random.nextInt(10) > 2) {
starView.setBlinkDurations(Arrays.asList(blinkDuration, blinkDuration, blinkDuration))
}
starViews?.add(starView)
if (viewWidth > 0 && viewHeight > 0) {
this.addView(starView, 0, starParams)
} else {
this.addView(starView, 0)
}
}
private fun updateStarLayoutParams() {
if (viewWidth <= 0 ||viewHeight <= 0 || didLayoutStars || starViews?.size == 0) {
return
}
for (view in starViews ?: emptyList<StarView>()) {
view.layoutParams = starParams
}
didLayoutStars = true
}
companion object {
private const val SIZE_FACTOR = 1.5f
private const val STAR_SIZE = 30
}
}

View file

@ -8,7 +8,6 @@ import android.support.v4.content.ContextCompat
import android.util.AttributeSet
import android.view.View
import android.widget.FrameLayout
import butterknife.ButterKnife
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.backgroundCompat
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper

View file

@ -1,58 +0,0 @@
package com.habitrpg.android.habitica.ui.views.setup;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.habitrpg.android.habitica.R;
import butterknife.BindView;
import butterknife.ButterKnife;
public class AvatarCategoryView extends LinearLayout {
private final Drawable icon;
@BindView(R.id.text_view)
TextView textView;
public AvatarCategoryView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
inflate(context, R.layout.avatar_category, this);
ButterKnife.bind(this);
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.AvatarCategoryView,
0, 0);
textView.setText(a.getText(R.styleable.AvatarCategoryView_categoryTitle));
icon = a.getDrawable(R.styleable.AvatarCategoryView_iconDrawable);
if (icon != null) {
textView.setCompoundDrawablesWithIntrinsicBounds(null, icon, null, null);
}
setActive(false);
}
public void setActive(boolean active) {
int color;
if (active) {
color = ContextCompat.getColor(getContext(), R.color.white);
} else {
color = ContextCompat.getColor(getContext(), R.color.white_50_alpha);
}
textView.setTextColor(color);
if (icon != null) {
icon.setColorFilter(color, PorterDuff.Mode.MULTIPLY);
textView.setCompoundDrawablesWithIntrinsicBounds(null, icon, null, null);
}
}
}

View file

@ -0,0 +1,49 @@
package com.habitrpg.android.habitica.ui.views.setup
import android.content.Context
import android.graphics.PorterDuff
import android.graphics.drawable.Drawable
import android.support.v4.content.ContextCompat
import android.util.AttributeSet
import android.view.View
import android.widget.LinearLayout
import android.widget.TextView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.ui.helpers.bindView
class AvatarCategoryView(context: Context, attrs: AttributeSet?) : LinearLayout(context, attrs) {
private val icon: Drawable?
private val textView: TextView by bindView(R.id.text_view)
init {
View.inflate(context, R.layout.avatar_category, this)
val a = context.theme.obtainStyledAttributes(
attrs,
R.styleable.AvatarCategoryView,
0, 0)
textView.text = a.getText(R.styleable.AvatarCategoryView_categoryTitle)
icon = a.getDrawable(R.styleable.AvatarCategoryView_iconDrawable)
if (icon != null) {
textView.setCompoundDrawablesWithIntrinsicBounds(null, icon, null, null)
}
setActive(false)
}
fun setActive(active: Boolean) {
val color: Int = if (active) {
ContextCompat.getColor(context, R.color.white)
} else {
ContextCompat.getColor(context, R.color.white_50_alpha)
}
textView.setTextColor(color)
if (icon != null) {
icon.setColorFilter(color, PorterDuff.Mode.MULTIPLY)
textView.setCompoundDrawablesWithIntrinsicBounds(null, icon, null, null)
}
}
}

View file

@ -17,7 +17,7 @@ import com.habitrpg.android.habitica.data.UserRepository
import com.habitrpg.android.habitica.events.GearPurchasedEvent
import com.habitrpg.android.habitica.events.ShowSnackbarEvent
import com.habitrpg.android.habitica.events.commands.OpenGemPurchaseFragmentCommand
import com.habitrpg.android.habitica.extensions.bindView
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.helpers.RemoteConfigManager
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.inventory.Equipment

View file

@ -7,7 +7,7 @@ import android.widget.LinearLayout
import android.widget.TextView
import com.facebook.drawee.view.SimpleDraweeView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.bindView
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.models.shops.ShopItem
import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils

View file

@ -5,7 +5,7 @@ import android.support.v4.content.ContextCompat
import android.util.AttributeSet
import android.widget.TextView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.bindView
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.models.inventory.Equipment
import com.habitrpg.android.habitica.models.shops.ShopItem

View file

@ -3,7 +3,7 @@ package com.habitrpg.android.habitica.ui.views.shops
import android.content.Context
import android.widget.TextView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.bindView
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.models.shops.ShopItem
internal class PurchaseDialogGemsContent(context: Context) : PurchaseDialogContent(context) {

View file

@ -4,7 +4,7 @@ import android.content.Context
import android.util.AttributeSet
import android.widget.TextView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.bindView
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.models.shops.ShopItem
class PurchaseDialogItemContent : PurchaseDialogContent {

View file

@ -11,7 +11,7 @@ import android.widget.RatingBar
import android.widget.TextView
import com.facebook.drawee.view.SimpleDraweeView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.bindView
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.models.inventory.QuestContent
import com.habitrpg.android.habitica.models.inventory.QuestDropItem
import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils

View file

@ -8,13 +8,11 @@ import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import butterknife.ButterKnife
import com.facebook.drawee.view.SimpleDraweeView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.bindView
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.extensions.setScaledPadding
import com.habitrpg.android.habitica.models.inventory.QuestContent
import com.habitrpg.android.habitica.models.inventory.QuestProgress

View file

@ -16,7 +16,7 @@ import android.widget.TextView
import com.facebook.drawee.view.SimpleDraweeView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.backgroundCompat
import com.habitrpg.android.habitica.extensions.bindView
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.models.inventory.Quest
import com.habitrpg.android.habitica.models.inventory.QuestContent
import com.habitrpg.android.habitica.models.user.User

View file

@ -19,7 +19,7 @@ import android.widget.TextView
import com.facebook.drawee.view.SimpleDraweeView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.backgroundCompat
import com.habitrpg.android.habitica.extensions.bindView
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.inventory.Quest
import com.habitrpg.android.habitica.models.inventory.QuestContent

View file

@ -1,133 +0,0 @@
package com.habitrpg.android.habitica.ui.views.subscriptions;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.habitrpg.android.habitica.BuildConfig;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.models.user.SubscriptionPlan;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class SubscriptionDetailsView extends LinearLayout {
@BindView(R.id.subscriptionDurationTextView)
TextView subscriptionDurationTextView;
@BindView(R.id.subscriptionStatusActive)
TextView subscriptionStatusActive;
@BindView(R.id.subscriptionStatusInactive)
TextView getSubscriptionStatusInactive;
@BindView(R.id.paymentProcessorTextView)
TextView paymentProcessorTextView;
@BindView(R.id.monthsSubscribedTextView)
TextView monthsSubscribedTextView;
@BindView(R.id.gemCapTextView)
TextView gemCapTextView;
@BindView(R.id.currentHourglassesTextView)
TextView currentHourglassesTextView;
@BindView(R.id.cancelSubscriptionDescription)
TextView cancelSubscripnDescription;
@BindView(R.id.visitWebsiteButton)
Button visitWebsiteButton;
private SubscriptionPlan plan;
public SubscriptionDetailsView(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
setupView();
}
public SubscriptionDetailsView(Context context) {
super(context);
setupView();
}
private void setupView() {
inflate(getContext(), R.layout.subscription_details, this);
ButterKnife.bind(this);
}
public void setPlan(SubscriptionPlan plan) {
this.plan = plan;
if (plan.isActive()) {
subscriptionStatusActive.setVisibility(View.VISIBLE);
getSubscriptionStatusInactive.setVisibility(View.GONE);
} else {
subscriptionStatusActive.setVisibility(View.GONE);
getSubscriptionStatusInactive.setVisibility(View.VISIBLE);
}
String duration = null;
if (plan.planId != null) {
if (plan.planId.equals(SubscriptionPlan.PLANID_BASIC) || plan.planId.equals(SubscriptionPlan.PLANID_BASICEARNED)) {
duration = getResources().getString(R.string.month);
} else if (plan.planId.equals(SubscriptionPlan.PLANID_BASIC3MONTH)) {
duration = getResources().getString(R.string.three_months);
} else if (plan.planId.equals(SubscriptionPlan.PLANID_BASIC6MONTH) || plan.planId.equals(SubscriptionPlan.PLANID_GOOGLE6MONTH)) {
duration = getResources().getString(R.string.six_months);
} else if (plan.planId.equals(SubscriptionPlan.PLANID_BASIC12MONTH)) {
duration = getResources().getString(R.string.twelve_months);
}
}
if (duration != null) {
subscriptionDurationTextView.setText(getResources().getString(R.string.subscription_duration, duration));
}
paymentProcessorTextView.setText(plan.paymentMethod);
if (plan.consecutive.getCount() == 1) {
monthsSubscribedTextView.setText(getResources().getString(R.string.one_month));
} else {
monthsSubscribedTextView.setText(getResources().getString(R.string.months, plan.consecutive.getCount()));
}
gemCapTextView.setText(String.valueOf(plan.consecutive.getGemCapExtra() + 25));
currentHourglassesTextView.setText(String.valueOf(plan.consecutive.getTrinkets()));
if (plan.paymentMethod != null) {
if (plan.paymentMethod.equals("Google")) {
cancelSubscripnDescription.setText(R.string.cancel_subscription_google_description);
visitWebsiteButton.setText(R.string.open_in_store);
} else {
cancelSubscripnDescription.setText(R.string.cancel_subscription_notgoogle_description);
visitWebsiteButton.setText(R.string.visit_habitica_website);
}
}
}
@OnClick(R.id.visitWebsiteButton)
public void openSubscriptionWebsite() {
if (plan.paymentMethod != null) {
Intent intent;
if (plan.paymentMethod.equals("Google")) {
intent = new Intent(Intent.ACTION_VIEW)
.setComponent(new ComponentName("com.android.vending",
"com.google.android.finsky.activities.MainActivity"))
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
} else {
intent = new Intent(Intent.ACTION_VIEW, Uri.parse(BuildConfig.BASE_URL + "/"));
}
getContext().startActivity(intent);
}
}
}

View file

@ -0,0 +1,110 @@
package com.habitrpg.android.habitica.ui.views.subscriptions
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.util.AttributeSet
import android.view.View
import android.widget.Button
import android.widget.LinearLayout
import android.widget.TextView
import butterknife.OnClick
import com.habitrpg.android.habitica.BuildConfig
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.extensions.inflate
import com.habitrpg.android.habitica.models.user.SubscriptionPlan
class SubscriptionDetailsView : LinearLayout {
internal val subscriptionDurationTextView: TextView by bindView(R.id.subscriptionDurationTextView)
internal val subscriptionStatusActive: TextView by bindView(R.id.subscriptionStatusActive)
private val getSubscriptionStatusInactive: TextView by bindView(R.id.subscriptionStatusInactive)
internal val paymentProcessorTextView: TextView by bindView(R.id.paymentProcessorTextView)
internal val monthsSubscribedTextView: TextView by bindView(R.id.monthsSubscribedTextView)
internal val gemCapTextView: TextView by bindView(R.id.gemCapTextView)
internal val currentHourglassesTextView: TextView by bindView(R.id.currentHourglassesTextView)
private val cancelSubscripnDescription: TextView by bindView(R.id.cancelSubscriptionDescription)
internal val visitWebsiteButton: Button by bindView(R.id.visitWebsiteButton)
private var plan: SubscriptionPlan? = null
constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet) {
setupView()
}
constructor(context: Context) : super(context) {
setupView()
}
private fun setupView() {
inflate(R.layout.subscription_details)
}
fun setPlan(plan: SubscriptionPlan) {
this.plan = plan
if (plan.isActive) {
subscriptionStatusActive.visibility = View.VISIBLE
getSubscriptionStatusInactive.visibility = View.GONE
} else {
subscriptionStatusActive.visibility = View.GONE
getSubscriptionStatusInactive.visibility = View.VISIBLE
}
var duration: String? = null
if (plan.planId != null) {
if (plan.planId == SubscriptionPlan.PLANID_BASIC || plan.planId == SubscriptionPlan.PLANID_BASICEARNED) {
duration = resources.getString(R.string.month)
} else if (plan.planId == SubscriptionPlan.PLANID_BASIC3MONTH) {
duration = resources.getString(R.string.three_months)
} else if (plan.planId == SubscriptionPlan.PLANID_BASIC6MONTH || plan.planId == SubscriptionPlan.PLANID_GOOGLE6MONTH) {
duration = resources.getString(R.string.six_months)
} else if (plan.planId == SubscriptionPlan.PLANID_BASIC12MONTH) {
duration = resources.getString(R.string.twelve_months)
}
}
if (duration != null) {
subscriptionDurationTextView.text = resources.getString(R.string.subscription_duration, duration)
}
paymentProcessorTextView.text = plan.paymentMethod
if (plan.consecutive.count == 1) {
monthsSubscribedTextView.text = resources.getString(R.string.one_month)
} else {
monthsSubscribedTextView.text = resources.getString(R.string.months, plan.consecutive.count)
}
gemCapTextView.text = (plan.consecutive.gemCapExtra + 25).toString()
currentHourglassesTextView.text = plan.consecutive.trinkets.toString()
if (plan.paymentMethod != null) {
if (plan.paymentMethod == "Google") {
cancelSubscripnDescription.setText(R.string.cancel_subscription_google_description)
visitWebsiteButton.setText(R.string.open_in_store)
} else {
cancelSubscripnDescription.setText(R.string.cancel_subscription_notgoogle_description)
visitWebsiteButton.setText(R.string.visit_habitica_website)
}
}
}
@OnClick(R.id.visitWebsiteButton)
fun openSubscriptionWebsite() {
if (plan?.paymentMethod != null) {
val intent: Intent = if (plan?.paymentMethod == "Google") {
Intent(Intent.ACTION_VIEW)
.setComponent(ComponentName("com.android.vending",
"com.google.android.finsky.activities.MainActivity"))
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
} else {
Intent(Intent.ACTION_VIEW, Uri.parse(BuildConfig.BASE_URL + "/"))
}
context.startActivity(intent)
}
}
}

View file

@ -1,103 +0,0 @@
package com.habitrpg.android.habitica.ui.views.subscriptions;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.TextView;
import com.habitrpg.android.habitica.R;
import butterknife.BindView;
import butterknife.ButterKnife;
public class SubscriptionOptionView extends FrameLayout {
@BindView(R.id.priceLabel)
TextView priceTextView;
@BindView(R.id.descriptionTextView)
TextView descriptionTextView;
@BindView(R.id.subscriptionSelectedView)
View subscriptionSelectedView;
@BindView(R.id.subscriptionSelectedFrameView)
View subscriptionSelectedFrameView;
@BindView(R.id.gemCapTextView)
TextView gemCapTextView;
@BindView(R.id.hourglassTextView)
TextView hourGlassTextView;
private String sku;
public SubscriptionOptionView(Context context, AttributeSet attrs) {
super(context, attrs);
inflate(context, R.layout.purchase_subscription_view, this);
ButterKnife.bind(this);
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.SubscriptionOptionView,
0, 0);
descriptionTextView.setText(context.getString(R.string.subscription_duration, a.getText(R.styleable.SubscriptionOptionView_recurringText)));
gemCapTextView.setText(a.getText(R.styleable.SubscriptionOptionView_gemCapText));
Integer hourGlassCount = a.getInteger(R.styleable.SubscriptionOptionView_hourGlassCount, 0);
if (hourGlassCount != 0) {
hourGlassTextView.setText(context.getString(R.string.subscription_hourglasses, hourGlassCount));
hourGlassTextView.setVisibility(View.VISIBLE);
} else {
hourGlassTextView.setVisibility(View.GONE);
}
}
public void setOnPurchaseClickListener(Button.OnClickListener listener) {
this.setOnClickListener(listener);
}
public void setPriceText(String text) {
this.priceTextView.setText(text);
}
public String getSku() {
return sku;
}
public void setSku(String sku) {
this.sku = sku;
}
public void setIsPurchased(boolean purchased) {
int horizontalPadding = (int) getResources().getDimension(R.dimen.pill_horizontal_padding);
int verticalPadding = (int) getResources().getDimension(R.dimen.pill_vertical_padding);
if (purchased) {
subscriptionSelectedView.setBackgroundResource(R.drawable.subscription_selected);
subscriptionSelectedFrameView.setBackgroundResource(R.color.brand_300);
gemCapTextView.setBackgroundResource(R.drawable.pill_bg_green);
gemCapTextView.setTextColor(getContext().getResources().getColor(R.color.white));
gemCapTextView.setPadding(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding);
hourGlassTextView.setBackgroundResource(R.drawable.pill_bg_green);
hourGlassTextView.setTextColor(getContext().getResources().getColor(R.color.white));
hourGlassTextView.setPadding(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding);
} else {
subscriptionSelectedView.setBackgroundResource(R.drawable.subscription_unselected);
subscriptionSelectedFrameView.setBackgroundResource(R.color.brand_700);
gemCapTextView.setBackgroundResource(R.drawable.pill_bg);
gemCapTextView.setTextColor(getContext().getResources().getColor(R.color.text_light));
gemCapTextView.setPadding(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding);
hourGlassTextView.setBackgroundResource(R.drawable.pill_bg);
hourGlassTextView.setTextColor(getContext().getResources().getColor(R.color.text_light));
hourGlassTextView.setPadding(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding);
}
}
}

View file

@ -0,0 +1,76 @@
package com.habitrpg.android.habitica.ui.views.subscriptions
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.widget.FrameLayout
import android.widget.TextView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.extensions.inflate
class SubscriptionOptionView(context: Context, attrs: AttributeSet) : FrameLayout(context, attrs) {
private val priceTextView: TextView by bindView(R.id.priceLabel)
internal val descriptionTextView: TextView by bindView(R.id.descriptionTextView)
internal val subscriptionSelectedView: View by bindView(R.id.subscriptionSelectedView)
internal val subscriptionSelectedFrameView: View by bindView(R.id.subscriptionSelectedFrameView)
internal val gemCapTextView: TextView by bindView(R.id.gemCapTextView)
private val hourGlassTextView: TextView by bindView(R.id.hourglassTextView)
var sku: String? = null
init {
inflate(R.layout.purchase_subscription_view)
val a = context.theme.obtainStyledAttributes(
attrs,
R.styleable.SubscriptionOptionView,
0, 0)
descriptionTextView.text = context.getString(R.string.subscription_duration, a.getText(R.styleable.SubscriptionOptionView_recurringText))
gemCapTextView.text = a.getText(R.styleable.SubscriptionOptionView_gemCapText)
val hourGlassCount = a.getInteger(R.styleable.SubscriptionOptionView_hourGlassCount, 0)
if (hourGlassCount != 0) {
hourGlassTextView.text = context.getString(R.string.subscription_hourglasses, hourGlassCount)
hourGlassTextView.visibility = View.VISIBLE
} else {
hourGlassTextView.visibility = View.GONE
}
}
fun setOnPurchaseClickListener(listener: OnClickListener) {
this.setOnClickListener(listener)
}
fun setPriceText(text: String) {
this.priceTextView.text = text
}
fun setIsPurchased(purchased: Boolean) {
val horizontalPadding = resources.getDimension(R.dimen.pill_horizontal_padding).toInt()
val verticalPadding = resources.getDimension(R.dimen.pill_vertical_padding).toInt()
if (purchased) {
subscriptionSelectedView.setBackgroundResource(R.drawable.subscription_selected)
subscriptionSelectedFrameView.setBackgroundResource(R.color.brand_300)
gemCapTextView.setBackgroundResource(R.drawable.pill_bg_green)
gemCapTextView.setTextColor(context.resources.getColor(R.color.white))
gemCapTextView.setPadding(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding)
hourGlassTextView.setBackgroundResource(R.drawable.pill_bg_green)
hourGlassTextView.setTextColor(context.resources.getColor(R.color.white))
hourGlassTextView.setPadding(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding)
} else {
subscriptionSelectedView.setBackgroundResource(R.drawable.subscription_unselected)
subscriptionSelectedFrameView.setBackgroundResource(R.color.brand_700)
gemCapTextView.setBackgroundResource(R.drawable.pill_bg)
gemCapTextView.setTextColor(context.resources.getColor(R.color.text_light))
gemCapTextView.setPadding(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding)
hourGlassTextView.setBackgroundResource(R.drawable.pill_bg)
hourGlassTextView.setTextColor(context.resources.getColor(R.color.text_light))
hourGlassTextView.setPadding(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding)
}
}
}

View file

@ -1,434 +0,0 @@
package com.habitrpg.android.habitica.ui.views.tasks;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.annotation.IdRes;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.support.v4.widget.CompoundButtonCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.AppCompatCheckBox;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.components.AppComponent;
import com.habitrpg.android.habitica.data.TagRepository;
import com.habitrpg.android.habitica.helpers.RxErrorHandler;
import com.habitrpg.android.habitica.models.Tag;
import com.habitrpg.android.habitica.models.tasks.Task;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import io.reactivex.Observable;
import io.reactivex.functions.Function;
public class TaskFilterDialog extends AlertDialog implements RadioGroup.OnCheckedChangeListener {
@Inject
TagRepository repository;
@BindView(R.id.task_type_title)
TextView taskTypeTitle;
@BindView(R.id.task_filter_wrapper)
RadioGroup taskFilters;
@BindView(R.id.all_task_filter)
RadioButton allTaskFilter;
@BindView(R.id.second_task_filter)
RadioButton secondTaskFilter;
@BindView(R.id.third_task_filter)
RadioButton thirdTaskFilter;
@BindView(R.id.tags_title)
TextView tagsTitleView;
@BindView(R.id.tag_edit_button)
Button tagsEditButton;
@BindView(R.id.tags_list)
LinearLayout tagsList;
private String taskType;
private OnFilterCompletedListener listener;
private String filterType;
private List<Tag> tags;
private List<String> activeTags;
private Map<String, Tag> editedTags = new HashMap<>();
private Map<String, Tag> createdTags = new HashMap<>();
private List<String> deletedTags = new ArrayList<>();
private Drawable addIcon;
private boolean isEditing;
public TaskFilterDialog(Context context, AppComponent component) {
super(context);
component.inject(this);
addIcon = ContextCompat.getDrawable(context, R.drawable.ic_add_purple_300_36dp);
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.dialog_task_filter, null);
ButterKnife.bind(this, view);
taskFilters.setOnCheckedChangeListener(this);
setTitle(R.string.filters);
setView(view);
this.setButton(AlertDialog.BUTTON_POSITIVE, context.getString(R.string.done), (dialog, which) -> {
if (isEditing) {
stopEditing();
}
if (listener != null) {
listener.onFilterCompleted(filterType, activeTags);
}
this.dismiss();
});
setButton(AlertDialog.BUTTON_NEUTRAL, getContext().getString(R.string.clear), (dialog, which) -> {
});
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Button clearButton = getButton(AlertDialog.BUTTON_NEUTRAL);
if (clearButton != null) {
clearButton.setOnClickListener(view1 -> {
if (isEditing) {
stopEditing();
}
setActiveFilter(null);
setActiveTags(null);
});
clearButton.setEnabled(hasActiveFilters());
}
}
@Override
public void show() {
super.show();
if (this.getWindow() != null) {
this.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
}
}
public void setTags(List<Tag> tags) {
this.tags = repository.getUnmanagedCopy(tags);
createTagViews();
}
private void createTagViews() {
ColorStateList colorStateList = new ColorStateList(
new int[][]{
new int[]{-android.R.attr.state_checked}, //disabled
new int[]{android.R.attr.state_checked} //enabled
},
new int[] {
Color.LTGRAY, //disabled
ContextCompat.getColor(getContext(), R.color.brand_400) //enabled
}
);
int leftPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16, getContext().getResources().getDisplayMetrics());
int verticalPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 12, getContext().getResources().getDisplayMetrics());
for (Tag tag : tags) {
AppCompatCheckBox tagCheckbox = new AppCompatCheckBox(getContext());
tagCheckbox.setText(tag.getName());
tagCheckbox.setTextSize(TypedValue.COMPLEX_UNIT_SP,16);
tagCheckbox.setPadding(tagCheckbox.getPaddingLeft()+ leftPadding,
verticalPadding,
tagCheckbox.getPaddingRight(),
verticalPadding);
tagCheckbox.setTextColor(ContextCompat.getColor(getContext(), R.color.textColorLight));
CompoundButtonCompat.setButtonTintList(tagCheckbox, colorStateList);
tagCheckbox.setOnCheckedChangeListener((buttonView, isChecked) -> {
if (isChecked) {
if (!activeTags.contains(tag.getId())) {
activeTags.add(tag.getId());
}
} else {
if (activeTags.contains(tag.getId())) {
activeTags.remove(tag.getId());
}
}
filtersChanged();
});
tagsList.addView(tagCheckbox);
}
createAddTagButton();
}
private void createAddTagButton() {
Button button = new Button(getContext());
button.setText(R.string.add_tag);
button.setOnClickListener(v -> createTag());
button.setCompoundDrawablesWithIntrinsicBounds(addIcon, null, null, null);
button.setBackgroundResource(R.drawable.layout_rounded_bg_lighter_gray);
button.setTextColor(ContextCompat.getColor(getContext(), R.color.text_light));
tagsList.addView(button);
}
private void createTag() {
Tag tag = new Tag();
tag.id = UUID.randomUUID().toString();
tags.add(tag);
createdTags.put(tag.getId(), tag);
startEditing();
}
private void startEditing() {
isEditing = true;
tagsList.removeAllViews();
createTagEditViews();
tagsEditButton.setText(R.string.done);
if (this.getWindow() != null) {
this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
}
}
private void stopEditing() {
isEditing = false;
tagsList.removeAllViews();
createTagViews();
tagsEditButton.setText(R.string.edit_tag_btn_edit);
if (this.getWindow() != null) {
this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
}
repository.updateTags(editedTags.values()).toObservable() .flatMap(tags -> Observable.fromIterable(tags)).subscribe(tag -> editedTags.remove(tag.id), RxErrorHandler.handleEmptyError());
repository.createTags(createdTags.values()).toObservable() .flatMap(tags -> Observable.fromIterable(tags)).subscribe(tag -> createdTags.remove(tag.getId()), RxErrorHandler.handleEmptyError());
repository.deleteTags(deletedTags).subscribe(tags1 -> deletedTags.clear(), RxErrorHandler.handleEmptyError());
}
private void createTagEditViews() {
LayoutInflater inflater = LayoutInflater.from(getContext());
if (tags != null) {
for (int index = 0; index < tags.size(); index++) {
Tag tag = tags.get(index);
createTagEditView(inflater, index, tag);
}
}
createAddTagButton();
}
private void createTagEditView(LayoutInflater inflater, int index, Tag tag) {
LinearLayout wrapper = (LinearLayout) inflater.inflate(R.layout.edit_tag_item, tagsList, false);
EditText tagEditText = (EditText) wrapper.findViewById(R.id.edit_text);
tagEditText.setText(tag.getName());
tagEditText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (index >= tags.size()) {
return;
}
Tag tag = tags.get(index);
tag.setName(s.toString());
if (createdTags.containsKey(tag.getId())) {
createdTags.put(tag.getId(), tag);
} else {
editedTags.put(tag.getId(), tag);
}
tags.set(index, tag);
}
@Override
public void afterTextChanged(Editable s) {
}
});
Button deleteButton = (Button) wrapper.findViewById(R.id.delete_button);
deleteButton.setOnClickListener(v -> {
deletedTags.add(tag.getId());
if (createdTags.containsKey(tag.getId())) {
createdTags.remove(tag.getId());
}
if (editedTags.containsKey(tag.getId())) {
editedTags.remove(tag.getId());
}
tags.remove(tag);
tagsList.removeView(wrapper);
});
tagsList.addView(wrapper);
}
public void setActiveTags(@Nullable List<String> tagIds) {
if (tagIds == null) {
this.activeTags.clear();
} else {
this.activeTags = tagIds;
}
for (int index = 0; index < tagsList.getChildCount()-1; index++) {
((AppCompatCheckBox)tagsList.getChildAt(index)).setChecked(false);
}
for (String tagId : this.activeTags) {
int index = indexForId(tagId);
if (index >= 0) {
((CheckBox)tagsList.getChildAt(index)).setChecked(true);
}
}
filtersChanged();
}
private int indexForId(String tagId) {
for (int index = 0; index < tags.size(); index++) {
if (tagId.equals(tags.get(index).id)) {
return index;
}
}
return -1;
}
public void setTaskType(String taskType, String activeFilter) {
this.taskType = taskType;
switch (taskType) {
case Task.TYPE_HABIT: {
taskTypeTitle.setText(R.string.habits);
allTaskFilter.setText(R.string.all);
secondTaskFilter.setText(R.string.weak);
thirdTaskFilter.setText(R.string.strong);
break;
}
case Task.TYPE_DAILY: {
taskTypeTitle.setText(R.string.dailies);
allTaskFilter.setText(R.string.all);
secondTaskFilter.setText(R.string.active);
thirdTaskFilter.setText(R.string.gray);
break;
}
case Task.TYPE_TODO: {
taskTypeTitle.setText(R.string.todos);
allTaskFilter.setText(R.string.active);
secondTaskFilter.setText(R.string.dated);
thirdTaskFilter.setText(R.string.completed);
break;
}
}
setActiveFilter(activeFilter);
}
private void setActiveFilter(@Nullable String activeFilter) {
filterType = activeFilter;
int checkedId = -1;
if (activeFilter == null) {
checkedId = R.id.all_task_filter;
} else {
switch (activeFilter) {
case Task.FILTER_ALL:
checkedId = R.id.all_task_filter;
break;
case Task.FILTER_WEAK:
case Task.FILTER_DATED:
checkedId = R.id.second_task_filter;
break;
case Task.FILTER_STRONG:
case Task.FILTER_GRAY:
case Task.FILTER_COMPLETED:
checkedId = R.id.third_task_filter;
break;
case Task.FILTER_ACTIVE:
if (taskType.equals(Task.TYPE_DAILY)) {
checkedId = R.id.second_task_filter;
} else {
checkedId = R.id.all_task_filter;
}
break;
}
}
taskFilters.check(checkedId);
filtersChanged();
}
@Override
public void onCheckedChanged(RadioGroup group, @IdRes int checkedId) {
if (taskType == null) {
return;
}
if (checkedId == R.id.all_task_filter) {
if (!taskType.equals(Task.TYPE_TODO)) {
filterType = Task.FILTER_ALL;
} else {
filterType = Task.FILTER_ACTIVE;
}
} else if (checkedId == R.id.second_task_filter) {
switch (taskType) {
case Task.TYPE_HABIT:
filterType = Task.FILTER_WEAK;
break;
case Task.FREQUENCY_DAILY:
filterType = Task.FILTER_ACTIVE;
break;
case Task.TYPE_TODO:
filterType = Task.FILTER_DATED;
break;
}
} else if (checkedId == R.id.third_task_filter) {
switch (taskType) {
case Task.TYPE_HABIT:
filterType = Task.FILTER_STRONG;
break;
case Task.FREQUENCY_DAILY:
filterType = Task.FILTER_GRAY;
break;
case Task.TYPE_TODO:
filterType = Task.FILTER_COMPLETED;
break;
}
}
filtersChanged();
}
@OnClick(R.id.tag_edit_button)
void editButtonClicked() {
isEditing = !isEditing;
if (isEditing) {
startEditing();
} else {
stopEditing();
}
}
private void filtersChanged() {
Button clearButton = getButton(AlertDialog.BUTTON_NEUTRAL);
if (clearButton != null) {
clearButton.setEnabled(hasActiveFilters());
}
}
private boolean hasActiveFilters() {
return taskFilters.getCheckedRadioButtonId() != R.id.all_task_filter || activeTags.size() > 0;
}
public void setListener(OnFilterCompletedListener listener) {
this.listener = listener;
}
public interface OnFilterCompletedListener {
void onFilterCompleted(@Nullable String activeTaskFilter, List<String> activeTags);
}
}

View file

@ -0,0 +1,363 @@
package com.habitrpg.android.habitica.ui.views.tasks
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Color
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.support.annotation.IdRes
import android.support.v4.content.ContextCompat
import android.support.v4.widget.CompoundButtonCompat
import android.support.v7.app.AlertDialog
import android.support.v7.widget.AppCompatCheckBox
import android.text.Editable
import android.text.TextWatcher
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.WindowManager
import android.widget.*
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.AppComponent
import com.habitrpg.android.habitica.data.TagRepository
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.Tag
import com.habitrpg.android.habitica.models.tasks.Task
import com.habitrpg.android.habitica.ui.helpers.bindView
import io.reactivex.Observable
import io.reactivex.functions.Consumer
import java.util.*
import javax.inject.Inject
class TaskFilterDialog(context: Context, component: AppComponent) : AlertDialog(context), RadioGroup.OnCheckedChangeListener {
@Inject
lateinit var repository: TagRepository
private val taskTypeTitle: TextView by bindView(R.id.task_type_title)
private val taskFilters: RadioGroup by bindView(R.id.task_filter_wrapper)
private val allTaskFilter: RadioButton by bindView(R.id.all_task_filter)
private val secondTaskFilter: RadioButton by bindView(R.id.second_task_filter)
private val thirdTaskFilter: RadioButton by bindView(R.id.third_task_filter)
private val tagsEditButton: Button by bindView(R.id.tag_edit_button)
private val tagsList: LinearLayout by bindView(R.id.tags_list)
private var taskType: String? = null
private var listener: OnFilterCompletedListener? = null
private var filterType: String? = null
private var tags = mutableListOf<Tag>()
private var activeTags = mutableListOf<String>()
private val editedTags = HashMap<String, Tag>()
private val createdTags = HashMap<String, Tag>()
private val deletedTags = ArrayList<String>()
private val addIcon: Drawable?
private var isEditing: Boolean = false
init {
component.inject(this)
addIcon = ContextCompat.getDrawable(context, R.drawable.ic_add_purple_300_36dp)
val inflater = LayoutInflater.from(context)
val view = inflater.inflate(R.layout.dialog_task_filter, null)
taskFilters.setOnCheckedChangeListener(this)
setTitle(R.string.filters)
setView(view)
this.setButton(AlertDialog.BUTTON_POSITIVE, context.getString(R.string.done)) { _, _ ->
if (isEditing) {
stopEditing()
}
listener?.onFilterCompleted(filterType, activeTags)
this.dismiss()
}
setButton(AlertDialog.BUTTON_NEUTRAL, getContext().getString(R.string.clear)) { _, _ -> }
tagsEditButton.setOnClickListener { editButtonClicked() }
}
override fun onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
val clearButton = getButton(AlertDialog.BUTTON_NEUTRAL)
if (clearButton != null) {
clearButton.setOnClickListener {
if (isEditing) {
stopEditing()
}
setActiveFilter(null)
setActiveTags(null)
}
clearButton.isEnabled = hasActiveFilters()
}
}
override fun show() {
super.show()
if (this.window != null) {
this.window.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM)
}
}
fun setTags(tags: List<Tag>) {
this.tags = repository.getUnmanagedCopy(tags).toMutableList()
createTagViews()
}
private fun createTagViews() {
val colorStateList = ColorStateList(
arrayOf(intArrayOf(-android.R.attr.state_checked), //disabled
intArrayOf(android.R.attr.state_checked) //enabled
),
intArrayOf(Color.LTGRAY, //disabled
ContextCompat.getColor(context, R.color.brand_400) //enabled
)
)
val leftPadding = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16f, context.resources.displayMetrics).toInt()
val verticalPadding = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 12f, context.resources.displayMetrics).toInt()
for (tag in tags) {
val tagCheckbox = AppCompatCheckBox(context)
tagCheckbox.text = tag.name
tagCheckbox.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16f)
tagCheckbox.setPadding(tagCheckbox.paddingLeft + leftPadding,
verticalPadding,
tagCheckbox.paddingRight,
verticalPadding)
tagCheckbox.setTextColor(ContextCompat.getColor(context, R.color.textColorLight))
CompoundButtonCompat.setButtonTintList(tagCheckbox, colorStateList)
tagCheckbox.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
if (!activeTags.contains(tag.getId())) {
activeTags.add(tag.getId())
}
} else {
if (activeTags.contains(tag.getId())) {
activeTags.remove(tag.getId())
}
}
filtersChanged()
}
tagsList.addView(tagCheckbox)
}
createAddTagButton()
}
private fun createAddTagButton() {
val button = Button(context)
button.setText(R.string.add_tag)
button.setOnClickListener { createTag() }
button.setCompoundDrawablesWithIntrinsicBounds(addIcon, null, null, null)
button.setBackgroundResource(R.drawable.layout_rounded_bg_lighter_gray)
button.setTextColor(ContextCompat.getColor(context, R.color.text_light))
tagsList.addView(button)
}
private fun createTag() {
val tag = Tag()
tag.id = UUID.randomUUID().toString()
tags.add(tag)
createdTags[tag.getId()] = tag
startEditing()
}
private fun startEditing() {
isEditing = true
tagsList.removeAllViews()
createTagEditViews()
tagsEditButton.setText(R.string.done)
if (this.window != null) {
this.window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)
}
}
private fun stopEditing() {
isEditing = false
tagsList.removeAllViews()
createTagViews()
tagsEditButton.setText(R.string.edit_tag_btn_edit)
if (this.window != null) {
this.window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN)
}
repository.updateTags(editedTags.values).toObservable().flatMap { tags -> Observable.fromIterable(tags) }.subscribe(Consumer { tag -> editedTags.remove(tag.id) }, RxErrorHandler.handleEmptyError())
repository.createTags(createdTags.values).toObservable().flatMap { tags -> Observable.fromIterable(tags) }.subscribe(Consumer { tag -> createdTags.remove(tag.getId()) }, RxErrorHandler.handleEmptyError())
repository.deleteTags(deletedTags).subscribe(Consumer { deletedTags.clear() }, RxErrorHandler.handleEmptyError())
}
private fun createTagEditViews() {
val inflater = LayoutInflater.from(context)
for (index in tags.indices) {
val tag = tags[index]
createTagEditView(inflater, index, tag)
}
createAddTagButton()
}
private fun createTagEditView(inflater: LayoutInflater, index: Int, tag: Tag) {
val wrapper = inflater.inflate(R.layout.edit_tag_item, tagsList, false) as LinearLayout
val tagEditText = wrapper.findViewById<View>(R.id.edit_text) as EditText
tagEditText.setText(tag.name)
tagEditText.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
if (index >= tags.size) {
return
}
val changedTag = tags[index]
changedTag.name = s.toString()
if (createdTags.containsKey(changedTag.getId())) {
createdTags[changedTag.getId()] = changedTag
} else {
editedTags[changedTag.getId()] = changedTag
}
tags[index] = changedTag
}
override fun afterTextChanged(s: Editable) {
}
})
val deleteButton = wrapper.findViewById<View>(R.id.delete_button) as Button
deleteButton.setOnClickListener {
deletedTags.add(tag.getId())
if (createdTags.containsKey(tag.getId())) {
createdTags.remove(tag.getId())
}
if (editedTags.containsKey(tag.getId())) {
editedTags.remove(tag.getId())
}
tags.remove(tag)
tagsList.removeView(wrapper)
}
tagsList.addView(wrapper)
}
fun setActiveTags(tagIds: MutableList<String>?) {
if (tagIds == null) {
this.activeTags.clear()
} else {
this.activeTags = tagIds
}
for (index in 0 until tagsList.childCount - 1) {
(tagsList.getChildAt(index) as AppCompatCheckBox).isChecked = false
}
for (tagId in this.activeTags) {
val index = indexForId(tagId)
if (index >= 0) {
(tagsList.getChildAt(index) as CheckBox).isChecked = true
}
}
filtersChanged()
}
private fun indexForId(tagId: String): Int {
for (index in tags.indices) {
if (tagId == tags[index].id) {
return index
}
}
return -1
}
fun setTaskType(taskType: String, activeFilter: String) {
this.taskType = taskType
when (taskType) {
Task.TYPE_HABIT -> {
taskTypeTitle.setText(R.string.habits)
allTaskFilter.setText(R.string.all)
secondTaskFilter.setText(R.string.weak)
thirdTaskFilter.setText(R.string.strong)
}
Task.TYPE_DAILY -> {
taskTypeTitle.setText(R.string.dailies)
allTaskFilter.setText(R.string.all)
secondTaskFilter.setText(R.string.active)
thirdTaskFilter.setText(R.string.gray)
}
Task.TYPE_TODO -> {
taskTypeTitle.setText(R.string.todos)
allTaskFilter.setText(R.string.active)
secondTaskFilter.setText(R.string.dated)
thirdTaskFilter.setText(R.string.completed)
}
}
setActiveFilter(activeFilter)
}
private fun setActiveFilter(activeFilter: String?) {
filterType = activeFilter
var checkedId = -1
if (activeFilter == null) {
checkedId = R.id.all_task_filter
} else {
when (activeFilter) {
Task.FILTER_ALL -> checkedId = R.id.all_task_filter
Task.FILTER_WEAK, Task.FILTER_DATED -> checkedId = R.id.second_task_filter
Task.FILTER_STRONG, Task.FILTER_GRAY, Task.FILTER_COMPLETED -> checkedId = R.id.third_task_filter
Task.FILTER_ACTIVE -> checkedId = if (taskType == Task.TYPE_DAILY) {
R.id.second_task_filter
} else {
R.id.all_task_filter
}
}
}
taskFilters.check(checkedId)
filtersChanged()
}
override fun onCheckedChanged(group: RadioGroup, @IdRes checkedId: Int) {
if (taskType == null) {
return
}
when (checkedId) {
R.id.all_task_filter -> filterType = if (taskType != Task.TYPE_TODO) {
Task.FILTER_ALL
} else {
Task.FILTER_ACTIVE
}
R.id.second_task_filter -> when (taskType) {
Task.TYPE_HABIT -> filterType = Task.FILTER_WEAK
Task.FREQUENCY_DAILY -> filterType = Task.FILTER_ACTIVE
Task.TYPE_TODO -> filterType = Task.FILTER_DATED
}
R.id.third_task_filter -> when (taskType) {
Task.TYPE_HABIT -> filterType = Task.FILTER_STRONG
Task.FREQUENCY_DAILY -> filterType = Task.FILTER_GRAY
Task.TYPE_TODO -> filterType = Task.FILTER_COMPLETED
}
}
filtersChanged()
}
private fun editButtonClicked() {
isEditing = !isEditing
if (isEditing) {
startEditing()
} else {
stopEditing()
}
}
private fun filtersChanged() {
val clearButton = getButton(AlertDialog.BUTTON_NEUTRAL)
if (clearButton != null) {
clearButton.isEnabled = hasActiveFilters()
}
}
private fun hasActiveFilters(): Boolean {
return taskFilters.checkedRadioButtonId != R.id.all_task_filter || activeTags.size > 0
}
fun setListener(listener: OnFilterCompletedListener) {
this.listener = listener
}
interface OnFilterCompletedListener {
fun onFilterCompleted(activeTaskFilter: String?, activeTags: List<String>?)
}
}

View file

@ -14,9 +14,9 @@ import java.util.Map;
import io.realm.Realm;
public class MountMapDeserializer implements JsonDeserializer<HashMap<String, Mount>> {
public class MountMapDeserializer implements JsonDeserializer<Map<String, Mount>> {
@Override
public HashMap<String, Mount> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
public Map<String, Mount> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
HashMap<String, Mount> vals = new HashMap<>();
if (json.isJsonObject()) {
JsonObject object = json.getAsJsonObject();

Some files were not shown because too many files have changed in this diff Show more