diff --git a/Habitica/build.gradle b/Habitica/build.gradle index 9a3f4c9eb..5c6057a92 100644 --- a/Habitica/build.gradle +++ b/Habitica/build.gradle @@ -119,7 +119,7 @@ dependencies { implementation 'com.google.firebase:firebase-core:16.0.9' implementation 'com.google.firebase:firebase-messaging:18.0.0' implementation 'com.google.firebase:firebase-config:17.0.0' - implementation 'com.google.firebase:firebase-perf:17.0.0' + implementation 'com.google.firebase:firebase-perf:17.0.2' implementation 'com.google.android.gms:play-services-auth:16.0.1' implementation 'io.realm:android-adapters:3.1.0' implementation(project(':seeds-sdk')) { diff --git a/Habitica/res/drawable-hdpi/bottom_navigation_inset.png b/Habitica/res/drawable-hdpi/bottom_navigation_inset.png new file mode 100644 index 000000000..860db0a48 Binary files /dev/null and b/Habitica/res/drawable-hdpi/bottom_navigation_inset.png differ diff --git a/Habitica/res/drawable-hdpi/fab_background.png b/Habitica/res/drawable-hdpi/fab_background.png new file mode 100644 index 000000000..ecb2de088 Binary files /dev/null and b/Habitica/res/drawable-hdpi/fab_background.png differ diff --git a/Habitica/res/drawable-hdpi/fab_plus.png b/Habitica/res/drawable-hdpi/fab_plus.png new file mode 100644 index 000000000..994b31f3a Binary files /dev/null and b/Habitica/res/drawable-hdpi/fab_plus.png differ diff --git a/Habitica/res/drawable-mdpi/bottom_navigation_inset.png b/Habitica/res/drawable-mdpi/bottom_navigation_inset.png new file mode 100644 index 000000000..2e127891d Binary files /dev/null and b/Habitica/res/drawable-mdpi/bottom_navigation_inset.png differ diff --git a/Habitica/res/drawable-mdpi/fab_background.png b/Habitica/res/drawable-mdpi/fab_background.png new file mode 100644 index 000000000..d13cf484c Binary files /dev/null and b/Habitica/res/drawable-mdpi/fab_background.png differ diff --git a/Habitica/res/drawable-mdpi/fab_plus.png b/Habitica/res/drawable-mdpi/fab_plus.png new file mode 100644 index 000000000..69965cefe Binary files /dev/null and b/Habitica/res/drawable-mdpi/fab_plus.png differ diff --git a/Habitica/res/drawable-xhdpi/bottom_navigation_inset.png b/Habitica/res/drawable-xhdpi/bottom_navigation_inset.png new file mode 100644 index 000000000..4e51dfca6 Binary files /dev/null and b/Habitica/res/drawable-xhdpi/bottom_navigation_inset.png differ diff --git a/Habitica/res/drawable-xhdpi/fab_background.png b/Habitica/res/drawable-xhdpi/fab_background.png new file mode 100644 index 000000000..913c83f7f Binary files /dev/null and b/Habitica/res/drawable-xhdpi/fab_background.png differ diff --git a/Habitica/res/drawable-xhdpi/fab_plus.png b/Habitica/res/drawable-xhdpi/fab_plus.png new file mode 100644 index 000000000..37645eed0 Binary files /dev/null and b/Habitica/res/drawable-xhdpi/fab_plus.png differ diff --git a/Habitica/res/drawable-xxhdpi/bottom_navigation_inset.png b/Habitica/res/drawable-xxhdpi/bottom_navigation_inset.png new file mode 100644 index 000000000..809c54194 Binary files /dev/null and b/Habitica/res/drawable-xxhdpi/bottom_navigation_inset.png differ diff --git a/Habitica/res/drawable-xxhdpi/fab_background.png b/Habitica/res/drawable-xxhdpi/fab_background.png new file mode 100644 index 000000000..05e7eb474 Binary files /dev/null and b/Habitica/res/drawable-xxhdpi/fab_background.png differ diff --git a/Habitica/res/drawable-xxhdpi/fab_plus.png b/Habitica/res/drawable-xxhdpi/fab_plus.png new file mode 100644 index 000000000..402a7f2d5 Binary files /dev/null and b/Habitica/res/drawable-xxhdpi/fab_plus.png differ diff --git a/Habitica/res/drawable-xxxhdpi/bottom_navigation_inset.png b/Habitica/res/drawable-xxxhdpi/bottom_navigation_inset.png new file mode 100644 index 000000000..fbcfe8429 Binary files /dev/null and b/Habitica/res/drawable-xxxhdpi/bottom_navigation_inset.png differ diff --git a/Habitica/res/drawable-xxxhdpi/fab_background.png b/Habitica/res/drawable-xxxhdpi/fab_background.png new file mode 100644 index 000000000..18cf2b0f2 Binary files /dev/null and b/Habitica/res/drawable-xxxhdpi/fab_background.png differ diff --git a/Habitica/res/drawable-xxxhdpi/fab_plus.png b/Habitica/res/drawable-xxxhdpi/fab_plus.png new file mode 100644 index 000000000..0dc5a9224 Binary files /dev/null and b/Habitica/res/drawable-xxxhdpi/fab_plus.png differ diff --git a/Habitica/res/drawable/nav_icon_colors.xml b/Habitica/res/drawable/nav_icon_colors.xml new file mode 100644 index 000000000..cd141807d --- /dev/null +++ b/Habitica/res/drawable/nav_icon_colors.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Habitica/res/fab_background.png b/Habitica/res/fab_background.png new file mode 100644 index 000000000..939ff7e20 Binary files /dev/null and b/Habitica/res/fab_background.png differ diff --git a/Habitica/res/layout/activity_challenge_detail.xml b/Habitica/res/layout/activity_challenge_detail.xml index 586245cbe..6917d816c 100644 --- a/Habitica/res/layout/activity_challenge_detail.xml +++ b/Habitica/res/layout/activity_challenge_detail.xml @@ -179,7 +179,7 @@ - - + - + - - + android:layout_height="wrap_content" + android:layout_alignParentBottom="true" + android:layout_alignParentStart="true" + android:layout_alignParentEnd="true" /> + + + + + + \ No newline at end of file diff --git a/Habitica/res/layout/main_navigation_view.xml b/Habitica/res/layout/main_navigation_view.xml new file mode 100644 index 000000000..be983d27b --- /dev/null +++ b/Habitica/res/layout/main_navigation_view.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Habitica/res/layout/snackbar_view.xml b/Habitica/res/layout/snackbar_view.xml index 4d67e26b2..4bfadd158 100644 --- a/Habitica/res/layout/snackbar_view.xml +++ b/Habitica/res/layout/snackbar_view.xml @@ -13,7 +13,7 @@ android:paddingStart="6dp" android:gravity="center" tools:background="@drawable/snackbar_background_green" - android:layout_marginBottom="16dp" + android:layout_marginBottom="32dp" android:layout_gravity="center_horizontal" android:elevation="24dp"> + + + + + + + \ No newline at end of file diff --git a/Habitica/res/values/attrs.xml b/Habitica/res/values/attrs.xml index d67ca7bad..aba07135f 100644 --- a/Habitica/res/values/attrs.xml +++ b/Habitica/res/values/attrs.xml @@ -12,6 +12,7 @@ + @@ -46,7 +47,7 @@ - + @@ -119,4 +120,8 @@ + + + + diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.kt index 44565adc7..84f6f9e0e 100755 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.kt @@ -68,8 +68,8 @@ import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar.SnackbarDisplayType import com.habitrpg.android.habitica.ui.views.ValueBar -import com.habitrpg.android.habitica.ui.views.bottombar.BottomBar import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog +import com.habitrpg.android.habitica.ui.views.navigation.HabiticaBottomNavigationView import com.habitrpg.android.habitica.ui.views.yesterdailies.YesterdailyDialog import com.habitrpg.android.habitica.userpicture.BitmapUtils import com.habitrpg.android.habitica.widget.AvatarStatsWidgetProvider @@ -121,8 +121,8 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction { @Inject internal lateinit var appConfigManager: AppConfigManager - val floatingMenuWrapper: ViewGroup by bindView(R.id.floating_menu_wrapper) - internal val bottomNavigation: BottomBar by bindView(R.id.bottom_navigation) + val snackbarContainer: ViewGroup by bindView(R.id.snackbar_container) + internal val bottomNavigation: HabiticaBottomNavigationView by bindView(R.id.bottom_navigation) private val appBar: AppBarLayout by bindView(R.id.appbar) internal val toolbar: Toolbar by bindView(R.id.toolbar) @@ -467,7 +467,7 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction { val pet = event.usingPet compositeSubscription.add(this.inventoryRepository.feedPet(event.usingPet, event.usingFood) .subscribe(Consumer { feedResponse -> - HabiticaSnackbar.showSnackbar(floatingMenuWrapper, getString(R.string.notification_pet_fed, pet.text), SnackbarDisplayType.NORMAL) + HabiticaSnackbar.showSnackbar(snackbarContainer, getString(R.string.notification_pet_fed, pet.text), SnackbarDisplayType.NORMAL) if (feedResponse.value == -1) { val mountWrapper = View.inflate(this, R.layout.pet_imageview, null) as? FrameLayout val mountImageView = mountWrapper?.findViewById(R.id.pet_imageview) as? SimpleDraweeView @@ -499,12 +499,12 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction { internal fun displayTaskScoringResponse(data: TaskScoringResult?) { if (user != null && data != null) { - compositeSubscription.add(notifyUserUseCase.observable(NotifyUserUseCase.RequestValues(this, floatingMenuWrapper, + compositeSubscription.add(notifyUserUseCase.observable(NotifyUserUseCase.RequestValues(this, snackbarContainer, user, data.experienceDelta, data.healthDelta, data.goldDelta, data.manaDelta, if (userIsOnQuest) data.questDamage else 0.0, data.hasLeveledUp)) .subscribe(Consumer { }, RxErrorHandler.handleEmptyError())) } - compositeSubscription.add(displayItemDropUseCase.observable(DisplayItemDropUseCase.RequestValues(data, this, floatingMenuWrapper)) + compositeSubscription.add(displayItemDropUseCase.observable(DisplayItemDropUseCase.RequestValues(data, this, snackbarContainer)) .subscribe(Consumer { }, RxErrorHandler.handleEmptyError())) } @@ -689,7 +689,7 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction { @Subscribe fun showSnackBarEvent(event: ShowSnackbarEvent) { - HabiticaSnackbar.showSnackbar(floatingMenuWrapper, event.leftImage, event.title, event.text, event.specialView, event.rightIcon, event.rightTextColor, event.rightText, event.type) + HabiticaSnackbar.showSnackbar(snackbarContainer, event.leftImage, event.title, event.text, event.specialView, event.rightIcon, event.rightTextColor, event.rightText, event.type) } @Subscribe diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseMainFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseMainFragment.kt index 9dcc8f904..f74fc97d7 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseMainFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseMainFragment.kt @@ -30,7 +30,7 @@ abstract class BaseMainFragment : BaseFragment() { val collapsingToolbar get() = activity?.toolbar val toolbarAccessoryContainer get() = activity?.toolbarAccessoryContainer val bottomNavigation get() = activity?.bottomNavigation - val floatingMenuWrapper get() = activity?.floatingMenuWrapper + val floatingMenuWrapper get() = activity?.snackbarContainer var usesTabLayout: Boolean = false var hidesToolbar: Boolean = false var usesBottomNavigation = false @@ -56,8 +56,6 @@ abstract class BaseMainFragment : BaseFragment() { } if (this.usesBottomNavigation) { - bottomNavigation?.removeOnTabSelectListener() - bottomNavigation?.removeOnTabReselectListener() bottomNavigation?.visibility = View.VISIBLE } else { bottomNavigation?.visibility = View.GONE diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/skills/SkillsFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/skills/SkillsFragment.kt index 7e8a98d75..98066e3f7 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/skills/SkillsFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/skills/SkillsFragment.kt @@ -110,10 +110,10 @@ class SkillsFragment : BaseMainFragment() { adapter?.mana = response.user.stats?.mp ?: 0.0 val activity = activity ?: return if ("special" == usedSkill?.habitClass) { - showSnackbar(activity.floatingMenuWrapper, context?.getString(R.string.used_skill_without_mana, usedSkill.text), HabiticaSnackbar.SnackbarDisplayType.BLUE) + showSnackbar(activity.snackbarContainer, context?.getString(R.string.used_skill_without_mana, usedSkill.text), HabiticaSnackbar.SnackbarDisplayType.BLUE) } else { context.notNull { - showSnackbar(activity.floatingMenuWrapper, null, + showSnackbar(activity.snackbarContainer, null, context?.getString(R.string.used_skill_without_mana, usedSkill?.text), BitmapDrawable(resources, HabiticaIconsHelper.imageOfMagic()), ContextCompat.getColor(it, R.color.blue_10), "-" + usedSkill?.mana, diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/ChatFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/ChatFragment.kt index ed091da5f..edc3fa18d 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/ChatFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/ChatFragment.kt @@ -195,7 +195,7 @@ class ChatFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener { clipMan?.primaryClip = messageText val activity = activity as? MainActivity if (activity != null) { - showSnackbar(activity.floatingMenuWrapper, getString(R.string.chat_message_copied), SnackbarDisplayType.NORMAL) + showSnackbar(activity.snackbarContainer, getString(R.string.chat_message_copied), SnackbarDisplayType.NORMAL) } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/ChatListFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/ChatListFragment.kt index b40f4f433..15a7e29b4 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/ChatListFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/ChatListFragment.kt @@ -250,7 +250,7 @@ class ChatListFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener { clipMan?.primaryClip = messageText val activity = activity as? MainActivity if (activity != null) { - showSnackbar(activity.floatingMenuWrapper, getString(R.string.chat_message_copied), SnackbarDisplayType.NORMAL) + showSnackbar(activity.snackbarContainer, getString(R.string.chat_message_copied), SnackbarDisplayType.NORMAL) } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/GroupInformationFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/GroupInformationFragment.kt index 77b46605d..8a444042b 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/GroupInformationFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/GroupInformationFragment.kt @@ -102,7 +102,7 @@ class GroupInformationFragment : BaseFragment() { clipboard?.primaryClip = clip val activity = activity as? MainActivity if (activity != null) { - HabiticaSnackbar.showSnackbar(activity.floatingMenuWrapper, getString(R.string.username_copied), HabiticaSnackbar.SnackbarDisplayType.NORMAL) + HabiticaSnackbar.showSnackbar(activity.snackbarContainer, getString(R.string.username_copied), HabiticaSnackbar.SnackbarDisplayType.NORMAL) } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/GuildDetailFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/GuildDetailFragment.kt index 466199147..9eb5c8658 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/GuildDetailFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/GuildDetailFragment.kt @@ -80,7 +80,7 @@ class GuildDetailFragment : BaseFragment() { viewModel?.leaveGroup { val activity = activity as? MainActivity if (activity != null) { - HabiticaSnackbar.showSnackbar(activity.floatingMenuWrapper, getString(R.string.left_guild), HabiticaSnackbar.SnackbarDisplayType.NORMAL) + HabiticaSnackbar.showSnackbar(activity.snackbarContainer, getString(R.string.left_guild), HabiticaSnackbar.SnackbarDisplayType.NORMAL) } } } @@ -88,7 +88,7 @@ class GuildDetailFragment : BaseFragment() { viewModel?.joinGroup { val activity = activity as? MainActivity if (activity != null) { - HabiticaSnackbar.showSnackbar(activity.floatingMenuWrapper, getString(R.string.joined_guild), HabiticaSnackbar.SnackbarDisplayType.NORMAL) + HabiticaSnackbar.showSnackbar(activity.snackbarContainer, getString(R.string.joined_guild), HabiticaSnackbar.SnackbarDisplayType.NORMAL) } } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/InboxMessageListFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/InboxMessageListFragment.kt index 1afbe70ad..d05dc7a5c 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/InboxMessageListFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/InboxMessageListFragment.kt @@ -143,7 +143,7 @@ class InboxMessageListFragment : BaseMainFragment(), androidx.swiperefreshlayout clipMan?.primaryClip = messageText val activity = getActivity() as? MainActivity if (activity != null) { - showSnackbar(activity.floatingMenuWrapper, getString(R.string.chat_message_copied), HabiticaSnackbar.SnackbarDisplayType.NORMAL) + showSnackbar(activity.snackbarContainer, getString(R.string.chat_message_copied), HabiticaSnackbar.SnackbarDisplayType.NORMAL) } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TaskRecyclerViewFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TaskRecyclerViewFragment.kt index 28bbd6056..f1d8dc51d 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TaskRecyclerViewFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TaskRecyclerViewFragment.kt @@ -99,7 +99,7 @@ open class TaskRecyclerViewFragment : BaseFragment(), androidx.swiperefreshlayou .doOnNext { soundManager.loadAndPlayAudio(SoundManager.SoundTodo) } .flatMap { taskRepository.taskChecked(user, it.first, it.second == TaskDirection.UP, false) { _ -> (activity as? MainActivity)?.let { activity -> - HabiticaSnackbar.showSnackbar(activity.floatingMenuWrapper, null, getString(R.string.notification_purchase_reward), + HabiticaSnackbar.showSnackbar(activity.snackbarContainer, null, getString(R.string.notification_purchase_reward), BitmapDrawable(resources, HabiticaIconsHelper.imageOfGold()), ContextCompat.getColor(activity, R.color.yellow_10), "-" + it.first.value.toInt(), diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TasksFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TasksFragment.kt index 4dc250856..6a216a776 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TasksFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TasksFragment.kt @@ -5,7 +5,6 @@ import android.content.Intent import android.os.Bundle import android.view.* import androidx.fragment.app.FragmentPagerAdapter -import com.github.clans.fab.FloatingActionButton import com.github.clans.fab.FloatingActionMenu import com.habitrpg.android.habitica.HabiticaBaseApplication import com.habitrpg.android.habitica.R @@ -44,32 +43,13 @@ class TasksFragment : BaseMainFragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - this.usesTabLayout = false this.usesBottomNavigation = true this.displayingTaskForm = false super.onCreateView(inflater, container, savedInstanceState) val v = inflater.inflate(R.layout.fragment_viewpager, container, false) - viewPager = v.findViewById(R.id.viewPager) - val view = inflater.inflate(R.layout.floating_menu_tasks, floatingMenuWrapper, true) - floatingMenu = if (FloatingActionMenu::class.java == view.javaClass) { - view as? FloatingActionMenu - } else { - val frame = view as? ViewGroup - frame?.findViewById(R.id.fab_menu) - } - val habitFab = floatingMenu?.findViewById(R.id.fab_new_habit) - habitFab?.setOnClickListener { openNewTaskActivity(Task.TYPE_HABIT) } - val dailyFab = floatingMenu?.findViewById(R.id.fab_new_daily) - dailyFab?.setOnClickListener { openNewTaskActivity(Task.TYPE_DAILY) } - val todoFab = floatingMenu?.findViewById(R.id.fab_new_todo) - todoFab?.setOnClickListener { openNewTaskActivity(Task.TYPE_TODO) } - val rewardFab = floatingMenu?.findViewById(R.id.fab_new_reward) - rewardFab?.setOnClickListener { openNewTaskActivity(Task.TYPE_REWARD) } - floatingMenu?.setOnMenuButtonLongClickListener { this.onFloatingMenuLongClicked() } - loadTaskLists() return v @@ -78,33 +58,25 @@ class TasksFragment : BaseMainFragment() { override fun onResume() { super.onResume() - bottomNavigation?.setBadgesHideWhenActive(true) - bottomNavigation?.setOnTabSelectListener { tabId -> - when (tabId) { - R.id.tab_habits -> viewPager?.currentItem = 0 - R.id.tab_dailies -> viewPager?.currentItem = 1 - R.id.tab_todos -> viewPager?.currentItem = 2 - R.id.tab_rewards -> viewPager?.currentItem = 3 + bottomNavigation?.onTabSelectedListener = { + when (it) { + Task.TYPE_HABIT -> viewPager?.currentItem = 0 + Task.TYPE_DAILY -> viewPager?.currentItem = 1 + Task.TYPE_TODO -> viewPager?.currentItem = 2 + Task.TYPE_REWARD -> viewPager?.currentItem = 3 } updateBottomBarBadges() } + bottomNavigation?.onAddListener = { + openNewTaskActivity(it) + } } override fun onDestroy() { tagRepository.close() - bottomNavigation?.removeOnTabSelectListener() super.onDestroy() } - private fun onFloatingMenuLongClicked(): Boolean { - val currentFragment = activeFragment - if (currentFragment != null) { - val className = currentFragment.className - openNewTaskActivity(className) - } - return true - } - override fun injectFragment(component: UserComponent) { component.inject(this) } @@ -145,7 +117,6 @@ class TasksFragment : BaseMainFragment() { } } dialog.setListener(object : TaskFilterDialog.OnFilterCompletedListener { - override fun onFilterCompleted(activeTaskFilter: String?, activeTags: MutableList) { if (viewFragmentsDictionary == null) { return @@ -204,7 +175,7 @@ class TasksFragment : BaseMainFragment() { } override fun onPageSelected(position: Int) { - bottomNavigation?.selectTabAtPosition(position) + bottomNavigation?.selectedPosition = position updateFilterIcon() } @@ -233,7 +204,7 @@ class TasksFragment : BaseMainFragment() { if (bottomNavigation == null) { return } - tutorialRepository.getTutorialSteps(Arrays.asList("habits", "dailies", "todos", "rewards")).subscribe(Consumer { tutorialSteps -> + compositeSubscription.add(tutorialRepository.getTutorialSteps(Arrays.asList("habits", "dailies", "todos", "rewards")).subscribe(Consumer { tutorialSteps -> val activeTutorialFragments = ArrayList() for (step in tutorialSteps) { var id = -1 @@ -256,13 +227,13 @@ class TasksFragment : BaseMainFragment() { } else -> "" } - val tab = bottomNavigation?.getTabWithId(id) + /*val tab = bottomNavigation?.id(id) if (step.shouldDisplay()) { tab?.setBadgeCount(1) activeTutorialFragments.add(taskType) } else { tab?.removeBadge() - } + }*/ } if (activeTutorialFragments.size == 1) { val fragment = viewFragmentsDictionary?.get(indexForTaskType(activeTutorialFragments[0])) @@ -273,7 +244,7 @@ class TasksFragment : BaseMainFragment() { } } } - }, RxErrorHandler.handleEmptyError()) + }, RxErrorHandler.handleEmptyError())) } // endregion diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/FloatingActionMenuBehavior.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/FloatingActionMenuBehavior.java index d33c2202b..97998db3d 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/FloatingActionMenuBehavior.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/FloatingActionMenuBehavior.java @@ -3,15 +3,16 @@ package com.habitrpg.android.habitica.ui.helpers; // https://gist.github.com/lodlock/e3cd12130bad70a098db import android.content.Context; -import androidx.annotation.Nullable; -import androidx.coordinatorlayout.widget.CoordinatorLayout; -import com.google.android.material.snackbar.Snackbar; -import androidx.core.view.ViewCompat; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; +import androidx.annotation.Nullable; +import androidx.coordinatorlayout.widget.CoordinatorLayout; +import androidx.core.view.ViewCompat; + import com.github.clans.fab.FloatingActionMenu; +import com.google.android.material.snackbar.Snackbar; import com.habitrpg.android.habitica.R; import java.util.List; @@ -59,7 +60,7 @@ public class FloatingActionMenuBehavior extends CoordinatorLayout.Behavior { .translationY(translationY) .setListener(null); } else { - ViewCompat.setTranslationY(child, translationY); + child.setTranslationY(translationY); } this.mTranslationY = translationY; @@ -75,7 +76,7 @@ public class FloatingActionMenuBehavior extends CoordinatorLayout.Behavior { for (int z = dependencies.size(); i < z; ++i) { View view = (View) dependencies.get(i); if (view instanceof Snackbar.SnackbarLayout && parent.doViewsOverlap(child, view)) { - minOffset = Math.min(minOffset, ViewCompat.getTranslationY(view) - (float) view.getHeight()); + minOffset = Math.min(minOffset, view.getTranslationY() - (float) view.getHeight()); } } @@ -84,7 +85,7 @@ public class FloatingActionMenuBehavior extends CoordinatorLayout.Behavior { @Override public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) { - ViewGroup fabContainer = child.findViewById(R.id.floating_menu_wrapper); + ViewGroup fabContainer = child.findViewById(R.id.snackbar_container); if (fabContainer.getChildCount() > 0) { if (fabContainer.getChildAt(0).getClass().equals(FloatingActionMenu.class)) { fab = (FloatingActionMenu) fabContainer.getChildAt(0); diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/HabiticaSnackbar.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/HabiticaSnackbar.kt index 1e9d2d8f2..9a5016afb 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/HabiticaSnackbar.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/HabiticaSnackbar.kt @@ -1,18 +1,17 @@ package com.habitrpg.android.habitica.ui.views import android.graphics.drawable.Drawable -import androidx.annotation.ColorInt -import com.google.android.material.snackbar.BaseTransientBottomBar -import com.google.android.material.snackbar.Snackbar -import androidx.core.content.ContextCompat -import androidx.core.view.ViewCompat import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ImageView import android.widget.LinearLayout import android.widget.TextView - +import androidx.annotation.ColorInt +import androidx.core.content.ContextCompat +import androidx.core.view.ViewCompat +import com.google.android.material.snackbar.BaseTransientBottomBar +import com.google.android.material.snackbar.Snackbar import com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.ui.helpers.NavbarUtils @@ -87,13 +86,17 @@ private constructor(parent: ViewGroup, content: View, callback: ContentViewCallb override fun animateContentIn(delay: Int, duration: Int) { content.scaleY = 0f + content.scaleX = 0f ViewCompat.animate(content).scaleY(1f).setDuration(duration.toLong()).startDelay = delay.toLong() + ViewCompat.animate(content).scaleX(1f).setDuration(duration.toLong()).startDelay = delay.toLong() ViewCompat.animate(content).alpha(1f).setDuration(duration.toLong()).startDelay = delay.toLong() } override fun animateContentOut(delay: Int, duration: Int) { content.scaleY = 1f + content.scaleX = 1f ViewCompat.animate(content).scaleY(0f).setDuration(duration.toLong()).startDelay = delay.toLong() + ViewCompat.animate(content).scaleX(0f).setDuration(duration.toLong()).startDelay = delay.toLong() ViewCompat.animate(content).alpha(0f).setDuration(duration.toLong()).startDelay = delay.toLong() } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/BadgeCircle.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/BadgeCircle.java deleted file mode 100644 index 095efa2a0..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/BadgeCircle.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * BottomBar library for Android - * Copyright (c) 2016 Iiro Krankka (http://github.com/roughike). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.habitrpg.android.habitica.ui.views.bottombar; - -import android.graphics.drawable.ShapeDrawable; -import android.graphics.drawable.shapes.OvalShape; -import androidx.annotation.ColorInt; -import androidx.annotation.IntRange; -import androidx.annotation.NonNull; - -/* - * BottomBar library for Android - * Copyright (c) 2016 Iiro Krankka (http://github.com/roughike). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -class BadgeCircle { - /** - * Creates a new circle for the Badge background. - * - * @param size the width and height for the circle - * @param color the activeIconColor for the circle - * @return a nice and adorable circle. - */ - @NonNull - static ShapeDrawable make(@IntRange(from = 0) int size, @ColorInt int color) { - ShapeDrawable indicator = new ShapeDrawable(new OvalShape()); - indicator.setIntrinsicWidth(size); - indicator.setIntrinsicHeight(size); - indicator.getPaint().setColor(color); - return indicator; - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/BadgeContainer.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/BadgeContainer.java deleted file mode 100644 index e7551c4be..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/BadgeContainer.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.habitrpg.android.habitica.ui.views.bottombar; - -import android.content.Context; -import androidx.annotation.NonNull; -import android.widget.FrameLayout; - -/** - * Created by iiro on 29.8.2016. - */ -public class BadgeContainer extends FrameLayout { - public BadgeContainer(@NonNull Context context) { - super(context); - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/BatchTabPropertyApplier.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/BatchTabPropertyApplier.java deleted file mode 100644 index 3a3849ebe..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/BatchTabPropertyApplier.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.habitrpg.android.habitica.ui.views.bottombar; - -import androidx.annotation.NonNull; - -class BatchTabPropertyApplier { - private final BottomBar bottomBar; - - interface TabPropertyUpdater { - void update(BottomBarTab tab); - } - - BatchTabPropertyApplier(@NonNull BottomBar bottomBar) { - this.bottomBar = bottomBar; - } - - void applyToAllTabs(@NonNull TabPropertyUpdater propertyUpdater) { - int tabCount = bottomBar.getTabCount(); - - if (tabCount > 0) { - for (int i = 0; i < tabCount; i++) { - BottomBarTab tab = bottomBar.getTabAtPosition(i); - propertyUpdater.update(tab); - } - } - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/BottomBar.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/BottomBar.java deleted file mode 100644 index bedf4f764..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/BottomBar.java +++ /dev/null @@ -1,1081 +0,0 @@ -package com.habitrpg.android.habitica.ui.views.bottombar; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.annotation.TargetApi; -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Color; -import android.graphics.Typeface; -import android.graphics.drawable.ColorDrawable; -import android.graphics.drawable.Drawable; -import android.os.Build; -import android.os.Bundle; -import android.os.Parcelable; -import androidx.annotation.ColorInt; -import androidx.annotation.IdRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; -import androidx.annotation.VisibleForTesting; -import androidx.annotation.XmlRes; -import androidx.coordinatorlayout.widget.CoordinatorLayout; -import androidx.core.content.ContextCompat; -import androidx.core.view.ViewCompat; -import androidx.core.view.ViewPropertyAnimatorListenerAdapter; - -import android.util.AttributeSet; -import android.util.Log; -import android.view.View; -import android.view.ViewAnimationUtils; -import android.view.ViewGroup; -import android.view.ViewOutlineProvider; -import android.view.ViewParent; -import android.widget.LinearLayout; -import android.widget.TextView; -import android.widget.Toast; - -import com.habitrpg.android.habitica.R; -import com.habitrpg.android.habitica.ui.helpers.NavbarUtils; - -import java.util.List; - -/* - * BottomBar library for Android - * Copyright (c) 2016 Iiro Krankka (http://github.com/roughike). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -public class BottomBar extends LinearLayout implements View.OnClickListener, View.OnLongClickListener { - private static final String STATE_CURRENT_SELECTED_TAB = "STATE_CURRENT_SELECTED_TAB"; - private static final float DEFAULT_INACTIVE_SHIFTING_TAB_ALPHA = 0.6f; - // Behaviors - private static final int BEHAVIOR_NONE = 0; - private static final int BEHAVIOR_SHIFTING = 1; - private static final int BEHAVIOR_SHY = 2; - private static final int BEHAVIOR_DRAW_UNDER_NAV = 4; - private static final int BEHAVIOR_ICONS_ONLY = 8; - - private BatchTabPropertyApplier batchPropertyApplier; - private int primaryColor; - private int screenWidth; - private int tenDp; - private int maxFixedItemWidth; - - // XML Attributes - private int tabXmlResource; - private boolean isTabletMode; - private int behaviors; - private float inActiveTabAlpha; - private float activeTabAlpha; - private int inActiveTabColor; - private int activeTabColor; - private int badgeBackgroundColor; - private boolean hideBadgeWhenActive; - private boolean longPressHintsEnabled; - private int titleTextAppearance; - private Typeface titleTypeFace; - private boolean showShadow; - private float shadowElevation; - private View shadowView; - - private View backgroundOverlay; - private ViewGroup outerContainer; - private ViewGroup tabContainer; - - private int defaultBackgroundColor = Color.WHITE; - private int currentBackgroundColor; - private int currentTabPosition; - - private int inActiveShiftingItemWidth; - private int activeShiftingItemWidth; - - @Nullable - private TabSelectionInterceptor tabSelectionInterceptor; - - @Nullable - private OnTabSelectListener onTabSelectListener; - - @Nullable - private OnTabReselectListener onTabReselectListener; - - private boolean isComingFromRestoredState; - private boolean ignoreTabReselectionListener; - - private ShySettings shySettings; - private boolean shyHeightAlreadyCalculated; - private boolean navBarAccountedHeightCalculated; - - private BottomBarTab[] currentTabs; - - public BottomBar(Context context) { - this(context, null); - } - - public BottomBar(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public BottomBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - init(context, attrs, defStyleAttr, 0); - } - - @RequiresApi(Build.VERSION_CODES.LOLLIPOP) - public BottomBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - init(context, attrs, defStyleAttr, defStyleRes); - } - - private void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - batchPropertyApplier = new BatchTabPropertyApplier(this); - - populateAttributes(context, attrs, defStyleAttr, defStyleRes); - initializeViews(); - determineInitialBackgroundColor(); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - init21(context); - } - - if (tabXmlResource != 0) { - setItems(tabXmlResource); - } - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - - // This is so that in Pre-Lollipop devices there is a shadow BUT without pushing the content - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP && showShadow && shadowView != null) { - shadowView.setVisibility(VISIBLE); - ViewGroup.LayoutParams params = getLayoutParams(); - if (params instanceof MarginLayoutParams) { - MarginLayoutParams layoutParams = (MarginLayoutParams) params; - final int shadowHeight = getResources().getDimensionPixelSize(R.dimen.bb_fake_shadow_height); - - layoutParams.setMargins(layoutParams.leftMargin, - layoutParams.topMargin - shadowHeight, - layoutParams.rightMargin, - layoutParams.bottomMargin); - setLayoutParams(params); - } - } - } - - @RequiresApi(Build.VERSION_CODES.LOLLIPOP) - private void init21(Context context) { - if (showShadow) { - shadowElevation = getElevation(); - shadowElevation = shadowElevation > 0 - ? shadowElevation - : getResources().getDimensionPixelSize(R.dimen.bb_default_elevation); - setElevation(MiscUtils.dpToPixel(context, shadowElevation)); - setOutlineProvider(ViewOutlineProvider.BOUNDS); - } - } - - private void populateAttributes(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - primaryColor = MiscUtils.getColor(getContext(), R.attr.colorPrimary); - screenWidth = MiscUtils.getScreenWidth(getContext()); - tenDp = MiscUtils.dpToPixel(getContext(), 10); - maxFixedItemWidth = MiscUtils.dpToPixel(getContext(), 168); - - TypedArray ta = context.getTheme() - .obtainStyledAttributes(attrs, R.styleable.BottomBar, defStyleAttr, defStyleRes); - - try { - tabXmlResource = ta.getResourceId(R.styleable.BottomBar_bb_tabXmlResource, 0); - isTabletMode = ta.getBoolean(R.styleable.BottomBar_bb_tabletMode, false); - behaviors = ta.getInteger(R.styleable.BottomBar_bb_behavior, BEHAVIOR_NONE); - inActiveTabAlpha = ta.getFloat(R.styleable.BottomBar_bb_inActiveTabAlpha, - isShiftingMode() ? DEFAULT_INACTIVE_SHIFTING_TAB_ALPHA : 1); - activeTabAlpha = ta.getFloat(R.styleable.BottomBar_bb_activeTabAlpha, 1); - - @ColorInt - int defaultInActiveColor = isShiftingMode() ? - Color.WHITE : ContextCompat.getColor(context, R.color.bb_inActiveBottomBarItemColor); - int defaultActiveColor = isShiftingMode() ? Color.WHITE : primaryColor; - - longPressHintsEnabled = ta.getBoolean(R.styleable.BottomBar_bb_longPressHintsEnabled, true); - inActiveTabColor = ta.getColor(R.styleable.BottomBar_bb_inActiveTabColor, defaultInActiveColor); - activeTabColor = ta.getColor(R.styleable.BottomBar_bb_activeTabColor, defaultActiveColor); - badgeBackgroundColor = ta.getColor(R.styleable.BottomBar_bb_badgeBackgroundColor, Color.RED); - hideBadgeWhenActive = ta.getBoolean(R.styleable.BottomBar_bb_badgesHideWhenActive, true); - titleTextAppearance = ta.getResourceId(R.styleable.BottomBar_bb_titleTextAppearance, 0); - titleTypeFace = getTypeFaceFromAsset(ta.getString(R.styleable.BottomBar_bb_titleTypeFace)); - showShadow = ta.getBoolean(R.styleable.BottomBar_bb_showShadow, true); - } finally { - ta.recycle(); - } - } - - private boolean isShiftingMode() { - return !isTabletMode && hasBehavior(BEHAVIOR_SHIFTING); - } - - private boolean drawUnderNav() { - return !isTabletMode - && hasBehavior(BEHAVIOR_DRAW_UNDER_NAV) - && NavbarUtils.INSTANCE.shouldDrawBehindNavbar(getContext()); - } - - boolean isShy() { - return !isTabletMode && hasBehavior(BEHAVIOR_SHY); - } - - boolean isShyHeightAlreadyCalculated() { - return shyHeightAlreadyCalculated; - } - - private boolean isIconsOnlyMode() { - return !isTabletMode && hasBehavior(BEHAVIOR_ICONS_ONLY); - } - - private boolean hasBehavior(int behavior) { - return (behaviors | behavior) == behaviors; - } - - private Typeface getTypeFaceFromAsset(String fontPath) { - if (fontPath != null) { - return Typeface.createFromAsset( - getContext().getAssets(), fontPath); - } - - return null; - } - - private void initializeViews() { - int width = isTabletMode ? LayoutParams.WRAP_CONTENT : LayoutParams.MATCH_PARENT; - int height = isTabletMode ? LayoutParams.MATCH_PARENT : LayoutParams.WRAP_CONTENT; - LayoutParams params = new LayoutParams(width, height); - - setLayoutParams(params); - setOrientation(isTabletMode ? HORIZONTAL : VERTICAL); - - View rootView = inflate(getContext(), - isTabletMode ? R.layout.bb_bottom_bar_item_container_tablet : R.layout.bb_bottom_bar_item_container, this); - rootView.setLayoutParams(params); - - backgroundOverlay = rootView.findViewById(R.id.bb_bottom_bar_background_overlay); - outerContainer = rootView.findViewById(R.id.bb_bottom_bar_outer_container); - tabContainer = rootView.findViewById(R.id.bb_bottom_bar_item_container); - shadowView = findViewById(R.id.bb_bottom_bar_shadow); - } - - private void determineInitialBackgroundColor() { - if (isShiftingMode()) { - defaultBackgroundColor = primaryColor; - } - - Drawable userDefinedBackground = getBackground(); - - boolean userHasDefinedBackgroundColor = userDefinedBackground != null - && userDefinedBackground instanceof ColorDrawable; - - if (userHasDefinedBackgroundColor) { - defaultBackgroundColor = ((ColorDrawable) userDefinedBackground).getColor(); - setBackgroundColor(Color.TRANSPARENT); - } - } - - /** - * Set the items for the BottomBar from XML Resource. - */ - public void setItems(@XmlRes int xmlRes) { - setItems(xmlRes, null); - } - - /** - * Set the item for the BottomBar from XML Resource with a default configuration - * for each tab. - */ - public void setItems(@XmlRes int xmlRes, BottomBarTab.Config defaultTabConfig) { - if (xmlRes == 0) { - throw new RuntimeException("No items specified for the BottomBar!"); - } - - if (defaultTabConfig == null) { - defaultTabConfig = getTabConfig(); - } - - TabParser parser = new TabParser(getContext(), defaultTabConfig, xmlRes); - updateItems(parser.parseTabs()); - } - - private BottomBarTab.Config getTabConfig() { - return new BottomBarTab.Config.Builder() - .inActiveTabAlpha(inActiveTabAlpha) - .activeTabAlpha(activeTabAlpha) - .inActiveTabColor(inActiveTabColor) - .activeTabColor(activeTabColor) - .barColorWhenSelected(defaultBackgroundColor) - .badgeBackgroundColor(badgeBackgroundColor) - .hideBadgeWhenSelected(hideBadgeWhenActive) - .titleTextAppearance(titleTextAppearance) - .titleTypeFace(titleTypeFace) - .build(); - } - - private void updateItems(final List bottomBarItems) { - tabContainer.removeAllViews(); - - int index = 0; - int biggestWidth = 0; - - BottomBarTab[] viewsToAdd = new BottomBarTab[bottomBarItems.size()]; - - for (BottomBarTab bottomBarTab : bottomBarItems) { - BottomBarTab.Type type; - - if (isShiftingMode()) { - type = BottomBarTab.Type.SHIFTING; - } else if (isTabletMode) { - type = BottomBarTab.Type.TABLET; - } else { - type = BottomBarTab.Type.FIXED; - } - - if (isIconsOnlyMode()) { - bottomBarTab.setIsTitleless(true); - } - - bottomBarTab.setType(type); - bottomBarTab.prepareLayout(); - - if (index == currentTabPosition) { - bottomBarTab.select(false); - - handleBackgroundColorChange(bottomBarTab, false); - } else { - bottomBarTab.deselect(false); - } - - if (!isTabletMode) { - if (bottomBarTab.getWidth() > biggestWidth) { - biggestWidth = bottomBarTab.getWidth(); - } - - viewsToAdd[index] = bottomBarTab; - } else { - tabContainer.addView(bottomBarTab); - } - - bottomBarTab.setOnClickListener(this); - bottomBarTab.setOnLongClickListener(this); - index++; - } - - currentTabs = viewsToAdd; - - if (!isTabletMode) { - resizeTabsToCorrectSizes(viewsToAdd); - } - } - - private void resizeTabsToCorrectSizes(BottomBarTab[] tabsToAdd) { - int viewWidth = MiscUtils.pixelToDp(getContext(), getWidth()); - - if (viewWidth <= 0 || viewWidth > screenWidth) { - viewWidth = screenWidth; - } - - int proposedItemWidth = Math.min( - MiscUtils.dpToPixel(getContext(), viewWidth / tabsToAdd.length), - maxFixedItemWidth - ); - - inActiveShiftingItemWidth = (int) (proposedItemWidth * 0.9); - activeShiftingItemWidth = (int) (proposedItemWidth + (proposedItemWidth * ((tabsToAdd.length - 1) * 0.1))); - int height = Math.round(getContext().getResources() - .getDimension(R.dimen.bb_height)); - - for (BottomBarTab tabView : tabsToAdd) { - ViewGroup.LayoutParams params = tabView.getLayoutParams(); - params.height = height; - - if (isShiftingMode()) { - if (tabView.isActive()) { - params.width = activeShiftingItemWidth; - } else { - params.width = inActiveShiftingItemWidth; - } - } else { - params.width = proposedItemWidth; - } - - if (tabView.getParent() == null) { - tabContainer.addView(tabView); - } - - tabView.setLayoutParams(params); - } - } - - /** - * Returns the settings specific for a shy BottomBar. - * - * @throws UnsupportedOperationException, if this BottomBar is not shy. - */ - public ShySettings getShySettings() { - if (!isShy()) { - Log.e("BottomBar", "Tried to get shy settings for a BottomBar " + - "that is not shy."); - } - - if (shySettings == null) { - shySettings = new ShySettings(this); - } - - return shySettings; - } - - /** - * Set a listener that gets fired when the selected {@link BottomBarTab} is about to change. - * - * @param interceptor a listener for potentially interrupting changes in tab selection. - */ - public void setTabSelectionInterceptor(@NonNull TabSelectionInterceptor interceptor) { - tabSelectionInterceptor = interceptor; - } - - /** - * Removes the current {@link TabSelectionInterceptor} listener - */ - public void removeOverrideTabSelectionListener() { - tabSelectionInterceptor = null; - } - - /** - * Set a listener that gets fired when the selected {@link BottomBarTab} changes. - *

- * Note: Will be immediately called for the currently selected tab - * once when set. - * - * @param listener a listener for monitoring changes in tab selection. - */ - public void setOnTabSelectListener(@NonNull OnTabSelectListener listener) { - setOnTabSelectListener(listener, true); - } - - /** - * Set a listener that gets fired when the selected {@link BottomBarTab} changes. - *

- * If {@code shouldFireInitially} is set to false, this listener isn't fired straight away - * it's set, but you'll get all events normally for consecutive tab selection changes. - * - * @param listener a listener for monitoring changes in tab selection. - * @param shouldFireInitially whether the listener should be fired the first time it's set. - */ - public void setOnTabSelectListener(@NonNull OnTabSelectListener listener, boolean shouldFireInitially) { - onTabSelectListener = listener; - - if (shouldFireInitially && getTabCount() > 0) { - listener.onTabSelected(getCurrentTabId()); - } - } - - /** - * Removes the current {@link OnTabSelectListener} listener - */ - public void removeOnTabSelectListener() { - onTabSelectListener = null; - } - - /** - * Set a listener that gets fired when a currently selected {@link BottomBarTab} is clicked. - * - * @param listener a listener for handling tab reselections. - */ - public void setOnTabReselectListener(@NonNull OnTabReselectListener listener) { - onTabReselectListener = listener; - } - - /** - * Removes the current {@link OnTabReselectListener} listener - */ - public void removeOnTabReselectListener() { - onTabReselectListener = null; - } - - /** - * Set the default selected to be the tab with the corresponding tab id. - * By default, the first tab in the container is the default tab. - */ - public void setDefaultTab(@IdRes int defaultTabId) { - int defaultTabPosition = findPositionForTabWithId(defaultTabId); - setDefaultTabPosition(defaultTabPosition); - } - - /** - * Sets the default tab for this BottomBar that is shown until the user changes - * the selection. - * - * @param defaultTabPosition the default tab position. - */ - public void setDefaultTabPosition(int defaultTabPosition) { - if (isComingFromRestoredState) return; - - selectTabAtPosition(defaultTabPosition); - } - - /** - * Select the tab with the corresponding id. - */ - public void selectTabWithId(@IdRes int tabResId) { - int tabPosition = findPositionForTabWithId(tabResId); - selectTabAtPosition(tabPosition); - } - - /** - * Select a tab at the specified position. - * - * @param position the position to select. - */ - public void selectTabAtPosition(int position) { - selectTabAtPosition(position, false); - } - - /** - * Select a tab at the specified position. - * - * @param position the position to select. - * @param animate should the tab change be animated or not. - */ - public void selectTabAtPosition(int position, boolean animate) { - if (position > getTabCount() - 1 || position < 0) { - throw new IndexOutOfBoundsException("Can't select tab at position " + - position + ". This BottomBar has no items at that position."); - } - - BottomBarTab oldTab = getCurrentTab(); - BottomBarTab newTab = getTabAtPosition(position); - - oldTab.deselect(animate); - newTab.select(animate); - - updateSelectedTab(position); - shiftingMagic(oldTab, newTab, animate); - handleBackgroundColorChange(newTab, animate); - } - - public int getTabCount() { - return tabContainer.getChildCount(); - } - - /** - * Get the currently selected tab. - */ - public BottomBarTab getCurrentTab() { - return getTabAtPosition(getCurrentTabPosition()); - } - - /** - * Get the tab at the specified position. - */ - public BottomBarTab getTabAtPosition(int position) { - View child = tabContainer.getChildAt(position); - - if (child instanceof BadgeContainer) { - return findTabInLayout((BadgeContainer) child); - } - - return (BottomBarTab) child; - } - - /** - * Get the resource id for the currently selected tab. - */ - @IdRes - public int getCurrentTabId() { - return getCurrentTab().getId(); - } - - /** - * Get the currently selected tab position. - */ - public int getCurrentTabPosition() { - return currentTabPosition; - } - - /** - * Find the tabs' position in the container by id. - */ - public int findPositionForTabWithId(@IdRes int tabId) { - return getTabWithId(tabId).getIndexInTabContainer(); - } - - /** - * Find a BottomBarTab with the corresponding id. - */ - public BottomBarTab getTabWithId(@IdRes int tabId) { - return (BottomBarTab) tabContainer.findViewById(tabId); - } - - /** - * Controls whether the long pressed tab title should be displayed in - * a helpful Toast if the title is not currently visible. - * - * @param enabled true if toasts should be shown to indicate the title - * of a long pressed tab, false otherwise. - */ - public void setLongPressHintsEnabled(boolean enabled) { - longPressHintsEnabled = enabled; - } - - /** - * Set alpha value used for inactive BottomBarTabs. - */ - public void setInActiveTabAlpha(float alpha) { - inActiveTabAlpha = alpha; - - batchPropertyApplier.applyToAllTabs(new BatchTabPropertyApplier.TabPropertyUpdater() { - @Override - public void update(BottomBarTab tab) { - tab.setInActiveAlpha(inActiveTabAlpha); - } - }); - } - - /** - * Set alpha value used for active BottomBarTabs. - */ - public void setActiveTabAlpha(float alpha) { - activeTabAlpha = alpha; - - batchPropertyApplier.applyToAllTabs(new BatchTabPropertyApplier.TabPropertyUpdater() { - @Override - public void update(BottomBarTab tab) { - tab.setActiveAlpha(activeTabAlpha); - } - }); - } - - public void setInActiveTabColor(@ColorInt int color) { - inActiveTabColor = color; - - batchPropertyApplier.applyToAllTabs(new BatchTabPropertyApplier.TabPropertyUpdater() { - @Override - public void update(BottomBarTab tab) { - tab.setInActiveColor(inActiveTabColor); - } - }); - } - - /** - * Set active color used for selected BottomBarTabs. - */ - public void setActiveTabColor(@ColorInt int color) { - activeTabColor = color; - - batchPropertyApplier.applyToAllTabs(new BatchTabPropertyApplier.TabPropertyUpdater() { - @Override - public void update(BottomBarTab tab) { - tab.setActiveColor(activeTabColor); - } - }); - } - - /** - * Set background color for the badge. - */ - public void setBadgeBackgroundColor(@ColorInt int color) { - badgeBackgroundColor = color; - - batchPropertyApplier.applyToAllTabs(new BatchTabPropertyApplier.TabPropertyUpdater() { - @Override - public void update(BottomBarTab tab) { - tab.setBadgeBackgroundColor(badgeBackgroundColor); - } - }); - } - - /** - * Controls whether the badge (if any) for active tabs - * should be hidden or not. - */ - public void setBadgesHideWhenActive(final boolean hideWhenSelected) { - hideBadgeWhenActive = hideWhenSelected; - batchPropertyApplier.applyToAllTabs(new BatchTabPropertyApplier.TabPropertyUpdater() { - @Override - public void update(BottomBarTab tab) { - tab.setBadgeHidesWhenActive(hideWhenSelected); - } - }); - } - - /** - * Set custom text apperance for all BottomBarTabs. - */ - public void setTabTitleTextAppearance(int textAppearance) { - titleTextAppearance = textAppearance; - - batchPropertyApplier.applyToAllTabs(new BatchTabPropertyApplier.TabPropertyUpdater() { - @Override - public void update(BottomBarTab tab) { - tab.setTitleTextAppearance(titleTextAppearance); - } - }); - } - - /** - * Set a custom typeface for all tab's titles. - * - * @param fontPath path for your custom font file, such as fonts/MySuperDuperFont.ttf. - * In that case your font path would look like src/main/assets/fonts/MySuperDuperFont.ttf, - * but you only need to provide fonts/MySuperDuperFont.ttf, as the asset folder - * will be auto-filled for you. - */ - public void setTabTitleTypeface(String fontPath) { - Typeface actualTypeface = getTypeFaceFromAsset(fontPath); - setTabTitleTypeface(actualTypeface); - } - - /** - * Set a custom typeface for all tab's titles. - */ - public void setTabTitleTypeface(Typeface typeface) { - titleTypeFace = typeface; - - batchPropertyApplier.applyToAllTabs(new BatchTabPropertyApplier.TabPropertyUpdater() { - @Override - public void update(BottomBarTab tab) { - tab.setTitleTypeface(titleTypeFace); - } - }); - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - - if (changed) { - if (!isTabletMode) { - resizeTabsToCorrectSizes(currentTabs); - } - - updateTitleBottomPadding(); - - if (isShy()) { - initializeShyBehavior(); - } - - if (drawUnderNav()) { - resizeForDrawingUnderNavbar(); - } - } - } - - private void updateTitleBottomPadding() { - if (isIconsOnlyMode()) { - return; - } - - int tabCount = getTabCount(); - - if (tabContainer == null || tabCount == 0 || !isShiftingMode()) { - return; - } - - for (int i = 0; i < tabCount; i++) { - BottomBarTab tab = getTabAtPosition(i); - TextView title = tab.getTitleView(); - - if (title == null) { - continue; - } - - int baseline = title.getBaseline(); - int height = title.getHeight(); - int paddingInsideTitle = height - baseline; - int missingPadding = tenDp - paddingInsideTitle; - - if (missingPadding > 0) { - title.setPadding(title.getPaddingLeft(), title.getPaddingTop(), - title.getPaddingRight(), missingPadding + title.getPaddingBottom()); - } - } - } - - private void initializeShyBehavior() { - ViewParent parent = getParent(); - - boolean hasAbusiveParent = parent != null - && parent instanceof CoordinatorLayout; - - if (!hasAbusiveParent) { - throw new RuntimeException("In order to have shy behavior, the " + - "BottomBar must be a direct child of a CoordinatorLayout."); - } - - if (!shyHeightAlreadyCalculated) { - int height = getHeight(); - - if (height != 0) { - updateShyHeight(height); - getShySettings().shyHeightCalculated(); - shyHeightAlreadyCalculated = true; - } - } - } - - private void updateShyHeight(int height) { - ((CoordinatorLayout.LayoutParams) getLayoutParams()) - .setBehavior(new BottomNavigationBehavior(height, 0, false)); - } - - private void resizeForDrawingUnderNavbar() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - int currentHeight = getHeight(); - - if (currentHeight != 0 && !navBarAccountedHeightCalculated) { - navBarAccountedHeightCalculated = true; - tabContainer.getLayoutParams().height = currentHeight; - - int navbarHeight = NavbarUtils.INSTANCE.getNavbarHeight(getContext()); - int finalHeight = currentHeight + navbarHeight; - getLayoutParams().height = finalHeight; - - if (isShy()) { - updateShyHeight(finalHeight); - } - } - } - } - - @Override - public Parcelable onSaveInstanceState() { - Bundle bundle = saveState(); - bundle.putParcelable("superstate", super.onSaveInstanceState()); - return bundle; - } - - @VisibleForTesting - Bundle saveState() { - Bundle outState = new Bundle(); - outState.putInt(STATE_CURRENT_SELECTED_TAB, currentTabPosition); - - return outState; - } - - @Override - public void onRestoreInstanceState(Parcelable state) { - if (state instanceof Bundle) { - Bundle bundle = (Bundle) state; - restoreState(bundle); - - state = bundle.getParcelable("superstate"); - } - super.onRestoreInstanceState(state); - } - - @VisibleForTesting - void restoreState(Bundle savedInstanceState) { - if (savedInstanceState != null) { - isComingFromRestoredState = true; - ignoreTabReselectionListener = true; - - int restoredPosition = savedInstanceState.getInt(STATE_CURRENT_SELECTED_TAB, currentTabPosition); - selectTabAtPosition(restoredPosition, false); - } - } - - @Override - public void onClick(View target) { - if (!(target instanceof BottomBarTab)) return; - handleClick((BottomBarTab) target); - } - - @Override - public boolean onLongClick(View target) { - return !(target instanceof BottomBarTab) || handleLongClick((BottomBarTab) target); - } - - private BottomBarTab findTabInLayout(ViewGroup child) { - for (int i = 0; i < child.getChildCount(); i++) { - View candidate = child.getChildAt(i); - - if (candidate instanceof BottomBarTab) { - return (BottomBarTab) candidate; - } - } - - return null; - } - - private void handleClick(BottomBarTab newTab) { - BottomBarTab oldTab = getCurrentTab(); - - if (tabSelectionInterceptor != null - && tabSelectionInterceptor.shouldInterceptTabSelection(oldTab.getId(), newTab.getId())) { - return; - } - - oldTab.deselect(true); - newTab.select(true); - - shiftingMagic(oldTab, newTab, true); - handleBackgroundColorChange(newTab, true); - updateSelectedTab(newTab.getIndexInTabContainer()); - } - - private boolean handleLongClick(BottomBarTab longClickedTab) { - boolean areInactiveTitlesHidden = isShiftingMode() || isTabletMode; - boolean isClickedTitleHidden = !longClickedTab.isActive(); - boolean shouldShowHint = areInactiveTitlesHidden - && isClickedTitleHidden - && longPressHintsEnabled; - - if (shouldShowHint) { - Toast.makeText(getContext(), longClickedTab.getTitle(), Toast.LENGTH_SHORT) - .show(); - } - - return true; - } - - private void updateSelectedTab(int newPosition) { - int newTabId = getTabAtPosition(newPosition).getId(); - - if (newPosition != currentTabPosition) { - if (onTabSelectListener != null) { - onTabSelectListener.onTabSelected(newTabId); - } - } else if (onTabReselectListener != null && !ignoreTabReselectionListener) { - onTabReselectListener.onTabReSelected(newTabId); - } - - currentTabPosition = newPosition; - - if (ignoreTabReselectionListener) { - ignoreTabReselectionListener = false; - } - } - - private void shiftingMagic(BottomBarTab oldTab, BottomBarTab newTab, boolean animate) { - if (isShiftingMode()) { - oldTab.updateWidth(inActiveShiftingItemWidth, animate); - newTab.updateWidth(activeShiftingItemWidth, animate); - } - } - - private void handleBackgroundColorChange(BottomBarTab tab, boolean animate) { - int newColor = tab.getBarColorWhenSelected(); - - if (currentBackgroundColor == newColor) { - return; - } - - if (!animate) { - outerContainer.setBackgroundColor(newColor); - return; - } - - View clickedView = tab; - - if (tab.hasActiveBadge()) { - clickedView = tab.getOuterView(); - } - - animateBGColorChange(clickedView, newColor); - currentBackgroundColor = newColor; - } - - private void animateBGColorChange(View clickedView, final int newColor) { - prepareForBackgroundColorAnimation(newColor); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - if (!outerContainer.isAttachedToWindow()) { - return; - } - - backgroundCircularRevealAnimation(clickedView, newColor); - } else { - backgroundCrossfadeAnimation(newColor); - } - } - - private void prepareForBackgroundColorAnimation(int newColor) { - outerContainer.clearAnimation(); - backgroundOverlay.clearAnimation(); - - backgroundOverlay.setBackgroundColor(newColor); - backgroundOverlay.setVisibility(View.VISIBLE); - } - - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - private void backgroundCircularRevealAnimation(View clickedView, final int newColor) { - int centerX = (int) (ViewCompat.getX(clickedView) + (clickedView.getMeasuredWidth() / 2)); - int yOffset = isTabletMode ? (int) ViewCompat.getY(clickedView) : 0; - int centerY = yOffset + clickedView.getMeasuredHeight() / 2; - int startRadius = 0; - int finalRadius = isTabletMode ? outerContainer.getHeight() : outerContainer.getWidth(); - - Animator animator = ViewAnimationUtils.createCircularReveal( - backgroundOverlay, - centerX, - centerY, - startRadius, - finalRadius - ); - - if (isTabletMode) { - animator.setDuration(500); - } - - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - onEnd(); - } - - @Override - public void onAnimationCancel(Animator animation) { - onEnd(); - } - - private void onEnd() { - outerContainer.setBackgroundColor(newColor); - backgroundOverlay.setVisibility(View.INVISIBLE); - ViewCompat.setAlpha(backgroundOverlay, 1); - } - }); - - animator.start(); - } - - private void backgroundCrossfadeAnimation(final int newColor) { - ViewCompat.setAlpha(backgroundOverlay, 0); - ViewCompat.animate(backgroundOverlay) - .alpha(1) - .setListener(new ViewPropertyAnimatorListenerAdapter() { - @Override - public void onAnimationEnd(View view) { - onEnd(); - } - - @Override - public void onAnimationCancel(View view) { - onEnd(); - } - - private void onEnd() { - outerContainer.setBackgroundColor(newColor); - backgroundOverlay.setVisibility(View.INVISIBLE); - ViewCompat.setAlpha(backgroundOverlay, 1); - } - }) - .start(); - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/BottomBarBadge.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/BottomBarBadge.java deleted file mode 100644 index d7aa57294..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/BottomBarBadge.java +++ /dev/null @@ -1,172 +0,0 @@ -package com.habitrpg.android.habitica.ui.views.bottombar; - -import android.content.Context; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.ShapeDrawable; -import android.os.Build; -import android.view.Gravity; -import android.view.ViewGroup; -import android.view.ViewTreeObserver; -import android.widget.TextView; - -import com.habitrpg.android.habitica.R; - -import androidx.appcompat.widget.AppCompatImageView; -import androidx.appcompat.widget.AppCompatTextView; -import androidx.core.view.ViewCompat; - -/* - * BottomBar library for Android - * Copyright (c) 2016 Iiro Krankka (http://github.com/roughike). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -class BottomBarBadge extends AppCompatTextView { - private int count; - private boolean isVisible = false; - - BottomBarBadge(Context context) { - super(context); - } - - /** - * Set the unread / new item / whatever count for this Badge. - * - * @param count the value this Badge should show. - */ - void setCount(int count) { - this.count = count; - setText(String.valueOf(count)); - } - - /** - * Get the currently showing count for this Badge. - * - * @return current count for the Badge. - */ - int getCount() { - return count; - } - - /** - * Shows the badge with a neat little scale animation. - */ - void show() { - isVisible = true; - ViewCompat.animate(this) - .setDuration(150) - .alpha(1) - .scaleX(1) - .scaleY(1) - .start(); - } - - /** - * Hides the badge with a neat little scale animation. - */ - void hide() { - isVisible = false; - ViewCompat.animate(this) - .setDuration(150) - .alpha(0) - .scaleX(0) - .scaleY(0) - .start(); - } - - /** - * Is this badge currently visible? - * - * @return true is this badge is visible, otherwise false. - */ - boolean isVisible() { - return isVisible; - } - - void attachToTab(BottomBarTab tab, int backgroundColor) { - ViewGroup.LayoutParams params = new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - - setLayoutParams(params); - setGravity(Gravity.CENTER); - MiscUtils.setTextAppearance(this, R.style.BB_BottomBarBadge_Text); - - setColoredCircleBackground(backgroundColor); - wrapTabAndBadgeInSameContainer(tab); - } - - void setColoredCircleBackground(int circleColor) { - int innerPadding = MiscUtils.dpToPixel(getContext(), 1); - ShapeDrawable backgroundCircle = BadgeCircle.make(innerPadding * 3, circleColor); - setPadding(innerPadding, innerPadding, innerPadding, innerPadding); - setBackgroundCompat(backgroundCircle); - } - - private void wrapTabAndBadgeInSameContainer(final BottomBarTab tab) { - ViewGroup tabContainer = (ViewGroup) tab.getParent(); - tabContainer.removeView(tab); - - final BadgeContainer badgeContainer = new BadgeContainer(getContext()); - badgeContainer.setLayoutParams(new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); - - badgeContainer.addView(tab); - badgeContainer.addView(this); - - tabContainer.addView(badgeContainer, tab.getIndexInTabContainer()); - - badgeContainer.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { - @SuppressWarnings("deprecation") - @Override - public void onGlobalLayout() { - badgeContainer.getViewTreeObserver().removeGlobalOnLayoutListener(this); - adjustPositionAndSize(tab); - } - }); - } - - void removeFromTab(BottomBarTab tab) { - BadgeContainer badgeAndTabContainer = (BadgeContainer) getParent(); - ViewGroup originalTabContainer = (ViewGroup) badgeAndTabContainer.getParent(); - - badgeAndTabContainer.removeView(tab); - originalTabContainer.removeView(badgeAndTabContainer); - originalTabContainer.addView(tab, tab.getIndexInTabContainer()); - } - - void adjustPositionAndSize(BottomBarTab tab) { - AppCompatImageView iconView = tab.getIconView(); - ViewGroup.LayoutParams params = getLayoutParams(); - - int size = Math.max(getWidth(), getHeight()); - float xOffset = (float) (iconView.getWidth() / 1.25); - - setX(iconView.getX() + xOffset); - setTranslationY(10); - - if (params.width != size || params.height != size) { - params.width = size; - params.height = size; - setLayoutParams(params); - } - } - - @SuppressWarnings("deprecation") - private void setBackgroundCompat(Drawable background) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - setBackground(background); - } else { - setBackgroundDrawable(background); - } - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/BottomBarTab.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/BottomBarTab.java deleted file mode 100644 index 88e9d8eaa..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/BottomBarTab.java +++ /dev/null @@ -1,730 +0,0 @@ -package com.habitrpg.android.habitica.ui.views.bottombar; - -import android.animation.ArgbEvaluator; -import android.animation.ValueAnimator; -import android.content.Context; -import android.graphics.Typeface; -import android.os.Build; -import android.os.Bundle; -import android.os.Parcelable; -import androidx.annotation.ColorInt; -import androidx.annotation.NonNull; -import androidx.annotation.VisibleForTesting; -import androidx.appcompat.widget.AppCompatImageView; -import androidx.core.view.ViewCompat; -import androidx.core.view.ViewPropertyAnimatorCompat; - -import android.view.Gravity; -import android.view.ViewGroup; -import android.widget.LinearLayout; -import android.widget.TextView; - -import com.habitrpg.android.habitica.R; - -/* - * BottomBar library for Android - * Copyright (c) 2016 Iiro Krankka (http://github.com/roughike). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -public class BottomBarTab extends LinearLayout { - @VisibleForTesting - static final String STATE_BADGE_COUNT = "STATE_BADGE_COUNT_FOR_TAB_"; - - private static final long ANIMATION_DURATION = 150; - private static final float ACTIVE_TITLE_SCALE = 1; - private static final float INACTIVE_FIXED_TITLE_SCALE = 0.86f; - private static final float ACTIVE_SHIFTING_TITLELESS_ICON_SCALE = 1.24f; - private static final float INACTIVE_SHIFTING_TITLELESS_ICON_SCALE = 1f; - - private final int sixDps; - private final int eightDps; - private final int sixteenDps; - - @VisibleForTesting - BottomBarBadge badge; - - private Type type = Type.FIXED; - private boolean isTitleless; - private int iconResId; - private String title; - private float inActiveAlpha; - private float activeAlpha; - private int inActiveColor; - private int activeColor; - private int barColorWhenSelected; - private int badgeBackgroundColor; - private boolean badgeHidesWhenActive; - private AppCompatImageView iconView; - private TextView titleView; - private boolean isActive; - private int indexInContainer; - private int titleTextAppearanceResId; - private Typeface titleTypeFace; - - BottomBarTab(Context context) { - super(context); - - sixDps = MiscUtils.dpToPixel(context, 6); - eightDps = MiscUtils.dpToPixel(context, 8); - sixteenDps = MiscUtils.dpToPixel(context, 16); - } - - void setConfig(@NonNull Config config) { - setInActiveAlpha(config.inActiveTabAlpha); - setActiveAlpha(config.activeTabAlpha); - setInActiveColor(config.inActiveTabColor); - setActiveColor(config.activeTabColor); - setBarColorWhenSelected(config.barColorWhenSelected); - setBadgeBackgroundColor(config.badgeBackgroundColor); - setBadgeHidesWhenActive(config.badgeHidesWhenSelected); - setTitleTextAppearance(config.titleTextAppearance); - setTitleTypeface(config.titleTypeFace); - } - - void prepareLayout() { - inflate(getContext(), getLayoutResource(), this); - setOrientation(VERTICAL); - setGravity(isTitleless? Gravity.CENTER : Gravity.CENTER_HORIZONTAL); - setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); - setBackgroundResource(MiscUtils.getDrawableRes(getContext(), R.attr.selectableItemBackgroundBorderless)); - - iconView = findViewById(R.id.bb_bottom_bar_icon); - iconView.setImageResource(iconResId); - - if (type != Type.TABLET && !isTitleless) { - titleView = findViewById(R.id.bb_bottom_bar_title); - titleView.setVisibility(VISIBLE); - - if (type == Type.SHIFTING) { - findViewById(R.id.spacer).setVisibility(VISIBLE); - } - - updateTitle(); - } - - updateCustomTextAppearance(); - updateCustomTypeface(); - } - - @VisibleForTesting - int getLayoutResource() { - int layoutResource; - switch (type) { - case FIXED: - layoutResource = R.layout.bb_bottom_bar_item_fixed; - break; - case SHIFTING: - layoutResource = R.layout.bb_bottom_bar_item_shifting; - break; - case TABLET: - layoutResource = R.layout.bb_bottom_bar_item_fixed_tablet; - break; - default: - // should never happen - throw new RuntimeException("Unknown BottomBarTab type."); - } - return layoutResource; - } - - private void updateTitle() { - if (titleView != null) { - titleView.setText(title); - } - } - - @SuppressWarnings("deprecation") - private void updateCustomTextAppearance() { - if (titleView == null || titleTextAppearanceResId == 0) { - return; - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - titleView.setTextAppearance(titleTextAppearanceResId); - } else { - titleView.setTextAppearance(getContext(), titleTextAppearanceResId); - } - - titleView.setTag(R.id.bb_bottom_bar_appearance_id, titleTextAppearanceResId); - } - - private void updateCustomTypeface() { - if (titleTypeFace != null && titleView != null) { - titleView.setTypeface(titleTypeFace); - } - } - - Type getType() { - return type; - } - - void setType(Type type) { - this.type = type; - } - - boolean isTitleless() { - return isTitleless; - } - - void setIsTitleless(boolean isTitleless) { - if (isTitleless && getIconResId() == 0) { - throw new IllegalStateException("This tab is supposed to be " + - "icon only, yet it has no icon specified. Index in " + - "container: " + getIndexInTabContainer()); - } - - this.isTitleless = isTitleless; - } - - public ViewGroup getOuterView() { - return (ViewGroup) getParent(); - } - - AppCompatImageView getIconView() { - return iconView; - } - - int getIconResId() { - return iconResId; - } - - void setIconResId(int iconResId) { - this.iconResId = iconResId; - } - - TextView getTitleView() { - return titleView; - } - - public String getTitle() { - return title; - } - - public void setTitle(String title) { - this.title = title; - updateTitle(); - } - - public float getInActiveAlpha() { - return inActiveAlpha; - } - - public void setInActiveAlpha(float inActiveAlpha) { - this.inActiveAlpha = inActiveAlpha; - - if (!isActive) { - setAlphas(inActiveAlpha); - } - } - - public float getActiveAlpha() { - return activeAlpha; - } - - public void setActiveAlpha(float activeAlpha) { - this.activeAlpha = activeAlpha; - - if (isActive) { - setAlphas(activeAlpha); - } - } - - public int getInActiveColor() { - return inActiveColor; - } - - public void setInActiveColor(int inActiveColor) { - this.inActiveColor = inActiveColor; - - if (!isActive) { - setColors(inActiveColor); - } - } - - public int getActiveColor() { - return activeColor; - } - - public void setActiveColor(int activeIconColor) { - this.activeColor = activeIconColor; - - if (isActive) { - setColors(activeColor); - } - } - - public int getBarColorWhenSelected() { - return barColorWhenSelected; - } - - public void setBarColorWhenSelected(int barColorWhenSelected) { - this.barColorWhenSelected = barColorWhenSelected; - } - - public int getBadgeBackgroundColor() { - return badgeBackgroundColor; - } - - public void setBadgeBackgroundColor(int badgeBackgroundColor) { - this.badgeBackgroundColor = badgeBackgroundColor; - - if (badge != null) { - badge.setColoredCircleBackground(badgeBackgroundColor); - } - } - - public boolean getBadgeHidesWhenActive() { - return badgeHidesWhenActive; - } - - public void setBadgeHidesWhenActive(boolean hideWhenActive) { - this.badgeHidesWhenActive = hideWhenActive; - } - - int getCurrentDisplayedIconColor() { - Object tag = iconView.getTag(R.id.bb_bottom_bar_color_id); - - if (tag instanceof Integer) { - return (int) tag; - } - - return 0; - } - - int getCurrentDisplayedTitleColor() { - if (titleView != null) { - return titleView.getCurrentTextColor(); - } - - return 0; - } - - int getCurrentDisplayedTextAppearance() { - Object tag = titleView.getTag(R.id.bb_bottom_bar_appearance_id); - - if (titleView != null && tag instanceof Integer) { - return (int) tag; - } - - return 0; - } - - public void setBadgeCount(int count) { - if (count <= 0) { - if (badge != null) { - badge.removeFromTab(this); - badge = null; - } - - return; - } - - if (badge == null) { - badge = new BottomBarBadge(getContext()); - badge.attachToTab(this, badgeBackgroundColor); - } - - badge.setCount(count); - - if (isActive && badgeHidesWhenActive) { - badge.hide(); - } - } - - public void removeBadge() { - setBadgeCount(0); - } - - boolean isActive() { - return isActive; - } - - boolean hasActiveBadge() { - return badge != null; - } - - int getIndexInTabContainer() { - return indexInContainer; - } - - void setIndexInContainer(int indexInContainer) { - this.indexInContainer = indexInContainer; - } - - void setIconTint(int tint) { - iconView.setColorFilter(tint); - } - - public int getTitleTextAppearance() { - return titleTextAppearanceResId; - } - - @SuppressWarnings("deprecation") - void setTitleTextAppearance(int resId) { - this.titleTextAppearanceResId = resId; - updateCustomTextAppearance(); - } - - public void setTitleTypeface(Typeface typeface) { - this.titleTypeFace = typeface; - updateCustomTypeface(); - } - - public Typeface getTitleTypeFace() { - return titleTypeFace; - } - - void select(boolean animate) { - isActive = true; - - if (animate) { - animateIcon(activeAlpha, ACTIVE_SHIFTING_TITLELESS_ICON_SCALE); - animateTitle(sixDps, ACTIVE_TITLE_SCALE, activeAlpha); - animateColors(inActiveColor, activeColor); - } else { - setTitleScale(ACTIVE_TITLE_SCALE); - setTopPadding(sixDps); - setIconScale(ACTIVE_SHIFTING_TITLELESS_ICON_SCALE); - setColors(activeColor); - setAlphas(activeAlpha); - } - - setSelected(true); - - if (badge != null && badgeHidesWhenActive) { - badge.hide(); - } - } - - void deselect(boolean animate) { - isActive = false; - - boolean isShifting = type == Type.SHIFTING; - - float titleScale = isShifting ? 0 : INACTIVE_FIXED_TITLE_SCALE; - int iconPaddingTop = isShifting ? sixteenDps : eightDps; - - if (animate) { - animateTitle(iconPaddingTop, titleScale, inActiveAlpha); - animateIcon(inActiveAlpha, INACTIVE_SHIFTING_TITLELESS_ICON_SCALE); - animateColors(activeColor, inActiveColor); - } else { - setTitleScale(titleScale); - setTopPadding(iconPaddingTop); - setIconScale(INACTIVE_SHIFTING_TITLELESS_ICON_SCALE); - setColors(inActiveColor); - setAlphas(inActiveAlpha); - } - - setSelected(false); - - if (!isShifting && badge != null && !badge.isVisible()) { - badge.show(); - } - } - - private void animateColors(int previousColor, int color) { - ValueAnimator anim = new ValueAnimator(); - anim.setIntValues(previousColor, color); - anim.setEvaluator(new ArgbEvaluator()); - anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator valueAnimator) { - setColors((Integer) valueAnimator.getAnimatedValue()); - } - }); - - anim.setDuration(150); - anim.start(); - } - - private void setColors(int color) { - if (iconView != null) { - iconView.setColorFilter(color); - iconView.setTag(R.id.bb_bottom_bar_color_id, color); - } - - if (titleView != null) { - titleView.setTextColor(color); - } - } - - private void setAlphas(float alpha) { - if (iconView != null) { - ViewCompat.setAlpha(iconView, alpha); - } - - if (titleView != null) { - ViewCompat.setAlpha(titleView, alpha); - } - } - - void updateWidth(float endWidth, boolean animated) { - if (!animated) { - getLayoutParams().width = (int) endWidth; - - if (!isActive && badge != null) { - badge.adjustPositionAndSize(this); - badge.show(); - } - return; - } - - float start = getWidth(); - - ValueAnimator animator = ValueAnimator.ofFloat(start, endWidth); - animator.setDuration(150); - animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animator) { - ViewGroup.LayoutParams params = getLayoutParams(); - if (params == null) return; - - params.width = Math.round((float) animator.getAnimatedValue()); - setLayoutParams(params); - } - }); - - // Workaround to avoid using faulty onAnimationEnd() listener - postDelayed(new Runnable() { - @Override - public void run() { - if (!isActive && badge != null) { - clearAnimation(); - badge.adjustPositionAndSize(BottomBarTab.this); - badge.show(); - } - } - }, animator.getDuration()); - - animator.start(); - } - - private void updateBadgePosition() { - if (badge != null) { - badge.adjustPositionAndSize(this); - } - } - - private void setTopPaddingAnimated(int start, int end) { - if (type == Type.TABLET || isTitleless) { - return; - } - - ValueAnimator paddingAnimator = ValueAnimator.ofInt(start, end); - paddingAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - iconView.setPadding( - iconView.getPaddingLeft(), - (Integer) animation.getAnimatedValue(), - iconView.getPaddingRight(), - iconView.getPaddingBottom() - ); - } - }); - - paddingAnimator.setDuration(ANIMATION_DURATION); - paddingAnimator.start(); - } - - private void animateTitle(int padding, float scale, float alpha) { - if (type == Type.TABLET && isTitleless) { - return; - } - - setTopPaddingAnimated(iconView.getPaddingTop(), padding); - - ViewPropertyAnimatorCompat titleAnimator = ViewCompat.animate(titleView) - .setDuration(ANIMATION_DURATION) - .scaleX(scale) - .scaleY(scale); - titleAnimator.alpha(alpha); - titleAnimator.start(); - } - - private void animateIconScale(float scale) { - ViewCompat.animate(iconView) - .setDuration(ANIMATION_DURATION) - .scaleX(scale) - .scaleY(scale) - .start(); - } - - private void animateIcon(float alpha, float scale) { - ViewCompat.animate(iconView) - .setDuration(ANIMATION_DURATION) - .alpha(alpha) - .start(); - - if (isTitleless && type == Type.SHIFTING) { - animateIconScale(scale); - } - } - - private void setTopPadding(int topPadding) { - if (type == Type.TABLET || isTitleless) { - return; - } - - iconView.setPadding( - iconView.getPaddingLeft(), - topPadding, - iconView.getPaddingRight(), - iconView.getPaddingBottom() - ); - } - - private void setTitleScale(float scale) { - if (type == Type.TABLET || isTitleless) { - return; - } - - ViewCompat.setScaleX(titleView, scale); - ViewCompat.setScaleY(titleView, scale); - } - - private void setIconScale(float scale) { - if (isTitleless && type == Type.SHIFTING) { - ViewCompat.setScaleX(iconView, scale); - ViewCompat.setScaleY(iconView, scale); - } - } - - @Override - public Parcelable onSaveInstanceState() { - if (badge != null) { - Bundle bundle = saveState(); - bundle.putParcelable("superstate", super.onSaveInstanceState()); - - return bundle; - } - - return super.onSaveInstanceState(); - } - - @VisibleForTesting - Bundle saveState() { - Bundle outState = new Bundle(); - outState.putInt(STATE_BADGE_COUNT + getIndexInTabContainer(), badge.getCount()); - - return outState; - } - - @Override - public void onRestoreInstanceState(Parcelable state) { - if (state instanceof Bundle) { - Bundle bundle = (Bundle) state; - restoreState(bundle); - - state = bundle.getParcelable("superstate"); - } - - super.onRestoreInstanceState(state); - } - - @VisibleForTesting - void restoreState(Bundle savedInstanceState) { - int previousBadgeCount = savedInstanceState.getInt(STATE_BADGE_COUNT + getIndexInTabContainer()); - setBadgeCount(previousBadgeCount); - } - - enum Type { - FIXED, SHIFTING, TABLET - } - - public static class Config { - private final float inActiveTabAlpha; - private final float activeTabAlpha; - private final int inActiveTabColor; - private final int activeTabColor; - private final int barColorWhenSelected; - private final int badgeBackgroundColor; - private final int titleTextAppearance; - private final Typeface titleTypeFace; - private boolean badgeHidesWhenSelected = true; - - private Config(Builder builder) { - this.inActiveTabAlpha = builder.inActiveTabAlpha; - this.activeTabAlpha = builder.activeTabAlpha; - this.inActiveTabColor = builder.inActiveTabColor; - this.activeTabColor = builder.activeTabColor; - this.barColorWhenSelected = builder.barColorWhenSelected; - this.badgeBackgroundColor = builder.badgeBackgroundColor; - this.badgeHidesWhenSelected = builder.hidesBadgeWhenSelected; - this.titleTextAppearance = builder.titleTextAppearance; - this.titleTypeFace = builder.titleTypeFace; - } - - public static class Builder { - private float inActiveTabAlpha; - private float activeTabAlpha; - private int inActiveTabColor; - private int activeTabColor; - private int barColorWhenSelected; - private int badgeBackgroundColor; - private boolean hidesBadgeWhenSelected = true; - private int titleTextAppearance; - private Typeface titleTypeFace; - - public Builder inActiveTabAlpha(float alpha) { - this.inActiveTabAlpha = alpha; - return this; - } - - public Builder activeTabAlpha(float alpha) { - this.activeTabAlpha = alpha; - return this; - } - - public Builder inActiveTabColor(@ColorInt int color) { - this.inActiveTabColor = color; - return this; - } - - public Builder activeTabColor(@ColorInt int color) { - this.activeTabColor = color; - return this; - } - - public Builder barColorWhenSelected(@ColorInt int color) { - this.barColorWhenSelected = color; - return this; - } - - public Builder badgeBackgroundColor(@ColorInt int color) { - this.badgeBackgroundColor = color; - return this; - } - - public Builder hideBadgeWhenSelected(boolean hide) { - this.hidesBadgeWhenSelected = hide; - return this; - } - - public Builder titleTextAppearance(int titleTextAppearance) { - this.titleTextAppearance = titleTextAppearance; - return this; - } - - public Builder titleTypeFace(Typeface titleTypeFace) { - this.titleTypeFace = titleTypeFace; - return this; - } - - public Config build() { - return new Config(this); - } - } - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/BottomNavigationBehavior.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/BottomNavigationBehavior.java deleted file mode 100644 index e4e2c554f..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/BottomNavigationBehavior.java +++ /dev/null @@ -1,153 +0,0 @@ -package com.habitrpg.android.habitica.ui.views.bottombar; - -import android.os.Build; -import androidx.annotation.NonNull; -import androidx.coordinatorlayout.widget.CoordinatorLayout; -import androidx.core.view.ViewCompat; -import androidx.core.view.ViewPropertyAnimatorCompat; -import androidx.interpolator.view.animation.LinearOutSlowInInterpolator; - -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.Interpolator; - -import com.google.android.material.snackbar.Snackbar; - -/** - * Created by Nikola D. on 3/15/2016. - * - * Credit goes to Nikola Despotoski: - * https://github.com/NikolaDespotoski - */ -class BottomNavigationBehavior extends VerticalScrollingBehavior { - private static final Interpolator INTERPOLATOR = new LinearOutSlowInInterpolator(); - private final int bottomNavHeight; - private final int defaultOffset; - private boolean isTablet = false; - - private ViewPropertyAnimatorCompat mTranslationAnimator; - private boolean hidden = false; - private int mSnackbarHeight = -1; - private final BottomNavigationWithSnackbar mWithSnackBarImpl = new LollipopBottomNavWithSnackBarImpl(); - private boolean mScrollingEnabled = true; - - BottomNavigationBehavior(int bottomNavHeight, int defaultOffset, boolean tablet) { - this.bottomNavHeight = bottomNavHeight; - this.defaultOffset = defaultOffset; - isTablet = tablet; - } - - @Override - public boolean layoutDependsOn(CoordinatorLayout parent, V child, View dependency) { - mWithSnackBarImpl.updateSnackbar(parent, dependency, child); - return dependency instanceof Snackbar.SnackbarLayout; - } - - @Override - public void onNestedVerticalOverScroll(CoordinatorLayout coordinatorLayout, V child, @ScrollDirection int direction, int currentOverScroll, int totalOverScroll) { - } - - @Override - public void onDependentViewRemoved(CoordinatorLayout parent, V child, View dependency) { - updateScrollingForSnackbar(dependency, true); - super.onDependentViewRemoved(parent, child, dependency); - } - - private void updateScrollingForSnackbar(View dependency, boolean enabled) { - if (!isTablet && dependency instanceof Snackbar.SnackbarLayout) { - mScrollingEnabled = enabled; - } - } - - @Override - public boolean onDependentViewChanged(CoordinatorLayout parent, V child, View dependency) { - updateScrollingForSnackbar(dependency, false); - return super.onDependentViewChanged(parent, child, dependency); - } - - @Override - public void onDirectionNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dx, int dy, int[] consumed, @ScrollDirection int scrollDirection) { - handleDirection(child, scrollDirection); - } - - private void handleDirection(V child, int scrollDirection) { - if (!mScrollingEnabled) return; - if (scrollDirection == ScrollDirection.SCROLL_DIRECTION_DOWN && hidden) { - hidden = false; - animateOffset(child, defaultOffset); - } else if (scrollDirection == ScrollDirection.SCROLL_DIRECTION_UP && !hidden) { - hidden = true; - animateOffset(child, bottomNavHeight + defaultOffset); - } - } - - @Override - protected boolean onNestedDirectionFling(CoordinatorLayout coordinatorLayout, V child, View target, float velocityX, float velocityY, @ScrollDirection int scrollDirection) { - handleDirection(child, scrollDirection); - return true; - } - - private void animateOffset(final V child, final int offset) { - ensureOrCancelAnimator(child); - mTranslationAnimator.translationY(offset).start(); - } - - private void ensureOrCancelAnimator(V child) { - if (mTranslationAnimator == null) { - mTranslationAnimator = ViewCompat.animate(child); - mTranslationAnimator.setDuration(300); - mTranslationAnimator.setInterpolator(INTERPOLATOR); - } else { - mTranslationAnimator.cancel(); - } - } - - - void setHidden(@NonNull V view, boolean bottomLayoutHidden) { - if (!bottomLayoutHidden && hidden) { - animateOffset(view, defaultOffset); - } else if (bottomLayoutHidden && !hidden) { - animateOffset(view, bottomNavHeight + defaultOffset); - } - hidden = bottomLayoutHidden; - } - - - static BottomNavigationBehavior from(@NonNull V view) { - ViewGroup.LayoutParams params = view.getLayoutParams(); - - if (!(params instanceof CoordinatorLayout.LayoutParams)) { - throw new IllegalArgumentException("The view is not a child of CoordinatorLayout"); - } - - CoordinatorLayout.Behavior behavior = ((CoordinatorLayout.LayoutParams) params) - .getBehavior(); - - if (behavior instanceof BottomNavigationBehavior) { - // noinspection unchecked - return (BottomNavigationBehavior) behavior; - } - - throw new IllegalArgumentException("The view is not associated with BottomNavigationBehavior"); - } - - private interface BottomNavigationWithSnackbar { - void updateSnackbar(CoordinatorLayout parent, View dependency, View child); - } - - private class LollipopBottomNavWithSnackBarImpl implements BottomNavigationWithSnackbar { - @Override - public void updateSnackbar(CoordinatorLayout parent, View dependency, View child) { - if (!isTablet && dependency instanceof Snackbar.SnackbarLayout) { - if (mSnackbarHeight == -1) { - mSnackbarHeight = dependency.getHeight(); - } - if (ViewCompat.getTranslationY(child) != 0) return; - int targetPadding = (mSnackbarHeight + bottomNavHeight - defaultOffset); - dependency.setPadding(dependency.getPaddingLeft(), - dependency.getPaddingTop(), dependency.getPaddingRight(), targetPadding - ); - } - } - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/MiscUtils.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/MiscUtils.java deleted file mode 100644 index cdc743907..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/MiscUtils.java +++ /dev/null @@ -1,121 +0,0 @@ -package com.habitrpg.android.habitica.ui.views.bottombar; - -import android.content.Context; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.os.Build; -import androidx.annotation.AttrRes; -import androidx.annotation.ColorInt; -import androidx.annotation.Dimension; -import androidx.annotation.DrawableRes; -import androidx.annotation.NonNull; -import androidx.annotation.Px; -import androidx.annotation.StyleRes; -import android.util.DisplayMetrics; -import android.util.TypedValue; -import android.widget.TextView; - -import static androidx.annotation.Dimension.DP; - -/* - * BottomBar library for Android - * Copyright (c) 2016 Iiro Krankka (http://github.com/roughike). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -class MiscUtils { - - @NonNull protected static TypedValue getTypedValue(@NonNull Context context, @AttrRes int resId) { - TypedValue tv = new TypedValue(); - context.getTheme().resolveAttribute(resId, tv, true); - return tv; - } - - @ColorInt - protected static int getColor(@NonNull Context context, @AttrRes int color) { - return getTypedValue(context, color).data; - } - - @DrawableRes - protected static int getDrawableRes(@NonNull Context context, @AttrRes int drawable) { - return getTypedValue(context, drawable).resourceId; - } - - /** - * Converts dps to pixels nicely. - * - * @param context the Context for getting the resources - * @param dp dimension in dps - * @return dimension in pixels - */ - protected static int dpToPixel(@NonNull Context context, @Dimension(unit = DP) float dp) { - Resources resources = context.getResources(); - DisplayMetrics metrics = resources.getDisplayMetrics(); - - try { - return (int) (dp * metrics.density); - } catch (NoSuchFieldError ignored) { - return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, metrics); - } - } - - /** - * Converts pixels to dps just as well. - * - * @param context the Context for getting the resources - * @param px dimension in pixels - * @return dimension in dps - */ - protected static int pixelToDp(@NonNull Context context, @Px int px) { - DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); - return Math.round(px / displayMetrics.density); - } - - /** - * Returns screen width. - * - * @param context Context to get resources and device specific display metrics - * @return screen width - */ - protected static int getScreenWidth(@NonNull Context context) { - DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); - return (int) (displayMetrics.widthPixels / displayMetrics.density); - } - - /** - * A convenience method for setting text appearance. - * - * @param textView a TextView which textAppearance to modify. - * @param resId a style resource for the text appearance. - */ - @SuppressWarnings("deprecation") - protected static void setTextAppearance(@NonNull TextView textView, @StyleRes int resId) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - textView.setTextAppearance(resId); - } else { - textView.setTextAppearance(textView.getContext(), resId); - } - } - - /** - * Determine if the current UI Mode is Night Mode. - * - * @param context Context to get the configuration. - * @return true if the night mode is enabled, otherwise false. - */ - protected static boolean isNightMode(@NonNull Context context) { - int currentNightMode = context.getResources().getConfiguration().uiMode - & Configuration.UI_MODE_NIGHT_MASK; - return currentNightMode == Configuration.UI_MODE_NIGHT_YES; - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/OnTabReselectListener.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/OnTabReselectListener.java deleted file mode 100644 index e8e5febd6..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/OnTabReselectListener.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.habitrpg.android.habitica.ui.views.bottombar; - -import androidx.annotation.IdRes; - -/* - * BottomBar library for Android - * Copyright (c) 2016 Iiro Krankka (http://github.com/roughike). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -public interface OnTabReselectListener { - /** - * The method being called when currently visible {@link BottomBarTab} is - * reselected. Use this method for scrolling to the top of your content, - * as recommended by the Material Design spec - * - * @param tabId the {@link BottomBarTab} that was reselected. - */ - void onTabReSelected(@IdRes int tabId); -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/OnTabSelectListener.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/OnTabSelectListener.java deleted file mode 100644 index 5d7f5374f..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/OnTabSelectListener.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.habitrpg.android.habitica.ui.views.bottombar; - -import androidx.annotation.IdRes; - -/* - * BottomBar library for Android - * Copyright (c) 2016 Iiro Krankka (http://github.com/roughike). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -public interface OnTabSelectListener { - /** - * The method being called when currently visible {@link BottomBarTab} changes. - * - * This listener is fired for the first time after the items have been set and - * also after a configuration change, such as when screen orientation changes - * from portrait to landscape. - * - * @param tabId the new visible {@link BottomBarTab} - */ - void onTabSelected(@IdRes int tabId); -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/ShySettings.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/ShySettings.java deleted file mode 100644 index 02e3e4fe1..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/ShySettings.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.habitrpg.android.habitica.ui.views.bottombar; - -/** - * Settings specific for a shy BottomBar. - */ -public class ShySettings { - private BottomBar bottomBar; - private Boolean pendingIsVisibleInShyMode; - - ShySettings(BottomBar bottomBar) { - this.bottomBar = bottomBar; - } - - void shyHeightCalculated() { - updatePendingShyVisibility(); - } - - /** - * Shows the BottomBar if it was hidden, with a translate animation. - */ - public void showBar() { - toggleIsVisibleInShyMode(true); - } - - /** - * Hides the BottomBar in if it was visible, with a translate animation. - */ - public void hideBar() { - toggleIsVisibleInShyMode(false); - } - - private void toggleIsVisibleInShyMode(boolean visible) { - if (!bottomBar.isShy()) { - return; - } - - if (bottomBar.isShyHeightAlreadyCalculated()) { - BottomNavigationBehavior behavior = BottomNavigationBehavior.from(bottomBar); - - if (behavior != null) { - boolean isHidden = !visible; - behavior.setHidden(bottomBar, isHidden); - } - } else { - pendingIsVisibleInShyMode = true; - } - } - - private void updatePendingShyVisibility() { - if (pendingIsVisibleInShyMode != null) { - toggleIsVisibleInShyMode(pendingIsVisibleInShyMode); - pendingIsVisibleInShyMode = null; - } - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/TabParser.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/TabParser.java deleted file mode 100644 index e4a6c9b26..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/TabParser.java +++ /dev/null @@ -1,210 +0,0 @@ -package com.habitrpg.android.habitica.ui.views.bottombar; - -import android.content.Context; -import android.content.res.XmlResourceParser; -import android.graphics.Color; -import androidx.annotation.CheckResult; -import androidx.annotation.ColorInt; -import androidx.annotation.IntRange; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.StringDef; -import androidx.annotation.XmlRes; -import androidx.core.content.ContextCompat; - -import org.xmlpull.v1.XmlPullParserException; - -import java.io.IOException; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.List; - -import static com.habitrpg.android.habitica.ui.views.bottombar.TabParser.TabAttribute.ACTIVE_COLOR; -import static com.habitrpg.android.habitica.ui.views.bottombar.TabParser.TabAttribute.BADGE_BACKGROUND_COLOR; -import static com.habitrpg.android.habitica.ui.views.bottombar.TabParser.TabAttribute.BADGE_HIDES_WHEN_ACTIVE; -import static com.habitrpg.android.habitica.ui.views.bottombar.TabParser.TabAttribute.BAR_COLOR_WHEN_SELECTED; -import static com.habitrpg.android.habitica.ui.views.bottombar.TabParser.TabAttribute.ICON; -import static com.habitrpg.android.habitica.ui.views.bottombar.TabParser.TabAttribute.ID; -import static com.habitrpg.android.habitica.ui.views.bottombar.TabParser.TabAttribute.INACTIVE_COLOR; -import static com.habitrpg.android.habitica.ui.views.bottombar.TabParser.TabAttribute.IS_TITLELESS; -import static com.habitrpg.android.habitica.ui.views.bottombar.TabParser.TabAttribute.TITLE; - -/** - * Created by iiro on 21.7.2016. - * - * BottomBar library for Android - * Copyright (c) 2016 Iiro Krankka (http://github.com/roughike). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -class TabParser { - private static final String TAB_TAG = "tab"; - private static final int AVG_NUMBER_OF_TABS = 5; - private static final int COLOR_NOT_SET = -1; - private static final int RESOURCE_NOT_FOUND = 0; - - @NonNull - private final Context context; - - @NonNull - private final BottomBarTab.Config defaultTabConfig; - - @NonNull - private final XmlResourceParser parser; - - @Nullable - private List tabs = null; - - TabParser(@NonNull Context context, @NonNull BottomBarTab.Config defaultTabConfig, @XmlRes int tabsXmlResId) { - this.context = context; - this.defaultTabConfig = defaultTabConfig; - this.parser = context.getResources().getXml(tabsXmlResId); - } - - @CheckResult - @NonNull - public List parseTabs() { - if (tabs == null) { - tabs = new ArrayList<>(AVG_NUMBER_OF_TABS); - try { - int eventType; - do { - eventType = parser.next(); - if (eventType == XmlResourceParser.START_TAG && TAB_TAG.equals(parser.getName())) { - BottomBarTab bottomBarTab = parseNewTab(parser, tabs.size()); - tabs.add(bottomBarTab); - } - } while (eventType != XmlResourceParser.END_DOCUMENT); - } catch (IOException | XmlPullParserException e) { - e.printStackTrace(); - throw new TabParserException(); - } - } - - return tabs; - } - - @NonNull - private BottomBarTab parseNewTab(@NonNull XmlResourceParser parser, @IntRange(from = 0) int containerPosition) { - BottomBarTab workingTab = tabWithDefaults(); - workingTab.setIndexInContainer(containerPosition); - - final int numberOfAttributes = parser.getAttributeCount(); - for (int i = 0; i < numberOfAttributes; i++) { - @TabAttribute - String attrName = parser.getAttributeName(i); - switch (attrName) { - case ID: - workingTab.setId(parser.getIdAttributeResourceValue(i)); - break; - case ICON: - workingTab.setIconResId(parser.getAttributeResourceValue(i, RESOURCE_NOT_FOUND)); - break; - case TITLE: - workingTab.setTitle(getTitleValue(parser, i)); - break; - case INACTIVE_COLOR: - int inactiveColor = getColorValue(parser, i); - if (inactiveColor == COLOR_NOT_SET) continue; - workingTab.setInActiveColor(inactiveColor); - break; - case ACTIVE_COLOR: - int activeColor = getColorValue(parser, i); - if (activeColor == COLOR_NOT_SET) continue; - workingTab.setActiveColor(activeColor); - break; - case BAR_COLOR_WHEN_SELECTED: - int barColorWhenSelected = getColorValue(parser, i); - if (barColorWhenSelected == COLOR_NOT_SET) continue; - workingTab.setBarColorWhenSelected(barColorWhenSelected); - break; - case BADGE_BACKGROUND_COLOR: - int badgeBackgroundColor = getColorValue(parser, i); - if (badgeBackgroundColor == COLOR_NOT_SET) continue; - workingTab.setBadgeBackgroundColor(badgeBackgroundColor); - break; - case BADGE_HIDES_WHEN_ACTIVE: - boolean badgeHidesWhenActive = parser.getAttributeBooleanValue(i, true); - workingTab.setBadgeHidesWhenActive(badgeHidesWhenActive); - break; - case IS_TITLELESS: - boolean isTitleless = parser.getAttributeBooleanValue(i, false); - workingTab.setIsTitleless(isTitleless); - break; - } - } - - return workingTab; - } - - @NonNull - private BottomBarTab tabWithDefaults() { - BottomBarTab tab = new BottomBarTab(context); - tab.setConfig(defaultTabConfig); - - return tab; - } - - @NonNull - private String getTitleValue(@NonNull XmlResourceParser parser, @IntRange(from = 0) int attrIndex) { - int titleResource = parser.getAttributeResourceValue(attrIndex, 0); - return titleResource == RESOURCE_NOT_FOUND - ? parser.getAttributeValue(attrIndex) : context.getString(titleResource); - } - - @ColorInt - private int getColorValue(@NonNull XmlResourceParser parser, @IntRange(from = 0) int attrIndex) { - int colorResource = parser.getAttributeResourceValue(attrIndex, 0); - - if (colorResource == RESOURCE_NOT_FOUND) { - try { - String colorValue = parser.getAttributeValue(attrIndex); - return Color.parseColor(colorValue); - } catch (Exception ignored) { - return COLOR_NOT_SET; - } - } - - return ContextCompat.getColor(context, colorResource); - } - - @Retention(RetentionPolicy.SOURCE) - @StringDef({ - ID, - ICON, - TITLE, - INACTIVE_COLOR, - ACTIVE_COLOR, - BAR_COLOR_WHEN_SELECTED, - BADGE_BACKGROUND_COLOR, - BADGE_HIDES_WHEN_ACTIVE, - IS_TITLELESS - }) - @interface TabAttribute { - String ID = "id"; - String ICON = "icon"; - String TITLE = "title"; - String INACTIVE_COLOR = "inActiveColor"; - String ACTIVE_COLOR = "activeColor"; - String BAR_COLOR_WHEN_SELECTED = "barColorWhenSelected"; - String BADGE_BACKGROUND_COLOR = "badgeBackgroundColor"; - String BADGE_HIDES_WHEN_ACTIVE = "badgeHidesWhenActive"; - String IS_TITLELESS = "iconOnly"; - } - - @SuppressWarnings("WeakerAccess") - public static class TabParserException extends RuntimeException { - // This class is just to be able to have a type of Runtime Exception that will make it clear where the error originated. - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/TabSelectionInterceptor.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/TabSelectionInterceptor.java deleted file mode 100644 index d510f6f8c..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/TabSelectionInterceptor.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.habitrpg.android.habitica.ui.views.bottombar; - -import androidx.annotation.IdRes; - -/* - * BottomBar library for Android - * Copyright (c) 2016 Iiro Krankka (http://github.com/roughike). - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -public interface TabSelectionInterceptor { - /** - * The method being called when currently visible {@link BottomBarTab} is about to change. - *

- * This listener is fired when the current {@link BottomBar} is about to change. This gives - * an opportunity to interrupt the {@link BottomBarTab} change. - * - * @param oldTabId the currently visible {@link BottomBarTab} - * @param newTabId the {@link BottomBarTab} that will be switched to - * @return true if you want to override/stop the tab change, false to continue as normal - */ - boolean shouldInterceptTabSelection(@IdRes int oldTabId, @IdRes int newTabId); -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/VerticalScrollingBehavior.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/VerticalScrollingBehavior.java deleted file mode 100644 index ab1e5bea8..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/bottombar/VerticalScrollingBehavior.java +++ /dev/null @@ -1,150 +0,0 @@ -package com.habitrpg.android.habitica.ui.views.bottombar; - -import android.content.Context; -import android.os.Parcelable; -import androidx.annotation.IntDef; -import androidx.annotation.NonNull; -import androidx.coordinatorlayout.widget.CoordinatorLayout; -import androidx.core.view.WindowInsetsCompat; - -import android.util.AttributeSet; -import android.view.View; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Created by Nikola D. on 11/22/2015. - * - * Credit goes to Nikola Despotoski: - * https://github.com/NikolaDespotoski - */ -abstract class VerticalScrollingBehavior extends CoordinatorLayout.Behavior { - - private int totalDyUnconsumed = 0; - private int totalDy = 0; - @ScrollDirection - private int overScrollDirection = ScrollDirection.SCROLL_NONE; - @ScrollDirection - private int scrollDirection = ScrollDirection.SCROLL_NONE; - - VerticalScrollingBehavior(Context context, AttributeSet attrs) { - super(context, attrs); - } - - VerticalScrollingBehavior() { - super(); - } - - @Retention(RetentionPolicy.SOURCE) - @IntDef({ScrollDirection.SCROLL_DIRECTION_UP, ScrollDirection.SCROLL_DIRECTION_DOWN, ScrollDirection.SCROLL_NONE}) - @interface ScrollDirection { - int SCROLL_DIRECTION_UP = 1; - int SCROLL_DIRECTION_DOWN = -1; - int SCROLL_NONE = 0; - } - - - /* - @return Overscroll direction: SCROLL_DIRECTION_UP, CROLL_DIRECTION_DOWN, SCROLL_NONE - */ - @ScrollDirection - int getOverScrollDirection() { - return overScrollDirection; - } - - - /** - * @return Scroll direction: SCROLL_DIRECTION_UP, SCROLL_DIRECTION_DOWN, SCROLL_NONE - */ - - @ScrollDirection - int getScrollDirection() { - return scrollDirection; - } - - - /** - * @param coordinatorLayout - * @param child - * @param direction Direction of the overscroll: SCROLL_DIRECTION_UP, SCROLL_DIRECTION_DOWN - * @param currentOverScroll Unconsumed value, negative or positive based on the direction; - * @param totalOverScroll Cumulative value for current direction - */ - abstract void onNestedVerticalOverScroll(CoordinatorLayout coordinatorLayout, V child, @ScrollDirection int direction, int currentOverScroll, int totalOverScroll); - - /** - * @param scrollDirection Direction of the overscroll: SCROLL_DIRECTION_UP, SCROLL_DIRECTION_DOWN - */ - abstract void onDirectionNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dx, int dy, int[] consumed, @ScrollDirection int scrollDirection); - - @Override - public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, V child, View directTargetChild, View target, int nestedScrollAxes) { - return (nestedScrollAxes & View.SCROLL_AXIS_VERTICAL) != 0; - } - - @Override - public void onNestedScrollAccepted(CoordinatorLayout coordinatorLayout, V child, View directTargetChild, View target, int nestedScrollAxes) { - super.onNestedScrollAccepted(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes); - } - - @Override - public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) { - super.onStopNestedScroll(coordinatorLayout, child, target); - } - - @Override - public void onNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) { - super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed); - if (dyUnconsumed > 0 && totalDyUnconsumed < 0) { - totalDyUnconsumed = 0; - overScrollDirection = ScrollDirection.SCROLL_DIRECTION_UP; - } else if (dyUnconsumed < 0 && totalDyUnconsumed > 0) { - totalDyUnconsumed = 0; - overScrollDirection = ScrollDirection.SCROLL_DIRECTION_DOWN; - } - totalDyUnconsumed += dyUnconsumed; - onNestedVerticalOverScroll(coordinatorLayout, child, overScrollDirection, dyConsumed, totalDyUnconsumed); - } - - @Override - public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dx, int dy, int[] consumed) { - super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed); - if (dy > 0 && totalDy < 0) { - totalDy = 0; - scrollDirection = ScrollDirection.SCROLL_DIRECTION_UP; - } else if (dy < 0 && totalDy > 0) { - totalDy = 0; - scrollDirection = ScrollDirection.SCROLL_DIRECTION_DOWN; - } - totalDy += dy; - onDirectionNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, scrollDirection); - } - - - @Override - public boolean onNestedFling(CoordinatorLayout coordinatorLayout, V child, View target, float velocityX, float velocityY, boolean consumed) { - super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed); - scrollDirection = velocityY > 0 ? ScrollDirection.SCROLL_DIRECTION_UP : ScrollDirection.SCROLL_DIRECTION_DOWN; - return onNestedDirectionFling(coordinatorLayout, child, target, velocityX, velocityY, scrollDirection); - } - - abstract boolean onNestedDirectionFling(CoordinatorLayout coordinatorLayout, V child, View target, float velocityX, float velocityY, @ScrollDirection int scrollDirection); - - @Override - public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, V child, View target, float velocityX, float velocityY) { - return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY); - } - - @NonNull - @Override - public WindowInsetsCompat onApplyWindowInsets(CoordinatorLayout coordinatorLayout, V child, WindowInsetsCompat insets) { - return super.onApplyWindowInsets(coordinatorLayout, child, insets); - } - - @Override - public Parcelable onSaveInstanceState(CoordinatorLayout parent, V child) { - return super.onSaveInstanceState(parent, child); - } - -} \ No newline at end of file diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/navigation/BottomNavigationItem.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/navigation/BottomNavigationItem.kt new file mode 100644 index 000000000..d5393bf8d --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/navigation/BottomNavigationItem.kt @@ -0,0 +1,52 @@ +package com.habitrpg.android.habitica.ui.views.navigation + +import android.content.Context +import android.graphics.PorterDuff +import android.util.AttributeSet +import android.view.View +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.TextView +import androidx.core.content.ContextCompat +import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.extensions.inflate +import com.habitrpg.android.habitica.ui.helpers.bindView + +class BottomNavigationItem @JvmOverloads constructor( + context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 +) : LinearLayout(context, attrs, defStyleAttr) { + + private val iconView: ImageView by bindView(R.id.icon_view) + private val selectedTitleView: TextView by bindView(R.id.selected_title_view) + private val titleView: TextView by bindView(R.id.title_view) + + var isActive = false + set(value) { + field = value + if (isActive) { + selectedTitleView.visibility = View.VISIBLE + titleView.visibility = View.GONE + iconView.drawable.setColorFilter(ContextCompat.getColor(context, R.color.white), PorterDuff.Mode.MULTIPLY ) + } else { + selectedTitleView.visibility = View.GONE + titleView.visibility = View.VISIBLE + iconView.drawable.setColorFilter(ContextCompat.getColor(context, R.color.brand_500), PorterDuff.Mode.MULTIPLY ) + } + } + + init { + inflate(R.layout.bottom_navigation_item, true) + + val attributes = context.theme?.obtainStyledAttributes( + attrs, + R.styleable.BottomNavigationItem, + 0, 0) + if (attributes != null) { + iconView.setImageDrawable(attributes.getDrawable(R.styleable.BottomNavigationItem_iconDrawable)) + titleView.text = attributes.getString(R.styleable.BottomNavigationItem_title) + selectedTitleView.text = attributes.getString(R.styleable.BottomNavigationItem_title) + } + orientation = VERTICAL + } + +} \ No newline at end of file diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/navigation/HabiticaBottomNavigationView.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/navigation/HabiticaBottomNavigationView.kt new file mode 100644 index 000000000..f3f6abab0 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/navigation/HabiticaBottomNavigationView.kt @@ -0,0 +1,66 @@ +package com.habitrpg.android.habitica.ui.views.navigation + +import android.content.Context +import android.util.AttributeSet +import android.widget.ImageButton +import android.widget.RelativeLayout +import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.extensions.inflate +import com.habitrpg.android.habitica.models.tasks.Task +import com.habitrpg.android.habitica.ui.helpers.bindView + +class HabiticaBottomNavigationView @JvmOverloads constructor( + context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 +) : RelativeLayout(context, attrs, defStyleAttr) { + + var selectedPosition: Int + get() { + return when (activeTaskType) { + Task.TYPE_DAILY -> 1 + Task.TYPE_REWARD -> 2 + Task.TYPE_TODO -> 3 + else -> 0 + } + } + set(value) { + activeTaskType = when (value) { + 1 -> Task.TYPE_DAILY + 2 -> Task.TYPE_TODO + 3 -> Task.TYPE_REWARD + else -> Task.TYPE_HABIT + } + } + var onTabSelectedListener: ((String) -> Unit)? = null + var onAddListener: ((String) -> Unit)? = null + var activeTaskType: String = Task.TYPE_HABIT + set(value) { + field = value + updateItemSelection() + onTabSelectedListener?.invoke(value) + } + + private val habitsTab: BottomNavigationItem by bindView(R.id.tab_habits) + private val dailiesTab: BottomNavigationItem by bindView(R.id.tab_dailies) + private val todosTab: BottomNavigationItem by bindView(R.id.tab_todos) + private val rewardsTab: BottomNavigationItem by bindView(R.id.tab_rewards) + private val addButton: ImageButton by bindView(R.id.add) + + init { + inflate(R.layout.main_navigation_view, true) + habitsTab.setOnClickListener { activeTaskType = Task.TYPE_HABIT } + dailiesTab.setOnClickListener { activeTaskType = Task.TYPE_DAILY } + todosTab.setOnClickListener { activeTaskType = Task.TYPE_TODO } + rewardsTab.setOnClickListener { activeTaskType = Task.TYPE_REWARD } + addButton.setOnClickListener { + onAddListener?.invoke(activeTaskType) + } + updateItemSelection() + } + + private fun updateItemSelection() { + habitsTab.isActive = activeTaskType == Task.TYPE_HABIT + dailiesTab.isActive = activeTaskType == Task.TYPE_DAILY + todosTab.isActive = activeTaskType == Task.TYPE_TODO + rewardsTab.isActive = activeTaskType == Task.TYPE_REWARD + } +} \ No newline at end of file