From 49eb74d12d053240010034dbe72221ab7b68e734 Mon Sep 17 00:00:00 2001 From: EdipoSouza Date: Sun, 1 Jan 2017 11:52:37 -0200 Subject: [PATCH 01/54] Add change user option --- .../teslesgendstracker/ui/DashActivity.kt | 53 ++++++++++++++----- .../ui/base/BaseActivity.kt | 37 +++++++------ .../ui/base/BaseFragment.kt | 4 ++ .../ui/cards/tabs/CardsAllFragment.kt | 3 +- .../ui/decks/new/NewDeckCardsListFragment.kt | 3 +- .../ui/decks/tabs/DecksPublicFragment.kt | 3 +- .../res/layout/navigation_drawer_header.xml | 10 +++- build.gradle | 2 +- 8 files changed, 77 insertions(+), 38 deletions(-) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DashActivity.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DashActivity.kt index 86358df..ed614b4 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DashActivity.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DashActivity.kt @@ -9,10 +9,12 @@ import android.view.Gravity import android.view.MenuItem import android.view.View import com.bumptech.glide.Glide +import com.ediposouza.teslesgendstracker.App import com.ediposouza.teslesgendstracker.R import com.ediposouza.teslesgendstracker.interactor.PrivateInteractor import com.ediposouza.teslesgendstracker.interactor.PublicInteractor import com.ediposouza.teslesgendstracker.ui.base.BaseFilterActivity +import com.ediposouza.teslesgendstracker.ui.base.CmdLoginSuccess import com.ediposouza.teslesgendstracker.ui.base.CmdShowTabs import com.ediposouza.teslesgendstracker.ui.cards.CardsFragment import com.ediposouza.teslesgendstracker.ui.cards.CmdFilterMagika @@ -31,7 +33,7 @@ import timber.log.Timber class DashActivity : BaseFilterActivity(), NavigationView.OnNavigationItemSelectedListener { - private val KEY_MENU_ITEM_SELECTED = "menu_index_key" + private val KEY_MENU_ITEM_SELECTED = "menuIndexKey" private var menuItemSelected = 0 private val publicInteractor = PublicInteractor() @@ -44,10 +46,18 @@ class DashActivity : BaseFilterActivity(), override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_dash) - snackbarNeedMargin = false decks_fab_add.hide() + snackbarNeedMargin = false filter_rarity.filterClick = { eventBus.post(CmdFilterRarity(it)) } filter_magika.filterClick = { eventBus.post(CmdFilterMagika(it)) } + with(dash_navigation_view.getHeaderView(0)) { + profile_change_user.setOnClickListener { showLogin() } + profile_image.setOnClickListener { + if (!App.hasUserLogged()) { + showLogin() + } + } + } } override fun onPostCreate(savedInstanceState: Bundle?) { @@ -57,23 +67,12 @@ class DashActivity : BaseFilterActivity(), override fun onDrawerOpened(drawerView: View) { super.onDrawerOpened(drawerView) - val user = FirebaseAuth.getInstance().currentUser - dash_navigation_view.menu.findItem(R.id.menu_matches)?.isVisible = user != null - with(dash_navigation_view.getHeaderView(0)) { - profile_name.text = user?.displayName ?: getString(R.string.unknown) - if (user != null) { - Glide.with(this@DashActivity) - .load(user.photoUrl) - .transform(CircleTransform(this@DashActivity)) - .into(profile_image) - updateCollectionStatistics() - } - } with(statisticsSheetBehavior) { if (state == BottomSheetBehavior.STATE_EXPANDED) { state = BottomSheetBehavior.STATE_COLLAPSED } } + updateCollectionStatistics() } } @@ -102,6 +101,26 @@ class DashActivity : BaseFilterActivity(), } } + override fun onResume() { + super.onResume() + updateUserMenuInfo() + } + + private fun updateUserMenuInfo() { + val user = FirebaseAuth.getInstance().currentUser + dash_navigation_view.menu.findItem(R.id.menu_matches)?.isVisible = App.hasUserLogged() + with(dash_navigation_view.getHeaderView(0)) { + profile_change_user.visibility = if (App.hasUserLogged()) View.VISIBLE else View.GONE + profile_name.text = user?.displayName ?: getString(R.string.unknown) + if (user != null) { + Glide.with(this@DashActivity) + .load(user.photoUrl) + .transform(CircleTransform(this@DashActivity)) + .into(profile_image) + } + } + } + override fun onBackPressed() { if (statisticsSheetBehavior.state == BottomSheetBehavior.STATE_EXPANDED) { statisticsSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED @@ -179,6 +198,12 @@ class DashActivity : BaseFilterActivity(), } } + @Subscribe + fun onLoginSuccess(cmdLoginSuccess: CmdLoginSuccess) { + updateUserMenuInfo() + updateCollectionStatistics() + } + @Subscribe fun onCmdShowTabs(cmdShowTabs: CmdShowTabs) { dash_app_bar_layout.setExpanded(true, true) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseActivity.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseActivity.kt index 659ab12..9eef97a 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseActivity.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseActivity.kt @@ -155,7 +155,25 @@ open class BaseActivity : AppCompatActivity(), GoogleApiClient.OnConnectionFaile protected fun showErrorUserNotLogged() { eventBus.post(CmdShowSnackbarMsg(CmdShowSnackbarMsg.TYPE_ERROR, R.string.error_auth) - .withAction(R.string.action_login, { eventBus.post(CmdShowLogin()) })) + .withAction(R.string.action_login, { showLogin() })) + } + + protected fun showLogin() { + eventBus.post(CmdShowLogin()) + } + + private fun showLoading() { + val progressBar = ProgressBar(this) + progressBar.isIndeterminate = true + val largeMargin = resources.getDimensionPixelSize(R.dimen.large_margin) + progressBar.setPadding(0, largeMargin, 0, largeMargin) + loading = AlertDialog.Builder(this) + .setView(progressBar) + .show() + } + + private fun hideLoading() { + loading?.dismiss() } private fun firebaseAuthWithGoogle(acct: GoogleSignInAccount?) { @@ -174,7 +192,7 @@ open class BaseActivity : AppCompatActivity(), GoogleApiClient.OnConnectionFaile toast("SignIn with " + currentUser?.displayName) }) PrivateInteractor().setUserInfo() - EventBus.getDefault().post(CmdLoginSuccess()) + eventBus.post(CmdLoginSuccess()) } else { Timber.w("signInWithCredential", task.exception) toast(getString(R.string.error_login)) @@ -184,20 +202,6 @@ open class BaseActivity : AppCompatActivity(), GoogleApiClient.OnConnectionFaile } } - private fun showLoading() { - val progressBar = ProgressBar(this) - progressBar.isIndeterminate = true - val largeMargin = resources.getDimensionPixelSize(R.dimen.large_margin) - progressBar.setPadding(0, largeMargin, 0, largeMargin) - loading = AlertDialog.Builder(this) - .setView(progressBar) - .show() - } - - private fun hideLoading() { - loading?.dismiss() - } - @SuppressWarnings("ResourceType") @Subscribe fun onCmdShowSnackMsg(cmdShowSnackbarMsg: CmdShowSnackbarMsg) { @@ -220,6 +224,7 @@ open class BaseActivity : AppCompatActivity(), GoogleApiClient.OnConnectionFaile @Subscribe fun onCmdShowLogin(showLogin: CmdShowLogin) { + googleApiClient?.clearDefaultAccountAndReconnect() val signInIntent = Auth.GoogleSignInApi.getSignInIntent(googleApiClient) startActivityForResult(signInIntent, RC_SIGN_IN) } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseFragment.kt index b32cbf9..cea222e 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseFragment.kt @@ -63,4 +63,8 @@ open class BaseFragment : Fragment() { isFragmentSelected = menuVisible } + protected fun showLogin() { + eventBus.post(CmdShowLogin()) + } + } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/tabs/CardsAllFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/tabs/CardsAllFragment.kt index 840ed55..9f8e6bf 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/tabs/CardsAllFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/tabs/CardsAllFragment.kt @@ -29,7 +29,6 @@ import jp.wasabeef.recyclerview.animators.ScaleInAnimator import kotlinx.android.synthetic.main.fragment_cards_list.* import kotlinx.android.synthetic.main.include_login_button.* import kotlinx.android.synthetic.main.itemlist_card.view.* -import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import java.util.* @@ -108,7 +107,7 @@ open class CardsAllFragment : BaseFragment() { } fun configLoggedViews() { - signin_button.setOnClickListener { EventBus.getDefault().post(CmdShowLogin()) } + signin_button.setOnClickListener { showLogin() } signin_button.visibility = if (App.hasUserLogged()) View.INVISIBLE else View.VISIBLE cards_recycler_view.visibility = if (App.hasUserLogged()) View.VISIBLE else View.INVISIBLE } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/new/NewDeckCardsListFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/new/NewDeckCardsListFragment.kt index e47e161..4398919 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/new/NewDeckCardsListFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/new/NewDeckCardsListFragment.kt @@ -8,7 +8,6 @@ import com.ediposouza.teslesgendstracker.ui.cards.tabs.CardsAllFragment import com.ediposouza.teslesgendstracker.ui.decks.CmdAddCard import com.ediposouza.teslesgendstracker.ui.util.GridSpacingItemDecoration import kotlinx.android.synthetic.main.fragment_cards_list.* -import org.greenrobot.eventbus.EventBus class NewDeckCardsListFragment : CardsAllFragment() { @@ -16,7 +15,7 @@ class NewDeckCardsListFragment : CardsAllFragment() { override val CARDS_PER_ROW = 2 val onItemClick = { view: View, card: Card -> - EventBus.getDefault().post(CmdAddCard(card)) + eventBus.post(CmdAddCard(card)) } override val itemDecoration by lazy { diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt index 1472e11..b8b098a 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt @@ -27,7 +27,6 @@ import jp.wasabeef.recyclerview.animators.SlideInLeftAnimator import kotlinx.android.synthetic.main.fragment_decks_list.* import kotlinx.android.synthetic.main.include_login_button.* import kotlinx.android.synthetic.main.itemlist_deck.view.* -import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import timber.log.Timber import java.text.NumberFormat @@ -93,7 +92,7 @@ open class DecksPublicFragment : BaseFragment() { } fun configLoggedViews() { - signin_button.setOnClickListener { EventBus.getDefault().post(CmdShowLogin()) } + signin_button.setOnClickListener { showLogin() } signin_button.visibility = if (App.hasUserLogged()) View.INVISIBLE else View.VISIBLE decks_recycler_view.visibility = if (App.hasUserLogged()) View.VISIBLE else View.INVISIBLE } diff --git a/app/src/main/res/layout/navigation_drawer_header.xml b/app/src/main/res/layout/navigation_drawer_header.xml index 3bb4180..cb3764f 100644 --- a/app/src/main/res/layout/navigation_drawer_header.xml +++ b/app/src/main/res/layout/navigation_drawer_header.xml @@ -6,6 +6,14 @@ android:background="@color/grey_500" android:paddingTop="@dimen/status_bar_height"> + + Date: Sun, 1 Jan 2017 22:57:41 -0200 Subject: [PATCH 02/54] Collapse card info when press Back button. Fix crop for large deck name --- .../teslesgendstracker/ui/CardActivity.kt | 14 +++++++-- app/src/main/res/layout/itemlist_deck.xml | 29 ++++++++++--------- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/CardActivity.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/CardActivity.kt index 855f6aa..1cd925c 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/CardActivity.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/CardActivity.kt @@ -31,6 +31,7 @@ class CardActivity : BaseActivity() { } val card: Card by lazy { intent.getParcelableExtra(EXTRA_CARD) } + val cardInfoSheetBehavior by lazy { BottomSheetBehavior.from(card_bottom_sheet) } var favorite: Boolean = false override fun onCreate(savedInstanceState: Bundle?) { @@ -61,9 +62,16 @@ class CardActivity : BaseActivity() { ActivityCompat.finishAfterTransition(this) } + override fun onBackPressed() { + if (cardInfoSheetBehavior.state == BottomSheetBehavior.STATE_EXPANDED) { + cardInfoSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED + } else { + super.onBackPressed() + } + } + private fun configureBottomSheet() { - val sheetBehavior = BottomSheetBehavior.from(card_bottom_sheet) - sheetBehavior.setBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() { + cardInfoSheetBehavior.setBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() { override fun onSlide(bottomSheet: View, slideOffset: Float) { } @@ -77,7 +85,7 @@ class CardActivity : BaseActivity() { } }) - card_bottom_sheet.setOnClickListener { sheetBehavior.toggleExpanded() } + card_bottom_sheet.setOnClickListener { cardInfoSheetBehavior.toggleExpanded() } } private fun loadCardInfo() { diff --git a/app/src/main/res/layout/itemlist_deck.xml b/app/src/main/res/layout/itemlist_deck.xml index 9a63f0c..3c80de0 100644 --- a/app/src/main/res/layout/itemlist_deck.xml +++ b/app/src/main/res/layout/itemlist_deck.xml @@ -46,20 +46,6 @@ android:layout_marginTop="@dimen/default_margin" android:src="@drawable/ic_private" /> - - + + \ No newline at end of file From 17a6f60990baffa781a3c321adb11a623897fefb Mon Sep 17 00:00:00 2001 From: EdipoSouza Date: Mon, 2 Jan 2017 01:08:41 -0200 Subject: [PATCH 03/54] Endless scroll --- .../interactor/PrivateInteractor.kt | 2 + .../ui/decks/tabs/DecksOwnerFragment.kt | 84 +++++++ .../ui/decks/tabs/DecksPublicFragment.kt | 29 ++- .../ui/util/AutoHideBehaviour.kt | 5 +- .../ui/util/firebase/FirebaseArray.kt | 218 ++++++++++++++++ .../firebase/FirebaseRecyclerViewAdapter.kt | 234 ++++++++++++++++++ app/src/main/res/layout/itemlist_loading.xml | 15 ++ 7 files changed, 571 insertions(+), 16 deletions(-) create mode 100644 app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/firebase/FirebaseArray.kt create mode 100644 app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/firebase/FirebaseRecyclerViewAdapter.kt create mode 100644 app/src/main/res/layout/itemlist_loading.xml diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt index a1574db..a159236 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt @@ -188,6 +188,8 @@ class PrivateInteractor : BaseInteractor() { } } + fun getOwnedPrivateDecksRef() = dbUser()?.child(NODE_DECKS)?.child(NODE_DECKS_PRIVATE)?.orderByChild(KEY_DECK_UPDATE_AT) + private fun getOwnedPrivateDecks(cls: Class?, onSuccess: (List) -> Unit) { dbUser()?.child(NODE_DECKS)?.child(NODE_DECKS_PRIVATE)?.orderByChild(KEY_DECK_UPDATE_AT)?.apply { keepSynced() diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksOwnerFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksOwnerFragment.kt index fc2615a..407a12f 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksOwnerFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksOwnerFragment.kt @@ -1,13 +1,21 @@ package com.ediposouza.teslesgendstracker.ui.decks.tabs import android.os.Bundle +import android.support.v7.widget.LinearLayoutManager +import android.support.v7.widget.RecyclerView import android.view.* +import android.widget.LinearLayout import android.widget.Switch import com.ediposouza.teslesgendstracker.R import com.ediposouza.teslesgendstracker.data.Class import com.ediposouza.teslesgendstracker.data.Deck +import com.ediposouza.teslesgendstracker.interactor.FirebaseParsers import com.ediposouza.teslesgendstracker.interactor.PrivateInteractor +import com.ediposouza.teslesgendstracker.ui.util.firebase.FirebaseRecyclerViewAdapter import com.ediposouza.teslesgendstracker.util.inflate +import com.google.firebase.database.DatabaseError +import kotlinx.android.synthetic.main.fragment_decks_list.* +import kotlinx.android.synthetic.main.itemlist_loading.view.* import timber.log.Timber /** @@ -20,12 +28,29 @@ class DecksOwnerFragment : DecksPublicFragment() { private val privateInteractor = PrivateInteractor() private var onlyPrivate: Switch? = null + val firebaseDecksAdapter = OwnerFirebaseAdapter(itemClick, itemLongClick, privateInteractor, DECK_PAGE_SIZE) { + decks_refresh_layout.isRefreshing = false + } + override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { return container?.inflate(R.layout.fragment_decks_list_owner) } override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + decks_recycler_view.adapter = firebaseDecksAdapter + decks_recycler_view.addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) { + if (dy < 0) { + return + } + val layoutManager = recyclerView?.layoutManager as LinearLayoutManager + if (layoutManager.findLastVisibleItemPosition() >= firebaseDecksAdapter.getContentCount() - 3) { + firebaseDecksAdapter.more() + } + } + }) + decks_refresh_layout.setOnRefreshListener { firebaseDecksAdapter.reset() } setHasOptionsMenu(true) configLoggedViews() } @@ -46,4 +71,63 @@ class DecksOwnerFragment : DecksPublicFragment() { }) } + class OwnerFirebaseAdapter(val itemClick: (View, Deck) -> Unit, val itemLongClick: (View, Deck) -> Boolean, + val privateInteractor: PrivateInteractor, pageSize: Int, + val onSyncEnd: (() -> Unit)? = null) : + FirebaseRecyclerViewAdapter( + FirebaseParsers.DeckParser::class.java, R.layout.itemlist_deck, DecksAllViewHolder::class.java, + privateInteractor.getOwnedPrivateDecksRef(), pageSize, false) { + + var VIEW_TYPE_HEADER = 0 + var VIEW_TYPE_CONTENT = 1 + var VIEW_TYPE_LOADING = 2 + + private var synced: Boolean = false + + override val snapShotOffset: Int = 1 + + fun getContentCount(): Int = super.getItemCount() + + override fun getItemCount(): Int = super.getItemCount() + 2 + + override fun getItemViewType(position: Int): Int { + return when (position) { + 0 -> VIEW_TYPE_HEADER + itemCount - 1 -> VIEW_TYPE_LOADING + else -> VIEW_TYPE_CONTENT + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DecksAllViewHolder { + val view = when (viewType) { + VIEW_TYPE_HEADER -> LinearLayout(parent.context).apply { minimumHeight = 1 } + VIEW_TYPE_LOADING -> parent.inflate(R.layout.itemlist_loading) + else -> parent.inflate(R.layout.itemlist_deck) + } + return DecksAllViewHolder(view, itemClick, itemLongClick) + } + + override fun populateViewHolder(viewHolder: DecksAllViewHolder, model: FirebaseParsers.DeckParser?, position: Int) { + if (model != null) { + viewHolder.bind(model.toDeck("", true), privateInteractor) + } else { + Timber.d("Loading: " + synced) + viewHolder.itemView.loadingBar?.visibility = if (synced) View.GONE else View.VISIBLE + } + } + + override fun onSyncStatusChanged(synced: Boolean) { + this.synced = synced + notifyItemChanged(itemCount - 1) + if (synced) { + onSyncEnd?.invoke() + } + } + + override fun onArrayError(firebaseError: DatabaseError) { + Timber.d(firebaseError.toException(), firebaseError.toString()) + } + + } + } \ No newline at end of file diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt index b8b098a..94c6b9b 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt @@ -38,6 +38,7 @@ import java.util.* open class DecksPublicFragment : BaseFragment() { val ADS_EACH_ITEMS = 15 //after 15 lines + val DECK_PAGE_SIZE = 7 val RC_DECK = 123 protected val publicInteractor = PublicInteractor() @@ -50,22 +51,24 @@ open class DecksPublicFragment : BaseFragment() { open protected val isDeckOwned: Boolean = false - protected val decksAdapter = DecksAllAdapter(ADS_EACH_ITEMS, R.layout.itemlist_deck_ads, - { view: View, deck: Deck -> - PrivateInteractor().getFavoriteDecks(deck.cls) { - val favorite = it?.filter { it.id == deck.id }?.isNotEmpty() ?: false - val like = deck.likes.contains(FirebaseAuth.getInstance().currentUser?.uid) - startActivityForResult(DeckActivity.newIntent(context, deck, favorite, like, isDeckOwned), - RC_DECK, ActivityOptionsCompat.makeSceneTransitionAnimation(activity, - Pair(view.deck_name as View, nameTransitionName), - Pair(view.deck_cover as View, coverTransitionName), - Pair(view.deck_attr1 as View, attr1TransitionName), - Pair(view.deck_attr2 as View, attr2TransitionName)).toBundle()) - } - }) { + val itemClick = { view: View, deck: Deck -> + PrivateInteractor().getFavoriteDecks(deck.cls) { + val favorite = it?.filter { it.id == deck.id }?.isNotEmpty() ?: false + val like = deck.likes.contains(FirebaseAuth.getInstance().currentUser?.uid) + startActivityForResult(DeckActivity.newIntent(context, deck, favorite, like, isDeckOwned), + RC_DECK, ActivityOptionsCompat.makeSceneTransitionAnimation(activity, + Pair(view.deck_name as View, nameTransitionName), + Pair(view.deck_cover as View, coverTransitionName), + Pair(view.deck_attr1 as View, attr1TransitionName), + Pair(view.deck_attr2 as View, attr2TransitionName)).toBundle()) + } + } + val itemLongClick = { view: View, deck: Deck -> true } + open protected val decksAdapter = DecksAllAdapter(ADS_EACH_ITEMS, R.layout.itemlist_deck_ads, + itemClick, itemLongClick) override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { return container?.inflate(R.layout.fragment_decks_list) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/AutoHideBehaviour.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/AutoHideBehaviour.kt index 3a2b57f..cc8bb42 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/AutoHideBehaviour.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/AutoHideBehaviour.kt @@ -7,7 +7,6 @@ import android.util.AttributeSet import android.view.View import com.ediposouza.teslesgendstracker.ui.base.CmdUpdateVisibility import org.greenrobot.eventbus.EventBus -import timber.log.Timber /** @@ -32,11 +31,11 @@ class AutoHideBehaviour(context: Context?, attrs: AttributeSet?) : super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed) if (dyConsumed > 0) { // User scrolled down and the FAB is currently visible -> hide the FAB - Timber.d("Scroll Down") +// Timber.d("Scroll Down") eventBus.post(CmdUpdateVisibility(false)) } else if (dyConsumed < 0) { // User scrolled up and the FAB is currently not visible -> show the FAB - Timber.d("Scroll Up") +// Timber.d("Scroll Up") eventBus.post(CmdUpdateVisibility(true)) } } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/firebase/FirebaseArray.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/firebase/FirebaseArray.kt new file mode 100644 index 0000000..d8b85b4 --- /dev/null +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/firebase/FirebaseArray.kt @@ -0,0 +1,218 @@ +/* + * Firebase UI Bindings Android Library + * + * Copyright © 2015 Firebase - All Rights Reserved + * https://www.firebase.com + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binaryform must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY FIREBASE AS IS AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL FIREBASE BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.ediposouza.teslesgendstracker.ui.util.firebase + +import com.google.firebase.database.* +import java.util.* + +/** + * This class implements an array-like collection on top of a Firebase location. + */ +class FirebaseArray(val mOriginalQuery: Query?, pageSize: Int = 0, + val mOrderASC: Boolean = true) : ChildEventListener, ValueEventListener { + + enum class EventType { + Added, Changed, Removed, Moved, Reset + } + + enum class SyncType { + UnSynced, Synced + } + + interface OnChangedListener { + fun onChanged(type: EventType, index: Int, oldIndex: Int) + } + + interface OnErrorListener { + fun onError(firebaseError: DatabaseError) + } + + interface OnSyncStatusChangedListener { + fun onChanged(type: SyncType) + } + + private var mQuery: Query? = null + private val mSnapshots: ArrayList = ArrayList() + private val mPageSize: Int + private var mCurrentSize: Int = 0 + private var isSyncing: Boolean = false + set(syncing) { + if (syncing == isSyncing) { + return + } + field = syncing + if (syncing) { + notifyOnSyncChangedListeners(SyncType.UnSynced) + } else { + notifyOnSyncChangedListeners(SyncType.Synced) + } + } + var mOnChangedListener: OnChangedListener? = null + var mOnErrorListener: OnErrorListener? = null + var mOnSyncStatusListener: OnSyncStatusChangedListener? = null + set(listener) { + field = listener + notifyOnSyncChangedListeners(if (isSyncing) SyncType.UnSynced else SyncType.Synced) + } + + init { + mCurrentSize = Math.abs(pageSize) + mPageSize = mCurrentSize + + setup() + } + + fun reset() { + mCurrentSize = mPageSize + mSnapshots.clear() + setup() + notifyChangedListeners(EventType.Reset, 0) + } + + fun more() { + if (mPageSize > 0 && !isSyncing) { + mCurrentSize += mPageSize + setup() + } + } + + fun cleanup() { + mQuery?.removeEventListener(this as ChildEventListener) + mQuery?.removeEventListener(this as ValueEventListener) + } + + val count: Int + get() = mSnapshots.size + + fun getItem(index: Int): DataSnapshot { + return mSnapshots[index] + } + + private fun setup() { + if (mQuery != null) { + cleanup() + } + if (mPageSize == 0) { + mQuery = mOriginalQuery + } else if (mOrderASC) { + mQuery = mOriginalQuery?.limitToFirst(mCurrentSize) + } else { + mQuery = mOriginalQuery?.limitToLast(mCurrentSize) + } + isSyncing = true + mQuery?.addChildEventListener(this) + mQuery?.addListenerForSingleValueEvent(this) + } + + private fun getIndexForKey(key: String): Int { + var index = 0 + for (snapshot in mSnapshots) { + if (snapshot.key == key) { + return index + } else { + index++ + } + } + throw IllegalArgumentException("Key not found") + } + + // Start of ChildEventListener and ValueEventListener methods + + override fun onChildAdded(snapshot: DataSnapshot, previousChildKey: String?) { + var index = if (mOrderASC) 0 else count + if (previousChildKey != null) { + if (mOrderASC) { + index = getIndexForKey(previousChildKey) + 1 + } else { + index = getIndexForKey(previousChildKey) + } + } + if (mOrderASC && + index < count && + mSnapshots[index].key == snapshot.key) { + return + } else if (!mOrderASC && + index < count + 1 && + index > 0 && + mSnapshots[index - 1].key == snapshot.key) { + return + } + + mSnapshots.add(index, snapshot) + notifyChangedListeners(EventType.Added, index) + } + + override fun onChildChanged(snapshot: DataSnapshot, previousChildKey: String) { + val index = getIndexForKey(snapshot.key) + mSnapshots[index] = snapshot + notifyChangedListeners(EventType.Changed, index) + } + + override fun onChildRemoved(snapshot: DataSnapshot) { + val index = getIndexForKey(snapshot.key) + mSnapshots.removeAt(index) + notifyChangedListeners(EventType.Removed, index) + } + + override fun onChildMoved(snapshot: DataSnapshot, previousChildKey: String?) { + val oldIndex = getIndexForKey(snapshot.key) + mSnapshots.removeAt(oldIndex) + val newIndex = if (previousChildKey == null) 0 else getIndexForKey(previousChildKey) + 1 + mSnapshots.add(newIndex, snapshot) + notifyChangedListeners(EventType.Moved, newIndex, oldIndex) + } + + override fun onDataChange(dataSnapshot: DataSnapshot) { + isSyncing = false + } + + override fun onCancelled(firebaseError: DatabaseError) { + notifyOnErrorListeners(firebaseError) + } + + // End of ChildEventListener and ValueEventListener methods + + fun notifyChangedListeners(type: EventType, index: Int, oldIndex: Int = -1) { + if (mOnChangedListener != null) { + mOnChangedListener!!.onChanged(type, index, oldIndex) + } + } + + fun notifyOnErrorListeners(firebaseError: DatabaseError) { + if (mOnErrorListener != null) { + mOnErrorListener!!.onError(firebaseError) + } + } + + fun notifyOnSyncChangedListeners(type: SyncType) { + if (mOnSyncStatusListener != null) { + mOnSyncStatusListener!!.onChanged(type) + } + } +} diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/firebase/FirebaseRecyclerViewAdapter.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/firebase/FirebaseRecyclerViewAdapter.kt new file mode 100644 index 0000000..17138e3 --- /dev/null +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/firebase/FirebaseRecyclerViewAdapter.kt @@ -0,0 +1,234 @@ +/* + * Firebase UI Bindings Android Library + * + * Copyright © 2015 Firebase - All Rights Reserved + * https://www.firebase.com + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binaryform must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY FIREBASE AS IS AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL FIREBASE BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.ediposouza.teslesgendstracker.ui.util.firebase + +import android.support.v7.widget.RecyclerView +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.google.firebase.database.DatabaseError +import com.google.firebase.database.DatabaseReference +import com.google.firebase.database.Query +import java.lang.reflect.InvocationTargetException + +/** + * This class is a generic way of backing an RecyclerView with a Firebase location. + * It handles all of the child events at the given Firebase location. It marshals received data into the given + * class type. + * To use this class in your app, subclass it passing in all required parameters and implement the + * populateViewHolder method. + *
+ * {@code
+ *     private static class ChatMessageViewHolder extends RecyclerView.ViewHolder {
+ *         TextView messageText;
+ *         TextView nameText;
+ *
+ *         public ChatMessageViewHolder(View itemView) {
+ *             super(itemView);
+ *             nameText = (TextView)itemView.findViewById(android.R.id.text1);
+ *             messageText = (TextView) itemView.findViewById(android.R.id.text2);
+ *         }
+ *     }
+ *
+ *     FirebaseRecyclerViewAdapter adapter;
+ *     ref = new Firebase("https://.firebaseio.com");
+ *
+ *     RecyclerView recycler = (RecyclerView) findViewById(R.id.messages_recycler);
+ *     recycler.setHasFixedSize(true);
+ *     recycler.setLayoutManager(new LinearLayoutManager(this));
+ *
+ *     adapter = new FirebaseRecyclerViewAdapter(ChatMessage.class, android.R.layout.two_line_list_item, ChatMessageViewHolder.class, mRef) {
+ *         public void populateViewHolder(ChatMessageViewHolder chatMessageViewHolder, ChatMessage chatMessage) {
+ *             chatMessageViewHolder.nameText.setText(chatMessage.getName());
+ *             chatMessageViewHolder.messageText.setText(chatMessage.getMessage());
+ *         }
+ *     };
+ *     recycler.setAdapter(mAdapter);
+ * }
+ * 
+ * + * @param The Java class that maps to the type of objects stored in the Firebase location. + * @param The ViewHolder class that contains the Views in the layout that is shown for each object. + * + * + * CONSTRUCTOR + * @param modelClass Firebase will marshall the data at a location into an instance of a class that you provide + * * + * @param modelLayout This is the layout used to represent a single item in the list. You will be responsible for populating an + * * instance of the corresponding view with the data from an instance of modelClass. + * * + * @param viewHolderClass The class that hold references to all sub-views in an instance modelLayout. + * * + * @param ref The Firebase location to watch for data changes. + * * + * @param pageSize initial page size. set 0 for no limit. + */ +abstract class FirebaseRecyclerViewAdapter( + var mModelClass: Class, + protected var mModelLayout: Int, + var mViewHolderClass: Class, + ref: Query?, + pageSize: Int = 0, + orderASC: Boolean = false) : RecyclerView.Adapter() { + + var mSnapshots: FirebaseArray = FirebaseArray(ref, pageSize, orderASC) + + init { + mSnapshots.mOnChangedListener = object : FirebaseArray.OnChangedListener { + override fun onChanged(type: FirebaseArray.EventType, index: Int, oldIndex: Int) { + when (type) { + FirebaseArray.EventType.Added -> notifyItemInserted(index + snapShotOffset) + FirebaseArray.EventType.Changed -> notifyItemChanged(index + snapShotOffset) + FirebaseArray.EventType.Removed -> notifyItemRemoved(index + snapShotOffset) + FirebaseArray.EventType.Moved -> notifyItemMoved(oldIndex + snapShotOffset, index + snapShotOffset) + FirebaseArray.EventType.Reset -> notifyDataSetChanged() + else -> throw IllegalStateException("Incomplete case statement") + } + } + + } + mSnapshots.mOnSyncStatusListener = object : FirebaseArray.OnSyncStatusChangedListener { + override fun onChanged(type: FirebaseArray.SyncType) { + onSyncStatusChanged(type == FirebaseArray.SyncType.Synced) + } + } + mSnapshots.mOnErrorListener = object : FirebaseArray.OnErrorListener { + override fun onError(firebaseError: DatabaseError) { + onArrayError(firebaseError) + } + } + + } + + /** + * Increase the limit of the query by one page. + */ + fun more() = mSnapshots.more() + + /** + * Reset the limit of the query to its original size. + */ + fun reset() = mSnapshots.reset() + + fun cleanup() = mSnapshots.cleanup() + + /** + * Override when adding headers. + * @return number of items inserted in front of the FirebaseArray + */ + open val snapShotOffset: Int = 0 + + /** + * Override when adding headers or footers + * @return number of items including headers and footers. + */ + override fun getItemCount(): Int { + return mSnapshots.count + } + + fun getItem(position: Int): T { + return mSnapshots.getItem(position - snapShotOffset).getValue(mModelClass) + } + + fun getRef(position: Int): DatabaseReference { + return mSnapshots.getItem(position).ref + } + + override fun getItemId(position: Int): Long { + if (position < snapShotOffset) return ("header" + position).hashCode().toLong() + if (position >= snapShotOffset + mSnapshots.count) return ("footer" + (position - (snapShotOffset + mSnapshots.count))).hashCode().toLong() + // http://stackoverflow.com/questions/5100071/whats-the-purpose-of-item-ids-in-android-listview-adapter + return mSnapshots.getItem(position).key.hashCode().toLong() + } + + /** + * Override when adding headers or footers. + */ + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH { + val view = LayoutInflater.from(parent.context).inflate(mModelLayout, parent, false) as ViewGroup + try { + val constructor = mViewHolderClass.getConstructor(View::class.java) + return constructor.newInstance(view) + } catch (e: NoSuchMethodException) { + throw RuntimeException(e) + } catch (e: InvocationTargetException) { + throw RuntimeException(e) + } catch (e: InstantiationException) { + throw RuntimeException(e) + } catch (e: IllegalAccessException) { + throw RuntimeException(e) + } + + } + + override fun onBindViewHolder(viewHolder: VH, position: Int) { + var model: T? = null + val arrayPosition = position - snapShotOffset + if (arrayPosition < mSnapshots.count && arrayPosition >= 0) { + model = getItem(position) + } + populateViewHolder(viewHolder, model, position) + } + + /** + * Each time the data at the given Firebase location changes, this method will be called for each item that needs + * to be displayed. The first two arguments correspond to the mLayout and mModelClass given to the constructor of + * this class. The third argument is the item's position in the list. + *

+ * Your implementation should populate the view using the data contained in the model. + * You should implement either this method or the other FirebaseRecyclerViewAdapter#populateViewHolder(VH, Object) method + * but not both. + * + * @param viewHolder The view to populate + * * + * @param model The object containing the data used to populate the view + * * + * @param position The position in the list of the view being populated + */ + open protected fun populateViewHolder(viewHolder: VH, model: T?, position: Int) { + populateViewHolder(viewHolder, model) + } + + /** + * This is a backwards compatible version of populateViewHolder. + * + * + * You should implement either this method or the other FirebaseRecyclerViewAdapter#populateViewHolder(VH, T, int) method + * but not both. + * see FirebaseListAdapter#populateView(View, Object, int) + * @param viewHolder The view to populate + * * + * @param model The object containing the data used to populate the view + */ + open protected fun populateViewHolder(viewHolder: VH, model: T?) { + } + + open protected fun onSyncStatusChanged(synced: Boolean) {} + open protected fun onArrayError(firebaseError: DatabaseError) {} +} \ No newline at end of file diff --git a/app/src/main/res/layout/itemlist_loading.xml b/app/src/main/res/layout/itemlist_loading.xml new file mode 100644 index 0000000..6a2b41a --- /dev/null +++ b/app/src/main/res/layout/itemlist_loading.xml @@ -0,0 +1,15 @@ + + + + + + \ No newline at end of file From 1214dab0051068c6b5927b5aa27a96567d14f469 Mon Sep 17 00:00:00 2001 From: EdipoSouza Date: Tue, 3 Jan 2017 00:37:47 -0200 Subject: [PATCH 04/54] BaseFirebaseRVAdapter --- .../ui/base/BaseAdsFirebaseAdapter.kt | 84 +++++++++++++++++ .../ui/base/BaseFirebaseRVAdapter.kt | 80 +++++++++++++++++ .../ui/decks/tabs/DecksOwnerFragment.kt | 89 +++++-------------- .../ui/decks/tabs/DecksPublicFragment.kt | 2 + ...lerViewAdapter.kt => FirebaseRVAdapter.kt} | 16 ++-- 5 files changed, 193 insertions(+), 78 deletions(-) create mode 100644 app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseAdsFirebaseAdapter.kt create mode 100644 app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseFirebaseRVAdapter.kt rename app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/firebase/{FirebaseRecyclerViewAdapter.kt => FirebaseRVAdapter.kt} (93%) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseAdsFirebaseAdapter.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseAdsFirebaseAdapter.kt new file mode 100644 index 0000000..bd10fa2 --- /dev/null +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseAdsFirebaseAdapter.kt @@ -0,0 +1,84 @@ +package com.ediposouza.teslesgendstracker.ui.base + +import android.support.annotation.LayoutRes +import android.support.v7.widget.GridLayoutManager +import android.support.v7.widget.RecyclerView +import android.view.ViewGroup +import com.ediposouza.teslesgendstracker.R +import com.ediposouza.teslesgendstracker.util.inflate +import com.ediposouza.teslesgendstracker.util.load +import com.google.android.gms.ads.AdView +import com.google.android.gms.ads.NativeExpressAdView + +/** + * Created by ediposouza on 08/12/16. + */ +abstract class BaseAdsFirebaseAdapter(val adsEachItems: Int, @LayoutRes val adsLayout: Int) : + RecyclerView.Adapter() { + + companion object { + + const val ITEM_VIEW_TYPE_DEFAULT = 0 + const val ITEM_VIEW_TYPE_ADS = 1 + + } + + constructor(adsEachItems: Int, layoutManager: GridLayoutManager, + @LayoutRes adsLayout: Int) : this(adsEachItems, adsLayout) { + onRestoreState(layoutManager) + } + + abstract fun onCreateDefaultViewHolder(parent: ViewGroup): RecyclerView.ViewHolder + abstract fun onBindDefaultViewHolder(holder: RecyclerView.ViewHolder?, position: Int) + abstract fun getDefaultItemCount(): Int + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + if (viewType == ITEM_VIEW_TYPE_DEFAULT) { + return onCreateDefaultViewHolder(parent) + } else { + val adsItemView = parent.inflate(adsLayout) + val ads = adsItemView.findViewById(R.id.ads_view) + when (ads) { + is AdView -> ads.load() + is NativeExpressAdView -> ads.load() + } + return object : RecyclerView.ViewHolder(adsItemView) {} + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder?, position: Int) { + val qtdAdsBefore = getAdsQtdBeforePosition(position) + if (getItemViewType(position) == ITEM_VIEW_TYPE_DEFAULT) { + onBindDefaultViewHolder(holder, position - qtdAdsBefore) + } + } + + override fun getItemCount(): Int = getDefaultItemCount() + getDefaultItemCount().div(adsEachItems) + + override fun getItemViewType(position: Int): Int { + val qtdAdsBefore = getAdsQtdBeforePosition(position) + val nextEachItems = adsEachItems * (qtdAdsBefore + 1) + qtdAdsBefore + return if (position == nextEachItems) ITEM_VIEW_TYPE_ADS else ITEM_VIEW_TYPE_DEFAULT + } + + private fun getAdsQtdBeforePosition(position: Int): Int { + val qtdAds = position.div(adsEachItems) + return (position - qtdAds).div(adsEachItems) + } + + protected fun getAdsQtdBeforeDefaultPosition(position: Int): Int { + return position.div(adsEachItems) + } + + fun onRestoreState(layoutManager: GridLayoutManager) { + layoutManager.apply { + spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { + override fun getSpanSize(position: Int): Int { + val itemType = getItemViewType(position) + return if (itemType == ITEM_VIEW_TYPE_ADS) spanCount else 1 + } + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseFirebaseRVAdapter.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseFirebaseRVAdapter.kt new file mode 100644 index 0000000..583cf47 --- /dev/null +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseFirebaseRVAdapter.kt @@ -0,0 +1,80 @@ +package com.ediposouza.teslesgendstracker.ui.base + +import android.support.annotation.IntegerRes +import android.support.v7.widget.RecyclerView +import android.view.View +import android.view.ViewGroup +import android.widget.LinearLayout +import com.ediposouza.teslesgendstracker.R +import com.ediposouza.teslesgendstracker.ui.util.firebase.FirebaseRVAdapter +import com.ediposouza.teslesgendstracker.util.inflate +import com.google.firebase.database.DatabaseError +import com.google.firebase.database.Query +import kotlinx.android.synthetic.main.itemlist_loading.view.* +import timber.log.Timber + +/** + * Created by EdipoSouza on 1/2/17. + */ +abstract class BaseFirebaseRVAdapter(@IntegerRes val layout: Int, + model: Class, + val viewHolder: Class, + ref: Query?, pageSize: Int) : + FirebaseRVAdapter(layout, model, viewHolder, ref, pageSize, false) { + + var VIEW_TYPE_HEADER = 0 + var VIEW_TYPE_CONTENT = 1 + var VIEW_TYPE_LOADING = 2 + + private var synced: Boolean = false + + override val snapShotOffset: Int = 1 + + abstract fun onCreateDefaultViewHolder(parent: ViewGroup): VH + abstract fun onBindContentHolder(model: T, viewHolder: VH) + abstract fun onSyncEnd() + + fun getContentCount(): Int = super.getItemCount() + + override fun getItemCount(): Int = super.getItemCount() + 2 + + override fun getItemViewType(position: Int): Int { + return when (position) { + 0 -> VIEW_TYPE_HEADER + itemCount - 1 -> VIEW_TYPE_LOADING + else -> VIEW_TYPE_CONTENT + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH { + when (viewType) { + VIEW_TYPE_CONTENT -> return onCreateDefaultViewHolder(parent) + else -> { + val view = if (viewType == VIEW_TYPE_LOADING) parent.inflate(R.layout.itemlist_loading) else + LinearLayout(parent.context).apply { minimumHeight = 1 } + return viewHolder.getConstructor(View::class.java).newInstance(view) + } + } + } + + override fun populateViewHolder(viewHolder: VH, model: T?) { + if (model != null) { + onBindContentHolder(model, viewHolder) + } else { + viewHolder.itemView.loadingBar?.visibility = if (synced) View.GONE else View.VISIBLE + } + } + + override fun onSyncStatusChanged(synced: Boolean) { + this.synced = synced + notifyItemChanged(itemCount - 1) + if (synced) { + onSyncEnd() + } + } + + override fun onArrayError(firebaseError: DatabaseError) { + Timber.d(firebaseError.toException(), firebaseError.toString()) + } + +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksOwnerFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksOwnerFragment.kt index 407a12f..eb6e42a 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksOwnerFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksOwnerFragment.kt @@ -4,18 +4,15 @@ import android.os.Bundle import android.support.v7.widget.LinearLayoutManager import android.support.v7.widget.RecyclerView import android.view.* -import android.widget.LinearLayout import android.widget.Switch import com.ediposouza.teslesgendstracker.R import com.ediposouza.teslesgendstracker.data.Class import com.ediposouza.teslesgendstracker.data.Deck import com.ediposouza.teslesgendstracker.interactor.FirebaseParsers import com.ediposouza.teslesgendstracker.interactor.PrivateInteractor -import com.ediposouza.teslesgendstracker.ui.util.firebase.FirebaseRecyclerViewAdapter +import com.ediposouza.teslesgendstracker.ui.base.BaseFirebaseRVAdapter import com.ediposouza.teslesgendstracker.util.inflate -import com.google.firebase.database.DatabaseError import kotlinx.android.synthetic.main.fragment_decks_list.* -import kotlinx.android.synthetic.main.itemlist_loading.view.* import timber.log.Timber /** @@ -28,8 +25,21 @@ class DecksOwnerFragment : DecksPublicFragment() { private val privateInteractor = PrivateInteractor() private var onlyPrivate: Switch? = null - val firebaseDecksAdapter = OwnerFirebaseAdapter(itemClick, itemLongClick, privateInteractor, DECK_PAGE_SIZE) { - decks_refresh_layout.isRefreshing = false + val ownerDecksAdapter = object : BaseFirebaseRVAdapter( + R.layout.itemlist_deck, FirebaseParsers.DeckParser::class.java, DecksAllViewHolder::class.java, + privateInteractor.getOwnedPrivateDecksRef(), DECK_PAGE_SIZE) { + override fun onCreateDefaultViewHolder(parent: ViewGroup): DecksAllViewHolder { + return DecksAllViewHolder(parent.inflate(R.layout.itemlist_deck), itemClick, itemLongClick) + } + + override fun onBindContentHolder(model: FirebaseParsers.DeckParser, viewHolder: DecksAllViewHolder) { + viewHolder.bind(model.toDeck("", true), privateInteractor) + } + + override fun onSyncEnd() { + decks_refresh_layout.isRefreshing = false + } + } override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { @@ -38,19 +48,19 @@ class DecksOwnerFragment : DecksPublicFragment() { override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - decks_recycler_view.adapter = firebaseDecksAdapter + decks_recycler_view.adapter = ownerDecksAdapter decks_recycler_view.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) { if (dy < 0) { return } val layoutManager = recyclerView?.layoutManager as LinearLayoutManager - if (layoutManager.findLastVisibleItemPosition() >= firebaseDecksAdapter.getContentCount() - 3) { - firebaseDecksAdapter.more() + if (layoutManager.findLastVisibleItemPosition() >= ownerDecksAdapter.getContentCount() - 3) { + ownerDecksAdapter.more() } } }) - decks_refresh_layout.setOnRefreshListener { firebaseDecksAdapter.reset() } + decks_refresh_layout.setOnRefreshListener { ownerDecksAdapter.reset() } setHasOptionsMenu(true) configLoggedViews() } @@ -71,63 +81,4 @@ class DecksOwnerFragment : DecksPublicFragment() { }) } - class OwnerFirebaseAdapter(val itemClick: (View, Deck) -> Unit, val itemLongClick: (View, Deck) -> Boolean, - val privateInteractor: PrivateInteractor, pageSize: Int, - val onSyncEnd: (() -> Unit)? = null) : - FirebaseRecyclerViewAdapter( - FirebaseParsers.DeckParser::class.java, R.layout.itemlist_deck, DecksAllViewHolder::class.java, - privateInteractor.getOwnedPrivateDecksRef(), pageSize, false) { - - var VIEW_TYPE_HEADER = 0 - var VIEW_TYPE_CONTENT = 1 - var VIEW_TYPE_LOADING = 2 - - private var synced: Boolean = false - - override val snapShotOffset: Int = 1 - - fun getContentCount(): Int = super.getItemCount() - - override fun getItemCount(): Int = super.getItemCount() + 2 - - override fun getItemViewType(position: Int): Int { - return when (position) { - 0 -> VIEW_TYPE_HEADER - itemCount - 1 -> VIEW_TYPE_LOADING - else -> VIEW_TYPE_CONTENT - } - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DecksAllViewHolder { - val view = when (viewType) { - VIEW_TYPE_HEADER -> LinearLayout(parent.context).apply { minimumHeight = 1 } - VIEW_TYPE_LOADING -> parent.inflate(R.layout.itemlist_loading) - else -> parent.inflate(R.layout.itemlist_deck) - } - return DecksAllViewHolder(view, itemClick, itemLongClick) - } - - override fun populateViewHolder(viewHolder: DecksAllViewHolder, model: FirebaseParsers.DeckParser?, position: Int) { - if (model != null) { - viewHolder.bind(model.toDeck("", true), privateInteractor) - } else { - Timber.d("Loading: " + synced) - viewHolder.itemView.loadingBar?.visibility = if (synced) View.GONE else View.VISIBLE - } - } - - override fun onSyncStatusChanged(synced: Boolean) { - this.synced = synced - notifyItemChanged(itemCount - 1) - if (synced) { - onSyncEnd?.invoke() - } - } - - override fun onArrayError(firebaseError: DatabaseError) { - Timber.d(firebaseError.toException(), firebaseError.toString()) - } - - } - } \ No newline at end of file diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt index 94c6b9b..16fb3d7 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt @@ -180,6 +180,8 @@ open class DecksPublicFragment : BaseFragment() { class DecksAllViewHolder(val view: View, val itemClick: (View, Deck) -> Unit, val itemLongClick: (View, Deck) -> Boolean) : RecyclerView.ViewHolder(view) { + constructor(view: View) : this(view, { view, deck -> }, { view, deck -> true }) + fun bind(deck: Deck, privateInteractor: PrivateInteractor) { itemView.setOnClickListener { itemClick(itemView, deck) } itemView.setOnLongClickListener { itemLongClick(itemView, deck) } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/firebase/FirebaseRecyclerViewAdapter.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/firebase/FirebaseRVAdapter.kt similarity index 93% rename from app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/firebase/FirebaseRecyclerViewAdapter.kt rename to app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/firebase/FirebaseRVAdapter.kt index 17138e3..8d4fd2d 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/firebase/FirebaseRecyclerViewAdapter.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/firebase/FirebaseRVAdapter.kt @@ -56,14 +56,14 @@ import java.lang.reflect.InvocationTargetException * } * } * - * FirebaseRecyclerViewAdapter adapter; + * FirebaseRVAdapter adapter; * ref = new Firebase("https://.firebaseio.com"); * * RecyclerView recycler = (RecyclerView) findViewById(R.id.messages_recycler); * recycler.setHasFixedSize(true); * recycler.setLayoutManager(new LinearLayoutManager(this)); * - * adapter = new FirebaseRecyclerViewAdapter(ChatMessage.class, android.R.layout.two_line_list_item, ChatMessageViewHolder.class, mRef) { + * adapter = new FirebaseRVAdapter(ChatMessage.class, android.R.layout.two_line_list_item, ChatMessageViewHolder.class, mRef) { * public void populateViewHolder(ChatMessageViewHolder chatMessageViewHolder, ChatMessage chatMessage) { * chatMessageViewHolder.nameText.setText(chatMessage.getName()); * chatMessageViewHolder.messageText.setText(chatMessage.getMessage()); @@ -78,8 +78,6 @@ import java.lang.reflect.InvocationTargetException * * * CONSTRUCTOR - * @param modelClass Firebase will marshall the data at a location into an instance of a class that you provide - * * * @param modelLayout This is the layout used to represent a single item in the list. You will be responsible for populating an * * instance of the corresponding view with the data from an instance of modelClass. * * @@ -89,9 +87,9 @@ import java.lang.reflect.InvocationTargetException * * * @param pageSize initial page size. set 0 for no limit. */ -abstract class FirebaseRecyclerViewAdapter( - var mModelClass: Class, +abstract class FirebaseRVAdapter( protected var mModelLayout: Int, + var mModel: Class, var mViewHolderClass: Class, ref: Query?, pageSize: Int = 0, @@ -153,7 +151,7 @@ abstract class FirebaseRecyclerViewAdapter( } fun getItem(position: Int): T { - return mSnapshots.getItem(position - snapShotOffset).getValue(mModelClass) + return mSnapshots.getItem(position - snapShotOffset).getValue(mModel) } fun getRef(position: Int): DatabaseReference { @@ -202,7 +200,7 @@ abstract class FirebaseRecyclerViewAdapter( * this class. The third argument is the item's position in the list. *

* Your implementation should populate the view using the data contained in the model. - * You should implement either this method or the other FirebaseRecyclerViewAdapter#populateViewHolder(VH, Object) method + * You should implement either this method or the other FirebaseRVAdapter#populateViewHolder(VH, Object) method * but not both. * * @param viewHolder The view to populate @@ -219,7 +217,7 @@ abstract class FirebaseRecyclerViewAdapter( * This is a backwards compatible version of populateViewHolder. * * - * You should implement either this method or the other FirebaseRecyclerViewAdapter#populateViewHolder(VH, T, int) method + * You should implement either this method or the other FirebaseRVAdapter#populateViewHolder(VH, T, int) method * but not both. * see FirebaseListAdapter#populateView(View, Object, int) * @param viewHolder The view to populate From ffb159fc582cee97cd841d8173f1ac6f56f2a10e Mon Sep 17 00:00:00 2001 From: EdipoSouza Date: Fri, 6 Jan 2017 18:44:41 -0200 Subject: [PATCH 05/54] Fix BaseAdsVHAdapter --- .../ui/base/BaseAdsFirebaseAdapter.kt | 69 ++++++++++++------- .../ui/base/BaseFirebaseRVAdapter.kt | 29 ++++---- .../ui/decks/tabs/DecksOwnerFragment.kt | 29 ++++++-- .../ui/decks/tabs/DecksPublicFragment.kt | 4 +- .../ui/util/firebase/FirebaseRVAdapter.kt | 53 +++----------- build.gradle | 2 +- 6 files changed, 96 insertions(+), 90 deletions(-) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseAdsFirebaseAdapter.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseAdsFirebaseAdapter.kt index bd10fa2..3234aa9 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseAdsFirebaseAdapter.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseAdsFirebaseAdapter.kt @@ -9,33 +9,31 @@ import com.ediposouza.teslesgendstracker.util.inflate import com.ediposouza.teslesgendstracker.util.load import com.google.android.gms.ads.AdView import com.google.android.gms.ads.NativeExpressAdView +import com.google.firebase.database.Query /** * Created by ediposouza on 08/12/16. */ -abstract class BaseAdsFirebaseAdapter(val adsEachItems: Int, @LayoutRes val adsLayout: Int) : - RecyclerView.Adapter() { +abstract class BaseAdsFirebaseAdapter(val adsEachItems: Int, + @LayoutRes val adsLayout: Int, + model: Class, + ref: Query?, + pageSize: Int) : + BaseFirebaseRVAdapter(model, ref, pageSize) { - companion object { + val VIEW_TYPE_ADS = 3 - const val ITEM_VIEW_TYPE_DEFAULT = 0 - const val ITEM_VIEW_TYPE_ADS = 1 - - } - - constructor(adsEachItems: Int, layoutManager: GridLayoutManager, - @LayoutRes adsLayout: Int) : this(adsEachItems, adsLayout) { + constructor(adsEachItems: Int, + layoutManager: GridLayoutManager, + @LayoutRes adsLayout: Int, + model: Class, + ref: Query?, + pageSize: Int) : this(adsEachItems, adsLayout, model, ref, pageSize) { onRestoreState(layoutManager) } - abstract fun onCreateDefaultViewHolder(parent: ViewGroup): RecyclerView.ViewHolder - abstract fun onBindDefaultViewHolder(holder: RecyclerView.ViewHolder?, position: Int) - abstract fun getDefaultItemCount(): Int - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { - if (viewType == ITEM_VIEW_TYPE_DEFAULT) { - return onCreateDefaultViewHolder(parent) - } else { + if (viewType == VIEW_TYPE_ADS) { val adsItemView = parent.inflate(adsLayout) val ads = adsItemView.findViewById(R.id.ads_view) when (ads) { @@ -44,21 +42,40 @@ abstract class BaseAdsFirebaseAdapter(val adsEachItems: Int, @LayoutRes val adsL } return object : RecyclerView.ViewHolder(adsItemView) {} } + return super.onCreateViewHolder(parent, viewType) } - override fun onBindViewHolder(holder: RecyclerView.ViewHolder?, position: Int) { - val qtdAdsBefore = getAdsQtdBeforePosition(position) - if (getItemViewType(position) == ITEM_VIEW_TYPE_DEFAULT) { - onBindDefaultViewHolder(holder, position - qtdAdsBefore) + override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder, position: Int) { + val adsQtdBeforePosition = getAdsQtdBeforePosition(position) + var model: T? = null + val arrayPosition = position - snapShotOffset + if (arrayPosition < getContentCount() + adsQtdBeforePosition && arrayPosition >= 0) { + model = getItem(position - adsQtdBeforePosition) } + populateViewHolder(viewHolder, model, position) } - override fun getItemCount(): Int = getDefaultItemCount() + getDefaultItemCount().div(adsEachItems) + @Suppress("UNCHECKED_CAST") + override fun populateViewHolder(viewHolder: RecyclerView.ViewHolder, model: T?, position: Int) { + if (getItemViewType(position) != VIEW_TYPE_ADS) { + super.populateViewHolder(viewHolder, model) + } + } + + override fun getItemCount(): Int { + val contentItemCount = super.getContentCount() + val adsContentItemCount = contentItemCount + if (adsEachItems > 0) contentItemCount.div(adsEachItems) else 0 + return adsContentItemCount + 2 + } override fun getItemViewType(position: Int): Int { - val qtdAdsBefore = getAdsQtdBeforePosition(position) - val nextEachItems = adsEachItems * (qtdAdsBefore + 1) + qtdAdsBefore - return if (position == nextEachItems) ITEM_VIEW_TYPE_ADS else ITEM_VIEW_TYPE_DEFAULT + var viewType = super.getItemViewType(position) + if (viewType == VIEW_TYPE_CONTENT) { + val qtdAdsBefore = getAdsQtdBeforePosition(position) + val nextEachItems = adsEachItems * (qtdAdsBefore + 1) + qtdAdsBefore + viewType = if (position == nextEachItems) VIEW_TYPE_ADS else VIEW_TYPE_CONTENT + } + return viewType } private fun getAdsQtdBeforePosition(position: Int): Int { @@ -75,7 +92,7 @@ abstract class BaseAdsFirebaseAdapter(val adsEachItems: Int, @LayoutRes val adsL spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { override fun getSpanSize(position: Int): Int { val itemType = getItemViewType(position) - return if (itemType == ITEM_VIEW_TYPE_ADS) spanCount else 1 + return if (itemType == VIEW_TYPE_ADS) spanCount else 1 } } } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseFirebaseRVAdapter.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseFirebaseRVAdapter.kt index 583cf47..d0f1465 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseFirebaseRVAdapter.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseFirebaseRVAdapter.kt @@ -1,6 +1,5 @@ package com.ediposouza.teslesgendstracker.ui.base -import android.support.annotation.IntegerRes import android.support.v7.widget.RecyclerView import android.view.View import android.view.ViewGroup @@ -16,17 +15,16 @@ import timber.log.Timber /** * Created by EdipoSouza on 1/2/17. */ -abstract class BaseFirebaseRVAdapter(@IntegerRes val layout: Int, - model: Class, - val viewHolder: Class, - ref: Query?, pageSize: Int) : - FirebaseRVAdapter(layout, model, viewHolder, ref, pageSize, false) { +abstract class BaseFirebaseRVAdapter(model: Class, + ref: Query?, + pageSize: Int) : + FirebaseRVAdapter(model, ref, pageSize, false) { - var VIEW_TYPE_HEADER = 0 - var VIEW_TYPE_CONTENT = 1 - var VIEW_TYPE_LOADING = 2 + protected var VIEW_TYPE_HEADER = 0 + protected var VIEW_TYPE_CONTENT = 1 + protected var VIEW_TYPE_LOADING = 2 - private var synced: Boolean = false + protected var synced: Boolean = false override val snapShotOffset: Int = 1 @@ -34,7 +32,7 @@ abstract class BaseFirebaseRVAdapter(@IntegerRe abstract fun onBindContentHolder(model: T, viewHolder: VH) abstract fun onSyncEnd() - fun getContentCount(): Int = super.getItemCount() + override fun getContentCount(): Int = super.getItemCount() override fun getItemCount(): Int = super.getItemCount() + 2 @@ -46,20 +44,21 @@ abstract class BaseFirebaseRVAdapter(@IntegerRe } } - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { when (viewType) { VIEW_TYPE_CONTENT -> return onCreateDefaultViewHolder(parent) else -> { val view = if (viewType == VIEW_TYPE_LOADING) parent.inflate(R.layout.itemlist_loading) else LinearLayout(parent.context).apply { minimumHeight = 1 } - return viewHolder.getConstructor(View::class.java).newInstance(view) + return object : RecyclerView.ViewHolder(view) {} } } } - override fun populateViewHolder(viewHolder: VH, model: T?) { + @Suppress("UNCHECKED_CAST") + override fun populateViewHolder(viewHolder: RecyclerView.ViewHolder, model: T?) { if (model != null) { - onBindContentHolder(model, viewHolder) + onBindContentHolder(model, viewHolder as VH) } else { viewHolder.itemView.loadingBar?.visibility = if (synced) View.GONE else View.VISIBLE } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksOwnerFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksOwnerFragment.kt index eb6e42a..0c41f29 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksOwnerFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksOwnerFragment.kt @@ -10,7 +10,7 @@ import com.ediposouza.teslesgendstracker.data.Class import com.ediposouza.teslesgendstracker.data.Deck import com.ediposouza.teslesgendstracker.interactor.FirebaseParsers import com.ediposouza.teslesgendstracker.interactor.PrivateInteractor -import com.ediposouza.teslesgendstracker.ui.base.BaseFirebaseRVAdapter +import com.ediposouza.teslesgendstracker.ui.base.BaseAdsFirebaseAdapter import com.ediposouza.teslesgendstracker.util.inflate import kotlinx.android.synthetic.main.fragment_decks_list.* import timber.log.Timber @@ -25,9 +25,10 @@ class DecksOwnerFragment : DecksPublicFragment() { private val privateInteractor = PrivateInteractor() private var onlyPrivate: Switch? = null - val ownerDecksAdapter = object : BaseFirebaseRVAdapter( - R.layout.itemlist_deck, FirebaseParsers.DeckParser::class.java, DecksAllViewHolder::class.java, + val ownerDecksAdapter = object : BaseAdsFirebaseAdapter( + ADS_EACH_ITEMS, R.layout.itemlist_deck_ads, FirebaseParsers.DeckParser::class.java, privateInteractor.getOwnedPrivateDecksRef(), DECK_PAGE_SIZE) { + override fun onCreateDefaultViewHolder(parent: ViewGroup): DecksAllViewHolder { return DecksAllViewHolder(parent.inflate(R.layout.itemlist_deck), itemClick, itemLongClick) } @@ -37,11 +38,27 @@ class DecksOwnerFragment : DecksPublicFragment() { } override fun onSyncEnd() { - decks_refresh_layout.isRefreshing = false + decks_refresh_layout?.isRefreshing = false } } +// val ownerDecksAdapter = object : BaseFirebaseRVAdapter( +// FirebaseParsers.DeckParser::class.java, privateInteractor.getOwnedPrivateDecksRef(), DECK_PAGE_SIZE) { +// override fun onCreateDefaultViewHolder(parent: ViewGroup): DecksAllViewHolder { +// return DecksAllViewHolder(parent.inflate(R.layout.itemlist_deck), itemClick, itemLongClick) +// } +// +// override fun onBindContentHolder(model: FirebaseParsers.DeckParser, viewHolder: DecksAllViewHolder) { +// viewHolder.bind(model.toDeck("", true), privateInteractor) +// } +// +// override fun onSyncEnd() { +// decks_refresh_layout?.isRefreshing = false +// } +// +// } + override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { return container?.inflate(R.layout.fragment_decks_list_owner) } @@ -65,6 +82,10 @@ class DecksOwnerFragment : DecksPublicFragment() { configLoggedViews() } + override fun showDecks() { + ownerDecksAdapter.reset() + } + override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { menu?.clear() inflater?.inflate(R.menu.menu_decks_owned, menu) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt index 16fb3d7..2e382e3 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt @@ -37,7 +37,7 @@ import java.util.* */ open class DecksPublicFragment : BaseFragment() { - val ADS_EACH_ITEMS = 15 //after 15 lines + val ADS_EACH_ITEMS = 5 //after 15 lines val DECK_PAGE_SIZE = 7 val RC_DECK = 123 @@ -120,7 +120,7 @@ open class DecksPublicFragment : BaseFragment() { } } - fun showDecks() { + open fun showDecks() { Timber.d("Classes: %s", currentClasses.toSet()) decksAdapter.clearItems() for (i in currentClasses.indices) { diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/firebase/FirebaseRVAdapter.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/firebase/FirebaseRVAdapter.kt index 8d4fd2d..5a0cdae 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/firebase/FirebaseRVAdapter.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/firebase/FirebaseRVAdapter.kt @@ -29,13 +29,9 @@ package com.ediposouza.teslesgendstracker.ui.util.firebase import android.support.v7.widget.RecyclerView -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup import com.google.firebase.database.DatabaseError import com.google.firebase.database.DatabaseReference import com.google.firebase.database.Query -import java.lang.reflect.InvocationTargetException /** * This class is a generic way of backing an RecyclerView with a Firebase location. @@ -88,12 +84,7 @@ import java.lang.reflect.InvocationTargetException * @param pageSize initial page size. set 0 for no limit. */ abstract class FirebaseRVAdapter( - protected var mModelLayout: Int, - var mModel: Class, - var mViewHolderClass: Class, - ref: Query?, - pageSize: Int = 0, - orderASC: Boolean = false) : RecyclerView.Adapter() { + var mModel: Class, ref: Query?, pageSize: Int = 0, orderASC: Boolean = false) : RecyclerView.Adapter() { var mSnapshots: FirebaseArray = FirebaseArray(ref, pageSize, orderASC) @@ -142,53 +133,31 @@ abstract class FirebaseRVAdapter( */ open val snapShotOffset: Int = 0 + open fun getContentCount(): Int = mSnapshots.count + /** * Override when adding headers or footers * @return number of items including headers and footers. */ - override fun getItemCount(): Int { - return mSnapshots.count - } + override fun getItemCount(): Int = mSnapshots.count - fun getItem(position: Int): T { - return mSnapshots.getItem(position - snapShotOffset).getValue(mModel) - } + open fun getItem(position: Int): T = mSnapshots.getItem(position - snapShotOffset).getValue(mModel) - fun getRef(position: Int): DatabaseReference { - return mSnapshots.getItem(position).ref - } + fun getRef(position: Int): DatabaseReference = mSnapshots.getItem(position).ref override fun getItemId(position: Int): Long { - if (position < snapShotOffset) return ("header" + position).hashCode().toLong() - if (position >= snapShotOffset + mSnapshots.count) return ("footer" + (position - (snapShotOffset + mSnapshots.count))).hashCode().toLong() + if (position < snapShotOffset) + return ("header" + position).hashCode().toLong() + if (position >= snapShotOffset + mSnapshots.count) + return ("footer" + (position - (snapShotOffset + mSnapshots.count))).hashCode().toLong() // http://stackoverflow.com/questions/5100071/whats-the-purpose-of-item-ids-in-android-listview-adapter return mSnapshots.getItem(position).key.hashCode().toLong() } - /** - * Override when adding headers or footers. - */ - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH { - val view = LayoutInflater.from(parent.context).inflate(mModelLayout, parent, false) as ViewGroup - try { - val constructor = mViewHolderClass.getConstructor(View::class.java) - return constructor.newInstance(view) - } catch (e: NoSuchMethodException) { - throw RuntimeException(e) - } catch (e: InvocationTargetException) { - throw RuntimeException(e) - } catch (e: InstantiationException) { - throw RuntimeException(e) - } catch (e: IllegalAccessException) { - throw RuntimeException(e) - } - - } - override fun onBindViewHolder(viewHolder: VH, position: Int) { var model: T? = null val arrayPosition = position - snapShotOffset - if (arrayPosition < mSnapshots.count && arrayPosition >= 0) { + if (arrayPosition < getContentCount() && arrayPosition >= 0) { model = getItem(position) } populateViewHolder(viewHolder, model, position) diff --git a/build.gradle b/build.gradle index 4208edb..b74c4f3 100644 --- a/build.gradle +++ b/build.gradle @@ -16,7 +16,7 @@ buildscript { } } dependencies { - classpath 'com.android.tools.build:gradle:2.2.3' + classpath 'com.android.tools.build:gradle:2.3.0-beta1' classpath "com.google.gms:google-services:3.0.0" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "io.fabric.tools:gradle:1.22.0" From 5c05b13019a2d56754ca64e7c3c26d938018aeb4 Mon Sep 17 00:00:00 2001 From: EdipoSouza Date: Fri, 6 Jan 2017 18:51:19 -0200 Subject: [PATCH 06/54] Add itemKey param on Bind. Add DeckOwnerPublic. Create OnLinearLayoutItemScrolled --- .../interactor/PrivateInteractor.kt | 14 +++- .../ui/base/BaseAdsFirebaseAdapter.kt | 8 +- .../ui/base/BaseFirebaseRVAdapter.kt | 8 +- .../ui/decks/tabs/DecksOwnerFragment.kt | 80 +++++++++---------- .../ui/decks/tabs/DecksPublicFragment.kt | 4 +- .../ui/util/firebase/FirebaseRVAdapter.kt | 12 ++- .../firebase/OnLinearLayoutItemScrolled.kt | 21 +++++ 7 files changed, 90 insertions(+), 57 deletions(-) create mode 100644 app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/firebase/OnLinearLayoutItemScrolled.kt diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt index a159236..f444ce0 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt @@ -166,8 +166,13 @@ class PrivateInteractor : BaseInteractor() { } } + fun getOwnedPublicDecksRef() = dbDecks.child(NODE_DECKS_PUBLIC) + .orderByChild(KEY_DECK_OWNER).equalTo(getUserID())?.apply { + keepSynced() + } + private fun getOwnedPublicDecks(cls: Class?, onSuccess: (List) -> Unit) { - with(dbDecks.child(NODE_DECKS_PUBLIC).orderByChild(KEY_DECK_OWNER).equalTo(getUserID())) { + getOwnedPublicDecksRef()?.apply { keepSynced() addListenerForSingleValueEvent(object : ValueEventListener { @@ -188,10 +193,13 @@ class PrivateInteractor : BaseInteractor() { } } - fun getOwnedPrivateDecksRef() = dbUser()?.child(NODE_DECKS)?.child(NODE_DECKS_PRIVATE)?.orderByChild(KEY_DECK_UPDATE_AT) + fun getOwnedPrivateDecksRef() = dbUser()?.child(NODE_DECKS)?.child(NODE_DECKS_PRIVATE) + ?.orderByChild(KEY_DECK_UPDATE_AT)?.apply { + keepSynced() + } private fun getOwnedPrivateDecks(cls: Class?, onSuccess: (List) -> Unit) { - dbUser()?.child(NODE_DECKS)?.child(NODE_DECKS_PRIVATE)?.orderByChild(KEY_DECK_UPDATE_AT)?.apply { + getOwnedPrivateDecksRef()?.apply { keepSynced() addListenerForSingleValueEvent(object : ValueEventListener { diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseAdsFirebaseAdapter.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseAdsFirebaseAdapter.kt index 3234aa9..43e3512 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseAdsFirebaseAdapter.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseAdsFirebaseAdapter.kt @@ -47,18 +47,20 @@ abstract class BaseAdsFirebaseAdapter(val adsEa override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder, position: Int) { val adsQtdBeforePosition = getAdsQtdBeforePosition(position) + var itemKey: String? = null var model: T? = null val arrayPosition = position - snapShotOffset if (arrayPosition < getContentCount() + adsQtdBeforePosition && arrayPosition >= 0) { + itemKey = getItemKey(position - adsQtdBeforePosition) model = getItem(position - adsQtdBeforePosition) } - populateViewHolder(viewHolder, model, position) + populateViewHolder(itemKey, viewHolder, model, position) } @Suppress("UNCHECKED_CAST") - override fun populateViewHolder(viewHolder: RecyclerView.ViewHolder, model: T?, position: Int) { + override fun populateViewHolder(itemKey: String?, viewHolder: RecyclerView.ViewHolder, model: T?, position: Int) { if (getItemViewType(position) != VIEW_TYPE_ADS) { - super.populateViewHolder(viewHolder, model) + super.populateViewHolder(itemKey, viewHolder, model) } } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseFirebaseRVAdapter.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseFirebaseRVAdapter.kt index d0f1465..019b7d1 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseFirebaseRVAdapter.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseFirebaseRVAdapter.kt @@ -29,7 +29,7 @@ abstract class BaseFirebaseRVAdapter(model: Cla override val snapShotOffset: Int = 1 abstract fun onCreateDefaultViewHolder(parent: ViewGroup): VH - abstract fun onBindContentHolder(model: T, viewHolder: VH) + abstract fun onBindContentHolder(itemKey: String, model: T, viewHolder: VH) abstract fun onSyncEnd() override fun getContentCount(): Int = super.getItemCount() @@ -56,9 +56,9 @@ abstract class BaseFirebaseRVAdapter(model: Cla } @Suppress("UNCHECKED_CAST") - override fun populateViewHolder(viewHolder: RecyclerView.ViewHolder, model: T?) { - if (model != null) { - onBindContentHolder(model, viewHolder as VH) + override fun populateViewHolder(itemKey: String?, viewHolder: RecyclerView.ViewHolder, model: T?) { + if (itemKey != null && model != null) { + onBindContentHolder(itemKey, model, viewHolder as VH) } else { viewHolder.itemView.loadingBar?.visibility = if (synced) View.GONE else View.VISIBLE } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksOwnerFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksOwnerFragment.kt index 0c41f29..c7fec78 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksOwnerFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksOwnerFragment.kt @@ -1,19 +1,17 @@ package com.ediposouza.teslesgendstracker.ui.decks.tabs import android.os.Bundle -import android.support.v7.widget.LinearLayoutManager -import android.support.v7.widget.RecyclerView import android.view.* import android.widget.Switch import com.ediposouza.teslesgendstracker.R import com.ediposouza.teslesgendstracker.data.Class -import com.ediposouza.teslesgendstracker.data.Deck import com.ediposouza.teslesgendstracker.interactor.FirebaseParsers import com.ediposouza.teslesgendstracker.interactor.PrivateInteractor import com.ediposouza.teslesgendstracker.ui.base.BaseAdsFirebaseAdapter +import com.ediposouza.teslesgendstracker.ui.util.firebase.FirebaseRVAdapter +import com.ediposouza.teslesgendstracker.ui.util.firebase.OnLinearLayoutItemScrolled import com.ediposouza.teslesgendstracker.util.inflate import kotlinx.android.synthetic.main.fragment_decks_list.* -import timber.log.Timber /** * Created by EdipoSouza on 11/18/16. @@ -25,7 +23,7 @@ class DecksOwnerFragment : DecksPublicFragment() { private val privateInteractor = PrivateInteractor() private var onlyPrivate: Switch? = null - val ownerDecksAdapter = object : BaseAdsFirebaseAdapter( + val ownerPrivateDecksAdapter = object : BaseAdsFirebaseAdapter( ADS_EACH_ITEMS, R.layout.itemlist_deck_ads, FirebaseParsers.DeckParser::class.java, privateInteractor.getOwnedPrivateDecksRef(), DECK_PAGE_SIZE) { @@ -33,8 +31,8 @@ class DecksOwnerFragment : DecksPublicFragment() { return DecksAllViewHolder(parent.inflate(R.layout.itemlist_deck), itemClick, itemLongClick) } - override fun onBindContentHolder(model: FirebaseParsers.DeckParser, viewHolder: DecksAllViewHolder) { - viewHolder.bind(model.toDeck("", true), privateInteractor) + override fun onBindContentHolder(itemKey: String, model: FirebaseParsers.DeckParser, viewHolder: DecksAllViewHolder) { + viewHolder.bind(model.toDeck(itemKey, true), privateInteractor) } override fun onSyncEnd() { @@ -43,21 +41,23 @@ class DecksOwnerFragment : DecksPublicFragment() { } -// val ownerDecksAdapter = object : BaseFirebaseRVAdapter( -// FirebaseParsers.DeckParser::class.java, privateInteractor.getOwnedPrivateDecksRef(), DECK_PAGE_SIZE) { -// override fun onCreateDefaultViewHolder(parent: ViewGroup): DecksAllViewHolder { -// return DecksAllViewHolder(parent.inflate(R.layout.itemlist_deck), itemClick, itemLongClick) -// } -// -// override fun onBindContentHolder(model: FirebaseParsers.DeckParser, viewHolder: DecksAllViewHolder) { -// viewHolder.bind(model.toDeck("", true), privateInteractor) -// } -// -// override fun onSyncEnd() { -// decks_refresh_layout?.isRefreshing = false -// } -// -// } + val ownerPublicDecksAdapter = object : BaseAdsFirebaseAdapter( + ADS_EACH_ITEMS, R.layout.itemlist_deck_ads, FirebaseParsers.DeckParser::class.java, + privateInteractor.getOwnedPublicDecksRef(), DECK_PAGE_SIZE) { + + override fun onCreateDefaultViewHolder(parent: ViewGroup): DecksAllViewHolder { + return DecksAllViewHolder(parent.inflate(R.layout.itemlist_deck), itemClick, itemLongClick) + } + + override fun onBindContentHolder(itemKey: String, model: FirebaseParsers.DeckParser, viewHolder: DecksAllViewHolder) { + viewHolder.bind(model.toDeck(itemKey, false), privateInteractor) + } + + override fun onSyncEnd() { + decks_refresh_layout?.isRefreshing = false + } + + } override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { return container?.inflate(R.layout.fragment_decks_list_owner) @@ -65,41 +65,39 @@ class DecksOwnerFragment : DecksPublicFragment() { override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - decks_recycler_view.adapter = ownerDecksAdapter - decks_recycler_view.addOnScrollListener(object : RecyclerView.OnScrollListener() { - override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) { - if (dy < 0) { - return - } - val layoutManager = recyclerView?.layoutManager as LinearLayoutManager - if (layoutManager.findLastVisibleItemPosition() >= ownerDecksAdapter.getContentCount() - 3) { - ownerDecksAdapter.more() - } + decks_recycler_view.adapter = ownerPrivateDecksAdapter + with(decks_recycler_view.adapter as FirebaseRVAdapter<*, *>) { + decks_recycler_view.addOnScrollListener(OnLinearLayoutItemScrolled(getContentCount() - 3) { + more() + }) + decks_refresh_layout.setOnRefreshListener { + reset() } - }) - decks_refresh_layout.setOnRefreshListener { ownerDecksAdapter.reset() } + } setHasOptionsMenu(true) configLoggedViews() } override fun showDecks() { - ownerDecksAdapter.reset() + (decks_recycler_view.adapter as FirebaseRVAdapter<*, *>).reset() } override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { menu?.clear() inflater?.inflate(R.menu.menu_decks_owned, menu) onlyPrivate = menu?.findItem(R.id.menu_only_private)?.actionView as Switch - onlyPrivate?.setOnCheckedChangeListener { button, checked -> showDecks() } + onlyPrivate?.setOnCheckedChangeListener { button, checked -> + decks_recycler_view.adapter = if (checked) ownerPublicDecksAdapter else ownerPrivateDecksAdapter + } super.onCreateOptionsMenu(menu, inflater) } override fun getDecks(cls: Class?, last: Boolean) { - privateInteractor.getOwnedDecks(cls, { - val decksToShow = if (onlyPrivate?.isChecked ?: false) it.filter(Deck::private) else it - decksToShow.forEach { Timber.d("Decks: %s", it.toString()) } - decksAdapter.showDecks(decksToShow, last) - }) +// privateInteractor.getOwnedDecks(cls, { +// val decksToShow = if (onlyPrivate?.isChecked ?: false) it.filter(Deck::private) else it +// decksToShow.forEach { Timber.d("Decks: %s", it.toString()) } +// decksAdapter.showDecks(decksToShow, last) +// }) } } \ No newline at end of file diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt index 2e382e3..87a1e45 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt @@ -37,8 +37,8 @@ import java.util.* */ open class DecksPublicFragment : BaseFragment() { - val ADS_EACH_ITEMS = 5 //after 15 lines - val DECK_PAGE_SIZE = 7 + val ADS_EACH_ITEMS = 10 //after 10 lines + val DECK_PAGE_SIZE = 8 val RC_DECK = 123 protected val publicInteractor = PublicInteractor() diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/firebase/FirebaseRVAdapter.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/firebase/FirebaseRVAdapter.kt index 5a0cdae..0da6e42 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/firebase/FirebaseRVAdapter.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/firebase/FirebaseRVAdapter.kt @@ -143,6 +143,8 @@ abstract class FirebaseRVAdapter( open fun getItem(position: Int): T = mSnapshots.getItem(position - snapShotOffset).getValue(mModel) + fun getItemKey(position: Int): String = mSnapshots.getItem(position - snapShotOffset).key + fun getRef(position: Int): DatabaseReference = mSnapshots.getItem(position).ref override fun getItemId(position: Int): Long { @@ -155,12 +157,14 @@ abstract class FirebaseRVAdapter( } override fun onBindViewHolder(viewHolder: VH, position: Int) { + var itemKey: String? = null var model: T? = null val arrayPosition = position - snapShotOffset if (arrayPosition < getContentCount() && arrayPosition >= 0) { + itemKey = getItemKey(position) model = getItem(position) } - populateViewHolder(viewHolder, model, position) + populateViewHolder(itemKey, viewHolder, model, position) } /** @@ -178,8 +182,8 @@ abstract class FirebaseRVAdapter( * * * @param position The position in the list of the view being populated */ - open protected fun populateViewHolder(viewHolder: VH, model: T?, position: Int) { - populateViewHolder(viewHolder, model) + open protected fun populateViewHolder(itemKey: String?, viewHolder: VH, model: T?, position: Int) { + populateViewHolder(itemKey, viewHolder, model) } /** @@ -193,7 +197,7 @@ abstract class FirebaseRVAdapter( * * * @param model The object containing the data used to populate the view */ - open protected fun populateViewHolder(viewHolder: VH, model: T?) { + open protected fun populateViewHolder(itemKey: String?, viewHolder: VH, model: T?) { } open protected fun onSyncStatusChanged(synced: Boolean) {} diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/firebase/OnLinearLayoutItemScrolled.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/firebase/OnLinearLayoutItemScrolled.kt new file mode 100644 index 0000000..faa3cff --- /dev/null +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/firebase/OnLinearLayoutItemScrolled.kt @@ -0,0 +1,21 @@ +package com.ediposouza.teslesgendstracker.ui.util.firebase + +import android.support.v7.widget.LinearLayoutManager +import android.support.v7.widget.RecyclerView + +/** + * Created by ediposouza on 04/01/17. + */ +class OnLinearLayoutItemScrolled(val itemPosition: Int, val onViewItem: () -> Unit) : RecyclerView.OnScrollListener() { + + override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) { + if (dy < 0) { + return + } + val layoutManager = recyclerView?.layoutManager as LinearLayoutManager + if (layoutManager.findLastVisibleItemPosition() >= itemPosition) { + onViewItem() + } + } + +} \ No newline at end of file From 42e7b973ff208369ef3dc7ecf3c0012cd067e509 Mon Sep 17 00:00:00 2001 From: EdipoSouza Date: Fri, 6 Jan 2017 18:54:28 -0200 Subject: [PATCH 07/54] Firebase adapter ref Dynamically and add filter option --- .../interactor/PrivateInteractor.kt | 52 ------------------- .../ui/base/BaseAdsAdapter.kt | 4 +- .../ui/base/BaseAdsFirebaseAdapter.kt | 16 +++--- .../ui/base/BaseFirebaseRVAdapter.kt | 8 +-- .../ui/cards/tabs/CardsAllFragment.kt | 2 +- .../ui/cards/tabs/CardsCollectionFragment.kt | 2 +- .../ui/decks/tabs/DecksOwnerFragment.kt | 44 ++++------------ .../ui/decks/tabs/DecksPublicFragment.kt | 2 + .../ui/util/firebase/FirebaseArray.kt | 16 +++--- .../ui/util/firebase/FirebaseRVAdapter.kt | 25 +++++---- 10 files changed, 56 insertions(+), 115 deletions(-) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt index f444ce0..31ed242 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt @@ -157,68 +157,16 @@ class PrivateInteractor : BaseInteractor() { } } - fun getOwnedDecks(cls: Class?, onSuccess: (List) -> Unit) { - getOwnedPublicDecks(cls) { - val decks = it - getOwnedPrivateDecks(cls) { - onSuccess.invoke(decks.plus(it)) - } - } - } - fun getOwnedPublicDecksRef() = dbDecks.child(NODE_DECKS_PUBLIC) .orderByChild(KEY_DECK_OWNER).equalTo(getUserID())?.apply { keepSynced() } - private fun getOwnedPublicDecks(cls: Class?, onSuccess: (List) -> Unit) { - getOwnedPublicDecksRef()?.apply { - keepSynced() - addListenerForSingleValueEvent(object : ValueEventListener { - - override fun onDataChange(ds: DataSnapshot) { - Timber.d(ds.value?.toString()) - val decks = ds.children.mapTo(arrayListOf()) { - it.getValue(FirebaseParsers.DeckParser::class.java).toDeck(it.key, false) - }.filter { cls == null || it.cls == cls } - Timber.d(decks.toString()) - onSuccess.invoke(decks) - } - - override fun onCancelled(de: DatabaseError) { - Timber.d("Fail: " + de.message) - } - - }) - } - } - fun getOwnedPrivateDecksRef() = dbUser()?.child(NODE_DECKS)?.child(NODE_DECKS_PRIVATE) ?.orderByChild(KEY_DECK_UPDATE_AT)?.apply { keepSynced() } - private fun getOwnedPrivateDecks(cls: Class?, onSuccess: (List) -> Unit) { - getOwnedPrivateDecksRef()?.apply { - keepSynced() - addListenerForSingleValueEvent(object : ValueEventListener { - - override fun onDataChange(ds: DataSnapshot) { - val decks = ds.children.mapTo(arrayListOf()) { - it.getValue(FirebaseParsers.DeckParser::class.java).toDeck(it.key, true) - }.filter { cls == null || it.cls == cls } - Timber.d(decks.toString()) - onSuccess.invoke(decks) - } - - override fun onCancelled(de: DatabaseError) { - Timber.d("Fail: " + de.message) - } - - }) - } - } - fun getFavoriteDecks(cls: Class?, onSuccess: (List?) -> Unit) { PublicInteractor().getPublicDecks(cls) { val publicDecks = it diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseAdsAdapter.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseAdsAdapter.kt index f3faeb9..3e02ad8 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseAdsAdapter.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseAdsAdapter.kt @@ -23,8 +23,8 @@ abstract class BaseAdsAdapter(val adsEachItems: Int, @LayoutRes val adsLayout: I } - constructor(adsEachItems: Int, layoutManager: GridLayoutManager, - @LayoutRes adsLayout: Int) : this(adsEachItems, adsLayout) { + constructor(adsEachItems: Int, @LayoutRes adsLayout: Int, + layoutManager: GridLayoutManager) : this(adsEachItems, adsLayout) { onRestoreState(layoutManager) } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseAdsFirebaseAdapter.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseAdsFirebaseAdapter.kt index 43e3512..943ae66 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseAdsFirebaseAdapter.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseAdsFirebaseAdapter.kt @@ -14,12 +14,14 @@ import com.google.firebase.database.Query /** * Created by ediposouza on 08/12/16. */ -abstract class BaseAdsFirebaseAdapter(val adsEachItems: Int, +abstract class BaseAdsFirebaseAdapter(model: Class, + ref: () -> Query?, + pageSize: Int, + val adsEachItems: Int, @LayoutRes val adsLayout: Int, - model: Class, - ref: Query?, - pageSize: Int) : - BaseFirebaseRVAdapter(model, ref, pageSize) { + orderASC: Boolean = false, + filter: ((T) -> Boolean)? = null) : + BaseFirebaseRVAdapter(model, ref, pageSize, orderASC, filter) { val VIEW_TYPE_ADS = 3 @@ -27,8 +29,8 @@ abstract class BaseAdsFirebaseAdapter(val adsEa layoutManager: GridLayoutManager, @LayoutRes adsLayout: Int, model: Class, - ref: Query?, - pageSize: Int) : this(adsEachItems, adsLayout, model, ref, pageSize) { + ref: () -> Query?, + pageSize: Int) : this(model, ref, pageSize, adsEachItems, adsLayout) { onRestoreState(layoutManager) } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseFirebaseRVAdapter.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseFirebaseRVAdapter.kt index 019b7d1..3a59d9c 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseFirebaseRVAdapter.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseFirebaseRVAdapter.kt @@ -16,9 +16,11 @@ import timber.log.Timber * Created by EdipoSouza on 1/2/17. */ abstract class BaseFirebaseRVAdapter(model: Class, - ref: Query?, - pageSize: Int) : - FirebaseRVAdapter(model, ref, pageSize, false) { + ref: () -> Query?, + pageSize: Int, + orderASC: Boolean = false, + filter: ((T) -> Boolean)? = null) : + FirebaseRVAdapter(model, ref, pageSize, orderASC, filter) { protected var VIEW_TYPE_HEADER = 0 protected var VIEW_TYPE_CONTENT = 1 diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/tabs/CardsAllFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/tabs/CardsAllFragment.kt index 9f8e6bf..4d3774b 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/tabs/CardsAllFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/tabs/CardsAllFragment.kt @@ -242,7 +242,7 @@ open class CardsAllFragment : BaseFragment() { class CardsAllAdapter(adsEachItems: Int, layoutManager: GridLayoutManager, @LayoutRes adsLayout: Int, @DimenRes val cardHeight: Int, val itemClick: (View, Card) -> Unit, - val itemLongClick: (View, Card) -> Boolean) : BaseAdsAdapter(adsEachItems, layoutManager, adsLayout) { + val itemLongClick: (View, Card) -> Boolean) : BaseAdsAdapter(adsEachItems, adsLayout, layoutManager) { var items: List = ArrayList() diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/tabs/CardsCollectionFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/tabs/CardsCollectionFragment.kt index 1ec7344..e86dc2f 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/tabs/CardsCollectionFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/tabs/CardsCollectionFragment.kt @@ -114,7 +114,7 @@ class CardsCollectionFragment : CardsAllFragment() { class CardsCollectionAdapter(adsEachItems: Int, layoutManager: GridLayoutManager, @LayoutRes adsLayout: Int, val itemClick: (CardSlot) -> Unit, - val itemLongClick: (View, Card) -> Boolean) : BaseAdsAdapter(adsEachItems, layoutManager, adsLayout) { + val itemLongClick: (View, Card) -> Boolean) : BaseAdsAdapter(adsEachItems, adsLayout, layoutManager) { var items: ArrayList = ArrayList() diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksOwnerFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksOwnerFragment.kt index c7fec78..a160c6b 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksOwnerFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksOwnerFragment.kt @@ -4,7 +4,6 @@ import android.os.Bundle import android.view.* import android.widget.Switch import com.ediposouza.teslesgendstracker.R -import com.ediposouza.teslesgendstracker.data.Class import com.ediposouza.teslesgendstracker.interactor.FirebaseParsers import com.ediposouza.teslesgendstracker.interactor.PrivateInteractor import com.ediposouza.teslesgendstracker.ui.base.BaseAdsFirebaseAdapter @@ -23,34 +22,21 @@ class DecksOwnerFragment : DecksPublicFragment() { private val privateInteractor = PrivateInteractor() private var onlyPrivate: Switch? = null - val ownerPrivateDecksAdapter = object : BaseAdsFirebaseAdapter( - ADS_EACH_ITEMS, R.layout.itemlist_deck_ads, FirebaseParsers.DeckParser::class.java, - privateInteractor.getOwnedPrivateDecksRef(), DECK_PAGE_SIZE) { - - override fun onCreateDefaultViewHolder(parent: ViewGroup): DecksAllViewHolder { - return DecksAllViewHolder(parent.inflate(R.layout.itemlist_deck), itemClick, itemLongClick) - } - - override fun onBindContentHolder(itemKey: String, model: FirebaseParsers.DeckParser, viewHolder: DecksAllViewHolder) { - viewHolder.bind(model.toDeck(itemKey, true), privateInteractor) - } - - override fun onSyncEnd() { - decks_refresh_layout?.isRefreshing = false - } - + private val dataRef = { + if (onlyPrivate?.isChecked ?: false) + privateInteractor.getOwnedPrivateDecksRef() else privateInteractor.getOwnedPublicDecksRef() } - val ownerPublicDecksAdapter = object : BaseAdsFirebaseAdapter( - ADS_EACH_ITEMS, R.layout.itemlist_deck_ads, FirebaseParsers.DeckParser::class.java, - privateInteractor.getOwnedPublicDecksRef(), DECK_PAGE_SIZE) { + val ownerDecksAdapter = object : BaseAdsFirebaseAdapter( + FirebaseParsers.DeckParser::class.java, dataRef, DECK_PAGE_SIZE, ADS_EACH_ITEMS, + R.layout.itemlist_deck_ads, false, { currentClasses.map { it.ordinal }.contains(it.cls) }) { override fun onCreateDefaultViewHolder(parent: ViewGroup): DecksAllViewHolder { return DecksAllViewHolder(parent.inflate(R.layout.itemlist_deck), itemClick, itemLongClick) } override fun onBindContentHolder(itemKey: String, model: FirebaseParsers.DeckParser, viewHolder: DecksAllViewHolder) { - viewHolder.bind(model.toDeck(itemKey, false), privateInteractor) + viewHolder.bind(model.toDeck(itemKey, true), privateInteractor) } override fun onSyncEnd() { @@ -65,7 +51,7 @@ class DecksOwnerFragment : DecksPublicFragment() { override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - decks_recycler_view.adapter = ownerPrivateDecksAdapter + decks_recycler_view.adapter = ownerDecksAdapter with(decks_recycler_view.adapter as FirebaseRVAdapter<*, *>) { decks_recycler_view.addOnScrollListener(OnLinearLayoutItemScrolled(getContentCount() - 3) { more() @@ -78,26 +64,18 @@ class DecksOwnerFragment : DecksPublicFragment() { configLoggedViews() } - override fun showDecks() { - (decks_recycler_view.adapter as FirebaseRVAdapter<*, *>).reset() - } - override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { menu?.clear() inflater?.inflate(R.menu.menu_decks_owned, menu) onlyPrivate = menu?.findItem(R.id.menu_only_private)?.actionView as Switch onlyPrivate?.setOnCheckedChangeListener { button, checked -> - decks_recycler_view.adapter = if (checked) ownerPublicDecksAdapter else ownerPrivateDecksAdapter + showDecks() } super.onCreateOptionsMenu(menu, inflater) } - override fun getDecks(cls: Class?, last: Boolean) { -// privateInteractor.getOwnedDecks(cls, { -// val decksToShow = if (onlyPrivate?.isChecked ?: false) it.filter(Deck::private) else it -// decksToShow.forEach { Timber.d("Decks: %s", it.toString()) } -// decksAdapter.showDecks(decksToShow, last) -// }) + override fun showDecks() { + (decks_recycler_view.adapter as FirebaseRVAdapter<*, *>).reset() } } \ No newline at end of file diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt index 87a1e45..d2dfada 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt @@ -63,10 +63,12 @@ open class DecksPublicFragment : BaseFragment() { Pair(view.deck_attr2 as View, attr2TransitionName)).toBundle()) } } + val itemLongClick = { view: View, deck: Deck -> true } + open protected val decksAdapter = DecksAllAdapter(ADS_EACH_ITEMS, R.layout.itemlist_deck_ads, itemClick, itemLongClick) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/firebase/FirebaseArray.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/firebase/FirebaseArray.kt index d8b85b4..92b78cb 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/firebase/FirebaseArray.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/firebase/FirebaseArray.kt @@ -34,7 +34,7 @@ import java.util.* /** * This class implements an array-like collection on top of a Firebase location. */ -class FirebaseArray(val mOriginalQuery: Query?, pageSize: Int = 0, +class FirebaseArray(val mOriginalQuery: () -> Query?, pageSize: Int = 0, val mOrderASC: Boolean = true) : ChildEventListener, ValueEventListener { enum class EventType { @@ -110,8 +110,10 @@ class FirebaseArray(val mOriginalQuery: Query?, pageSize: Int = 0, val count: Int get() = mSnapshots.size - fun getItem(index: Int): DataSnapshot { - return mSnapshots[index] + fun getCount(cond: (DataSnapshot) -> Boolean): Int = mSnapshots.filter { cond.invoke(it) }.size + + fun getItem(index: Int, cond: (DataSnapshot) -> Boolean): DataSnapshot { + return mSnapshots.filter { cond.invoke(it) }[index] } private fun setup() { @@ -119,11 +121,11 @@ class FirebaseArray(val mOriginalQuery: Query?, pageSize: Int = 0, cleanup() } if (mPageSize == 0) { - mQuery = mOriginalQuery + mQuery = mOriginalQuery() } else if (mOrderASC) { - mQuery = mOriginalQuery?.limitToFirst(mCurrentSize) + mQuery = mOriginalQuery()?.limitToFirst(mCurrentSize) } else { - mQuery = mOriginalQuery?.limitToLast(mCurrentSize) + mQuery = mOriginalQuery()?.limitToLast(mCurrentSize) } isSyncing = true mQuery?.addChildEventListener(this) @@ -168,7 +170,7 @@ class FirebaseArray(val mOriginalQuery: Query?, pageSize: Int = 0, notifyChangedListeners(EventType.Added, index) } - override fun onChildChanged(snapshot: DataSnapshot, previousChildKey: String) { + override fun onChildChanged(snapshot: DataSnapshot, previousChildKey: String?) { val index = getIndexForKey(snapshot.key) mSnapshots[index] = snapshot notifyChangedListeners(EventType.Changed, index) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/firebase/FirebaseRVAdapter.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/firebase/FirebaseRVAdapter.kt index 0da6e42..067f6ef 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/firebase/FirebaseRVAdapter.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/util/firebase/FirebaseRVAdapter.kt @@ -29,6 +29,7 @@ package com.ediposouza.teslesgendstracker.ui.util.firebase import android.support.v7.widget.RecyclerView +import com.google.firebase.database.DataSnapshot import com.google.firebase.database.DatabaseError import com.google.firebase.database.DatabaseReference import com.google.firebase.database.Query @@ -84,7 +85,11 @@ import com.google.firebase.database.Query * @param pageSize initial page size. set 0 for no limit. */ abstract class FirebaseRVAdapter( - var mModel: Class, ref: Query?, pageSize: Int = 0, orderASC: Boolean = false) : RecyclerView.Adapter() { + var mModel: Class, + ref: () -> Query?, + pageSize: Int = 0, + orderASC: Boolean = false, + val filter: ((T) -> Boolean)? = null) : RecyclerView.Adapter() { var mSnapshots: FirebaseArray = FirebaseArray(ref, pageSize, orderASC) @@ -127,33 +132,35 @@ abstract class FirebaseRVAdapter( fun cleanup() = mSnapshots.cleanup() + private fun filterResult(it: DataSnapshot) = filter?.invoke(it.getValue(mModel)) ?: true + /** * Override when adding headers. * @return number of items inserted in front of the FirebaseArray */ open val snapShotOffset: Int = 0 - open fun getContentCount(): Int = mSnapshots.count + open fun getContentCount(): Int = mSnapshots.getCount { filterResult(it) } /** * Override when adding headers or footers * @return number of items including headers and footers. */ - override fun getItemCount(): Int = mSnapshots.count + override fun getItemCount(): Int = mSnapshots.getCount { filterResult(it) } - open fun getItem(position: Int): T = mSnapshots.getItem(position - snapShotOffset).getValue(mModel) + open fun getItem(position: Int): T = mSnapshots.getItem(position - snapShotOffset, { filterResult(it) }).getValue(mModel) - fun getItemKey(position: Int): String = mSnapshots.getItem(position - snapShotOffset).key + fun getItemKey(position: Int): String = mSnapshots.getItem(position - snapShotOffset, { filterResult(it) }).key - fun getRef(position: Int): DatabaseReference = mSnapshots.getItem(position).ref + fun getRef(position: Int): DatabaseReference = mSnapshots.getItem(position, { filterResult(it) }).ref override fun getItemId(position: Int): Long { if (position < snapShotOffset) return ("header" + position).hashCode().toLong() - if (position >= snapShotOffset + mSnapshots.count) - return ("footer" + (position - (snapShotOffset + mSnapshots.count))).hashCode().toLong() + if (position >= snapShotOffset + mSnapshots.getCount { filterResult(it) }) + return ("footer" + (position - (snapShotOffset + mSnapshots.getCount { filterResult(it) }))).hashCode().toLong() // http://stackoverflow.com/questions/5100071/whats-the-purpose-of-item-ids-in-android-listview-adapter - return mSnapshots.getItem(position).key.hashCode().toLong() + return mSnapshots.getItem(position, { filterResult(it) }).key.hashCode().toLong() } override fun onBindViewHolder(viewHolder: VH, position: Int) { From 44ef621ddd1348912d6da6e68df4d4c9bfcdb8d9 Mon Sep 17 00:00:00 2001 From: EdipoSouza Date: Fri, 6 Jan 2017 18:55:14 -0200 Subject: [PATCH 08/54] public deck --- .../interactor/PrivateInteractor.kt | 2 + .../interactor/PublicInteractor.kt | 5 + .../ui/decks/tabs/DecksFavoritedFragment.kt | 18 +- .../ui/decks/tabs/DecksOwnerFragment.kt | 40 +--- .../ui/decks/tabs/DecksPublicFragment.kt | 181 ++++++++++-------- 5 files changed, 115 insertions(+), 131 deletions(-) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt index 31ed242..45c9b3f 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt @@ -167,6 +167,8 @@ class PrivateInteractor : BaseInteractor() { keepSynced() } + fun getFavoriteDecks() = PublicInteractor().getPublicDecksRef() + fun getFavoriteDecks(cls: Class?, onSuccess: (List?) -> Unit) { PublicInteractor().getPublicDecks(cls) { val publicDecks = it diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PublicInteractor.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PublicInteractor.kt index 09f1de3..633d67d 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PublicInteractor.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PublicInteractor.kt @@ -104,6 +104,11 @@ class PublicInteractor : BaseInteractor() { } } + fun getPublicDecksRef() = dbDecks.child(NODE_DECKS_PUBLIC) + .orderByChild(KEY_DECK_UPDATE_AT)?.apply { + keepSynced() + } + fun getPublicDecks(cls: Class?, onSuccess: (List) -> Unit) { val dbPublicDeck = dbDecks.child(NODE_DECKS_PUBLIC) dbPublicDeck.keepSynced() diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksFavoritedFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksFavoritedFragment.kt index ffc4e9a..160dba0 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksFavoritedFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksFavoritedFragment.kt @@ -5,17 +5,20 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import com.ediposouza.teslesgendstracker.R -import com.ediposouza.teslesgendstracker.data.Class -import com.ediposouza.teslesgendstracker.interactor.PrivateInteractor import com.ediposouza.teslesgendstracker.util.inflate -import timber.log.Timber /** * Created by EdipoSouza on 11/18/16. */ class DecksFavoritedFragment : DecksPublicFragment() { - private val privateInteractor = PrivateInteractor() + override val dataRef = { + privateInteractor.getFavoriteDecks() + } + +// override val dataFilter: (FirebaseParsers.DeckParser) -> Boolean = { +// currentClasses.map { it.ordinal }.contains(it.cls) +// } override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { return container?.inflate(R.layout.fragment_decks_list) @@ -26,11 +29,4 @@ class DecksFavoritedFragment : DecksPublicFragment() { configLoggedViews() } - override fun getDecks(cls: Class?, last: Boolean) { - privateInteractor.getFavoriteDecks(cls, { - it?.forEach { Timber.d("Public: %s", it.toString()) } - decksAdapter.showDecks(it ?: listOf(), last) - }) - } - } \ No newline at end of file diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksOwnerFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksOwnerFragment.kt index a160c6b..2dbf221 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksOwnerFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksOwnerFragment.kt @@ -4,13 +4,7 @@ import android.os.Bundle import android.view.* import android.widget.Switch import com.ediposouza.teslesgendstracker.R -import com.ediposouza.teslesgendstracker.interactor.FirebaseParsers -import com.ediposouza.teslesgendstracker.interactor.PrivateInteractor -import com.ediposouza.teslesgendstracker.ui.base.BaseAdsFirebaseAdapter -import com.ediposouza.teslesgendstracker.ui.util.firebase.FirebaseRVAdapter -import com.ediposouza.teslesgendstracker.ui.util.firebase.OnLinearLayoutItemScrolled import com.ediposouza.teslesgendstracker.util.inflate -import kotlinx.android.synthetic.main.fragment_decks_list.* /** * Created by EdipoSouza on 11/18/16. @@ -19,47 +13,19 @@ class DecksOwnerFragment : DecksPublicFragment() { override val isDeckOwned: Boolean = true - private val privateInteractor = PrivateInteractor() private var onlyPrivate: Switch? = null - private val dataRef = { + override val dataRef = { if (onlyPrivate?.isChecked ?: false) privateInteractor.getOwnedPrivateDecksRef() else privateInteractor.getOwnedPublicDecksRef() } - val ownerDecksAdapter = object : BaseAdsFirebaseAdapter( - FirebaseParsers.DeckParser::class.java, dataRef, DECK_PAGE_SIZE, ADS_EACH_ITEMS, - R.layout.itemlist_deck_ads, false, { currentClasses.map { it.ordinal }.contains(it.cls) }) { - - override fun onCreateDefaultViewHolder(parent: ViewGroup): DecksAllViewHolder { - return DecksAllViewHolder(parent.inflate(R.layout.itemlist_deck), itemClick, itemLongClick) - } - - override fun onBindContentHolder(itemKey: String, model: FirebaseParsers.DeckParser, viewHolder: DecksAllViewHolder) { - viewHolder.bind(model.toDeck(itemKey, true), privateInteractor) - } - - override fun onSyncEnd() { - decks_refresh_layout?.isRefreshing = false - } - - } - override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { return container?.inflate(R.layout.fragment_decks_list_owner) } override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - decks_recycler_view.adapter = ownerDecksAdapter - with(decks_recycler_view.adapter as FirebaseRVAdapter<*, *>) { - decks_recycler_view.addOnScrollListener(OnLinearLayoutItemScrolled(getContentCount() - 3) { - more() - }) - decks_refresh_layout.setOnRefreshListener { - reset() - } - } setHasOptionsMenu(true) configLoggedViews() } @@ -74,8 +40,4 @@ class DecksOwnerFragment : DecksPublicFragment() { super.onCreateOptionsMenu(menu, inflater) } - override fun showDecks() { - (decks_recycler_view.adapter as FirebaseRVAdapter<*, *>).reset() - } - } \ No newline at end of file diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt index d2dfada..7e7bf97 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt @@ -1,12 +1,8 @@ package com.ediposouza.teslesgendstracker.ui.decks.tabs -import android.app.Activity -import android.content.Intent import android.os.Bundle -import android.support.annotation.LayoutRes import android.support.v4.app.ActivityOptionsCompat import android.support.v4.util.Pair -import android.support.v7.util.DiffUtil import android.support.v7.widget.LinearLayoutManager import android.support.v7.widget.RecyclerView import android.view.LayoutInflater @@ -16,11 +12,12 @@ import com.ediposouza.teslesgendstracker.App import com.ediposouza.teslesgendstracker.R import com.ediposouza.teslesgendstracker.data.Class import com.ediposouza.teslesgendstracker.data.Deck +import com.ediposouza.teslesgendstracker.interactor.FirebaseParsers import com.ediposouza.teslesgendstracker.interactor.PrivateInteractor import com.ediposouza.teslesgendstracker.interactor.PublicInteractor import com.ediposouza.teslesgendstracker.ui.DeckActivity import com.ediposouza.teslesgendstracker.ui.base.* -import com.ediposouza.teslesgendstracker.ui.util.SimpleDiffCallback +import com.ediposouza.teslesgendstracker.ui.util.firebase.OnLinearLayoutItemScrolled import com.ediposouza.teslesgendstracker.util.inflate import com.google.firebase.auth.FirebaseAuth import jp.wasabeef.recyclerview.animators.SlideInLeftAnimator @@ -30,7 +27,6 @@ import kotlinx.android.synthetic.main.itemlist_deck.view.* import org.greenrobot.eventbus.Subscribe import timber.log.Timber import java.text.NumberFormat -import java.util.* /** * Created by EdipoSouza on 11/18/16. @@ -39,24 +35,32 @@ open class DecksPublicFragment : BaseFragment() { val ADS_EACH_ITEMS = 10 //after 10 lines val DECK_PAGE_SIZE = 8 - val RC_DECK = 123 protected val publicInteractor = PublicInteractor() + protected val privateInteractor = PrivateInteractor() protected var currentClasses: Array = Class.values() - val nameTransitionName: String by lazy { getString(R.string.deck_name_transition_name) } - val coverTransitionName: String by lazy { getString(R.string.deck_cover_transition_name) } - val attr1TransitionName: String by lazy { getString(R.string.deck_attr1_transition_name) } - val attr2TransitionName: String by lazy { getString(R.string.deck_attr2_transition_name) } + private val nameTransitionName: String by lazy { getString(R.string.deck_name_transition_name) } + private val coverTransitionName: String by lazy { getString(R.string.deck_cover_transition_name) } + private val attr1TransitionName: String by lazy { getString(R.string.deck_attr1_transition_name) } + private val attr2TransitionName: String by lazy { getString(R.string.deck_attr2_transition_name) } open protected val isDeckOwned: Boolean = false + open protected val dataRef = { + publicInteractor.getPublicDecksRef() + } + + open protected val dataFilter: (FirebaseParsers.DeckParser) -> Boolean = { + currentClasses.map { it.ordinal }.contains(it.cls) + } + val itemClick = { view: View, deck: Deck -> PrivateInteractor().getFavoriteDecks(deck.cls) { val favorite = it?.filter { it.id == deck.id }?.isNotEmpty() ?: false val like = deck.likes.contains(FirebaseAuth.getInstance().currentUser?.uid) - startActivityForResult(DeckActivity.newIntent(context, deck, favorite, like, isDeckOwned), - RC_DECK, ActivityOptionsCompat.makeSceneTransitionAnimation(activity, + startActivity(DeckActivity.newIntent(context, deck, favorite, like, isDeckOwned), + ActivityOptionsCompat.makeSceneTransitionAnimation(activity, Pair(view.deck_name as View, nameTransitionName), Pair(view.deck_cover as View, coverTransitionName), Pair(view.deck_attr1 as View, attr1TransitionName), @@ -69,8 +73,25 @@ open class DecksPublicFragment : BaseFragment() { true } - open protected val decksAdapter = DecksAllAdapter(ADS_EACH_ITEMS, R.layout.itemlist_deck_ads, - itemClick, itemLongClick) + private val decksAdapter by lazy { + object : BaseAdsFirebaseAdapter( + FirebaseParsers.DeckParser::class.java, dataRef, DECK_PAGE_SIZE, ADS_EACH_ITEMS, + R.layout.itemlist_deck_ads, false, dataFilter) { + + override fun onCreateDefaultViewHolder(parent: ViewGroup): DecksAllViewHolder { + return DecksAllViewHolder(parent.inflate(R.layout.itemlist_deck), itemClick, itemLongClick) + } + + override fun onBindContentHolder(itemKey: String, model: FirebaseParsers.DeckParser, viewHolder: DecksAllViewHolder) { + viewHolder.bind(model.toDeck(itemKey, true), privateInteractor) + } + + override fun onSyncEnd() { + decks_refresh_layout?.isRefreshing = false + } + + } + } override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { return container?.inflate(R.layout.fragment_decks_list) @@ -78,21 +99,18 @@ open class DecksPublicFragment : BaseFragment() { override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - decks_recycler_view.adapter = decksAdapter - decks_recycler_view.itemAnimator = SlideInLeftAnimator() - decks_recycler_view.layoutManager = object : LinearLayoutManager(context) { - override fun supportsPredictiveItemAnimations(): Boolean = false + with(decks_recycler_view) { + adapter = decksAdapter + itemAnimator = SlideInLeftAnimator() + layoutManager = object : LinearLayoutManager(context) { + override fun supportsPredictiveItemAnimations(): Boolean = false + } + addOnScrollListener(OnLinearLayoutItemScrolled(decksAdapter.getContentCount() - 3) { + decksAdapter.more() + }) } decks_refresh_layout.setOnRefreshListener { - decks_refresh_layout.isRefreshing = false - showDecks() - } - } - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - if (requestCode == RC_DECK && resultCode == Activity.RESULT_OK) { - showDecks() + decksAdapter.reset() } } @@ -123,61 +141,62 @@ open class DecksPublicFragment : BaseFragment() { } open fun showDecks() { - Timber.d("Classes: %s", currentClasses.toSet()) - decksAdapter.clearItems() - for (i in currentClasses.indices) { - getDecks(currentClasses[i], i == currentClasses.size - 1) - } + decksAdapter.reset() +// Timber.d("Classes: %s", currentClasses.toSet()) +// decksAdapter.clearItems() +// for (i in currentClasses.indices) { +// getDecks(currentClasses[i], i == currentClasses.size - 1) +// } } - open fun getDecks(cls: Class?, last: Boolean) { - publicInteractor.getPublicDecks(cls, { - it.forEach { Timber.d("Public: %s", it.toString()) } - decksAdapter.showDecks(it.sortedByDescending(Deck::updatedAt), last) - }) - } - - class DecksAllAdapter(adsEachItems: Int, @LayoutRes adsLayout: Int, val itemClick: (View, Deck) -> Unit, - val itemLongClick: (View, Deck) -> Boolean) : BaseAdsAdapter(adsEachItems, adsLayout) { - - val privateInteractor = PrivateInteractor() - - var items: List = listOf() - var newItems: ArrayList = ArrayList() - - override fun onCreateDefaultViewHolder(parent: ViewGroup): RecyclerView.ViewHolder { - return DecksAllViewHolder(parent.inflate(R.layout.itemlist_deck), itemClick, itemLongClick) - } - - override fun onBindDefaultViewHolder(holder: RecyclerView.ViewHolder?, position: Int) { - val deck = items[position] - (holder as DecksAllViewHolder).bind(deck, privateInteractor) - } - - override fun getDefaultItemCount(): Int = items.size - - fun clearItems() { - newItems.clear() - } - - fun showDecks(decks: List, last: Boolean) { - newItems.addAll(decks) - if (!last) { - return - } - Collections.sort(newItems, { d1, d2 -> d2.updatedAt.compareTo(d1.updatedAt) }) - val oldItems = items - items = newItems - if (items.isEmpty() || items.minus(oldItems).isEmpty()) { - notifyDataSetChanged() - return - } - DiffUtil.calculateDiff(SimpleDiffCallback(items, oldItems) { oldItem, newItem -> - oldItem.id == newItem.id - }).dispatchUpdatesTo(this) - } - - } +// open fun getDecks(cls: Class?, last: Boolean) { +// publicInteractor.getPublicDecks(cls, { +// it.forEach { Timber.d("Public: %s", it.toString()) } +// decksAdapter.showDecks(it.sortedByDescending(Deck::updatedAt), last) +// }) +// } +// +// class DecksAllAdapter(adsEachItems: Int, @LayoutRes adsLayout: Int, val itemClick: (View, Deck) -> Unit, +// val itemLongClick: (View, Deck) -> Boolean) : BaseAdsAdapter(adsEachItems, adsLayout) { +// +// val privateInteractor = PrivateInteractor() +// +// var items: List = listOf() +// var newItems: ArrayList = ArrayList() +// +// override fun onCreateDefaultViewHolder(parent: ViewGroup): RecyclerView.ViewHolder { +// return DecksAllViewHolder(parent.inflate(R.layout.itemlist_deck), itemClick, itemLongClick) +// } +// +// override fun onBindDefaultViewHolder(holder: RecyclerView.ViewHolder?, position: Int) { +// val deck = items[position] +// (holder as DecksAllViewHolder).bind(deck, privateInteractor) +// } +// +// override fun getDefaultItemCount(): Int = items.size +// +// fun clearItems() { +// newItems.clear() +// } +// +// fun showDecks(decks: List, last: Boolean) { +// newItems.addAll(decks) +// if (!last) { +// return +// } +// Collections.sort(newItems, { d1, d2 -> d2.updatedAt.compareTo(d1.updatedAt) }) +// val oldItems = items +// items = newItems +// if (items.isEmpty() || items.minus(oldItems).isEmpty()) { +// notifyDataSetChanged() +// return +// } +// DiffUtil.calculateDiff(SimpleDiffCallback(items, oldItems) { oldItem, newItem -> +// oldItem.id == newItem.id +// }).dispatchUpdatesTo(this) +// } +// +// } class DecksAllViewHolder(val view: View, val itemClick: (View, Deck) -> Unit, val itemLongClick: (View, Deck) -> Boolean) : RecyclerView.ViewHolder(view) { From 2b3a2d5f85b3d4de2798743083554fe3d8df3e6b Mon Sep 17 00:00:00 2001 From: EdipoSouza Date: Fri, 6 Jan 2017 22:53:11 -0200 Subject: [PATCH 09/54] Favorite decks --- .../teslesgendstracker/data/Deck.kt | 8 ++ .../interactor/FirebaseParsers.kt | 4 + .../interactor/PrivateInteractor.kt | 4 +- .../interactor/PublicInteractor.kt | 29 ++++++- .../ui/decks/tabs/DecksFavoritedFragment.kt | 34 +++++++- .../ui/decks/tabs/DecksPublicFragment.kt | 85 +++++-------------- app/src/main/res/layout/itemlist_deck.xml | 9 ++ 7 files changed, 102 insertions(+), 71 deletions(-) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/data/Deck.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/data/Deck.kt index 94fd63f..b9dd123 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/data/Deck.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/data/Deck.kt @@ -66,6 +66,9 @@ data class DeckComment( dest?.writeString(comment) dest?.writeSerializable(date) } + + override fun toString(): String = "DeckComment(id='$id', owner='$owner', comment='$comment', date=$date)" + } data class Deck( @@ -125,4 +128,9 @@ data class Deck( dest?.writeList(updates) dest?.writeList(comments) } + + override fun toString(): String { + return "Deck(id='$id', name='$name', owner='$owner', private=$private, type=$type, cls=$cls, cost=$cost, createdAt=$createdAt, updatedAt=$updatedAt, patch='$patch', likes=$likes, views=$views, cards=$cards, updates=$updates, comments=$comments)" + } + } \ No newline at end of file diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/FirebaseParsers.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/FirebaseParsers.kt index e5a18f2..cdea766 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/FirebaseParsers.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/FirebaseParsers.kt @@ -107,6 +107,10 @@ abstract class FirebaseParsers { return mapOf(KEY_DECK_NAME to name, KEY_DECK_TYPE to type, KEY_DECK_CLASS to cls, KEY_DECK_PATCH to patch) } + override fun toString(): String { + return "DeckParser(name='$name', type=$type, cls=$cls, cost=$cost, owner='$owner', createdAt='$createdAt', updatedAt='$updatedAt', patch='$patch', views=$views, likes=$likes, cards=$cards, updates=$updates, comments=$comments)" + } + } class PatchParser { diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt index 45c9b3f..eb2de85 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt @@ -167,7 +167,9 @@ class PrivateInteractor : BaseInteractor() { keepSynced() } - fun getFavoriteDecks() = PublicInteractor().getPublicDecksRef() + fun getFavoriteDecksRef() = dbUser()?.child(NODE_DECKS)?.child(NODE_FAVORITE)?.apply { + keepSynced() + } fun getFavoriteDecks(cls: Class?, onSuccess: (List?) -> Unit) { PublicInteractor().getPublicDecks(cls) { diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PublicInteractor.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PublicInteractor.kt index 633d67d..2d8f5ee 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PublicInteractor.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PublicInteractor.kt @@ -109,6 +109,28 @@ class PublicInteractor : BaseInteractor() { keepSynced() } + fun getPublicDeck(uuid: String, onSuccess: (Deck) -> Unit) { + with(dbDecks.child(NODE_DECKS_PUBLIC).child(uuid)) { + keepSynced() + addListenerForSingleValueEvent(object : ValueEventListener { + + override fun onDataChange(ds: DataSnapshot) { + val value = ds.getValue(FirebaseParsers.DeckParser::class.java) + val deck = value?.toDeck(ds.key, false) + if (deck != null) { + Timber.d(deck.toString()) + onSuccess.invoke(deck) + } + } + + override fun onCancelled(de: DatabaseError) { + Timber.d("Fail: " + de.message) + } + + }) + } + } + fun getPublicDecks(cls: Class?, onSuccess: (List) -> Unit) { val dbPublicDeck = dbDecks.child(NODE_DECKS_PUBLIC) dbPublicDeck.keepSynced() @@ -134,11 +156,12 @@ class PublicInteractor : BaseInteractor() { }) } - fun incDeckView(deck: Deck, onError: ((e: Exception?) -> Unit)? = null, onSuccess: () -> Unit) { + fun incDeckView(deck: Deck, onError: ((e: Exception?) -> Unit)? = null, onSuccess: (Int) -> Unit) { with(dbDecks.child(NODE_DECKS_PUBLIC)) { - child(deck.id).updateChildren(mapOf(KEY_DECK_VIEWS to deck.views.inc())).addOnCompleteListener({ + val views = deck.views.inc() + child(deck.id).updateChildren(mapOf(KEY_DECK_VIEWS to views)).addOnCompleteListener({ Timber.d(it.toString()) - if (it.isSuccessful) onSuccess.invoke() else onError?.invoke(it.exception) + if (it.isSuccessful) onSuccess.invoke(views) else onError?.invoke(it.exception) }) } } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksFavoritedFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksFavoritedFragment.kt index 160dba0..9430186 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksFavoritedFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksFavoritedFragment.kt @@ -1,11 +1,15 @@ package com.ediposouza.teslesgendstracker.ui.decks.tabs import android.os.Bundle +import android.text.TextUtils import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import com.ediposouza.teslesgendstracker.R +import com.ediposouza.teslesgendstracker.interactor.FirebaseParsers +import com.ediposouza.teslesgendstracker.ui.base.BaseAdsFirebaseAdapter import com.ediposouza.teslesgendstracker.util.inflate +import kotlinx.android.synthetic.main.fragment_decks_list.* /** * Created by EdipoSouza on 11/18/16. @@ -13,12 +17,34 @@ import com.ediposouza.teslesgendstracker.util.inflate class DecksFavoritedFragment : DecksPublicFragment() { override val dataRef = { - privateInteractor.getFavoriteDecks() + privateInteractor.getFavoriteDecksRef() } -// override val dataFilter: (FirebaseParsers.DeckParser) -> Boolean = { -// currentClasses.map { it.ordinal }.contains(it.cls) -// } + override val dataFilter: (FirebaseParsers.DeckParser) -> Boolean = { + true//currentClasses.map { it.ordinal }.contains(it.cls) + } + + override val decksAdapter: BaseAdsFirebaseAdapter by lazy { + object : BaseAdsFirebaseAdapter( + Boolean::class.java, dataRef, DECK_PAGE_SIZE, ADS_EACH_ITEMS, + R.layout.itemlist_deck_ads, false) { + + override fun onCreateDefaultViewHolder(parent: ViewGroup): DecksAllViewHolder { + return DecksAllViewHolder(parent.inflate(R.layout.itemlist_deck), itemClick, itemLongClick) + } + + override fun onBindContentHolder(itemKey: String, model: Boolean, viewHolder: DecksAllViewHolder) { + if (!TextUtils.isEmpty(itemKey)) { + viewHolder.bind(itemKey, publicInteractor, privateInteractor) + } + } + + override fun onSyncEnd() { + decks_refresh_layout?.isRefreshing = false + } + + } + } override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { return container?.inflate(R.layout.fragment_decks_list) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt index 7e7bf97..4bdc776 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt @@ -36,9 +36,9 @@ open class DecksPublicFragment : BaseFragment() { val ADS_EACH_ITEMS = 10 //after 10 lines val DECK_PAGE_SIZE = 8 + protected var currentClasses = Class.values() protected val publicInteractor = PublicInteractor() protected val privateInteractor = PrivateInteractor() - protected var currentClasses: Array = Class.values() private val nameTransitionName: String by lazy { getString(R.string.deck_name_transition_name) } private val coverTransitionName: String by lazy { getString(R.string.deck_cover_transition_name) } @@ -61,10 +61,10 @@ open class DecksPublicFragment : BaseFragment() { val like = deck.likes.contains(FirebaseAuth.getInstance().currentUser?.uid) startActivity(DeckActivity.newIntent(context, deck, favorite, like, isDeckOwned), ActivityOptionsCompat.makeSceneTransitionAnimation(activity, - Pair(view.deck_name as View, nameTransitionName), - Pair(view.deck_cover as View, coverTransitionName), - Pair(view.deck_attr1 as View, attr1TransitionName), - Pair(view.deck_attr2 as View, attr2TransitionName)).toBundle()) + Pair(view.deck_name as View, nameTransitionName), + Pair(view.deck_cover as View, coverTransitionName), + Pair(view.deck_attr1 as View, attr1TransitionName), + Pair(view.deck_attr2 as View, attr2TransitionName)).toBundle()) } } @@ -73,7 +73,7 @@ open class DecksPublicFragment : BaseFragment() { true } - private val decksAdapter by lazy { + open protected val decksAdapter: BaseAdsFirebaseAdapter<*, DecksAllViewHolder> by lazy { object : BaseAdsFirebaseAdapter( FirebaseParsers.DeckParser::class.java, dataRef, DECK_PAGE_SIZE, ADS_EACH_ITEMS, R.layout.itemlist_deck_ads, false, dataFilter) { @@ -83,7 +83,8 @@ open class DecksPublicFragment : BaseFragment() { } override fun onBindContentHolder(itemKey: String, model: FirebaseParsers.DeckParser, viewHolder: DecksAllViewHolder) { - viewHolder.bind(model.toDeck(itemKey, true), privateInteractor) + Timber.d(model.toString()) + viewHolder.bind(model.toDeck(itemKey, isDeckOwned), privateInteractor) } override fun onSyncEnd() { @@ -142,68 +143,26 @@ open class DecksPublicFragment : BaseFragment() { open fun showDecks() { decksAdapter.reset() -// Timber.d("Classes: %s", currentClasses.toSet()) -// decksAdapter.clearItems() -// for (i in currentClasses.indices) { -// getDecks(currentClasses[i], i == currentClasses.size - 1) -// } } -// open fun getDecks(cls: Class?, last: Boolean) { -// publicInteractor.getPublicDecks(cls, { -// it.forEach { Timber.d("Public: %s", it.toString()) } -// decksAdapter.showDecks(it.sortedByDescending(Deck::updatedAt), last) -// }) -// } -// -// class DecksAllAdapter(adsEachItems: Int, @LayoutRes adsLayout: Int, val itemClick: (View, Deck) -> Unit, -// val itemLongClick: (View, Deck) -> Boolean) : BaseAdsAdapter(adsEachItems, adsLayout) { -// -// val privateInteractor = PrivateInteractor() -// -// var items: List = listOf() -// var newItems: ArrayList = ArrayList() -// -// override fun onCreateDefaultViewHolder(parent: ViewGroup): RecyclerView.ViewHolder { -// return DecksAllViewHolder(parent.inflate(R.layout.itemlist_deck), itemClick, itemLongClick) -// } -// -// override fun onBindDefaultViewHolder(holder: RecyclerView.ViewHolder?, position: Int) { -// val deck = items[position] -// (holder as DecksAllViewHolder).bind(deck, privateInteractor) -// } -// -// override fun getDefaultItemCount(): Int = items.size -// -// fun clearItems() { -// newItems.clear() -// } -// -// fun showDecks(decks: List, last: Boolean) { -// newItems.addAll(decks) -// if (!last) { -// return -// } -// Collections.sort(newItems, { d1, d2 -> d2.updatedAt.compareTo(d1.updatedAt) }) -// val oldItems = items -// items = newItems -// if (items.isEmpty() || items.minus(oldItems).isEmpty()) { -// notifyDataSetChanged() -// return -// } -// DiffUtil.calculateDiff(SimpleDiffCallback(items, oldItems) { oldItem, newItem -> -// oldItem.id == newItem.id -// }).dispatchUpdatesTo(this) -// } -// -// } - class DecksAllViewHolder(val view: View, val itemClick: (View, Deck) -> Unit, val itemLongClick: (View, Deck) -> Boolean) : RecyclerView.ViewHolder(view) { constructor(view: View) : this(view, { view, deck -> }, { view, deck -> true }) + fun bind(itemKey: String, publicInteractor: PublicInteractor, privateInteractor: PrivateInteractor) { + itemView.deck_loading.visibility = View.VISIBLE + itemView.deck_cover.visibility = View.GONE + itemView.deck_info.visibility = View.GONE + publicInteractor.getPublicDeck(itemKey) { + bind(it, privateInteractor) + } + } + fun bind(deck: Deck, privateInteractor: PrivateInteractor) { + itemView.deck_loading.visibility = View.GONE + itemView.deck_cover.visibility = View.VISIBLE + itemView.deck_info.visibility = View.VISIBLE itemView.setOnClickListener { itemClick(itemView, deck) } itemView.setOnLongClickListener { itemLongClick(itemView, deck) } itemView.deck_cover.setImageResource(deck.cls.imageRes) @@ -223,11 +182,11 @@ open class DecksPublicFragment : BaseFragment() { calculateMissingSoul(deck, privateInteractor) } - fun calculateMissingSoul(deck: Deck, interactor: PrivateInteractor) { + fun calculateMissingSoul(deck: Deck, privateInteractor: PrivateInteractor) { with(itemView.deck_soul_missing) { visibility = View.INVISIBLE itemView.deck_soul_missing_loading.visibility = View.VISIBLE - interactor.getMissingCards(deck, { itemView.deck_soul_missing_loading.visibility = View.VISIBLE }) { + privateInteractor.getMissingCards(deck, { itemView.deck_soul_missing_loading.visibility = View.VISIBLE }) { itemView.deck_soul_missing_loading.visibility = View.GONE val missingSoul = it.map { it.qtd * it.rarity.soulCost }.sum() Timber.d("Missing %d", missingSoul) diff --git a/app/src/main/res/layout/itemlist_deck.xml b/app/src/main/res/layout/itemlist_deck.xml index 3c80de0..40f4227 100644 --- a/app/src/main/res/layout/itemlist_deck.xml +++ b/app/src/main/res/layout/itemlist_deck.xml @@ -6,6 +6,14 @@ android:layout_marginTop="@dimen/default_margin" tools:layout_marginTop="@dimen/status_bar_height"> + + From 702ba5dc0b7aee652e279fccec6c4d7f73f1d1d1 Mon Sep 17 00:00:00 2001 From: EdipoSouza Date: Sat, 7 Jan 2017 00:40:26 -0200 Subject: [PATCH 10/54] Handler deck public/owned/private details dinamically --- .../teslesgendstracker/ui/DeckActivity.kt | 13 +++- .../ui/decks/tabs/DecksOwnerFragment.kt | 8 +-- .../ui/decks/tabs/DecksPublicFragment.kt | 65 ++++++++++++------- 3 files changed, 56 insertions(+), 30 deletions(-) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DeckActivity.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DeckActivity.kt index 1a0b66d..52859fd 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DeckActivity.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DeckActivity.kt @@ -96,6 +96,12 @@ class DeckActivity : BaseActivity() { } private fun configViews() { + if (deckOwned) { + deck_fab_favorite.hide() + deck_details_likes.visibility = View.GONE + deck_details_views.visibility = View.GONE + commentsSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN + } deck_fab_favorite.setOnClickListener { if (App.hasUserLogged()) { privateInteractor.setUserDeckFavorite(deck, !favorite) { @@ -183,6 +189,7 @@ class DeckActivity : BaseActivity() { override fun onCreateOptionsMenu(menu: Menu?): Boolean { menuInflater.inflate(if (deckOwned) R.menu.menu_deck_owner else R.menu.menu_deck, menu) menuLike = menu?.findItem(R.id.menu_like) + menuLike?.isVisible = !deckOwned updateLikeItem() return super.onCreateOptionsMenu(menu) } @@ -284,8 +291,10 @@ class DeckActivity : BaseActivity() { private fun loadDeckRemoteInfo() { doAsync { calculateMissingSoul(deck, privateInteractor) - publicInteractor.incDeckView(deck) { - deck_details_views.text = deck.views.inc().toString() + if (!deckOwned) { + publicInteractor.incDeckView(deck) { + deck_details_views.text = it.toString() + } } publicInteractor.getPatches { val patch = it.find { it.uidDate == deck.patch } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksOwnerFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksOwnerFragment.kt index 2dbf221..ed62624 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksOwnerFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksOwnerFragment.kt @@ -11,13 +11,13 @@ import com.ediposouza.teslesgendstracker.util.inflate */ class DecksOwnerFragment : DecksPublicFragment() { - override val isDeckOwned: Boolean = true - private var onlyPrivate: Switch? = null + override val isDeckPrivate: Boolean + get() = onlyPrivate?.isChecked ?: false + override val dataRef = { - if (onlyPrivate?.isChecked ?: false) - privateInteractor.getOwnedPrivateDecksRef() else privateInteractor.getOwnedPublicDecksRef() + if (isDeckPrivate) privateInteractor.getOwnedPrivateDecksRef() else privateInteractor.getOwnedPublicDecksRef() } override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt index 4bdc776..6f0982b 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt @@ -8,6 +8,7 @@ import android.support.v7.widget.RecyclerView import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.RelativeLayout import com.ediposouza.teslesgendstracker.App import com.ediposouza.teslesgendstracker.R import com.ediposouza.teslesgendstracker.data.Class @@ -45,7 +46,7 @@ open class DecksPublicFragment : BaseFragment() { private val attr1TransitionName: String by lazy { getString(R.string.deck_attr1_transition_name) } private val attr2TransitionName: String by lazy { getString(R.string.deck_attr2_transition_name) } - open protected val isDeckOwned: Boolean = false + open protected val isDeckPrivate: Boolean = false open protected val dataRef = { publicInteractor.getPublicDecksRef() @@ -58,8 +59,9 @@ open class DecksPublicFragment : BaseFragment() { val itemClick = { view: View, deck: Deck -> PrivateInteractor().getFavoriteDecks(deck.cls) { val favorite = it?.filter { it.id == deck.id }?.isNotEmpty() ?: false - val like = deck.likes.contains(FirebaseAuth.getInstance().currentUser?.uid) - startActivity(DeckActivity.newIntent(context, deck, favorite, like, isDeckOwned), + val userId = FirebaseAuth.getInstance().currentUser?.uid + val like = deck.likes.contains(userId) + startActivity(DeckActivity.newIntent(context, deck, favorite, like, deck.owner == userId), ActivityOptionsCompat.makeSceneTransitionAnimation(activity, Pair(view.deck_name as View, nameTransitionName), Pair(view.deck_cover as View, coverTransitionName), @@ -84,7 +86,7 @@ open class DecksPublicFragment : BaseFragment() { override fun onBindContentHolder(itemKey: String, model: FirebaseParsers.DeckParser, viewHolder: DecksAllViewHolder) { Timber.d(model.toString()) - viewHolder.bind(model.toDeck(itemKey, isDeckOwned), privateInteractor) + viewHolder.bind(model.toDeck(itemKey, isDeckPrivate), privateInteractor) } override fun onSyncEnd() { @@ -160,26 +162,41 @@ open class DecksPublicFragment : BaseFragment() { } fun bind(deck: Deck, privateInteractor: PrivateInteractor) { - itemView.deck_loading.visibility = View.GONE - itemView.deck_cover.visibility = View.VISIBLE - itemView.deck_info.visibility = View.VISIBLE - itemView.setOnClickListener { itemClick(itemView, deck) } - itemView.setOnLongClickListener { itemLongClick(itemView, deck) } - itemView.deck_cover.setImageResource(deck.cls.imageRes) - itemView.deck_private.layoutParams.width = if (deck.private) ViewGroup.LayoutParams.WRAP_CONTENT else 0 - itemView.deck_name.text = deck.name - itemView.deck_attr1.setImageResource(deck.cls.attr1.imageRes) - itemView.deck_attr2.setImageResource(deck.cls.attr2.imageRes) - itemView.deck_type.text = deck.type.name.toLowerCase().capitalize() - itemView.deck_date.setCompoundDrawablesWithIntrinsicBounds(if (deck.updates.isEmpty()) - R.drawable.ic_create_at else R.drawable.ic_updated_at, 0, 0, 0) - itemView.deck_date.text = deck.updatedAt.toLocalDate().toString() - val numberInstance = NumberFormat.getNumberInstance() - itemView.deck_soul_cost.text = numberInstance.format(deck.cost) - itemView.deck_comments.text = numberInstance.format(deck.comments.size) - itemView.deck_likes.text = numberInstance.format(deck.likes.size) - itemView.deck_views.text = numberInstance.format(deck.views) - calculateMissingSoul(deck, privateInteractor) + with(itemView) { + deck_loading.visibility = View.GONE + deck_cover.visibility = View.VISIBLE + deck_info.visibility = View.VISIBLE + setOnClickListener { itemClick(itemView, deck) } + setOnLongClickListener { itemLongClick(itemView, deck) } + deck_cover.setImageResource(deck.cls.imageRes) + deck_private.layoutParams.width = if (deck.private) ViewGroup.LayoutParams.WRAP_CONTENT else 0 + deck_name.text = deck.name + deck_attr1.setImageResource(deck.cls.attr1.imageRes) + deck_attr2.setImageResource(deck.cls.attr2.imageRes) + deck_type.text = deck.type.name.toLowerCase().capitalize() + deck_date.text = deck.updatedAt.toLocalDate().toString() + (deck_date.layoutParams as RelativeLayout.LayoutParams).apply { + if (deck.private) { + addRule(RelativeLayout.ALIGN_PARENT_END) + removeRule(RelativeLayout.END_OF) + } else { + addRule(RelativeLayout.END_OF, R.id.deck_center) + removeRule(RelativeLayout.ALIGN_PARENT_END) + } + deck_date.layoutParams = this + } + deck_date.setCompoundDrawablesWithIntrinsicBounds(if (deck.updates.isEmpty()) + R.drawable.ic_create_at else R.drawable.ic_updated_at, 0, 0, 0) + val numberInstance = NumberFormat.getNumberInstance() + deck_soul_cost.text = numberInstance.format(deck.cost) + deck_comments.text = numberInstance.format(deck.comments.size) + deck_comments.visibility = if (deck.private) View.INVISIBLE else View.VISIBLE + deck_likes.text = numberInstance.format(deck.likes.size) + deck_likes.visibility = if (deck.private) View.INVISIBLE else View.VISIBLE + deck_views.text = numberInstance.format(deck.views) + deck_views.visibility = if (deck.private) View.INVISIBLE else View.VISIBLE + calculateMissingSoul(deck, privateInteractor) + } } fun calculateMissingSoul(deck: Deck, privateInteractor: PrivateInteractor) { From 9364f0df242544860b85b0595f164ef3486145c2 Mon Sep 17 00:00:00 2001 From: EdipoSouza Date: Sat, 7 Jan 2017 11:21:14 -0200 Subject: [PATCH 11/54] Favorite class filter --- .../interactor/PrivateInteractor.kt | 2 +- .../ui/decks/tabs/DecksFavoritedFragment.kt | 15 +- .../ui/decks/tabs/DecksPublicFragment.kt | 2 +- cards.json | 1725 +++++++++++++---- 4 files changed, 1393 insertions(+), 351 deletions(-) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt index eb2de85..e21005c 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt @@ -237,7 +237,7 @@ class PrivateInteractor : BaseInteractor() { fun setUserDeckFavorite(deck: Deck, favorite: Boolean, onError: ((e: Exception?) -> Unit)? = null, onSuccess: () -> Unit) { dbUser()?.child(NODE_DECKS)?.child(NODE_FAVORITE)?.apply { if (favorite) { - child(deck.id)?.setValue(true)?.addOnCompleteListener { onSuccess.invoke() } + child(deck.id)?.setValue(deck.cls.ordinal)?.addOnCompleteListener { onSuccess.invoke() } } else { child(deck.id)?.removeValue()?.addOnCompleteListener { onSuccess.invoke() } } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksFavoritedFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksFavoritedFragment.kt index 9430186..4eb33b0 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksFavoritedFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksFavoritedFragment.kt @@ -6,7 +6,6 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import com.ediposouza.teslesgendstracker.R -import com.ediposouza.teslesgendstracker.interactor.FirebaseParsers import com.ediposouza.teslesgendstracker.ui.base.BaseAdsFirebaseAdapter import com.ediposouza.teslesgendstracker.util.inflate import kotlinx.android.synthetic.main.fragment_decks_list.* @@ -20,20 +19,20 @@ class DecksFavoritedFragment : DecksPublicFragment() { privateInteractor.getFavoriteDecksRef() } - override val dataFilter: (FirebaseParsers.DeckParser) -> Boolean = { - true//currentClasses.map { it.ordinal }.contains(it.cls) + private val dataFilter: (Int) -> Boolean = { + currentClasses.map { it.ordinal }.contains(it) } - override val decksAdapter: BaseAdsFirebaseAdapter by lazy { - object : BaseAdsFirebaseAdapter( - Boolean::class.java, dataRef, DECK_PAGE_SIZE, ADS_EACH_ITEMS, - R.layout.itemlist_deck_ads, false) { + override val decksAdapter: BaseAdsFirebaseAdapter by lazy { + object : BaseAdsFirebaseAdapter( + Int::class.java, dataRef, DECK_PAGE_SIZE, ADS_EACH_ITEMS, + R.layout.itemlist_deck_ads, false, dataFilter) { override fun onCreateDefaultViewHolder(parent: ViewGroup): DecksAllViewHolder { return DecksAllViewHolder(parent.inflate(R.layout.itemlist_deck), itemClick, itemLongClick) } - override fun onBindContentHolder(itemKey: String, model: Boolean, viewHolder: DecksAllViewHolder) { + override fun onBindContentHolder(itemKey: String, model: Int, viewHolder: DecksAllViewHolder) { if (!TextUtils.isEmpty(itemKey)) { viewHolder.bind(itemKey, publicInteractor, privateInteractor) } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt index 6f0982b..be65e59 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt @@ -52,7 +52,7 @@ open class DecksPublicFragment : BaseFragment() { publicInteractor.getPublicDecksRef() } - open protected val dataFilter: (FirebaseParsers.DeckParser) -> Boolean = { + private val dataFilter: (FirebaseParsers.DeckParser) -> Boolean = { currentClasses.map { it.ordinal }.contains(it.cls) } diff --git a/cards.json b/cards.json index c02696c..782a087 100644 --- a/cards.json +++ b/cards.json @@ -4796,88 +4796,284 @@ } } }, - "season": { - "05": { - "reward": "elsweyrlookout", - "month": "2016_12" - }, - "04": { - "reward": "battlereeveofdusk", - "month": "2016_11" - }, - "03": { - "reward": "mechanicalally", - "month": "2016_10" - }, - "02": { - "reward": "histgrove", - "month": "2016_09" - }, - "01": { - "reward": "smugglershaul", - "month": "2016_08" - }, - "00": { - "reward": "dagirahtmystic", - "month": "2016_07" - }, - "2016_06": { - "reward": "plunder", - "month": "2016_06" - }, - "2016_05": { - "reward": "staffofsparks", - "month": "2016_05" - }, - "2016_04": { - "reward": "witheredhandcultist", - "month": "2016_04" - }, - "2016_03": { - "reward": "magesguildretreat", - "month": "2016_03" + "decks": { + "public": { + "-K_-MCInX62rl826jQf1": { + "cards": { + "belligerentgiant": 2, + "blooddragon": 3, + "cliffracer": 2, + "daringcutpurse": 3, + "earthbonespinner": 3, + "fightersguildrecruit": 3, + "finishoff": 3, + "housekinsman": 3, + "leaflurker": 3, + "moonlightwerebat": 3, + "murkwaterwitch": 3, + "rapidshot": 3, + "skavenpyromancer": 3, + "snaketoothnecklace": 1, + "soulrestmarshal": 3, + "tazkadthepackmaster": 1, + "thievesguildrecruit": 3, + "triumphantjarl": 2, + "ungolimthelistener": 1, + "witheredhandcultist": 2 + }, + "cls": 0, + "comments": { + "-K_6sQmQSJYrQVKYojSJ": { + "comment": "Great deck, I just change Tazkar beacuse I don't have it", + "createdAt": "2016-12-28T22:01:32.864", + "owner": "ZYO8GAaZfVYo2evDwPedYIFODcm1" + } + }, + "cost": 14400, + "createdAt": "2016-12-27T09:59:03.560", + "name": "Cycle Archer", + "owner": "ZYO8GAaZfVYo2evDwPedYIFODcm1", + "patch": "2016_12_01", + "type": 4, + "updatedAt": "2016-12-27T09:59:03.561", + "views": 50 + }, + "-K_9ongYoCXYadur5EVZ": { + "cards": { + "blackwormnecromancer": 3, + "cliffracer": 3, + "daringcutpurse": 3, + "dunesmuggler": 3, + "fightersguildrecruit": 3, + "giantbat": 3, + "housekinsman": 3, + "illusorymimic": 3, + "moonlightwerebat": 3, + "mournholdtraitor": 3, + "murkwaterwitch": 3, + "nahagliiv": 1, + "shadowfenpriest": 2, + "soulrestmarshal": 3, + "tazkadthepackmaster": 1, + "territorialviper": 3, + "ungolimthelistener": 1, + "windkeepspellsword": 3, + "youngmammoth": 3 + }, + "cls": 6, + "cost": 11900, + "createdAt": "2016-12-29T11:44:33.906", + "likes": [ + "ZYO8GAaZfVYo2evDwPedYIFODcm1", + "h2KTcpHAnxRY0EaIM8KFKXVWz8L2" + ], + "name": "Mimic Scout", + "owner": "ZYO8GAaZfVYo2evDwPedYIFODcm1", + "patch": "2016_12_14", + "type": 0, + "updatedAt": "2016-12-29T11:44:33.906", + "views": 33 + }, + "-K_iNXh_xHakLMUcDbMK": { + "cards": { + "blackwormnecromancer": 3, + "cursedspectre": 2, + "daggerfallmage": 3, + "darkrift": 3, + "firebolt": 3, + "hauntingspirit": 3, + "highkingemeric": 1, + "lightningbolt": 3, + "lurkingcrocodile": 3, + "midnightsweep": 3, + "mummify": 3, + "nahagliiv": 1, + "nightshadow": 2, + "royalsage": 2, + "shadowfenpriest": 2, + "shriekingharpy": 3, + "supremeatromancer": 1, + "wardcrafter": 3, + "windkeepspellsword": 3, + "youngmammoth": 3 + }, + "cls": 7, + "cost": 13000, + "createdAt": "2017-01-05T09:27:24.423", + "name": "Prophecy Sorcerer", + "owner": "ZYO8GAaZfVYo2evDwPedYIFODcm1", + "patch": "2017_01_01", + "type": 0, + "updatedAt": "2017-01-05T09:27:24.424", + "views": 8 + }, + "-K_nqdgsJMWGGrLyGN9v": { + "cards": { + "afflictedalit": 3, + "castout": 3, + "darkrift": 3, + "earthbonespinner": 3, + "fateweaver": 3, + "gladiatorarena": 3, + "grahtwoodambusher": 3, + "graystoneravager": 3, + "lightningbolt": 3, + "morkulgatekeeper": 3, + "relentlessraider": 3, + "sentinelbattlemace": 3, + "sharpshooterscout": 3, + "shriekingharpy": 3, + "slaughterfishspawning": 3, + "triumphantjarl": 2, + "wardcrafter": 3 + }, + "cls": 2, + "cost": 12200, + "createdAt": "2017-01-06T10:57:03.207", + "name": "Prophecy battlemage", + "owner": "ZYO8GAaZfVYo2evDwPedYIFODcm1", + "patch": "2017_01_01", + "type": 0, + "updatedAt": "2017-01-06T10:57:03.207", + "views": 1 + } } }, "patches": { - "2017_01_01": { - "desc": "Elsweyr Lookout" - }, - "2016_12_14": { + "2016_08_25": { "core": { - "strength": { - "burnandpillage": { + "agility": { + "murkwatersavage": { "cost": { - "after": "5", - "before": "6" - }, + "after": "3", + "before": "2" + } + }, + "soulrestmarshal": { "text": { - "after": "Deal 1 damage to all enemy creatures in a lane for each destroyed enemy rune.", - "before": "Deal 1 damage to all enemy creatures for each destroyed enemy rune." + "after": "Summon: If you have more health than your opponent, the next card you play this turn costs 5 less.", + "before": "Summon: If you have more health than your opponent, the next card you play this turn costs 6 less." } } } }, - "desc": "Madhouse Collection" + "desc": "Soulrest Marshal Nerf" }, - "2016_12_01": { - "desc": "Battlereeve Of Dusk" + "2016_09_01": { + "desc": "Smuggler's Haul" }, - "2016_11_03": { + "2016_09_20": { "core": { - "strength": { - "morkulgatekeeper": { - "keyword": { - "after": "Prophecy, Guard, Summon", - "before": "Guard, Summon" + "endurance": { + "bonecolossus": { + "text": { + "after": "Summon: Summon 1/1 Skeletons to fill this lane. Other friendly Skeleton have +1/+1.", + "before": "Summon: Summon 1/1 Skeletons to fill this lane. Other friendly Undead have +1/+1." + } + } + } + }, + "desc": "Bone Colossus Nerf" + }, + "2016_09_28": { + "core": { + "agility": { + "blacksapprotector": { + "rarity": { + "after": "Rare", + "before": "Common" } }, - "volendrung": { + "voraciousspriggan": { + "rarity": { + "after": "Common", + "before": "Rare" + } + } + }, + "intelligence": { + "evermoresteward": { + "rarity": { + "after": "Rare", + "before": "Common" + } + }, + "farsightnereid": { + "rarity": { + "after": "Common", + "before": "Rare" + } + }, + "nibenbaycutthroat": { + "rarity": { + "after": "Common", + "before": "Rare" + } + }, + "shockingwamasu": { "text": { - "after": "Uses: 2. Activate: Give a creature +4/+4.", - "before": "Uses: 2. Activate: Give a creature +4/+4 and Breakthrough." + "after": "Summon: Deal 4 damage to a creature.", + "before": "Summon: Deal 4 damage." + } + }, + "shriekingharpy": { + "rarity": { + "after": "Rare", + "before": "Common" + } + } + }, + "strength": { + "fieryimp": { + "rarity": { + "after": "Rare", + "before": "Common" + } + }, + "fortresswatchman": { + "rarity": { + "after": "Common", + "before": "Rare" + } + }, + "intimidate": { + "rarity": { + "after": "Rare", + "before": "Common" + } + }, + "valenwoodhuntsman": { + "rarity": { + "after": "Common", + "before": "Rare" + } + }, + "valenwoodtrapper": { + "rarity": { + "after": "Common", + "before": "Rare" } } }, + "willpower": { + "mantikora": { + "text": { + "after": "Guard. Summon: Destroy an enemy creature in this lane.", + "before": "Guard. Summon: Destroy an enemy creature." + } + } + } + }, + "desc": "Mantikora Nerf" + }, + "2016_10_01": { + "desc": "Hist Grove" + }, + "2016_11_01": { + "desc": "Mechanical Ally" + }, + "2016_11_03": { + "core": { "agility": { "housekinsman": { "health": { @@ -4904,6 +5100,22 @@ } } }, + "dual": { + "allenabenoch": { + "text": { + "after": "Lethal. Summon: Deal 1 damage to an enemy.", + "before": "Lethal. Summon: Deal 1 damage to an enemy creature." + } + } + }, + "endurance": { + "wrothgarartisan": { + "health": { + "after": 3, + "before": 2 + } + } + }, "intelligence": { "brilliantexperiment": { "cost": { @@ -4934,176 +5146,107 @@ } } }, - "willpower": { - "divinefervor": { - "cost": { - "after": "05", - "before": "04" + "strength": { + "morkulgatekeeper": { + "keyword": { + "after": "Prophecy, Guard, Summon", + "before": "Guard, Summon" } }, + "volendrung": { + "text": { + "after": "Uses: 2. Activate: Give a creature +4/+4.", + "before": "Uses: 2. Activate: Give a creature +4/+4 and Breakthrough." + } + } + }, + "willpower": { "apprenticespotion": { "cost": { "after": "02", "before": "01" } }, + "divinefervor": { + "cost": { + "after": "05", + "before": "04" + } + }, "healingpotion": { "cost": { "after": "02", "before": "01" } } - }, - "endurance": { - "wrothgarartisan": { - "health": { - "after": 3, - "before": 2 - } - } - }, - "dual": { - "allenabenoch": { - "text": { - "after": "Lethal. Summon: Deal 1 damage to an enemy.", - "before": "Lethal. Summon: Deal 1 damage to an enemy creature." - } - } } }, "desc": "Nahkriin Nerf" }, - "2016_11_01": { - "desc": "Mechanical Ally" - }, - "2016_10_01": { - "desc": "Hist Grove" - }, - "2016_09_01": { - "desc": "Smuggler's Haul" + "2016_12_01": { + "desc": "Battlereeve Of Dusk" }, - "2016_09_28": { + "2016_12_14": { "core": { "strength": { - "intimidate": { - "rarity": { - "after": "Rare", - "before": "Common" - } - }, - "fieryimp": { - "rarity": { - "after": "Rare", - "before": "Common" - } - }, - "valenwoodhuntsman": { - "rarity": { - "after": "Common", - "before": "Rare" - } - }, - "valenwoodtrapper": { - "rarity": { - "after": "Common", - "before": "Rare" - } - }, - "fortresswatchman": { - "rarity": { - "after": "Common", - "before": "Rare" - } - } - }, - "intelligence": { - "evermoresteward": { - "rarity": { - "after": "Rare", - "before": "Common" - } - }, - "shriekingharpy": { - "rarity": { - "after": "Rare", - "before": "Common" - } - }, - "nibenbaycutthroat": { - "rarity": { - "after": "Common", - "before": "Rare" - } - }, - "farsightnereid": { - "rarity": { - "after": "Common", - "before": "Rare" - } - }, - "shockingwamasu": { - "text": { - "after": "Summon: Deal 4 damage to a creature.", - "before": "Summon: Deal 4 damage." - } - } - }, - "agility": { - "blacksapprotector": { - "rarity": { - "after": "Rare", - "before": "Common" - } - }, - "voraciousspriggan": { - "rarity": { - "after": "Common", - "before": "Rare" - } - } - }, - "willpower": { - "mantikora": { + "burnandpillage": { + "cost": { + "after": "5", + "before": "6" + }, "text": { - "after": "Guard. Summon: Destroy an enemy creature in this lane.", - "before": "Guard. Summon: Destroy an enemy creature." + "after": "Deal 1 damage to all enemy creatures in a lane for each destroyed enemy rune.", + "before": "Deal 1 damage to all enemy creatures for each destroyed enemy rune." } } } }, - "desc": "Mantikora Nerf" + "desc": "Madhouse Collection" }, - "2016_09_20": { - "core": { - "endurance": { - "bonecolossus": { - "text": { - "after": "Summon: Summon 1/1 Skeletons to fill this lane. Other friendly Skeleton have +1/+1.", - "before": "Summon: Summon 1/1 Skeletons to fill this lane. Other friendly Undead have +1/+1." - } - } - } - }, - "desc": "Bone Colossus Nerf" + "2017_01_01": { + "desc": "Elsweyr Lookout" + } + }, + "season": { + "00": { + "month": "2016_07", + "reward": "dagirahtmystic" }, - "2016_08_25": { - "core": { - "agility": { - "soulrestmarshal": { - "text": { - "after": "Summon: If you have more health than your opponent, the next card you play this turn costs 5 less.", - "before": "Summon: If you have more health than your opponent, the next card you play this turn costs 6 less." - } - }, - "murkwatersavage": { - "cost": { - "after": "3", - "before": "2" - } - } - } - }, - "desc": "Soulrest Marshal Nerf" + "01": { + "month": "2016_08", + "reward": "smugglershaul" + }, + "02": { + "month": "2016_09", + "reward": "histgrove" + }, + "03": { + "month": "2016_10", + "reward": "mechanicalally" + }, + "04": { + "month": "2016_11", + "reward": "battlereeveofdusk" + }, + "05": { + "month": "2016_12", + "reward": "elsweyrlookout" + }, + "2016_03": { + "month": "2016_03", + "reward": "magesguildretreat" + }, + "2016_04": { + "month": "2016_04", + "reward": "witheredhandcultist" + }, + "2016_05": { + "month": "2016_05", + "reward": "staffofsparks" + }, + "2016_06": { + "month": "2016_06", + "reward": "plunder" } }, "users": { @@ -5111,124 +5254,1087 @@ "cards": { "core": { "agility": { + "anxileelinvader": { + "qtd": 3 + }, + "arrowintheknee": { + "qtd": 3 + }, + "baandaribruiser": { + "qtd": 3 + }, + "blacksapprotector": { + "qtd": 3 + }, + "chaurusreaper": { + "qtd": 3 + }, + "cliffracer": { + "qtd": 3 + }, + "curse": { + "qtd": 3 + }, + "daringcutpurse": { + "qtd": 3 + }, + "deshaanavenger": { + "qtd": 3 + }, + "dunesmuggler": { + "qtd": 3 + }, + "dunestalker": { + "qtd": 2 + }, + "eldercentaur": { + "qtd": 2 + }, + "feastingvulture": { + "qtd": 0 + }, + "fightersguildrecruit": { + "qtd": 3 + }, "finishoff": { + "qtd": 3 + }, + "giantbat": { + "qtd": 3 + }, + "giantsnake": { + "qtd": 1 + }, + "goblinskulk": { + "qtd": 3 + }, + "greenheartknight": { + "qtd": 3 + }, + "greenpactstalker": { + "qtd": 3 + }, + "greentouchedspriggan": { + "qtd": 1 + }, + "helstromfootpad": { + "qtd": 3 + }, + "highlandlurcher": { + "qtd": 3 + }, + "housekinsman": { + "qtd": 3 + }, + "leaflurker": { + "qtd": 3 + }, + "leafwaterblessing": { + "qtd": 1 + }, + "maleficwreath": { + "qtd": 2 + }, + "moonlightwerebat": { + "qtd": 3 + }, + "mournholdguardian": { + "qtd": 3 + }, + "mournholdtraitor": { + "qtd": 3 + }, + "murkwaterbutcher": { + "qtd": 3 + }, + "murkwatergoblin": { + "qtd": 3 + }, + "murkwatersavage": { + "qtd": 3 + }, + "murkwatershaman": { + "qtd": 2 + }, + "murkwaterwitch": { + "qtd": 3 + }, + "nimbleally": { + "qtd": 3 + }, + "quinrawlburglar": { "qtd": 2 }, - "murkwaterwitch": { + "quinrawlskulker": { + "qtd": 0 + }, + "ransack": { + "qtd": 3 + }, + "shadowshift": { + "qtd": 3 + }, + "skoomaracketeer": { + "qtd": 3 + }, + "smugglershaul": { + "qtd": 3 + }, + "snaketoothnecklace": { + "qtd": 3 + }, + "snowysabrecat": { + "qtd": 1 + }, + "soulrestmarshal": { + "qtd": 3 + }, + "spiderdaedra": { + "qtd": 1 + }, + "spiderlair": { + "qtd": 1 + }, + "swiftstrike": { + "qtd": 3 + }, + "tazkadthepackmaster": { + "qtd": 1 + }, + "tenmarswiftclaw": { + "qtd": 1 + }, + "territorialviper": { + "qtd": 3 + }, + "thievery": { + "qtd": 3 + }, + "thievesguildrecruit": { + "qtd": 3 + }, + "ungolimthelistener": { + "favorite": true, + "qtd": 1 + }, + "varaniscourier": { + "qtd": 3 + }, + "voraciousspriggan": { + "qtd": 3 + }, + "wildbeastcaller": { + "qtd": 2 + } + }, + "dual": { + "allenabenoch": { + "qtd": 1 + }, + "edictofazura": { + "qtd": 1 + }, + "falinestireaver": { + "qtd": 1 + }, + "highkingemeric": { + "qtd": 1 + }, + "masterofthieves": { + "qtd": 3 + }, + "militantchieftain": { + "qtd": 1 + }, + "riftthane": { + "qtd": 1 + }, + "sadrasagent": { + "qtd": 2 + }, + "sentinelbattlemace": { + "qtd": 1 + }, + "shornhelmchampion": { + "qtd": 2 + }, + "skywatchvindicator": { + "qtd": 2 + }, + "thornhistmage": { + "qtd": 1 + }, + "tyr": { + "qtd": 1 + } + }, + "endurance": { + "angrygrahl": { + "qtd": 1 + }, + "archeinelite": { + "qtd": 2 + }, + "archeinvenomtongue": { + "qtd": 3 + }, + "blackwormnecromancer": { + "qtd": 3 + }, + "bloodmagiclord": { + "qtd": 1 + }, + "brumaarmorer": { + "qtd": 3 + }, + "cursedspectre": { + "qtd": 3 + }, + "daedricdagger": { + "qtd": 3 + }, + "deadlydraugr": { + "qtd": 3 + }, + "deathlessdraugr": { + "qtd": 3 + }, + "discipleofnamira": { + "qtd": 3 + }, + "doomcragvampire": { + "qtd": 1 + }, + "dragontailsavior": { + "qtd": 3 + }, + "elixirofvigor": { + "qtd": 3 + }, + "enchantedplate": { + "qtd": 3 + }, + "fharundefender": { + "qtd": 3 + }, + "fleshatronach": { + "qtd": 1 + }, + "frostbitespider": { + "qtd": 3 + }, + "hackwingfeather": { + "qtd": 1 + }, + "hauntingspirit": { + "qtd": 3 + }, + "healinghands": { + "qtd": 3 + }, + "histgrove": { + "qtd": 3 + }, + "histspeaker": { + "qtd": 3 + }, + "iliacsorcerer": { + "qtd": 1 + }, + "imperialarmor": { + "qtd": 3 + }, + "imprisoneddeathlord": { + "qtd": 2 + }, + "ironatronach": { + "qtd": 1 + }, + "lowlandtroll": { + "qtd": 2 + }, + "lurkingmummy": { + "qtd": 3 + }, + "midnightsweep": { + "qtd": 3 + }, + "mummify": { + "qtd": 3 + }, + "nahagliiv": { + "qtd": 1 + }, + "nightshadow": { + "qtd": 2 + }, + "northpointcaptain": { + "qtd": 3 + }, + "oldgatewarden": { + "qtd": 3 + }, + "pleatokynareth": { + "qtd": 3 + }, + "preserveroftheroot": { + "qtd": 2 + }, + "restlesstemplar": { + "qtd": 3 + }, + "shadowfenpriest": { + "qtd": 3 + }, + "siegecatapult": { + "qtd": 2 + }, + "stalwartally": { + "qtd": 3 + }, + "stampedingmammoth": { + "qtd": 2 + }, + "stonetoothscrapper": { + "qtd": 3 + }, + "stormholdhenchman": { + "qtd": 3 + }, + "suppress": { + "qtd": 2 + }, + "swampleviathan": { + "qtd": 3 + }, + "treeminder": { + "qtd": 3 + }, + "watchcommander": { + "qtd": 3 + }, + "windkeepspellsword": { + "qtd": 3 + }, + "wrothgarartisan": { + "qtd": 3 + }, + "wrothgarkingpin": { + "qtd": 1 + }, + "yewshield": { + "qtd": 1 + }, + "youngmammoth": { + "qtd": 3 + } + }, + "intelligence": { + "abeceannavigator": { + "qtd": 3 + }, + "ashservant": { + "qtd": 3 + }, + "baronoftear": { + "qtd": 3 + }, + "battlereeveofdusk": { + "qtd": 3 + }, + "bretonconjurer": { + "qtd": 3 + }, + "brilliantexperiment": { + "qtd": 3 + }, + "brutalashlander": { + "qtd": 3 + }, + "camlornhero": { + "qtd": 3 + }, + "camlornsentinel": { + "qtd": 3 + }, + "craglornscavenger": { + "qtd": 1 + }, + "crownquartermaster": { + "qtd": 3 + }, + "cruelfirebloom": { + "qtd": 3 + }, + "crystaltowercrafter": { + "qtd": 3 + }, + "cunningally": { + "qtd": 3 + }, + "daggerfallmage": { + "qtd": 2 + }, + "darkrift": { + "qtd": 1 + }, + "desperateconjuring": { + "qtd": 1 + }, + "divaythfyr": { + "qtd": 1 + }, + "dragonstarrider": { + "qtd": 1 + }, + "dresrenegade": { + "qtd": 3 + }, + "drestormentor": { + "qtd": 3 + }, + "elixirofdeflection": { + "qtd": 3 + }, + "elusiveschemer": { + "qtd": 3 + }, + "evermoresteward": { + "qtd": 3 + }, + "farsightnereid": { + "qtd": 3 + }, + "fateweaver": { + "qtd": 3 + }, + "firebolt": { + "qtd": 3 + }, + "firestorm": { + "qtd": 1 + }, + "glenumbrasorceress": { + "qtd": 1 + }, + "highrocksummoner": { + "qtd": 3 + }, + "icespike": { + "qtd": 3 + }, + "icewraith": { + "qtd": 1 + }, + "indorilarchmage": { + "qtd": 1 + }, + "keeperofwhispers": { + "qtd": 1 + }, + "lesserward": { + "qtd": 3 + }, + "lightningbolt": { + "qtd": 3 + }, + "lillandrilhexmage": { + "qtd": 3 + }, + "maceofencumbrance": { + "qtd": 3 + }, + "magesguildretreat": { + "qtd": 2 + }, + "masterofarms": { + "qtd": 3 + }, + "mentorsring": { + "qtd": 1 + }, + "momentofclarity": { + "qtd": 2 + }, + "nibenbaycutthroat": { + "qtd": 3 + }, + "redoranenforcer": { + "qtd": 3 + }, + "royalsage": { + "qtd": 3 + }, + "shimmerenepeddler": { + "qtd": 2 + }, + "shockingwamasu": { + "qtd": 1 + }, + "shriekingharpy": { + "qtd": 3 + }, + "skilledblacksmith": { + "qtd": 3 + }, + "soulsplit": { + "qtd": 3 + }, + "staffofsparks": { + "qtd": 2 + }, + "steelsword": { + "qtd": 3 + }, + "studiumheadmaster": { + "qtd": 1 + }, + "summersetorrery": { + "qtd": 2 + }, + "supremeatromancer": { + "qtd": 1 + }, + "telvanniarcanist": { + "qtd": 3 + }, + "tomeofalteration": { + "qtd": 3 + }, + "wardcrafter": { + "qtd": 2 + }, + "wintersgrasp": { + "qtd": 2 + }, + "wisdomofancients": { + "qtd": 1 + } + }, + "neutral": { + "adoringfan": { + "qtd": 1 + }, + "agelessautomaton": { + "qtd": 3 + }, + "bardedguar": { + "qtd": 3 + }, + "crushingblow": { + "qtd": 3 + }, + "darkharvester": { + "qtd": 3 + }, + "dreughshellarmor": { + "qtd": 3 + }, + "dwarvenarmaments": { + "qtd": 2 + }, + "dwarvenballista": { + "qtd": 3 + }, + "dwarvencenturion": { + "qtd": 1 + }, + "dwarvensphere": { + "qtd": 3 + }, + "dwarvenspider": { + "qtd": 3 + }, + "elixirofconflict": { + "qtd": 3 + }, + "enragedmudcrab": { + "qtd": 3 + }, + "ferociousdreugh": { + "qtd": 3 + }, + "forswornguide": { + "qtd": 3 + }, + "frenziedwitchman": { + "qtd": 3 + }, + "goldbrand": { + "qtd": 1 + }, + "lurkingcrocodile": { + "qtd": 3 + }, + "mapleshield": { + "qtd": 3 + }, + "mechanicalally": { + "qtd": 3 + }, + "orbofvaermina": { + "qtd": 1 + }, + "portcullis": { + "qtd": 3 + }, + "ravenouscrocodile": { + "qtd": 3 + }, + "reachmanshaman": { + "qtd": 3 + }, + "slaughterfish": { + "qtd": 0 + }, + "slaughterfishspawning": { + "qtd": 3 + }, + "sparkingspider": { + "qtd": 2 + }, + "spiderworker": { + "qtd": 3 + }, + "strongholderadicator": { + "qtd": 2 + }, + "strongholdincubator": { + "qtd": 1 + }, + "viciousdreugh": { + "qtd": 3 + } + }, + "strength": { + "afflictedalit": { + "qtd": 3 + }, + "alikrsurvivalist": { + "qtd": 1 + }, + "assassinsbow": { + "qtd": 3 + }, + "bangkoraibutcher": { + "qtd": 3 + }, + "battlerageorc": { + "qtd": 3 + }, + "belligerentgiant": { + "qtd": 2 + }, + "blooddragon": { + "qtd": 2 + }, + "boglurcher": { + "qtd": 2 + }, + "bonebow": { + "qtd": 3 + }, + "burnandpillage": { + "qtd": 2 + }, + "castout": { + "qtd": 3 + }, + "childofhircine": { + "qtd": 1 + }, + "covenantmarauder": { + "qtd": 0 + }, + "dreadclannfear": { + "qtd": 1 + }, + "dremoramarkynaz": { + "qtd": 1 + }, + "earthbonespinner": { "qtd": 3 }, - "ungolimthelistener": { - "favorite": true - } - }, - "strength": { "fearlessnorthlander": { - "qtd": 1 + "qtd": 0 }, "fieryimp": { "favorite": true, + "qtd": 3 + }, + "fireball": { + "qtd": 3 + }, + "fortresswatchman": { "qtd": 2 }, + "grahtwoodambusher": { + "qtd": 3 + }, + "graystoneravager": { + "qtd": 3 + }, + "heavybattleaxe": { + "qtd": 3 + }, "improvisedweapon": { - "favorite": true + "favorite": true, + "qtd": 3 + }, + "initiateofhircine": { + "qtd": 0 }, "intimidate": { + "favorite": true, + "qtd": 2 + }, + "jerallforager": { "qtd": 1 }, + "mageslayer": { + "favorite": true, + "qtd": 2 + }, + "markarthbannerman": { + "qtd": 2 + }, + "mightyally": { + "qtd": 3 + }, + "morkulgatekeeper": { + "qtd": 3 + }, "morthalwatchman": { + "qtd": 2 + }, + "nordfirebrand": { + "qtd": 3 + }, + "northwindoutpost": { + "qtd": 0 + }, + "orcclanshaman": { + "qtd": 3 + }, + "orcclansman": { + "qtd": 0 + }, + "orcishwarhammer": { + "qtd": 3 + }, + "plunder": { "qtd": 1 }, + "raidingparty": { + "qtd": 2 + }, "rampagingminotaur": { - "qtd": 1 + "qtd": 3 }, "rapidshot": { "favorite": true, "qtd": 3 }, + "reclusivegiant": { + "qtd": 0 + }, "reiveblademaster": { - "qtd": 1 + "qtd": 0 }, "relentlessraider": { - "qtd": 1 + "qtd": 0 + }, + "riftenpillager": { + "qtd": 3 }, "rihadbattlemage": { "qtd": 3 }, - "steelscimitar": { + "rihadhorseman": { + "qtd": 3 + }, + "rihadnomad": { + "qtd": 0 + }, + "savageogre": { + "qtd": 3 + }, + "scuttler": { + "qtd": 2 + }, + "sharpshooterscout": { + "qtd": 3 + }, + "silvenartracker": { + "qtd": 3 + }, + "skavenpyromancer": { + "qtd": 3 + }, + "skirmisherselixir": { "qtd": 1 }, + "steelscimitar": { + "qtd": 3 + }, + "stonethrow": { + "qtd": 3 + }, "triumphantjarl": { + "qtd": 3 + }, + "valenwoodhuntsman": { + "qtd": 3 + }, + "vigilantgiant": { + "qtd": 1 + }, + "volendrung": { + "qtd": 0 + }, + "whirlingduelist": { + "qtd": 1 + }, + "whiteruntrooper": { + "qtd": 2 + }, + "witheredhandcultist": { + "qtd": 3 + }, + "wrothgarforge": { + "qtd": 2 + } + }, + "willpower": { + "aldmeripatriot": { + "qtd": 2 + }, + "alphawolf": { + "qtd": 1 + }, + "arrowstorm": { + "qtd": 3 + }, + "auridonpaladin": { + "qtd": 3 + }, + "brumaprofiteer": { + "qtd": 3 + }, + "calm": { + "qtd": 2 + }, + "cathayrahtveteran": { + "qtd": 3 + }, + "cheydinhalsapper": { + "qtd": 3 + }, + "dawnstarhealer": { + "qtd": 3 + }, + "divinefervor": { + "qtd": 3 + }, + "eastmarchcrusader": { + "qtd": 3 + }, + "elixirofthedefender": { + "qtd": 3 + }, + "execute": { + "qtd": 3 + }, + "fifthlegiontrainer": { + "qtd": 3 + }, + "goldensaint": { + "qtd": 3 + }, + "haafingarmarauder": { + "qtd": 1 + }, + "healingpotion": { + "qtd": 3 + }, + "heroofanvil": { + "qtd": 1 + }, + "hivedefender": { + "qtd": 3 + }, + "hivewarrior": { + "qtd": 1 + }, + "imperiallegionnaire": { + "qtd": 3 + }, + "imperialmight": { + "qtd": 1 + }, + "imperialreinforcements": { + "qtd": 3 + }, + "imperialsiegeengine": { + "qtd": 3 + }, + "imprison": { + "qtd": 3 + }, + "kvatchsoldier": { + "qtd": 3 + }, + "legionpraefect": { + "qtd": 0 + }, + "legionshield": { + "qtd": 3 + }, + "loyalhousecarl": { + "qtd": 3 + }, + "morthalexecutioner": { + "qtd": 2 + }, + "packwolf": { + "qtd": 3 + }, + "piercingjavelin": { + "qtd": 3 + }, + "pitlion": { + "qtd": 1 + }, + "priestofthemoons": { + "qtd": 3 + }, + "rajhinihighwayman": { + "qtd": 3 + }, + "ravenoushunger": { + "qtd": 3 + }, + "renownedlegate": { "qtd": 1 + }, + "resoluteally": { + "qtd": 3 + }, + "riverholdescort": { + "qtd": 3 + }, + "scoutingpatrol": { + "qtd": 3 + }, + "senchetiger": { + "qtd": 3 + }, + "septimguardsman": { + "qtd": 3 + }, + "skingradpatroller": { + "qtd": 3 + }, + "summersetshieldmage": { + "qtd": 3 + }, + "sunholdmedic": { + "qtd": 3 + }, + "tuskedbristleback": { + "qtd": 3 + }, + "twomoonscontemplation": { + "qtd": 3 + }, + "warcry": { + "qtd": 3 + } + } + }, + "madhouse": { + "agility": { + "swindlersmarket": { + "qtd": 3 + } + }, + "endurance": { + "ringofimaginarymight": { + "qtd": 3 + } + }, + "intelligence": { + "gardenerofswords": { + "qtd": 3 + } + }, + "neutral": { + "altarofdespair": { + "qtd": 3 + }, + "closecall": { + "qtd": 3 + }, + "forsakenchampion": { + "qtd": 3 + }, + "illusorymimic": { + "qtd": 3 + }, + "merchantscamel": { + "qtd": 3 + } + }, + "strength": { + "stoneshardorc": { + "favorite": true, + "qtd": 3 + } + }, + "willpower": { + "shiveringapothecary": { + "qtd": 3 } } } }, "decks": { "favorite": { - "-KWYopKFZVdqz3-YtBh-": true + "-KZwF4-30Ip7qC4wnMix": 1, + "-K_-MCInX62rl826jQf1": 0, + "-K_iNXh_xHakLMUcDbMK": 7 }, "private": { - "-KWYo4K8PDs_zO1q8aqh": { - "cards": { - "finishoff": 3, - "leaflurker": 1, - "moonlightwerebat": 3, - "murkwaterwitch": 3, - "rapidshot": 3, - "skavenpyromancer": 2, - "soulrestmarshal": 3, - "triumphantjarl": 2, - "ungolimthelistener": 1 - }, - "cls": 0, - "cost": 4000, - "createdAt": "2016-10-28T10:00:00", - "likes": [ - "Iu45y3i5UY345uhVi3u5h3Huf5h" - ], - "name": "My Archer", - "patch": "2016_09_28", - "type": 4, - "updatedAt": "2016-10-30T12:32:54", - "updates": { - "2016-10-30T12:32:54": { - "leaflurker": 2, - "rapidshot": -1, - "skavenpyromancer": 1 - } - }, - "views": 12 - }, - "-KWYopLfHszRwXRYOL_q": { + "-K_t7BMw5-q1sFGXCfoi": { "cards": { - "dunmernightblade": 3, - "elusiveschemer": 2, - "tazkadthepackmaster": 1, - "ungolimthelistener": 1 + "camlornsentinel": 3, + "crownquartermaster": 3, + "cunningally": 3, + "daggerfallmage": 3, + "enchantedplate": 3, + "firebolt": 3, + "gardenerofswords": 3, + "highkingemeric": 1, + "lightningbolt": 3, + "maceofencumbrance": 3, + "masterofarms": 3, + "royalsage": 2, + "shriekingharpy": 3, + "skilledblacksmith": 3, + "steelsword": 2, + "wardcrafter": 3, + "windkeepspellsword": 3, + "youngmammoth": 3 }, - "cls": 1, - "cost": 2850, - "createdAt": "2016-10-28T10:00:00", - "likes": [ - "Iu45y3i5UY345uhVi3u5h3Huf5h", - "ZYO8GAaZfVYo2evDwPedYIFODcm1" - ], - "name": "My Deck", - "patch": "2016_09_28", - "type": 4, - "updatedAt": "2016-10-31T15:30:00", - "updates": { - "2016-10-30T12:32:54": { - "dunmernightblade": -1, - "elusiveschemer": 1 - }, - "2016-10-31T15:30:00": { - "dunmernightblade": -1, - "elusiveschemer": 1, - "tazkadthepackmaster": -1 - } - }, - "views": 7 + "cls": 7, + "cost": 9150, + "createdAt": "2017-01-07T11:31:47.932", + "name": "Item Sorcerer", + "owner": "ZYO8GAaZfVYo2evDwPedYIFODcm1", + "patch": "2017_01_01", + "type": 0, + "updatedAt": "2017-01-07T11:31:47.932", + "views": 0 } } }, @@ -5239,6 +6345,7 @@ "matches": { "2016-11-30T08:22:34": { "first": false, + "legend": false, "me": { "cls": 0, "deck": "-KWYo4K8PDs_zO1q8aqh", @@ -5287,69 +6394,5 @@ "photoUrl": "https://lh5.googleusercontent.com/-o2RlvOLMZ7Y/AAAAAAAAAAI/AAAAAAAAPAM/1pxgQ_u-41g/s96-c/photo.jpg" } } - }, - "decks": { - "public": { - "-KWYo4Jw4Wd6nc8oaUeh": { - "cards": { - "finishoff": 3, - "leaflurker": 1, - "moonlightwerebat": 3, - "murkwaterwitch": 3, - "rapidshot": 3, - "skavenpyromancer": 2, - "soulrestmarshal": 3, - "tazkadthepackmaster": 1, - "triumphantjarl": 2, - "ungolimthelistener": 1 - }, - "cls": 0, - "comments": { - "hre8g7er6ger7g6er9gg8e6": { - "comment": "Nice deck", - "createdAt": "2016-10-28T14:17:23", - "owner": "ZYO8GAaZfVYo2evDwPedYIFODcm1" - } - }, - "cost": 5200, - "createdAt": "2016-10-28T10:00:00", - "likes": [ - "Iu45y3i5UY345uhVi3u5h3Huf5h" - ], - "name": "Cycle Archer", - "owner": "ZYO8GAaZfVYo2evDwPedYIFODcm1", - "patch": "2016_09_28", - "type": 4, - "updatedAt": "2016-10-30T12:32:54", - "updates": { - "2016-10-30T12:32:54": { - "leaflurker": 2, - "rapidshot": -1, - "skavenpyromancer": 1 - } - }, - "views": 12 - }, - "-KWYopKFZVdqz3-YtBh-": { - "cards": { - "dunmernightblade": 3, - "elusiveschemer": 3, - "tazkadthepackmaster": 1, - "ungolimthelistener": 1 - }, - "cls": 1, - "cost": 2850, - "createdAt": "2016-10-28T10:00:00", - "likes": [ - "Iu45y3i5UY345uhVi3u5h3Huf5h" - ], - "name": "Deck Test", - "owner": "ZYO8GAaZfVYo2evDwPedYIFODcm1", - "patch": "2016_09_28", - "type": 4, - "updatedAt": "2016-10-28T10:00:00", - "views": 7 - } - } } } \ No newline at end of file From e687e7bc1765144e49a3fe65164c498a5c8ea218 Mon Sep 17 00:00:00 2001 From: EdipoSouza Date: Tue, 3 Jan 2017 23:38:21 -0200 Subject: [PATCH 12/54] Created Matches fragment --- .../teslesgendstracker/ui/DashActivity.kt | 3 +- .../ui/matches/MatchesFragment.kt | 112 ++++++++++++++++++ .../ui/matches/tabs/MatchesHistory.kt | 24 ++++ .../ui/matches/tabs/MatchesStatistics.kt | 24 ++++ .../util/MetricsConstants.kt | 2 + app/src/main/res/drawable/ic_timer_sand.xml | 4 + .../main/res/drawable/ic_timer_sand_svg.xml | 10 ++ app/src/main/res/layout/fragment_matches.xml | 14 +++ .../res/layout/fragment_matches_history.xml | 6 + .../layout/fragment_matches_statistics.xml | 6 + app/src/main/res/menu/menu_drawer.xml | 3 +- app/src/main/res/menu/menu_season.xml | 11 ++ app/src/main/res/values/arrays.xml | 5 + app/src/main/res/values/strings.xml | 4 + 14 files changed, 225 insertions(+), 3 deletions(-) create mode 100644 app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesFragment.kt create mode 100644 app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesHistory.kt create mode 100644 app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt create mode 100644 app/src/main/res/drawable/ic_timer_sand.xml create mode 100644 app/src/main/res/drawable/ic_timer_sand_svg.xml create mode 100644 app/src/main/res/layout/fragment_matches.xml create mode 100644 app/src/main/res/layout/fragment_matches_history.xml create mode 100644 app/src/main/res/layout/fragment_matches_statistics.xml create mode 100644 app/src/main/res/menu/menu_season.xml diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DashActivity.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DashActivity.kt index ed614b4..b85374d 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DashActivity.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DashActivity.kt @@ -20,6 +20,7 @@ import com.ediposouza.teslesgendstracker.ui.cards.CardsFragment import com.ediposouza.teslesgendstracker.ui.cards.CmdFilterMagika import com.ediposouza.teslesgendstracker.ui.cards.CmdFilterRarity import com.ediposouza.teslesgendstracker.ui.decks.DecksFragment +import com.ediposouza.teslesgendstracker.ui.matches.MatchesFragment import com.ediposouza.teslesgendstracker.ui.util.CircleTransform import com.ediposouza.teslesgendstracker.ui.widget.CollectionStatistics import com.google.firebase.auth.FirebaseAuth @@ -146,7 +147,7 @@ class DashActivity : BaseFilterActivity(), return when (item.itemId) { R.id.menu_cards -> supportFragmentManager.popBackStackImmediate() R.id.menu_decks -> showFragment(DecksFragment()) - R.id.menu_matches, + R.id.menu_matches -> showFragment(MatchesFragment()) R.id.menu_arena, R.id.menu_season, R.id.menu_about -> { diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesFragment.kt new file mode 100644 index 0000000..e61be15 --- /dev/null +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesFragment.kt @@ -0,0 +1,112 @@ +package com.ediposouza.teslesgendstracker.ui.matches + +import android.content.Context +import android.os.Bundle +import android.support.v4.app.Fragment +import android.support.v4.app.FragmentManager +import android.support.v4.app.FragmentStatePagerAdapter +import android.support.v4.view.ViewPager +import android.view.* +import com.ediposouza.teslesgendstracker.R +import com.ediposouza.teslesgendstracker.ui.base.BaseFragment +import com.ediposouza.teslesgendstracker.ui.base.CmdShowTabs +import com.ediposouza.teslesgendstracker.ui.matches.tabs.MatchesHistory +import com.ediposouza.teslesgendstracker.ui.matches.tabs.MatchesStatistics +import com.ediposouza.teslesgendstracker.util.MetricScreen +import com.ediposouza.teslesgendstracker.util.MetricsManager +import com.ediposouza.teslesgendstracker.util.inflate +import kotlinx.android.synthetic.main.activity_dash.* +import kotlinx.android.synthetic.main.fragment_matches.* + +/** + * Created by EdipoSouza on 1/3/17. + */ +class MatchesFragment : BaseFragment() { + + private val KEY_PAGE_VIEW_POSITION = "pageViewPositionKey" + + val pageChange = object : ViewPager.SimpleOnPageChangeListener() { + override fun onPageSelected(position: Int) { + updateActivityTitle(position) +// (matches_view_pager.adapter as MatchesPageAdapter).getItem(position).updateCardsList() + MetricsManager.trackScreen(when (position) { + 0 -> MetricScreen.SCREEN_MATCHES_STATISTICS() + else -> MetricScreen.SCREEN_MATCHES_HISTORY() + }) + } + + } + + private fun updateActivityTitle(position: Int) { + activity.toolbar_title?.setText(when (position) { + 0 -> R.string.title_tab_matches_statistics + else -> R.string.title_tab_matches_history + }) + } + + override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return container?.inflate(R.layout.fragment_matches) + } + + override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setHasOptionsMenu(true) + activity.dash_navigation_view.setCheckedItem(R.id.menu_matches) + matches_view_pager.adapter = MatchesPageAdapter(context, childFragmentManager) + matches_view_pager.addOnPageChangeListener(pageChange) + MetricsManager.trackScreen(MetricScreen.SCREEN_MATCHES_STATISTICS()) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + activity.toolbar_title.setText(R.string.title_tab_matches_statistics) + activity.dash_tab_layout.setupWithViewPager(matches_view_pager) + } + + override fun onSaveInstanceState(outState: Bundle?) { + outState?.apply { putInt(KEY_PAGE_VIEW_POSITION, matches_view_pager?.currentItem ?: 0) } + super.onSaveInstanceState(outState) + } + + override fun onViewStateRestored(savedInstanceState: Bundle?) { + super.onViewStateRestored(savedInstanceState) + savedInstanceState?.apply { + matches_view_pager.currentItem = getInt(KEY_PAGE_VIEW_POSITION) + } + } + + override fun onResume() { + super.onResume() + eventBus.post(CmdShowTabs()) + } + + override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { + menu?.clear() + inflater?.inflate(R.menu.menu_season, menu) + super.onCreateOptionsMenu(menu, inflater) + } + + class MatchesPageAdapter(ctx: Context, fm: FragmentManager) : FragmentStatePagerAdapter(fm) { + + var titles: Array = ctx.resources.getStringArray(R.array.matches_tabs) + val matchesStatisticsFragment by lazy { MatchesStatistics() } + val matchesHistoryFragment by lazy { MatchesHistory() } + + override fun getItem(position: Int): Fragment { + return when (position) { + 0 -> matchesStatisticsFragment + else -> matchesHistoryFragment + } + } + + override fun getCount(): Int { + return titles.size + } + + override fun getPageTitle(position: Int): CharSequence { + return titles[position] + } + + } + +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesHistory.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesHistory.kt new file mode 100644 index 0000000..f700d00 --- /dev/null +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesHistory.kt @@ -0,0 +1,24 @@ +package com.ediposouza.teslesgendstracker.ui.matches.tabs + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.ediposouza.teslesgendstracker.R +import com.ediposouza.teslesgendstracker.ui.base.BaseFragment +import com.ediposouza.teslesgendstracker.util.inflate + +/** + * Created by EdipoSouza on 1/3/17. + */ +class MatchesHistory : BaseFragment() { + + override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return container?.inflate(R.layout.fragment_matches_history) + } + + override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + } + +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt new file mode 100644 index 0000000..885448a --- /dev/null +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt @@ -0,0 +1,24 @@ +package com.ediposouza.teslesgendstracker.ui.matches.tabs + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.ediposouza.teslesgendstracker.R +import com.ediposouza.teslesgendstracker.ui.base.BaseFragment +import com.ediposouza.teslesgendstracker.util.inflate + +/** + * Created by EdipoSouza on 1/3/17. + */ +class MatchesStatistics : BaseFragment() { + + override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return container?.inflate(R.layout.fragment_matches_statistics) + } + + override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + } + +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/util/MetricsConstants.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/util/MetricsConstants.kt index 22f7583..03a49fc 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/util/MetricsConstants.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/util/MetricsConstants.kt @@ -104,5 +104,7 @@ sealed class MetricScreen(val name: String) { class SCREEN_DECKS_FAVORED : MetricScreen("DecksFavored") class SCREEN_DECK_DETAILS : MetricScreen("DeckDetails") class SCREEN_NEW_DECKS : MetricScreen("NewDeck") + class SCREEN_MATCHES_STATISTICS : MetricScreen("MatchesStatistics") + class SCREEN_MATCHES_HISTORY : MetricScreen("MatchesHistory") } \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_timer_sand.xml b/app/src/main/res/drawable/ic_timer_sand.xml new file mode 100644 index 0000000..fdcca7b --- /dev/null +++ b/app/src/main/res/drawable/ic_timer_sand.xml @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/main/res/drawable/ic_timer_sand_svg.xml b/app/src/main/res/drawable/ic_timer_sand_svg.xml new file mode 100644 index 0000000..a1d0663 --- /dev/null +++ b/app/src/main/res/drawable/ic_timer_sand_svg.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_matches.xml b/app/src/main/res/layout/fragment_matches.xml new file mode 100644 index 0000000..8a5c304 --- /dev/null +++ b/app/src/main/res/layout/fragment_matches.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_matches_history.xml b/app/src/main/res/layout/fragment_matches_history.xml new file mode 100644 index 0000000..0f36c22 --- /dev/null +++ b/app/src/main/res/layout/fragment_matches_history.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_matches_statistics.xml b/app/src/main/res/layout/fragment_matches_statistics.xml new file mode 100644 index 0000000..0f36c22 --- /dev/null +++ b/app/src/main/res/layout/fragment_matches_statistics.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_drawer.xml b/app/src/main/res/menu/menu_drawer.xml index 87534eb..2781809 100644 --- a/app/src/main/res/menu/menu_drawer.xml +++ b/app/src/main/res/menu/menu_drawer.xml @@ -17,8 +17,7 @@ android:id="@+id/menu_matches" android:title="@string/menu_matches" android:visible="false" - tools:visible="true" - android:enabled="false" /> + tools:visible="true" /> +

+ + + + \ No newline at end of file diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index dd6b54f..9d68321 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -24,4 +24,9 @@ @string/tab_decks_favorites + + @string/tab_matches_statistics + @string/tab_matches_history + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0df728a..3277dbc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -42,6 +42,8 @@ Public Owned Favorites + Statistics + History @string/app_name_full Cards Collection @@ -49,6 +51,8 @@ Decks Public Decks Owned Decks Favorites + Matches Statistics + Matches History All Core From 111041e70a29f0172c65b21f9b34a9fbd75061e1 Mon Sep 17 00:00:00 2001 From: EdipoSouza Date: Wed, 4 Jan 2017 00:49:12 -0200 Subject: [PATCH 13/54] Matches statistics --- app/build.gradle | 1 + .../ui/matches/tabs/MatchesStatistics.kt | 3 + .../matches/tabs/basic/BasicCellViewGroup.kt | 59 ++++++++++++ .../matches/tabs/basic/BasicTableFixHeader.kt | 91 +++++++++++++++++++ .../tabs/basic/BasicTableFixHeaderAdapter.kt | 61 +++++++++++++ app/src/main/res/layout/activity_card.xml | 2 +- app/src/main/res/layout/activity_dash.xml | 2 +- app/src/main/res/layout/activity_deck.xml | 2 +- app/src/main/res/layout/activity_new_deck.xml | 2 +- .../layout/fragment_matches_statistics.xml | 5 + app/src/main/res/layout/itemcell_class.xml | 22 +++++ app/src/main/res/layout/itemcell_result.xml | 19 ++++ app/src/main/res/values/colors.xml | 2 + app/src/main/res/values/dimens.xml | 4 + 14 files changed, 271 insertions(+), 4 deletions(-) create mode 100644 app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/basic/BasicCellViewGroup.kt create mode 100644 app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/basic/BasicTableFixHeader.kt create mode 100644 app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/basic/BasicTableFixHeaderAdapter.kt create mode 100644 app/src/main/res/layout/itemcell_class.xml create mode 100644 app/src/main/res/layout/itemcell_result.xml diff --git a/app/build.gradle b/app/build.gradle index 4dbb44d..0fa0c07 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -117,6 +117,7 @@ dependencies { compile "com.github.emanzanoaxa:RippleEffect:52ea2a0ab6" // compile "com.github.traex.rippleeffect:library:1.3" + compile "com.github.miguelbcr:TableFixHeaders-Wrapper:0.2.0" compile "jp.wasabeef:recyclerview-animators:2.2.4" testCompile "junit:junit:4.12" diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt index 885448a..ac62546 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt @@ -6,7 +6,9 @@ import android.view.View import android.view.ViewGroup import com.ediposouza.teslesgendstracker.R import com.ediposouza.teslesgendstracker.ui.base.BaseFragment +import com.ediposouza.teslesgendstracker.ui.matches.tabs.basic.BasicTableFixHeader import com.ediposouza.teslesgendstracker.util.inflate +import kotlinx.android.synthetic.main.fragment_matches_statistics.* /** * Created by EdipoSouza on 1/3/17. @@ -19,6 +21,7 @@ class MatchesStatistics : BaseFragment() { override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + matches_statistics_table.adapter = BasicTableFixHeader(context).instance } } \ No newline at end of file diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/basic/BasicCellViewGroup.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/basic/BasicCellViewGroup.kt new file mode 100644 index 0000000..84d60c4 --- /dev/null +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/basic/BasicCellViewGroup.kt @@ -0,0 +1,59 @@ +package com.ediposouza.teslesgendstracker.ui.matches.tabs.basic + +import android.content.Context +import android.graphics.Typeface +import android.util.AttributeSet +import android.view.View +import android.widget.FrameLayout +import android.widget.TextView +import com.ediposouza.teslesgendstracker.R +import miguelbcr.ui.tableFixHeadesWrapper.TableFixHeaderAdapter + + +/** + * Created by miguel on 09/02/2016. + */ +class BasicCellViewGroup : FrameLayout, TableFixHeaderAdapter.FirstHeaderBinder, TableFixHeaderAdapter.HeaderBinder, TableFixHeaderAdapter.FirstBodyBinder>, TableFixHeaderAdapter.BodyBinder>, TableFixHeaderAdapter.SectionBinder> { + + private var ctx: Context? = null + var textView: TextView? = null + var vg_root: View? = null + + constructor(ctx: Context) : super(ctx) { + this.ctx = ctx + } + + constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs) { + this.ctx = ctx + } + + init { + View.inflate(context, R.layout.itemcell_result, this) + textView = rootView.findViewById(R.id.cell_result) as TextView + vg_root = rootView.findViewById(R.id.cell_bg) + } + + override fun bindFirstHeader(headerName: String) { + textView?.text = headerName + textView?.setTypeface(null, Typeface.BOLD) + } + + override fun bindHeader(headerName: String, column: Int) { + textView?.text = headerName + textView?.setTypeface(null, Typeface.BOLD) + } + + override fun bindFirstBody(items: List, row: Int) { + textView?.text = items[0] + textView?.setTypeface(null, Typeface.NORMAL) + } + + override fun bindBody(items: List, row: Int, column: Int) { + textView?.text = items[column + 1] + textView?.setTypeface(null, Typeface.NORMAL) + } + + override fun bindSection(item: List, row: Int, column: Int) { + textView?.text = if (column == 0) "Section" else "" + } +} diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/basic/BasicTableFixHeader.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/basic/BasicTableFixHeader.kt new file mode 100644 index 0000000..b1cf342 --- /dev/null +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/basic/BasicTableFixHeader.kt @@ -0,0 +1,91 @@ +package com.ediposouza.teslesgendstracker.ui.matches.tabs.basic + +import android.content.Context +import android.support.design.widget.Snackbar +import android.support.v4.content.ContextCompat +import com.ediposouza.teslesgendstracker.R +import com.inqbarna.tablefixheaders.adapters.BaseTableAdapter +import miguelbcr.ui.tableFixHeadesWrapper.TableFixHeaderAdapter +import java.util.* + +/** + * Created by miguel on 12/02/2016. + */ +class BasicTableFixHeader(private val context: Context) { + + val instance: BaseTableAdapter + get() { + val adapter = BasicTableFixHeaderAdapter(context) + val body = body + + adapter.setFirstHeader("FH") + adapter.header = header + adapter.setFirstBody(body) + adapter.body = body + adapter.setSection(body) + + setListeners(adapter) + + return adapter + } + + private fun setListeners(adapter: BasicTableFixHeaderAdapter) { + val clickListenerHeader = TableFixHeaderAdapter.ClickListener { s, viewGroup, row, column -> + viewGroup.vg_root?.setBackgroundColor(ContextCompat.getColor(context, R.color.colorAccent)) + Snackbar.make(viewGroup, "Click on " + viewGroup.textView?.text + " (" + row + "," + column + ")", Snackbar.LENGTH_SHORT).show() + } + val clickListenerBody = TableFixHeaderAdapter.ClickListener, BasicCellViewGroup> { array, viewGroup, row, column -> + viewGroup.vg_root?.setBackgroundColor(ContextCompat.getColor(context, R.color.colorYellow)) + Snackbar.make(viewGroup, "Click on " + viewGroup.textView?.text + " (" + row + "," + column + ")", Snackbar.LENGTH_SHORT).show() + } + + val longClickListenerHeader = TableFixHeaderAdapter.LongClickListener { s, viewGroup, row, column -> + viewGroup.vg_root?.setBackgroundColor(ContextCompat.getColor(context, R.color.colorPrimary)) + Snackbar.make(viewGroup, "LongClick on " + viewGroup.textView?.text + " (" + row + "," + column + ")", Snackbar.LENGTH_SHORT).show() + } + + val longClickListenerBody = TableFixHeaderAdapter.LongClickListener, BasicCellViewGroup> { array, viewGroup, row, column -> + viewGroup.vg_root?.setBackgroundColor(ContextCompat.getColor(context, R.color.colorBlue)) + Snackbar.make(viewGroup, "LongClick on " + viewGroup.textView?.text + " (" + row + "," + column + ")", Snackbar.LENGTH_SHORT).show() + } + + adapter.setClickListenerFirstHeader(clickListenerHeader) + adapter.setLongClickListenerFirstHeader(longClickListenerHeader) + adapter.setClickListenerHeader(clickListenerHeader) + adapter.setLongClickListenerHeader(longClickListenerHeader) + adapter.setClickListenerFirstBody(clickListenerBody) + adapter.setLongClickListenerFirstBody(longClickListenerBody) + adapter.setClickListenerBody(clickListenerBody) + adapter.setLongClickListenerBody(longClickListenerBody) + adapter.setClickListenerSection(clickListenerBody) + adapter.setLongClickListenerSection(longClickListenerBody) + } + + private val header: List + get() { + val header = ArrayList() + + for (i in 0..19) + header.add("H " + (i + 1)) + + return header + } + + private val body: List> + get() { + val rows = ArrayList>() + + for (row in 1..100) { + val cols = ArrayList() + + for (col in 0..29) { + val type = if (col == 0) "FB" else "B" + cols.add("$type ($row, $col)") + } + + rows.add(cols) + } + + return rows + } +} diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/basic/BasicTableFixHeaderAdapter.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/basic/BasicTableFixHeaderAdapter.kt new file mode 100644 index 0000000..1992ba5 --- /dev/null +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/basic/BasicTableFixHeaderAdapter.kt @@ -0,0 +1,61 @@ +package com.ediposouza.teslesgendstracker.ui.matches.tabs.basic + +import android.content.Context +import com.ediposouza.teslesgendstracker.R +import miguelbcr.ui.tableFixHeadesWrapper.TableFixHeaderAdapter +import java.util.* + + +/** + * Created by miguel on 11/02/2016. + */ +class BasicTableFixHeaderAdapter(private val context: Context) : TableFixHeaderAdapter, BasicCellViewGroup, BasicCellViewGroup, BasicCellViewGroup>(context) { + + override fun inflateFirstHeader(): BasicCellViewGroup { + return BasicCellViewGroup(context) + } + + override fun inflateHeader(): BasicCellViewGroup { + return BasicCellViewGroup(context) + } + + override fun inflateFirstBody(): BasicCellViewGroup { + return BasicCellViewGroup(context) + } + + override fun inflateBody(): BasicCellViewGroup { + return BasicCellViewGroup(context) + } + + override fun inflateSection(): BasicCellViewGroup { + return BasicCellViewGroup(context) + } + + override fun getHeaderWidths(): List { + val headerWidths = ArrayList() + + // First header + headerWidths.add(context.resources.getDimension(R.dimen._150dp).toInt()) + + for (i in 0..19) + headerWidths.add(context.resources.getDimension(R.dimen._100dp).toInt()) + + return headerWidths + } + + override fun getHeaderHeight(): Int { + return context.resources.getDimension(R.dimen._40dp).toInt() + } + + override fun getSectionHeight(): Int { + return context.resources.getDimension(R.dimen._40dp).toInt() + } + + override fun getBodyHeight(): Int { + return context.resources.getDimension(R.dimen._40dp).toInt() + } + + override fun isSection(items: List>, row: Int): Boolean { + return row % 10 == 0 + } +} diff --git a/app/src/main/res/layout/activity_card.xml b/app/src/main/res/layout/activity_card.xml index 8b3694f..a8aeb8b 100644 --- a/app/src/main/res/layout/activity_card.xml +++ b/app/src/main/res/layout/activity_card.xml @@ -6,7 +6,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:animateLayoutChanges="true" - tools:context="com.ediposouza.teslesgendstracker.ui.CardActivity"> + tools:ctx="com.ediposouza.teslesgendstracker.ui.CardActivity"> + tools:ctx="com.ediposouza.teslesgendstracker.ui.DashActivity"> + tools:ctx="com.ediposouza.teslesgendstracker.ui.DeckActivity"> + tools:ctx="com.ediposouza.teslesgendstracker.ui.decks.new.NewDeckActivity"> + + \ No newline at end of file diff --git a/app/src/main/res/layout/itemcell_class.xml b/app/src/main/res/layout/itemcell_class.xml new file mode 100644 index 0000000..06006e2 --- /dev/null +++ b/app/src/main/res/layout/itemcell_class.xml @@ -0,0 +1,22 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/itemcell_result.xml b/app/src/main/res/layout/itemcell_result.xml new file mode 100644 index 0000000..23e7d92 --- /dev/null +++ b/app/src/main/res/layout/itemcell_result.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 8ccc111..8141b51 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -32,4 +32,6 @@ #e57373 #C62828 + #6f99ff + #FFD54F diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index ea1f938..6895e2a 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -72,4 +72,8 @@ 48dp 8dp + 150dp + 100dp + 40dp + From 2fd938e5aed71c1e87f478832a8296ad853b47f6 Mon Sep 17 00:00:00 2001 From: EdipoSouza Date: Thu, 5 Jan 2017 00:38:45 -0200 Subject: [PATCH 14/54] Matches statistics Table --- .../teslesgendstracker/data/User.kt | 11 +- .../teslesgendstracker/ui/DashActivity.kt | 2 +- .../ui/matches/tabs/MatchesStatistics.kt | 145 +++++++++++++++++- .../matches/tabs/basic/BasicCellViewGroup.kt | 59 ------- .../matches/tabs/basic/BasicTableFixHeader.kt | 91 ----------- .../tabs/basic/BasicTableFixHeaderAdapter.kt | 61 -------- .../ui/widget/CollectionStatistics.kt | 4 +- .../ui/widget/CollectionStatisticsAttr.kt | 10 +- .../teslesgendstracker/util/TestUtils.kt | 48 ++++++ .../layout/fragment_matches_statistics.xml | 3 +- app/src/main/res/layout/itemcell_class.xml | 15 +- app/src/main/res/layout/itemcell_result.xml | 19 --- app/src/main/res/layout/itemcell_text.xml | 10 ++ .../layout/widget_collection_statistics.xml | 2 +- app/src/main/res/values/dimens.xml | 6 +- app/src/main/res/values/strings.xml | 11 +- 16 files changed, 241 insertions(+), 256 deletions(-) delete mode 100644 app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/basic/BasicCellViewGroup.kt delete mode 100644 app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/basic/BasicTableFixHeader.kt delete mode 100644 app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/basic/BasicTableFixHeaderAdapter.kt create mode 100644 app/src/main/kotlin/com/ediposouza/teslesgendstracker/util/TestUtils.kt delete mode 100644 app/src/main/res/layout/itemcell_result.xml create mode 100644 app/src/main/res/layout/itemcell_text.xml diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/data/User.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/data/User.kt index a533e5a..6a523f7 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/data/User.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/data/User.kt @@ -25,16 +25,17 @@ data class Match( val player: MatchDeck, val opponent: MatchDeck, - val result: Boolean, - val rack: Int, + val win: Boolean, + val rank: Int, val legend: Boolean ) data class MatchDeck( - val name: String?, - val cls: Class?, - val type: DeckType? + val name: String, + val cls: Class, + val type: DeckType, + val version: String ) \ No newline at end of file diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DashActivity.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DashActivity.kt index b85374d..66ba745 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DashActivity.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DashActivity.kt @@ -183,7 +183,7 @@ class DashActivity : BaseFilterActivity(), allAttrCards.map { it.shortName }.contains(it.key) }.values.sum() Timber.d("Out: ${it.filter { !allAttrCards.map { it.shortName }.contains(it.key) }}") - val stringPercent = getString(R.string.statistics_percent, + val stringPercent = getString(R.string.collection_statistics_percent, if (allCardsTotal > 0) userCardsTotal.toFloat() / allCardsTotal.toFloat() * 100f else 0f) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt index ac62546..e9bf78d 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt @@ -1,27 +1,168 @@ package com.ediposouza.teslesgendstracker.ui.matches.tabs +import android.content.Context import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.FrameLayout import com.ediposouza.teslesgendstracker.R +import com.ediposouza.teslesgendstracker.data.Attribute +import com.ediposouza.teslesgendstracker.data.Class +import com.ediposouza.teslesgendstracker.data.Match import com.ediposouza.teslesgendstracker.ui.base.BaseFragment -import com.ediposouza.teslesgendstracker.ui.matches.tabs.basic.BasicTableFixHeader +import com.ediposouza.teslesgendstracker.util.TestUtils import com.ediposouza.teslesgendstracker.util.inflate import kotlinx.android.synthetic.main.fragment_matches_statistics.* +import kotlinx.android.synthetic.main.itemcell_class.view.* +import kotlinx.android.synthetic.main.itemcell_text.view.* +import miguelbcr.ui.tableFixHeadesWrapper.TableFixHeaderAdapter +import java.util.* /** * Created by EdipoSouza on 1/3/17. */ class MatchesStatistics : BaseFragment() { + val results = HashMap(Class.values().map { it to mutableListOf() }.toMap()) + override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { return container?.inflate(R.layout.fragment_matches_statistics) } override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - matches_statistics_table.adapter = BasicTableFixHeader(context).instance + matches_statistics_table.adapter = StatisticsTableAdapter(context).apply { + setFirstHeader("Vs") + val classTotal: Class? = null + header = Class.values().asList().plus(classTotal) + setFirstBody(Class.values().map { listOf(BodyItem(it)) }.plus(listOf(listOf(BodyItem())))) + updateStatisticsData(this) + setSection(listOf()) + } + getMatches() + } + + private fun getMatches() { + val matches = TestUtils.getTestMatches() + matches.groupBy { it.player.cls }.forEach { + results[it.key]?.addAll(it.value) + updateStatisticsData(matches_statistics_table.adapter as StatisticsTableAdapter) + } + } + + private fun updateStatisticsData(statisticsTableAdapter: StatisticsTableAdapter) { + statisticsTableAdapter.body = mutableListOf>().apply { + Class.values().forEach { myCls -> + add(mutableListOf().apply { + val resByMyCls = results[myCls]!! + Class.values().forEach { opponentCls -> + val matchesVsOpponent = resByMyCls.filter { it.opponent.cls == opponentCls } + add(getWinLossBodyItem(matchesVsOpponent)) + } + add(getWinLossBodyItem(resByMyCls)) + }) + } + val allMatches = results.flatMap { it.value } + add(mutableListOf().apply { + Class.values().forEach { + val resByOpponent = allMatches.groupBy { it.opponent.cls }[it] ?: listOf() + add(getWinLossBodyItem(resByOpponent)) + } + add(getWinLossBodyItem(allMatches)) + }) + } + } + + private fun getWinLossBodyItem(matches: List): BodyItem { + val result = matches.groupBy { it.win } + return BodyItem(result = "${result[true]?.size ?: 0}/${result[false]?.size ?: 0}") + } + + class BodyItem(val cls: Class? = null, val result: String? = null) + + class StatisticsTableAdapter(val context: Context) : TableFixHeaderAdapter, CellClass, CellTextCenter, CellTextCenter>(context) { + + override fun inflateFirstHeader() = CellTextCenter(context) + + override fun inflateHeader() = CellClass(context) + + override fun inflateFirstBody() = CellClass(context) + + override fun inflateBody() = CellTextCenter(context) + + override fun inflateSection() = CellTextCenter(context) + + override fun getHeaderHeight() = context.resources.getDimensionPixelSize(R.dimen.match_statistics_cell_height) + + override fun getHeaderWidths(): List { + val headerWidth = context.resources.getDimensionPixelSize(R.dimen.match_statistics_header_width) + val cellWidth = context.resources.getDimensionPixelSize(R.dimen.match_statistics_cell_width) + val colWidths = mutableListOf(headerWidth) + Class.values().forEach { colWidths.add(cellWidth) } + colWidths.add(headerWidth) + return colWidths + } + + override fun getBodyHeight() = context.resources.getDimensionPixelSize(R.dimen.match_statistics_cell_height) + + override fun isSection(items: List>?, row: Int): Boolean = false + + override fun getSectionHeight() = 0 + + } + + class CellClass(context: Context) : FrameLayout(context), + TableFixHeaderAdapter.HeaderBinder, + TableFixHeaderAdapter.FirstBodyBinder> { + + init { + LayoutInflater.from(context).inflate(R.layout.itemcell_class, this, true) + } + + override fun bindHeader(cls: Class?, col: Int) { + bindClass(cls) + } + + override fun bindFirstBody(bodyItems: List, row: Int) { + bindClass(bodyItems[0].cls) + } + + private fun bindClass(cls: Class?) { + with(rootView) { + val attr1Visibility = if (cls != null) View.VISIBLE else View.GONE + val attr2Visibility = if (cls?.attr2 != Attribute.NEUTRAL) attr1Visibility else View.GONE + cell_class_attr1.visibility = attr1Visibility + cell_class_attr2.visibility = attr2Visibility + cell_class_attr1.setImageResource(cls?.attr1?.imageRes ?: 0) + cell_class_attr2.setImageResource(cls?.attr2?.imageRes ?: 0) + cell_total.visibility = if (cls == null) View.VISIBLE else View.GONE + } + } + + } + + class CellTextCenter(context: Context) : FrameLayout(context), + TableFixHeaderAdapter.FirstHeaderBinder, + TableFixHeaderAdapter.BodyBinder>, + TableFixHeaderAdapter.SectionBinder> { + + init { + LayoutInflater.from(context).inflate(R.layout.itemcell_text, this, true) + } + + override fun bindFirstHeader(result: String) { + rootView.cell_text.text = result + } + + override fun bindBody(bodyItems: List, row: Int, col: Int) { + rootView.cell_text.text = bodyItems[col].result + } + + override fun bindSection(bodyItems: List, row: Int, col: Int) { + } + } } \ No newline at end of file diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/basic/BasicCellViewGroup.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/basic/BasicCellViewGroup.kt deleted file mode 100644 index 84d60c4..0000000 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/basic/BasicCellViewGroup.kt +++ /dev/null @@ -1,59 +0,0 @@ -package com.ediposouza.teslesgendstracker.ui.matches.tabs.basic - -import android.content.Context -import android.graphics.Typeface -import android.util.AttributeSet -import android.view.View -import android.widget.FrameLayout -import android.widget.TextView -import com.ediposouza.teslesgendstracker.R -import miguelbcr.ui.tableFixHeadesWrapper.TableFixHeaderAdapter - - -/** - * Created by miguel on 09/02/2016. - */ -class BasicCellViewGroup : FrameLayout, TableFixHeaderAdapter.FirstHeaderBinder, TableFixHeaderAdapter.HeaderBinder, TableFixHeaderAdapter.FirstBodyBinder>, TableFixHeaderAdapter.BodyBinder>, TableFixHeaderAdapter.SectionBinder> { - - private var ctx: Context? = null - var textView: TextView? = null - var vg_root: View? = null - - constructor(ctx: Context) : super(ctx) { - this.ctx = ctx - } - - constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs) { - this.ctx = ctx - } - - init { - View.inflate(context, R.layout.itemcell_result, this) - textView = rootView.findViewById(R.id.cell_result) as TextView - vg_root = rootView.findViewById(R.id.cell_bg) - } - - override fun bindFirstHeader(headerName: String) { - textView?.text = headerName - textView?.setTypeface(null, Typeface.BOLD) - } - - override fun bindHeader(headerName: String, column: Int) { - textView?.text = headerName - textView?.setTypeface(null, Typeface.BOLD) - } - - override fun bindFirstBody(items: List, row: Int) { - textView?.text = items[0] - textView?.setTypeface(null, Typeface.NORMAL) - } - - override fun bindBody(items: List, row: Int, column: Int) { - textView?.text = items[column + 1] - textView?.setTypeface(null, Typeface.NORMAL) - } - - override fun bindSection(item: List, row: Int, column: Int) { - textView?.text = if (column == 0) "Section" else "" - } -} diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/basic/BasicTableFixHeader.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/basic/BasicTableFixHeader.kt deleted file mode 100644 index b1cf342..0000000 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/basic/BasicTableFixHeader.kt +++ /dev/null @@ -1,91 +0,0 @@ -package com.ediposouza.teslesgendstracker.ui.matches.tabs.basic - -import android.content.Context -import android.support.design.widget.Snackbar -import android.support.v4.content.ContextCompat -import com.ediposouza.teslesgendstracker.R -import com.inqbarna.tablefixheaders.adapters.BaseTableAdapter -import miguelbcr.ui.tableFixHeadesWrapper.TableFixHeaderAdapter -import java.util.* - -/** - * Created by miguel on 12/02/2016. - */ -class BasicTableFixHeader(private val context: Context) { - - val instance: BaseTableAdapter - get() { - val adapter = BasicTableFixHeaderAdapter(context) - val body = body - - adapter.setFirstHeader("FH") - adapter.header = header - adapter.setFirstBody(body) - adapter.body = body - adapter.setSection(body) - - setListeners(adapter) - - return adapter - } - - private fun setListeners(adapter: BasicTableFixHeaderAdapter) { - val clickListenerHeader = TableFixHeaderAdapter.ClickListener { s, viewGroup, row, column -> - viewGroup.vg_root?.setBackgroundColor(ContextCompat.getColor(context, R.color.colorAccent)) - Snackbar.make(viewGroup, "Click on " + viewGroup.textView?.text + " (" + row + "," + column + ")", Snackbar.LENGTH_SHORT).show() - } - val clickListenerBody = TableFixHeaderAdapter.ClickListener, BasicCellViewGroup> { array, viewGroup, row, column -> - viewGroup.vg_root?.setBackgroundColor(ContextCompat.getColor(context, R.color.colorYellow)) - Snackbar.make(viewGroup, "Click on " + viewGroup.textView?.text + " (" + row + "," + column + ")", Snackbar.LENGTH_SHORT).show() - } - - val longClickListenerHeader = TableFixHeaderAdapter.LongClickListener { s, viewGroup, row, column -> - viewGroup.vg_root?.setBackgroundColor(ContextCompat.getColor(context, R.color.colorPrimary)) - Snackbar.make(viewGroup, "LongClick on " + viewGroup.textView?.text + " (" + row + "," + column + ")", Snackbar.LENGTH_SHORT).show() - } - - val longClickListenerBody = TableFixHeaderAdapter.LongClickListener, BasicCellViewGroup> { array, viewGroup, row, column -> - viewGroup.vg_root?.setBackgroundColor(ContextCompat.getColor(context, R.color.colorBlue)) - Snackbar.make(viewGroup, "LongClick on " + viewGroup.textView?.text + " (" + row + "," + column + ")", Snackbar.LENGTH_SHORT).show() - } - - adapter.setClickListenerFirstHeader(clickListenerHeader) - adapter.setLongClickListenerFirstHeader(longClickListenerHeader) - adapter.setClickListenerHeader(clickListenerHeader) - adapter.setLongClickListenerHeader(longClickListenerHeader) - adapter.setClickListenerFirstBody(clickListenerBody) - adapter.setLongClickListenerFirstBody(longClickListenerBody) - adapter.setClickListenerBody(clickListenerBody) - adapter.setLongClickListenerBody(longClickListenerBody) - adapter.setClickListenerSection(clickListenerBody) - adapter.setLongClickListenerSection(longClickListenerBody) - } - - private val header: List - get() { - val header = ArrayList() - - for (i in 0..19) - header.add("H " + (i + 1)) - - return header - } - - private val body: List> - get() { - val rows = ArrayList>() - - for (row in 1..100) { - val cols = ArrayList() - - for (col in 0..29) { - val type = if (col == 0) "FB" else "B" - cols.add("$type ($row, $col)") - } - - rows.add(cols) - } - - return rows - } -} diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/basic/BasicTableFixHeaderAdapter.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/basic/BasicTableFixHeaderAdapter.kt deleted file mode 100644 index 1992ba5..0000000 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/basic/BasicTableFixHeaderAdapter.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.ediposouza.teslesgendstracker.ui.matches.tabs.basic - -import android.content.Context -import com.ediposouza.teslesgendstracker.R -import miguelbcr.ui.tableFixHeadesWrapper.TableFixHeaderAdapter -import java.util.* - - -/** - * Created by miguel on 11/02/2016. - */ -class BasicTableFixHeaderAdapter(private val context: Context) : TableFixHeaderAdapter, BasicCellViewGroup, BasicCellViewGroup, BasicCellViewGroup>(context) { - - override fun inflateFirstHeader(): BasicCellViewGroup { - return BasicCellViewGroup(context) - } - - override fun inflateHeader(): BasicCellViewGroup { - return BasicCellViewGroup(context) - } - - override fun inflateFirstBody(): BasicCellViewGroup { - return BasicCellViewGroup(context) - } - - override fun inflateBody(): BasicCellViewGroup { - return BasicCellViewGroup(context) - } - - override fun inflateSection(): BasicCellViewGroup { - return BasicCellViewGroup(context) - } - - override fun getHeaderWidths(): List { - val headerWidths = ArrayList() - - // First header - headerWidths.add(context.resources.getDimension(R.dimen._150dp).toInt()) - - for (i in 0..19) - headerWidths.add(context.resources.getDimension(R.dimen._100dp).toInt()) - - return headerWidths - } - - override fun getHeaderHeight(): Int { - return context.resources.getDimension(R.dimen._40dp).toInt() - } - - override fun getSectionHeight(): Int { - return context.resources.getDimension(R.dimen._40dp).toInt() - } - - override fun getBodyHeight(): Int { - return context.resources.getDimension(R.dimen._40dp).toInt() - } - - override fun isSection(items: List>, row: Int): Boolean { - return row % 10 == 0 - } -} diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/widget/CollectionStatistics.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/widget/CollectionStatistics.kt index ce24283..d6ea677 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/widget/CollectionStatistics.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/widget/CollectionStatistics.kt @@ -77,8 +77,8 @@ class CollectionStatistics(ctx: Context?, attrs: AttributeSet?, defStyleAttr: In rarity_statistics_endurance.soulMissing + rarity_statistics_dual.soulMissing + rarity_statistics_neutral.soulMissing val percent = if (total > 0) owned.toFloat() / total.toFloat() * 100f else 0f - collection_statistics_total.text = context.getString(R.string.statistics_total, owned, total) - collection_statistics_percent.text = context.getString(R.string.statistics_percent, percent) + collection_statistics_total.text = context.getString(R.string.collection_statistics_total, owned, total) + collection_statistics_percent.text = context.getString(R.string.collection_statistics_percent, percent) collection_statistics_soul.text = NumberFormat.getNumberInstance().format(soulMissing) } } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/widget/CollectionStatisticsAttr.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/widget/CollectionStatisticsAttr.kt index 9b35308..a7e4037 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/widget/CollectionStatisticsAttr.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/widget/CollectionStatisticsAttr.kt @@ -37,25 +37,25 @@ class CollectionStatisticsAttr(ctx: Context?, attrs: AttributeSet?, defStyleAttr constructor(ctx: Context?, attrs: AttributeSet) : this(ctx, attrs, 0) fun setCommon(owned: Int, total: Int) { - attr_statistics_common.text = context.getString(R.string.statistics_rarity, owned, total) + attr_statistics_common.text = context.getString(R.string.collection_statistics_rarity, owned, total) cards[CardRarity.COMMON] = Pair(owned, total) updateTotal() } fun setRare(owned: Int, total: Int) { - attr_statistics_rare.text = context.getString(R.string.statistics_rarity, owned, total) + attr_statistics_rare.text = context.getString(R.string.collection_statistics_rarity, owned, total) cards[CardRarity.RARE] = Pair(owned, total) updateTotal() } fun setEpic(owned: Int, total: Int) { - attr_statistics_epic.text = context.getString(R.string.statistics_rarity, owned, total) + attr_statistics_epic.text = context.getString(R.string.collection_statistics_rarity, owned, total) cards[CardRarity.EPIC] = Pair(owned, total) updateTotal() } fun setLegendary(owned: Int, total: Int) { - attr_statistics_legendary.text = context.getString(R.string.statistics_rarity, owned, total) + attr_statistics_legendary.text = context.getString(R.string.collection_statistics_rarity, owned, total) cards[CardRarity.LEGENDARY] = Pair(owned, total) updateTotal() } @@ -64,7 +64,7 @@ class CollectionStatisticsAttr(ctx: Context?, attrs: AttributeSet?, defStyleAttr owned = cards.map { it.value.first }.sum() total = cards.map { it.value.second }.sum() soulMissing = cards.map { (it.value.second - it.value.first) * it.key.soulCost }.sum() - attr_statistics_total.text = context.getString(R.string.statistics_total, owned, total) + attr_statistics_total.text = context.getString(R.string.collection_statistics_total, owned, total) } } \ No newline at end of file diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/util/TestUtils.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/util/TestUtils.kt new file mode 100644 index 0000000..ee27def --- /dev/null +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/util/TestUtils.kt @@ -0,0 +1,48 @@ +package com.ediposouza.teslesgendstracker.util + +import com.ediposouza.teslesgendstracker.data.Class +import com.ediposouza.teslesgendstracker.data.DeckType +import com.ediposouza.teslesgendstracker.data.Match +import com.ediposouza.teslesgendstracker.data.MatchDeck + +/** + * Created by EdipoSouza on 1/5/17. + */ +object TestUtils { + + fun getTestMatches(): List { + val decks = listOf( + MatchDeck("", Class.ARCHER, DeckType.AGGRO, ""), + MatchDeck("", Class.ASSASSIN, DeckType.AGGRO, ""), + MatchDeck("", Class.BATTLEMAGE, DeckType.AGGRO, ""), + MatchDeck("", Class.CRUSADER, DeckType.AGGRO, ""), + MatchDeck("", Class.MAGE, DeckType.AGGRO, ""), + MatchDeck("", Class.MONK, DeckType.AGGRO, ""), + MatchDeck("", Class.SCOUT, DeckType.AGGRO, ""), + MatchDeck("", Class.SORCERER, DeckType.AGGRO, ""), + MatchDeck("", Class.SPELLSWORD, DeckType.AGGRO, ""), + MatchDeck("", Class.WARRIOR, DeckType.AGGRO, ""), + MatchDeck("", Class.STRENGTH, DeckType.AGGRO, ""), + MatchDeck("", Class.INTELLIGENCE, DeckType.AGGRO, ""), + MatchDeck("", Class.AGILITY, DeckType.AGGRO, ""), + MatchDeck("", Class.WILLPOWER, DeckType.AGGRO, ""), + MatchDeck("", Class.ENDURANCE, DeckType.AGGRO, ""), + MatchDeck("", Class.NEUTRAL, DeckType.AGGRO, "") + ) + return mutableListOf().apply { + for ((indexPlayer, playerDeck) in decks.withIndex()) { + for ((indexOpponent, opponentDeck) in decks.withIndex()) { + addAll(mutableListOf().apply { + for (i in 1..indexPlayer + 1) { + add(Match(playerDeck, opponentDeck, true, 0, false)) + } + for (i in 1..indexOpponent + 1) { + add(Match(playerDeck, opponentDeck, false, 0, false)) + } + }) + } + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_matches_statistics.xml b/app/src/main/res/layout/fragment_matches_statistics.xml index a1e264c..1faa834 100644 --- a/app/src/main/res/layout/fragment_matches_statistics.xml +++ b/app/src/main/res/layout/fragment_matches_statistics.xml @@ -6,6 +6,7 @@ + android:layout_height="match_parent" + android:layout_marginBottom="@dimen/navigation_bar_height" /> \ No newline at end of file diff --git a/app/src/main/res/layout/itemcell_class.xml b/app/src/main/res/layout/itemcell_class.xml index 06006e2..73d7acc 100644 --- a/app/src/main/res/layout/itemcell_class.xml +++ b/app/src/main/res/layout/itemcell_class.xml @@ -1,8 +1,9 @@ @@ -10,6 +11,7 @@ android:id="@+id/cell_class_attr1" android:layout_width="@dimen/size_small" android:layout_height="@dimen/size_small" + android:layout_marginStart="@dimen/default_margin" android:src="@drawable/attr_strength" /> + + \ No newline at end of file diff --git a/app/src/main/res/layout/itemcell_result.xml b/app/src/main/res/layout/itemcell_result.xml deleted file mode 100644 index 23e7d92..0000000 --- a/app/src/main/res/layout/itemcell_result.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/itemcell_text.xml b/app/src/main/res/layout/itemcell_text.xml new file mode 100644 index 0000000..b8fc772 --- /dev/null +++ b/app/src/main/res/layout/itemcell_text.xml @@ -0,0 +1,10 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/widget_collection_statistics.xml b/app/src/main/res/layout/widget_collection_statistics.xml index 3ea8969..217aac6 100644 --- a/app/src/main/res/layout/widget_collection_statistics.xml +++ b/app/src/main/res/layout/widget_collection_statistics.xml @@ -158,7 +158,7 @@ android:layout_alignTop="@id/collection_statistics_pack" android:layout_toEndOf="@id/collection_statistics_pack" android:gravity="center_vertical" - android:text="@string/statistics_total_cards" + android:text="@string/collection_statistics_total_cards" android:textSize="@dimen/font_medium" /> 48dp 8dp - 150dp - 100dp - 40dp + 48dp + 72dp + 88dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3277dbc..25ea174 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -76,10 +76,10 @@ Sign in with Twitter 0/0 - %1$02d/%2$02d - %1$03d/%2$03d - %1$.2f%% - Total Cards\nSoul to complete + %1$02d/%2$02d + %1$03d/%2$03d + %1$.2f%% + Total Cards\nSoul to complete Details Create by @@ -112,4 +112,7 @@ Deck saved as Private Please complete your deck before save it + Total + %1$.1f%% + From 2a22474078a86db37f58cf6d8640d03ab47314e1 Mon Sep 17 00:00:00 2001 From: EdipoSouza Date: Fri, 6 Jan 2017 00:32:16 -0200 Subject: [PATCH 15/54] Matches statistics percent winrate --- .../teslesgendstracker/ui/DeckActivity.kt | 5 ++- .../ui/decks/tabs/DecksOwnerFragment.kt | 3 +- .../ui/matches/tabs/MatchesStatistics.kt | 44 +++++++++++++------ app/src/main/res/drawable/ic_percent_menu.xml | 4 ++ .../res/drawable/ic_percent_menu_checked.xml | 4 ++ .../drawable/ic_percent_menu_checked_svg.xml | 10 +++++ .../main/res/drawable/ic_percent_menu_svg.xml | 10 +++++ .../drawable/ic_private_menu_checked_svg.xml | 4 +- .../main/res/drawable/ic_private_menu_svg.xml | 4 +- app/src/main/res/drawable/xml_percent.xml | 37 ++++++++++++++++ app/src/main/res/drawable/xml_switch.xml | 6 +-- .../main/res/layout/widget_switch_percent.xml | 5 +++ .../{menu_deck_owner.xml => menu_delete.xml} | 6 --- .../res/menu/{menu_deck.xml => menu_like.xml} | 0 app/src/main/res/menu/menu_percent.xml | 13 ++++++ ...{menu_decks_owned.xml => menu_private.xml} | 7 --- app/src/main/res/values/dimens.xml | 5 ++- app/src/main/res/values/strings.xml | 1 + 18 files changed, 130 insertions(+), 38 deletions(-) create mode 100644 app/src/main/res/drawable/ic_percent_menu.xml create mode 100644 app/src/main/res/drawable/ic_percent_menu_checked.xml create mode 100644 app/src/main/res/drawable/ic_percent_menu_checked_svg.xml create mode 100644 app/src/main/res/drawable/ic_percent_menu_svg.xml create mode 100644 app/src/main/res/drawable/xml_percent.xml create mode 100644 app/src/main/res/layout/widget_switch_percent.xml rename app/src/main/res/menu/{menu_deck_owner.xml => menu_delete.xml} (65%) rename app/src/main/res/menu/{menu_deck.xml => menu_like.xml} (100%) create mode 100644 app/src/main/res/menu/menu_percent.xml rename app/src/main/res/menu/{menu_decks_owned.xml => menu_private.xml} (62%) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DeckActivity.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DeckActivity.kt index 52859fd..af9c4f9 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DeckActivity.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DeckActivity.kt @@ -187,7 +187,10 @@ class DeckActivity : BaseActivity() { } override fun onCreateOptionsMenu(menu: Menu?): Boolean { - menuInflater.inflate(if (deckOwned) R.menu.menu_deck_owner else R.menu.menu_deck, menu) + if (deckOwned) { + menuInflater.inflate(R.menu.menu_delete, menu) + } + menuInflater.inflate(R.menu.menu_like, menu) menuLike = menu?.findItem(R.id.menu_like) menuLike?.isVisible = !deckOwned updateLikeItem() diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksOwnerFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksOwnerFragment.kt index ed62624..b20eab4 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksOwnerFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksOwnerFragment.kt @@ -32,7 +32,8 @@ class DecksOwnerFragment : DecksPublicFragment() { override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { menu?.clear() - inflater?.inflate(R.menu.menu_decks_owned, menu) + inflater?.inflate(R.menu.menu_private, menu) + inflater?.inflate(R.menu.menu_search, menu) onlyPrivate = menu?.findItem(R.id.menu_only_private)?.actionView as Switch onlyPrivate?.setOnCheckedChangeListener { button, checked -> showDecks() diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt index e9bf78d..1f7fc7d 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt @@ -2,10 +2,9 @@ package com.ediposouza.teslesgendstracker.ui.matches.tabs import android.content.Context import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup +import android.view.* import android.widget.FrameLayout +import android.widget.Switch import com.ediposouza.teslesgendstracker.R import com.ediposouza.teslesgendstracker.data.Attribute import com.ediposouza.teslesgendstracker.data.Class @@ -24,6 +23,9 @@ import java.util.* */ class MatchesStatistics : BaseFragment() { + private var showPercent: Switch? = null + + var statisticsTableAdapter: StatisticsTableAdapter? = null val results = HashMap(Class.values().map { it to mutableListOf() }.toMap()) override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { @@ -32,7 +34,8 @@ class MatchesStatistics : BaseFragment() { override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - matches_statistics_table.adapter = StatisticsTableAdapter(context).apply { + setHasOptionsMenu(true) + statisticsTableAdapter = StatisticsTableAdapter(context).apply { setFirstHeader("Vs") val classTotal: Class? = null header = Class.values().asList().plus(classTotal) @@ -40,45 +43,60 @@ class MatchesStatistics : BaseFragment() { updateStatisticsData(this) setSection(listOf()) } + matches_statistics_table.adapter = statisticsTableAdapter getMatches() } + override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { + menu?.clear() + inflater?.inflate(R.menu.menu_percent, menu) + inflater?.inflate(R.menu.menu_season, menu) + showPercent = menu?.findItem(R.id.menu_percent)?.actionView as Switch + showPercent?.setOnCheckedChangeListener { button, checked -> updateStatisticsData() } + super.onCreateOptionsMenu(menu, inflater) + } + private fun getMatches() { val matches = TestUtils.getTestMatches() matches.groupBy { it.player.cls }.forEach { results[it.key]?.addAll(it.value) - updateStatisticsData(matches_statistics_table.adapter as StatisticsTableAdapter) + updateStatisticsData() } } - private fun updateStatisticsData(statisticsTableAdapter: StatisticsTableAdapter) { - statisticsTableAdapter.body = mutableListOf>().apply { + private fun updateStatisticsData(tableAdapter: StatisticsTableAdapter? = statisticsTableAdapter) { + tableAdapter?.body = mutableListOf>().apply { Class.values().forEach { myCls -> add(mutableListOf().apply { val resByMyCls = results[myCls]!! Class.values().forEach { opponentCls -> val matchesVsOpponent = resByMyCls.filter { it.opponent.cls == opponentCls } - add(getWinLossBodyItem(matchesVsOpponent)) + add(getResultBodyItem(matchesVsOpponent)) } - add(getWinLossBodyItem(resByMyCls)) + add(getResultBodyItem(resByMyCls)) }) } val allMatches = results.flatMap { it.value } add(mutableListOf().apply { Class.values().forEach { val resByOpponent = allMatches.groupBy { it.opponent.cls }[it] ?: listOf() - add(getWinLossBodyItem(resByOpponent)) + add(getResultBodyItem(resByOpponent)) } - add(getWinLossBodyItem(allMatches)) + add(getResultBodyItem(allMatches)) }) } } - private fun getWinLossBodyItem(matches: List): BodyItem { + private fun getResultBodyItem(matches: List): BodyItem { val result = matches.groupBy { it.win } - return BodyItem(result = "${result[true]?.size ?: 0}/${result[false]?.size ?: 0}") + val wins = result[true]?.size ?: 0 + val losses = result[false]?.size ?: 0 + return BodyItem(result = if (!(showPercent?.isChecked ?: false)) "$wins/$losses" else + getString(R.string.match_statistics_percent, calcWinRate(wins.toFloat(), losses.toFloat()))) } + private fun calcWinRate(losses: Float, wins: Float) = losses / (wins + losses) * 100 + class BodyItem(val cls: Class? = null, val result: String? = null) class StatisticsTableAdapter(val context: Context) : TableFixHeaderAdapter + + + diff --git a/app/src/main/res/drawable/ic_percent_menu_checked.xml b/app/src/main/res/drawable/ic_percent_menu_checked.xml new file mode 100644 index 0000000..8be09db --- /dev/null +++ b/app/src/main/res/drawable/ic_percent_menu_checked.xml @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/main/res/drawable/ic_percent_menu_checked_svg.xml b/app/src/main/res/drawable/ic_percent_menu_checked_svg.xml new file mode 100644 index 0000000..251efc3 --- /dev/null +++ b/app/src/main/res/drawable/ic_percent_menu_checked_svg.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_percent_menu_svg.xml b/app/src/main/res/drawable/ic_percent_menu_svg.xml new file mode 100644 index 0000000..fa2be92 --- /dev/null +++ b/app/src/main/res/drawable/ic_percent_menu_svg.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_private_menu_checked_svg.xml b/app/src/main/res/drawable/ic_private_menu_checked_svg.xml index d0ff80e..75e3b51 100644 --- a/app/src/main/res/drawable/ic_private_menu_checked_svg.xml +++ b/app/src/main/res/drawable/ic_private_menu_checked_svg.xml @@ -1,7 +1,7 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/xml_switch.xml b/app/src/main/res/drawable/xml_switch.xml index 5d0e232..da67b77 100644 --- a/app/src/main/res/drawable/xml_switch.xml +++ b/app/src/main/res/drawable/xml_switch.xml @@ -11,9 +11,7 @@ - + - + diff --git a/app/src/main/res/layout/widget_switch_percent.xml b/app/src/main/res/layout/widget_switch_percent.xml new file mode 100644 index 0000000..b322cc7 --- /dev/null +++ b/app/src/main/res/layout/widget_switch_percent.xml @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_deck_owner.xml b/app/src/main/res/menu/menu_delete.xml similarity index 65% rename from app/src/main/res/menu/menu_deck_owner.xml rename to app/src/main/res/menu/menu_delete.xml index fd3d156..4c05f6b 100644 --- a/app/src/main/res/menu/menu_deck_owner.xml +++ b/app/src/main/res/menu/menu_delete.xml @@ -8,10 +8,4 @@ android:title="@string/menu_delete" app:showAsAction="always" /> - - \ No newline at end of file diff --git a/app/src/main/res/menu/menu_deck.xml b/app/src/main/res/menu/menu_like.xml similarity index 100% rename from app/src/main/res/menu/menu_deck.xml rename to app/src/main/res/menu/menu_like.xml diff --git a/app/src/main/res/menu/menu_percent.xml b/app/src/main/res/menu/menu_percent.xml new file mode 100644 index 0000000..b07c89e --- /dev/null +++ b/app/src/main/res/menu/menu_percent.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_decks_owned.xml b/app/src/main/res/menu/menu_private.xml similarity index 62% rename from app/src/main/res/menu/menu_decks_owned.xml rename to app/src/main/res/menu/menu_private.xml index 8f9521d..a98b074 100644 --- a/app/src/main/res/menu/menu_decks_owned.xml +++ b/app/src/main/res/menu/menu_private.xml @@ -10,11 +10,4 @@ app:actionLayout="@layout/widget_switch_private" app:showAsAction="always" /> - - \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index a8be31e..6a4036f 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -52,8 +52,9 @@ 88dp 24dp 16dp - 20dp - @dimen/medium_margin + 20dp + 12dp + 14dp 115dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 25ea174..ec62456 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -30,6 +30,7 @@ Delete Unlike Done + Percent Are you sure? Open drawer From 997bd55d218b222a58f6a27c17db4ee59ed24ae5 Mon Sep 17 00:00:00 2001 From: EdipoSouza Date: Sat, 7 Jan 2017 12:52:04 -0200 Subject: [PATCH 16/54] Fix drawer menu flow. Some performance fixes --- .../teslesgendstracker/ui/DashActivity.kt | 13 +++--- .../teslesgendstracker/ui/DeckActivity.kt | 6 +-- .../ui/decks/DecksFragment.kt | 9 +--- .../ui/matches/tabs/MatchesStatistics.kt | 41 +++++++++++-------- cards.json | 4 +- 5 files changed, 36 insertions(+), 37 deletions(-) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DashActivity.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DashActivity.kt index 66ba745..6d90fde 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DashActivity.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DashActivity.kt @@ -159,11 +159,14 @@ class DashActivity : BaseFilterActivity(), private fun showFragment(frag: Fragment): Boolean { statisticsSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN - supportFragmentManager.beginTransaction() - .replace(R.id.dash_content, frag) - .setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out) - .addToBackStack(null) - .commit() + if (supportFragmentManager.backStackEntryCount > 0) { + supportFragmentManager.popBackStackImmediate() + } + supportFragmentManager.beginTransaction().apply { + replace(R.id.dash_content, frag) + setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out) + addToBackStack(null) + }.commit() return true } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DeckActivity.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DeckActivity.kt index af9c4f9..114f436 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DeckActivity.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DeckActivity.kt @@ -187,12 +187,8 @@ class DeckActivity : BaseActivity() { } override fun onCreateOptionsMenu(menu: Menu?): Boolean { - if (deckOwned) { - menuInflater.inflate(R.menu.menu_delete, menu) - } - menuInflater.inflate(R.menu.menu_like, menu) + menuInflater.inflate(if (deckOwned) R.menu.menu_delete else R.menu.menu_like, menu) menuLike = menu?.findItem(R.id.menu_like) - menuLike?.isVisible = !deckOwned updateLikeItem() return super.onCreateOptionsMenu(menu) } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/DecksFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/DecksFragment.kt index b293dcc..adc25a1 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/DecksFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/DecksFragment.kt @@ -51,7 +51,6 @@ class DecksFragment : BaseFragment(), SearchView.OnQueryTextListener { 1 -> MetricScreen.SCREEN_DECKS_OWNED() else -> MetricScreen.SCREEN_DECKS_FAVORED() }) - (adapter.getItem(position) as DecksPublicFragment).showDecks() } } @@ -82,9 +81,8 @@ class DecksFragment : BaseFragment(), SearchView.OnQueryTextListener { } else { decks_attr_filter.selectAttr(it, false) } - requestDecks() + eventBus.post(CmdShowDecksByClasses(Class.getClasses(decks_attr_filter.getSelectedAttrs()))) } - Handler().postDelayed({ requestDecks() }, DateUtils.SECOND_IN_MILLIS) MetricsManager.trackScreen(MetricScreen.SCREEN_DECKS_PUBLIC()) } @@ -165,11 +163,6 @@ class DecksFragment : BaseFragment(), SearchView.OnQueryTextListener { return true } - private fun requestDecks() { - val classesToShow = Class.getClasses(decks_attr_filter.getSelectedAttrs()) - eventBus.post(CmdShowDecksByClasses(classesToShow)) - } - @Subscribe fun onCmdUpdateRarityMagikaFiltersVisibility(update: CmdUpdateVisibility) { if (update.show) { diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt index 1f7fc7d..6dedd85 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt @@ -16,6 +16,8 @@ import kotlinx.android.synthetic.main.fragment_matches_statistics.* import kotlinx.android.synthetic.main.itemcell_class.view.* import kotlinx.android.synthetic.main.itemcell_text.view.* import miguelbcr.ui.tableFixHeadesWrapper.TableFixHeaderAdapter +import org.jetbrains.anko.doAsync +import org.jetbrains.anko.uiThread import java.util.* /** @@ -23,6 +25,8 @@ import java.util.* */ class MatchesStatistics : BaseFragment() { + private val HEADER_FIRST = "Vs" + private var showPercent: Switch? = null var statisticsTableAdapter: StatisticsTableAdapter? = null @@ -36,7 +40,7 @@ class MatchesStatistics : BaseFragment() { super.onViewCreated(view, savedInstanceState) setHasOptionsMenu(true) statisticsTableAdapter = StatisticsTableAdapter(context).apply { - setFirstHeader("Vs") + setFirstHeader(HEADER_FIRST) val classTotal: Class? = null header = Class.values().asList().plus(classTotal) setFirstBody(Class.values().map { listOf(BodyItem(it)) }.plus(listOf(listOf(BodyItem())))) @@ -65,25 +69,30 @@ class MatchesStatistics : BaseFragment() { } private fun updateStatisticsData(tableAdapter: StatisticsTableAdapter? = statisticsTableAdapter) { - tableAdapter?.body = mutableListOf>().apply { - Class.values().forEach { myCls -> + doAsync { + val data = mutableListOf>().apply { + Class.values().forEach { myCls -> + add(mutableListOf().apply { + val resByMyCls = results[myCls]!! + Class.values().forEach { opponentCls -> + val matchesVsOpponent = resByMyCls.filter { it.opponent.cls == opponentCls } + add(getResultBodyItem(matchesVsOpponent)) + } + add(getResultBodyItem(resByMyCls)) + }) + } + val allMatches = results.flatMap { it.value } add(mutableListOf().apply { - val resByMyCls = results[myCls]!! - Class.values().forEach { opponentCls -> - val matchesVsOpponent = resByMyCls.filter { it.opponent.cls == opponentCls } - add(getResultBodyItem(matchesVsOpponent)) + Class.values().forEach { + val resByOpponent = allMatches.groupBy { it.opponent.cls }[it] ?: listOf() + add(getResultBodyItem(resByOpponent)) } - add(getResultBodyItem(resByMyCls)) + add(getResultBodyItem(allMatches)) }) } - val allMatches = results.flatMap { it.value } - add(mutableListOf().apply { - Class.values().forEach { - val resByOpponent = allMatches.groupBy { it.opponent.cls }[it] ?: listOf() - add(getResultBodyItem(resByOpponent)) - } - add(getResultBodyItem(allMatches)) - }) + uiThread { + tableAdapter?.body = data + } } } diff --git a/cards.json b/cards.json index 782a087..aee493c 100644 --- a/cards.json +++ b/cards.json @@ -6300,9 +6300,7 @@ }, "decks": { "favorite": { - "-KZwF4-30Ip7qC4wnMix": 1, - "-K_-MCInX62rl826jQf1": 0, - "-K_iNXh_xHakLMUcDbMK": 7 + "-KZwF4-30Ip7qC4wnMix": 1 }, "private": { "-K_t7BMw5-q1sFGXCfoi": { From 6c349b6bbc6ddf282c9a07922188442cd7b609b9 Mon Sep 17 00:00:00 2001 From: EdipoSouza Date: Sat, 7 Jan 2017 17:20:53 -0200 Subject: [PATCH 17/54] Load Matches from Firebase. PrivateInteractor refactoring --- .../teslesgendstracker/data/Deck.kt | 12 +- .../teslesgendstracker/data/General.kt | 2 +- .../teslesgendstracker/data/User.kt | 20 +-- .../interactor/FirebaseParsers.kt | 65 ++++++++-- .../interactor/PrivateInteractor.kt | 118 +++++++++++------- .../interactor/PublicInteractor.kt | 42 +++---- .../teslesgendstracker/ui/DeckActivity.kt | 8 +- .../ui/cards/tabs/CardsAllFragment.kt | 2 +- .../ui/cards/tabs/CardsFavoritesFragment.kt | 2 +- .../ui/decks/new/NewDeckActivity.kt | 2 +- .../ui/decks/tabs/DecksFavoritedFragment.kt | 2 +- .../ui/decks/tabs/DecksOwnerFragment.kt | 2 +- .../ui/decks/tabs/DecksPublicFragment.kt | 6 +- .../ui/decks/widget/DeckList.kt | 4 +- .../ui/matches/MatchesFragment.kt | 7 ++ .../ui/matches/tabs/MatchesStatistics.kt | 44 +++++-- .../teslesgendstracker/util/TestUtils.kt | 4 +- app/src/main/res/layout/itemcell_text.xml | 30 +++-- cards.json | 2 +- 19 files changed, 238 insertions(+), 136 deletions(-) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/data/Deck.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/data/Deck.kt index b9dd123..c036825 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/data/Deck.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/data/Deck.kt @@ -42,7 +42,7 @@ data class DeckUpdate( data class DeckComment( - val id: String, + val uuid: String, val owner: String, val comment: String, val date: LocalDateTime @@ -61,19 +61,19 @@ data class DeckComment( override fun describeContents() = 0 override fun writeToParcel(dest: Parcel?, flags: Int) { - dest?.writeString(id) + dest?.writeString(uuid) dest?.writeString(owner) dest?.writeString(comment) dest?.writeSerializable(date) } - override fun toString(): String = "DeckComment(id='$id', owner='$owner', comment='$comment', date=$date)" + override fun toString(): String = "DeckComment(id='$uuid', owner='$owner', comment='$comment', date=$date)" } data class Deck( - val id: String, + val uuid: String, val name: String, val owner: String, val private: Boolean, @@ -112,7 +112,7 @@ data class Deck( override fun describeContents() = 0 override fun writeToParcel(dest: Parcel?, flags: Int) { - dest?.writeString(id) + dest?.writeString(uuid) dest?.writeString(name) dest?.writeString(owner) dest?.writeInt((if (private) 1 else 0)) @@ -130,7 +130,7 @@ data class Deck( } override fun toString(): String { - return "Deck(id='$id', name='$name', owner='$owner', private=$private, type=$type, cls=$cls, cost=$cost, createdAt=$createdAt, updatedAt=$updatedAt, patch='$patch', likes=$likes, views=$views, cards=$cards, updates=$updates, comments=$comments)" + return "Deck(id='$uuid', name='$name', owner='$owner', private=$private, type=$type, cls=$cls, cost=$cost, createdAt=$createdAt, updatedAt=$updatedAt, patch='$patch', likes=$likes, views=$views, cards=$cards, updates=$updates, comments=$comments)" } } \ No newline at end of file diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/data/General.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/data/General.kt index 88cf258..f6f3366 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/data/General.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/data/General.kt @@ -35,7 +35,7 @@ data class CardSlot( data class Patch( - val uidDate: String, + val uuidDate: String, val desc: String ) \ No newline at end of file diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/data/User.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/data/User.kt index 6a523f7..560de3d 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/data/User.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/data/User.kt @@ -3,17 +3,6 @@ package com.ediposouza.teslesgendstracker.data /** * Created by ediposouza on 10/31/16. */ -data class User( - - val name: String, - val collection: List, - val collectionPercent: Float, - val decks: List, - val decksFavorites: List, - val matches: List - -) - data class UserInfo( val name: String, @@ -23,11 +12,13 @@ data class UserInfo( data class Match( + val uuid: String, + val first: Boolean, val player: MatchDeck, val opponent: MatchDeck, - val win: Boolean, val rank: Int, - val legend: Boolean + val legend: Boolean, + val win: Boolean ) @@ -36,6 +27,7 @@ data class MatchDeck( val name: String, val cls: Class, val type: DeckType, - val version: String + val deck: String? = null, + val version: String? = null ) \ No newline at end of file diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/FirebaseParsers.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/FirebaseParsers.kt index cdea766..cd86c89 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/FirebaseParsers.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/FirebaseParsers.kt @@ -3,6 +3,7 @@ package com.ediposouza.teslesgendstracker.interactor import com.ediposouza.teslesgendstracker.data.* import com.ediposouza.teslesgendstracker.util.toIntSafely import org.threeten.bp.LocalDateTime +import timber.log.Timber abstract class FirebaseParsers { @@ -65,13 +66,13 @@ abstract class FirebaseParsers { companion object { - private val KEY_DECK_NAME = "owner" - private val KEY_DECK_TYPE = "type" - private val KEY_DECK_CLASS = "cls" - private val KEY_DECK_PATCH = "patch" - private val KEY_DECK_COMMENT_OWNER = "owner" - private val KEY_DECK_COMMENT_MSG = "comment" - private val KEY_DECK_COMMENT_CREATE_AT = "createdAt" + private const val KEY_DECK_NAME = "owner" + private const val KEY_DECK_TYPE = "type" + private const val KEY_DECK_CLASS = "cls" + private const val KEY_DECK_PATCH = "patch" + private const val KEY_DECK_COMMENT_OWNER = "owner" + private const val KEY_DECK_COMMENT_MSG = "comment" + private const val KEY_DECK_COMMENT_CREATE_AT = "createdAt" fun toNewCommentMap(owner: String, comment: String): Map { return mapOf(KEY_DECK_COMMENT_OWNER to owner, KEY_DECK_COMMENT_MSG to comment, @@ -80,8 +81,8 @@ abstract class FirebaseParsers { } - fun toDeck(id: String, private: Boolean): Deck { - return Deck(id, name, owner, private, DeckType.values()[type], Class.values()[cls], cost, + fun toDeck(uuid: String, private: Boolean): Deck { + return Deck(uuid, name, owner, private, DeckType.values()[type], Class.values()[cls], cost, LocalDateTime.parse(createdAt), LocalDateTime.parse(updatedAt), patch, likes, views, cards, updates.map { DeckUpdate(LocalDateTime.parse(it.key), it.value) }, comments.map { @@ -96,7 +97,7 @@ abstract class FirebaseParsers { deck.createdAt.toString(), deck.updatedAt.toString(), deck.patch, deck.views, deck.likes, deck.cards, deck.updates.map { it.date.toString() to it.changes }.toMap(), deck.comments.map { - it.id to mapOf( + it.uuid to mapOf( KEY_DECK_COMMENT_OWNER to it.owner, KEY_DECK_COMMENT_MSG to it.comment, KEY_DECK_COMMENT_CREATE_AT to it.date.toString()) @@ -117,8 +118,48 @@ abstract class FirebaseParsers { val desc: String = "" - fun toPatch(uidDate: String): Patch { - return Patch(uidDate, desc) + fun toPatch(uuidDate: String): Patch { + return Patch(uuidDate, desc) + } + + } + + class MatchParser( + + val first: Boolean = false, + val player: Map = mapOf(), + val opponent: Map = mapOf(), + val rank: Int = 0, + val legend: Boolean = false, + val win: Boolean = false + + ) { + + companion object { + + private const val KEY_MATCH_DECK_CLASS = "cls" + private const val KEY_MATCH_DECK_DECK_UUID = "deck" + private const val KEY_MATCH_DECK_NAME = "name" + private const val KEY_MATCH_DECK_TYPE = "type" + private const val KEY_MATCH_DECK_VERSION = "version" + + } + + fun toMatch(uuid: String): Match { + Timber.d(this.toString()) + val playerDeck = MatchDeck(player[KEY_MATCH_DECK_NAME].toString(), + Class.values()[player[KEY_MATCH_DECK_CLASS].toString().toInt()], + DeckType.values()[player[KEY_MATCH_DECK_TYPE].toString().toInt()], + player[KEY_MATCH_DECK_DECK_UUID].toString(), + player[KEY_MATCH_DECK_VERSION].toString()) + val opponentDeck = MatchDeck(opponent[KEY_MATCH_DECK_NAME].toString(), + Class.values()[opponent[KEY_MATCH_DECK_CLASS].toString().toInt()], + DeckType.values()[opponent[KEY_MATCH_DECK_TYPE].toString().toInt()]) + return Match(uuid, first, playerDeck, opponentDeck, rank, legend, win) + } + + override fun toString(): String { + return "MatchParser(first=$first, player=$player, opponent=$opponent, rank=$rank, legend=$legend, win=$win)" } } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt index e21005c..2210931 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt @@ -20,6 +20,7 @@ class PrivateInteractor : BaseInteractor() { private val NODE_DECKS_PRIVATE = "private" private val NODE_FAVORITE = "favorite" + private val NODE_MATCHES = "matches" private val KEY_CARD_FAVORITE = "favorite" private val KEY_CARD_QTD = "qtd" @@ -137,7 +138,7 @@ class PrivateInteractor : BaseInteractor() { } } - fun getFavoriteCards(set: CardSet?, attr: Attribute, onSuccess: (List) -> Unit) { + fun getUserFavoriteCards(set: CardSet?, attr: Attribute, onSuccess: (List) -> Unit) { getListFromSets(set, attr, onSuccess) { set, attr, onEachSuccess -> dbUserCards(set, attr)?.addListenerForSingleValueEvent(object : ValueEventListener { @@ -157,21 +158,21 @@ class PrivateInteractor : BaseInteractor() { } } - fun getOwnedPublicDecksRef() = dbDecks.child(NODE_DECKS_PUBLIC) + fun getUserPublicDecksRef() = dbDecks.child(NODE_DECKS_PUBLIC) .orderByChild(KEY_DECK_OWNER).equalTo(getUserID())?.apply { keepSynced() } - fun getOwnedPrivateDecksRef() = dbUser()?.child(NODE_DECKS)?.child(NODE_DECKS_PRIVATE) + fun getUserPrivateDecksRef() = dbUser()?.child(NODE_DECKS)?.child(NODE_DECKS_PRIVATE) ?.orderByChild(KEY_DECK_UPDATE_AT)?.apply { keepSynced() } - fun getFavoriteDecksRef() = dbUser()?.child(NODE_DECKS)?.child(NODE_FAVORITE)?.apply { + fun getUserFavoriteDecksRef() = dbUser()?.child(NODE_DECKS)?.child(NODE_FAVORITE)?.apply { keepSynced() } - fun getFavoriteDecks(cls: Class?, onSuccess: (List?) -> Unit) { + fun getUserFavoriteDecks(cls: Class?, onSuccess: (List?) -> Unit) { PublicInteractor().getPublicDecks(cls) { val publicDecks = it dbUser()?.child(NODE_DECKS)?.child(NODE_FAVORITE)?.apply { @@ -182,7 +183,7 @@ class PrivateInteractor : BaseInteractor() { Timber.d(ds.value?.toString()) val decks = ds.children.map { val deckId = it.key - publicDecks.find { it.id == deckId } ?: Deck() + publicDecks.find { it.uuid == deckId } ?: Deck() }.filter { it.cost > 0 }.filter { cls == null || it.cls == cls } Timber.d(decks.toString()) onSuccess.invoke(decks) @@ -197,7 +198,29 @@ class PrivateInteractor : BaseInteractor() { } } - fun getMissingCards(deck: Deck, onError: ((e: Exception?) -> Unit)? = null, onSuccess: (List) -> Unit) { + fun setUserDeckFavorite(deck: Deck, favorite: Boolean, onError: ((e: Exception?) -> Unit)? = null, onSuccess: () -> Unit) { + dbUser()?.child(NODE_DECKS)?.child(NODE_FAVORITE)?.apply { + if (favorite) { + child(deck.uuid)?.setValue(deck.cls.ordinal)?.addOnCompleteListener { onSuccess.invoke() } + } else { + child(deck.uuid)?.removeValue()?.addOnCompleteListener { onSuccess.invoke() } + } + } + } + + fun setUserDeckLike(deck: Deck, like: Boolean, onError: ((e: Exception?) -> Unit)? = null, onSuccess: () -> Unit) { + dbUser()?.apply { + with(if (deck.private) child(NODE_DECKS).child(NODE_DECKS_PRIVATE) else dbDecks.child(NODE_DECKS_PUBLIC)) { + val deckLikesUpdated = if (like) deck.likes.plus(getUserID()) else deck.likes.minus(getUserID()) + child(deck.uuid).updateChildren(mapOf(KEY_DECK_LIKES to deckLikesUpdated)).addOnCompleteListener({ + Timber.d(it.toString()) + if (it.isSuccessful) onSuccess.invoke() else onError?.invoke(it.exception) + }) + } + } + } + + fun getDeckMissingCards(deck: Deck, onError: ((e: Exception?) -> Unit)? = null, onSuccess: (List) -> Unit) { val publicInteractor = PublicInteractor() val attr1 = deck.cls.attr1 val attr2 = deck.cls.attr2 @@ -221,9 +244,9 @@ class PrivateInteractor : BaseInteractor() { with(if (private) child(NODE_DECKS).child(NODE_DECKS_PRIVATE) else dbDecks.child(NODE_DECKS_PUBLIC)) { val deck = Deck(push().key, name, getUserID(), private, type, cls, cost, LocalDateTime.now(), LocalDateTime.now(), patch, ArrayList(), 0, cards, ArrayList(), ArrayList()) - child(deck.id).setValue(FirebaseParsers.DeckParser().fromDeck(deck)).addOnCompleteListener({ + child(deck.uuid).setValue(FirebaseParsers.DeckParser().fromDeck(deck)).addOnCompleteListener({ Timber.d(it.toString()) - if (it.isSuccessful) onSuccess.invoke(deck.id) + if (it.isSuccessful) onSuccess.invoke(deck.uuid) else { val errorMsg = it.exception?.message ?: it.exception.toString() EventBus.getDefault().post(CmdShowSnackbarMsg(CmdShowSnackbarMsg.TYPE_ERROR, errorMsg)) @@ -234,16 +257,6 @@ class PrivateInteractor : BaseInteractor() { } } - fun setUserDeckFavorite(deck: Deck, favorite: Boolean, onError: ((e: Exception?) -> Unit)? = null, onSuccess: () -> Unit) { - dbUser()?.child(NODE_DECKS)?.child(NODE_FAVORITE)?.apply { - if (favorite) { - child(deck.id)?.setValue(deck.cls.ordinal)?.addOnCompleteListener { onSuccess.invoke() } - } else { - child(deck.id)?.removeValue()?.addOnCompleteListener { onSuccess.invoke() } - } - } - } - /** * Name, Type, Class, Patch, Private */ @@ -251,12 +264,12 @@ class PrivateInteractor : BaseInteractor() { dbUser()?.apply { with(if (deck.private) child(NODE_DECKS).child(NODE_DECKS_PRIVATE) else dbDecks.child(NODE_DECKS_PUBLIC)) { if (deck.private == oldPrivate) - child(deck.id).updateChildren(FirebaseParsers.DeckParser().fromDeck(deck).toDeckUpdateMap()).addOnCompleteListener({ + child(deck.uuid).updateChildren(FirebaseParsers.DeckParser().fromDeck(deck).toDeckUpdateMap()).addOnCompleteListener({ Timber.d(it.toString()) if (it.isSuccessful) onSuccess.invoke() else onError?.invoke(it.exception) }) else - child(deck.id).setValue(FirebaseParsers.DeckParser().fromDeck(deck)).addOnCompleteListener({ + child(deck.uuid).setValue(FirebaseParsers.DeckParser().fromDeck(deck)).addOnCompleteListener({ Timber.d(it.toString()) deleteDeck(deck, oldPrivate, onError, onSuccess) }) @@ -271,34 +284,11 @@ class PrivateInteractor : BaseInteractor() { val updateKey = org.threeten.bp.LocalDateTime.now().toString() val cardsRem = oldCards.filter { !deck.cards.keys.contains(it.key) }.mapValues { it.key to it.value * -1 } val cardsDiff = deck.cards.mapValues { it.key to it.value.minus(oldCards[it.key] ?: 0) }.plus(cardsRem) - child(deck.id).child(KEY_DECK_UPDATES).child(updateKey).setValue(cardsDiff).addOnCompleteListener({ - Timber.d(it.toString()) - if (it.isSuccessful) onSuccess.invoke() else onError?.invoke(it.exception) - }) - child(deck.id).child(KEY_DECK_COST).setValue(cost) - } - } - } - - fun deleteDeck(deck: Deck, private: Boolean, onError: ((e: Exception?) -> Unit)? = null, onSuccess: () -> Unit) { - dbUser()?.apply { - with(if (private) child(NODE_DECKS).child(NODE_DECKS_PRIVATE) else dbDecks.child(NODE_DECKS_PUBLIC)) { - child(deck.id).removeValue().addOnCompleteListener({ - Timber.d(it.toString()) - if (it.isSuccessful) onSuccess.invoke() else onError?.invoke(it.exception) - }) - } - } - } - - fun setUserDeckLike(deck: Deck, like: Boolean, onError: ((e: Exception?) -> Unit)? = null, onSuccess: () -> Unit) { - dbUser()?.apply { - with(if (deck.private) child(NODE_DECKS).child(NODE_DECKS_PRIVATE) else dbDecks.child(NODE_DECKS_PUBLIC)) { - val deckLikesUpdated = if (like) deck.likes.plus(getUserID()) else deck.likes.minus(getUserID()) - child(deck.id).updateChildren(mapOf(KEY_DECK_LIKES to deckLikesUpdated)).addOnCompleteListener({ + child(deck.uuid).child(KEY_DECK_UPDATES).child(updateKey).setValue(cardsDiff).addOnCompleteListener({ Timber.d(it.toString()) if (it.isSuccessful) onSuccess.invoke() else onError?.invoke(it.exception) }) + child(deck.uuid).child(KEY_DECK_COST).setValue(cost) } } } @@ -308,7 +298,7 @@ class PrivateInteractor : BaseInteractor() { dbUser()?.apply { with(if (deck.private) child(NODE_DECKS).child(NODE_DECKS_PRIVATE) else dbDecks.child(NODE_DECKS_PUBLIC)) { val comment = FirebaseParsers.DeckParser.toNewCommentMap(getUserID(), msg) - with(child(deck.id).child(KEY_DECK_COMMENTS)) { + with(child(deck.uuid).child(KEY_DECK_COMMENTS)) { val commentKey = push().key child(commentKey).setValue(comment).addOnCompleteListener({ Timber.d(it.toString()) @@ -325,7 +315,7 @@ class PrivateInteractor : BaseInteractor() { fun remDeckComment(deck: Deck, commentId: String, onError: ((e: Exception?) -> Unit)? = null, onSuccess: () -> Unit) { dbUser()?.apply { with(if (deck.private) child(NODE_DECKS).child(NODE_DECKS_PRIVATE) else dbDecks.child(NODE_DECKS_PUBLIC)) { - child(deck.id).child(KEY_DECK_COMMENTS).child(commentId).removeValue().addOnCompleteListener({ + child(deck.uuid).child(KEY_DECK_COMMENTS).child(commentId).removeValue().addOnCompleteListener({ Timber.d(it.toString()) if (it.isSuccessful) onSuccess.invoke() else onError?.invoke(it.exception) }) @@ -333,4 +323,36 @@ class PrivateInteractor : BaseInteractor() { } } + fun deleteDeck(deck: Deck, private: Boolean, onError: ((e: Exception?) -> Unit)? = null, onSuccess: () -> Unit) { + dbUser()?.apply { + with(if (private) child(NODE_DECKS).child(NODE_DECKS_PRIVATE) else dbDecks.child(NODE_DECKS_PUBLIC)) { + child(deck.uuid).removeValue().addOnCompleteListener({ + Timber.d(it.toString()) + if (it.isSuccessful) onSuccess.invoke() else onError?.invoke(it.exception) + }) + } + } + } + + fun getUserMatches(onSuccess: (List) -> Unit) { + dbUser()?.child(NODE_MATCHES)?.apply { + keepSynced() + addListenerForSingleValueEvent(object : ValueEventListener { + + override fun onDataChange(ds: DataSnapshot) { + val matches = ds.children.mapTo(arrayListOf()) { + it.getValue(FirebaseParsers.MatchParser::class.java).toMatch(it.key) + } + Timber.d(matches.toString()) + onSuccess.invoke(matches) + } + + override fun onCancelled(de: DatabaseError) { + Timber.d("Fail: " + de.message) + } + + }) + } + } + } \ No newline at end of file diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PublicInteractor.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PublicInteractor.kt index 2d8f5ee..2ad1324 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PublicInteractor.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PublicInteractor.kt @@ -104,11 +104,6 @@ class PublicInteractor : BaseInteractor() { } } - fun getPublicDecksRef() = dbDecks.child(NODE_DECKS_PUBLIC) - .orderByChild(KEY_DECK_UPDATE_AT)?.apply { - keepSynced() - } - fun getPublicDeck(uuid: String, onSuccess: (Deck) -> Unit) { with(dbDecks.child(NODE_DECKS_PUBLIC).child(uuid)) { keepSynced() @@ -131,6 +126,11 @@ class PublicInteractor : BaseInteractor() { } } + fun getPublicDecksRef() = dbDecks.child(NODE_DECKS_PUBLIC) + .orderByChild(KEY_DECK_UPDATE_AT)?.apply { + keepSynced() + } + fun getPublicDecks(cls: Class?, onSuccess: (List) -> Unit) { val dbPublicDeck = dbDecks.child(NODE_DECKS_PUBLIC) dbPublicDeck.keepSynced() @@ -159,7 +159,7 @@ class PublicInteractor : BaseInteractor() { fun incDeckView(deck: Deck, onError: ((e: Exception?) -> Unit)? = null, onSuccess: (Int) -> Unit) { with(dbDecks.child(NODE_DECKS_PUBLIC)) { val views = deck.views.inc() - child(deck.id).updateChildren(mapOf(KEY_DECK_VIEWS to views)).addOnCompleteListener({ + child(deck.uuid).updateChildren(mapOf(KEY_DECK_VIEWS to views)).addOnCompleteListener({ Timber.d(it.toString()) if (it.isSuccessful) onSuccess.invoke(views) else onError?.invoke(it.exception) }) @@ -177,15 +177,16 @@ class PublicInteractor : BaseInteractor() { } } - fun getUserInfo(uuid: String, onError: ((e: Exception?) -> Unit)? = null, - onSuccess: (UserInfo) -> Unit) { - dbUsers.child(uuid)?.child(NODE_USERS_INFO)?.addListenerForSingleValueEvent(object : ValueEventListener { + fun getPatches(onError: ((e: Exception?) -> Unit)? = null, + onSuccess: (List) -> Unit) { + database.child(NODE_PATCHES).addListenerForSingleValueEvent(object : ValueEventListener { override fun onDataChange(ds: DataSnapshot) { - val userInfo = UserInfo(ds.child(KEY_USER_NAME)?.value?.toString() ?: "", - ds.child(KEY_USER_PHOTO)?.value?.toString() ?: "") - Timber.d(userInfo.toString()) - onSuccess.invoke(userInfo) + val patches = ds.children.mapTo(arrayListOf()) { + it.getValue(FirebaseParsers.PatchParser::class.java).toPatch(it.key) + } + Timber.d(patches.toString()) + onSuccess.invoke(patches) } override fun onCancelled(de: DatabaseError) { @@ -196,16 +197,15 @@ class PublicInteractor : BaseInteractor() { }) } - fun getPatches(onError: ((e: Exception?) -> Unit)? = null, - onSuccess: (List) -> Unit) { - database.child(NODE_PATCHES).addListenerForSingleValueEvent(object : ValueEventListener { + fun getUserInfo(uuid: String, onError: ((e: Exception?) -> Unit)? = null, + onSuccess: (UserInfo) -> Unit) { + dbUsers.child(uuid)?.child(NODE_USERS_INFO)?.addListenerForSingleValueEvent(object : ValueEventListener { override fun onDataChange(ds: DataSnapshot) { - val patches = ds.children.mapTo(arrayListOf()) { - it.getValue(FirebaseParsers.PatchParser::class.java).toPatch(it.key) - } - Timber.d(patches.toString()) - onSuccess.invoke(patches) + val userInfo = UserInfo(ds.child(KEY_USER_NAME)?.value?.toString() ?: "", + ds.child(KEY_USER_PHOTO)?.value?.toString() ?: "") + Timber.d(userInfo.toString()) + onSuccess.invoke(userInfo) } override fun onCancelled(de: DatabaseError) { diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DeckActivity.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DeckActivity.kt index 114f436..70c00d7 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DeckActivity.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DeckActivity.kt @@ -296,7 +296,7 @@ class DeckActivity : BaseActivity() { } } publicInteractor.getPatches { - val patch = it.find { it.uidDate == deck.patch } + val patch = it.find { it.uuidDate == deck.patch } runOnUiThread { deck_details_patch.text = patch?.desc ?: "" } @@ -318,7 +318,7 @@ class DeckActivity : BaseActivity() { with(deck_details_soul_missing) { visibility = View.INVISIBLE deck_details_soul_missing_loading.visibility = View.VISIBLE - privateInteractor.getMissingCards(deck, { deck_details_soul_missing_loading.visibility = View.VISIBLE }) { + privateInteractor.getDeckMissingCards(deck, { deck_details_soul_missing_loading.visibility = View.VISIBLE }) { deck_details_soul_missing_loading.visibility = View.GONE val missingSoul = it.map { it.qtd * it.rarity.soulCost }.sum() Timber.d("Missing %d", missingSoul) @@ -365,7 +365,7 @@ class DeckActivity : BaseActivity() { } fun rem(commentId: String) { - val deckComment = items.find { it.id == commentId } + val deckComment = items.find { it.uuid == commentId } val deckCommentIndex = items.indexOf(deckComment) (items as ArrayList).remove(deckComment) notifyItemRemoved(deckCommentIndex) @@ -392,7 +392,7 @@ class DeckActivity : BaseActivity() { with(itemView.deck_comment_delete) { val owner = comment.owner == FirebaseAuth.getInstance().currentUser?.uid visibility = if (owner) View.VISIBLE else View.GONE - setOnClickListener { onRemComment(comment.id) } + setOnClickListener { onRemComment(comment.uuid) } } Glide.with(itemView.context) .load(ownerUser.photoUrl) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/tabs/CardsAllFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/tabs/CardsAllFragment.kt index 4d3774b..0c10998 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/tabs/CardsAllFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/tabs/CardsAllFragment.kt @@ -178,7 +178,7 @@ open class CardsAllFragment : BaseFragment() { cardsLoaded = it showCards() } - privateInteractor.getFavoriteCards(setFilter, currentAttr) { + privateInteractor.getUserFavoriteCards(setFilter, currentAttr) { userFavorites = it } } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/tabs/CardsFavoritesFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/tabs/CardsFavoritesFragment.kt index 6eda0cc..97c68a0 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/tabs/CardsFavoritesFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/tabs/CardsFavoritesFragment.kt @@ -28,7 +28,7 @@ class CardsFavoritesFragment : CardsAllFragment() { } override fun showCards() { - privateInteractor.getFavoriteCards(setFilter, currentAttr) { + privateInteractor.getUserFavoriteCards(setFilter, currentAttr) { userFavorites = it cardsAdapter.showCards(filteredCards().filter { userFavorites.contains(it.shortName) }) cards_recycler_view?.scrollToPosition(0) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/new/NewDeckActivity.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/new/NewDeckActivity.kt index 4f1f1a1..d55cfff 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/new/NewDeckActivity.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/new/NewDeckActivity.kt @@ -178,7 +178,7 @@ class NewDeckActivity : BaseFilterActivity() { val deckCards = new_deck_cardlist.getCards().map { it.card.shortName to it.qtd }.toMap() val deckPrivate = !view.new_deck_dialog_public.isChecked PrivateInteractor().saveDeck(deckName, deckCls, deckTypeSelected, new_deck_cardlist.getSoulCost(), - deckPatchSelected.uidDate, deckCards, deckPrivate) { + deckPatchSelected.uuidDate, deckCards, deckPrivate) { toast(if (deckPrivate) R.string.new_deck_save_as_private else R.string.new_deck_save_as_public) val data = intentFor(DECK_PRIVATE_EXTRA to deckPrivate) setResult(Activity.RESULT_OK, data) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksFavoritedFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksFavoritedFragment.kt index 4eb33b0..5f01434 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksFavoritedFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksFavoritedFragment.kt @@ -16,7 +16,7 @@ import kotlinx.android.synthetic.main.fragment_decks_list.* class DecksFavoritedFragment : DecksPublicFragment() { override val dataRef = { - privateInteractor.getFavoriteDecksRef() + privateInteractor.getUserFavoriteDecksRef() } private val dataFilter: (Int) -> Boolean = { diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksOwnerFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksOwnerFragment.kt index b20eab4..90851f2 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksOwnerFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksOwnerFragment.kt @@ -17,7 +17,7 @@ class DecksOwnerFragment : DecksPublicFragment() { get() = onlyPrivate?.isChecked ?: false override val dataRef = { - if (isDeckPrivate) privateInteractor.getOwnedPrivateDecksRef() else privateInteractor.getOwnedPublicDecksRef() + if (isDeckPrivate) privateInteractor.getUserPrivateDecksRef() else privateInteractor.getUserPublicDecksRef() } override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt index be65e59..a4b4928 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt @@ -57,8 +57,8 @@ open class DecksPublicFragment : BaseFragment() { } val itemClick = { view: View, deck: Deck -> - PrivateInteractor().getFavoriteDecks(deck.cls) { - val favorite = it?.filter { it.id == deck.id }?.isNotEmpty() ?: false + PrivateInteractor().getUserFavoriteDecks(deck.cls) { + val favorite = it?.filter { it.uuid == deck.uuid }?.isNotEmpty() ?: false val userId = FirebaseAuth.getInstance().currentUser?.uid val like = deck.likes.contains(userId) startActivity(DeckActivity.newIntent(context, deck, favorite, like, deck.owner == userId), @@ -203,7 +203,7 @@ open class DecksPublicFragment : BaseFragment() { with(itemView.deck_soul_missing) { visibility = View.INVISIBLE itemView.deck_soul_missing_loading.visibility = View.VISIBLE - privateInteractor.getMissingCards(deck, { itemView.deck_soul_missing_loading.visibility = View.VISIBLE }) { + privateInteractor.getDeckMissingCards(deck, { itemView.deck_soul_missing_loading.visibility = View.VISIBLE }) { itemView.deck_soul_missing_loading.visibility = View.GONE val missingSoul = it.map { it.qtd * it.rarity.soulCost }.sum() Timber.d("Missing %d", missingSoul) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/widget/DeckList.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/widget/DeckList.kt index e64e580..bf69f99 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/widget/DeckList.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/widget/DeckList.kt @@ -86,9 +86,9 @@ class DeckList(ctx: Context?, attrs: AttributeSet?, defStyleAttr: Int) : onCardListChange() } userFavorites.clear() - PrivateInteractor().getFavoriteCards(null, deck.cls.attr1) { + PrivateInteractor().getUserFavoriteCards(null, deck.cls.attr1) { userFavorites.addAll(it) - PrivateInteractor().getFavoriteCards(null, deck.cls.attr2) { + PrivateInteractor().getUserFavoriteCards(null, deck.cls.attr2) { userFavorites.addAll(it) } } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesFragment.kt index e61be15..da73c33 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesFragment.kt @@ -6,8 +6,10 @@ import android.support.v4.app.Fragment import android.support.v4.app.FragmentManager import android.support.v4.app.FragmentStatePagerAdapter import android.support.v4.view.ViewPager +import android.text.format.DateUtils import android.view.* import com.ediposouza.teslesgendstracker.R +import com.ediposouza.teslesgendstracker.ui.base.BaseFilterActivity import com.ediposouza.teslesgendstracker.ui.base.BaseFragment import com.ediposouza.teslesgendstracker.ui.base.CmdShowTabs import com.ediposouza.teslesgendstracker.ui.matches.tabs.MatchesHistory @@ -78,6 +80,11 @@ class MatchesFragment : BaseFragment() { override fun onResume() { super.onResume() eventBus.post(CmdShowTabs()) + matches_view_pager.postDelayed({ + if (activity != null) { + (activity as BaseFilterActivity).updateRarityMagikaFiltersVisibility(false) + } + }, DateUtils.SECOND_IN_MILLIS) } override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt index 6dedd85..e0915f5 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt @@ -9,8 +9,8 @@ import com.ediposouza.teslesgendstracker.R import com.ediposouza.teslesgendstracker.data.Attribute import com.ediposouza.teslesgendstracker.data.Class import com.ediposouza.teslesgendstracker.data.Match +import com.ediposouza.teslesgendstracker.interactor.PrivateInteractor import com.ediposouza.teslesgendstracker.ui.base.BaseFragment -import com.ediposouza.teslesgendstracker.util.TestUtils import com.ediposouza.teslesgendstracker.util.inflate import kotlinx.android.synthetic.main.fragment_matches_statistics.* import kotlinx.android.synthetic.main.itemcell_class.view.* @@ -44,7 +44,22 @@ class MatchesStatistics : BaseFragment() { val classTotal: Class? = null header = Class.values().asList().plus(classTotal) setFirstBody(Class.values().map { listOf(BodyItem(it)) }.plus(listOf(listOf(BodyItem())))) - updateStatisticsData(this) + body = mutableListOf>().apply { + Class.values().forEach { myCls -> + add(mutableListOf().apply { + Class.values().forEach { opponentCls -> + add(BodyItem()) + } + add(BodyItem()) + }) + } + add(mutableListOf().apply { + Class.values().forEach { + add(BodyItem()) + } + add(BodyItem()) + }) + } setSection(listOf()) } matches_statistics_table.adapter = statisticsTableAdapter @@ -61,14 +76,15 @@ class MatchesStatistics : BaseFragment() { } private fun getMatches() { - val matches = TestUtils.getTestMatches() - matches.groupBy { it.player.cls }.forEach { - results[it.key]?.addAll(it.value) - updateStatisticsData() + PrivateInteractor().getUserMatches { + it.groupBy { it.player.cls }.forEach { + results[it.key]?.addAll(it.value) + updateStatisticsData() + } } } - private fun updateStatisticsData(tableAdapter: StatisticsTableAdapter? = statisticsTableAdapter) { + private fun updateStatisticsData() { doAsync { val data = mutableListOf>().apply { Class.values().forEach { myCls -> @@ -91,7 +107,7 @@ class MatchesStatistics : BaseFragment() { }) } uiThread { - tableAdapter?.body = data + statisticsTableAdapter?.body = data } } } @@ -180,11 +196,19 @@ class MatchesStatistics : BaseFragment() { } override fun bindFirstHeader(result: String) { - rootView.cell_text.text = result + bindResult(result) } override fun bindBody(bodyItems: List, row: Int, col: Int) { - rootView.cell_text.text = bodyItems[col].result + bindResult(bodyItems[col].result) + } + + private fun bindResult(result: String?) { + with(rootView) { + cell_text.text = result + cell_text.visibility = if (result == null) View.GONE else View.VISIBLE + cell_progress.visibility = if (result == null) View.VISIBLE else View.GONE + } } override fun bindSection(bodyItems: List, row: Int, col: Int) { diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/util/TestUtils.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/util/TestUtils.kt index ee27def..d42c0c3 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/util/TestUtils.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/util/TestUtils.kt @@ -34,10 +34,10 @@ object TestUtils { for ((indexOpponent, opponentDeck) in decks.withIndex()) { addAll(mutableListOf().apply { for (i in 1..indexPlayer + 1) { - add(Match(playerDeck, opponentDeck, true, 0, false)) + add(Match("", false, playerDeck, opponentDeck, 0, false, true)) } for (i in 1..indexOpponent + 1) { - add(Match(playerDeck, opponentDeck, false, 0, false)) + add(Match("", false, playerDeck, opponentDeck, 0, false, false)) } }) } diff --git a/app/src/main/res/layout/itemcell_text.xml b/app/src/main/res/layout/itemcell_text.xml index b8fc772..b71f825 100644 --- a/app/src/main/res/layout/itemcell_text.xml +++ b/app/src/main/res/layout/itemcell_text.xml @@ -1,10 +1,26 @@ - \ No newline at end of file + android:layout_height="match_parent"> + + + + + + \ No newline at end of file diff --git a/cards.json b/cards.json index aee493c..941cbc9 100644 --- a/cards.json +++ b/cards.json @@ -6344,7 +6344,7 @@ "2016-11-30T08:22:34": { "first": false, "legend": false, - "me": { + "player": { "cls": 0, "deck": "-KWYo4K8PDs_zO1q8aqh", "name": "My Archer", From 316b1671b20996c5e2899e78b666573b29b9dd3c Mon Sep 17 00:00:00 2001 From: EdipoSouza Date: Sat, 7 Jan 2017 23:55:31 -0200 Subject: [PATCH 18/54] Matches statistics by season --- .../com/ediposouza/teslesgendstracker/App.kt | 1 + .../teslesgendstracker/data/General.kt | 48 +++++++ .../teslesgendstracker/data/User.kt | 33 ----- .../interactor/BaseInteractor.kt | 1 + .../interactor/FirebaseParsers.kt | 11 +- .../interactor/PrivateInteractor.kt | 7 +- .../interactor/PublicInteractor.kt | 29 +++- .../ui/matches/tabs/MatchesStatistics.kt | 84 +++++++---- .../teslesgendstracker/util/TestUtils.kt | 9 +- app/src/main/res/menu/menu_season.xml | 13 +- app/src/main/res/values/strings.xml | 1 + cards.json | 133 ++++++++++++------ 12 files changed, 256 insertions(+), 114 deletions(-) delete mode 100644 app/src/main/kotlin/com/ediposouza/teslesgendstracker/data/User.kt diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/App.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/App.kt index bdb2969..d00ce8b 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/App.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/App.kt @@ -46,6 +46,7 @@ class App : Application() { val sync = !ConfigManager.isDBUpdating() && !ConfigManager.isVersionUnsupported() reference.child(BaseInteractor.NODE_CARDS).keepSynced(sync) reference.child(BaseInteractor.NODE_PATCHES).keepSynced(sync) + reference.child(BaseInteractor.NODE_SEASONS).keepSynced(sync) } } } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/data/General.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/data/General.kt index f6f3366..bc751fa 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/data/General.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/data/General.kt @@ -6,6 +6,13 @@ import android.os.Parcelable /** * Created by EdipoSouza on 10/31/16. */ +data class UserInfo( + + val name: String, + val photoUrl: String + +) + data class CardSlot( val card: Card, @@ -38,4 +45,45 @@ data class Patch( val uuidDate: String, val desc: String +) + +data class Season( + + val id: Int, + val uuid: String, + val desc: String, + val reward: String + +) + +enum class MatchType { + + RANKED, + CASUAL, + ARENA + +} + +data class MatchDeck( + + val name: String, + val cls: Class, + val type: DeckType, + val deck: String? = null, + val version: String? = null + +) + +data class Match( + + val uuid: String, + val first: Boolean, + val player: MatchDeck, + val opponent: MatchDeck, + val mode: MatchType, + val season: String, + val rank: Int, + val legend: Boolean, + val win: Boolean + ) \ No newline at end of file diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/data/User.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/data/User.kt deleted file mode 100644 index 560de3d..0000000 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/data/User.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.ediposouza.teslesgendstracker.data - -/** - * Created by ediposouza on 10/31/16. - */ -data class UserInfo( - - val name: String, - val photoUrl: String - -) - -data class Match( - - val uuid: String, - val first: Boolean, - val player: MatchDeck, - val opponent: MatchDeck, - val rank: Int, - val legend: Boolean, - val win: Boolean - -) - -data class MatchDeck( - - val name: String, - val cls: Class, - val type: DeckType, - val deck: String? = null, - val version: String? = null - -) \ No newline at end of file diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/BaseInteractor.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/BaseInteractor.kt index bfb5639..81e73ed 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/BaseInteractor.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/BaseInteractor.kt @@ -16,6 +16,7 @@ open class BaseInteractor { val NODE_CARDS = "cards" val NODE_PATCHES = "patches" + val NODE_SEASONS = "seasons" } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/FirebaseParsers.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/FirebaseParsers.kt index cd86c89..079532d 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/FirebaseParsers.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/FirebaseParsers.kt @@ -3,7 +3,6 @@ package com.ediposouza.teslesgendstracker.interactor import com.ediposouza.teslesgendstracker.data.* import com.ediposouza.teslesgendstracker.util.toIntSafely import org.threeten.bp.LocalDateTime -import timber.log.Timber abstract class FirebaseParsers { @@ -129,8 +128,10 @@ abstract class FirebaseParsers { val first: Boolean = false, val player: Map = mapOf(), val opponent: Map = mapOf(), - val rank: Int = 0, val legend: Boolean = false, + val mode: Int = 0, + val rank: Int = 0, + val season: String = "", val win: Boolean = false ) { @@ -146,7 +147,6 @@ abstract class FirebaseParsers { } fun toMatch(uuid: String): Match { - Timber.d(this.toString()) val playerDeck = MatchDeck(player[KEY_MATCH_DECK_NAME].toString(), Class.values()[player[KEY_MATCH_DECK_CLASS].toString().toInt()], DeckType.values()[player[KEY_MATCH_DECK_TYPE].toString().toInt()], @@ -155,11 +155,12 @@ abstract class FirebaseParsers { val opponentDeck = MatchDeck(opponent[KEY_MATCH_DECK_NAME].toString(), Class.values()[opponent[KEY_MATCH_DECK_CLASS].toString().toInt()], DeckType.values()[opponent[KEY_MATCH_DECK_TYPE].toString().toInt()]) - return Match(uuid, first, playerDeck, opponentDeck, rank, legend, win) + val matchMode = MatchType.values()[mode] + return Match(uuid, first, playerDeck, opponentDeck, matchMode, season, rank, legend, win) } override fun toString(): String { - return "MatchParser(first=$first, player=$player, opponent=$opponent, rank=$rank, legend=$legend, win=$win)" + return "MatchParser(first=$first, player=$player, opponent=$opponent, legend=$legend, mode=$mode, rank=$rank, win=$win)" } } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt index 2210931..4b8a676 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt @@ -31,6 +31,8 @@ class PrivateInteractor : BaseInteractor() { private val KEY_DECK_UPDATES = "updates" private val KEY_DECK_COMMENTS = "comments" + private val KEY_MATCH_SEASON = "season" + private fun getUserID(): String { return FirebaseAuth.getInstance().currentUser?.uid ?: "" } @@ -334,10 +336,11 @@ class PrivateInteractor : BaseInteractor() { } } - fun getUserMatches(onSuccess: (List) -> Unit) { + fun getUserMatches(season: Season?, onSuccess: (List) -> Unit) { dbUser()?.child(NODE_MATCHES)?.apply { keepSynced() - addListenerForSingleValueEvent(object : ValueEventListener { + val query = orderByChild(KEY_MATCH_SEASON).equalTo(season?.uuid) + (if (season != null) query else this).addListenerForSingleValueEvent(object : ValueEventListener { override fun onDataChange(ds: DataSnapshot) { val matches = ds.children.mapTo(arrayListOf()) { diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PublicInteractor.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PublicInteractor.kt index 2ad1324..5da2650 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PublicInteractor.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PublicInteractor.kt @@ -4,7 +4,10 @@ import com.ediposouza.teslesgendstracker.data.* import com.google.firebase.database.DataSnapshot import com.google.firebase.database.DatabaseError import com.google.firebase.database.ValueEventListener +import org.threeten.bp.Month +import org.threeten.bp.format.TextStyle import timber.log.Timber +import java.util.* /** * Created by ediposouza on 01/11/16. @@ -177,8 +180,7 @@ class PublicInteractor : BaseInteractor() { } } - fun getPatches(onError: ((e: Exception?) -> Unit)? = null, - onSuccess: (List) -> Unit) { + fun getPatches(onError: ((e: Exception?) -> Unit)? = null, onSuccess: (List) -> Unit) { database.child(NODE_PATCHES).addListenerForSingleValueEvent(object : ValueEventListener { override fun onDataChange(ds: DataSnapshot) { @@ -197,6 +199,29 @@ class PublicInteractor : BaseInteractor() { }) } + fun getSeasons(onError: ((e: Exception?) -> Unit)? = null, onSuccess: (List) -> Unit) { + database.child(NODE_SEASONS).addListenerForSingleValueEvent(object : ValueEventListener { + + override fun onDataChange(ds: DataSnapshot) { + val seasons = ds.children.mapTo(arrayListOf()) { + val id = it.key.replace("_", "").toInt() + val date = it.key.split("_") + val month = Month.of(date[1].toInt()) + val desc = "${month.getDisplayName(TextStyle.FULL, Locale.getDefault())}/${date[0].toInt()}" + Season(id, it.key, desc, it.value.toString()) + } + Timber.d(seasons.toString()) + onSuccess.invoke(seasons) + } + + override fun onCancelled(de: DatabaseError) { + Timber.d("Fail: " + de.message) + onError?.invoke(de.toException()) + } + + }) + } + fun getUserInfo(uuid: String, onError: ((e: Exception?) -> Unit)? = null, onSuccess: (UserInfo) -> Unit) { dbUsers.child(uuid)?.child(NODE_USERS_INFO)?.addListenerForSingleValueEvent(object : ValueEventListener { diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt index e0915f5..f49126d 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt @@ -9,7 +9,9 @@ import com.ediposouza.teslesgendstracker.R import com.ediposouza.teslesgendstracker.data.Attribute import com.ediposouza.teslesgendstracker.data.Class import com.ediposouza.teslesgendstracker.data.Match +import com.ediposouza.teslesgendstracker.data.Season import com.ediposouza.teslesgendstracker.interactor.PrivateInteractor +import com.ediposouza.teslesgendstracker.interactor.PublicInteractor import com.ediposouza.teslesgendstracker.ui.base.BaseFragment import com.ediposouza.teslesgendstracker.util.inflate import kotlinx.android.synthetic.main.fragment_matches_statistics.* @@ -27,10 +29,11 @@ class MatchesStatistics : BaseFragment() { private val HEADER_FIRST = "Vs" + private var seasons: List = listOf() private var showPercent: Switch? = null var statisticsTableAdapter: StatisticsTableAdapter? = null - val results = HashMap(Class.values().map { it to mutableListOf() }.toMap()) + var results: HashMap> = HashMap() override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { return container?.inflate(R.layout.fragment_matches_statistics) @@ -43,23 +46,8 @@ class MatchesStatistics : BaseFragment() { setFirstHeader(HEADER_FIRST) val classTotal: Class? = null header = Class.values().asList().plus(classTotal) - setFirstBody(Class.values().map { listOf(BodyItem(it)) }.plus(listOf(listOf(BodyItem())))) - body = mutableListOf>().apply { - Class.values().forEach { myCls -> - add(mutableListOf().apply { - Class.values().forEach { opponentCls -> - add(BodyItem()) - } - add(BodyItem()) - }) - } - add(mutableListOf().apply { - Class.values().forEach { - add(BodyItem()) - } - add(BodyItem()) - }) - } + setFirstBody(Class.values().map { listOf(BodyItem(cls = it)) }.plus(listOf(listOf(BodyItem())))) + loadingStatisticsData(this) setSection(listOf()) } matches_statistics_table.adapter = statisticsTableAdapter @@ -70,17 +58,60 @@ class MatchesStatistics : BaseFragment() { menu?.clear() inflater?.inflate(R.menu.menu_percent, menu) inflater?.inflate(R.menu.menu_season, menu) + getSeasons(menu?.findItem(R.id.menu_season)) showPercent = menu?.findItem(R.id.menu_percent)?.actionView as Switch showPercent?.setOnCheckedChangeListener { button, checked -> updateStatisticsData() } super.onCreateOptionsMenu(menu, inflater) } - private fun getMatches() { - PrivateInteractor().getUserMatches { + override fun onOptionsItemSelected(item: MenuItem?): Boolean { + when (item?.itemId) { + R.id.menu_season_all -> getMatches() + else -> seasons.find { it.id == item?.itemId }?.apply { getMatches(this) } + } + return super.onOptionsItemSelected(item) + } + + private fun getSeasons(menuSeason: MenuItem?) { + menuSeason?.subMenu?.apply { + clear() + add(0, R.id.menu_season_all, 0, getString(R.string.matches_seasons_all)) + PublicInteractor().getSeasons { + seasons = it.reversed() + seasons.forEach { + add(0, it.id, 0, it.desc) + } + } + } + } + + private fun getMatches(season: Season? = null) { + loadingStatisticsData() + PrivateInteractor().getUserMatches(season) { it.groupBy { it.player.cls }.forEach { results[it.key]?.addAll(it.value) - updateStatisticsData() } + updateStatisticsData() + } + } + + private fun loadingStatisticsData(tableAdapter: StatisticsTableAdapter? = statisticsTableAdapter) { + results = HashMap(Class.values().map { it to ArrayList() }.toMap()) + tableAdapter?.body = mutableListOf>().apply { + Class.values().forEach { myCls -> + add(mutableListOf().apply { + Class.values().forEach { opponentCls -> + add(BodyItem()) + } + add(BodyItem()) + }) + } + add(mutableListOf().apply { + Class.values().forEach { + add(BodyItem()) + } + add(BodyItem()) + }) } } @@ -116,13 +147,16 @@ class MatchesStatistics : BaseFragment() { val result = matches.groupBy { it.win } val wins = result[true]?.size ?: 0 val losses = result[false]?.size ?: 0 - return BodyItem(result = if (!(showPercent?.isChecked ?: false)) "$wins/$losses" else + return BodyItem(if (!(showPercent?.isChecked ?: false)) "$wins/$losses" else getString(R.string.match_statistics_percent, calcWinRate(wins.toFloat(), losses.toFloat()))) } - private fun calcWinRate(losses: Float, wins: Float) = losses / (wins + losses) * 100 + private fun calcWinRate(wins: Float, losses: Float): Float { + val total = (wins + losses) + return if (total == 0f) -1f else 100 / total * wins + } - class BodyItem(val cls: Class? = null, val result: String? = null) + class BodyItem(val result: String? = null, val cls: Class? = null) class StatisticsTableAdapter(val context: Context) : TableFixHeaderAdapter, CellClass, CellTextCenter, CellTextCenter>(context) { @@ -205,7 +239,7 @@ class MatchesStatistics : BaseFragment() { private fun bindResult(result: String?) { with(rootView) { - cell_text.text = result + cell_text.text = if (result == "0/0" || result == "-1.0%") "-" else result cell_text.visibility = if (result == null) View.GONE else View.VISIBLE cell_progress.visibility = if (result == null) View.VISIBLE else View.GONE } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/util/TestUtils.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/util/TestUtils.kt index d42c0c3..a51ab70 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/util/TestUtils.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/util/TestUtils.kt @@ -1,9 +1,6 @@ package com.ediposouza.teslesgendstracker.util -import com.ediposouza.teslesgendstracker.data.Class -import com.ediposouza.teslesgendstracker.data.DeckType -import com.ediposouza.teslesgendstracker.data.Match -import com.ediposouza.teslesgendstracker.data.MatchDeck +import com.ediposouza.teslesgendstracker.data.* /** * Created by EdipoSouza on 1/5/17. @@ -34,10 +31,10 @@ object TestUtils { for ((indexOpponent, opponentDeck) in decks.withIndex()) { addAll(mutableListOf().apply { for (i in 1..indexPlayer + 1) { - add(Match("", false, playerDeck, opponentDeck, 0, false, true)) + add(Match("", false, playerDeck, opponentDeck, MatchType.RANKED, "2016_12", 0, false, true)) } for (i in 1..indexOpponent + 1) { - add(Match("", false, playerDeck, opponentDeck, 0, false, false)) + add(Match("", false, playerDeck, opponentDeck, MatchType.RANKED, "2016_12", 0, false, false)) } }) } diff --git a/app/src/main/res/menu/menu_season.xml b/app/src/main/res/menu/menu_season.xml index cadf6d9..0b5efba 100644 --- a/app/src/main/res/menu/menu_season.xml +++ b/app/src/main/res/menu/menu_season.xml @@ -6,6 +6,17 @@ android:id="@+id/menu_season" android:icon="@drawable/ic_timer_sand" android:title="@string/menu_season" - app:showAsAction="always" /> + app:showAsAction="always"> + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ec62456..d76e4f5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -113,6 +113,7 @@ Deck saved as Private Please complete your deck before save it + All Seasons Total %1$.1f%% diff --git a/cards.json b/cards.json index 941cbc9..a612315 100644 --- a/cards.json +++ b/cards.json @@ -5208,46 +5208,12 @@ } }, "season": { - "00": { - "month": "2016_07", - "reward": "dagirahtmystic" - }, - "01": { - "month": "2016_08", - "reward": "smugglershaul" - }, - "02": { - "month": "2016_09", - "reward": "histgrove" - }, - "03": { - "month": "2016_10", - "reward": "mechanicalally" - }, - "04": { - "month": "2016_11", - "reward": "battlereeveofdusk" - }, - "05": { - "month": "2016_12", - "reward": "elsweyrlookout" - }, - "2016_03": { - "month": "2016_03", - "reward": "magesguildretreat" - }, - "2016_04": { - "month": "2016_04", - "reward": "witheredhandcultist" - }, - "2016_05": { - "month": "2016_05", - "reward": "staffofsparks" - }, - "2016_06": { - "month": "2016_06", - "reward": "plunder" - } + "2016_08": "smugglershaul", + "2016_09": "histgrove", + "2016_10": "mechanicalally", + "2016_11": "battlereeveofdusk", + "2016_12": "elsweyrlookout", + "2017_01": "unknown" }, "users": { "ZYO8GAaZfVYo2evDwPedYIFODcm1": { @@ -6356,8 +6322,95 @@ "name": "Prophecy Assassin", "type": 0 }, + "type": 0, + "mode": 0, "rank": 4, + "season": "2016_11", "win": true + }, + "2016-12-03T09:22:34": { + "first": true, + "legend": false, + "player": { + "cls": 0, + "deck": "-KWYo4K8PDs_zO1q8aqh", + "name": "My Archer", + "type": 4, + "version": "v1 (2016-10-30)" + }, + "opponent": { + "cls": 0, + "name": "Cycle Archer", + "type": 0 + }, + "type": 1, + "mode": 0, + "rank": 4, + "season": "2016_12", + "win": false + }, + "2017-01-03T10:22:34": { + "first": true, + "legend": false, + "player": { + "cls": 7, + "deck": "-K_t7BMw5-q1sFGXCfoi", + "name": "Item Sorcerer", + "type": 0, + "version": "v1 (2016-10-30)" + }, + "opponent": { + "cls": 2, + "name": "Prophecy Assassin", + "type": 0 + }, + "type": 1, + "mode": 1, + "rank": 3, + "season": "2017_01", + "win": true + }, + "2017-01-03T11:20:34": { + "first": false, + "legend": false, + "player": { + "cls": 7, + "deck": "-K_t7BMw5-q1sFGXCfoi", + "name": "Item Sorcerer", + "type": 0, + "version": "v1 (2016-10-30)" + }, + "opponent": { + "cls": 6, + "name": "Mimic Scout", + "type": 0 + }, + "type": 1, + "mode": 1, + "rank": 3, + "season": "2017_01", + "win": true + }, + "2017-01-03T12:20:34": { + "first": true, + "legend": false, + "player": { + "cls": 7, + "deck": "-K_t7BMw5-q1sFGXCfoi", + "name": "Item Sorcerer", + "type": 0, + "version": "v1 (2016-10-30)" + }, + "opponent": { + "cls": 6, + "name": "Mimic Scout", + "type": 0 + }, + "type": 1, + "mode": 2, + "rank": 3, + "season": "2017_01", + "win": false } } }, From 195ef3fb21eec6f6348f0c4c46c7a2cd2326d055 Mon Sep 17 00:00:00 2001 From: EdipoSouza Date: Sun, 8 Jan 2017 01:37:11 -0200 Subject: [PATCH 19/54] Matches statistics by game mode --- .../teslesgendstracker/data/General.kt | 4 +- .../interactor/FirebaseParsers.kt | 2 +- .../teslesgendstracker/ui/matches/Commands.kt | 8 ++ .../ui/matches/MatchesFragment.kt | 9 ++ .../ui/matches/tabs/MatchesStatistics.kt | 16 ++- .../teslesgendstracker/util/TestUtils.kt | 4 +- app/src/main/res/drawable/ic_mode_arena.xml | 4 + .../main/res/drawable/ic_mode_arena_svg.xml | 111 ++++++++++++++++++ app/src/main/res/drawable/ic_mode_casual.xml | 4 + .../main/res/drawable/ic_mode_casual_svg.xml | 11 ++ app/src/main/res/drawable/ic_mode_ranked.xml | 4 + .../main/res/drawable/ic_mode_ranked_svg.xml | 10 ++ .../res/drawable/selector_bottom_tab_mode.xml | 5 + app/src/main/res/layout/activity_dash.xml | 2 +- app/src/main/res/layout/fragment_matches.xml | 13 +- .../layout/fragment_matches_statistics.xml | 3 +- .../main/res/menu/navigation_bottom_mode.xml | 19 +++ ...{menu_drawer.xml => navigation_drawer.xml} | 0 app/src/main/res/values/colors.xml | 1 + app/src/main/res/values/strings.xml | 3 + 20 files changed, 219 insertions(+), 14 deletions(-) create mode 100644 app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/Commands.kt create mode 100644 app/src/main/res/drawable/ic_mode_arena.xml create mode 100644 app/src/main/res/drawable/ic_mode_arena_svg.xml create mode 100644 app/src/main/res/drawable/ic_mode_casual.xml create mode 100644 app/src/main/res/drawable/ic_mode_casual_svg.xml create mode 100644 app/src/main/res/drawable/ic_mode_ranked.xml create mode 100644 app/src/main/res/drawable/ic_mode_ranked_svg.xml create mode 100644 app/src/main/res/drawable/selector_bottom_tab_mode.xml create mode 100644 app/src/main/res/menu/navigation_bottom_mode.xml rename app/src/main/res/menu/{menu_drawer.xml => navigation_drawer.xml} (100%) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/data/General.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/data/General.kt index bc751fa..55e34d6 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/data/General.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/data/General.kt @@ -56,7 +56,7 @@ data class Season( ) -enum class MatchType { +enum class MatchMode { RANKED, CASUAL, @@ -80,7 +80,7 @@ data class Match( val first: Boolean, val player: MatchDeck, val opponent: MatchDeck, - val mode: MatchType, + val mode: MatchMode, val season: String, val rank: Int, val legend: Boolean, diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/FirebaseParsers.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/FirebaseParsers.kt index 079532d..c770144 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/FirebaseParsers.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/FirebaseParsers.kt @@ -155,7 +155,7 @@ abstract class FirebaseParsers { val opponentDeck = MatchDeck(opponent[KEY_MATCH_DECK_NAME].toString(), Class.values()[opponent[KEY_MATCH_DECK_CLASS].toString().toInt()], DeckType.values()[opponent[KEY_MATCH_DECK_TYPE].toString().toInt()]) - val matchMode = MatchType.values()[mode] + val matchMode = MatchMode.values()[mode] return Match(uuid, first, playerDeck, opponentDeck, matchMode, season, rank, legend, win) } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/Commands.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/Commands.kt new file mode 100644 index 0000000..f46153a --- /dev/null +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/Commands.kt @@ -0,0 +1,8 @@ +package com.ediposouza.teslesgendstracker.ui.matches + +import com.ediposouza.teslesgendstracker.data.MatchMode + +/** + * Created by EdipoSouza on 1/8/17. + */ +data class CmdUpdateMode(val mode: MatchMode) \ No newline at end of file diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesFragment.kt index da73c33..147fe33 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesFragment.kt @@ -9,6 +9,7 @@ import android.support.v4.view.ViewPager import android.text.format.DateUtils import android.view.* import com.ediposouza.teslesgendstracker.R +import com.ediposouza.teslesgendstracker.data.MatchMode import com.ediposouza.teslesgendstracker.ui.base.BaseFilterActivity import com.ediposouza.teslesgendstracker.ui.base.BaseFragment import com.ediposouza.teslesgendstracker.ui.base.CmdShowTabs @@ -56,6 +57,14 @@ class MatchesFragment : BaseFragment() { activity.dash_navigation_view.setCheckedItem(R.id.menu_matches) matches_view_pager.adapter = MatchesPageAdapter(context, childFragmentManager) matches_view_pager.addOnPageChangeListener(pageChange) + matches_nav_mode.setOnNavigationItemSelectedListener { + when (it.itemId) { + R.id.tab_mode_ranked -> eventBus.post(CmdUpdateMode(MatchMode.RANKED)) + R.id.tab_mode_casual -> eventBus.post(CmdUpdateMode(MatchMode.CASUAL)) + R.id.tab_mode_arena -> eventBus.post(CmdUpdateMode(MatchMode.ARENA)) + } + true + } MetricsManager.trackScreen(MetricScreen.SCREEN_MATCHES_STATISTICS()) } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt index f49126d..ed087ad 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt @@ -6,18 +6,17 @@ import android.view.* import android.widget.FrameLayout import android.widget.Switch import com.ediposouza.teslesgendstracker.R -import com.ediposouza.teslesgendstracker.data.Attribute -import com.ediposouza.teslesgendstracker.data.Class -import com.ediposouza.teslesgendstracker.data.Match -import com.ediposouza.teslesgendstracker.data.Season +import com.ediposouza.teslesgendstracker.data.* import com.ediposouza.teslesgendstracker.interactor.PrivateInteractor import com.ediposouza.teslesgendstracker.interactor.PublicInteractor import com.ediposouza.teslesgendstracker.ui.base.BaseFragment +import com.ediposouza.teslesgendstracker.ui.matches.CmdUpdateMode import com.ediposouza.teslesgendstracker.util.inflate import kotlinx.android.synthetic.main.fragment_matches_statistics.* import kotlinx.android.synthetic.main.itemcell_class.view.* import kotlinx.android.synthetic.main.itemcell_text.view.* import miguelbcr.ui.tableFixHeadesWrapper.TableFixHeaderAdapter +import org.greenrobot.eventbus.Subscribe import org.jetbrains.anko.doAsync import org.jetbrains.anko.uiThread import java.util.* @@ -29,6 +28,7 @@ class MatchesStatistics : BaseFragment() { private val HEADER_FIRST = "Vs" + private var currentMatchMode = MatchMode.RANKED private var seasons: List = listOf() private var showPercent: Switch? = null @@ -88,7 +88,7 @@ class MatchesStatistics : BaseFragment() { private fun getMatches(season: Season? = null) { loadingStatisticsData() PrivateInteractor().getUserMatches(season) { - it.groupBy { it.player.cls }.forEach { + it.filter { it.mode == currentMatchMode }.groupBy { it.player.cls }.forEach { results[it.key]?.addAll(it.value) } updateStatisticsData() @@ -156,6 +156,12 @@ class MatchesStatistics : BaseFragment() { return if (total == 0f) -1f else 100 / total * wins } + @Subscribe + fun onUpdateMode(cmdUpdateMode: CmdUpdateMode) { + currentMatchMode = cmdUpdateMode.mode + getMatches() + } + class BodyItem(val result: String? = null, val cls: Class? = null) class StatisticsTableAdapter(val context: Context) : TableFixHeaderAdapter().apply { for (i in 1..indexPlayer + 1) { - add(Match("", false, playerDeck, opponentDeck, MatchType.RANKED, "2016_12", 0, false, true)) + add(Match("", false, playerDeck, opponentDeck, MatchMode.RANKED, "2016_12", 0, false, true)) } for (i in 1..indexOpponent + 1) { - add(Match("", false, playerDeck, opponentDeck, MatchType.RANKED, "2016_12", 0, false, false)) + add(Match("", false, playerDeck, opponentDeck, MatchMode.RANKED, "2016_12", 0, false, false)) } }) } diff --git a/app/src/main/res/drawable/ic_mode_arena.xml b/app/src/main/res/drawable/ic_mode_arena.xml new file mode 100644 index 0000000..1c41ba8 --- /dev/null +++ b/app/src/main/res/drawable/ic_mode_arena.xml @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/main/res/drawable/ic_mode_arena_svg.xml b/app/src/main/res/drawable/ic_mode_arena_svg.xml new file mode 100644 index 0000000..a643075 --- /dev/null +++ b/app/src/main/res/drawable/ic_mode_arena_svg.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_mode_casual.xml b/app/src/main/res/drawable/ic_mode_casual.xml new file mode 100644 index 0000000..9839b4d --- /dev/null +++ b/app/src/main/res/drawable/ic_mode_casual.xml @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/main/res/drawable/ic_mode_casual_svg.xml b/app/src/main/res/drawable/ic_mode_casual_svg.xml new file mode 100644 index 0000000..4cdaf98 --- /dev/null +++ b/app/src/main/res/drawable/ic_mode_casual_svg.xml @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_mode_ranked.xml b/app/src/main/res/drawable/ic_mode_ranked.xml new file mode 100644 index 0000000..57d8cc9 --- /dev/null +++ b/app/src/main/res/drawable/ic_mode_ranked.xml @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/main/res/drawable/ic_mode_ranked_svg.xml b/app/src/main/res/drawable/ic_mode_ranked_svg.xml new file mode 100644 index 0000000..0f7c1a1 --- /dev/null +++ b/app/src/main/res/drawable/ic_mode_ranked_svg.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/selector_bottom_tab_mode.xml b/app/src/main/res/drawable/selector_bottom_tab_mode.xml new file mode 100644 index 0000000..5594557 --- /dev/null +++ b/app/src/main/res/drawable/selector_bottom_tab_mode.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_dash.xml b/app/src/main/res/layout/activity_dash.xml index 39c7060..9fe8de0 100644 --- a/app/src/main/res/layout/activity_dash.xml +++ b/app/src/main/res/layout/activity_dash.xml @@ -118,6 +118,6 @@ app:itemIconTint="@drawable/selector_menu_item" app:itemTextAppearance="?android:attr/textAppearanceMedium" app:itemTextColor="@drawable/selector_menu_item" - app:menu="@menu/menu_drawer" /> + app:menu="@menu/navigation_drawer" /> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_matches.xml b/app/src/main/res/layout/fragment_matches.xml index 8a5c304..f0d3806 100644 --- a/app/src/main/res/layout/fragment_matches.xml +++ b/app/src/main/res/layout/fragment_matches.xml @@ -9,6 +9,17 @@ + android:layout_height="0dp" + android:layout_weight="1"/> + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_matches_statistics.xml b/app/src/main/res/layout/fragment_matches_statistics.xml index 1faa834..a1e264c 100644 --- a/app/src/main/res/layout/fragment_matches_statistics.xml +++ b/app/src/main/res/layout/fragment_matches_statistics.xml @@ -6,7 +6,6 @@ + android:layout_height="match_parent" /> \ No newline at end of file diff --git a/app/src/main/res/menu/navigation_bottom_mode.xml b/app/src/main/res/menu/navigation_bottom_mode.xml new file mode 100644 index 0000000..69c92e8 --- /dev/null +++ b/app/src/main/res/menu/navigation_bottom_mode.xml @@ -0,0 +1,19 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_drawer.xml b/app/src/main/res/menu/navigation_drawer.xml similarity index 100% rename from app/src/main/res/menu/menu_drawer.xml rename to app/src/main/res/menu/navigation_drawer.xml diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 8141b51..535a439 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -26,6 +26,7 @@ #212121 #4db6ac #AA4db6ac + #664db6ac #4CAF50 #0097A7 #FFCDD2 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d76e4f5..84d1f8c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -45,6 +45,9 @@ Favorites Statistics History + Ranked + Casual + Arena @string/app_name_full Cards Collection From 7fb0889fa9b7cb45ea9adb61fe6be7c870a1e970 Mon Sep 17 00:00:00 2001 From: EdipoSouza Date: Sun, 8 Jan 2017 10:56:09 -0200 Subject: [PATCH 20/54] Change matches game mode bar colors. Show checked option in sets and season menu --- .../ui/cards/tabs/CardsAllFragment.kt | 25 +++++++++++++------ .../ui/matches/tabs/MatchesStatistics.kt | 11 ++++++-- app/src/main/res/drawable/ic_checked.xml | 4 +++ app/src/main/res/drawable/ic_checked_svg.xml | 10 ++++++++ .../res/drawable/selector_bottom_tab_mode.xml | 4 +-- app/src/main/res/layout/fragment_matches.xml | 2 +- app/src/main/res/menu/menu_season.xml | 1 + app/src/main/res/menu/menu_sets.xml | 1 + app/src/main/res/values/colors.xml | 2 +- 9 files changed, 47 insertions(+), 13 deletions(-) create mode 100644 app/src/main/res/drawable/ic_checked.xml create mode 100644 app/src/main/res/drawable/ic_checked_svg.xml diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/tabs/CardsAllFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/tabs/CardsAllFragment.kt index 0c10998..7a166bd 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/tabs/CardsAllFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/tabs/CardsAllFragment.kt @@ -8,10 +8,7 @@ import android.support.v4.app.ActivityOptionsCompat import android.support.v7.util.DiffUtil import android.support.v7.widget.GridLayoutManager import android.support.v7.widget.RecyclerView -import android.view.LayoutInflater -import android.view.MenuItem -import android.view.View -import android.view.ViewGroup +import android.view.* import com.ediposouza.teslesgendstracker.App import com.ediposouza.teslesgendstracker.R import com.ediposouza.teslesgendstracker.data.* @@ -30,6 +27,7 @@ import kotlinx.android.synthetic.main.fragment_cards_list.* import kotlinx.android.synthetic.main.include_login_button.* import kotlinx.android.synthetic.main.itemlist_card.view.* import org.greenrobot.eventbus.Subscribe +import org.jetbrains.anko.itemsSequence import java.util.* /** @@ -48,6 +46,7 @@ open class CardsAllFragment : BaseFragment() { var classFilter: Class? = null var rarityFilter: CardRarity? = null var searchFilter: String? = null + var menuSets: SubMenu? = null val privateInteractor: PrivateInteractor by lazy { PrivateInteractor() } val transitionName: String by lazy { getString(R.string.card_transition_name) } @@ -84,15 +83,27 @@ open class CardsAllFragment : BaseFragment() { cardsAdapter.onRestoreState(cards_recycler_view.layoutManager as GridLayoutManager) } + override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { + super.onCreateOptionsMenu(menu, inflater) + menuSets = menu?.findItem(R.id.menu_sets)?.subMenu + } + override fun onOptionsItemSelected(item: MenuItem?): Boolean { when (item?.itemId) { - R.id.menu_sets_all -> eventBus.post(CmdFilterSet(null)) - R.id.menu_sets_core -> eventBus.post(CmdFilterSet(CardSet.CORE)) - R.id.menu_sets_madhouse -> eventBus.post(CmdFilterSet(CardSet.MADHOUSE)) + R.id.menu_sets_all -> filterSet(item, null) + R.id.menu_sets_core -> filterSet(item, CardSet.CORE) + R.id.menu_sets_madhouse -> filterSet(item, CardSet.MADHOUSE) } return super.onOptionsItemSelected(item) } + private fun filterSet(menuItem: MenuItem?, set: CardSet?) { + menuSets?.itemsSequence()?.forEach { + it.setIcon(if (it.itemId == menuItem?.itemId) R.drawable.ic_checked else 0) + } + eventBus.post(CmdFilterSet(set)) + } + open fun configRecycleView() { cards_recycler_view.layoutManager = object : GridLayoutManager(context, CARDS_PER_ROW) { override fun supportsPredictiveItemAnimations(): Boolean = false diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt index ed087ad..7c07fa7 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt @@ -18,6 +18,7 @@ import kotlinx.android.synthetic.main.itemcell_text.view.* import miguelbcr.ui.tableFixHeadesWrapper.TableFixHeaderAdapter import org.greenrobot.eventbus.Subscribe import org.jetbrains.anko.doAsync +import org.jetbrains.anko.itemsSequence import org.jetbrains.anko.uiThread import java.util.* @@ -31,6 +32,7 @@ class MatchesStatistics : BaseFragment() { private var currentMatchMode = MatchMode.RANKED private var seasons: List = listOf() private var showPercent: Switch? = null + private var menuSeasons: SubMenu? = null var statisticsTableAdapter: StatisticsTableAdapter? = null var results: HashMap> = HashMap() @@ -73,9 +75,10 @@ class MatchesStatistics : BaseFragment() { } private fun getSeasons(menuSeason: MenuItem?) { - menuSeason?.subMenu?.apply { + menuSeasons = menuSeason?.subMenu + menuSeasons?.apply { clear() - add(0, R.id.menu_season_all, 0, getString(R.string.matches_seasons_all)) + add(0, R.id.menu_season_all, 0, getString(R.string.matches_seasons_all)).setIcon(R.drawable.ic_checked) PublicInteractor().getSeasons { seasons = it.reversed() seasons.forEach { @@ -86,6 +89,10 @@ class MatchesStatistics : BaseFragment() { } private fun getMatches(season: Season? = null) { + val seasonId = season?.id ?: R.id.menu_season_all + menuSeasons?.itemsSequence()?.forEach { + it.setIcon(if (it.itemId == seasonId) R.drawable.ic_checked else 0) + } loadingStatisticsData() PrivateInteractor().getUserMatches(season) { it.filter { it.mode == currentMatchMode }.groupBy { it.player.cls }.forEach { diff --git a/app/src/main/res/drawable/ic_checked.xml b/app/src/main/res/drawable/ic_checked.xml new file mode 100644 index 0000000..b7684ec --- /dev/null +++ b/app/src/main/res/drawable/ic_checked.xml @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/main/res/drawable/ic_checked_svg.xml b/app/src/main/res/drawable/ic_checked_svg.xml new file mode 100644 index 0000000..0e3fcf1 --- /dev/null +++ b/app/src/main/res/drawable/ic_checked_svg.xml @@ -0,0 +1,10 @@ + + + + diff --git a/app/src/main/res/drawable/selector_bottom_tab_mode.xml b/app/src/main/res/drawable/selector_bottom_tab_mode.xml index 5594557..050f7b3 100644 --- a/app/src/main/res/drawable/selector_bottom_tab_mode.xml +++ b/app/src/main/res/drawable/selector_bottom_tab_mode.xml @@ -1,5 +1,5 @@ - - + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_matches.xml b/app/src/main/res/layout/fragment_matches.xml index f0d3806..ee9bc9c 100644 --- a/app/src/main/res/layout/fragment_matches.xml +++ b/app/src/main/res/layout/fragment_matches.xml @@ -17,7 +17,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="@dimen/navigation_bar_height" - app:itemBackground="@color/teal_300_transparent2" + app:itemBackground="@color/white_transparent" app:itemIconTint="@drawable/selector_bottom_tab_mode" app:itemTextColor="@drawable/selector_bottom_tab_mode" app:menu="@menu/navigation_bottom_mode"/> diff --git a/app/src/main/res/menu/menu_season.xml b/app/src/main/res/menu/menu_season.xml index 0b5efba..ea6d1d8 100644 --- a/app/src/main/res/menu/menu_season.xml +++ b/app/src/main/res/menu/menu_season.xml @@ -13,6 +13,7 @@ diff --git a/app/src/main/res/menu/menu_sets.xml b/app/src/main/res/menu/menu_sets.xml index 0211011..cc51fc7 100644 --- a/app/src/main/res/menu/menu_sets.xml +++ b/app/src/main/res/menu/menu_sets.xml @@ -12,6 +12,7 @@ #212121 #4db6ac #AA4db6ac - #664db6ac #4CAF50 #0097A7 #FFCDD2 #e57373 #C62828 + #33ffffff #6f99ff #FFD54F From b84bb8c50b9dce8fb1696044dc5f73fd472aa947 Mon Sep 17 00:00:00 2001 From: EdipoSouza Date: Sun, 8 Jan 2017 16:56:13 -0200 Subject: [PATCH 21/54] Matches history --- .../interactor/PrivateInteractor.kt | 13 ++- .../ui/decks/tabs/DecksPublicFragment.kt | 4 +- .../teslesgendstracker/ui/matches/Commands.kt | 5 +- .../ui/matches/MatchesFragment.kt | 45 +++++++- .../ui/matches/tabs/MatchesHistory.kt | 105 +++++++++++++++++- .../ui/matches/tabs/MatchesStatistics.kt | 58 +++------- .../main/res/drawable-hdpi/ic_rank_legend.png | Bin 0 -> 4758 bytes .../res/drawable-xhdpi/ic_rank_legend.png | Bin 0 -> 5733 bytes .../res/drawable-xxhdpi/ic_rank_legend.png | Bin 0 -> 16060 bytes .../res/drawable-xxxhdpi/ic_rank_legend.png | Bin 0 -> 26916 bytes app/src/main/res/drawable/ic_first.xml | 4 + app/src/main/res/drawable/ic_first_svg.xml | 10 ++ .../res/layout/fragment_matches_history.xml | 19 +++- .../layout/fragment_matches_statistics.xml | 12 +- .../res/layout/itemlist_match_history.xml | 78 +++++++++++++ .../res/layout/itemlist_match_history_ads.xml | 9 ++ app/src/main/res/values/strings.xml | 9 +- 17 files changed, 302 insertions(+), 69 deletions(-) create mode 100644 app/src/main/res/drawable-hdpi/ic_rank_legend.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_rank_legend.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_rank_legend.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_rank_legend.png create mode 100644 app/src/main/res/drawable/ic_first.xml create mode 100644 app/src/main/res/drawable/ic_first_svg.xml create mode 100644 app/src/main/res/layout/itemlist_match_history.xml create mode 100644 app/src/main/res/layout/itemlist_match_history_ads.xml diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt index 4b8a676..978380a 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt @@ -175,10 +175,8 @@ class PrivateInteractor : BaseInteractor() { } fun getUserFavoriteDecks(cls: Class?, onSuccess: (List?) -> Unit) { - PublicInteractor().getPublicDecks(cls) { - val publicDecks = it - dbUser()?.child(NODE_DECKS)?.child(NODE_FAVORITE)?.apply { - keepSynced() + PublicInteractor().getPublicDecks(cls) { publicDecks -> + getUserFavoriteDecksRef()?.apply { addListenerForSingleValueEvent(object : ValueEventListener { override fun onDataChange(ds: DataSnapshot) { @@ -336,9 +334,12 @@ class PrivateInteractor : BaseInteractor() { } } + fun getUserMatchesRef() = dbUser()?.child(NODE_MATCHES)?.apply { + keepSynced() + } + fun getUserMatches(season: Season?, onSuccess: (List) -> Unit) { - dbUser()?.child(NODE_MATCHES)?.apply { - keepSynced() + getUserMatchesRef()?.apply { val query = orderByChild(KEY_MATCH_SEASON).equalTo(season?.uuid) (if (season != null) query else this).addListenerForSingleValueEvent(object : ValueEventListener { diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt index a4b4928..8c6d7ad 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt @@ -85,7 +85,6 @@ open class DecksPublicFragment : BaseFragment() { } override fun onBindContentHolder(itemKey: String, model: FirebaseParsers.DeckParser, viewHolder: DecksAllViewHolder) { - Timber.d(model.toString()) viewHolder.bind(model.toDeck(itemKey, isDeckPrivate), privateInteractor) } @@ -145,9 +144,10 @@ open class DecksPublicFragment : BaseFragment() { open fun showDecks() { decksAdapter.reset() + decksAdapter.notifyDataSetChanged() } - class DecksAllViewHolder(val view: View, val itemClick: (View, Deck) -> Unit, + class DecksAllViewHolder(view: View, val itemClick: (View, Deck) -> Unit, val itemLongClick: (View, Deck) -> Boolean) : RecyclerView.ViewHolder(view) { constructor(view: View) : this(view, { view, deck -> }, { view, deck -> true }) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/Commands.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/Commands.kt index f46153a..7c3e0f3 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/Commands.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/Commands.kt @@ -1,8 +1,11 @@ package com.ediposouza.teslesgendstracker.ui.matches import com.ediposouza.teslesgendstracker.data.MatchMode +import com.ediposouza.teslesgendstracker.data.Season /** * Created by EdipoSouza on 1/8/17. */ -data class CmdUpdateMode(val mode: MatchMode) \ No newline at end of file +data class CmdFilterMode(val mode: MatchMode) + +data class CmdFilterSeason(val season: Season?) \ No newline at end of file diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesFragment.kt index 147fe33..d9ff83a 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesFragment.kt @@ -10,6 +10,8 @@ import android.text.format.DateUtils import android.view.* import com.ediposouza.teslesgendstracker.R import com.ediposouza.teslesgendstracker.data.MatchMode +import com.ediposouza.teslesgendstracker.data.Season +import com.ediposouza.teslesgendstracker.interactor.PublicInteractor import com.ediposouza.teslesgendstracker.ui.base.BaseFilterActivity import com.ediposouza.teslesgendstracker.ui.base.BaseFragment import com.ediposouza.teslesgendstracker.ui.base.CmdShowTabs @@ -20,6 +22,7 @@ import com.ediposouza.teslesgendstracker.util.MetricsManager import com.ediposouza.teslesgendstracker.util.inflate import kotlinx.android.synthetic.main.activity_dash.* import kotlinx.android.synthetic.main.fragment_matches.* +import org.jetbrains.anko.itemsSequence /** * Created by EdipoSouza on 1/3/17. @@ -28,10 +31,12 @@ class MatchesFragment : BaseFragment() { private val KEY_PAGE_VIEW_POSITION = "pageViewPositionKey" + private var seasons: List = listOf() + private var menuSeasons: SubMenu? = null + val pageChange = object : ViewPager.SimpleOnPageChangeListener() { override fun onPageSelected(position: Int) { updateActivityTitle(position) -// (matches_view_pager.adapter as MatchesPageAdapter).getItem(position).updateCardsList() MetricsManager.trackScreen(when (position) { 0 -> MetricScreen.SCREEN_MATCHES_STATISTICS() else -> MetricScreen.SCREEN_MATCHES_HISTORY() @@ -59,9 +64,9 @@ class MatchesFragment : BaseFragment() { matches_view_pager.addOnPageChangeListener(pageChange) matches_nav_mode.setOnNavigationItemSelectedListener { when (it.itemId) { - R.id.tab_mode_ranked -> eventBus.post(CmdUpdateMode(MatchMode.RANKED)) - R.id.tab_mode_casual -> eventBus.post(CmdUpdateMode(MatchMode.CASUAL)) - R.id.tab_mode_arena -> eventBus.post(CmdUpdateMode(MatchMode.ARENA)) + R.id.tab_mode_ranked -> eventBus.post(CmdFilterMode(MatchMode.RANKED)) + R.id.tab_mode_casual -> eventBus.post(CmdFilterMode(MatchMode.CASUAL)) + R.id.tab_mode_arena -> eventBus.post(CmdFilterMode(MatchMode.ARENA)) } true } @@ -98,10 +103,42 @@ class MatchesFragment : BaseFragment() { override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { menu?.clear() + inflater?.inflate(R.menu.menu_percent, menu) inflater?.inflate(R.menu.menu_season, menu) + getSeasons(menu?.findItem(R.id.menu_season)) super.onCreateOptionsMenu(menu, inflater) } + override fun onOptionsItemSelected(item: MenuItem?): Boolean { + when (item?.itemId) { + R.id.menu_season_all -> filterSeason(null) + else -> seasons.find { it.id == item?.itemId }?.apply { filterSeason(this) } + } + return super.onOptionsItemSelected(item) + } + + private fun filterSeason(season: Season?) { + val seasonId = season?.id ?: R.id.menu_season_all + menuSeasons?.itemsSequence()?.forEach { + it.setIcon(if (it.itemId == seasonId) R.drawable.ic_checked else 0) + } + eventBus.post(CmdFilterSeason(season)) + } + + private fun getSeasons(menuSeason: MenuItem?) { + menuSeasons = menuSeason?.subMenu + menuSeasons?.apply { + clear() + add(0, R.id.menu_season_all, 0, getString(R.string.matches_seasons_all)).setIcon(R.drawable.ic_checked) + PublicInteractor().getSeasons { + seasons = it.reversed() + seasons.forEach { + add(0, it.id, 0, it.desc) + } + } + } + } + class MatchesPageAdapter(ctx: Context, fm: FragmentManager) : FragmentStatePagerAdapter(fm) { var titles: Array = ctx.resources.getStringArray(R.array.matches_tabs) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesHistory.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesHistory.kt index f700d00..7d62a3a 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesHistory.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesHistory.kt @@ -1,24 +1,123 @@ package com.ediposouza.teslesgendstracker.ui.matches.tabs import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup +import android.support.v4.content.ContextCompat +import android.support.v7.widget.LinearLayoutManager +import android.support.v7.widget.RecyclerView +import android.view.* import com.ediposouza.teslesgendstracker.R +import com.ediposouza.teslesgendstracker.data.Match +import com.ediposouza.teslesgendstracker.data.MatchMode +import com.ediposouza.teslesgendstracker.data.Season +import com.ediposouza.teslesgendstracker.interactor.FirebaseParsers +import com.ediposouza.teslesgendstracker.interactor.PrivateInteractor +import com.ediposouza.teslesgendstracker.ui.base.BaseAdsFirebaseAdapter import com.ediposouza.teslesgendstracker.ui.base.BaseFragment +import com.ediposouza.teslesgendstracker.ui.matches.CmdFilterMode +import com.ediposouza.teslesgendstracker.ui.matches.CmdFilterSeason +import com.ediposouza.teslesgendstracker.ui.util.firebase.OnLinearLayoutItemScrolled import com.ediposouza.teslesgendstracker.util.inflate +import jp.wasabeef.recyclerview.animators.SlideInLeftAnimator +import kotlinx.android.synthetic.main.fragment_matches_history.* +import kotlinx.android.synthetic.main.itemlist_match_history.view.* +import org.greenrobot.eventbus.Subscribe /** * Created by EdipoSouza on 1/3/17. */ class MatchesHistory : BaseFragment() { + val ADS_EACH_ITEMS = 20 //after 10 lines + val MATCH_PAGE_SIZE = 15 + + private var currentMatchMode = MatchMode.RANKED + private var currentSeason: Season? = null + + private val dataFilter: (FirebaseParsers.MatchParser) -> Boolean = { + it.mode == currentMatchMode.ordinal && (it.season == currentSeason?.uuid || currentSeason == null) + } + + private val matchesAdapter: BaseAdsFirebaseAdapter by lazy { + object : BaseAdsFirebaseAdapter( + FirebaseParsers.MatchParser::class.java, { PrivateInteractor().getUserMatchesRef() }, + MATCH_PAGE_SIZE, ADS_EACH_ITEMS, R.layout.itemlist_match_history_ads, false, dataFilter) { + + override fun onCreateDefaultViewHolder(parent: ViewGroup): MatchViewHolder { + return MatchViewHolder(parent.inflate(R.layout.itemlist_match_history)) + } + + override fun onBindContentHolder(itemKey: String, model: FirebaseParsers.MatchParser, viewHolder: MatchViewHolder) { + viewHolder.bind(model.toMatch(itemKey)) + } + + override fun onSyncEnd() { + matches_refresh_layout?.isRefreshing = false + } + + } + } + override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { return container?.inflate(R.layout.fragment_matches_history) } override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + setHasOptionsMenu(true) + with(matches_recycler_view) { + adapter = matchesAdapter + itemAnimator = SlideInLeftAnimator() + layoutManager = object : LinearLayoutManager(context) { + override fun supportsPredictiveItemAnimations(): Boolean = false + } + addOnScrollListener(OnLinearLayoutItemScrolled(matchesAdapter.getContentCount() - 3) { + matchesAdapter.more() + }) + } + matches_refresh_layout.setOnRefreshListener { + matchesAdapter.reset() + } + } + + override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { + menu?.findItem(R.id.menu_percent)?.isVisible = false + super.onCreateOptionsMenu(menu, inflater) + } + + @Subscribe + fun onFilterMode(cmdFilterMode: CmdFilterMode) { + currentMatchMode = cmdFilterMode.mode + matchesAdapter.reset() + matches_recycler_view.scrollToPosition(0) + } + + @Subscribe + fun onFilterSeason(cmdFilterSeason: CmdFilterSeason) { + currentSeason = cmdFilterSeason.season + matchesAdapter.reset() + matches_recycler_view.scrollToPosition(0) + } + + class MatchViewHolder(view: View) : RecyclerView.ViewHolder(view) { + + fun bind(match: Match) { + with(itemView) { + match_history_first.visibility = if (match.first) View.VISIBLE else View.INVISIBLE + match_history_player_class_attr1.setImageResource(match.player.cls.attr1.imageRes) + match_history_player_class_attr2.setImageResource(match.player.cls.attr2.imageRes) + match_history_opponent_class_attr1.setImageResource(match.opponent.cls.attr1.imageRes) + match_history_opponent_class_attr2.setImageResource(match.opponent.cls.attr2.imageRes) + val resultColor = if (match.win) R.color.green_500 else R.color.red_800 + val resultText = if (match.win) R.string.match_win else R.string.match_loss + match_history_result.setTextColor(ContextCompat.getColor(context, resultColor)) + match_history_result.text = context.getString(resultText) + match_history_legend.visibility = if (match.legend) View.VISIBLE else View.INVISIBLE + val rankText = if (match.legend) R.string.match_rank_legend else R.string.match_rank_normal + match_history_rank.text = context.getString(rankText, match.rank) + match_history_rank.visibility = if (match.mode == MatchMode.RANKED) View.VISIBLE else View.INVISIBLE + } + } + } } \ No newline at end of file diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt index 7c07fa7..e639f0e 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt @@ -8,9 +8,9 @@ import android.widget.Switch import com.ediposouza.teslesgendstracker.R import com.ediposouza.teslesgendstracker.data.* import com.ediposouza.teslesgendstracker.interactor.PrivateInteractor -import com.ediposouza.teslesgendstracker.interactor.PublicInteractor import com.ediposouza.teslesgendstracker.ui.base.BaseFragment -import com.ediposouza.teslesgendstracker.ui.matches.CmdUpdateMode +import com.ediposouza.teslesgendstracker.ui.matches.CmdFilterMode +import com.ediposouza.teslesgendstracker.ui.matches.CmdFilterSeason import com.ediposouza.teslesgendstracker.util.inflate import kotlinx.android.synthetic.main.fragment_matches_statistics.* import kotlinx.android.synthetic.main.itemcell_class.view.* @@ -18,7 +18,6 @@ import kotlinx.android.synthetic.main.itemcell_text.view.* import miguelbcr.ui.tableFixHeadesWrapper.TableFixHeaderAdapter import org.greenrobot.eventbus.Subscribe import org.jetbrains.anko.doAsync -import org.jetbrains.anko.itemsSequence import org.jetbrains.anko.uiThread import java.util.* @@ -27,12 +26,11 @@ import java.util.* */ class MatchesStatistics : BaseFragment() { - private val HEADER_FIRST = "Vs" + private val HEADER_FIRST by lazy { getString(R.string.match_vs) } private var currentMatchMode = MatchMode.RANKED - private var seasons: List = listOf() + private var currentSeason: Season? = null private var showPercent: Switch? = null - private var menuSeasons: SubMenu? = null var statisticsTableAdapter: StatisticsTableAdapter? = null var results: HashMap> = HashMap() @@ -57,44 +55,16 @@ class MatchesStatistics : BaseFragment() { } override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { - menu?.clear() - inflater?.inflate(R.menu.menu_percent, menu) - inflater?.inflate(R.menu.menu_season, menu) - getSeasons(menu?.findItem(R.id.menu_season)) - showPercent = menu?.findItem(R.id.menu_percent)?.actionView as Switch + val menuPercent = menu?.findItem(R.id.menu_percent) + menuPercent?.isVisible = true + showPercent = menuPercent?.actionView as Switch showPercent?.setOnCheckedChangeListener { button, checked -> updateStatisticsData() } super.onCreateOptionsMenu(menu, inflater) } - override fun onOptionsItemSelected(item: MenuItem?): Boolean { - when (item?.itemId) { - R.id.menu_season_all -> getMatches() - else -> seasons.find { it.id == item?.itemId }?.apply { getMatches(this) } - } - return super.onOptionsItemSelected(item) - } - - private fun getSeasons(menuSeason: MenuItem?) { - menuSeasons = menuSeason?.subMenu - menuSeasons?.apply { - clear() - add(0, R.id.menu_season_all, 0, getString(R.string.matches_seasons_all)).setIcon(R.drawable.ic_checked) - PublicInteractor().getSeasons { - seasons = it.reversed() - seasons.forEach { - add(0, it.id, 0, it.desc) - } - } - } - } - - private fun getMatches(season: Season? = null) { - val seasonId = season?.id ?: R.id.menu_season_all - menuSeasons?.itemsSequence()?.forEach { - it.setIcon(if (it.itemId == seasonId) R.drawable.ic_checked else 0) - } + private fun getMatches() { loadingStatisticsData() - PrivateInteractor().getUserMatches(season) { + PrivateInteractor().getUserMatches(currentSeason) { it.filter { it.mode == currentMatchMode }.groupBy { it.player.cls }.forEach { results[it.key]?.addAll(it.value) } @@ -164,8 +134,14 @@ class MatchesStatistics : BaseFragment() { } @Subscribe - fun onUpdateMode(cmdUpdateMode: CmdUpdateMode) { - currentMatchMode = cmdUpdateMode.mode + fun onFilterMode(cmdFilterMode: CmdFilterMode) { + currentMatchMode = cmdFilterMode.mode + getMatches() + } + + @Subscribe + fun onFilterSeason(cmdFilterSeason: CmdFilterSeason) { + currentSeason = cmdFilterSeason.season getMatches() } diff --git a/app/src/main/res/drawable-hdpi/ic_rank_legend.png b/app/src/main/res/drawable-hdpi/ic_rank_legend.png new file mode 100644 index 0000000000000000000000000000000000000000..7f3afef53235429ff363f6520aca0619219c78f9 GIT binary patch literal 4758 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 XD=3q5?DCg200000NkvXXu0mjfT(ulq literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxhdpi/ic_rank_legend.png b/app/src/main/res/drawable-xxhdpi/ic_rank_legend.png new file mode 100644 index 0000000000000000000000000000000000000000..a0c11c9eaf23d27dd91b46c7e0ff9b98aff13de7 GIT binary patch literal 16060 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%0AsT#>ax5N2HFzw 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/ic_first.xml b/app/src/main/res/drawable/ic_first.xml new file mode 100644 index 0000000..7ae97a2 --- /dev/null +++ b/app/src/main/res/drawable/ic_first.xml @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/main/res/drawable/ic_first_svg.xml b/app/src/main/res/drawable/ic_first_svg.xml new file mode 100644 index 0000000..95a7c6a --- /dev/null +++ b/app/src/main/res/drawable/ic_first_svg.xml @@ -0,0 +1,10 @@ + + + + diff --git a/app/src/main/res/layout/fragment_matches_history.xml b/app/src/main/res/layout/fragment_matches_history.xml index 0f36c22..6f8aa42 100644 --- a/app/src/main/res/layout/fragment_matches_history.xml +++ b/app/src/main/res/layout/fragment_matches_history.xml @@ -1,6 +1,21 @@ - - \ No newline at end of file + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_matches_statistics.xml b/app/src/main/res/layout/fragment_matches_statistics.xml index a1e264c..cf283e2 100644 --- a/app/src/main/res/layout/fragment_matches_statistics.xml +++ b/app/src/main/res/layout/fragment_matches_statistics.xml @@ -1,11 +1,5 @@ - - - - - \ No newline at end of file + android:layout_height="match_parent" /> \ No newline at end of file diff --git a/app/src/main/res/layout/itemlist_match_history.xml b/app/src/main/res/layout/itemlist_match_history.xml new file mode 100644 index 0000000..79e3dd9 --- /dev/null +++ b/app/src/main/res/layout/itemlist_match_history.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/itemlist_match_history_ads.xml b/app/src/main/res/layout/itemlist_match_history_ads.xml new file mode 100644 index 0000000..cffd8a9 --- /dev/null +++ b/app/src/main/res/layout/itemlist_match_history_ads.xml @@ -0,0 +1,9 @@ + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 84d1f8c..87747e5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -9,6 +9,8 @@ ca-app-pub-7881623561544201/2208682379 ca-app-pub-7881623561544201/6037962770 240x50 + ca-app-pub-7881623561544201/6811091571 + 360x80 Press again to exit TESL Tracker database is under update. You will be notified when it is completed. This TESL Tracker version became unsupported. Please update to continue using. @@ -116,8 +118,13 @@ Deck saved as Private Please complete your deck before save it - All Seasons + Vs + %1$d + #%1$d + Win + Loss Total %1$.1f%% + All Seasons From c3d90f1b30b212f8aa982df6f7d60c7ddecd8d3c Mon Sep 17 00:00:00 2001 From: EdipoSouza Date: Sun, 8 Jan 2017 23:05:35 -0200 Subject: [PATCH 22/54] Matches history item changes. Remove tab and filters from dash_activity --- .../teslesgendstracker/ui/DashActivity.kt | 41 +++++----- .../ui/base/BaseFilterActivity.kt | 4 +- .../teslesgendstracker/ui/base/Commands.kt | 5 +- .../ui/cards/CardsFragment.kt | 25 +++--- .../ui/cards/tabs/CardsCollectionFragment.kt | 4 +- .../ui/decks/DecksFragment.kt | 48 +++-------- .../ui/decks/new/NewDeckActivity.kt | 10 +-- .../ui/matches/MatchesFragment.kt | 31 ++++--- .../ui/matches/tabs/MatchesHistory.kt | 2 +- app/src/main/res/drawable/ic_first_svg.xml | 17 +++- app/src/main/res/layout/activity_dash.xml | 68 +--------------- app/src/main/res/layout/activity_new_deck.xml | 6 +- app/src/main/res/layout/fragment_cards.xml | 80 ++++++++++++++++--- app/src/main/res/layout/fragment_decks.xml | 68 +++++++++++++--- app/src/main/res/layout/fragment_matches.xml | 77 ++++++++++++++---- .../res/layout/fragment_matches_history.xml | 1 - .../res/layout/itemlist_match_history.xml | 26 +++--- app/src/main/res/values/colors.xml | 2 + app/src/main/res/values/dimens.xml | 1 + 19 files changed, 306 insertions(+), 210 deletions(-) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DashActivity.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DashActivity.kt index 6d90fde..3afd266 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DashActivity.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DashActivity.kt @@ -15,14 +15,11 @@ import com.ediposouza.teslesgendstracker.interactor.PrivateInteractor import com.ediposouza.teslesgendstracker.interactor.PublicInteractor import com.ediposouza.teslesgendstracker.ui.base.BaseFilterActivity import com.ediposouza.teslesgendstracker.ui.base.CmdLoginSuccess -import com.ediposouza.teslesgendstracker.ui.base.CmdShowTabs +import com.ediposouza.teslesgendstracker.ui.base.CmdUpdateTitle import com.ediposouza.teslesgendstracker.ui.cards.CardsFragment -import com.ediposouza.teslesgendstracker.ui.cards.CmdFilterMagika -import com.ediposouza.teslesgendstracker.ui.cards.CmdFilterRarity import com.ediposouza.teslesgendstracker.ui.decks.DecksFragment import com.ediposouza.teslesgendstracker.ui.matches.MatchesFragment import com.ediposouza.teslesgendstracker.ui.util.CircleTransform -import com.ediposouza.teslesgendstracker.ui.widget.CollectionStatistics import com.google.firebase.auth.FirebaseAuth import kotlinx.android.synthetic.main.activity_dash.* import kotlinx.android.synthetic.main.navigation_drawer_header.view.* @@ -40,17 +37,10 @@ class DashActivity : BaseFilterActivity(), private val publicInteractor = PublicInteractor() private val privateInteractor = PrivateInteractor() - private val statisticsSheetBehavior: BottomSheetBehavior by lazy { - BottomSheetBehavior.from(cards_collection_statistics) - } - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_dash) - decks_fab_add.hide() snackbarNeedMargin = false - filter_rarity.filterClick = { eventBus.post(CmdFilterRarity(it)) } - filter_magika.filterClick = { eventBus.post(CmdFilterMagika(it)) } with(dash_navigation_view.getHeaderView(0)) { profile_change_user.setOnClickListener { showLogin() } profile_image.setOnClickListener { @@ -68,7 +58,7 @@ class DashActivity : BaseFilterActivity(), override fun onDrawerOpened(drawerView: View) { super.onDrawerOpened(drawerView) - with(statisticsSheetBehavior) { + getStatisticsBottomSheetBehavior()?.apply { if (state == BottomSheetBehavior.STATE_EXPANDED) { state = BottomSheetBehavior.STATE_COLLAPSED } @@ -123,9 +113,11 @@ class DashActivity : BaseFilterActivity(), } override fun onBackPressed() { - if (statisticsSheetBehavior.state == BottomSheetBehavior.STATE_EXPANDED) { - statisticsSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED - return + getStatisticsBottomSheetBehavior()?.apply { + if (state == BottomSheetBehavior.STATE_EXPANDED) { + state = BottomSheetBehavior.STATE_COLLAPSED + return + } } if (dash_drawer_layout.isDrawerOpen(Gravity.START)) { dash_drawer_layout.closeDrawer(Gravity.START) @@ -158,7 +150,7 @@ class DashActivity : BaseFilterActivity(), } private fun showFragment(frag: Fragment): Boolean { - statisticsSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN + getStatisticsBottomSheetBehavior()?.state = BottomSheetBehavior.STATE_HIDDEN if (supportFragmentManager.backStackEntryCount > 0) { supportFragmentManager.popBackStackImmediate() } @@ -170,6 +162,13 @@ class DashActivity : BaseFilterActivity(), return true } + private fun getStatisticsBottomSheetBehavior(): BottomSheetBehavior<*>? { + findViewById(R.id.cards_collection_statistics)?.apply { + return BottomSheetBehavior.from(this) + } + return null + } + private fun updateCollectionStatistics() { var allCardsTotal = 0 var userCardsTotal = 0 @@ -203,14 +202,14 @@ class DashActivity : BaseFilterActivity(), } @Subscribe - fun onLoginSuccess(cmdLoginSuccess: CmdLoginSuccess) { - updateUserMenuInfo() - updateCollectionStatistics() + fun onUpdateTitle(cmdUpdateTitle: CmdUpdateTitle) { + dash_toolbar_title.setText(cmdUpdateTitle.title) } @Subscribe - fun onCmdShowTabs(cmdShowTabs: CmdShowTabs) { - dash_app_bar_layout.setExpanded(true, true) + fun onLoginSuccess(cmdLoginSuccess: CmdLoginSuccess) { + updateUserMenuInfo() + updateCollectionStatistics() } } \ No newline at end of file diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseFilterActivity.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseFilterActivity.kt index 39a859e..4804f97 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseFilterActivity.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseFilterActivity.kt @@ -24,8 +24,8 @@ open class BaseFilterActivity : BaseActivity() { var filterGreatMargin = false - val fab_filter_magika by lazy { find(R.id.filter_magika) } - val fab_filter_rarity by lazy { find(R.id.filter_rarity) } + val fab_filter_magika by lazy { find(R.id.cards_filter_magika) } + val fab_filter_rarity by lazy { find(R.id.cards_filter_rarity) } val filterMagikaLP by lazy { fab_filter_magika.layoutParams as CoordinatorLayout.LayoutParams } val filterRarityLP by lazy { fab_filter_rarity.layoutParams as CoordinatorLayout.LayoutParams } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/Commands.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/Commands.kt index 8921cc6..637efa3 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/Commands.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/Commands.kt @@ -2,6 +2,7 @@ package com.ediposouza.teslesgendstracker.ui.base import android.R import android.support.annotation.IntDef +import android.support.annotation.IntegerRes import android.support.annotation.StringRes import android.support.design.widget.BaseTransientBottomBar import android.support.design.widget.Snackbar @@ -11,14 +12,14 @@ import com.ediposouza.teslesgendstracker.data.Class /** * Created by EdipoSouza on 11/6/16. */ -class CmdShowTabs - class CmdShowLogin class CmdLoginSuccess class CmdUpdateDeckAndShowDeck +data class CmdUpdateTitle(@IntegerRes val title: Int) + data class CmdShowCardsByAttr(val attr: Attribute) data class CmdShowDecksByClasses(val classes: List) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/CardsFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/CardsFragment.kt index 58b03e6..fcb7a01 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/CardsFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/CardsFragment.kt @@ -37,8 +37,8 @@ class CardsFragment : BaseFragment(), SearchView.OnQueryTextListener { private val handler = Handler() private val trackSearch = Runnable { MetricsManager.trackSearch(query ?: "") } - val statisticsSheetBehavior: BottomSheetBehavior by lazy { - BottomSheetBehavior.from(activity.cards_collection_statistics) + private val statisticsSheetBehavior: BottomSheetBehavior by lazy { + BottomSheetBehavior.from(cards_collection_statistics) } val pageChange = object : ViewPager.SimpleOnPageChangeListener() { @@ -47,7 +47,7 @@ class CardsFragment : BaseFragment(), SearchView.OnQueryTextListener { (cards_view_pager.adapter as CardsPageAdapter).getItem(position).updateCardsList() if (position == 1) { statisticsSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED - activity.cards_collection_statistics.updateStatistics() + cards_collection_statistics.updateStatistics() } else { statisticsSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN } @@ -62,11 +62,12 @@ class CardsFragment : BaseFragment(), SearchView.OnQueryTextListener { } private fun updateActivityTitle(position: Int) { - activity.toolbar_title?.setText(when (position) { + val title = when (position) { 1 -> R.string.title_tab_cards_collection 2 -> R.string.title_tab_cards_favorites else -> R.string.title_tab_cards_all - }) + } + eventBus.post(CmdUpdateTitle(title)) } override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { @@ -77,16 +78,18 @@ class CardsFragment : BaseFragment(), SearchView.OnQueryTextListener { super.onViewCreated(view, savedInstanceState) setHasOptionsMenu(true) activity.dash_navigation_view.setCheckedItem(R.id.menu_cards) - activity.cards_collection_statistics.setOnClickListener { + cards_collection_statistics.setOnClickListener { statisticsSheetBehavior.toggleExpanded() } statisticsSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN cards_view_pager.adapter = CardsPageAdapter(context, childFragmentManager) cards_view_pager.addOnPageChangeListener(pageChange) - attr_filter.filterClick = { + cards_filter_attr.filterClick = { eventBus.post(CmdShowCardsByAttr(it)) - attr_filter.selectAttr(it, true) + cards_filter_attr.selectAttr(it, true) } + cards_filter_rarity.filterClick = { eventBus.post(CmdFilterRarity(it)) } + cards_filter_magika.filterClick = { eventBus.post(CmdFilterMagika(it)) } Handler().postDelayed({ eventBus.post(CmdShowCardsByAttr(Attribute.STRENGTH)) }, DateUtils.SECOND_IN_MILLIS) @@ -95,8 +98,8 @@ class CardsFragment : BaseFragment(), SearchView.OnQueryTextListener { override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) - activity.toolbar_title.setText(R.string.app_name_full) - activity.dash_tab_layout.setupWithViewPager(cards_view_pager) + eventBus.post(CmdUpdateTitle(R.string.app_name_full)) + cards_tab_layout.setupWithViewPager(cards_view_pager) } override fun onSaveInstanceState(outState: Bundle?) { @@ -113,7 +116,7 @@ class CardsFragment : BaseFragment(), SearchView.OnQueryTextListener { override fun onResume() { super.onResume() - eventBus.post(CmdShowTabs()) + cards_app_bar_layout.setExpanded(true, true) (activity as BaseFilterActivity).updateRarityMagikaFiltersVisibility(true) } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/tabs/CardsCollectionFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/tabs/CardsCollectionFragment.kt index e86dc2f..56bf42c 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/tabs/CardsCollectionFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/tabs/CardsCollectionFragment.kt @@ -21,9 +21,9 @@ import com.ediposouza.teslesgendstracker.util.MetricScreen import com.ediposouza.teslesgendstracker.util.MetricsManager import com.ediposouza.teslesgendstracker.util.inflate import jp.wasabeef.recyclerview.animators.ScaleInAnimator -import kotlinx.android.synthetic.main.activity_dash.* import kotlinx.android.synthetic.main.fragment_cards_list.* import kotlinx.android.synthetic.main.itemlist_card_collection.view.* +import org.jetbrains.anko.find import java.util.* /** @@ -33,7 +33,7 @@ class CardsCollectionFragment : CardsAllFragment() { override val isCardsCollection: Boolean = true - val view_statistics: CollectionStatistics by lazy { activity.cards_collection_statistics } + val view_statistics by lazy { activity.find(R.id.cards_collection_statistics) } val statisticsSheetBehavior: BottomSheetBehavior by lazy { BottomSheetBehavior.from(view_statistics) } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/DecksFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/DecksFragment.kt index adc25a1..7f8f62d 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/DecksFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/DecksFragment.kt @@ -16,7 +16,6 @@ import android.view.* import android.view.inputmethod.InputMethodManager import com.ediposouza.teslesgendstracker.R import com.ediposouza.teslesgendstracker.data.Class -import com.ediposouza.teslesgendstracker.ui.DashActivity import com.ediposouza.teslesgendstracker.ui.base.* import com.ediposouza.teslesgendstracker.ui.cards.CmdFilterSearch import com.ediposouza.teslesgendstracker.ui.decks.new.NewDeckActivity @@ -56,11 +55,12 @@ class DecksFragment : BaseFragment(), SearchView.OnQueryTextListener { } private fun updateActivityTitle(position: Int) { - activity.toolbar_title?.setText(when (position) { + val title = CmdUpdateTitle(when (position) { 1 -> R.string.title_tab_decks_owned 2 -> R.string.title_tab_decks_favorites else -> R.string.title_tab_decks_public }) + eventBus.post(title) } override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { @@ -71,9 +71,9 @@ class DecksFragment : BaseFragment(), SearchView.OnQueryTextListener { super.onViewCreated(view, savedInstanceState) setHasOptionsMenu(true) decks_view_pager.adapter = adapter - activity.dash_tab_layout.setupWithViewPager(decks_view_pager) + decks_tab_layout.setupWithViewPager(decks_view_pager) activity.dash_navigation_view.setCheckedItem(R.id.menu_decks) - activity.toolbar_title?.setText(R.string.title_tab_decks_public) + eventBus.post(CmdUpdateTitle(R.string.title_tab_decks_public)) decks_view_pager.addOnPageChangeListener(pageChange) decks_attr_filter.filterClick = { if (decks_attr_filter.isAttrSelected(it)) { @@ -83,13 +83,17 @@ class DecksFragment : BaseFragment(), SearchView.OnQueryTextListener { } eventBus.post(CmdShowDecksByClasses(Class.getClasses(decks_attr_filter.getSelectedAttrs()))) } + decks_fab_add.setOnClickListener { + val anim = ActivityOptionsCompat.makeCustomAnimation(context, R.anim.slide_up, R.anim.slide_down) + startActivityForResult(context.intentFor(), RC_NEW_DECK, anim.toBundle()) + } MetricsManager.trackScreen(MetricScreen.SCREEN_DECKS_PUBLIC()) } override fun onSaveInstanceState(outState: Bundle?) { outState?.apply { putInt(KEY_PAGE_VIEW_POSITION, decks_view_pager?.currentItem ?: 0) - putBoolean(KEY_FAB_NEW_DECK, activity?.decks_fab_add?.isShown ?: false) + putBoolean(KEY_FAB_NEW_DECK, decks_fab_add?.isShown ?: false) } super.onSaveInstanceState(outState) } @@ -98,38 +102,12 @@ class DecksFragment : BaseFragment(), SearchView.OnQueryTextListener { super.onViewStateRestored(savedInstanceState) savedInstanceState?.apply { decks_view_pager.currentItem = getInt(KEY_PAGE_VIEW_POSITION) - if (getBoolean(KEY_FAB_NEW_DECK)) { - configFABNewDeck(activity as DashActivity) - } - } - } - - override fun onAttach(context: Context?) { - super.onAttach(context) - configFABNewDeck(context as DashActivity) - } - - private fun configFABNewDeck(dashActivity: DashActivity) { - dashActivity.decks_fab_add?.apply { - setOnClickListener { - val anim = ActivityOptionsCompat.makeCustomAnimation(context, R.anim.slide_up, R.anim.slide_down) - startActivityForResult(context.intentFor(), RC_NEW_DECK, anim.toBundle()) - } - postDelayed({ if (this@DecksFragment.isAdded) show() }, DateUtils.SECOND_IN_MILLIS * 2) } } override fun onResume() { super.onResume() - eventBus.post(CmdShowTabs()) - } - - override fun onDetach() { - super.onDetach() - with(activity.decks_fab_add) { - setOnClickListener { } - hide() - } + decks_app_bar_layout.setExpanded(true, true) } override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { @@ -164,11 +142,11 @@ class DecksFragment : BaseFragment(), SearchView.OnQueryTextListener { } @Subscribe - fun onCmdUpdateRarityMagikaFiltersVisibility(update: CmdUpdateVisibility) { + fun onCmdUpdateVisibility(update: CmdUpdateVisibility) { if (update.show) { - activity.decks_fab_add.show() + decks_fab_add.show() } else { - activity.decks_fab_add.hide() + decks_fab_add.hide() } } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/new/NewDeckActivity.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/new/NewDeckActivity.kt index d55cfff..abd4619 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/new/NewDeckActivity.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/new/NewDeckActivity.kt @@ -63,7 +63,7 @@ class NewDeckActivity : BaseFilterActivity() { override fun onPostCreate(savedInstanceState: Bundle?) { super.onPostCreate(savedInstanceState) supportActionBar?.setDisplayHomeAsUpEnabled(true) - toolbar_title.text = getString(R.string.new_deck_title) + new_deck_toolbar_title.text = getString(R.string.new_deck_title) new_deck_cardlist.editMode = true configDeckFilters() supportFragmentManager.beginTransaction() @@ -81,18 +81,18 @@ class NewDeckActivity : BaseFilterActivity() { onAttrLock = { attr1: Attribute, attr2: Attribute -> val deckCls = Class.getClasses(listOf(attr1, attr2)).first() new_deck_class_cover.setImageResource(deckCls.imageRes) - toolbar_title.text = getString(R.string.new_deck_class_title, deckCls.name.toLowerCase().capitalize()) + new_deck_toolbar_title.text = getString(R.string.new_deck_class_title, deckCls.name.toLowerCase().capitalize()) val outValue = TypedValue() resources.getValue(R.dimen.deck_class_cover_alpha, outValue, true) new_deck_class_cover.animate().alpha(outValue.float).setDuration(ANIM_DURATION).start() } onAttrUnlock = { - toolbar_title.text = getString(R.string.new_deck_title) + new_deck_toolbar_title.text = getString(R.string.new_deck_title) new_deck_class_cover.animate().alpha(0f).setDuration(ANIM_DURATION).start() } } - filter_rarity.filterClick = { eventBus.post(CmdFilterRarity(it)) } - filter_magika.filterClick = { eventBus.post(CmdFilterMagika(it)) } + cards_filter_rarity.filterClick = { eventBus.post(CmdFilterRarity(it)) } + cards_filter_magika.filterClick = { eventBus.post(CmdFilterMagika(it)) } } override fun onSaveInstanceState(outState: Bundle?) { diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesFragment.kt index d9ff83a..c76c776 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesFragment.kt @@ -6,15 +6,14 @@ import android.support.v4.app.Fragment import android.support.v4.app.FragmentManager import android.support.v4.app.FragmentStatePagerAdapter import android.support.v4.view.ViewPager -import android.text.format.DateUtils import android.view.* import com.ediposouza.teslesgendstracker.R import com.ediposouza.teslesgendstracker.data.MatchMode import com.ediposouza.teslesgendstracker.data.Season import com.ediposouza.teslesgendstracker.interactor.PublicInteractor -import com.ediposouza.teslesgendstracker.ui.base.BaseFilterActivity import com.ediposouza.teslesgendstracker.ui.base.BaseFragment -import com.ediposouza.teslesgendstracker.ui.base.CmdShowTabs +import com.ediposouza.teslesgendstracker.ui.base.CmdUpdateTitle +import com.ediposouza.teslesgendstracker.ui.base.CmdUpdateVisibility import com.ediposouza.teslesgendstracker.ui.matches.tabs.MatchesHistory import com.ediposouza.teslesgendstracker.ui.matches.tabs.MatchesStatistics import com.ediposouza.teslesgendstracker.util.MetricScreen @@ -22,6 +21,7 @@ import com.ediposouza.teslesgendstracker.util.MetricsManager import com.ediposouza.teslesgendstracker.util.inflate import kotlinx.android.synthetic.main.activity_dash.* import kotlinx.android.synthetic.main.fragment_matches.* +import org.greenrobot.eventbus.Subscribe import org.jetbrains.anko.itemsSequence /** @@ -46,10 +46,11 @@ class MatchesFragment : BaseFragment() { } private fun updateActivityTitle(position: Int) { - activity.toolbar_title?.setText(when (position) { + val title = when (position) { 0 -> R.string.title_tab_matches_statistics else -> R.string.title_tab_matches_history - }) + } + eventBus.post(CmdUpdateTitle(title)) } override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { @@ -75,8 +76,8 @@ class MatchesFragment : BaseFragment() { override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) - activity.toolbar_title.setText(R.string.title_tab_matches_statistics) - activity.dash_tab_layout.setupWithViewPager(matches_view_pager) + eventBus.post(CmdUpdateTitle(R.string.title_tab_matches_statistics)) + matches_tab_layout.setupWithViewPager(matches_view_pager) } override fun onSaveInstanceState(outState: Bundle?) { @@ -93,12 +94,7 @@ class MatchesFragment : BaseFragment() { override fun onResume() { super.onResume() - eventBus.post(CmdShowTabs()) - matches_view_pager.postDelayed({ - if (activity != null) { - (activity as BaseFilterActivity).updateRarityMagikaFiltersVisibility(false) - } - }, DateUtils.SECOND_IN_MILLIS) + matches_app_bar_layout.setExpanded(true, true) } override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { @@ -139,6 +135,15 @@ class MatchesFragment : BaseFragment() { } } + @Subscribe + fun onCmdUpdateVisibility(update: CmdUpdateVisibility) { + if (update.show) { + matches_fab_add.show() + } else { + matches_fab_add.hide() + } + } + class MatchesPageAdapter(ctx: Context, fm: FragmentManager) : FragmentStatePagerAdapter(fm) { var titles: Array = ctx.resources.getStringArray(R.array.matches_tabs) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesHistory.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesHistory.kt index 7d62a3a..f58fda2 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesHistory.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesHistory.kt @@ -107,7 +107,7 @@ class MatchesHistory : BaseFragment() { match_history_player_class_attr2.setImageResource(match.player.cls.attr2.imageRes) match_history_opponent_class_attr1.setImageResource(match.opponent.cls.attr1.imageRes) match_history_opponent_class_attr2.setImageResource(match.opponent.cls.attr2.imageRes) - val resultColor = if (match.win) R.color.green_500 else R.color.red_800 + val resultColor = if (match.win) R.color.green_200 else R.color.red_100 val resultText = if (match.win) R.string.match_win else R.string.match_loss match_history_result.setTextColor(ContextCompat.getColor(context, resultColor)) match_history_result.text = context.getString(resultText) diff --git a/app/src/main/res/drawable/ic_first_svg.xml b/app/src/main/res/drawable/ic_first_svg.xml index 95a7c6a..34037ed 100644 --- a/app/src/main/res/drawable/ic_first_svg.xml +++ b/app/src/main/res/drawable/ic_first_svg.xml @@ -1,10 +1,21 @@ - + android:fillColor="@android:color/white" + android:pathData="M3.723,9.088L3.723,7.875C4.865,7.764 5.662,7.578 6.113,7.317C6.564,7.056 6.901,6.439 7.124,5.467L8.372,5.467L8.372,18L6.685,18L6.685,9.088L3.723,9.088Z" + android:strokeColor="#00000000" + android:strokeWidth="1" /> + + diff --git a/app/src/main/res/layout/activity_dash.xml b/app/src/main/res/layout/activity_dash.xml index 9fe8de0..557a561 100644 --- a/app/src/main/res/layout/activity_dash.xml +++ b/app/src/main/res/layout/activity_dash.xml @@ -27,7 +27,7 @@ tools:targetApi="lollipop"> - - - - - - - - - - - - - - - - - - - + android:layout_height="match_parent" /> diff --git a/app/src/main/res/layout/activity_new_deck.xml b/app/src/main/res/layout/activity_new_deck.xml index fe3be74..655ec50 100644 --- a/app/src/main/res/layout/activity_new_deck.xml +++ b/app/src/main/res/layout/activity_new_deck.xml @@ -48,7 +48,7 @@ tools:targetApi="lollipop"> - + tools:layout_marginBottom="@dimen/navigation_bar_height" + tools:layout_marginTop="@dimen/status_bar_height"> - + android:background="@null"> - + + + + + + + + + + + + + + + + + + + + + android:layout_height="match_parent" + android:clickable="true" + android:elevation="@dimen/statistics_elevation" + app:behavior_hideable="true" + app:behavior_peekHeight="@dimen/statistics_bottom_peek_height" + app:layout_behavior="@string/bottom_sheet_behavior" /> - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_decks.xml b/app/src/main/res/layout/fragment_decks.xml index 3a664a7..8b4be24 100644 --- a/app/src/main/res/layout/fragment_decks.xml +++ b/app/src/main/res/layout/fragment_decks.xml @@ -1,21 +1,67 @@ - + tools:layout_marginBottom="@dimen/navigation_bar_height" + tools:layout_marginTop="@dimen/status_bar_height"> - + android:background="@null"> - + + + + + android:layout_height="match_parent" + app:layout_behavior="@string/appbar_scrolling_view_behavior"> + + + + + + + + + + + + - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_matches.xml b/app/src/main/res/layout/fragment_matches.xml index ee9bc9c..7ac3a22 100644 --- a/app/src/main/res/layout/fragment_matches.xml +++ b/app/src/main/res/layout/fragment_matches.xml @@ -1,25 +1,72 @@ - + tools:layout_marginBottom="@dimen/navigation_bar_height" + tools:layout_marginTop="@dimen/status_bar_height"> - + android:layout_height="wrap_content" + android:background="@null"> + + + + - + app:layout_behavior="@string/appbar_scrolling_view_behavior"> + + + + + + + + + + + + - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_matches_history.xml b/app/src/main/res/layout/fragment_matches_history.xml index 6f8aa42..81644a1 100644 --- a/app/src/main/res/layout/fragment_matches_history.xml +++ b/app/src/main/res/layout/fragment_matches_history.xml @@ -11,7 +11,6 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:fadeScrollbars="false" - android:padding="@dimen/default_margin" android:scrollbarSize="@dimen/small_margin" android:scrollbarThumbVertical="@drawable/scrollbar_thumb" android:scrollbars="vertical" diff --git a/app/src/main/res/layout/itemlist_match_history.xml b/app/src/main/res/layout/itemlist_match_history.xml index 79e3dd9..db33ec2 100644 --- a/app/src/main/res/layout/itemlist_match_history.xml +++ b/app/src/main/res/layout/itemlist_match_history.xml @@ -51,14 +51,16 @@ android:src="@drawable/attr_intelligence" /> + android:ems="5" + android:textColor="@color/teal_100" + android:textStyle="bold" + tools:text="7" /> + android:textColor="@color/green_200" + android:textStyle="bold" + tools:text="Win" /> \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index cf75dcd..545c9e5 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -24,8 +24,10 @@ #BDBDBD #9e9e9e #212121 + #b2dfdb #4db6ac #AA4db6ac + #a5d6a7 #4CAF50 #0097A7 #FFCDD2 diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 6a4036f..55a6a24 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -2,6 +2,7 @@ 24dp 0dp + 56dp 8dp 4dp From 9b5298033669ead605b9fb23a0543b01f808a4a4 Mon Sep 17 00:00:00 2001 From: EdipoSouza Date: Mon, 9 Jan 2017 01:27:18 -0200 Subject: [PATCH 23/54] New matches dialog --- app/build.gradle | 1 + .../com/ediposouza/teslesgendstracker/App.kt | 4 +- .../interactor/PrivateInteractor.kt | 45 +++++++++++ .../ui/decks/new/NewDeckActivity.kt | 2 +- .../ui/matches/MatchesFragment.kt | 74 +++++++++++++++++++ app/src/main/res/layout/dialog_new_deck.xml | 4 +- app/src/main/res/layout/dialog_new_match.xml | 72 ++++++++++++++++++ app/src/main/res/layout/itemcell_class.xml | 4 +- app/src/main/res/layout/itemlist_class.xml | 33 +++++++++ app/src/main/res/values/dimens.xml | 1 + app/src/main/res/values/strings.xml | 7 ++ 11 files changed, 241 insertions(+), 6 deletions(-) create mode 100644 app/src/main/res/layout/dialog_new_match.xml create mode 100644 app/src/main/res/layout/itemlist_class.xml diff --git a/app/build.gradle b/app/build.gradle index 0fa0c07..5d03c43 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -21,6 +21,7 @@ android { buildConfigField("boolean", "PREPARE_TO_RELEASE", "$prepareToRelease") buildConfigField("String", "MIXPANEL_TOKEN", '"eb99af1dad563cbaaf02f008b28e321f"') buildConfigField("String", "GCM_SENDER", '"597127048287"') + multiDexEnabled true vectorDrawables.useSupportLibrary true } signingConfigs { diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/App.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/App.kt index d00ce8b..d24ffea 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/App.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/App.kt @@ -1,7 +1,7 @@ package com.ediposouza.teslesgendstracker -import android.app.Application import android.content.Context +import android.support.multidex.MultiDexApplication import android.support.v7.app.AppCompatDelegate import com.ediposouza.teslesgendstracker.interactor.BaseInteractor import com.ediposouza.teslesgendstracker.util.ConfigManager @@ -15,7 +15,7 @@ import timber.log.Timber /** * Created by EdipoSouza on 10/30/16. */ -class App : Application() { +class App : MultiDexApplication() { companion object { diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt index 978380a..98f29d3 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt @@ -174,6 +174,51 @@ class PrivateInteractor : BaseInteractor() { keepSynced() } + fun getUserDecks(cls: Class?, onSuccess: (List) -> Unit) { + getUserPublicDecks(cls) { decks -> + getUserPrivateDecks(cls) { + onSuccess.invoke(decks.plus(it)) + } + } + } + + private fun getUserPublicDecks(cls: Class?, onSuccess: (List) -> Unit) { + getUserPublicDecksRef()?.addListenerForSingleValueEvent(object : ValueEventListener { + + override fun onDataChange(ds: DataSnapshot) { + Timber.d(ds.value?.toString()) + val decks = ds.children.mapTo(arrayListOf()) { + it.getValue(FirebaseParsers.DeckParser::class.java).toDeck(it.key, false) + }.filter { cls == null || it.cls == cls } + Timber.d(decks.toString()) + onSuccess.invoke(decks) + } + + override fun onCancelled(de: DatabaseError) { + Timber.d("Fail: " + de.message) + } + + }) + } + + private fun getUserPrivateDecks(cls: Class?, onSuccess: (List) -> Unit) { + getUserPrivateDecksRef()?.addListenerForSingleValueEvent(object : ValueEventListener { + + override fun onDataChange(ds: DataSnapshot) { + val decks = ds.children.mapTo(arrayListOf()) { + it.getValue(FirebaseParsers.DeckParser::class.java).toDeck(it.key, true) + }.filter { cls == null || it.cls == cls } + Timber.d(decks.toString()) + onSuccess.invoke(decks) + } + + override fun onCancelled(de: DatabaseError) { + Timber.d("Fail: " + de.message) + } + + }) + } + fun getUserFavoriteDecks(cls: Class?, onSuccess: (List?) -> Unit) { PublicInteractor().getPublicDecks(cls) { publicDecks -> getUserFavoriteDecksRef()?.apply { diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/new/NewDeckActivity.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/new/NewDeckActivity.kt index abd4619..1d2e4b7 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/new/NewDeckActivity.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/new/NewDeckActivity.kt @@ -149,7 +149,7 @@ class NewDeckActivity : BaseFilterActivity() { } private fun showSaveDialog() { - val view = View.inflate(this@NewDeckActivity, R.layout.dialog_new_deck, null) + val view = View.inflate(this, R.layout.dialog_new_deck, null) val deckTypes = DeckType.values().filter { it != DeckType.ARENA }.map { it.name.toLowerCase().capitalize() } view.new_deck_dialog_type_spinner.adapter = ArrayAdapter(this, android.R.layout.simple_spinner_dropdown_item, deckTypes) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesFragment.kt index c76c776..c6738ae 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesFragment.kt @@ -7,9 +7,14 @@ import android.support.v4.app.FragmentManager import android.support.v4.app.FragmentStatePagerAdapter import android.support.v4.view.ViewPager import android.view.* +import android.widget.AdapterView +import android.widget.ArrayAdapter import com.ediposouza.teslesgendstracker.R +import com.ediposouza.teslesgendstracker.data.Class +import com.ediposouza.teslesgendstracker.data.Deck import com.ediposouza.teslesgendstracker.data.MatchMode import com.ediposouza.teslesgendstracker.data.Season +import com.ediposouza.teslesgendstracker.interactor.PrivateInteractor import com.ediposouza.teslesgendstracker.interactor.PublicInteractor import com.ediposouza.teslesgendstracker.ui.base.BaseFragment import com.ediposouza.teslesgendstracker.ui.base.CmdUpdateTitle @@ -20,9 +25,13 @@ import com.ediposouza.teslesgendstracker.util.MetricScreen import com.ediposouza.teslesgendstracker.util.MetricsManager import com.ediposouza.teslesgendstracker.util.inflate import kotlinx.android.synthetic.main.activity_dash.* +import kotlinx.android.synthetic.main.dialog_new_match.view.* import kotlinx.android.synthetic.main.fragment_matches.* +import kotlinx.android.synthetic.main.itemlist_class.view.* import org.greenrobot.eventbus.Subscribe +import org.jetbrains.anko.alert import org.jetbrains.anko.itemsSequence +import timber.log.Timber /** * Created by EdipoSouza on 1/3/17. @@ -71,6 +80,7 @@ class MatchesFragment : BaseFragment() { } true } + matches_fab_add.setOnClickListener { showNewMatchDialog() } MetricsManager.trackScreen(MetricScreen.SCREEN_MATCHES_STATISTICS()) } @@ -135,6 +145,45 @@ class MatchesFragment : BaseFragment() { } } + private fun showNewMatchDialog() { + val dialogView = View.inflate(context, R.layout.dialog_new_match, null) + var decks = listOf() + dialogView.new_match_dialog_class_spinner.apply { + adapter = ClassAdapter(context) + onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { + PrivateInteractor().getUserDecks(Class.values()[position]) { + decks = it + dialogView.new_match_dialog_deck_spinner.adapter = ArrayAdapter(context, + android.R.layout.simple_spinner_dropdown_item, decks.map(Deck::name)) + } + } + + override fun onNothingSelected(parent: AdapterView<*>?) { + } + + } + } + val modeTypes = MatchMode.values().map { it.name.toLowerCase().capitalize() } + dialogView.new_match_dialog_mode_spinner.adapter = ArrayAdapter(context, + android.R.layout.simple_spinner_dropdown_item, modeTypes) + context.alert { + customView(dialogView) + positiveButton(R.string.new_match_dialog_start) { + val deckPosition = dialogView.new_match_dialog_deck_spinner.selectedItemPosition + val cls = Class.values()[dialogView.new_match_dialog_class_spinner.selectedItemPosition] + val deck = if (deckPosition >= 0) decks[deckPosition] else null + val mode = MatchMode.values()[dialogView.new_match_dialog_mode_spinner.selectedItemPosition] + startNewMatches(cls, deck, mode) + } + cancelButton { } + }.show() + } + + private fun startNewMatches(cls: Class, deck: Deck?, mode: MatchMode) { + Timber.d("$cls $deck $mode") + } + @Subscribe fun onCmdUpdateVisibility(update: CmdUpdateVisibility) { if (update.show) { @@ -167,4 +216,29 @@ class MatchesFragment : BaseFragment() { } + class ClassAdapter(ctx: Context) : ArrayAdapter(ctx, R.layout.itemlist_class, + R.id.class_name, Class.values()) { + + override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup?): View { + return super.getDropDownView(position, convertView, parent).apply { + with(getItem(position)) { + class_attr1.setImageResource(attr1.imageRes) + class_attr2.setImageResource(attr2.imageRes) + class_name.text = name.toLowerCase().capitalize() + } + } + } + + override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { + return super.getView(position, convertView, parent).apply { + with(getItem(position)) { + class_attr1.setImageResource(attr1.imageRes) + class_attr2.setImageResource(attr2.imageRes) + class_name.text = name.toLowerCase().capitalize() + } + } + } + + } + } \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_new_deck.xml b/app/src/main/res/layout/dialog_new_deck.xml index 25b49d4..7a2b2ba 100644 --- a/app/src/main/res/layout/dialog_new_deck.xml +++ b/app/src/main/res/layout/dialog_new_deck.xml @@ -1,7 +1,9 @@ + android:layout_height="wrap_content" + tools:layout_marginTop="@dimen/status_bar_height"> + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/itemcell_class.xml b/app/src/main/res/layout/itemcell_class.xml index 73d7acc..3a1f056 100644 --- a/app/src/main/res/layout/itemcell_class.xml +++ b/app/src/main/res/layout/itemcell_class.xml @@ -12,14 +12,14 @@ android:layout_width="@dimen/size_small" android:layout_height="@dimen/size_small" android:layout_marginStart="@dimen/default_margin" - android:src="@drawable/attr_strength" /> + tools:src="@drawable/attr_strength" /> + tools:src="@drawable/attr_intelligence" /> + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 55a6a24..596ffa9 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -2,6 +2,7 @@ 24dp 0dp + 48dp 56dp 8dp 4dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 87747e5..98ef2e0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -127,4 +127,11 @@ %1$.1f%% All Seasons + I\'m playing with + Name + Class + Deck + Mode + Start + From 040a4f6156f9c468d28de125bb3405d72d73574b Mon Sep 17 00:00:00 2001 From: EdipoSouza Date: Mon, 9 Jan 2017 21:46:07 -0200 Subject: [PATCH 24/54] Fix new matches dialog over navigation bar --- .../ui/matches/MatchesFragment.kt | 82 +++++++++++-------- .../teslesgendstracker/util/AppExtensions.kt | 17 +++- app/src/main/res/layout/itemlist_class.xml | 30 ++++--- app/src/main/res/values/dimens.xml | 3 + 4 files changed, 84 insertions(+), 48 deletions(-) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesFragment.kt index c6738ae..8a2272d 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesFragment.kt @@ -6,30 +6,28 @@ import android.support.v4.app.Fragment import android.support.v4.app.FragmentManager import android.support.v4.app.FragmentStatePagerAdapter import android.support.v4.view.ViewPager +import android.support.v7.app.AlertDialog import android.view.* import android.widget.AdapterView import android.widget.ArrayAdapter import com.ediposouza.teslesgendstracker.R -import com.ediposouza.teslesgendstracker.data.Class -import com.ediposouza.teslesgendstracker.data.Deck -import com.ediposouza.teslesgendstracker.data.MatchMode -import com.ediposouza.teslesgendstracker.data.Season +import com.ediposouza.teslesgendstracker.data.* import com.ediposouza.teslesgendstracker.interactor.PrivateInteractor import com.ediposouza.teslesgendstracker.interactor.PublicInteractor import com.ediposouza.teslesgendstracker.ui.base.BaseFragment import com.ediposouza.teslesgendstracker.ui.base.CmdUpdateTitle import com.ediposouza.teslesgendstracker.ui.base.CmdUpdateVisibility -import com.ediposouza.teslesgendstracker.ui.matches.tabs.MatchesHistory -import com.ediposouza.teslesgendstracker.ui.matches.tabs.MatchesStatistics +import com.ediposouza.teslesgendstracker.ui.matches.tabs.MatchesHistoryFragment +import com.ediposouza.teslesgendstracker.ui.matches.tabs.MatchesStatisticsFragment import com.ediposouza.teslesgendstracker.util.MetricScreen import com.ediposouza.teslesgendstracker.util.MetricsManager import com.ediposouza.teslesgendstracker.util.inflate +import com.ediposouza.teslesgendstracker.util.limitHeight import kotlinx.android.synthetic.main.activity_dash.* import kotlinx.android.synthetic.main.dialog_new_match.view.* import kotlinx.android.synthetic.main.fragment_matches.* import kotlinx.android.synthetic.main.itemlist_class.view.* import org.greenrobot.eventbus.Subscribe -import org.jetbrains.anko.alert import org.jetbrains.anko.itemsSequence import timber.log.Timber @@ -147,37 +145,47 @@ class MatchesFragment : BaseFragment() { private fun showNewMatchDialog() { val dialogView = View.inflate(context, R.layout.dialog_new_match, null) - var decks = listOf() - dialogView.new_match_dialog_class_spinner.apply { - adapter = ClassAdapter(context) - onItemSelectedListener = object : AdapterView.OnItemSelectedListener { - override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { - PrivateInteractor().getUserDecks(Class.values()[position]) { - decks = it - dialogView.new_match_dialog_deck_spinner.adapter = ArrayAdapter(context, - android.R.layout.simple_spinner_dropdown_item, decks.map(Deck::name)) - } - } - - override fun onNothingSelected(parent: AdapterView<*>?) { + val classClickListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected(parent: AdapterView<*>?, view: View?, pos: Int, id: Long) { + PrivateInteractor().getUserDecks(Class.values()[pos]) { decks -> + dialogView.new_match_dialog_deck_spinner.adapter = ArrayAdapter(context, + android.R.layout.simple_spinner_dropdown_item, decks.map(Deck::name)) } + } + override fun onNothingSelected(parent: AdapterView<*>?) { } } - val modeTypes = MatchMode.values().map { it.name.toLowerCase().capitalize() } - dialogView.new_match_dialog_mode_spinner.adapter = ArrayAdapter(context, - android.R.layout.simple_spinner_dropdown_item, modeTypes) - context.alert { - customView(dialogView) - positiveButton(R.string.new_match_dialog_start) { - val deckPosition = dialogView.new_match_dialog_deck_spinner.selectedItemPosition - val cls = Class.values()[dialogView.new_match_dialog_class_spinner.selectedItemPosition] - val deck = if (deckPosition >= 0) decks[deckPosition] else null - val mode = MatchMode.values()[dialogView.new_match_dialog_mode_spinner.selectedItemPosition] - startNewMatches(cls, deck, mode) - } - cancelButton { } - }.show() + val decks = listOf() + AlertDialog.Builder(context) + .setView(dialogView) + .setPositiveButton(R.string.new_match_dialog_start, { dialog, which -> + val deckPosition = dialogView.new_match_dialog_deck_spinner.selectedItemPosition + val cls = Class.values()[dialogView.new_match_dialog_class_spinner.selectedItemPosition] + val deck = if (deckPosition >= 0) decks[deckPosition] else null + val mode = MatchMode.values()[dialogView.new_match_dialog_mode_spinner.selectedItemPosition] + startNewMatches(cls, deck, mode) + }) + .setNegativeButton(android.R.string.cancel, { dialog, which -> }) + .create() + .apply { + setOnShowListener { + dialogView.new_match_dialog_class_spinner.apply { + adapter = ClassAdapter(context) + onItemSelectedListener = classClickListener + limitHeight() + } + dialogView.new_match_dialog_deck_spinner.apply { + if (decks.size >= 5) { + limitHeight() + } + } + val modeTypes = MatchMode.values().map { it.name.toLowerCase().capitalize() } + dialogView.new_match_dialog_mode_spinner.adapter = ArrayAdapter(context, + android.R.layout.simple_spinner_dropdown_item, modeTypes) + } + show() + } } private fun startNewMatches(cls: Class, deck: Deck?, mode: MatchMode) { @@ -196,8 +204,8 @@ class MatchesFragment : BaseFragment() { class MatchesPageAdapter(ctx: Context, fm: FragmentManager) : FragmentStatePagerAdapter(fm) { var titles: Array = ctx.resources.getStringArray(R.array.matches_tabs) - val matchesStatisticsFragment by lazy { MatchesStatistics() } - val matchesHistoryFragment by lazy { MatchesHistory() } + val matchesStatisticsFragment by lazy { MatchesStatisticsFragment() } + val matchesHistoryFragment by lazy { MatchesHistoryFragment() } override fun getItem(position: Int): Fragment { return when (position) { @@ -224,6 +232,7 @@ class MatchesFragment : BaseFragment() { with(getItem(position)) { class_attr1.setImageResource(attr1.imageRes) class_attr2.setImageResource(attr2.imageRes) + class_attr2.visibility = if (attr2 != Attribute.NEUTRAL) View.VISIBLE else View.GONE class_name.text = name.toLowerCase().capitalize() } } @@ -234,6 +243,7 @@ class MatchesFragment : BaseFragment() { with(getItem(position)) { class_attr1.setImageResource(attr1.imageRes) class_attr2.setImageResource(attr2.imageRes) + class_attr2.visibility = if (attr2 != Attribute.NEUTRAL) View.VISIBLE else View.GONE class_name.text = name.toLowerCase().capitalize() } } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/util/AppExtensions.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/util/AppExtensions.kt index 7b6b29d..2262171 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/util/AppExtensions.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/util/AppExtensions.kt @@ -7,6 +7,8 @@ import android.support.design.widget.BottomSheetBehavior import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.ListPopupWindow +import android.widget.Spinner import com.ediposouza.teslesgendstracker.R import com.google.android.gms.ads.AdRequest import com.google.android.gms.ads.AdView @@ -54,4 +56,17 @@ private fun createAdRequest(context: Context): AdRequest { fun MixpanelAPI.trackBundle(eventName: String, bundle: Bundle) { trackMap(eventName, bundle.keySet().map { it to bundle[it] }.toMap()) -} \ No newline at end of file +} + + +fun Spinner.limitHeight() { + val displayHeight = context.resources.displayMetrics.heightPixels + Spinner::class.java.getDeclaredField("mPopup") + ?.apply { isAccessible = true }?.get(this) + ?.apply popup@ { + IntArray(2).apply { + getLocationOnScreen(this) + (this@popup as ListPopupWindow).height = displayHeight - get(1) + } + } +} diff --git a/app/src/main/res/layout/itemlist_class.xml b/app/src/main/res/layout/itemlist_class.xml index 04f3a45..9d8bde2 100644 --- a/app/src/main/res/layout/itemlist_class.xml +++ b/app/src/main/res/layout/itemlist_class.xml @@ -6,19 +6,27 @@ android:orientation="horizontal" tools:layout_marginTop="@dimen/status_bar_height"> - + android:gravity="center" + android:orientation="horizontal"> - + + + + + 48dp 72dp 88dp + 100dp + + 56dp From 84f29ce4f179d5107f747930cb32d20a261772c6 Mon Sep 17 00:00:00 2001 From: EdipoSouza Date: Mon, 9 Jan 2017 23:48:20 -0200 Subject: [PATCH 25/54] Matchs by class decks activity --- .../teslesgendstracker/util/MetricsManager.kt | 2 +- app/src/main/AndroidManifest.xml | 3 + .../teslesgendstracker/data/General.kt | 53 ++- .../teslesgendstracker/ui/CardActivity.kt | 3 +- .../teslesgendstracker/ui/DashActivity.kt | 1 + .../ui/base/BaseActivity.kt | 1 + .../ui/cards/tabs/CardsAllFragment.kt | 1 + .../ui/decks/tabs/DecksPublicFragment.kt | 2 + .../matches/MatchesStatisticsClassActivity.kt | 312 ++++++++++++++++++ ...esHistory.kt => MatchesHistoryFragment.kt} | 2 +- ...istics.kt => MatchesStatisticsFragment.kt} | 70 +++- .../activity_matches_statistics_class.xml | 99 ++++++ app/src/main/res/layout/itemcell_text.xml | 2 + app/src/main/res/values/dimens.xml | 2 +- app/src/main/res/values/strings.xml | 1 + 15 files changed, 533 insertions(+), 21 deletions(-) create mode 100644 app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesStatisticsClassActivity.kt rename app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/{MatchesHistory.kt => MatchesHistoryFragment.kt} (99%) rename app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/{MatchesStatistics.kt => MatchesStatisticsFragment.kt} (73%) create mode 100644 app/src/main/res/layout/activity_matches_statistics_class.xml diff --git a/app/src/debug/java/com/ediposouza/teslesgendstracker/util/MetricsManager.kt b/app/src/debug/java/com/ediposouza/teslesgendstracker/util/MetricsManager.kt index 1f0e2c6..eb39381 100644 --- a/app/src/debug/java/com/ediposouza/teslesgendstracker/util/MetricsManager.kt +++ b/app/src/debug/java/com/ediposouza/teslesgendstracker/util/MetricsManager.kt @@ -8,7 +8,7 @@ import timber.log.Timber /** * Created by ediposouza on 08/12/16. */ -@SuppressWarnings("unused") +@Suppress("UNUSED_PARAMETER") object MetricsManager : MetricsConstants() { fun initialize(context: Context) { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5adb704..0abfcd6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -42,6 +42,9 @@ android:name=".ui.decks.new.NewDeckActivity" android:screenOrientation="portrait" android:windowSoftInputMode="adjustPan" /> + = object : Parcelable.Creator { + override fun createFromParcel(source: Parcel): MatchDeck = MatchDeck(source) + override fun newArray(size: Int): Array = arrayOfNulls(size) + } + } + + constructor(source: Parcel) : this(source.readString(), Class.values()[source.readInt()], + DeckType.values()[source.readInt()], source.readString(), source.readString()) + + override fun describeContents() = 0 + + override fun writeToParcel(dest: Parcel?, flags: Int) { + dest?.writeString(name) + dest?.writeInt(cls.ordinal) + dest?.writeInt(type.ordinal) + dest?.writeString(deck) + dest?.writeString(version) + } +} data class Match( @@ -86,4 +107,32 @@ data class Match( val legend: Boolean, val win: Boolean -) \ No newline at end of file +) : Parcelable { + + companion object { + @JvmField val CREATOR: Parcelable.Creator = object : Parcelable.Creator { + override fun createFromParcel(source: Parcel): Match = Match(source) + override fun newArray(size: Int): Array = arrayOfNulls(size) + } + } + + constructor(source: Parcel) : this(source.readString(), 1 == source.readInt(), + source.readParcelable(MatchDeck::class.java.classLoader), + source.readParcelable(MatchDeck::class.java.classLoader), + MatchMode.values()[source.readInt()], source.readString(), source.readInt(), + 1 == source.readInt(), 1 == source.readInt()) + + override fun describeContents() = 0 + + override fun writeToParcel(dest: Parcel?, flags: Int) { + dest?.writeString(uuid) + dest?.writeInt((if (first) 1 else 0)) + dest?.writeParcelable(player, 0) + dest?.writeParcelable(opponent, 0) + dest?.writeInt(mode.ordinal) + dest?.writeString(season) + dest?.writeInt(rank) + dest?.writeInt((if (legend) 1 else 0)) + dest?.writeInt((if (win) 1 else 0)) + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/CardActivity.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/CardActivity.kt index 1cd925c..467776e 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/CardActivity.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/CardActivity.kt @@ -6,6 +6,7 @@ import android.content.Intent import android.os.Bundle import android.support.design.widget.BottomSheetBehavior import android.support.v4.app.ActivityCompat +import android.support.v7.widget.CardView import android.view.View import com.ediposouza.teslesgendstracker.App import com.ediposouza.teslesgendstracker.R @@ -31,7 +32,7 @@ class CardActivity : BaseActivity() { } val card: Card by lazy { intent.getParcelableExtra(EXTRA_CARD) } - val cardInfoSheetBehavior by lazy { BottomSheetBehavior.from(card_bottom_sheet) } + val cardInfoSheetBehavior: BottomSheetBehavior by lazy { BottomSheetBehavior.from(card_bottom_sheet) } var favorite: Boolean = false override fun onCreate(savedInstanceState: Bundle?) { diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DashActivity.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DashActivity.kt index 3afd266..048c646 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DashActivity.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DashActivity.kt @@ -207,6 +207,7 @@ class DashActivity : BaseFilterActivity(), } @Subscribe + @Suppress("UNUSED_PARAMETER") fun onLoginSuccess(cmdLoginSuccess: CmdLoginSuccess) { updateUserMenuInfo() updateCollectionStatistics() diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseActivity.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseActivity.kt index 9eef97a..a6000e0 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseActivity.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/base/BaseActivity.kt @@ -223,6 +223,7 @@ open class BaseActivity : AppCompatActivity(), GoogleApiClient.OnConnectionFaile } @Subscribe + @Suppress("UNUSED_PARAMETER") fun onCmdShowLogin(showLogin: CmdShowLogin) { googleApiClient?.clearDefaultAccountAndReconnect() val signInIntent = Auth.GoogleSignInApi.getSignInIntent(googleApiClient) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/tabs/CardsAllFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/tabs/CardsAllFragment.kt index 7a166bd..51918d3 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/tabs/CardsAllFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/tabs/CardsAllFragment.kt @@ -131,6 +131,7 @@ open class CardsAllFragment : BaseFragment() { } @Subscribe + @Suppress("UNUSED_PARAMETER") fun onCmdLoginSuccess(cmdLoginSuccess: CmdLoginSuccess) { configLoggedViews() loadCardsByAttr(currentAttr) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt index 8c6d7ad..3472f8f 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt @@ -123,12 +123,14 @@ open class DecksPublicFragment : BaseFragment() { } @Subscribe + @Suppress("UNUSED_PARAMETER") fun onCmdLoginSuccess(cmdLoginSuccess: CmdLoginSuccess) { configLoggedViews() showDecks() } @Subscribe + @Suppress("UNUSED_PARAMETER") fun onCmdUpdateDeckAndShowDeck(cmdUpdateDeckAndShowDeck: CmdUpdateDeckAndShowDeck) { showDecks() } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesStatisticsClassActivity.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesStatisticsClassActivity.kt new file mode 100644 index 0000000..bfd64a4 --- /dev/null +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesStatisticsClassActivity.kt @@ -0,0 +1,312 @@ +package com.ediposouza.teslesgendstracker.ui.matches + +import android.content.Context +import android.content.Intent +import android.os.Build +import android.os.Bundle +import android.support.design.widget.CollapsingToolbarLayout +import android.support.v4.app.ActivityCompat +import android.view.* +import android.widget.FrameLayout +import android.widget.RelativeLayout +import android.widget.Switch +import com.ediposouza.teslesgendstracker.R +import com.ediposouza.teslesgendstracker.data.* +import com.ediposouza.teslesgendstracker.interactor.PrivateInteractor +import com.ediposouza.teslesgendstracker.interactor.PublicInteractor +import com.ediposouza.teslesgendstracker.ui.base.BaseActivity +import kotlinx.android.synthetic.main.activity_matches_statistics_class.* +import kotlinx.android.synthetic.main.itemcell_class.view.* +import kotlinx.android.synthetic.main.itemcell_text.view.* +import miguelbcr.ui.tableFixHeadesWrapper.TableFixHeaderAdapter +import org.jetbrains.anko.doAsync +import org.jetbrains.anko.intentFor +import org.jetbrains.anko.itemsSequence +import org.jetbrains.anko.uiThread +import java.util.* + +/** + * Created by EdipoSouza on 1/3/17. + */ +class MatchesStatisticsClassActivity : BaseActivity() { + + companion object { + + private val EXTRA_CLASS = "classExtra" + private val EXTRA_SEASON_ID = "SeasonIdExtra" + private val EXTRA_MATCH_MODE = "MatchModeExtra" + + fun newIntent(context: Context, mode: MatchMode, season: Season?, cls: Class): Intent { + return context.intentFor( + EXTRA_MATCH_MODE to mode.ordinal, + EXTRA_SEASON_ID to (season?.id ?: 0), + EXTRA_CLASS to cls.ordinal) + } + + } + + private val HEADER_FIRST by lazy { getString(R.string.match_vs) } + + private var seasons = listOf() + private var matchMode = MatchMode.RANKED + private var currentSeason: Season? = null + private var menuSeasons: SubMenu? = null + private var showPercent: Switch? = null + + var statisticsClassTableAdapter: StatisticsTableAdapter? = null + var results: HashMap> = HashMap() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_matches_statistics_class) + val statusBarHeight = resources.getDimensionPixelSize(R.dimen.status_bar_height) + if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) { + val coverLP = statistics_class_cover.layoutParams as RelativeLayout.LayoutParams + coverLP.height = coverLP.height - statusBarHeight + statistics_class_cover.layoutParams = coverLP + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + val layoutParams = toolbar.layoutParams as CollapsingToolbarLayout.LayoutParams + layoutParams.topMargin = statusBarHeight + toolbar.layoutParams = layoutParams + } + } + + @Suppress("unchecked_cast") + override fun onPostCreate(savedInstanceState: Bundle?) { + super.onPostCreate(savedInstanceState) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + statisticsClassTableAdapter = StatisticsTableAdapter(this).apply { + setFirstHeader(HEADER_FIRST) + val classTotal: Class? = null + header = Class.values().asList().plus(classTotal) + setFirstBody(Class.values().map { listOf(BodyItem(cls = it)) }.plus(listOf(listOf(BodyItem())))) + loadingStatisticsData(this) + setSection(listOf()) + } + matches_statistics_class_table.adapter = statisticsClassTableAdapter + matchMode = MatchMode.values()[intent.getIntExtra(EXTRA_MATCH_MODE, 0)] + with(Class.values()[intent.getIntExtra(EXTRA_CLASS, 0)]) { + statistics_class_name.text = getString(R.string.matches_class_title, name.toLowerCase().capitalize()) + statistics_class_cover.setImageResource(imageRes) + statistics_class_attr1.setImageResource(attr1.imageRes) + statistics_class_attr2.setImageResource(attr2.imageRes) + } + } + + override fun onCreateOptionsMenu(menu: Menu?): Boolean { + menuInflater.inflate(R.menu.menu_percent, menu) + menuInflater?.inflate(R.menu.menu_season, menu) + getSeasons(menu?.findItem(R.id.menu_season)) + val menuPercent = menu?.findItem(R.id.menu_percent) + showPercent = menuPercent?.actionView as Switch + showPercent?.setOnCheckedChangeListener { button, checked -> updateStatisticsData() } + return super.onCreateOptionsMenu(menu) + } + + override fun onOptionsItemSelected(item: MenuItem?): Boolean { + when (item?.itemId) { + android.R.id.home -> { + ActivityCompat.finishAfterTransition(this) + return true + } + R.id.menu_season_all -> filterSeason(null) + else -> seasons.find { it.id == item?.itemId }?.apply { filterSeason(this) } + } + return super.onOptionsItemSelected(item) + } + + private fun filterSeason(season: Season?) { + currentSeason = season + val seasonId = season?.id ?: R.id.menu_season_all + menuSeasons?.itemsSequence()?.forEach { + it.setIcon(if (it.itemId == seasonId) R.drawable.ic_checked else 0) + } + getMatches() + } + + private fun getSeasons(menuSeason: MenuItem?) { + menuSeasons = menuSeason?.subMenu + menuSeasons?.apply { + clear() + add(0, R.id.menu_season_all, 0, getString(R.string.matches_seasons_all)).setIcon(R.drawable.ic_checked) + PublicInteractor().getSeasons { + seasons = it.reversed() + seasons.forEach { + add(0, it.id, 0, it.desc) + } + currentSeason = seasons.find { it.id == intent.getIntExtra(EXTRA_SEASON_ID, 0) } + filterSeason(currentSeason) + } + } + } + + private fun getMatches() { + loadingStatisticsData() + PrivateInteractor().getUserMatches(currentSeason) { + results.clear() + it.filter { it.mode == matchMode }.groupBy { it.player }.forEach { + results.put(it.key, it.value as ArrayList) + } + updateStatisticsData() + } + } + + private fun loadingStatisticsData(tableAdapter: StatisticsTableAdapter? = statisticsClassTableAdapter) { + tableAdapter?.setFirstBody(Class.values().map { listOf(BodyItem(cls = it)) }.plus(listOf(listOf(BodyItem())))) + tableAdapter?.body = mutableListOf>().apply { + Class.values().forEach { myCls -> + add(mutableListOf().apply { + Class.values().forEach { opponentCls -> + add(BodyItem()) + } + add(BodyItem()) + }) + } + add(mutableListOf().apply { + Class.values().forEach { + add(BodyItem()) + } + add(BodyItem()) + }) + } + } + + private fun updateStatisticsData() { + doAsync { + val data = mutableListOf>().apply { + results.forEach { myDeck, matches -> + add(mutableListOf().apply { + Class.values().forEach { opponentCls -> + val matchesVsOpponent = matches.filter { it.opponent.cls == opponentCls } + add(getResultBodyItem(matchesVsOpponent)) + } + add(getResultBodyItem(matches)) + }) + } + val allMatches = results.flatMap { it.value } + add(mutableListOf().apply { + Class.values().forEach { + val resByOpponent = allMatches.groupBy { it.opponent.cls }[it] ?: listOf() + add(getResultBodyItem(resByOpponent)) + } + add(getResultBodyItem(allMatches)) + }) + } + uiThread { + val decks = results.map { listOf(BodyItem(it.key.name)) } + val total = listOf(listOf(BodyItem(getString(R.string.match_statistics_total_label)))) + statisticsClassTableAdapter?.setFirstBody(decks.plus(total)) + statisticsClassTableAdapter?.body = data + } + } + } + + private fun getResultBodyItem(matches: List): BodyItem { + val result = matches.groupBy { it.win } + val wins = result[true]?.size ?: 0 + val losses = result[false]?.size ?: 0 + return BodyItem(if (!(showPercent?.isChecked ?: false)) "$wins/$losses" else + getString(R.string.match_statistics_percent, calcWinRate(wins.toFloat(), losses.toFloat()))) + } + + private fun calcWinRate(wins: Float, losses: Float): Float { + val total = (wins + losses) + return if (total == 0f) -1f else 100 / total * wins + } + + class BodyItem(val result: String? = null, val cls: Class? = null) + + class StatisticsTableAdapter(val context: Context) : TableFixHeaderAdapter, CellTextCenter, CellTextCenter, CellTextCenter>(context) { + + override fun inflateFirstHeader() = CellTextCenter(context) + + override fun inflateHeader() = CellClass(context) + + override fun inflateFirstBody() = CellTextCenter(context) + + override fun inflateBody() = CellTextCenter(context) + + override fun inflateSection() = CellTextCenter(context) + + override fun getHeaderHeight() = context.resources.getDimensionPixelSize(R.dimen.match_statistics_cell_height) + + override fun getHeaderWidths(): List { + val headerWidth = context.resources.getDimensionPixelSize(R.dimen.match_statistics_class_header_width) + val cellWidth = context.resources.getDimensionPixelSize(R.dimen.match_statistics_cell_width) + val colWidths = mutableListOf(headerWidth) + Class.values().forEach { colWidths.add(cellWidth) } + colWidths.add(headerWidth) + return colWidths + } + + override fun getBodyHeight() = context.resources.getDimensionPixelSize(R.dimen.match_statistics_cell_height) + + override fun isSection(items: List>?, row: Int): Boolean = false + + override fun getSectionHeight() = 0 + + } + + class CellClass(context: Context) : FrameLayout(context), + TableFixHeaderAdapter.HeaderBinder { + + init { + LayoutInflater.from(context).inflate(R.layout.itemcell_class, this, true) + } + + override fun bindHeader(cls: Class?, col: Int) { + bindClass(cls) + } + + private fun bindClass(cls: Class?) { + with(rootView) { + val attr1Visibility = if (cls != null) View.VISIBLE else View.GONE + val attr2Visibility = if (cls?.attr2 != Attribute.NEUTRAL) attr1Visibility else View.GONE + cell_class_attr1.visibility = attr1Visibility + cell_class_attr2.visibility = attr2Visibility + cell_class_attr1.setImageResource(cls?.attr1?.imageRes ?: 0) + cell_class_attr2.setImageResource(cls?.attr2?.imageRes ?: 0) + cell_total.visibility = if (cls == null) View.VISIBLE else View.GONE + } + } + + } + + class CellTextCenter(context: Context) : FrameLayout(context), + TableFixHeaderAdapter.FirstHeaderBinder, + TableFixHeaderAdapter.FirstBodyBinder>, + TableFixHeaderAdapter.BodyBinder>, + TableFixHeaderAdapter.SectionBinder> { + + init { + LayoutInflater.from(context).inflate(R.layout.itemcell_text, this, true) + } + + override fun bindFirstHeader(result: String) { + bindResult(result) + } + + override fun bindFirstBody(bodyItems: List, row: Int) { + bindResult(bodyItems[0].result) + } + + override fun bindBody(bodyItems: List, row: Int, col: Int) { + bindResult(bodyItems[col].result) + } + + private fun bindResult(result: String?) { + with(rootView) { + cell_text.text = if (result == "0/0" || result == "-1.0%") "-" else result + cell_text.visibility = if (result == null) View.GONE else View.VISIBLE + cell_progress.visibility = if (result == null) View.VISIBLE else View.GONE + } + } + + override fun bindSection(bodyItems: List, row: Int, col: Int) { + } + + } + +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesHistory.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesHistoryFragment.kt similarity index 99% rename from app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesHistory.kt rename to app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesHistoryFragment.kt index f58fda2..4a65bc2 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesHistory.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesHistoryFragment.kt @@ -25,7 +25,7 @@ import org.greenrobot.eventbus.Subscribe /** * Created by EdipoSouza on 1/3/17. */ -class MatchesHistory : BaseFragment() { +class MatchesHistoryFragment : BaseFragment() { val ADS_EACH_ITEMS = 20 //after 10 lines val MATCH_PAGE_SIZE = 15 diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatisticsFragment.kt similarity index 73% rename from app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt rename to app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatisticsFragment.kt index e639f0e..913eccd 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatistics.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatisticsFragment.kt @@ -2,6 +2,9 @@ package com.ediposouza.teslesgendstracker.ui.matches.tabs import android.content.Context import android.os.Bundle +import android.support.v4.app.ActivityOptionsCompat +import android.support.v4.content.ContextCompat +import android.support.v4.util.Pair import android.view.* import android.widget.FrameLayout import android.widget.Switch @@ -11,12 +14,14 @@ import com.ediposouza.teslesgendstracker.interactor.PrivateInteractor import com.ediposouza.teslesgendstracker.ui.base.BaseFragment import com.ediposouza.teslesgendstracker.ui.matches.CmdFilterMode import com.ediposouza.teslesgendstracker.ui.matches.CmdFilterSeason +import com.ediposouza.teslesgendstracker.ui.matches.MatchesStatisticsClassActivity import com.ediposouza.teslesgendstracker.util.inflate import kotlinx.android.synthetic.main.fragment_matches_statistics.* import kotlinx.android.synthetic.main.itemcell_class.view.* import kotlinx.android.synthetic.main.itemcell_text.view.* import miguelbcr.ui.tableFixHeadesWrapper.TableFixHeaderAdapter import org.greenrobot.eventbus.Subscribe +import org.jetbrains.anko.childrenSequence import org.jetbrains.anko.doAsync import org.jetbrains.anko.uiThread import java.util.* @@ -24,12 +29,15 @@ import java.util.* /** * Created by EdipoSouza on 1/3/17. */ -class MatchesStatistics : BaseFragment() { +class MatchesStatisticsFragment : BaseFragment() { private val HEADER_FIRST by lazy { getString(R.string.match_vs) } + private val attr1TransitionName: String by lazy { getString(R.string.deck_attr1_transition_name) } + private val attr2TransitionName: String by lazy { getString(R.string.deck_attr2_transition_name) } private var currentMatchMode = MatchMode.RANKED private var currentSeason: Season? = null + private var selectedClass: Class? = null private var showPercent: Switch? = null var statisticsTableAdapter: StatisticsTableAdapter? = null @@ -49,6 +57,8 @@ class MatchesStatistics : BaseFragment() { setFirstBody(Class.values().map { listOf(BodyItem(cls = it)) }.plus(listOf(listOf(BodyItem())))) loadingStatisticsData(this) setSection(listOf()) + setClickListenerFirstBody { rowItems, view, row, col -> selectRow(row) } + setClickListenerBody { rowItems, view, row, col -> selectRow(row) } } matches_statistics_table.adapter = statisticsTableAdapter getMatches() @@ -62,6 +72,29 @@ class MatchesStatistics : BaseFragment() { super.onCreateOptionsMenu(menu, inflater) } + override fun onResume() { + super.onResume() + selectRow(-1) + } + + private fun selectRow(row: Int) { + selectedClass = if (row >= 0 && row < Class.values().size) Class.values()[row] else null + updateStatisticsData() + statisticsTableAdapter?.setFirstBody(Class.values().map { listOf(BodyItem(null, it, it == selectedClass)) } + .plus(listOf(listOf(BodyItem())))) + if (selectedClass != null) { + val classView = matches_statistics_table.childrenSequence() + .filter { + it.getTag(com.inqbarna.tablefixheaders.R.id.tag_row) == row && + it.getTag(com.inqbarna.tablefixheaders.R.id.tag_type_view) == 2 + }.first() + startActivity(MatchesStatisticsClassActivity.newIntent(context, currentMatchMode, currentSeason, + selectedClass!!), ActivityOptionsCompat.makeSceneTransitionAnimation(activity, + Pair(classView.cell_class_attr1 as View, attr1TransitionName), + Pair(classView.cell_class_attr2 as View, attr2TransitionName)).toBundle()) + } + } + private fun getMatches() { loadingStatisticsData() PrivateInteractor().getUserMatches(currentSeason) { @@ -100,18 +133,18 @@ class MatchesStatistics : BaseFragment() { val resByMyCls = results[myCls]!! Class.values().forEach { opponentCls -> val matchesVsOpponent = resByMyCls.filter { it.opponent.cls == opponentCls } - add(getResultBodyItem(matchesVsOpponent)) + add(getResultBodyItem(matchesVsOpponent, myCls == selectedClass)) } - add(getResultBodyItem(resByMyCls)) + add(getResultBodyItem(resByMyCls, myCls == selectedClass)) }) } val allMatches = results.flatMap { it.value } add(mutableListOf().apply { Class.values().forEach { val resByOpponent = allMatches.groupBy { it.opponent.cls }[it] ?: listOf() - add(getResultBodyItem(resByOpponent)) + add(getResultBodyItem(resByOpponent, false)) } - add(getResultBodyItem(allMatches)) + add(getResultBodyItem(allMatches, false)) }) } uiThread { @@ -120,12 +153,13 @@ class MatchesStatistics : BaseFragment() { } } - private fun getResultBodyItem(matches: List): BodyItem { + private fun getResultBodyItem(matches: List, cellSelected: Boolean): BodyItem { val result = matches.groupBy { it.win } val wins = result[true]?.size ?: 0 val losses = result[false]?.size ?: 0 - return BodyItem(if (!(showPercent?.isChecked ?: false)) "$wins/$losses" else - getString(R.string.match_statistics_percent, calcWinRate(wins.toFloat(), losses.toFloat()))) + val resultText = if (!(showPercent?.isChecked ?: false)) "$wins/$losses" else + getString(R.string.match_statistics_percent, calcWinRate(wins.toFloat(), losses.toFloat())) + return BodyItem(resultText, selected = cellSelected) } private fun calcWinRate(wins: Float, losses: Float): Float { @@ -145,7 +179,7 @@ class MatchesStatistics : BaseFragment() { getMatches() } - class BodyItem(val result: String? = null, val cls: Class? = null) + class BodyItem(val result: String? = null, val cls: Class? = null, val selected: Boolean = false) class StatisticsTableAdapter(val context: Context) : TableFixHeaderAdapter, CellClass, CellTextCenter, CellTextCenter>(context) { @@ -188,14 +222,15 @@ class MatchesStatistics : BaseFragment() { } override fun bindHeader(cls: Class?, col: Int) { - bindClass(cls) + bindClass(cls, false) } override fun bindFirstBody(bodyItems: List, row: Int) { - bindClass(bodyItems[0].cls) + val bodyItem = bodyItems[0] + bindClass(bodyItem.cls, bodyItem.selected) } - private fun bindClass(cls: Class?) { + private fun bindClass(cls: Class?, selected: Boolean) { with(rootView) { val attr1Visibility = if (cls != null) View.VISIBLE else View.GONE val attr2Visibility = if (cls?.attr2 != Attribute.NEUTRAL) attr1Visibility else View.GONE @@ -204,6 +239,8 @@ class MatchesStatistics : BaseFragment() { cell_class_attr1.setImageResource(cls?.attr1?.imageRes ?: 0) cell_class_attr2.setImageResource(cls?.attr2?.imageRes ?: 0) cell_total.visibility = if (cls == null) View.VISIBLE else View.GONE + val cellColor = if (selected) R.color.colorAccent else android.R.color.transparent + setBackgroundColor(ContextCompat.getColor(context, cellColor)) } } @@ -219,18 +256,21 @@ class MatchesStatistics : BaseFragment() { } override fun bindFirstHeader(result: String) { - bindResult(result) + bindResult(result, false) } override fun bindBody(bodyItems: List, row: Int, col: Int) { - bindResult(bodyItems[col].result) + val bodyItem = bodyItems[col] + bindResult(bodyItem.result, bodyItem.selected) } - private fun bindResult(result: String?) { + private fun bindResult(result: String?, selected: Boolean) { with(rootView) { cell_text.text = if (result == "0/0" || result == "-1.0%") "-" else result cell_text.visibility = if (result == null) View.GONE else View.VISIBLE cell_progress.visibility = if (result == null) View.VISIBLE else View.GONE + val cellColor = if (selected) R.color.colorAccent else android.R.color.transparent + setBackgroundColor(ContextCompat.getColor(context, cellColor)) } } diff --git a/app/src/main/res/layout/activity_matches_statistics_class.xml b/app/src/main/res/layout/activity_matches_statistics_class.xml new file mode 100644 index 0000000..b987b81 --- /dev/null +++ b/app/src/main/res/layout/activity_matches_statistics_class.xml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/itemcell_text.xml b/app/src/main/res/layout/itemcell_text.xml index b71f825..15cbf3a 100644 --- a/app/src/main/res/layout/itemcell_text.xml +++ b/app/src/main/res/layout/itemcell_text.xml @@ -9,6 +9,8 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" + android:gravity="center" + android:maxLines="2" android:orientation="horizontal" android:textColor="@android:color/white" android:visibility="gone" diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 4dcef89..ed27a04 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -78,7 +78,7 @@ 48dp 72dp 88dp - 100dp + 120dp 56dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 98ef2e0..18d3a62 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -126,6 +126,7 @@ Total %1$.1f%% All Seasons + %1$s Statistics I\'m playing with Name From 4af0d2e484e9fc54a31c0158ef94cf0fd6be17f0 Mon Sep 17 00:00:00 2001 From: EdipoSouza Date: Tue, 10 Jan 2017 21:26:54 -0200 Subject: [PATCH 26/54] Matches history section with date --- app/build.gradle | 1 + .../ui/matches/MatchesFragment.kt | 3 + .../ui/matches/tabs/MatchesHistoryFragment.kt | 39 +++++++++- .../teslesgendstracker/util/TestUtils.kt | 77 +++++++++++++++---- app/src/main/res/layout/itemcell_text.xml | 1 - .../layout/itemlist_match_history_section.xml | 13 ++++ 6 files changed, 118 insertions(+), 16 deletions(-) create mode 100644 app/src/main/res/layout/itemlist_match_history_section.xml diff --git a/app/build.gradle b/app/build.gradle index 5d03c43..1faec57 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -119,6 +119,7 @@ dependencies { compile "com.github.emanzanoaxa:RippleEffect:52ea2a0ab6" // compile "com.github.traex.rippleeffect:library:1.3" compile "com.github.miguelbcr:TableFixHeaders-Wrapper:0.2.0" + compile "com.timehop.stickyheadersrecyclerview:library:0.4.3" compile "jp.wasabeef:recyclerview-animators:2.2.4" testCompile "junit:junit:4.12" diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesFragment.kt index 8a2272d..ba225f6 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesFragment.kt @@ -44,6 +44,9 @@ class MatchesFragment : BaseFragment() { val pageChange = object : ViewPager.SimpleOnPageChangeListener() { override fun onPageSelected(position: Int) { updateActivityTitle(position) + if (position == 0) { + matches_fab_add.show() + } MetricsManager.trackScreen(when (position) { 0 -> MetricScreen.SCREEN_MATCHES_STATISTICS() else -> MetricScreen.SCREEN_MATCHES_HISTORY() diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesHistoryFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesHistoryFragment.kt index 4a65bc2..a06bea2 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesHistoryFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesHistoryFragment.kt @@ -17,10 +17,17 @@ import com.ediposouza.teslesgendstracker.ui.matches.CmdFilterMode import com.ediposouza.teslesgendstracker.ui.matches.CmdFilterSeason import com.ediposouza.teslesgendstracker.ui.util.firebase.OnLinearLayoutItemScrolled import com.ediposouza.teslesgendstracker.util.inflate +import com.timehop.stickyheadersrecyclerview.StickyRecyclerHeadersAdapter +import com.timehop.stickyheadersrecyclerview.StickyRecyclerHeadersDecoration import jp.wasabeef.recyclerview.animators.SlideInLeftAnimator import kotlinx.android.synthetic.main.fragment_matches_history.* import kotlinx.android.synthetic.main.itemlist_match_history.view.* +import kotlinx.android.synthetic.main.itemlist_match_history_section.view.* import org.greenrobot.eventbus.Subscribe +import org.threeten.bp.LocalDate +import org.threeten.bp.LocalDateTime +import org.threeten.bp.format.DateTimeFormatter +import org.threeten.bp.format.FormatStyle /** * Created by EdipoSouza on 1/3/17. @@ -40,7 +47,8 @@ class MatchesHistoryFragment : BaseFragment() { private val matchesAdapter: BaseAdsFirebaseAdapter by lazy { object : BaseAdsFirebaseAdapter( FirebaseParsers.MatchParser::class.java, { PrivateInteractor().getUserMatchesRef() }, - MATCH_PAGE_SIZE, ADS_EACH_ITEMS, R.layout.itemlist_match_history_ads, false, dataFilter) { + MATCH_PAGE_SIZE, ADS_EACH_ITEMS, R.layout.itemlist_match_history_ads, false, dataFilter), + StickyRecyclerHeadersAdapter { override fun onCreateDefaultViewHolder(parent: ViewGroup): MatchViewHolder { return MatchViewHolder(parent.inflate(R.layout.itemlist_match_history)) @@ -54,9 +62,33 @@ class MatchesHistoryFragment : BaseFragment() { matches_refresh_layout?.isRefreshing = false } + override fun onCreateHeaderViewHolder(parent: ViewGroup): MatchViewHolder { + return MatchViewHolder(parent.inflate(R.layout.itemlist_match_history_section)) + } + + override fun onBindHeaderViewHolder(holder: MatchViewHolder?, position: Int) { + holder?.bindSection(LocalDateTime.parse(getItemKey(position)).toLocalDate()) + } + + override fun getHeaderId(position: Int): Long { + if (getItemViewType(position) != VIEW_TYPE_CONTENT){ + return -1 + } + val date = LocalDateTime.parse(getItemKey(position)).toLocalDate() + return date.year + date.monthValue + date.dayOfMonth.toLong() + } + + }.apply { + registerAdapterDataObserver(object: RecyclerView.AdapterDataObserver() { + override fun onChanged() { + sectionDecoration.invalidateHeaders() + } + }) } } + private val sectionDecoration by lazy { StickyRecyclerHeadersDecoration(matchesAdapter as StickyRecyclerHeadersAdapter<*>) } + override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { return container?.inflate(R.layout.fragment_matches_history) } @@ -70,6 +102,7 @@ class MatchesHistoryFragment : BaseFragment() { layoutManager = object : LinearLayoutManager(context) { override fun supportsPredictiveItemAnimations(): Boolean = false } + addItemDecoration(sectionDecoration) addOnScrollListener(OnLinearLayoutItemScrolled(matchesAdapter.getContentCount() - 3) { matchesAdapter.more() }) @@ -118,6 +151,10 @@ class MatchesHistoryFragment : BaseFragment() { } } + fun bindSection(date: LocalDate) { + itemView.match_history_date.text = date.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL)) + } + } } \ No newline at end of file diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/util/TestUtils.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/util/TestUtils.kt index d841e3f..803f232 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/util/TestUtils.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/util/TestUtils.kt @@ -1,6 +1,12 @@ package com.ediposouza.teslesgendstracker.util +import android.support.v7.widget.RecyclerView +import android.view.ViewGroup +import com.ediposouza.teslesgendstracker.R import com.ediposouza.teslesgendstracker.data.* +import com.ediposouza.teslesgendstracker.ui.matches.tabs.MatchesHistoryFragment +import com.timehop.stickyheadersrecyclerview.StickyRecyclerHeadersAdapter +import org.threeten.bp.LocalDateTime /** * Created by EdipoSouza on 1/5/17. @@ -13,28 +19,30 @@ object TestUtils { MatchDeck("", Class.ASSASSIN, DeckType.AGGRO, ""), MatchDeck("", Class.BATTLEMAGE, DeckType.AGGRO, ""), MatchDeck("", Class.CRUSADER, DeckType.AGGRO, ""), - MatchDeck("", Class.MAGE, DeckType.AGGRO, ""), - MatchDeck("", Class.MONK, DeckType.AGGRO, ""), - MatchDeck("", Class.SCOUT, DeckType.AGGRO, ""), - MatchDeck("", Class.SORCERER, DeckType.AGGRO, ""), - MatchDeck("", Class.SPELLSWORD, DeckType.AGGRO, ""), - MatchDeck("", Class.WARRIOR, DeckType.AGGRO, ""), - MatchDeck("", Class.STRENGTH, DeckType.AGGRO, ""), - MatchDeck("", Class.INTELLIGENCE, DeckType.AGGRO, ""), - MatchDeck("", Class.AGILITY, DeckType.AGGRO, ""), - MatchDeck("", Class.WILLPOWER, DeckType.AGGRO, ""), - MatchDeck("", Class.ENDURANCE, DeckType.AGGRO, ""), - MatchDeck("", Class.NEUTRAL, DeckType.AGGRO, "") + MatchDeck("", Class.MAGE, DeckType.COMBO, ""), + MatchDeck("", Class.MONK, DeckType.COMBO, ""), + MatchDeck("", Class.SCOUT, DeckType.COMBO, ""), + MatchDeck("", Class.SORCERER, DeckType.COMBO, ""), + MatchDeck("", Class.SPELLSWORD, DeckType.CONTROL, ""), + MatchDeck("", Class.WARRIOR, DeckType.CONTROL, ""), + MatchDeck("", Class.STRENGTH, DeckType.CONTROL, ""), + MatchDeck("", Class.INTELLIGENCE, DeckType.CONTROL, ""), + MatchDeck("", Class.AGILITY, DeckType.MIDRANGE, ""), + MatchDeck("", Class.WILLPOWER, DeckType.MIDRANGE, ""), + MatchDeck("", Class.ENDURANCE, DeckType.MIDRANGE, ""), + MatchDeck("", Class.NEUTRAL, DeckType.MIDRANGE, "") ) return mutableListOf().apply { for ((indexPlayer, playerDeck) in decks.withIndex()) { for ((indexOpponent, opponentDeck) in decks.withIndex()) { addAll(mutableListOf().apply { for (i in 1..indexPlayer + 1) { - add(Match("", false, playerDeck, opponentDeck, MatchMode.RANKED, "2016_12", 0, false, true)) + val uuid = LocalDateTime.now().minusDays(playerDeck.type.ordinal.toLong()).toString() + add(Match(uuid, false, playerDeck, opponentDeck, MatchMode.RANKED, "2016_12", 0, false, true)) } for (i in 1..indexOpponent + 1) { - add(Match("", false, playerDeck, opponentDeck, MatchMode.RANKED, "2016_12", 0, false, false)) + val uuid = LocalDateTime.now().minusDays(opponentDeck.type.ordinal.toLong()).toString() + add(Match(uuid, false, playerDeck, opponentDeck, MatchMode.RANKED, "2016_12", 0, false, false)) } }) } @@ -42,4 +50,45 @@ object TestUtils { } } + fun getTestMatchesHistoryAdapter() = TestMatchesHistoryAdapter() + + class TestMatchesHistoryAdapter : RecyclerView.Adapter(), + StickyRecyclerHeadersAdapter { + + val items = TestUtils.getTestMatches() + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MatchesHistoryFragment.MatchViewHolder { + return MatchesHistoryFragment.MatchViewHolder(parent.inflate(R.layout.itemlist_match_history)) + } + + + override fun onBindViewHolder(holder: MatchesHistoryFragment.MatchViewHolder?, position: Int) { + holder?.bind(items[position]) + } + + override fun getItemCount(): Int = items.size + + override fun onCreateHeaderViewHolder(parent: ViewGroup): MatchesHistoryFragment.MatchViewHolder { + return MatchesHistoryFragment.MatchViewHolder(parent.inflate(R.layout.itemlist_match_history_section)) + } + + override fun onBindHeaderViewHolder(holder: MatchesHistoryFragment.MatchViewHolder?, position: Int) { + holder?.bindSection(LocalDateTime.parse(items[position].uuid).toLocalDate()) + } + + override fun getHeaderId(position: Int): Long { + val date = LocalDateTime.parse(items[position].uuid).toLocalDate() + return date.year + date.monthValue + date.dayOfMonth.toLong() + } + + + fun getContentCount(): Int = items.size + fun more() {} + fun reset() { + notifyDataSetChanged() + } + + } + + } \ No newline at end of file diff --git a/app/src/main/res/layout/itemcell_text.xml b/app/src/main/res/layout/itemcell_text.xml index 15cbf3a..14775f7 100644 --- a/app/src/main/res/layout/itemcell_text.xml +++ b/app/src/main/res/layout/itemcell_text.xml @@ -11,7 +11,6 @@ android:layout_gravity="center" android:gravity="center" android:maxLines="2" - android:orientation="horizontal" android:textColor="@android:color/white" android:visibility="gone" tools:text="23/12" diff --git a/app/src/main/res/layout/itemlist_match_history_section.xml b/app/src/main/res/layout/itemlist_match_history_section.xml new file mode 100644 index 0000000..721add1 --- /dev/null +++ b/app/src/main/res/layout/itemlist_match_history_section.xml @@ -0,0 +1,13 @@ + + \ No newline at end of file From 1dfbdf4466ae378d3b58f0b13c69b0fe377f6542 Mon Sep 17 00:00:00 2001 From: EdipoSouza Date: Tue, 10 Jan 2017 21:43:55 -0200 Subject: [PATCH 27/54] Repackage activities and widgets --- app/src/main/AndroidManifest.xml | 6 +++--- .../ui/base/BaseFilterActivity.kt | 4 ++-- .../ui/{ => cards}/CardActivity.kt | 2 +- .../teslesgendstracker/ui/cards/CardsFragment.kt | 2 +- .../ui/cards/tabs/CardsAllFragment.kt | 1 - .../ui/cards/tabs/CardsCollectionFragment.kt | 2 +- .../ui/cards/tabs/CardsFavoritesFragment.kt | 2 +- .../ui/{ => cards}/widget/CollectionStatistics.kt | 4 ++-- .../{ => cards}/widget/CollectionStatisticsAttr.kt | 2 +- .../filter => cards/widget}/FilterMagika.kt | 2 +- .../filter => cards/widget}/FilterRarity.kt | 2 +- .../ui/{ => decks}/DeckActivity.kt | 2 +- .../teslesgendstracker/ui/decks/DecksFragment.kt | 1 - .../ui/decks/{new => }/NewDeckActivity.kt | 4 +--- .../ui/decks/{new => }/NewDeckCardsListFragment.kt | 3 +-- .../ui/decks/tabs/DecksPublicFragment.kt | 2 +- .../teslesgendstracker/ui/decks/widget/DeckList.kt | 2 +- .../filter => decks/widget}/FilterAttrLockable.kt | 3 ++- .../ui/widget/{filter => }/FilterAttr.kt | 2 +- app/src/main/res/layout/activity_card.xml | 2 +- app/src/main/res/layout/activity_deck.xml | 2 +- app/src/main/res/layout/activity_new_deck.xml | 8 ++++---- app/src/main/res/layout/fragment_cards.xml | 8 ++++---- app/src/main/res/layout/fragment_decks.xml | 2 +- .../res/layout/widget_collection_statistics.xml | 14 +++++++------- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 27 files changed, 43 insertions(+), 47 deletions(-) rename app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/{ => cards}/CardActivity.kt (98%) rename app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/{ => cards}/widget/CollectionStatistics.kt (98%) rename app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/{ => cards}/widget/CollectionStatisticsAttr.kt (97%) rename app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/{widget/filter => cards/widget}/FilterMagika.kt (97%) rename app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/{widget/filter => cards/widget}/FilterRarity.kt (98%) rename app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/{ => decks}/DeckActivity.kt (99%) rename app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/{new => }/NewDeckActivity.kt (98%) rename app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/{new => }/NewDeckCardsListFragment.kt (92%) rename app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/{widget/filter => decks/widget}/FilterAttrLockable.kt (98%) rename app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/widget/{filter => }/FilterAttr.kt (98%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0abfcd6..eaa5dae 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -32,14 +32,14 @@ = Build.VERSION_CODES.LOLLIPOP) { - val layoutParams = collection_statistics_container.layoutParams as FrameLayout.LayoutParams + val layoutParams = collection_statistics_container.layoutParams as LayoutParams layoutParams.bottomMargin = resources.getDimensionPixelSize(R.dimen.navigation_bar_height) collection_statistics_container.layoutParams = layoutParams } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/widget/CollectionStatisticsAttr.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/widget/CollectionStatisticsAttr.kt similarity index 97% rename from app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/widget/CollectionStatisticsAttr.kt rename to app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/widget/CollectionStatisticsAttr.kt index a7e4037..765c92f 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/widget/CollectionStatisticsAttr.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/widget/CollectionStatisticsAttr.kt @@ -1,4 +1,4 @@ -package com.ediposouza.teslesgendstracker.ui.widget +package com.ediposouza.teslesgendstracker.ui.cards.widget import android.content.Context import android.util.AttributeSet diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/widget/filter/FilterMagika.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/widget/FilterMagika.kt similarity index 97% rename from app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/widget/filter/FilterMagika.kt rename to app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/widget/FilterMagika.kt index 8d23cce..7b49e71 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/widget/filter/FilterMagika.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/widget/FilterMagika.kt @@ -1,4 +1,4 @@ -package com.ediposouza.teslesgendstracker.ui.widget.filter +package com.ediposouza.teslesgendstracker.ui.cards.widget import android.content.Context import android.util.AttributeSet diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/widget/filter/FilterRarity.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/widget/FilterRarity.kt similarity index 98% rename from app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/widget/filter/FilterRarity.kt rename to app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/widget/FilterRarity.kt index 2cd33a9..43db3b1 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/widget/filter/FilterRarity.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/widget/FilterRarity.kt @@ -1,4 +1,4 @@ -package com.ediposouza.teslesgendstracker.ui.widget.filter +package com.ediposouza.teslesgendstracker.ui.cards.widget import android.animation.Animator import android.animation.ValueAnimator diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DeckActivity.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/DeckActivity.kt similarity index 99% rename from app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DeckActivity.kt rename to app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/DeckActivity.kt index 70c00d7..976c47c 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/DeckActivity.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/DeckActivity.kt @@ -1,4 +1,4 @@ -package com.ediposouza.teslesgendstracker.ui +package com.ediposouza.teslesgendstracker.ui.decks import android.app.Activity import android.content.Context diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/DecksFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/DecksFragment.kt index 7f8f62d..3f41da2 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/DecksFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/DecksFragment.kt @@ -18,7 +18,6 @@ import com.ediposouza.teslesgendstracker.R import com.ediposouza.teslesgendstracker.data.Class import com.ediposouza.teslesgendstracker.ui.base.* import com.ediposouza.teslesgendstracker.ui.cards.CmdFilterSearch -import com.ediposouza.teslesgendstracker.ui.decks.new.NewDeckActivity import com.ediposouza.teslesgendstracker.ui.decks.tabs.DecksFavoritedFragment import com.ediposouza.teslesgendstracker.ui.decks.tabs.DecksOwnerFragment import com.ediposouza.teslesgendstracker.ui.decks.tabs.DecksPublicFragment diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/new/NewDeckActivity.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/NewDeckActivity.kt similarity index 98% rename from app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/new/NewDeckActivity.kt rename to app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/NewDeckActivity.kt index 1d2e4b7..6931136 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/new/NewDeckActivity.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/NewDeckActivity.kt @@ -1,4 +1,4 @@ -package com.ediposouza.teslesgendstracker.ui.decks.new +package com.ediposouza.teslesgendstracker.ui.decks import android.app.Activity import android.content.Intent @@ -21,8 +21,6 @@ import com.ediposouza.teslesgendstracker.ui.base.CmdShowSnackbarMsg import com.ediposouza.teslesgendstracker.ui.cards.CmdFilterClass import com.ediposouza.teslesgendstracker.ui.cards.CmdFilterMagika import com.ediposouza.teslesgendstracker.ui.cards.CmdFilterRarity -import com.ediposouza.teslesgendstracker.ui.decks.CmdAddCard -import com.ediposouza.teslesgendstracker.ui.decks.CmdRemAttr import com.ediposouza.teslesgendstracker.util.MetricAction import com.ediposouza.teslesgendstracker.util.MetricScreen import com.ediposouza.teslesgendstracker.util.MetricsManager diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/new/NewDeckCardsListFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/NewDeckCardsListFragment.kt similarity index 92% rename from app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/new/NewDeckCardsListFragment.kt rename to app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/NewDeckCardsListFragment.kt index 4398919..3111a69 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/new/NewDeckCardsListFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/NewDeckCardsListFragment.kt @@ -1,11 +1,10 @@ -package com.ediposouza.teslesgendstracker.ui.decks.new +package com.ediposouza.teslesgendstracker.ui.decks import android.support.v7.widget.GridLayoutManager import android.view.View import com.ediposouza.teslesgendstracker.R import com.ediposouza.teslesgendstracker.data.Card import com.ediposouza.teslesgendstracker.ui.cards.tabs.CardsAllFragment -import com.ediposouza.teslesgendstracker.ui.decks.CmdAddCard import com.ediposouza.teslesgendstracker.ui.util.GridSpacingItemDecoration import kotlinx.android.synthetic.main.fragment_cards_list.* diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt index 3472f8f..15a87f7 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt @@ -16,8 +16,8 @@ import com.ediposouza.teslesgendstracker.data.Deck import com.ediposouza.teslesgendstracker.interactor.FirebaseParsers import com.ediposouza.teslesgendstracker.interactor.PrivateInteractor import com.ediposouza.teslesgendstracker.interactor.PublicInteractor -import com.ediposouza.teslesgendstracker.ui.DeckActivity import com.ediposouza.teslesgendstracker.ui.base.* +import com.ediposouza.teslesgendstracker.ui.decks.DeckActivity import com.ediposouza.teslesgendstracker.ui.util.firebase.OnLinearLayoutItemScrolled import com.ediposouza.teslesgendstracker.util.inflate import com.google.firebase.auth.FirebaseAuth diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/widget/DeckList.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/widget/DeckList.kt index bf69f99..2d3a76a 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/widget/DeckList.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/widget/DeckList.kt @@ -16,7 +16,7 @@ import com.ediposouza.teslesgendstracker.R import com.ediposouza.teslesgendstracker.data.* import com.ediposouza.teslesgendstracker.interactor.PrivateInteractor import com.ediposouza.teslesgendstracker.interactor.PublicInteractor -import com.ediposouza.teslesgendstracker.ui.CardActivity +import com.ediposouza.teslesgendstracker.ui.cards.CardActivity import com.ediposouza.teslesgendstracker.ui.decks.CmdRemAttr import com.ediposouza.teslesgendstracker.util.inflate import jp.wasabeef.recyclerview.animators.SlideInLeftAnimator diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/widget/filter/FilterAttrLockable.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/widget/FilterAttrLockable.kt similarity index 98% rename from app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/widget/filter/FilterAttrLockable.kt rename to app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/widget/FilterAttrLockable.kt index 2f3a470..56539fb 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/widget/filter/FilterAttrLockable.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/widget/FilterAttrLockable.kt @@ -1,4 +1,4 @@ -package com.ediposouza.teslesgendstracker.ui.widget.filter +package com.ediposouza.teslesgendstracker.ui.decks.widget import android.content.Context import android.util.AttributeSet @@ -6,6 +6,7 @@ import android.view.View import android.view.animation.AnimationUtils import com.ediposouza.teslesgendstracker.R import com.ediposouza.teslesgendstracker.data.Attribute +import com.ediposouza.teslesgendstracker.ui.widget.FilterAttr import kotlinx.android.synthetic.main.widget_attributes_filter.view.* /** diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/widget/filter/FilterAttr.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/widget/FilterAttr.kt similarity index 98% rename from app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/widget/filter/FilterAttr.kt rename to app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/widget/FilterAttr.kt index 36362e8..bbf473d 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/widget/filter/FilterAttr.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/widget/FilterAttr.kt @@ -1,4 +1,4 @@ -package com.ediposouza.teslesgendstracker.ui.widget.filter +package com.ediposouza.teslesgendstracker.ui.widget import android.content.Context import android.util.AttributeSet diff --git a/app/src/main/res/layout/activity_card.xml b/app/src/main/res/layout/activity_card.xml index a8aeb8b..35b76d9 100644 --- a/app/src/main/res/layout/activity_card.xml +++ b/app/src/main/res/layout/activity_card.xml @@ -6,7 +6,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:animateLayoutChanges="true" - tools:ctx="com.ediposouza.teslesgendstracker.ui.CardActivity"> + tools:ctx="com.ediposouza.teslesgendstracker.ui.cards.CardActivity"> + tools:ctx="com.ediposouza.teslesgendstracker.ui.decks.DeckActivity"> + tools:ctx="com.ediposouza.teslesgendstracker.ui.decks.NewDeckActivity"> - - - - - - - - - - - - - - - Date: Tue, 10 Jan 2017 22:55:50 -0200 Subject: [PATCH 28/54] Deck search. Deck search hint. Deck favorite toast --- .../interactor/FirebaseParsers.kt | 7 +++++++ .../interactor/PrivateInteractor.kt | 3 ++- .../teslesgendstracker/ui/cards/CardActivity.kt | 2 +- .../teslesgendstracker/ui/cards/CardsFragment.kt | 2 +- .../teslesgendstracker/ui/decks/DeckActivity.kt | 2 ++ .../teslesgendstracker/ui/decks/DecksFragment.kt | 2 +- .../ui/decks/tabs/DecksFavoritedFragment.kt | 14 ++++++++------ .../ui/decks/tabs/DecksPublicFragment.kt | 11 ++++++++++- app/src/main/res/values/strings.xml | 7 ++++--- 9 files changed, 36 insertions(+), 14 deletions(-) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/FirebaseParsers.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/FirebaseParsers.kt index c770144..d03fd6c 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/FirebaseParsers.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/FirebaseParsers.kt @@ -113,6 +113,13 @@ abstract class FirebaseParsers { } + class DeckFavoriteParser( + + val name: String = "", + val cls: Int = 0 + + ) + class PatchParser { val desc: String = "" diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt index 98f29d3..1e3ff1a 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/interactor/PrivateInteractor.kt @@ -246,7 +246,8 @@ class PrivateInteractor : BaseInteractor() { fun setUserDeckFavorite(deck: Deck, favorite: Boolean, onError: ((e: Exception?) -> Unit)? = null, onSuccess: () -> Unit) { dbUser()?.child(NODE_DECKS)?.child(NODE_FAVORITE)?.apply { if (favorite) { - child(deck.uuid)?.setValue(deck.cls.ordinal)?.addOnCompleteListener { onSuccess.invoke() } + val deckFavorite = FirebaseParsers.DeckFavoriteParser(deck.name, deck.cls.ordinal) + child(deck.uuid)?.setValue(deckFavorite)?.addOnCompleteListener { onSuccess.invoke() } } else { child(deck.uuid)?.removeValue()?.addOnCompleteListener { onSuccess.invoke() } } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/CardActivity.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/CardActivity.kt index 5252003..91e764d 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/CardActivity.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/CardActivity.kt @@ -104,7 +104,7 @@ class CardActivity : BaseActivity() { if (App.hasUserLogged()) { PrivateInteractor().setUserCardFavorite(card, !favorite) { favorite = !favorite - val stringRes = if (favorite) R.string.card_favorited else R.string.card_unfavorited + val stringRes = if (favorite) R.string.action_favorited else R.string.action_unfavorited toast(getString(stringRes, card.name)) loadCardInfo() setResult(Activity.RESULT_OK, Intent()) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/CardsFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/CardsFragment.kt index 24ce8db..50f8dd8 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/CardsFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/cards/CardsFragment.kt @@ -130,7 +130,7 @@ class CardsFragment : BaseFragment(), SearchView.OnQueryTextListener { inflater?.inflate(R.menu.menu_search, menu) inflater?.inflate(R.menu.menu_sets, menu) with(MenuItemCompat.getActionView(menu?.findItem(R.id.menu_search)) as SearchView) { - queryHint = getString(R.string.search_hint) + queryHint = getString(R.string.cards_search_hint) setOnQueryTextListener(this@CardsFragment) } super.onCreateOptionsMenu(menu, inflater) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/DeckActivity.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/DeckActivity.kt index 976c47c..e961aee 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/DeckActivity.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/DeckActivity.kt @@ -106,6 +106,8 @@ class DeckActivity : BaseActivity() { if (App.hasUserLogged()) { privateInteractor.setUserDeckFavorite(deck, !favorite) { favorite = !favorite + val stringRes = if (favorite) R.string.action_favorited else R.string.action_unfavorited + toast(getString(stringRes, deck.name)) updateFavoriteItem() MetricsManager.trackAction(if (favorite) MetricAction.ACTION_DECK_DETAILS_FAVORITE() else MetricAction.ACTION_DECK_DETAILS_UNFAVORITE()) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/DecksFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/DecksFragment.kt index 3f41da2..9fc182b 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/DecksFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/DecksFragment.kt @@ -113,7 +113,7 @@ class DecksFragment : BaseFragment(), SearchView.OnQueryTextListener { menu?.clear() inflater?.inflate(R.menu.menu_search, menu) with(MenuItemCompat.getActionView(menu?.findItem(R.id.menu_search)) as SearchView) { - queryHint = getString(R.string.search_hint) + queryHint = getString(R.string.decks_search_hint) setOnQueryTextListener(this@DecksFragment) } super.onCreateOptionsMenu(menu, inflater) diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksFavoritedFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksFavoritedFragment.kt index 5f01434..6ad8b61 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksFavoritedFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksFavoritedFragment.kt @@ -6,6 +6,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import com.ediposouza.teslesgendstracker.R +import com.ediposouza.teslesgendstracker.interactor.FirebaseParsers import com.ediposouza.teslesgendstracker.ui.base.BaseAdsFirebaseAdapter import com.ediposouza.teslesgendstracker.util.inflate import kotlinx.android.synthetic.main.fragment_decks_list.* @@ -19,20 +20,21 @@ class DecksFavoritedFragment : DecksPublicFragment() { privateInteractor.getUserFavoriteDecksRef() } - private val dataFilter: (Int) -> Boolean = { - currentClasses.map { it.ordinal }.contains(it) + private val dataFilter: (FirebaseParsers.DeckFavoriteParser) -> Boolean = { + currentClasses.map { it.ordinal }.contains(it.cls) && + it.name.toLowerCase().trim().contains(searchFilter ?: "") } - override val decksAdapter: BaseAdsFirebaseAdapter by lazy { - object : BaseAdsFirebaseAdapter( - Int::class.java, dataRef, DECK_PAGE_SIZE, ADS_EACH_ITEMS, + override val decksAdapter: BaseAdsFirebaseAdapter by lazy { + object : BaseAdsFirebaseAdapter( + FirebaseParsers.DeckFavoriteParser::class.java, dataRef, DECK_PAGE_SIZE, ADS_EACH_ITEMS, R.layout.itemlist_deck_ads, false, dataFilter) { override fun onCreateDefaultViewHolder(parent: ViewGroup): DecksAllViewHolder { return DecksAllViewHolder(parent.inflate(R.layout.itemlist_deck), itemClick, itemLongClick) } - override fun onBindContentHolder(itemKey: String, model: Int, viewHolder: DecksAllViewHolder) { + override fun onBindContentHolder(itemKey: String, model: FirebaseParsers.DeckFavoriteParser, viewHolder: DecksAllViewHolder) { if (!TextUtils.isEmpty(itemKey)) { viewHolder.bind(itemKey, publicInteractor, privateInteractor) } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt index 15a87f7..67b7a1f 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/tabs/DecksPublicFragment.kt @@ -17,6 +17,7 @@ import com.ediposouza.teslesgendstracker.interactor.FirebaseParsers import com.ediposouza.teslesgendstracker.interactor.PrivateInteractor import com.ediposouza.teslesgendstracker.interactor.PublicInteractor import com.ediposouza.teslesgendstracker.ui.base.* +import com.ediposouza.teslesgendstracker.ui.cards.CmdFilterSearch import com.ediposouza.teslesgendstracker.ui.decks.DeckActivity import com.ediposouza.teslesgendstracker.ui.util.firebase.OnLinearLayoutItemScrolled import com.ediposouza.teslesgendstracker.util.inflate @@ -37,6 +38,7 @@ open class DecksPublicFragment : BaseFragment() { val ADS_EACH_ITEMS = 10 //after 10 lines val DECK_PAGE_SIZE = 8 + protected var searchFilter: String? = null protected var currentClasses = Class.values() protected val publicInteractor = PublicInteractor() protected val privateInteractor = PrivateInteractor() @@ -53,7 +55,8 @@ open class DecksPublicFragment : BaseFragment() { } private val dataFilter: (FirebaseParsers.DeckParser) -> Boolean = { - currentClasses.map { it.ordinal }.contains(it.cls) + currentClasses.map { it.ordinal }.contains(it.cls) && + it.name.toLowerCase().trim().contains(searchFilter ?: "") } val itemClick = { view: View, deck: Deck -> @@ -135,6 +138,12 @@ open class DecksPublicFragment : BaseFragment() { showDecks() } + @Subscribe + fun onCmdFilterSearch(filterSearch: CmdFilterSearch) { + searchFilter = filterSearch.search?.toLowerCase()?.trim() + decksAdapter.reset() + } + @Subscribe fun onCmdShowDecksByClasses(cmdShowDecksByClasses: CmdShowDecksByClasses) { currentClasses = cmdShowDecksByClasses.classes.toTypedArray() diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 18d3a62..8c936ef 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -37,7 +37,6 @@ Are you sure? Open drawer Close drawer - Name, race, type or keyword Cards Collection @@ -68,10 +67,10 @@ Set: Race: card_transition - %1$s favorited - %1$s unfavorited Do Login + %1$s favorited + %1$s unfavorited Collection Unknown 0% @@ -82,11 +81,13 @@ Sign in with Twitter 0/0 + Name, race, type or keyword %1$02d/%2$02d %1$03d/%2$03d %1$.2f%% Total Cards\nSoul to complete + Deck Name Details Create by Create at From ef2a40a878f9f7e0cca0dd528b5b13714e760ecf Mon Sep 17 00:00:00 2001 From: EdipoSouza Date: Wed, 11 Jan 2017 00:18:26 -0200 Subject: [PATCH 29/54] New Matches activity --- app/src/main/AndroidManifest.xml | 6 +- .../ui/decks/DeckActivity.kt | 2 +- .../ui/decks/widget/DeckList.kt | 3 +- .../teslesgendstracker/ui/matches/Commands.kt | 5 +- .../ui/matches/MatchesFragment.kt | 24 ++-- .../matches/MatchesStatisticsClassActivity.kt | 3 + .../ui/matches/NewMatchesActivity.kt | 105 ++++++++++++++++++ .../ui/matches/tabs/MatchesHistoryFragment.kt | 6 + .../matches/tabs/MatchesStatisticsFragment.kt | 6 + .../util/MetricsConstants.kt | 33 +++++- .../main/res/layout/activity_new_matches.xml | 104 +++++++++++++++++ .../main/res/layout/include_new_matches.xml | 35 ++++++ 12 files changed, 314 insertions(+), 18 deletions(-) create mode 100644 app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/NewMatchesActivity.kt create mode 100644 app/src/main/res/layout/activity_new_matches.xml create mode 100644 app/src/main/res/layout/include_new_matches.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index eaa5dae..fa797ae 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -42,9 +42,11 @@ android:name=".ui.decks.NewDeckActivity" android:screenOrientation="portrait" android:windowSoftInputMode="adjustPan" /> + + android:name=".ui.matches.NewMatchesActivity" + android:screenOrientation="portrait" + android:windowSoftInputMode="adjustPan" /> (EXTRA_DECK) } + private val deck by lazy { intent.getParcelableExtra(EXTRA_DECK) } private val numberInstance: NumberFormat by lazy { NumberFormat.getNumberInstance() } private val commentsSheetBehavior: BottomSheetBehavior by lazy { BottomSheetBehavior.from(deck_bottom_sheet) } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/widget/DeckList.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/widget/DeckList.kt index 2d3a76a..d78529a 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/widget/DeckList.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/decks/widget/DeckList.kt @@ -77,8 +77,9 @@ class DeckList(ctx: Context?, attrs: AttributeSet?, defStyleAttr: Int) : constructor(ctx: Context?, attrs: AttributeSet) : this(ctx, attrs, 0) - fun showDeck(deck: Deck, showSoulCost: Boolean = true) { + fun showDeck(deck: Deck, showSoulCost: Boolean = true, showMagikaCosts: Boolean = true) { decklist_soul.visibility = if (showSoulCost) View.VISIBLE else View.GONE + decklist_costs.visibility = if (showMagikaCosts) View.VISIBLE else View.GONE doAsync { PublicInteractor().getDeckCards(deck) { context.runOnUiThread { diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/Commands.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/Commands.kt index 7c3e0f3..737c23a 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/Commands.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/Commands.kt @@ -6,6 +6,9 @@ import com.ediposouza.teslesgendstracker.data.Season /** * Created by EdipoSouza on 1/8/17. */ +class CmdUpdateMatches + data class CmdFilterMode(val mode: MatchMode) -data class CmdFilterSeason(val season: Season?) \ No newline at end of file +data class CmdFilterSeason(val season: Season?) + diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesFragment.kt index ba225f6..3eb027d 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesFragment.kt @@ -1,6 +1,8 @@ package com.ediposouza.teslesgendstracker.ui.matches +import android.app.Activity import android.content.Context +import android.content.Intent import android.os.Bundle import android.support.v4.app.Fragment import android.support.v4.app.FragmentManager @@ -29,7 +31,6 @@ import kotlinx.android.synthetic.main.fragment_matches.* import kotlinx.android.synthetic.main.itemlist_class.view.* import org.greenrobot.eventbus.Subscribe import org.jetbrains.anko.itemsSequence -import timber.log.Timber /** * Created by EdipoSouza on 1/3/17. @@ -37,6 +38,7 @@ import timber.log.Timber class MatchesFragment : BaseFragment() { private val KEY_PAGE_VIEW_POSITION = "pageViewPositionKey" + private val RC_NEW_MATCHES = 145 private var seasons: List = listOf() private var menuSeasons: SubMenu? = null @@ -124,6 +126,13 @@ class MatchesFragment : BaseFragment() { return super.onOptionsItemSelected(item) } + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + if (requestCode == RC_NEW_MATCHES && resultCode == Activity.RESULT_OK) { + eventBus.post(CmdUpdateMatches()) + } + } + private fun filterSeason(season: Season?) { val seasonId = season?.id ?: R.id.menu_season_all menuSeasons?.itemsSequence()?.forEach { @@ -148,18 +157,19 @@ class MatchesFragment : BaseFragment() { private fun showNewMatchDialog() { val dialogView = View.inflate(context, R.layout.dialog_new_match, null) + var decks = listOf(null) val classClickListener = object : AdapterView.OnItemSelectedListener { override fun onItemSelected(parent: AdapterView<*>?, view: View?, pos: Int, id: Long) { - PrivateInteractor().getUserDecks(Class.values()[pos]) { decks -> + PrivateInteractor().getUserDecks(Class.values()[pos]) { myDecks -> + decks = (myDecks as List).plusElement(null).sortedBy { it?.name ?: "" } dialogView.new_match_dialog_deck_spinner.adapter = ArrayAdapter(context, - android.R.layout.simple_spinner_dropdown_item, decks.map(Deck::name)) + android.R.layout.simple_spinner_dropdown_item, decks.map { it?.name ?: "" }) } } override fun onNothingSelected(parent: AdapterView<*>?) { } } - val decks = listOf() AlertDialog.Builder(context) .setView(dialogView) .setPositiveButton(R.string.new_match_dialog_start, { dialog, which -> @@ -167,7 +177,7 @@ class MatchesFragment : BaseFragment() { val cls = Class.values()[dialogView.new_match_dialog_class_spinner.selectedItemPosition] val deck = if (deckPosition >= 0) decks[deckPosition] else null val mode = MatchMode.values()[dialogView.new_match_dialog_mode_spinner.selectedItemPosition] - startNewMatches(cls, deck, mode) + startActivityForResult(NewMatchesActivity.newIntent(context, cls, deck, mode), RC_NEW_MATCHES) }) .setNegativeButton(android.R.string.cancel, { dialog, which -> }) .create() @@ -191,10 +201,6 @@ class MatchesFragment : BaseFragment() { } } - private fun startNewMatches(cls: Class, deck: Deck?, mode: MatchMode) { - Timber.d("$cls $deck $mode") - } - @Subscribe fun onCmdUpdateVisibility(update: CmdUpdateVisibility) { if (update.show) { diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesStatisticsClassActivity.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesStatisticsClassActivity.kt index bfd64a4..b7d965b 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesStatisticsClassActivity.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/MatchesStatisticsClassActivity.kt @@ -15,6 +15,8 @@ import com.ediposouza.teslesgendstracker.data.* import com.ediposouza.teslesgendstracker.interactor.PrivateInteractor import com.ediposouza.teslesgendstracker.interactor.PublicInteractor import com.ediposouza.teslesgendstracker.ui.base.BaseActivity +import com.ediposouza.teslesgendstracker.util.MetricScreen +import com.ediposouza.teslesgendstracker.util.MetricsManager import kotlinx.android.synthetic.main.activity_matches_statistics_class.* import kotlinx.android.synthetic.main.itemcell_class.view.* import kotlinx.android.synthetic.main.itemcell_text.view.* @@ -92,6 +94,7 @@ class MatchesStatisticsClassActivity : BaseActivity() { statistics_class_attr1.setImageResource(attr1.imageRes) statistics_class_attr2.setImageResource(attr2.imageRes) } + MetricsManager.trackScreen(MetricScreen.SCREEN_MATCHES_STATISTICS_CLASS()) } override fun onCreateOptionsMenu(menu: Menu?): Boolean { diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/NewMatchesActivity.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/NewMatchesActivity.kt new file mode 100644 index 0000000..3099d62 --- /dev/null +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/NewMatchesActivity.kt @@ -0,0 +1,105 @@ +package com.ediposouza.teslesgendstracker.ui.matches + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.os.Build +import android.os.Bundle +import android.support.design.widget.CollapsingToolbarLayout +import android.support.v4.app.ActivityCompat +import android.view.Menu +import android.view.MenuItem +import android.widget.RelativeLayout +import com.ediposouza.teslesgendstracker.R +import com.ediposouza.teslesgendstracker.data.Class +import com.ediposouza.teslesgendstracker.data.Deck +import com.ediposouza.teslesgendstracker.data.MatchMode +import com.ediposouza.teslesgendstracker.interactor.PrivateInteractor +import com.ediposouza.teslesgendstracker.ui.base.BaseActivity +import com.ediposouza.teslesgendstracker.util.MetricScreen +import com.ediposouza.teslesgendstracker.util.MetricsManager +import kotlinx.android.synthetic.main.activity_new_matches.* +import kotlinx.android.synthetic.main.include_new_matches.* +import org.jetbrains.anko.intentFor + +class NewMatchesActivity : BaseActivity() { + + companion object { + + private val EXTRA_CLASS = "classExtra" + private val EXTRA_DECK = "deckExtra" + private val EXTRA_MATCH_MODE = "modeExtra" + + fun newIntent(context: Context, cls: Class, deck: Deck?, mode: MatchMode): Intent { + if (deck != null) { + return context.intentFor(EXTRA_CLASS to cls.ordinal, EXTRA_DECK to deck, + EXTRA_MATCH_MODE to mode.ordinal) + } else { + return context.intentFor(EXTRA_CLASS to cls.ordinal, + EXTRA_MATCH_MODE to mode.ordinal) + } + } + + } + + private val privateInteractor by lazy { PrivateInteractor() } + private val cls by lazy { Class.values()[intent.getIntExtra(EXTRA_CLASS, 0)] } + private val mode by lazy { MatchMode.values()[intent.getIntExtra(EXTRA_MATCH_MODE, 0)] } + private val deck: Deck? by lazy { + if (intent.hasExtra(EXTRA_DECK)) intent.getParcelableExtra(EXTRA_DECK) else null + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_new_matches) + val statusBarHeight = resources.getDimensionPixelSize(R.dimen.status_bar_height) + if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) { + val coverLP = new_matches_class_cover.layoutParams as RelativeLayout.LayoutParams + coverLP.height = coverLP.height - statusBarHeight + new_matches_class_cover.layoutParams = coverLP + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + val layoutParams = toolbar.layoutParams as CollapsingToolbarLayout.LayoutParams + layoutParams.topMargin = statusBarHeight + toolbar.layoutParams = layoutParams + } + + configViews() + } + + private fun configViews() { + new_matches_deck_class_name.text = if (deck != null) deck?.name else cls.name.toLowerCase().capitalize() + new_matches_class_cover.setImageResource(cls.imageRes) + new_matches_class_attr1.setImageResource(cls.attr1.imageRes) + new_matches_class_attr2.setImageResource(cls.attr2.imageRes) + if (deck != null) { + new_matches_deck_cardlist.showDeck(deck!!, false, false) + } + } + + override fun onPostCreate(savedInstanceState: Bundle?) { + super.onPostCreate(savedInstanceState) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + setResult(Activity.RESULT_CANCELED, Intent()) + MetricsManager.trackScreen(MetricScreen.SCREEN_NEW_MATCHES()) + } + + override fun onCreateOptionsMenu(menu: Menu?): Boolean { + menuInflater.inflate(R.menu.menu_done, menu) + return super.onCreateOptionsMenu(menu) + } + + override fun onOptionsItemSelected(item: MenuItem?): Boolean { + when (item?.itemId) { + android.R.id.home -> { + ActivityCompat.finishAfterTransition(this) + return true + } + R.id.menu_done -> { + return true + } + } + return super.onOptionsItemSelected(item) + } + +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesHistoryFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesHistoryFragment.kt index a06bea2..61ed372 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesHistoryFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesHistoryFragment.kt @@ -15,6 +15,7 @@ import com.ediposouza.teslesgendstracker.ui.base.BaseAdsFirebaseAdapter import com.ediposouza.teslesgendstracker.ui.base.BaseFragment import com.ediposouza.teslesgendstracker.ui.matches.CmdFilterMode import com.ediposouza.teslesgendstracker.ui.matches.CmdFilterSeason +import com.ediposouza.teslesgendstracker.ui.matches.CmdUpdateMatches import com.ediposouza.teslesgendstracker.ui.util.firebase.OnLinearLayoutItemScrolled import com.ediposouza.teslesgendstracker.util.inflate import com.timehop.stickyheadersrecyclerview.StickyRecyclerHeadersAdapter @@ -131,6 +132,11 @@ class MatchesHistoryFragment : BaseFragment() { matches_recycler_view.scrollToPosition(0) } + @Subscribe + fun onUpdateMatches(cmdUpdateMatches: CmdUpdateMatches) { + matchesAdapter.reset() + } + class MatchViewHolder(view: View) : RecyclerView.ViewHolder(view) { fun bind(match: Match) { diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatisticsFragment.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatisticsFragment.kt index 913eccd..8fd8f57 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatisticsFragment.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/tabs/MatchesStatisticsFragment.kt @@ -14,6 +14,7 @@ import com.ediposouza.teslesgendstracker.interactor.PrivateInteractor import com.ediposouza.teslesgendstracker.ui.base.BaseFragment import com.ediposouza.teslesgendstracker.ui.matches.CmdFilterMode import com.ediposouza.teslesgendstracker.ui.matches.CmdFilterSeason +import com.ediposouza.teslesgendstracker.ui.matches.CmdUpdateMatches import com.ediposouza.teslesgendstracker.ui.matches.MatchesStatisticsClassActivity import com.ediposouza.teslesgendstracker.util.inflate import kotlinx.android.synthetic.main.fragment_matches_statistics.* @@ -179,6 +180,11 @@ class MatchesStatisticsFragment : BaseFragment() { getMatches() } + @Subscribe + fun onUpdateMatches(cmdUpdateMatches: CmdUpdateMatches) { + getMatches() + } + class BodyItem(val result: String? = null, val cls: Class? = null, val selected: Boolean = false) class StatisticsTableAdapter(val context: Context) : TableFixHeaderAdapter + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/include_new_matches.xml b/app/src/main/res/layout/include_new_matches.xml new file mode 100644 index 0000000..2431919 --- /dev/null +++ b/app/src/main/res/layout/include_new_matches.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + \ No newline at end of file From 8b2f32d2f7506e0b693d8daeff57ba9df91441c3 Mon Sep 17 00:00:00 2001 From: EdipoSouza Date: Thu, 12 Jan 2017 00:18:32 -0200 Subject: [PATCH 30/54] New Matches activity layout --- app/src/main/AndroidManifest.xml | 3 +- .../ui/decks/widget/DeckList.kt | 25 +-- .../ui/matches/MatchesFragment.kt | 5 +- .../ui/matches/NewMatchesActivity.kt | 13 +- .../teslesgendstracker/util/AppExtensions.kt | 6 +- .../main/res/drawable/selector_menu_item.xml | 2 +- .../main/res/drawable/xml_button_green.xml | 16 ++ app/src/main/res/drawable/xml_button_red.xml | 16 ++ .../main/res/layout/activity_new_matches.xml | 18 +- app/src/main/res/layout/dialog_new_deck.xml | 6 +- app/src/main/res/layout/dialog_new_match.xml | 9 +- .../main/res/layout/include_login_button.xml | 2 +- .../main/res/layout/include_new_matches.xml | 155 ++++++++++++++++-- .../res/layout/itemlist_new_match_class.xml | 42 +++++ app/src/main/res/values/colors.xml | 6 +- app/src/main/res/values/strings.xml | 5 + app/src/main/res/values/styles.xml | 23 +++ 17 files changed, 314 insertions(+), 38 deletions(-) create mode 100644 app/src/main/res/drawable/xml_button_green.xml create mode 100644 app/src/main/res/drawable/xml_button_red.xml create mode 100644 app/src/main/res/layout/itemlist_new_match_class.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index fa797ae..0499d7f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -46,7 +46,8 @@ + android:windowSoftInputMode="adjustPan" + android:theme="@style/AppThemeAccentEqualsPrimaryDark"/> (ctx, R.layout.itemlist_class, - R.id.class_name, Class.values()) { + class ClassAdapter(ctx: Context, @IntegerRes layout: Int = R.layout.itemlist_class) : + ArrayAdapter(ctx, layout, R.id.class_name, Class.values()) { override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup?): View { return super.getDropDownView(position, convertView, parent).apply { diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/NewMatchesActivity.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/NewMatchesActivity.kt index 3099d62..2e0ef41 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/NewMatchesActivity.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/ui/matches/NewMatchesActivity.kt @@ -9,15 +9,18 @@ import android.support.design.widget.CollapsingToolbarLayout import android.support.v4.app.ActivityCompat import android.view.Menu import android.view.MenuItem +import android.widget.ArrayAdapter import android.widget.RelativeLayout import com.ediposouza.teslesgendstracker.R import com.ediposouza.teslesgendstracker.data.Class import com.ediposouza.teslesgendstracker.data.Deck +import com.ediposouza.teslesgendstracker.data.DeckType import com.ediposouza.teslesgendstracker.data.MatchMode import com.ediposouza.teslesgendstracker.interactor.PrivateInteractor import com.ediposouza.teslesgendstracker.ui.base.BaseActivity import com.ediposouza.teslesgendstracker.util.MetricScreen import com.ediposouza.teslesgendstracker.util.MetricsManager +import com.ediposouza.teslesgendstracker.util.limitHeight import kotlinx.android.synthetic.main.activity_new_matches.* import kotlinx.android.synthetic.main.include_new_matches.* import org.jetbrains.anko.intentFor @@ -72,8 +75,14 @@ class NewMatchesActivity : BaseActivity() { new_matches_class_cover.setImageResource(cls.imageRes) new_matches_class_attr1.setImageResource(cls.attr1.imageRes) new_matches_class_attr2.setImageResource(cls.attr2.imageRes) - if (deck != null) { - new_matches_deck_cardlist.showDeck(deck!!, false, false) + new_matches_deck_cardlist.editMode = true + new_matches_deck_cardlist.showDeck(deck, false, false, false) + new_match_type_spinner.adapter = ArrayAdapter(this, + android.R.layout.simple_spinner_dropdown_item, DeckType.values() + .filter { it != DeckType.ARENA }.map { it.name.toLowerCase().capitalize() }) + new_match_class_spinner.apply { + adapter = MatchesFragment.ClassAdapter(context, R.layout.itemlist_new_match_class) + limitHeight(8) } } diff --git a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/util/AppExtensions.kt b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/util/AppExtensions.kt index 2262171..c896adc 100644 --- a/app/src/main/kotlin/com/ediposouza/teslesgendstracker/util/AppExtensions.kt +++ b/app/src/main/kotlin/com/ediposouza/teslesgendstracker/util/AppExtensions.kt @@ -59,14 +59,16 @@ fun MixpanelAPI.trackBundle(eventName: String, bundle: Bundle) { } -fun Spinner.limitHeight() { +fun Spinner.limitHeight(lines: Int? = null) { val displayHeight = context.resources.displayMetrics.heightPixels + val itemHeight = context.resources.getDimensionPixelSize(R.dimen.material_design_default_height) Spinner::class.java.getDeclaredField("mPopup") ?.apply { isAccessible = true }?.get(this) ?.apply popup@ { IntArray(2).apply { getLocationOnScreen(this) - (this@popup as ListPopupWindow).height = displayHeight - get(1) + val listPopupWindow = this@popup as ListPopupWindow + listPopupWindow.height = if(lines != null) lines * itemHeight else displayHeight - get(1) } } } diff --git a/app/src/main/res/drawable/selector_menu_item.xml b/app/src/main/res/drawable/selector_menu_item.xml index f5c81e5..2a7a9db 100644 --- a/app/src/main/res/drawable/selector_menu_item.xml +++ b/app/src/main/res/drawable/selector_menu_item.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/xml_button_green.xml b/app/src/main/res/drawable/xml_button_green.xml new file mode 100644 index 0000000..3b91d50 --- /dev/null +++ b/app/src/main/res/drawable/xml_button_green.xml @@ -0,0 +1,16 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/xml_button_red.xml b/app/src/main/res/drawable/xml_button_red.xml new file mode 100644 index 0000000..794d87d --- /dev/null +++ b/app/src/main/res/drawable/xml_button_red.xml @@ -0,0 +1,16 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_new_matches.xml b/app/src/main/res/layout/activity_new_matches.xml index 2e6a73e..8fa478e 100644 --- a/app/src/main/res/layout/activity_new_matches.xml +++ b/app/src/main/res/layout/activity_new_matches.xml @@ -84,6 +84,22 @@ android:transitionName="@string/deck_attr2_transition_name" tools:src="@drawable/attr_agility" /> + + + + @@ -93,7 +109,7 @@ diff --git a/app/src/main/res/layout/dialog_new_deck.xml b/app/src/main/res/layout/dialog_new_deck.xml index 7a2b2ba..b3abe05 100644 --- a/app/src/main/res/layout/dialog_new_deck.xml +++ b/app/src/main/res/layout/dialog_new_deck.xml @@ -44,7 +44,8 @@ android:layout_below="@id/new_deck_dialog_nameLayout" android:layout_marginBottom="@dimen/default_margin" android:layout_marginTop="@dimen/medium_margin" - android:text="@string/new_deck_save_dialog_type_label" /> + android:text="@string/new_deck_save_dialog_type_label" + android:textColor="@color/grey_500" /> + android:text="@string/new_deck_save_dialog_patch_label" + android:textColor="@color/grey_500" /> + android:text="@string/new_match_dialog_class_label" + android:textColor="@color/grey_500" /> + android:text="@string/new_match_dialog_deck_label" + android:textColor="@color/grey_500" /> + android:text="@string/new_match_dialog_mode_label" + android:textColor="@color/grey_500" /> - + android:focusable="true" + android:focusableInTouchMode="true" + android:paddingStart="@dimen/default_margin"> - + android:layout_height="wrap_content" + android:layout_below="@id/new_match_dialog_title" + android:layout_marginTop="@dimen/huge_margin" + android:text="@string/new_match_dialog_class_label" /> - + + + + + + + + + + + + + + + + + + + + +