Full Code of roughike/BottomBar for AI

master 885d70e62359 cached
105 files
576.4 KB
147.6k tokens
339 symbols
1 requests
Download .txt
Showing preview only (612K chars total). Download the full file or copy to clipboard to get everything.
Repository: roughike/BottomBar
Branch: master
Commit: 885d70e62359
Files: 105
Total size: 576.4 KB

Directory structure:
gitextract_7tzdrylq/

├── .gitignore
├── .idea/
│   ├── .name
│   ├── compiler.xml
│   ├── copyright/
│   │   └── profiles_settings.xml
│   ├── encodings.xml
│   ├── gradle.xml
│   ├── kotlinc.xml
│   ├── markdown-exported-files.xml
│   ├── markdown-navigator/
│   │   └── profiles_settings.xml
│   ├── markdown-navigator.xml
│   ├── misc.xml
│   ├── modules.xml
│   ├── runConfigurations/
│   │   └── All_Tests.xml
│   ├── runConfigurations.xml
│   └── vcs.xml
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── NOTICE
├── README.md
├── app/
│   ├── build.gradle
│   ├── proguard-rules.pro
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── assets/
│           │   └── fonts/
│           │       ├── GREAT-VIBES-LICENSE
│           │       └── GreatVibes-Regular.otf
│           ├── java/
│           │   └── com/
│           │       └── example/
│           │           └── bottombar/
│           │               └── sample/
│           │                   ├── BadgeActivity.java
│           │                   ├── CustomColorAndFontActivity.java
│           │                   ├── FiveColorChangingTabsActivity.java
│           │                   ├── IconsOnlyActivity.java
│           │                   ├── MainActivity.java
│           │                   ├── SampleFragment.java
│           │                   ├── TabMessage.java
│           │                   ├── ThreeTabsActivity.java
│           │                   └── ThreeTabsQRActivity.java
│           └── res/
│               ├── layout/
│               │   ├── activity_color_changing_tabs.xml
│               │   ├── activity_custom_color_and_font.xml
│               │   ├── activity_icons_only.xml
│               │   ├── activity_main.xml
│               │   ├── activity_three_tabs.xml
│               │   └── activity_three_tabs_quick_return.xml
│               ├── layout-sw600dp/
│               │   ├── activity_color_changing_tabs.xml
│               │   └── activity_three_tabs.xml
│               ├── values/
│               │   ├── colors.xml
│               │   ├── dimens.xml
│               │   ├── strings.xml
│               │   └── styles.xml
│               ├── values-land-v21/
│               │   └── styles.xml
│               ├── values-sw600dp/
│               │   └── styles.xml
│               ├── values-v21/
│               │   └── styles.xml
│               ├── values-w820dp/
│               │   └── dimens.xml
│               └── xml/
│                   ├── bottombar_tabs_color_changing.xml
│                   ├── bottombar_tabs_five.xml
│                   └── bottombar_tabs_three.xml
├── bottom-bar/
│   ├── build.gradle
│   └── src/
│       ├── androidTest/
│       │   ├── assets/
│       │   │   └── fonts/
│       │   │       ├── GREAT-VIBES-LICENSE
│       │   │       └── GreatVibes-Regular.otf
│       │   ├── java/
│       │   │   └── com/
│       │   │       └── roughike/
│       │   │           └── bottombar/
│       │   │               ├── BadgeTest.java
│       │   │               ├── BottomBarTabTest.java
│       │   │               ├── BottomBarTest.java
│       │   │               └── TabParserTest.java
│       │   └── res/
│       │       ├── values/
│       │       │   ├── colors.xml
│       │       │   ├── strings.xml
│       │       │   └── styles.xml
│       │       └── xml/
│       │           ├── dummy_tabs_five.xml
│       │           └── dummy_tabs_three.xml
│       ├── main/
│       │   ├── AndroidManifest.xml
│       │   ├── java/
│       │   │   └── com/
│       │   │       └── roughike/
│       │   │           └── bottombar/
│       │   │               ├── BadgeCircle.java
│       │   │               ├── BadgeContainer.java
│       │   │               ├── BatchTabPropertyApplier.java
│       │   │               ├── BottomBar.java
│       │   │               ├── BottomBarBadge.java
│       │   │               ├── BottomBarTab.java
│       │   │               ├── BottomNavigationBehavior.java
│       │   │               ├── MiscUtils.java
│       │   │               ├── NavbarUtils.java
│       │   │               ├── OnTabReselectListener.java
│       │   │               ├── OnTabSelectListener.java
│       │   │               ├── ShySettings.java
│       │   │               ├── TabParser.java
│       │   │               ├── TabSelectionInterceptor.java
│       │   │               └── VerticalScrollingBehavior.java
│       │   └── res/
│       │       ├── drawable/
│       │       │   └── bb_bottom_bar_top_shadow.xml
│       │       ├── layout/
│       │       │   ├── bb_bottom_bar_item_container.xml
│       │       │   ├── bb_bottom_bar_item_container_tablet.xml
│       │       │   ├── bb_bottom_bar_item_fixed.xml
│       │       │   ├── bb_bottom_bar_item_fixed_tablet.xml
│       │       │   ├── bb_bottom_bar_item_shifting.xml
│       │       │   └── bb_bottom_bar_item_titleless.xml
│       │       ├── layout-v21/
│       │       │   └── bb_bottom_bar_item_container.xml
│       │       ├── values/
│       │       │   ├── attrs.xml
│       │       │   ├── bools.xml
│       │       │   ├── colors.xml
│       │       │   ├── dimensions.xml
│       │       │   ├── ids.xml
│       │       │   ├── strings.xml
│       │       │   └── styles.xml
│       │       └── values-land/
│       │           └── bools.xml
│       └── test/
│           └── java/
│               └── com/
│                   └── roughike/
│                       └── bottombar/
│                           └── ExampleUnitTest.java
├── build.gradle
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── projectFilesBackup/
│   └── .idea/
│       └── workspace.xml
└── settings.gradle

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# Built application files
*.apk
*.ap_

# Files for the ART/Dalvik VM
*.dex

# Java class files
*.class

# Generated files
bin/
gen/
out/

# Gradle files
.gradle/
build/

# Local configuration file (sdk path, etc)
local.properties

# Proguard folder generated by Eclipse
proguard/

# Log Files
*.log

# Android Studio Navigation editor temp files
.navigation/

# Android Studio captures folder
captures/

# Intellij
*.iml
.idea/workspace.xml
.idea/libraries

# Keystore files
*.jks


================================================
FILE: .idea/.name
================================================
BottomBar

================================================
FILE: .idea/compiler.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="CompilerConfiguration">
    <resourceExtensions />
    <wildcardResourcePatterns>
      <entry name="!?*.java" />
      <entry name="!?*.form" />
      <entry name="!?*.class" />
      <entry name="!?*.groovy" />
      <entry name="!?*.scala" />
      <entry name="!?*.flex" />
      <entry name="!?*.kt" />
      <entry name="!?*.clj" />
      <entry name="!?*.aj" />
    </wildcardResourcePatterns>
    <annotationProcessing>
      <profile default="true" name="Default" enabled="false">
        <processorPath useClasspath="true" />
      </profile>
    </annotationProcessing>
  </component>
</project>

================================================
FILE: .idea/copyright/profiles_settings.xml
================================================
<component name="CopyrightManager">
  <settings default="" />
</component>

================================================
FILE: .idea/encodings.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="Encoding">
    <file url="PROJECT" charset="UTF-8" />
  </component>
</project>

================================================
FILE: .idea/gradle.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="GradleSettings">
    <option name="linkedExternalProjectsSettings">
      <GradleProjectSettings>
        <option name="distributionType" value="DEFAULT_WRAPPED" />
        <option name="externalProjectPath" value="$PROJECT_DIR$" />
        <option name="modules">
          <set>
            <option value="$PROJECT_DIR$" />
            <option value="$PROJECT_DIR$/app" />
            <option value="$PROJECT_DIR$/bottom-bar" />
          </set>
        </option>
        <option name="resolveModulePerSourceSet" value="false" />
      </GradleProjectSettings>
    </option>
  </component>
</project>

================================================
FILE: .idea/kotlinc.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="KotlinCommonCompilerArguments">
    <option name="languageVersion" value="1.1" />
    <option name="apiVersion" value="1.1" />
  </component>
</project>

================================================
FILE: .idea/markdown-exported-files.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="MarkdownExportedFiles">
    <htmlFiles />
    <imageFiles />
    <otherFiles />
  </component>
</project>

================================================
FILE: .idea/markdown-navigator/profiles_settings.xml
================================================
<component name="MarkdownNavigator.ProfileManager">
  <settings default="" />
</component>

================================================
FILE: .idea/markdown-navigator.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="MarkdownProjectSettings">
    <PreviewSettings splitEditorLayout="SPLIT" splitEditorPreview="PREVIEW" useGrayscaleRendering="false" zoomFactor="1.0" maxImageWidth="0" showGitHubPageIfSynced="false" allowBrowsingInPreview="false" synchronizePreviewPosition="true" highlightPreviewType="NONE" highlightFadeOut="5" highlightOnTyping="true" synchronizeSourcePosition="true">
      <PanelProvider>
        <provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.panel" providerName="Default - Swing" />
      </PanelProvider>
    </PreviewSettings>
    <ParserSettings>
      <PegdownExtensions>
        <option name="ABBREVIATIONS" value="false" />
        <option name="ANCHORLINKS" value="true" />
        <option name="ATXHEADERSPACE" value="true" />
        <option name="AUTOLINKS" value="true" />
        <option name="DEFINITIONS" value="false" />
        <option name="FENCED_CODE_BLOCKS" value="true" />
        <option name="FOOTNOTES" value="false" />
        <option name="HARDWRAPS" value="false" />
        <option name="INSERTED" value="false" />
        <option name="QUOTES" value="false" />
        <option name="RELAXEDHRULES" value="true" />
        <option name="SMARTS" value="false" />
        <option name="STRIKETHROUGH" value="true" />
        <option name="SUBSCRIPT" value="false" />
        <option name="SUPERSCRIPT" value="false" />
        <option name="SUPPRESS_HTML_BLOCKS" value="false" />
        <option name="SUPPRESS_INLINE_HTML" value="false" />
        <option name="TABLES" value="true" />
        <option name="TASKLISTITEMS" value="true" />
        <option name="TOC" value="false" />
        <option name="WIKILINKS" value="true" />
      </PegdownExtensions>
      <ParserOptions>
        <option name="COMMONMARK_LISTS" value="false" />
        <option name="DUMMY" value="false" />
        <option name="EMOJI_SHORTCUTS" value="true" />
        <option name="FLEXMARK_FRONT_MATTER" value="false" />
        <option name="GFM_TABLE_RENDERING" value="true" />
        <option name="GITBOOK_URL_ENCODING" value="false" />
        <option name="GITHUB_EMOJI_URL" value="false" />
        <option name="GITHUB_LISTS" value="true" />
        <option name="GITHUB_WIKI_LINKS" value="true" />
        <option name="JEKYLL_FRONT_MATTER" value="false" />
        <option name="SIM_TOC_BLANK_LINE_SPACER" value="true" />
      </ParserOptions>
    </ParserSettings>
    <HtmlSettings headerTopEnabled="false" headerBottomEnabled="false" bodyTopEnabled="false" bodyBottomEnabled="false" embedUrlContent="false" addPageHeader="true">
      <GeneratorProvider>
        <provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.generator" providerName="Default Swing HTML Generator" />
      </GeneratorProvider>
      <headerTop />
      <headerBottom />
      <bodyTop />
      <bodyBottom />
    </HtmlSettings>
    <CssSettings previewScheme="UI_SCHEME" cssUri="" isCssUriEnabled="false" isCssTextEnabled="false" isDynamicPageWidth="true">
      <StylesheetProvider>
        <provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.css" providerName="Default Swing Stylesheet" />
      </StylesheetProvider>
      <ScriptProviders />
      <cssText />
    </CssSettings>
    <HtmlExportSettings updateOnSave="false" parentDir="$ProjectFileDir$" targetDir="$ProjectFileDir$" cssDir="" scriptDir="" plainHtml="false" imageDir="" copyLinkedImages="false" imageUniquifyType="0" targetExt="" useTargetExt="false" noCssNoScripts="false" linkToExportedHtml="true" exportOnSettingsChange="true" regenerateOnProjectOpen="false" />
    <LinkMapSettings>
      <textMaps />
    </LinkMapSettings>
  </component>
</project>

================================================
FILE: .idea/misc.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="EntryPointsManager">
    <entry_points version="2.0" />
  </component>
  <component name="NullableNotNullManager">
    <option name="myDefaultNullable" value="android.support.annotation.Nullable" />
    <option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
    <option name="myNullables">
      <value>
        <list size="4">
          <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
          <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
          <item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
          <item index="3" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
        </list>
      </value>
    </option>
    <option name="myNotNulls">
      <value>
        <list size="4">
          <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
          <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
          <item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
          <item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
        </list>
      </value>
    </option>
  </component>
  <component name="ProjectLevelVcsManager" settingsEditedManually="false">
    <OptionsSetting value="true" id="Add" />
    <OptionsSetting value="true" id="Remove" />
    <OptionsSetting value="true" id="Checkout" />
    <OptionsSetting value="true" id="Update" />
    <OptionsSetting value="true" id="Status" />
    <OptionsSetting value="true" id="Edit" />
    <ConfirmationsSetting value="0" id="Add" />
    <ConfirmationsSetting value="0" id="Remove" />
  </component>
  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
    <output url="file://$PROJECT_DIR$/build/classes" />
  </component>
  <component name="ProjectType">
    <option name="id" value="Android" />
  </component>
</project>

================================================
FILE: .idea/modules.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="ProjectModuleManager">
    <modules>
      <module fileurl="file://$PROJECT_DIR$/BottomBar.iml" filepath="$PROJECT_DIR$/BottomBar.iml" />
      <module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
      <module fileurl="file://$PROJECT_DIR$/bottom-bar/bottom-bar.iml" filepath="$PROJECT_DIR$/bottom-bar/bottom-bar.iml" />
    </modules>
  </component>
</project>

================================================
FILE: .idea/runConfigurations/All_Tests.xml
================================================
<component name="ProjectRunConfigurationManager">
  <configuration default="false" name="All Tests" type="AndroidTestRunConfigurationType" factoryName="Android Tests">
    <module name="bottom-bar" />
    <option name="TESTING_TYPE" value="1" />
    <option name="INSTRUMENTATION_RUNNER_CLASS" value="android.support.test.runner.AndroidJUnitRunner" />
    <option name="METHOD_NAME" value="" />
    <option name="CLASS_NAME" value="" />
    <option name="PACKAGE_NAME" value="com.roughike.bottombar" />
    <option name="EXTRA_OPTIONS" value="" />
    <option name="TARGET_SELECTION_MODE" value="SHOW_DIALOG" />
    <option name="PREFERRED_AVD" value="" />
    <option name="CLEAR_LOGCAT" value="false" />
    <option name="SHOW_LOGCAT_AUTOMATICALLY" value="true" />
    <option name="SKIP_NOOP_APK_INSTALLATIONS" value="true" />
    <option name="FORCE_STOP_RUNNING_APP" value="true" />
    <option name="DEBUGGER_TYPE" value="Java" />
    <option name="USE_LAST_SELECTED_DEVICE" value="true" />
    <option name="PREFERRED_AVD" value="" />
    <option name="SELECTED_CLOUD_MATRIX_CONFIGURATION_ID" value="-1" />
    <option name="SELECTED_CLOUD_MATRIX_PROJECT_ID" value="" />
    <Auto>
      <option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
      <option name="WORKING_DIR" value="" />
      <option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
    </Auto>
    <Hybrid>
      <option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
      <option name="WORKING_DIR" value="" />
      <option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
    </Hybrid>
    <Java />
    <Native>
      <option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
      <option name="WORKING_DIR" value="" />
      <option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
    </Native>
    <Profilers>
      <option name="ENABLE_ADVANCED_PROFILING" value="true" />
      <option name="GAPID_ENABLED" value="false" />
      <option name="GAPID_DISABLE_PCS" value="false" />
      <option name="SUPPORT_LIB_ENABLED" value="true" />
      <option name="INSTRUMENTATION_ENABLED" value="true" />
    </Profilers>
    <method />
  </configuration>
</component>

================================================
FILE: .idea/runConfigurations.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="RunConfigurationProducerService">
    <option name="ignoredProducers">
      <set>
        <option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
        <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
        <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
      </set>
    </option>
  </component>
</project>

================================================
FILE: .idea/vcs.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="VcsDirectoryMappings">
    <mapping directory="$PROJECT_DIR$" vcs="Git" />
  </component>
</project>

================================================
FILE: .travis.yml
================================================
language: android

jdk: oraclejdk8

android:
  components:
    - tools
    - platform-tools
    - build-tools-25.0.2
    - android-25
    - extra-android-m2repository

before_script:
  - echo no | android create avd --force -n test -t android-18 --abi armeabi-v7a
  - emulator -avd test -no-audio -no-window &
  - android-wait-for-emulator
  - adb shell input keyevent 82 &

script:
   - ./gradlew connectedAndroidTest coveralls

================================================
FILE: CHANGELOG.md
================================================
## Changelog

### 2.3.1

* [#749](https://github.com/roughike/BottomBar/pull/749): Quick fix for the issue where *bb_showShadow* set to false didn't have any effect. Thanks @yombunker!

### 2.3.0

* [#713](https://github.com/roughike/BottomBar/pull/713): Ripple touch feedback for tabs!
* [#716](https://github.com/roughike/BottomBar/pull/716): Bugfix for misbehaving shadow. No more weird white spaces above the bar!
* [#717](https://github.com/roughike/BottomBar/pull/717): Support for tabs with icons only, without titles!
* [#722](https://github.com/roughike/BottomBar/pull/722): Showing / hiding the BottomBar when on shy mode.
* [#714](https://github.com/roughike/BottomBar/pull/714): Controlling whether Toasts of tab titles are shown when long pressing tabs.
* [#719](https://github.com/roughike/BottomBar/pull/719): Fix for wrong size in tabs
* [#712](https://github.com/roughike/BottomBar/pull/712): Data binding fixes.

Thanks for @yombunker, @MarcRubio and @tushar-acharya for their contributions!

### 2.2.0

* Ability to change icons when the tabs are selected, using drawable selectors
* Overriding tab selections is now supported, by using [TabSelectionInterceptor](https://github.com/roughike/BottomBar/blob/master/bottom-bar/src/main/java/com/roughike/bottombar/TabSelectionInterceptor.java)
* Internal code quality improvements and small changes

### 2.2.0

* Ability to change icons when the tabs are selected, using drawable selectors
* Overriding tab selections is now supported, by using [TabSelectionInterceptor](https://github.com/roughike/BottomBar/blob/master/bottom-bar/src/main/java/com/roughike/bottombar/TabSelectionInterceptor.java)
* Internal code quality improvements and small changes

### 2.2.0

* Ability to change icons when the tabs are selected, using drawable selectors
* Overriding tab selections is now supported, by using [TabSelectionInterceptor](https://github.com/roughike/BottomBar/blob/master/bottom-bar/src/main/java/com/roughike/bottombar/TabSelectionInterceptor.java)
* Internal code quality improvements and small changes

### 2.1.2

* Merged [#703](https://github.com/roughike/BottomBar/pull/703) that allows controlling badge visibility for tabs that are active.

### 2.1.1

* A quick fix for a really critical bug that could affect some devices. More specifically, [this one.](https://github.com/roughike/BottomBar/issues/625)

### 2.1.0

* Fixed a bug in the Badge positioning, causing the Badges to clip when there was many tabs.
* Fixed a bug where the lower portion of unselected titles were clipped off in fixed mode.
* Made changes to Badge restoration logic to fix [445](https://github.com/roughike/BottomBar/issues/445). Credit goes to [@Kevinrob](https://github.com/Kevinrob) for reporting and helping to reproduce the issue.
* Fixed [#448](https://github.com/roughike/BottomBar/issues/448), [#471](https://github.com/roughike/BottomBar/issues/471), [#436](https://github.com/roughike/BottomBar/issues/436) and [#591](https://github.com/roughike/BottomBar/issues/591)
* Fixed a faulty behavior where the tabs' widths were calculated according to phone screen width, but should've been calculated according to the parent view's width by merging [#504](https://github.com/roughike/BottomBar/pull/504) 
* Optimized the tab resizing calculations; now the tabs aren't needlessly removed and readded, only the layout params get changed.
* Merged [#468](https://github.com/roughike/BottomBar/pull/468) and [#457](https://github.com/roughike/BottomBar/pull/457)
* Fixed [#554](https://github.com/roughike/BottomBar/issues/554) by merging [#512](https://github.com/roughike/BottomBar/pull/512).
* Made most of the BottomBarTab methods public.

### 2.0.2

* Now we're animating the color change on tab titles and icons.
* Fixed a bug where the BottomBar wouldn't hide completely when it was both shy and drawing under navbar.
* Made possible to inflate the BottomBar programmatically.
* Made it possible to control whether the shadow is shown or not.
* Made setItems to be public to allow writing tests without a designated Activity
* Made setters for allowing setting tab colors, alphas, textappearances and typefaces programmatically.
* Increased test coverage a little bit.

### 2.0.1

* Fixed a bug where the tab selection listener is set and multiple tabs could be selected at once
* Fixed a bug where the reselection listener was fired even it shouldn't have.

### 2.0

* Cleaner code and better APIs
* No more unnecessary stuff or spaghetti mess
* Now the look, feel and behavior is defined in XML, as it should be
* No more nasty regressions, thanks to the automated tests
* **Everything is a little different compared to earlier, but it's for the greater good!**

See the readme for how to use the new version.

### 1.4.0.1

* Merged a [PR that fixes removing all tabs from BottomBar](https://github.com/roughike/BottomBar/pull/339)

### 1.4.0

* Started writing some tests. **Contributions more than welcome**, as I'm still a noob when it comes to testing.
* Merged a [PR that adds support for vector drawables](https://github.com/roughike/BottomBar/pull/280)
* Merged a [PR that adds support for disabling text scale animation](https://github.com/roughike/BottomBar/pull/298)
* Merged a [PR that adds support for custom background and tab icon colors, and also custom alpha](https://github.com/roughike/BottomBar/pull/302)
* Merged a [PR that fixes wrong method name for message shown by an exception](https://github.com/roughike/BottomBar/pull/320)

### 1.3.9

* Merged [another PR that should get rid of the infinite Badge loop for good](https://github.com/roughike/BottomBar/pull/289).

### 1.3.8

* Merged a [PR that fixes infinite loop caused by a layout listener when adding Badges.](https://github.com/roughike/BottomBar/pull/286)

### 1.3.7

* Merged a [PR that fixes elliptical Badges](https://github.com/roughike/BottomBar/pull/275).
* Fixed issues [#276](https://github.com/roughike/BottomBar/issues/276) and [#277](https://github.com/roughike/BottomBar/issues/277)

### 1.3.6

* Fixed a bug that would cause the navigation bar to not be transparent.
* Flattened View hierarchy.
* Throwing a nice little Exception if someone tries to call ```noResizeGoodness()``` improperly, instead of just failing silently.

### 1.3.5

* [Merged](https://github.com/roughike/BottomBar/pull/260) [some](https://github.com/roughike/BottomBar/pull/268) [pull](https://github.com/roughike/BottomBar/pull/269) [requests.](https://github.com/roughike/BottomBar/pull/271)
* Thanks to @henhal, now the unselection bug when using badges is fixed.
* Deprecated the ```setItemsFromMenu(@MenuRes int resId, OnMenuTabClickListener listener)``` method in favor of two separate methods: ```mBottomBar.setItems(@MenuRes int resId)``` and ```mBottomBar.setOnMenuTabClickListener(OnMenuTabClickListener listener)```. Not only because deprecating stuff is so fun (it is), but because this actually makes more sense than the old approach. The old approach still works.

### 1.3.4

* Now the BottomBar is 56dp tall, as it should be! Make sure your icons are 24dp and **trimmed**, meaning that the whole png size musn't be more than 24dp. So **don't use padding** around the icon.
* Fixed a minor bug when programmatically selecting a tab.
* Added a ```setAutoHideOnSelection(boolean autoHideOnSelection)``` method for the BottomBarBadge to control whether it is automatically hidden after the tab that contains it is selected or not. 
* Titles are now forced to be single line, make sure your title texts are short enough, or else they'll get truncated with a "..." !
* Updated some dependencies and Gradle.

### 1.3.3

* The show / hide methods now behave nicely with CoordinatorLayout.
* Added alpha animation for the tab titles when using the shifting tabs.

### 1.3.2

* Now it's possible to use fixed mode (show titles on inactive tabs) even when there's more than three tabs.

### 1.3.1

* Fixed a critical bug in OnLongClickListener behavior (why didn't I see that before?) when using badges.

### 1.3.0

* Fixed a critical bug in OnClickListener behavior when using badges.

### 1.2.9

* Fixed the issue when using badges and the tab resize animation is enabled. Now the badges automatically adjust their position when the tab's size (or position) updates.

### 1.2.8

* Fixed the [ugly layout bug](https://github.com/roughike/BottomBar/issues/126) that happened when calling the ```setDefaultTabPosition()``` or ```selectTabAtPosition()``` methods.

### Versions 0.0.1 - 1.2.7

* Sweating my ass off making this library and trying to compete with the other ones.


================================================
FILE: LICENSE
================================================
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

================================================
FILE: NOTICE
================================================
BottomBar library for Android
Copyright 2016 Iiro Krankka

This product includes software developed by
Iiro Krankka (https://github.com/roughike).

================================================
FILE: README.md
================================================
# BottomBar (Deprecated)

I don't have time to maintain this anymore. I basically wrote the whole library in a rush, without tests, while being a serious expert beginner at the time. As a result, there's a lot of unpredictable moving parts and the tests probably aren't that great either. Don't really know, since I haven't touched this in ages.

I'd recommend you to use the official BottomNavigationView from Google and urge them to implement the features you need. Or use another 3rd party library.

[![Build Status](https://travis-ci.org/roughike/BottomBar.svg?branch=master)](https://travis-ci.org/roughike/BottomBar) [![Coverage Status](https://coveralls.io/repos/github/roughike/BottomBar/badge.svg?branch=development)](https://coveralls.io/github/roughike/BottomBar?branch=master) [![Download](https://api.bintray.com/packages/roughike/maven/bottom-bar/images/download.svg)](https://bintray.com/roughike/maven/bottom-bar/_latestVersion)

<img src="https://raw.githubusercontent.com/roughike/BottomBar/master/graphics/shy-demo.gif" width="30%" /> <img src="https://raw.githubusercontent.com/roughike/BottomBar/master/graphics/shifting-demo.gif" width="30%" /> <img src="https://raw.githubusercontent.com/roughike/BottomBar/master/graphics/screenshot_tablet.png" width="33%" />

## Version 2.0 released!

[The latest version before that can be found in the v1 branch](https://github.com/roughike/BottomBar/tree/v1)

* Cleaner code and better APIs
* No more unnecessary stuff or spaghetti mess
* Now the look, feel and behavior is defined in XML, as it should be
* No more nasty regressions, thanks to the automated tests
* **Everything is a little different compared to earlier, but it's for the greater good!**

[How to contribute](https://github.com/roughike/BottomBar/blob/master/README.md#contributions)

[Changelog](https://github.com/roughike/BottomBar/blob/master/CHANGELOG.md)

## What?

A custom view component that mimics the new [Material Design Bottom Navigation pattern](https://www.google.com/design/spec/components/bottom-navigation.html).

## Does it work on my Grandpa Gary's HTC Dream?

Nope. The minSDK version is **API level 11 (Honeycomb).**

## Gimme that Gradle sweetness, pls?

```groovy
compile 'com.roughike:bottom-bar:2.3.1'
```

**Maven:**
```xml
<dependency>
  <groupId>com.roughike</groupId>
  <artifactId>bottom-bar</artifactId>
  <version>2.3.1</version>
  <type>pom</type>
</dependency>
```

## How?

You can add items by **writing a XML resource file**.

### Creating the icons

The icons must be fully opaque, solid black color, 24dp and **with no padding**. For example, [with Android Asset Studio Generic Icon generator](https://romannurik.github.io/AndroidAssetStudio/icons-generic.html), select "TRIM" and make sure the padding is 0dp. Here's what your icons should look like:

![Sample icons](https://raw.githubusercontent.com/roughike/BottomBar/master/graphics/icons-howto.png)

### Adding items from XML resource

Define your tabs in an XML resource file.

**res/xml/bottombar_tabs.xml:**

```xml
<tabs>
    <tab
        id="@+id/tab_favorites"
        icon="@drawable/ic_favorites"
        title="Favorites" />
    <tab
        id="@+id/tab_nearby"
        icon="@drawable/ic_nearby"
        title="Nearby" />
    <tab
        id="@+id/tab_friends"
        icon="@drawable/ic_friends"
        title="Friends" />
</tabs>
```

Then, add the BottomBar to your layout and give it a resource id for your tabs xml file.

**layout/activity_main.xml**

```xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <!-- This could be your fragment container, or something -->
    <FrameLayout
        android:id="@+id/contentContainer"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/bottomBar" />

    <com.roughike.bottombar.BottomBar
        android:id="@+id/bottomBar"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:layout_alignParentBottom="true"
        app:bb_tabXmlResource="@xml/bottombar_tabs" />

</RelativeLayout>
```

### Setting up listeners

By default, the tabs don't do anything unless you listen for selection events and do something when the tabs are selected.

**MainActivity.java:**

```java
public class MainActivity extends Activity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        BottomBar bottomBar = (BottomBar) findViewById(R.id.bottomBar);
        bottomBar.setOnTabSelectListener(new OnTabSelectListener() {
            @Override
            public void onTabSelected(@IdRes int tabId) {
                if (tabId == R.id.tab_favorites) {
                    // The tab with id R.id.tab_favorites was selected,
                    // change your content accordingly.
                }
            }
        });
    }
}
```

If you want to listen for reselection events, here's how you do it:

```java
bottomBar.setOnTabReselectListener(new OnTabReselectListener() {
    @Override
    public void onTabReSelected(@IdRes int tabId) {
        if (tabId == R.id.tab_favorites) {
            // The tab with id R.id.tab_favorites was reselected,
            // change your content accordingly.
        }
    }
});
```

### Intercepting tab selections

If you want to conditionally cancel selection of any tab, you absolutely can. Just assign a ```TabSelectionInterceptor``` to the BottomBar, and return true from the ```shouldInterceptTabSelection()``` method.

```java
bottomBar.setTabSelectionInterceptor(new TabSelectionInterceptor() {
    @Override
    public boolean shouldInterceptTabSelection(@IdRes int oldTabId, @IdRes int newTabId) {
        if (newTabId == R.id.tab_pro_feature && !userHasProVersion()) {
          startProVersionPurchaseFlow();
          return true;
        }
        
        return false;
    }
});
```

### Changing icons based on selection state

If you want to have different icon when a specific tab is selected, just use state list drawables.

**res/drawable/my_tab_icon.xml**

```xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/ic_myicon_selected" android:state_selected="true" />
    <item android:drawable="@drawable/ic_myicon_default" android:state_selected="false" />
</selector>
```

**res/xml/bottombar_tabs.xml**

```xml
...
<tab
    id="@+id/tab_favorites"
    icon="@drawable/my_tab_icon"
    title="Favorites" />
<!-- You can use @color resources too! -->
...
```

### Those color changing tabs look dope. Howdoidodat?

Just add ```barColorWhenSelected``` to each tab. When that tab is selected, the whole BottomBar background color is changed with a nice animation.

**res/xml/bottombar_tabs.xml**

```xml
<tabs>
    <tab
        id="@+id/tab_favorites"
        icon="@drawable/ic_favorites"
        title="Favorites"
        barColorWhenSelected="#5D4037" />
    <!-- You can use @color resources too! -->
</tabs>
```

### How do I draw it under the navbar?

First, define a style that is a child of your main application theme:

**res/values-v21/styles.xml**

```xml
<style name="AppTheme.TransNav" parent="AppTheme">
    <item name="android:navigationBarColor">@android:color/transparent</item>
    <item name="android:windowTranslucentNavigation">true</item>
    <item name="android:windowDrawsSystemBarBackgrounds">true</item>
</style>
```

You'll also have to **make a stub version of the same theme** to avoid crashes in previous API levels than Lollipop:

**res/values/styles.xml**

```xml
<style name="AppTheme.TransNav" parent="AppTheme" />
```

Also include the same stub in your ```values-land-v21.xml``` to avoid transparent navbar and weird behavior on landscape.

**res/values-land-v21.xml:**

```xml
<style name="AppTheme.TransNav" parent="AppTheme" />
```

Apply the theme in ```AndroidManifest.xml``` for your Activity.

**AndroidManifest.xml:**

```xml
<activity android:name=".MyAwesomeActivity" android:theme="@style/AppTheme.TransNav" />
```

Finally, set ```bb_behavior``` to include the ```underNavbar``` flag and you're good to go!

**activity_my_awesome.xml:**

```xml
<com.roughike.bottombar.BottomBar
    android:id="@+id/bottomBar"
    android:layout_width="match_parent"
    android:layout_height="56dp"
    android:layout_alignParentBottom="true"
    app:bb_tabXmlResource="@xml/my_awesome_bottombar_tabs"
    app:bb_behavior="underNavbar" />
```

### What about Tablets?

Specify a different layout for your activity in ```res/layout-sw600dp``` folder and set ```bb_tabletMode``` to true.

**res/layout-sw600dp/activity_main.xml:**

```xml
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.roughike.bottombar.BottomBar
        android:id="@+id/bottomBar"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_alignParentLeft="true"
        app:bb_tabXmlResource="@xml/bottombar_tabs_three"
        app:bb_tabletMode="true" />

    <!-- This could be your fragment container, or something -->
    <FrameLayout
        android:id="@+id/contentContainer"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_toRightOf="@+id/bottomBar" />

</RelativeLayout>
```

### How do I hide it automatically on scroll?

Easy-peasy!

**activity_main.xml:**

```xml
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v4.widget.NestedScrollView
        android:id="@+id/myScrollingContent"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <!-- Your loooooong scrolling content here. -->

    </android.support.v4.widget.NestedScrollView>

    <com.roughike.bottombar.BottomBar
        android:id="@+id/bottomBar"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:layout_gravity="bottom"
        app:bb_tabXmlResource="@xml/bottombar_tabs_three"
        app:bb_behavior="shy"/>

</android.support.design.widget.CoordinatorLayout>
```

### Badges

You can easily add badges for showing an unread message count or new items / whatever you like.

```java
BottomBarTab nearby = bottomBar.getTabWithId(R.id.tab_nearby);
nearby.setBadgeCount(5);

// Remove the badge when you're done with it.
nearby.removeBadge/();
```

## All customization options

### For the BottomBar

```xml
<com.roughike.bottombar.BottomBar
    android:id="@+id/bottomBar"
    android:layout_width="match_parent"
    android:layout_height="60dp"
    android:layout_alignParentBottom="true"
    app:bb_tabXmlResource="@xml/bottombar_tabs_three"
    app:bb_tabletMode="true"
    app:bb_behavior="shifting|shy|underNavbar"
    app:bb_inActiveTabAlpha="0.6"
    app:bb_activeTabAlpha="1"
    app:bb_inActiveTabColor="#222222"
    app:bb_activeTabColor="@color/colorPrimary"
    app:bb_badgesHideWhenActive="true"
    app:bb_titleTextAppearance="@style/MyTextAppearance"
    app:bb_titleTypeFace="fonts/MySuperDuperFont.ttf"
    app:bb_showShadow="true" />
```

<dl>
    <dt>bb_tabXmlResource</dt>
    <dd>the XML Resource id for your tabs, that reside in <code>values/xml/</code></dd>
    <dt>bb_tabletMode</dt>
    <dd>if you want the BottomBar to behave differently for tablets. <u>There's an example of this in the sample project!</u></dd>
    <dt>bb_behavior</dt>
    <dd><code>shifting</code>: the selected tab is wider than the rest. <code>shy</code>: put the BottomBar inside a CoordinatorLayout and it'll automatically hide on scroll! <code>underNavbar</code>: draw the BottomBar under the navBar!</dd>
    <dt>bb_inActiveTabAlpha</dt>
    <dd>the alpha value for inactive tabs, that's used in the tab icons and titles.</dd>
    <dt>bb_activeTabAlpha</dt>
    <dd>the alpha value for active tabs, that's used in the tab icons and titles.</dd>
    <dt>bb_inActiveTabColor</dt>
    <dd>the color for inactive tabs, that's used in the tab icons and titles.</dd>
    <dt>bb_activeTabColor</dt>
    <dd>the color for active tabs, that's used in the tab icons and titles.</dd>
    <dt>bb_badgeBackgroundColor</dt>
    <dd>the background color for any Badges in this BottomBar.</dd>
    <dt>bb_badgesHideWhenActive</dt>
    <dd>whether badges should be hidden for active tabs, defaults to true.</dd>
    <dt>bb_titleTextAppearance</dt>
    <dd>custom textAppearance for the titles</dd>
    <dt>bb_titleTypeFace</dt>
    <dd>path for your custom font file, such as <code>fonts/MySuperDuperFont.ttf</code>. In that case your font path would look like <code>src/main/assets/fonts/MySuperDuperFont.ttf</code>, but you only need to provide <code>fonts/MySuperDuperFont.ttf</code>, as the asset folder will be auto-filled for you.</dd>
    <dt>bb_showShadow</dt>
    <dd>controls whether the shadow is shown or hidden, defaults to true.</dd>
</dl>

### For the tabs

```xml
<tab
    id="@+id/tab_recents"
    title="Recents"
    icon="@drawable/empty_icon"
    inActiveColor="#00FF00"
    activeColor="#FF0000"
    barColorWhenSelected="#FF0000"
    badgeBackgroundColor="#FF0000"
    badgeHidesWhenActive="true" />
```

<dl>
    <dt>inActiveColor</dt>
    <dd>the color for inactive tabs, that's used in the tab icons and titles.</dd>
    <dt>activeColor</dt>
    <dd>the color for active tabs, that's used in the tab icons and titles.</dd>
    <dt>barColorWhenSelected</dt>
    <dd>the color that the whole BottomBar should be when selected this tab.</dd>
    <dt>badgeBackgroundColor</dt>
    <dd>the background color for any Badges in this tab.</dd>
    <dt>badgeHidesWhenActive</dt>
    <dd>whether or not the badge should be hidden when this tab is selected, defaults to true.</dd>
    <dt></dt>
    <dd></dd>
</dl>

## Apps using BottomBar


  * [Nearby](https://play.google.com/store/apps/details?id=com.synergetechsolutions.nearbylive) : A location-based social networking app with over 5 million users.
  * [FragNav](https://github.com/ncapdevi/FragNav) : An Android Library for managing multiple stacks of Fragments. BottomBar is used in the sample app.
  * [BottomNavigationBar](https://github.com/pocheshire/BottomNavigationBar) : BottomBar ported to C# for Xamarin developers
  * [KyudoScoreBookTeam](https://play.google.com/store/apps/details?id=com.bowyer.app.android.kyudoscoreteam&hl=en) : BottomBar is used in the KyudoScoreBookTeam app.
  * [memeham](https://play.google.com/store/apps/details?id=com.memeham.beyourself.memeham) : BottomBar is used in the memeham app.
  * [NewsCatchr](https://play.google.com/store/apps/details?id=jlelse.readit) : A newsreader app, which uses this BottomBar library.
  * [GitSkarios](https://play.google.com/store/apps/details?id=com.alorma.github) : A Github android App, to visit your repositories, gists and  more!
    * [Code](https://github.com/gitskarios/Gitskarios)
  
Send me a pull request with modified README.md to get a shoutout!

## Contributions

Feel free to create issues and pull requests.

When creating pull requests, **more is more:** I'd like to see ten small pull requests separated by feature rather than all those combined into a huge one.

## License

```
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.
```


================================================
FILE: app/build.gradle
================================================
apply plugin: 'com.android.application'

android {
    compileSdkVersion rootProject.ext.compileSdkVersion
    buildToolsVersion rootProject.ext.buildToolsVersion

    defaultConfig {
        applicationId "com.example.bottombar.sample"
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}
configurations.all {
    resolutionStrategy.force "com.android.support:support-annotations:${rootProject.ext.supportLibraryVersion}"
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile project(':bottom-bar')
    compile "com.android.support:appcompat-v7:${rootProject.ext.supportLibraryVersion}"
    compile "com.android.support:design:${rootProject.ext.supportLibraryVersion}"

    androidTestCompile 'junit:junit:4.12'
    androidTestCompile 'com.android.support.test:runner:0.5'
    androidTestCompile 'com.android.support.test:rules:0.5'
    androidTestCompile 'org.mockito:mockito-core:1.10.19'
    androidTestCompile "com.google.dexmaker:dexmaker:1.2"
    androidTestCompile "com.google.dexmaker:dexmaker-mockito:1.2"
}


================================================
FILE: app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in C:\Users\Iiro\AppData\Local\Android\sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# Add any project specific keep options here:

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
#   public *;
#}


================================================
FILE: app/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.bottombar.sample">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:launchMode="singleInstance">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".ThreeTabsActivity" />
        <activity android:name=".IconsOnlyActivity" />
        <activity android:name=".FiveColorChangingTabsActivity" android:theme="@style/AppTheme.TransNav" />
        <activity android:name=".ThreeTabsQRActivity" android:theme="@style/AppTheme.TransNav" />
        <activity android:name=".BadgeActivity" />
        <activity android:name=".CustomColorAndFontActivity" />
    </application>

</manifest>

================================================
FILE: app/src/main/assets/fonts/GREAT-VIBES-LICENSE
================================================
SIL Open Font License v1.10

This license can also be found at this permalink: https://www.fontsquirrel.com/license/great-vibes

Copyright (c) 2012, TypeSETit, LLC (typesetit@att.net),
with Reserved Font Name "Great Vibes"

This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL

—————————————————————————————-
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
—————————————————————————————-

PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others.

The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives.

DEFINITIONS
“Font Software” refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation.

“Reserved Font Name” refers to any names specified as such after the copyright statement(s).

“Original Version” refers to the collection of Font Software components as distributed by the Copyright Holder(s).

“Modified Version” refers to any derivative made by adding to, deleting, or substituting—in part or in whole—any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment.

“Author” refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.

PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:

1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself.

2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.

3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users.

4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission.

5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software.

TERMINATION
This license becomes null and void if any of the above conditions are not met.

DISCLAIMER
THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.

================================================
FILE: app/src/main/java/com/example/bottombar/sample/BadgeActivity.java
================================================
package com.example.bottombar.sample;

import android.os.Bundle;
import android.support.annotation.IdRes;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
import android.widget.Toast;

import com.roughike.bottombar.BottomBar;
import com.roughike.bottombar.BottomBarTab;
import com.roughike.bottombar.OnTabReselectListener;
import com.roughike.bottombar.OnTabSelectListener;

/**
 * Created by iiro on 7.6.2016.
 */
public class BadgeActivity extends AppCompatActivity {
    private TextView messageView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_three_tabs);

        messageView = (TextView) findViewById(R.id.messageView);

        final BottomBar bottomBar = (BottomBar) findViewById(R.id.bottomBar);
        bottomBar.setOnTabSelectListener(new OnTabSelectListener() {
            @Override
            public void onTabSelected(@IdRes int tabId) {
                messageView.setText(TabMessage.get(tabId, false));
            }
        });

        bottomBar.setOnTabReselectListener(new OnTabReselectListener() {
            @Override
            public void onTabReSelected(@IdRes int tabId) {
                Toast.makeText(getApplicationContext(), TabMessage.get(tabId, true), Toast.LENGTH_LONG).show();
            }
        });

        BottomBarTab nearby = bottomBar.getTabWithId(R.id.tab_nearby);
        nearby.setBadgeCount(5);
    }
}


================================================
FILE: app/src/main/java/com/example/bottombar/sample/CustomColorAndFontActivity.java
================================================
package com.example.bottombar.sample;

import android.os.Bundle;
import android.support.annotation.IdRes;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
import android.widget.Toast;

import com.roughike.bottombar.BottomBar;
import com.roughike.bottombar.OnTabReselectListener;
import com.roughike.bottombar.OnTabSelectListener;

/**
 * Created by mikemilla on 7.17.2016.
 * http://mikemilla.com
 */
public class CustomColorAndFontActivity extends AppCompatActivity {

    private TextView messageView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_custom_color_and_font);

        messageView = (TextView) findViewById(R.id.messageView);

        BottomBar bottomBar = (BottomBar) findViewById(R.id.bottomBar);
        bottomBar.setOnTabSelectListener(new OnTabSelectListener() {
            @Override
            public void onTabSelected(@IdRes int tabId) {
                messageView.setText(TabMessage.get(tabId, false));
            }
        });

        bottomBar.setOnTabReselectListener(new OnTabReselectListener() {
            @Override
            public void onTabReSelected(@IdRes int tabId) {
                Toast.makeText(getApplicationContext(), TabMessage.get(tabId, true), Toast.LENGTH_LONG).show();
            }
        });
    }
}


================================================
FILE: app/src/main/java/com/example/bottombar/sample/FiveColorChangingTabsActivity.java
================================================
package com.example.bottombar.sample;

import android.os.Bundle;
import android.support.annotation.IdRes;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
import android.widget.Toast;

import com.roughike.bottombar.BottomBar;
import com.roughike.bottombar.OnTabReselectListener;
import com.roughike.bottombar.OnTabSelectListener;

/**
 * Created by iiro on 7.6.2016.
 */
public class FiveColorChangingTabsActivity extends AppCompatActivity {
    private TextView messageView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_color_changing_tabs);

        messageView = (TextView) findViewById(R.id.messageView);

        BottomBar bottomBar = (BottomBar) findViewById(R.id.bottomBar);
        bottomBar.setOnTabSelectListener(new OnTabSelectListener() {
            @Override
            public void onTabSelected(@IdRes int tabId) {
                messageView.setText(TabMessage.get(tabId, false));
            }
        });

        bottomBar.setOnTabReselectListener(new OnTabReselectListener() {
            @Override
            public void onTabReSelected(@IdRes int tabId) {
                Toast.makeText(getApplicationContext(), TabMessage.get(tabId, true), Toast.LENGTH_LONG).show();
            }
        });
    }
}

================================================
FILE: app/src/main/java/com/example/bottombar/sample/IconsOnlyActivity.java
================================================
package com.example.bottombar.sample;

import android.app.Activity;
import android.os.Bundle;
import android.support.annotation.IdRes;
import android.support.annotation.Nullable;
import android.widget.TextView;
import android.widget.Toast;

import com.roughike.bottombar.BottomBar;
import com.roughike.bottombar.OnTabReselectListener;
import com.roughike.bottombar.OnTabSelectListener;

public class IconsOnlyActivity extends Activity {
    private TextView messageView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_icons_only);

        messageView = (TextView) findViewById(R.id.messageView);

        BottomBar bottomBar = (BottomBar) findViewById(R.id.bottomBar);
        bottomBar.setOnTabSelectListener(new OnTabSelectListener() {
            @Override
            public void onTabSelected(@IdRes int tabId) {
                messageView.setText(TabMessage.get(tabId, false));
            }
        });

        bottomBar.setOnTabReselectListener(new OnTabReselectListener() {
            @Override
            public void onTabReSelected(@IdRes int tabId) {
                Toast.makeText(getApplicationContext(), TabMessage.get(tabId, true), Toast.LENGTH_LONG).show();
            }
        });
    }
}

================================================
FILE: app/src/main/java/com/example/bottombar/sample/MainActivity.java
================================================
package com.example.bottombar.sample;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    @SuppressWarnings("ConstantConditions")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.simple_three_tabs).setOnClickListener(this);
        findViewById(R.id.icons_only).setOnClickListener(this);
        findViewById(R.id.five_tabs_changing_colors).setOnClickListener(this);
        findViewById(R.id.three_tabs_quick_return).setOnClickListener(this);
        findViewById(R.id.five_tabs_custom_colors).setOnClickListener(this);
        findViewById(R.id.badges).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        Class clazz = null;

        switch (v.getId()) {
            case R.id.simple_three_tabs:
                clazz = ThreeTabsActivity.class;
                break;
            case R.id.icons_only:
                clazz = IconsOnlyActivity.class;
                break;
            case R.id.five_tabs_changing_colors:
                clazz = FiveColorChangingTabsActivity.class;
                break;
            case R.id.three_tabs_quick_return:
                clazz = ThreeTabsQRActivity.class;
                break;
            case R.id.five_tabs_custom_colors:
                clazz = CustomColorAndFontActivity.class;
                break;
            case R.id.badges:
                clazz = BadgeActivity.class;
                break;
        }

        startActivity(new Intent(this, clazz));
    }
}


================================================
FILE: app/src/main/java/com/example/bottombar/sample/SampleFragment.java
================================================
/*
 * 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.example.bottombar.sample;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

/**
 * Created by Iiro Krankka (http://github.com/roughike)
 */
public class SampleFragment extends Fragment {
    private static final String ARG_TEXT = "ARG_TEXT";

    public SampleFragment() {
    }

    public static SampleFragment newInstance(String text) {
        Bundle args = new Bundle();
        args.putString(ARG_TEXT, text);

        SampleFragment sampleFragment = new SampleFragment();
        sampleFragment.setArguments(args);

        return sampleFragment;
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        TextView textView = new TextView(getActivity());
        textView.setText(getArguments().getString(ARG_TEXT));

        return textView;
    }
}


================================================
FILE: app/src/main/java/com/example/bottombar/sample/TabMessage.java
================================================
package com.example.bottombar.sample;

/**
 * Created by iiro on 7.6.2016.
 */
public class TabMessage {
    public static String get(int menuItemId, boolean isReselection) {
        String message = "Content for ";

        switch (menuItemId) {
            case R.id.tab_recents:
                message += "recents";
                break;
            case R.id.tab_favorites:
                message += "favorites";
                break;
            case R.id.tab_nearby:
                message += "nearby";
                break;
            case R.id.tab_friends:
                message += "friends";
                break;
            case R.id.tab_food:
                message += "food";
                break;
        }

        if (isReselection) {
            message += " WAS RESELECTED! YAY!";
        }

        return message;
    }
}


================================================
FILE: app/src/main/java/com/example/bottombar/sample/ThreeTabsActivity.java
================================================
package com.example.bottombar.sample;

import android.app.Activity;
import android.os.Bundle;
import android.support.annotation.IdRes;
import android.support.annotation.Nullable;
import android.widget.TextView;
import android.widget.Toast;

import com.roughike.bottombar.BottomBar;
import com.roughike.bottombar.OnTabReselectListener;
import com.roughike.bottombar.OnTabSelectListener;

/**
 * Created by iiro on 7.6.2016.
 */
public class ThreeTabsActivity extends Activity {
    private TextView messageView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_three_tabs);

        messageView = (TextView) findViewById(R.id.messageView);

        BottomBar bottomBar = (BottomBar) findViewById(R.id.bottomBar);
        bottomBar.setOnTabSelectListener(new OnTabSelectListener() {
            @Override
            public void onTabSelected(@IdRes int tabId) {
                messageView.setText(TabMessage.get(tabId, false));
            }
        });

        bottomBar.setOnTabReselectListener(new OnTabReselectListener() {
            @Override
            public void onTabReSelected(@IdRes int tabId) {
                Toast.makeText(getApplicationContext(), TabMessage.get(tabId, true), Toast.LENGTH_LONG).show();
            }
        });
    }
}

================================================
FILE: app/src/main/java/com/example/bottombar/sample/ThreeTabsQRActivity.java
================================================
package com.example.bottombar.sample;

import android.os.Bundle;
import android.support.annotation.IdRes;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;

import com.roughike.bottombar.BottomBar;
import com.roughike.bottombar.OnTabSelectListener;

/**
 * Created by iiro on 7.6.2016.
 */
public class ThreeTabsQRActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_three_tabs_quick_return);

        BottomBar bottomBar = (BottomBar) findViewById(R.id.bottomBar);

        // We're doing nothing with this listener here this time. Check example usage
        // from ThreeTabsActivity on how to use it.
        bottomBar.setOnTabSelectListener(new OnTabSelectListener() {
            @Override
            public void onTabSelected(@IdRes int tabId) {

            }
        });
    }
}


================================================
FILE: app/src/main/res/layout/activity_color_changing_tabs.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <TextView
        android:id="@+id/messageView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/bottomBar"
        android:gravity="center"
        android:text="Hi mom!" />

    <com.roughike.bottombar.BottomBar
        android:id="@+id/bottomBar"
        android:layout_width="match_parent"
        android:layout_height="64dp"
        android:layout_alignParentBottom="true"
        app:bb_tabXmlResource="@xml/bottombar_tabs_color_changing"
        app:bb_behavior="shifting|underNavbar" />

</RelativeLayout>

================================================
FILE: app/src/main/res/layout/activity_custom_color_and_font.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <TextView
        android:id="@+id/messageView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/bottomBar"
        android:gravity="center"
        android:text="Hi mom!" />

    <com.roughike.bottombar.BottomBar
        android:id="@+id/bottomBar"
        android:layout_width="match_parent"
        android:layout_height="64dp"
        android:layout_alignParentBottom="true"
        android:background="#FFFFFF"
        app:bb_tabXmlResource="@xml/bottombar_tabs_five"
        app:bb_behavior="shifting"
        app:bb_inActiveTabAlpha="0.3"
        app:bb_inActiveTabColor="@color/colorAccent"
        app:bb_activeTabColor="@color/colorAccent"
        app:bb_titleTypeFace="fonts/GreatVibes-Regular.otf"
        app:bb_titleTextAppearance="@style/CustomTitleTextAppearance"/>

</RelativeLayout>

================================================
FILE: app/src/main/res/layout/activity_icons_only.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <TextView
        android:id="@+id/messageView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/bottomBar"
        android:gravity="center"
        android:text="Hi mom!" />

    <com.roughike.bottombar.BottomBar
        android:id="@+id/bottomBar"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:layout_alignParentBottom="true"
        app:bb_tabXmlResource="@xml/bottombar_tabs_five"
        app:bb_behavior="iconsOnly" />

</RelativeLayout>

================================================
FILE: app/src/main/res/layout/activity_main.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp"
    tools:ignore="HardcodedText">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Check out these samples and their sources for the most basic use cases."
        android:layout_marginBottom="16dp" />

    <Button
        android:id="@+id/simple_three_tabs"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Simple example with three tabs" />

    <Button
        android:id="@+id/icons_only"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Tabs with icons only" />

    <Button
        android:id="@+id/five_tabs_custom_colors"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Using custom colors and fonts" />

    <Button
        android:id="@+id/five_tabs_changing_colors"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Five tabs with changing colors" />

    <Button
        android:id="@+id/three_tabs_quick_return"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Three tabs with Quick Return" />

    <Button
        android:id="@+id/badges"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Using Badges" />

</LinearLayout>

================================================
FILE: app/src/main/res/layout/activity_three_tabs.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <TextView
        android:id="@+id/messageView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/bottomBar"
        android:gravity="center"
        android:text="Hi mom!" />

    <com.roughike.bottombar.BottomBar
        android:id="@+id/bottomBar"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:layout_alignParentBottom="true"
        app:bb_tabXmlResource="@xml/bottombar_tabs_three" />

</RelativeLayout>

================================================
FILE: app/src/main/res/layout/activity_three_tabs_quick_return.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/myCoordinator"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v4.widget.NestedScrollView
        android:id="@+id/myScrollingContent"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="16dp"
            android:text="@string/lorem_ipsum" />

    </android.support.v4.widget.NestedScrollView>

    <com.roughike.bottombar.BottomBar
        android:id="@+id/bottomBar"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:layout_gravity="bottom"
        app:bb_tabXmlResource="@xml/bottombar_tabs_three"
        app:bb_behavior="underNavbar|shy"/>

</android.support.design.widget.CoordinatorLayout>

================================================
FILE: app/src/main/res/layout-sw600dp/activity_color_changing_tabs.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.roughike.bottombar.BottomBar
        android:id="@+id/bottomBar"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        app:bb_tabXmlResource="@xml/bottombar_tabs_color_changing"
        app:bb_tabletMode="true"
        app:bb_inActiveTabColor="#FFFFFF"
        app:bb_activeTabColor="#FFFFFF"
        app:bb_inActiveTabAlpha="0.6"
        app:bb_activeTabAlpha="1" />

    <TextView
        android:id="@+id/messageView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_toRightOf="@+id/bottomBar"
        android:layout_toEndOf="@+id/bottomBar"
        android:gravity="center"
        android:text="Hi mom!" />

</RelativeLayout>

================================================
FILE: app/src/main/res/layout-sw600dp/activity_three_tabs.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.roughike.bottombar.BottomBar
        android:id="@+id/bottomBar"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        app:bb_tabXmlResource="@xml/bottombar_tabs_three"
        app:bb_tabletMode="true" />

    <TextView
        android:id="@+id/messageView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_toRightOf="@+id/bottomBar"
        android:layout_toEndOf="@+id/bottomBar"
        android:gravity="center"
        android:text="Hi mom!" />

</RelativeLayout>

================================================
FILE: app/src/main/res/values/colors.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#3F51B5</color>
    <color name="colorPrimaryDark">#303F9F</color>
    <color name="colorAccent">#FF4081</color>
</resources>


================================================
FILE: app/src/main/res/values/dimens.xml
================================================
<resources>
    <!-- Default screen margins, per the Android Design guidelines. -->
    <dimen name="activity_horizontal_margin">16dp</dimen>
    <dimen name="activity_vertical_margin">16dp</dimen>
    <dimen name="bottom_bar_elevation">8dp</dimen>
    <dimen name="fab_margin">16dp</dimen>
</resources>


================================================
FILE: app/src/main/res/values/strings.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="lorem_ipsum">Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed posuere interdum sem. Quisque ligula eros ullamcorper quis, lacinia quis facilisis sed sapien. Mauris varius diam vitae arcu. Sed arcu lectus auctor vitae, consectetuer et venenatis eget velit. Sed augue orci, lacinia eu tincidunt et eleifend nec lacus. Donec ultricies nisl ut felis, suspendisse potenti. Lorem ipsum ligula ut hendrerit mollis, ipsum erat vehicula risus, eu suscipit sem libero nec erat. Aliquam erat volutpat. Sed congue augue vitae neque. Nulla consectetuer porttitor pede. Fusce purus morbi tortor magna condimentum vel, placerat id blandit sit amet tortor.

Mauris sed libero. Suspendisse facilisis nulla in lacinia laoreet, lorem velit accumsan velit vel mattis libero nisl et sem. Proin interdum maecenas massa turpis sagittis in, interdum non lobortis vitae massa. Quisque purus lectus, posuere eget imperdiet nec sodales id arcu. Vestibulum elit pede dictum eu, viverra non tincidunt eu ligula.

Nam molestie nec tortor. Donec placerat leo sit amet velit. Vestibulum id justo ut vitae massa. Proin in dolor mauris consequat aliquam. Donec ipsum, vestibulum ullamcorper venenatis augue. Aliquam tempus nisi in auctor vulputate, erat felis pellentesque augue nec, pellentesque lectus justo nec erat. Aliquam et nisl. Quisque sit amet dolor in justo pretium condimentum.

Vivamus placerat lacus vel vehicula scelerisque, dui enim adipiscing lacus sit amet sagittis, libero enim vitae mi. In neque magna posuere, euismod ac tincidunt tempor est. Ut suscipit nisi eu purus. Proin ut pede mauris eget ipsum. Integer vel quam nunc commodo consequat. Integer ac eros eu tellus dignissim viverra. Maecenas erat aliquam erat volutpat. Ut venenatis ipsum quis turpis. Integer cursus scelerisque lorem. Sed nec mauris id quam blandit consequat. Cras nibh mi hendrerit vitae, dapibus et aliquam et magna. Nulla vitae elit. Mauris consectetuer odio vitae augue.

 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed posuere interdum sem. Quisque ligula eros ullamcorper quis, lacinia quis facilisis sed sapien. Mauris varius diam vitae arcu. Sed arcu lectus auctor vitae, consectetuer et venenatis eget velit. Sed augue orci, lacinia eu tincidunt et eleifend nec lacus. Donec ultricies nisl ut felis, suspendisse potenti. Lorem ipsum ligula ut hendrerit mollis, ipsum erat vehicula risus, eu suscipit sem libero nec erat. Aliquam erat volutpat. Sed congue augue vitae neque. Nulla consectetuer porttitor pede. Fusce purus morbi tortor magna condimentum vel, placerat id blandit sit amet tortor.

Mauris sed libero. Suspendisse facilisis nulla in lacinia laoreet, lorem velit accumsan velit vel mattis libero nisl et sem. Proin interdum maecenas massa turpis sagittis in, interdum non lobortis vitae massa. Quisque purus lectus, posuere eget imperdiet nec sodales id arcu. Vestibulum elit pede dictum eu, viverra non tincidunt eu ligula.

Nam molestie nec tortor. Donec placerat leo sit amet velit. Vestibulum id justo ut vitae massa. Proin in dolor mauris consequat aliquam. Donec ipsum, vestibulum ullamcorper venenatis augue. Aliquam tempus nisi in auctor vulputate, erat felis pellentesque augue nec, pellentesque lectus justo nec erat. Aliquam et nisl. Quisque sit amet dolor in justo pretium condimentum.

Vivamus placerat lacus vel vehicula scelerisque, dui enim adipiscing lacus sit amet sagittis, libero enim vitae mi. In neque magna posuere, euismod ac tincidunt tempor est. Ut suscipit nisi eu purus. Proin ut pede mauris eget ipsum. Integer vel quam nunc commodo consequat. Integer ac eros eu tellus dignissim viverra. Maecenas erat aliquam erat volutpat. Ut venenatis ipsum quis turpis. Integer cursus scelerisque lorem. Sed nec mauris id quam blandit consequat. Cras nibh mi hendrerit vitae, dapibus et aliquam et magna. Nulla vitae elit. Mauris consectetuer odio vitae augue.</string>
    <string name="title_activity_custom_color">CustomColorActivity</string>
</resources>

================================================
FILE: app/src/main/res/values/styles.xml
================================================
<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

    <style name="AppTheme.TransNav" parent="AppTheme" />

    <style name="CustomTitleTextAppearance">
        <item name="android:textSize">23sp</item>
        <item name="android:textStyle">bold</item>
    </style>

</resources>


================================================
FILE: app/src/main/res/values-land-v21/styles.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="AppTheme.TransNav" parent="AppTheme" />
</resources>

================================================
FILE: app/src/main/res/values-sw600dp/styles.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="AppTheme.TransNav" parent="AppTheme" />
</resources>

================================================
FILE: app/src/main/res/values-v21/styles.xml
================================================
<resources>
    <style name="AppTheme.NoActionBar">
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
        <item name="android:windowDrawsSystemBarBackgrounds">true</item>
        <item name="android:statusBarColor">@android:color/transparent</item>
    </style>

    <style name="AppTheme.TransNav" parent="AppTheme">
        <item name="android:navigationBarColor">@android:color/transparent</item>
        <item name="android:windowTranslucentNavigation">true</item>
        <item name="android:windowDrawsSystemBarBackgrounds">true</item>
    </style>
</resources>


================================================
FILE: app/src/main/res/values-w820dp/dimens.xml
================================================
<resources>
    <!-- Example customization of dimensions originally defined in res/values/dimens.xml
         (such as screen margins) for screens with more than 820dp of available width. This
         would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
    <dimen name="activity_horizontal_margin">64dp</dimen>
</resources>


================================================
FILE: app/src/main/res/xml/bottombar_tabs_color_changing.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<tabs>
    <tab
        id="@+id/tab_recents"
        icon="@drawable/ic_recents"
        title="Recents"
        barColorWhenSelected="@color/colorAccent" />
    <tab
        id="@+id/tab_favorites"
        icon="@drawable/ic_favorites"
        title="Favorites"
        barColorWhenSelected="#5D4037" />
    <tab
        id="@+id/tab_nearby"
        icon="@drawable/ic_nearby"
        title="Nearby"
        barColorWhenSelected="#7B1FA2" />
    <tab
        id="@+id/tab_friends"
        icon="@drawable/ic_friends"
        title="Friends"
        barColorWhenSelected="#FF5252" />
    <tab
        id="@+id/tab_food"
        icon="@drawable/ic_restaurants"
        title="Food"
        barColorWhenSelected="#FF9800" />
</tabs>

================================================
FILE: app/src/main/res/xml/bottombar_tabs_five.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<tabs>
    <tab
        id="@+id/tab_recents"
        icon="@drawable/ic_recents"
        title="Recents" />
    <tab
        id="@+id/tab_favorites"
        icon="@drawable/ic_favorites"
        title="Favorites" />
    <tab
        id="@+id/tab_nearby"
        icon="@drawable/ic_nearby"
        title="Nearby" />
    <tab
        id="@+id/tab_friends"
        icon="@drawable/ic_friends"
        title="Friends" />
    <tab
        id="@+id/tab_food"
        icon="@drawable/ic_restaurants"
        title="Food" />
</tabs>

================================================
FILE: app/src/main/res/xml/bottombar_tabs_three.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<tabs>
    <tab
        id="@+id/tab_favorites"
        icon="@drawable/ic_favorites"
        title="Favorites" />
    <tab
        id="@+id/tab_nearby"
        icon="@drawable/ic_nearby"
        title="Nearby" />
    <tab
        id="@+id/tab_friends"
        icon="@drawable/ic_friends"
        title="Friends" />
</tabs>

================================================
FILE: bottom-bar/build.gradle
================================================
apply plugin: 'com.android.library'
apply plugin: 'com.github.kt3k.coveralls'

ext {
    bintrayRepo = 'maven'
    bintrayName = 'bottom-bar'

    publishedGroupId = 'com.roughike'
    libraryName = 'bottom-bar'
    artifact = 'bottom-bar'

    libraryDescription = 'A custom view component that mimics the Material Design "Bottom navigation" pattern.'

    siteUrl = 'https://github.com/roughike/BottomBar'
    gitUrl = 'https://github.com/roughike/BottomBar.git'

    libraryVersion = '2.3.1'

    developerId = 'roughike'
    developerName = 'Iiro Krankka'
    developerEmail = 'iiro.krankka@gmail.com'

    licenseName = 'The Apache Software License, Version 2.0'
    licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
    allLicenses = ["Apache-2.0"]
}

android {
    compileSdkVersion rootProject.ext.compileSdkVersion
    buildToolsVersion rootProject.ext.buildToolsVersion


    buildTypes {
        debug {
            testCoverageEnabled true
        }
    }

    defaultConfig {
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
}

coveralls {
    jacocoReportPath = "${buildDir}/reports/coverage/debug/report.xml"
}

tasks.coveralls {
    dependsOn 'connectedAndroidTest'
    onlyIf { System.env.'CI' }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:' + rootProject.ext.supportLibraryVersion
    compile 'com.android.support:design:' + rootProject.ext.supportLibraryVersion

    testCompile 'junit:junit:' + rootProject.ext.junitVersion

    androidTestCompile 'junit:junit:4.12'
    androidTestCompile 'com.android.support.test:runner:0.5'
    androidTestCompile 'com.android.support.test:rules:0.5'
    androidTestCompile 'org.mockito:mockito-core:1.+'
    androidTestCompile "com.google.dexmaker:dexmaker:1.2"
    androidTestCompile "com.google.dexmaker:dexmaker-mockito:1.2"
}

apply plugin: 'com.github.dcendents.android-maven'

group = publishedGroupId                               // Maven Group ID for the artifact

install {
    repositories.mavenInstaller {
        // This generates POM.xml with proper parameters
        pom {
            project {
                packaging 'aar'
                groupId publishedGroupId
                artifactId artifact

                // Add your description here
                name libraryName
                description libraryDescription
                url siteUrl

                // Set your license
                licenses {
                    license {
                        name licenseName
                        url licenseUrl
                    }
                }
                developers {
                    developer {
                        id developerId
                        name developerName
                        email developerEmail
                    }
                }
                scm {
                    connection gitUrl
                    developerConnection gitUrl
                    url siteUrl

                }
            }
        }
    }
}

apply plugin: 'com.jfrog.bintray'

version = libraryVersion

if (project.hasProperty("android")) { // Android libraries
    task sourcesJar(type: Jar) {
        classifier = 'sources'
        from android.sourceSets.main.java.srcDirs
    }

    task javadoc(type: Javadoc) {
        source = android.sourceSets.main.java.srcDirs
        classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
    }
} else { // Java libraries
    task sourcesJar(type: Jar, dependsOn: classes) {
        classifier = 'sources'
        from sourceSets.main.allSource
    }
}

task javadocJar(type: Jar, dependsOn: javadoc) {
    classifier = 'javadoc'
    from javadoc.destinationDir
}

artifacts {
    archives javadocJar
    archives sourcesJar
}

// Bintray
File localProps = project.rootProject.file('local.properties')

if (localProps.exists()) {
    Properties properties = new Properties()
    properties.load(localProps.newDataInputStream())

    bintray {
        user = properties.getProperty("bintray.user")
        key = properties.getProperty("bintray.apikey")

        configurations = ['archives']
        pkg {
            repo = bintrayRepo
            name = bintrayName
            desc = libraryDescription
            websiteUrl = siteUrl
            vcsUrl = gitUrl
            licenses = allLicenses
            publish = true
            publicDownloadNumbers = true
            version {
                desc = libraryDescription
                gpg {
                    sign = true //Determines whether to GPG sign the files. The default is false
                    passphrase = properties.getProperty("bintray.gpg.password")
                    //Optional. The passphrase for GPG signing'
                }
            }
        }
    }
}

================================================
FILE: bottom-bar/src/androidTest/assets/fonts/GREAT-VIBES-LICENSE
================================================
SIL Open Font License v1.10

This license can also be found at this permalink: https://www.fontsquirrel.com/license/great-vibes

Copyright (c) 2012, TypeSETit, LLC (typesetit@att.net),
with Reserved Font Name "Great Vibes"

This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL

—————————————————————————————-
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
—————————————————————————————-

PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others.

The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives.

DEFINITIONS
“Font Software” refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation.

“Reserved Font Name” refers to any names specified as such after the copyright statement(s).

“Original Version” refers to the collection of Font Software components as distributed by the Copyright Holder(s).

“Modified Version” refers to any derivative made by adding to, deleting, or substituting—in part or in whole—any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment.

“Author” refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.

PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:

1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself.

2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.

3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users.

4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission.

5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software.

TERMINATION
This license becomes null and void if any of the above conditions are not met.

DISCLAIMER
THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.

================================================
FILE: bottom-bar/src/androidTest/java/com/roughike/bottombar/BadgeTest.java
================================================
package com.roughike.bottombar;

import android.os.Bundle;
import android.support.test.InstrumentationRegistry;
import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.LargeTest;
import android.support.test.runner.AndroidJUnit4;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNull;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

/**
 * Created by iiro on 8.8.2016.
 */
@RunWith(AndroidJUnit4.class)
@LargeTest
public class BadgeTest {
    private BottomBar bottomBar;
    private BottomBarTab nearby;

    @Before
    public void setUp() {
        bottomBar = new BottomBar(InstrumentationRegistry.getContext());
        bottomBar.setItems(com.roughike.bottombar.test.R.xml.dummy_tabs_three);
        nearby = bottomBar.getTabWithId(com.roughike.bottombar.test.R.id.tab_nearby);
        nearby.setBadgeCount(5);
    }

    @Test
    public void hasNoBadges_ExceptNearby() {
        assertNull(bottomBar.getTabWithId(com.roughike.bottombar.test.R.id.tab_favorites).badge);
        assertNull(bottomBar.getTabWithId(com.roughike.bottombar.test.R.id.tab_friends).badge);

        assertNotNull(nearby.badge);
    }

    @Test
    @UiThreadTest
    public void whenTabWithBadgeClicked_BadgeIsHidden() {
        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
            @Override
            public void run() {
                bottomBar.selectTabWithId(com.roughike.bottombar.test.R.id.tab_nearby);
                assertFalse(nearby.badge.isVisible());
            }
        });
    }

    @Test
    @UiThreadTest
    public void whenBadgeCountIsZero_BadgeIsRemoved() {
        nearby.setBadgeCount(0);
        assertNull(nearby.badge);
    }

    @Test
    @UiThreadTest
    public void whenBadgeCountIsNegative_BadgeIsRemoved() {
        nearby.setBadgeCount(-1);
        assertNull(nearby.badge);
    }

    @Test
    @UiThreadTest
    public void whenBadgeStateRestored_CountPersists() {
        nearby.setBadgeCount(1);
        assertEquals(1, nearby.badge.getCount());


        int tabIndex = nearby.getIndexInTabContainer();
        Bundle savedInstanceState = new Bundle();
        savedInstanceState.putInt(BottomBarTab.STATE_BADGE_COUNT + tabIndex, 2);
        nearby.restoreState(savedInstanceState);

        assertEquals(2, nearby.badge.getCount());
    }

    @Test
    @UiThreadTest
    public void badgeRemovedProperly() {
        assertNotEquals(bottomBar.findViewById(R.id.bb_bottom_bar_item_container), nearby.getOuterView());
        assertEquals(2, nearby.getOuterView().getChildCount());
        assertTrue(nearby.getOuterView().getChildAt(1) instanceof BottomBarBadge);

        nearby.removeBadge();
        assertNull(nearby.badge);
        assertEquals(bottomBar.findViewById(R.id.bb_bottom_bar_item_container), nearby.getOuterView());
    }
}


================================================
FILE: bottom-bar/src/androidTest/java/com/roughike/bottombar/BottomBarTabTest.java
================================================
package com.roughike.bottombar;

import android.os.Bundle;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.widget.FrameLayout;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import static org.junit.Assert.assertEquals;

@RunWith(AndroidJUnit4.class)
public class BottomBarTabTest {
    private FrameLayout tabContainer;
    private BottomBarTab tab;

    @Before
    public void setUp() {
        tabContainer = new FrameLayout(InstrumentationRegistry.getContext());
        tab = new BottomBarTab(InstrumentationRegistry.getContext());

        tabContainer.addView(tab);
    }

    @Test
    public void correctLayoutReturnedForFixedTab() {
        tab.setType(BottomBarTab.Type.FIXED);
        assertEquals(R.layout.bb_bottom_bar_item_fixed, tab.getLayoutResource());
    }

    @Test(expected = IllegalStateException.class)
    public void setIsTitleless_WhenTrueAndIconDoesNotExist_ThrowsException() {
        tab.setIsTitleless(true);
        assertEquals(R.layout.bb_bottom_bar_item_titleless, tab.getLayoutResource());
    }

    @Test
    public void correctLayoutForShiftingTab() {
        tab.setType(BottomBarTab.Type.SHIFTING);
        assertEquals(R.layout.bb_bottom_bar_item_shifting, tab.getLayoutResource());
    }

    @Test
    public void correctLayoutForTabletTab() {
        tab.setType(BottomBarTab.Type.TABLET);
        assertEquals(R.layout.bb_bottom_bar_item_fixed_tablet, tab.getLayoutResource());
    }

    @Test
    public void testSavedStateWithBadge_StaysIntact() {
        tab.setBadgeCount(5);
        tab.setIndexInContainer(69);
        assertEquals(69, tab.getIndexInTabContainer());

        Bundle savedState = (Bundle) tab.onSaveInstanceState();
        assertEquals(5, savedState.getInt(BottomBarTab.STATE_BADGE_COUNT + 69));

        tab.setBadgeCount(9);
        assertEquals(9, tab.badge.getCount());

        tab.onRestoreInstanceState(savedState);
        assertEquals(5, tab.badge.getCount());
    }
}


================================================
FILE: bottom-bar/src/androidTest/java/com/roughike/bottombar/BottomBarTest.java
================================================
package com.roughike.bottombar;

import android.content.Context;
import android.graphics.Color;
import android.graphics.Typeface;
import android.os.Bundle;
import android.support.test.InstrumentationRegistry;
import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.LargeTest;
import android.support.test.runner.AndroidJUnit4;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;

import static android.support.test.InstrumentationRegistry.getInstrumentation;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;

/**
 * Created by iiro on 13.8.2016.
 */
@RunWith(AndroidJUnit4.class)
@LargeTest
public class BottomBarTest {
    private static final int THREE_TABS = com.roughike.bottombar.test.R.xml.dummy_tabs_three;

    private static final float INACTIVE_TAB_ALPHA = 0.69f;
    private static final float ACTIVE_TAB_ALPHA = 0.96f;
    private static final int INACTIVE_TAB_COLOR = Color.parseColor("#111111");
    private static final int ACTIVE_TAB_COLOR = Color.parseColor("#222222");
    private static final int BACKGROUND_COLOR = Color.parseColor("#333333");
    private static final int BADGE_BACKGROUND_COLOR = Color.parseColor("#444444");
    private static final boolean DEFAULT_BADGE_HIDES_WHEN_SELECTED_VALUE = true;
    private static final int TITLE_TEXT_APPEARANCE = com.roughike.bottombar.test.R.style.dummy_text_appearance;
    private static final Typeface TYPEFACE = Typeface.DEFAULT_BOLD;

    private static final BottomBarTab.Config DEFAULT_CONFIG = new BottomBarTab.Config.Builder()
            .inActiveTabAlpha(INACTIVE_TAB_ALPHA)
            .activeTabAlpha(ACTIVE_TAB_ALPHA)
            .inActiveTabColor(INACTIVE_TAB_COLOR)
            .activeTabColor(ACTIVE_TAB_COLOR)
            .barColorWhenSelected(BACKGROUND_COLOR)
            .badgeBackgroundColor(BADGE_BACKGROUND_COLOR)
            .hideBadgeWhenSelected(DEFAULT_BADGE_HIDES_WHEN_SELECTED_VALUE)
            .titleTextAppearance(TITLE_TEXT_APPEARANCE)
            .titleTypeFace(TYPEFACE)
            .build();

    private Context context;

    private TabSelectionInterceptor tabSelectionInterceptor;
    private OnTabSelectListener selectListener;
    private OnTabReselectListener reselectListener;

    private BottomBar bottomBar;

    @Before
    public void setUp() {
        context = InstrumentationRegistry.getTargetContext();

        tabSelectionInterceptor = mock(TabSelectionInterceptor.class);
        selectListener = mock(OnTabSelectListener.class);
        reselectListener = mock(OnTabReselectListener.class);

        bottomBar = new BottomBar(context);
        bottomBar.setItems(THREE_TABS, DEFAULT_CONFIG);
        bottomBar.setOnTabSelectListener(selectListener);
        bottomBar.setOnTabReselectListener(reselectListener);
    }

    @Test
    public void canCreateNewInstanceFromXml_WithoutXmlMenuResource() {
        new BottomBar(context, null);
    }

    @Test(expected = RuntimeException.class)
    public void setItems_ThrowsExceptionWithNoResource() {
        BottomBar secondBar = new BottomBar(context);
        secondBar.setItems(0);
    }

    @Test
    public void setItems_AfterAlreadySet_ReplacesPreviousWithNewOnes() {
        int previousItemCount = bottomBar.getTabCount();
        assertThat(previousItemCount, is(3));

        bottomBar.setItems(com.roughike.bottombar.test.R.xml.dummy_tabs_five);
        assertThat(bottomBar.getTabCount(), is(5));
    }

    @Test
    public void setItemsWithCustomConfig_OverridesPreviousValues() {
        BottomBar newBar = new BottomBar(context);
        newBar.setItems(THREE_TABS, DEFAULT_CONFIG);

        BottomBarTab first = newBar.getTabAtPosition(0);
        assertEquals(INACTIVE_TAB_ALPHA, first.getInActiveAlpha(), 0);
        assertEquals(ACTIVE_TAB_ALPHA, first.getActiveAlpha(), 0);
        assertEquals(INACTIVE_TAB_COLOR, first.getInActiveColor());
        assertEquals(ACTIVE_TAB_COLOR, first.getActiveColor());
        assertEquals(BACKGROUND_COLOR, first.getBarColorWhenSelected());
        assertEquals(BADGE_BACKGROUND_COLOR, first.getBadgeBackgroundColor());
        assertEquals(DEFAULT_BADGE_HIDES_WHEN_SELECTED_VALUE, first.getBadgeHidesWhenActive());
        assertEquals(TITLE_TEXT_APPEARANCE, first.getTitleTextAppearance());
        assertEquals(TYPEFACE, first.getTitleTypeFace());
    }

    @Test
    public void setOverrideTabSelectionListener_preventSelection() {
        bottomBar.setTabSelectionInterceptor(tabSelectionInterceptor);

        when(tabSelectionInterceptor.shouldInterceptTabSelection(anyInt(), anyInt())).thenReturn(true);

        BottomBarTab oldTab = bottomBar.getCurrentTab();
        BottomBarTab newTab = bottomBar.getTabAtPosition(2);
        newTab.performClick();

        verify(tabSelectionInterceptor, times(1)).shouldInterceptTabSelection(oldTab.getId(), newTab.getId());
        assertNotSame(bottomBar.getCurrentTab(), newTab);
    }

    @Test
    @UiThreadTest
    public void setOverrideTabSelectionListener_allowingSelection() {
        bottomBar.setTabSelectionInterceptor(tabSelectionInterceptor);

        when(tabSelectionInterceptor.shouldInterceptTabSelection(anyInt(), anyInt())).thenReturn(false);

        BottomBarTab oldTab = bottomBar.getCurrentTab();
        final BottomBarTab newTab = bottomBar.getTabAtPosition(2);
        getInstrumentation().runOnMainSync(new Runnable() {
            @Override
            public void run() {
                newTab.performClick();
            }
        });


        verify(tabSelectionInterceptor, times(1)).shouldInterceptTabSelection(oldTab.getId(), newTab.getId());
        assertSame(bottomBar.getCurrentTab(), newTab);
    }

    @Test
    @UiThreadTest
    public void setOverrideTabSelectionListener_whenNoListenerSet() {
        bottomBar.removeOverrideTabSelectionListener();

        BottomBarTab oldTab = bottomBar.getCurrentTab();
        final BottomBarTab newTab = bottomBar.getTabAtPosition(2);
        getInstrumentation().runOnMainSync(new Runnable() {
            @Override
            public void run() {
                newTab.performClick();
            }
        });

        verify(tabSelectionInterceptor, times(0)).shouldInterceptTabSelection(oldTab.getId(), newTab.getId());
        assertSame(bottomBar.getCurrentTab(), newTab);
    }

    @Test
    public void setOnTabSelectListener_WhenShouldFireInitiallySetToTrue_FiresWhenSet() {
        OnTabSelectListener listener = mock(OnTabSelectListener.class);

        bottomBar.setOnTabSelectListener(listener);
        bottomBar.setOnTabSelectListener(listener, true);

        verify(listener, times(2)).onTabSelected(com.roughike.bottombar.test.R.id.tab_favorites);
    }

    @Test
    public void setOnTabSelectListener_WhenShouldFireInitiallySetToFalse_DoesNotFireWhenSet() {
        OnTabSelectListener listener = mock(OnTabSelectListener.class);

        bottomBar.setOnTabSelectListener(listener, false);
        verifyZeroInteractions(listener);
    }

    @Test
    @UiThreadTest
    public void tabCount_IsCorrect() {
        assertEquals(3, bottomBar.getTabCount());
    }

    @Test
    @UiThreadTest
    public void findingPositionForTabs_ReturnsCorrectPositions() {
        assertEquals(0, bottomBar.findPositionForTabWithId(com.roughike.bottombar.test.R.id.tab_favorites));
        assertEquals(1, bottomBar.findPositionForTabWithId(com.roughike.bottombar.test.R.id.tab_nearby));
        assertEquals(2, bottomBar.findPositionForTabWithId(com.roughike.bottombar.test.R.id.tab_friends));
    }

    @Test
    @UiThreadTest
    public void whenTabIsSelected_SelectionListenerIsFired() {
        bottomBar.selectTabWithId(com.roughike.bottombar.test.R.id.tab_friends);
        bottomBar.selectTabWithId(com.roughike.bottombar.test.R.id.tab_nearby);
        bottomBar.selectTabWithId(com.roughike.bottombar.test.R.id.tab_favorites);

        InOrder inOrder = inOrder(selectListener);
        inOrder.verify(selectListener, times(1)).onTabSelected(com.roughike.bottombar.test.R.id.tab_friends);
        inOrder.verify(selectListener, times(1)).onTabSelected(com.roughike.bottombar.test.R.id.tab_nearby);
        inOrder.verify(selectListener, times(1)).onTabSelected(com.roughike.bottombar.test.R.id.tab_favorites);
        inOrder.verifyNoMoreInteractions();
    }

    @Test
    @UiThreadTest
    public void afterConfigurationChanged_SavedStateRestored_AndSelectedTabPersists() {
        bottomBar.selectTabWithId(com.roughike.bottombar.test.R.id.tab_favorites);

        Bundle savedState = bottomBar.saveState();
        bottomBar.selectTabWithId(com.roughike.bottombar.test.R.id.tab_nearby);
        bottomBar.restoreState(savedState);

        assertEquals(com.roughike.bottombar.test.R.id.tab_favorites, bottomBar.getCurrentTabId());
    }

    @Test
    @UiThreadTest
    public void whenTabIsReselected_ReselectionListenerIsFired() {
        int firstTabId = com.roughike.bottombar.test.R.id.tab_favorites;
        bottomBar.selectTabWithId(firstTabId);
        verify(reselectListener, times(1)).onTabReSelected(firstTabId);

        int secondTabId = com.roughike.bottombar.test.R.id.tab_nearby;
        bottomBar.selectTabWithId(secondTabId);
        bottomBar.selectTabWithId(secondTabId);
        verify(reselectListener, times(1)).onTabReSelected(secondTabId);

        int thirdTabId = com.roughike.bottombar.test.R.id.tab_friends;
        bottomBar.selectTabWithId(thirdTabId);
        bottomBar.selectTabWithId(thirdTabId);
        verify(reselectListener, times(1)).onTabReSelected(thirdTabId);
    }

    @Test
    @UiThreadTest
    public void whenDefaultTabIsSet_ItsSelectedAtFirst() {
        int defaultTabId = com.roughike.bottombar.test.R.id.tab_friends;

        bottomBar.setDefaultTab(defaultTabId);
        verify(selectListener).onTabSelected(defaultTabId);
    }

    @Test(expected = IndexOutOfBoundsException.class)
    public void settingTooLowDefaultPosition_Throws() {
        bottomBar.setDefaultTabPosition(-1);
    }

    @Test(expected = IndexOutOfBoundsException.class)
    public void settingTooHighDefaultPosition_Throws() {
        bottomBar.setDefaultTabPosition(bottomBar.getTabCount());
    }

    @Test
    @UiThreadTest
    public void afterConfigurationChanged_UserSelectedTabPersistsWhenResettingDefaultTab() {
        int defaultTabId = com.roughike.bottombar.test.R.id.tab_friends;

        bottomBar.setDefaultTab(defaultTabId);
        bottomBar.selectTabWithId(com.roughike.bottombar.test.R.id.tab_nearby);

        Bundle savedState = bottomBar.saveState();
        bottomBar.restoreState(savedState);
        bottomBar.setDefaultTab(defaultTabId);

        assertNotSame(defaultTabId, bottomBar.getCurrentTabId());
        assertEquals(com.roughike.bottombar.test.R.id.tab_nearby, bottomBar.getCurrentTabId());
    }

    @Test
    @UiThreadTest
    public void whenGettingCurrentTab_ReturnsCorrectOne() {
        int firstTabId = com.roughike.bottombar.test.R.id.tab_favorites;
        bottomBar.selectTabWithId(firstTabId);

        assertEquals(firstTabId, bottomBar.getCurrentTabId());
        assertEquals(bottomBar.findPositionForTabWithId(firstTabId), bottomBar.getCurrentTabPosition());
        assertEquals(bottomBar.getTabWithId(firstTabId), bottomBar.getCurrentTab());

        int secondTabId = com.roughike.bottombar.test.R.id.tab_nearby;
        bottomBar.selectTabWithId(secondTabId);

        assertEquals(secondTabId, bottomBar.getCurrentTabId());
        assertEquals(bottomBar.findPositionForTabWithId(secondTabId), bottomBar.getCurrentTabPosition());
        assertEquals(bottomBar.getTabWithId(secondTabId), bottomBar.getCurrentTab());

        int thirdTabId = com.roughike.bottombar.test.R.id.tab_friends;
        bottomBar.selectTabWithId(thirdTabId);

        assertEquals(thirdTabId, bottomBar.getCurrentTabId());
        assertEquals(bottomBar.findPositionForTabWithId(thirdTabId), bottomBar.getCurrentTabPosition());
        assertEquals(bottomBar.getTabWithId(thirdTabId), bottomBar.getCurrentTab());
    }

    @Test
    @UiThreadTest
    public void whenSelectionChanges_AndHasNoListeners_onlyOneTabIsSelectedAtATime() {
        bottomBar.removeOnTabSelectListener();
        bottomBar.removeOnTabReselectListener();

        int firstTabId = com.roughike.bottombar.test.R.id.tab_favorites;
        int secondTabId = com.roughike.bottombar.test.R.id.tab_nearby;
        int thirdTabId = com.roughike.bottombar.test.R.id.tab_friends;

        bottomBar.selectTabWithId(secondTabId);
        assertOnlyHasOnlyOneSelectedTabWithId(secondTabId);

        bottomBar.selectTabWithId(thirdTabId);
        assertOnlyHasOnlyOneSelectedTabWithId(thirdTabId);

        bottomBar.selectTabWithId(firstTabId);
        assertOnlyHasOnlyOneSelectedTabWithId(firstTabId);
    }

    private void assertOnlyHasOnlyOneSelectedTabWithId(int tabId) {
        for (int i = 0; i < bottomBar.getTabCount(); i++) {
            BottomBarTab tab = bottomBar.getTabAtPosition(i);

            if (tab.getId() == tabId) {
                assertTrue(tab.isActive());
            } else {
                assertFalse(tab.isActive());
            }
        }
    }

    @Test
    @UiThreadTest
    public void whenTabIsSelectedOnce_AndNoSelectionListenerSet_ReselectionListenerIsNotFired() {
        bottomBar.removeOnTabSelectListener();
        bottomBar.selectTabWithId(com.roughike.bottombar.test.R.id.tab_friends);
        bottomBar.selectTabWithId(com.roughike.bottombar.test.R.id.tab_nearby);
        bottomBar.selectTabWithId(com.roughike.bottombar.test.R.id.tab_favorites);

        verifyZeroInteractions(reselectListener);
    }

    @Test
    @UiThreadTest
    public void setInActiveAlpha_UpdatesAlpha() {
        BottomBarTab inActiveTab = bottomBar.getTabAtPosition(1);
        assertNotEquals(bottomBar.getCurrentTab(), inActiveTab);

        float previousAlpha = inActiveTab.getInActiveAlpha();
        float testAlpha = 0.1f;

        assertNotEquals(testAlpha, previousAlpha);
        assertNotEquals(testAlpha, inActiveTab.getIconView().getAlpha());
        assertNotEquals(testAlpha, inActiveTab.getTitleView().getAlpha());

        bottomBar.setInActiveTabAlpha(testAlpha);
        assertEquals(testAlpha, inActiveTab.getInActiveAlpha(), 0);
        assertEquals(testAlpha, inActiveTab.getIconView().getAlpha(), 0);
        assertEquals(testAlpha, inActiveTab.getTitleView().getAlpha(), 0);
    }

    @Test
    public void setInactiveTabAlpha_LeavesOtherValuesIntact() {
        bottomBar.setInActiveTabAlpha(0.2f);

        BottomBarTab inActiveTab = bottomBar.getTabAtPosition(1);
        assertNotEquals(inActiveTab, bottomBar.getCurrentTab());

        assertEquals(0.2f, inActiveTab.getInActiveAlpha(), 0);
        assertEquals(ACTIVE_TAB_ALPHA, inActiveTab.getActiveAlpha(), 0);
        assertEquals(INACTIVE_TAB_COLOR, inActiveTab.getInActiveColor());
        assertEquals(ACTIVE_TAB_COLOR, inActiveTab.getActiveColor());
        assertEquals(BACKGROUND_COLOR, inActiveTab.getBarColorWhenSelected());
        assertEquals(BADGE_BACKGROUND_COLOR, inActiveTab.getBadgeBackgroundColor());
        assertEquals(TITLE_TEXT_APPEARANCE, inActiveTab.getTitleTextAppearance());
        assertEquals(TYPEFACE, inActiveTab.getTitleTypeFace());
    }

    @Test
    @UiThreadTest
    public void setActiveAlpha_UpdatesAlpha() {
        BottomBarTab activeTab = bottomBar.getCurrentTab();

        float previousAlpha = activeTab.getActiveAlpha();
        float testAlpha = 0.69f;

        assertNotEquals(testAlpha, previousAlpha);
        assertNotEquals(testAlpha, activeTab.getIconView().getAlpha());
        assertNotEquals(testAlpha, activeTab.getTitleView().getAlpha());

        bottomBar.setActiveTabAlpha(testAlpha);
        assertEquals(testAlpha, activeTab.getActiveAlpha(), 0);
        assertEquals(testAlpha, activeTab.getIconView().getAlpha(), 0);
        assertEquals(testAlpha, activeTab.getTitleView().getAlpha(), 0);
    }

    @Test
    public void setActiveTabAlpha_LeavesOtherValuesIntact() {
        bottomBar.setActiveTabAlpha(0.2f);

        BottomBarTab activeTab = bottomBar.getCurrentTab();
        assertEquals(INACTIVE_TAB_ALPHA, activeTab.getInActiveAlpha(), 0);
        assertEquals(0.2f, activeTab.getActiveAlpha(), 0);
        assertEquals(INACTIVE_TAB_COLOR, activeTab.getInActiveColor());
        assertEquals(ACTIVE_TAB_COLOR, activeTab.getActiveColor());
        assertEquals(BACKGROUND_COLOR, activeTab.getBarColorWhenSelected());
        assertEquals(BADGE_BACKGROUND_COLOR, activeTab.getBadgeBackgroundColor());
        assertEquals(TITLE_TEXT_APPEARANCE, activeTab.getTitleTextAppearance());
        assertEquals(TYPEFACE, activeTab.getTitleTypeFace());
    }

    @Test
    @UiThreadTest
    public void setInActiveColor_UpdatesColor() {
        BottomBarTab inActiveTab = bottomBar.getTabAtPosition(1);
        assertNotEquals(bottomBar.getCurrentTab(), inActiveTab);

        int previousInActiveColor = inActiveTab.getInActiveColor();
        int previousIconColor = inActiveTab.getCurrentDisplayedIconColor();
        int previousTitleColor = inActiveTab.getCurrentDisplayedTitleColor();

        int testColor = Color.GREEN;
        assertNotEquals(testColor, previousInActiveColor);
        assertNotEquals(testColor, previousIconColor);
        assertNotEquals(testColor, previousTitleColor);

        bottomBar.setInActiveTabColor(testColor);
        assertEquals(testColor, inActiveTab.getInActiveColor());
        assertEquals(testColor, inActiveTab.getCurrentDisplayedIconColor());
        assertEquals(testColor, inActiveTab.getCurrentDisplayedTitleColor());
    }

    @Test
    public void setInactiveColor_LeavesOtherValuesIntact() {
        bottomBar.setInActiveTabColor(Color.BLUE);

        BottomBarTab inActiveTab = bottomBar.getTabAtPosition(1);
        assertNotEquals(inActiveTab, bottomBar.getCurrentTab());

        assertEquals(INACTIVE_TAB_ALPHA, inActiveTab.getInActiveAlpha(), 0);
        assertEquals(ACTIVE_TAB_ALPHA, inActiveTab.getActiveAlpha(), 0);
        assertEquals(Color.BLUE, inActiveTab.getInActiveColor());
        assertEquals(ACTIVE_TAB_COLOR, inActiveTab.getActiveColor());
        assertEquals(BACKGROUND_COLOR, inActiveTab.getBarColorWhenSelected());
        assertEquals(BADGE_BACKGROUND_COLOR, inActiveTab.getBadgeBackgroundColor());
        assertEquals(TITLE_TEXT_APPEARANCE, inActiveTab.getTitleTextAppearance());
        assertEquals(TYPEFACE, inActiveTab.getTitleTypeFace());
    }

    @Test
    @UiThreadTest
    public void setActiveColor_UpdatesColor() {
        BottomBarTab activeTab = bottomBar.getCurrentTab();
        int previousActiveColor = activeTab.getActiveColor();
        int previousIconColor = activeTab.getCurrentDisplayedIconColor();
        int previousTitleColor = activeTab.getCurrentDisplayedTitleColor();

        int testColor = Color.GRAY;
        assertNotEquals(testColor, previousActiveColor);
        assertNotEquals(testColor, previousIconColor);
        assertNotEquals(testColor, previousTitleColor);

        bottomBar.setActiveTabColor(testColor);
        assertEquals(testColor, activeTab.getActiveColor());
        assertEquals(testColor, activeTab.getCurrentDisplayedIconColor());
        assertEquals(testColor, activeTab.getCurrentDisplayedTitleColor());
    }

    @Test
    public void setActiveColor_LeavesOtherValuesIntact() {
        bottomBar.setActiveTabColor(Color.BLUE);

        BottomBarTab inActiveTab = bottomBar.getTabAtPosition(1);
        assertNotEquals(inActiveTab, bottomBar.getCurrentTab());

        assertEquals(INACTIVE_TAB_ALPHA, inActiveTab.getInActiveAlpha(), 0);
        assertEquals(ACTIVE_TAB_ALPHA, inActiveTab.getActiveAlpha(), 0);
        assertEquals(INACTIVE_TAB_COLOR, inActiveTab.getInActiveColor());
        assertEquals(Color.BLUE, inActiveTab.getActiveColor());
        assertEquals(BACKGROUND_COLOR, inActiveTab.getBarColorWhenSelected());
        assertEquals(BADGE_BACKGROUND_COLOR, inActiveTab.getBadgeBackgroundColor());
        assertEquals(TITLE_TEXT_APPEARANCE, inActiveTab.getTitleTextAppearance());
        assertEquals(TYPEFACE, inActiveTab.getTitleTypeFace());
    }

    @Test
    @UiThreadTest
    public void setBadgeBackgroundColor_UpdatesColor() {
        BottomBarTab inActiveTab = bottomBar.getTabAtPosition(1);
        inActiveTab.setBadgeCount(3);

        int previousBadgeColor = inActiveTab.getBadgeBackgroundColor();
        int testColor = Color.GREEN;
        assertNotEquals(testColor, previousBadgeColor);

        bottomBar.setBadgeBackgroundColor(testColor);
        assertEquals(testColor, inActiveTab.getBadgeBackgroundColor());
    }

    @Test
    public void setBadgeBackgroundColor_LeavesOtherValuesIntact() {
        bottomBar.setBadgeBackgroundColor(Color.BLUE);

        BottomBarTab inActiveTab = bottomBar.getTabAtPosition(1);
        assertNotEquals(inActiveTab, bottomBar.getCurrentTab());

        assertEquals(INACTIVE_TAB_ALPHA, inActiveTab.getInActiveAlpha(), 0);
        assertEquals(ACTIVE_TAB_ALPHA, inActiveTab.getActiveAlpha(), 0);
        assertEquals(INACTIVE_TAB_COLOR, inActiveTab.getInActiveColor());
        assertEquals(ACTIVE_TAB_COLOR, inActiveTab.getActiveColor());
        assertEquals(BACKGROUND_COLOR, inActiveTab.getBarColorWhenSelected());
        assertEquals(Color.BLUE, inActiveTab.getBadgeBackgroundColor());
        assertEquals(TITLE_TEXT_APPEARANCE, inActiveTab.getTitleTextAppearance());
        assertEquals(TYPEFACE, inActiveTab.getTitleTypeFace());
    }

    @Test
    @UiThreadTest
    public void setBadgeHidesWhenSelected_UpdatesBadgeHidesWhenSelected() {
        BottomBarTab tab = bottomBar.getCurrentTab();

        boolean previousBadgeHidesValue = tab.getBadgeHidesWhenActive();
        assertTrue(previousBadgeHidesValue);

        bottomBar.setBadgesHideWhenActive(false);
        assertFalse(tab.getBadgeHidesWhenActive());
    }

    @Test
    public void setBadgeHidesWhenSelected_LeavesOtherValuesIntact() {
        bottomBar.setBadgesHideWhenActive(true);

        BottomBarTab tab = bottomBar.getCurrentTab();

        assertEquals(INACTIVE_TAB_ALPHA, tab.getInActiveAlpha(), 0);
        assertEquals(ACTIVE_TAB_ALPHA, tab.getActiveAlpha(), 0);
        assertEquals(INACTIVE_TAB_COLOR, tab.getInActiveColor());
        assertEquals(ACTIVE_TAB_COLOR, tab.getActiveColor());
        assertEquals(BACKGROUND_COLOR, tab.getBarColorWhenSelected());
        assertEquals(BADGE_BACKGROUND_COLOR, tab.getBadgeBackgroundColor());
        assertTrue(tab.getBadgeHidesWhenActive());
        assertEquals(TITLE_TEXT_APPEARANCE, tab.getTitleTextAppearance());
        assertEquals(TYPEFACE, tab.getTitleTypeFace());
    }

    @Test
    @UiThreadTest
    public void setTitleTextAppearance_UpdatesAppearance() {
        BottomBarTab tab = bottomBar.getCurrentTab();

        int testTextApperance = -666;
        assertNotEquals(testTextApperance, tab.getTitleTextAppearance());
        assertNotEquals(testTextApperance, tab.getCurrentDisplayedTextAppearance());

        bottomBar.setTabTitleTextAppearance(testTextApperance);
        assertEquals(testTextApperance, tab.getTitleTextAppearance());
        assertEquals(testTextApperance, tab.getCurrentDisplayedTextAppearance());
    }

    @Test
    public void setTitleTextAppearance_LeavesOtherValuesIntact() {
        bottomBar.setTabTitleTextAppearance(-666);

        BottomBarTab inActiveTab = bottomBar.getTabAtPosition(1);
        assertNotEquals(inActiveTab, bottomBar.getCurrentTab());

        assertEquals(INACTIVE_TAB_ALPHA, inActiveTab.getInActiveAlpha(), 0);
        assertEquals(ACTIVE_TAB_ALPHA, inActiveTab.getActiveAlpha(), 0);
        assertEquals(INACTIVE_TAB_COLOR, inActiveTab.getInActiveColor());
        assertEquals(ACTIVE_TAB_COLOR, inActiveTab.getActiveColor());
        assertEquals(BACKGROUND_COLOR, inActiveTab.getBarColorWhenSelected());
        assertEquals(BADGE_BACKGROUND_COLOR, inActiveTab.getBadgeBackgroundColor());
        assertEquals(-666, inActiveTab.getTitleTextAppearance());
        assertEquals(TYPEFACE, inActiveTab.getTitleTypeFace());
    }

    @Test
    @UiThreadTest
    public void setTitleTypeface_UpdatesTypeface() {
        BottomBarTab tab = bottomBar.getCurrentTab();
        Typeface testTypeface = Typeface.createFromAsset(
                bottomBar.getContext().getAssets(), "fonts/GreatVibes-Regular.otf");

        assertNotEquals(testTypeface, tab.getTitleTypeFace());
        assertNotEquals(testTypeface, tab.getTitleView().getTypeface());

        bottomBar.setTabTitleTypeface(testTypeface);
        assertEquals(testTypeface, tab.getTitleTypeFace());
        assertEquals(testTypeface, tab.getTitleView().getTypeface());
    }

    @Test
    public void setTitleTypeface_LeavesOtherValuesIntact() {
        bottomBar.setTabTitleTypeface(Typeface.DEFAULT);

        BottomBarTab inActiveTab = bottomBar.getTabAtPosition(1);
        assertNotEquals(inActiveTab, bottomBar.getCurrentTab());

        assertEquals(INACTIVE_TAB_ALPHA, inActiveTab.getInActiveAlpha(), 0);
        assertEquals(ACTIVE_TAB_ALPHA, inActiveTab.getActiveAlpha(), 0);
        assertEquals(INACTIVE_TAB_COLOR, inActiveTab.getInActiveColor());
        assertEquals(ACTIVE_TAB_COLOR, inActiveTab.getActiveColor());
        assertEquals(BACKGROUND_COLOR, inActiveTab.getBarColorWhenSelected());
        assertEquals(BADGE_BACKGROUND_COLOR, inActiveTab.getBadgeBackgroundColor());
        assertEquals(TITLE_TEXT_APPEARANCE, inActiveTab.getTitleTextAppearance());
        assertEquals(Typeface.DEFAULT, inActiveTab.getTitleTypeFace());
    }
}


================================================
FILE: bottom-bar/src/androidTest/java/com/roughike/bottombar/TabParserTest.java
================================================
package com.roughike.bottombar;

import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.LargeTest;
import android.support.test.runner.AndroidJUnit4;
import android.support.v4.content.ContextCompat;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.util.List;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;

@RunWith(AndroidJUnit4.class)
@LargeTest
public class TabParserTest {
    private Context context;
    private List<BottomBarTab> tabs;

    @Before
    public void setUp() throws Exception {
        context = InstrumentationRegistry.getContext();
        tabs = new TabParser(
                context,
                new BottomBarTab.Config.Builder().build(),
                com.roughike.bottombar.test.R.xml.dummy_tabs_five
        ).parseTabs();
    }

    @Test
    public void correctAmountOfTabs() {
        assertEquals(5, tabs.size());
    }

    @Test
    public void idsNotEmpty() {
        assertNotSame(0, tabs.get(0).getId());
        assertNotSame(0, tabs.get(1).getId());
        assertNotSame(0, tabs.get(2).getId());
        assertNotSame(0, tabs.get(3).getId());
        assertNotSame(0, tabs.get(4).getId());
    }

    @Test
    public void correctTabTitles() {
        assertEquals("Recents", tabs.get(0).getTitle());
        assertEquals("Favorites", tabs.get(1).getTitle());
        assertEquals("Nearby", tabs.get(2).getTitle());
        assertEquals("Friends", tabs.get(3).getTitle());
        assertEquals("Food", tabs.get(4).getTitle());
    }

    @Test
    public void correctInActiveColors() {
        assertEquals(Color.parseColor("#00FF00"), tabs.get(0).getInActiveColor());
        assertEquals(Color.parseColor("#0000FF"), tabs.get(1).getInActiveColor());
        assertEquals(Color.parseColor("#FF0000"), tabs.get(2).getInActiveColor());
        assertEquals(Color.parseColor("#F0F000"), tabs.get(3).getInActiveColor());
        assertEquals(Color.parseColor("#F00F00"), tabs.get(4).getInActiveColor());
    }

    @Test
    public void correctActiveColors() {
        assertEquals(Color.parseColor("#FF0000"), tabs.get(0).getActiveColor());

        assertEquals(
                ContextCompat.getColor(context, com.roughike.bottombar.test.R.color.test_random_color),
                tabs.get(1).getActiveColor()
        );

        assertEquals(Color.parseColor("#0000FF"), tabs.get(2).getActiveColor());
        assertEquals(Color.parseColor("#DAD666"), tabs.get(3).getActiveColor());
        assertEquals(Color.parseColor("#F00F00"), tabs.get(4).getActiveColor());
    }

    @Test
    public void iconResourcesExist() {
        assertNotNull(getDrawableByResource(tabs.get(0).getIconResId()));
        assertNotNull(getDrawableByResource(tabs.get(1).getIconResId()));
        assertNotNull(getDrawableByResource(tabs.get(2).getIconResId()));
        assertNotNull(getDrawableByResource(tabs.get(3).getIconResId()));
        assertNotNull(getDrawableByResource(tabs.get(4).getIconResId()));
    }

    @Test
    public void iconResourceIdsAsExpected() {
        int expectedId = com.roughike.bottombar.test.R.drawable.empty_icon;

        assertEquals(expectedId, tabs.get(0).getIconResId());
        assertEquals(expectedId, tabs.get(1).getIconResId());
        assertEquals(expectedId, tabs.get(2).getIconResId());
        assertEquals(expectedId, tabs.get(3).getIconResId());
        assertEquals(expectedId, tabs.get(4).getIconResId());
    }

    @Test
    public void barColorWhenSelectedAsExpected() {
        assertEquals(Color.parseColor("#FF0000"), tabs.get(0).getBarColorWhenSelected());
        assertEquals(Color.parseColor("#00FF00"), tabs.get(1).getBarColorWhenSelected());
        assertEquals(Color.parseColor("#F00000"), tabs.get(2).getBarColorWhenSelected());
        assertEquals(Color.parseColor("#00F000"), tabs.get(3).getBarColorWhenSelected());
        assertEquals(Color.parseColor("#00F0F0"), tabs.get(4).getBarColorWhenSelected());
    }

    @Test
    public void badgeBackgroundColorAsExpected() {
        assertEquals(Color.parseColor("#FF0000"), tabs.get(0).getBadgeBackgroundColor());
        assertEquals(Color.parseColor("#00FF00"), tabs.get(1).getBadgeBackgroundColor());
        assertEquals(Color.parseColor("#F00000"), tabs.get(2).getBadgeBackgroundColor());
        assertEquals(Color.parseColor("#00F000"), tabs.get(3).getBadgeBackgroundColor());
        assertEquals(Color.parseColor("#00F0F0"), tabs.get(4).getBadgeBackgroundColor());
    }

    @Test
    public void correctBadgeHidingPolicies() {
        assertFalse(tabs.get(0).getBadgeHidesWhenActive());
        assertTrue(tabs.get(1).getBadgeHidesWhenActive());
        assertFalse(tabs.get(2).getBadgeHidesWhenActive());
        assertTrue(tabs.get(3).getBadgeHidesWhenActive());
        assertTrue(tabs.get(4).getBadgeHidesWhenActive());
    }

    @Test
    public void titlelessTabsAsExpected() {
        assertFalse(tabs.get(0).isTitleless());
        assertFalse(tabs.get(1).isTitleless());
        assertTrue(tabs.get(2).isTitleless());
        assertFalse(tabs.get(3).isTitleless());
        assertTrue(tabs.get(4).isTitleless());
    }

    private Drawable getDrawableByResource(int iconResId) {
        return ContextCompat.getDrawable(context, iconResId);
    }
}

================================================
FILE: bottom-bar/src/androidTest/res/values/colors.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="test_random_color">#FAB123</color>
    <color name="test_random_color_two">#DAD666</color>
</resources>

================================================
FILE: bottom-bar/src/androidTest/res/values/strings.xml
================================================
<resources>
    <string name="favorites">Favorites</string>
    <string name="friends">Friends</string>
</resources>


================================================
FILE: bottom-bar/src/androidTest/res/values/styles.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="dummy_text_appearance" />
</resources>

================================================
FILE: bottom-bar/src/androidTest/res/xml/dummy_tabs_five.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<tabs>
    <tab inActiveColor="#00FF00" activeColor="#FF0000" id="@+id/tab_recents" title="Recents" icon="@drawable/empty_icon" barColorWhenSelected="#FF0000" badgeBackgroundColor="#FF0000" badgeHidesWhenActive="false" />
    <tab inActiveColor="#0000FF" activeColor="@color/test_random_color" id="@+id/tab_favorites" title="@string/favorites" icon="@drawable/empty_icon" barColorWhenSelected="#00FF00" badgeBackgroundColor="#00FF00" />
    <tab inActiveColor="#FF0000" activeColor="#0000FF" id="@+id/tab_nearby" title="Nearby" icon="@drawable/empty_icon" barColorWhenSelected="#F00000" badgeBackgroundColor="#F00000" badgeHidesWhenActive="false" iconOnly="true" />
    <tab inActiveColor="#F0F000" activeColor="@color/test_random_color_two" id="@+id/tab_friends" title="@string/friends" icon="@drawable/empty_icon" barColorWhenSelected="#00F000" badgeBackgroundColor="#00F000" badgeHidesWhenActive="true" />
    <tab inActiveColor="#F00F00" activeColor="#F00F00" id="@+id/tab_food" title="Food" icon="@drawable/empty_icon" barColorWhenSelected="#00F0F0" badgeBackgroundColor="#00F0F0" iconOnly="true" />
</tabs>

================================================
FILE: bottom-bar/src/androidTest/res/xml/dummy_tabs_three.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<tabs>
    <tab
        id="@+id/tab_favorites"
        icon="@drawable/empty_icon"
        title="Favorites" />
    <tab
        id="@+id/tab_nearby"
        icon="@drawable/empty_icon"
        title="Nearby" />
    <tab
        id="@+id/tab_friends"
        icon="@drawable/empty_icon"
        title="Friends" />
</tabs>

================================================
FILE: bottom-bar/src/main/AndroidManifest.xml
================================================
<manifest package="com.roughike.bottombar" />


================================================
FILE: bottom-bar/src/main/java/com/roughike/bottombar/BadgeCircle.java
================================================
/*
 * 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.roughike.bottombar;

import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.support.annotation.ColorInt;
import android.support.annotation.IntRange;
import android.support.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;
    }
}


================================================
FILE: bottom-bar/src/main/java/com/roughike/bottombar/BadgeContainer.java
================================================
package com.roughike.bottombar;

import android.content.Context;
import android.support.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);
    }
}


================================================
FILE: bottom-bar/src/main/java/com/roughike/bottombar/BatchTabPropertyApplier.java
================================================
package com.roughike.bottombar;

import android.support.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);
            }
        }
    }
}


================================================
FILE: bottom-bar/src/main/java/com/roughike/bottombar/BottomBar.java
================================================
package com.roughike.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 android.support.annotation.ColorInt;
import android.support.annotation.IdRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.support.annotation.VisibleForTesting;
import android.support.annotation.XmlRes;
import android.support.design.widget.CoordinatorLayout;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.ViewCompat;
import android.support.v4.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 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.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 = (ViewGroup) rootView.findViewById(R.id.bb_bottom_bar_outer_container);
        tabContainer = (ViewGroup) 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<BottomBarTab> 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.
     * <p>
     * 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.
     * <p>
     * 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.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();
    }
}


================================================
FILE: bottom-bar/src/main/java/com/roughike/bottombar/BottomBarBadge.java
================================================
package com.roughike.bottombar;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ShapeDrawable;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.VisibleForTesting;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.AppCompatImageView;
import android.view.Gravity;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.TextView;

/*
 * 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 TextView {
    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);
        }
    }
}


================================================
FILE: bottom-bar/src/main/java/com/roughike/bottombar/BottomBarTab.java
================================================
package com.roughike.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 android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewPropertyAnimatorCompat;
import android.support.v7.widget.AppCompatImageView;
import android.view.Gravity;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;

/*
 * 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 = (AppCompatImageView) findViewById(R.id.bb_bottom_bar_icon);
        iconView.setImageResource(iconResId);

        if (type != Type.TABLET && !isTitleless) {
            titleView = (TextView) 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 ? sixtee
Download .txt
gitextract_7tzdrylq/

├── .gitignore
├── .idea/
│   ├── .name
│   ├── compiler.xml
│   ├── copyright/
│   │   └── profiles_settings.xml
│   ├── encodings.xml
│   ├── gradle.xml
│   ├── kotlinc.xml
│   ├── markdown-exported-files.xml
│   ├── markdown-navigator/
│   │   └── profiles_settings.xml
│   ├── markdown-navigator.xml
│   ├── misc.xml
│   ├── modules.xml
│   ├── runConfigurations/
│   │   └── All_Tests.xml
│   ├── runConfigurations.xml
│   └── vcs.xml
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── NOTICE
├── README.md
├── app/
│   ├── build.gradle
│   ├── proguard-rules.pro
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── assets/
│           │   └── fonts/
│           │       ├── GREAT-VIBES-LICENSE
│           │       └── GreatVibes-Regular.otf
│           ├── java/
│           │   └── com/
│           │       └── example/
│           │           └── bottombar/
│           │               └── sample/
│           │                   ├── BadgeActivity.java
│           │                   ├── CustomColorAndFontActivity.java
│           │                   ├── FiveColorChangingTabsActivity.java
│           │                   ├── IconsOnlyActivity.java
│           │                   ├── MainActivity.java
│           │                   ├── SampleFragment.java
│           │                   ├── TabMessage.java
│           │                   ├── ThreeTabsActivity.java
│           │                   └── ThreeTabsQRActivity.java
│           └── res/
│               ├── layout/
│               │   ├── activity_color_changing_tabs.xml
│               │   ├── activity_custom_color_and_font.xml
│               │   ├── activity_icons_only.xml
│               │   ├── activity_main.xml
│               │   ├── activity_three_tabs.xml
│               │   └── activity_three_tabs_quick_return.xml
│               ├── layout-sw600dp/
│               │   ├── activity_color_changing_tabs.xml
│               │   └── activity_three_tabs.xml
│               ├── values/
│               │   ├── colors.xml
│               │   ├── dimens.xml
│               │   ├── strings.xml
│               │   └── styles.xml
│               ├── values-land-v21/
│               │   └── styles.xml
│               ├── values-sw600dp/
│               │   └── styles.xml
│               ├── values-v21/
│               │   └── styles.xml
│               ├── values-w820dp/
│               │   └── dimens.xml
│               └── xml/
│                   ├── bottombar_tabs_color_changing.xml
│                   ├── bottombar_tabs_five.xml
│                   └── bottombar_tabs_three.xml
├── bottom-bar/
│   ├── build.gradle
│   └── src/
│       ├── androidTest/
│       │   ├── assets/
│       │   │   └── fonts/
│       │   │       ├── GREAT-VIBES-LICENSE
│       │   │       └── GreatVibes-Regular.otf
│       │   ├── java/
│       │   │   └── com/
│       │   │       └── roughike/
│       │   │           └── bottombar/
│       │   │               ├── BadgeTest.java
│       │   │               ├── BottomBarTabTest.java
│       │   │               ├── BottomBarTest.java
│       │   │               └── TabParserTest.java
│       │   └── res/
│       │       ├── values/
│       │       │   ├── colors.xml
│       │       │   ├── strings.xml
│       │       │   └── styles.xml
│       │       └── xml/
│       │           ├── dummy_tabs_five.xml
│       │           └── dummy_tabs_three.xml
│       ├── main/
│       │   ├── AndroidManifest.xml
│       │   ├── java/
│       │   │   └── com/
│       │   │       └── roughike/
│       │   │           └── bottombar/
│       │   │               ├── BadgeCircle.java
│       │   │               ├── BadgeContainer.java
│       │   │               ├── BatchTabPropertyApplier.java
│       │   │               ├── BottomBar.java
│       │   │               ├── BottomBarBadge.java
│       │   │               ├── BottomBarTab.java
│       │   │               ├── BottomNavigationBehavior.java
│       │   │               ├── MiscUtils.java
│       │   │               ├── NavbarUtils.java
│       │   │               ├── OnTabReselectListener.java
│       │   │               ├── OnTabSelectListener.java
│       │   │               ├── ShySettings.java
│       │   │               ├── TabParser.java
│       │   │               ├── TabSelectionInterceptor.java
│       │   │               └── VerticalScrollingBehavior.java
│       │   └── res/
│       │       ├── drawable/
│       │       │   └── bb_bottom_bar_top_shadow.xml
│       │       ├── layout/
│       │       │   ├── bb_bottom_bar_item_container.xml
│       │       │   ├── bb_bottom_bar_item_container_tablet.xml
│       │       │   ├── bb_bottom_bar_item_fixed.xml
│       │       │   ├── bb_bottom_bar_item_fixed_tablet.xml
│       │       │   ├── bb_bottom_bar_item_shifting.xml
│       │       │   └── bb_bottom_bar_item_titleless.xml
│       │       ├── layout-v21/
│       │       │   └── bb_bottom_bar_item_container.xml
│       │       ├── values/
│       │       │   ├── attrs.xml
│       │       │   ├── bools.xml
│       │       │   ├── colors.xml
│       │       │   ├── dimensions.xml
│       │       │   ├── ids.xml
│       │       │   ├── strings.xml
│       │       │   └── styles.xml
│       │       └── values-land/
│       │           └── bools.xml
│       └── test/
│           └── java/
│               └── com/
│                   └── roughike/
│                       └── bottombar/
│                           └── ExampleUnitTest.java
├── build.gradle
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── projectFilesBackup/
│   └── .idea/
│       └── workspace.xml
└── settings.gradle
Download .txt
SYMBOL INDEX (339 symbols across 29 files)

FILE: app/src/main/java/com/example/bottombar/sample/BadgeActivity.java
  class BadgeActivity (line 18) | public class BadgeActivity extends AppCompatActivity {
    method onCreate (line 21) | @Override

FILE: app/src/main/java/com/example/bottombar/sample/CustomColorAndFontActivity.java
  class CustomColorAndFontActivity (line 17) | public class CustomColorAndFontActivity extends AppCompatActivity {
    method onCreate (line 21) | @Override

FILE: app/src/main/java/com/example/bottombar/sample/FiveColorChangingTabsActivity.java
  class FiveColorChangingTabsActivity (line 17) | public class FiveColorChangingTabsActivity extends AppCompatActivity {
    method onCreate (line 20) | @Override

FILE: app/src/main/java/com/example/bottombar/sample/IconsOnlyActivity.java
  class IconsOnlyActivity (line 14) | public class IconsOnlyActivity extends Activity {
    method onCreate (line 17) | @Override

FILE: app/src/main/java/com/example/bottombar/sample/MainActivity.java
  class MainActivity (line 8) | public class MainActivity extends AppCompatActivity implements View.OnCl...
    method onCreate (line 9) | @SuppressWarnings("ConstantConditions")
    method onClick (line 23) | @Override

FILE: app/src/main/java/com/example/bottombar/sample/SampleFragment.java
  class SampleFragment (line 31) | public class SampleFragment extends Fragment {
    method SampleFragment (line 34) | public SampleFragment() {
    method newInstance (line 37) | public static SampleFragment newInstance(String text) {
    method onCreateView (line 47) | @Nullable

FILE: app/src/main/java/com/example/bottombar/sample/TabMessage.java
  class TabMessage (line 6) | public class TabMessage {
    method get (line 7) | public static String get(int menuItemId, boolean isReselection) {

FILE: app/src/main/java/com/example/bottombar/sample/ThreeTabsActivity.java
  class ThreeTabsActivity (line 17) | public class ThreeTabsActivity extends Activity {
    method onCreate (line 20) | @Override

FILE: app/src/main/java/com/example/bottombar/sample/ThreeTabsQRActivity.java
  class ThreeTabsQRActivity (line 14) | public class ThreeTabsQRActivity extends AppCompatActivity {
    method onCreate (line 16) | @Override

FILE: bottom-bar/src/androidTest/java/com/roughike/bottombar/BadgeTest.java
  class BadgeTest (line 24) | @RunWith(AndroidJUnit4.class)
    method setUp (line 30) | @Before
    method hasNoBadges_ExceptNearby (line 38) | @Test
    method whenTabWithBadgeClicked_BadgeIsHidden (line 46) | @Test
    method whenBadgeCountIsZero_BadgeIsRemoved (line 58) | @Test
    method whenBadgeCountIsNegative_BadgeIsRemoved (line 65) | @Test
    method whenBadgeStateRestored_CountPersists (line 72) | @Test
    method badgeRemovedProperly (line 87) | @Test

FILE: bottom-bar/src/androidTest/java/com/roughike/bottombar/BottomBarTabTest.java
  class BottomBarTabTest (line 14) | @RunWith(AndroidJUnit4.class)
    method setUp (line 19) | @Before
    method correctLayoutReturnedForFixedTab (line 27) | @Test
    method setIsTitleless_WhenTrueAndIconDoesNotExist_ThrowsException (line 33) | @Test(expected = IllegalStateException.class)
    method correctLayoutForShiftingTab (line 39) | @Test
    method correctLayoutForTabletTab (line 45) | @Test
    method testSavedStateWithBadge_StaysIntact (line 51) | @Test

FILE: bottom-bar/src/androidTest/java/com/roughike/bottombar/BottomBarTest.java
  class BottomBarTest (line 37) | @RunWith(AndroidJUnit4.class)
    method setUp (line 72) | @Before
    method canCreateNewInstanceFromXml_WithoutXmlMenuResource (line 86) | @Test
    method setItems_ThrowsExceptionWithNoResource (line 91) | @Test(expected = RuntimeException.class)
    method setItems_AfterAlreadySet_ReplacesPreviousWithNewOnes (line 97) | @Test
    method setItemsWithCustomConfig_OverridesPreviousValues (line 106) | @Test
    method setOverrideTabSelectionListener_preventSelection (line 123) | @Test
    method setOverrideTabSelectionListener_allowingSelection (line 137) | @Test
    method setOverrideTabSelectionListener_whenNoListenerSet (line 158) | @Test
    method setOnTabSelectListener_WhenShouldFireInitiallySetToTrue_FiresWhenSet (line 176) | @Test
    method setOnTabSelectListener_WhenShouldFireInitiallySetToFalse_DoesNotFireWhenSet (line 186) | @Test
    method tabCount_IsCorrect (line 194) | @Test
    method findingPositionForTabs_ReturnsCorrectPositions (line 200) | @Test
    method whenTabIsSelected_SelectionListenerIsFired (line 208) | @Test
    method afterConfigurationChanged_SavedStateRestored_AndSelectedTabPersists (line 222) | @Test
    method whenTabIsReselected_ReselectionListenerIsFired (line 234) | @Test
    method whenDefaultTabIsSet_ItsSelectedAtFirst (line 252) | @Test
    method settingTooLowDefaultPosition_Throws (line 261) | @Test(expected = IndexOutOfBoundsException.class)
    method settingTooHighDefaultPosition_Throws (line 266) | @Test(expected = IndexOutOfBoundsException.class)
    method afterConfigurationChanged_UserSelectedTabPersistsWhenResettingDefaultTab (line 271) | @Test
    method whenGettingCurrentTab_ReturnsCorrectOne (line 287) | @Test
    method whenSelectionChanges_AndHasNoListeners_onlyOneTabIsSelectedAtATime (line 312) | @Test
    method assertOnlyHasOnlyOneSelectedTabWithId (line 332) | private void assertOnlyHasOnlyOneSelectedTabWithId(int tabId) {
    method whenTabIsSelectedOnce_AndNoSelectionListenerSet_ReselectionListenerIsNotFired (line 344) | @Test
    method setInActiveAlpha_UpdatesAlpha (line 355) | @Test
    method setInactiveTabAlpha_LeavesOtherValuesIntact (line 374) | @Test
    method setActiveAlpha_UpdatesAlpha (line 391) | @Test
    method setActiveTabAlpha_LeavesOtherValuesIntact (line 409) | @Test
    method setInActiveColor_UpdatesColor (line 424) | @Test
    method setInactiveColor_LeavesOtherValuesIntact (line 445) | @Test
    method setActiveColor_UpdatesColor (line 462) | @Test
    method setActiveColor_LeavesOtherValuesIntact (line 481) | @Test
    method setBadgeBackgroundColor_UpdatesColor (line 498) | @Test
    method setBadgeBackgroundColor_LeavesOtherValuesIntact (line 512) | @Test
    method setBadgeHidesWhenSelected_UpdatesBadgeHidesWhenSelected (line 529) | @Test
    method setBadgeHidesWhenSelected_LeavesOtherValuesIntact (line 541) | @Test
    method setTitleTextAppearance_UpdatesAppearance (line 558) | @Test
    method setTitleTextAppearance_LeavesOtherValuesIntact (line 572) | @Test
    method setTitleTypeface_UpdatesTypeface (line 589) | @Test
    method setTitleTypeface_LeavesOtherValuesIntact (line 604) | @Test

FILE: bottom-bar/src/androidTest/java/com/roughike/bottombar/TabParserTest.java
  class TabParserTest (line 23) | @RunWith(AndroidJUnit4.class)
    method setUp (line 29) | @Before
    method correctAmountOfTabs (line 39) | @Test
    method idsNotEmpty (line 44) | @Test
    method correctTabTitles (line 53) | @Test
    method correctInActiveColors (line 62) | @Test
    method correctActiveColors (line 71) | @Test
    method iconResourcesExist (line 85) | @Test
    method iconResourceIdsAsExpected (line 94) | @Test
    method barColorWhenSelectedAsExpected (line 105) | @Test
    method badgeBackgroundColorAsExpected (line 114) | @Test
    method correctBadgeHidingPolicies (line 123) | @Test
    method titlelessTabsAsExpected (line 132) | @Test
    method getDrawableByResource (line 141) | private Drawable getDrawableByResource(int iconResId) {

FILE: bottom-bar/src/main/java/com/roughike/bottombar/BadgeCircle.java
  class BadgeCircle (line 42) | class BadgeCircle {
    method make (line 50) | @NonNull

FILE: bottom-bar/src/main/java/com/roughike/bottombar/BadgeContainer.java
  class BadgeContainer (line 10) | public class BadgeContainer extends FrameLayout {
    method BadgeContainer (line 11) | public BadgeContainer(@NonNull Context context) {

FILE: bottom-bar/src/main/java/com/roughike/bottombar/BatchTabPropertyApplier.java
  class BatchTabPropertyApplier (line 5) | class BatchTabPropertyApplier {
    type TabPropertyUpdater (line 8) | interface TabPropertyUpdater {
      method update (line 9) | void update(BottomBarTab tab);
    method BatchTabPropertyApplier (line 12) | BatchTabPropertyApplier(@NonNull BottomBar bottomBar) {
    method applyToAllTabs (line 16) | void applyToAllTabs(@NonNull TabPropertyUpdater propertyUpdater) {

FILE: bottom-bar/src/main/java/com/roughike/bottombar/BottomBar.java
  class BottomBar (line 55) | public class BottomBar extends LinearLayout implements View.OnClickListe...
    method BottomBar (line 117) | public BottomBar(Context context) {
    method BottomBar (line 121) | public BottomBar(Context context, AttributeSet attrs) {
    method BottomBar (line 125) | public BottomBar(Context context, @Nullable AttributeSet attrs, int de...
    method BottomBar (line 130) | @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    method init (line 136) | private void init(Context context, AttributeSet attrs, int defStyleAtt...
    method onAttachedToWindow (line 152) | @Override
    method init21 (line 173) | @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    method populateAttributes (line 185) | private void populateAttributes(Context context, AttributeSet attrs, i...
    method isShiftingMode (line 220) | private boolean isShiftingMode() {
    method drawUnderNav (line 224) | private boolean drawUnderNav() {
    method isShy (line 230) | boolean isShy() {
    method isShyHeightAlreadyCalculated (line 234) | boolean isShyHeightAlreadyCalculated() {
    method isIconsOnlyMode (line 238) | private boolean isIconsOnlyMode() {
    method hasBehavior (line 242) | private boolean hasBehavior(int behavior) {
    method getTypeFaceFromAsset (line 246) | private Typeface getTypeFaceFromAsset(String fontPath) {
    method initializeViews (line 255) | private void initializeViews() {
    method determineInitialBackgroundColor (line 273) | private void determineInitialBackgroundColor() {
    method setItems (line 292) | public void setItems(@XmlRes int xmlRes) {
    method setItems (line 300) | public void setItems(@XmlRes int xmlRes, BottomBarTab.Config defaultTa...
    method getTabConfig (line 313) | private BottomBarTab.Config getTabConfig() {
    method updateItems (line 327) | private void updateItems(final List<BottomBarTab> bottomBarItems) {
    method resizeTabsToCorrectSizes (line 383) | private void resizeTabsToCorrectSizes(BottomBarTab[] tabsToAdd) {
    method getShySettings (line 427) | public ShySettings getShySettings() {
    method setTabSelectionInterceptor (line 445) | public void setTabSelectionInterceptor(@NonNull TabSelectionIntercepto...
    method removeOverrideTabSelectionListener (line 452) | public void removeOverrideTabSelectionListener() {
    method setOnTabSelectListener (line 464) | public void setOnTabSelectListener(@NonNull OnTabSelectListener listen...
    method setOnTabSelectListener (line 477) | public void setOnTabSelectListener(@NonNull OnTabSelectListener listen...
    method removeOnTabSelectListener (line 488) | public void removeOnTabSelectListener() {
    method setOnTabReselectListener (line 497) | public void setOnTabReselectListener(@NonNull OnTabReselectListener li...
    method removeOnTabReselectListener (line 504) | public void removeOnTabReselectListener() {
    method setDefaultTab (line 512) | public void setDefaultTab(@IdRes int defaultTabId) {
    method setDefaultTabPosition (line 523) | public void setDefaultTabPosition(int defaultTabPosition) {
    method selectTabWithId (line 532) | public void selectTabWithId(@IdRes int tabResId) {
    method selectTabAtPosition (line 542) | public void selectTabAtPosition(int position) {
    method selectTabAtPosition (line 552) | public void selectTabAtPosition(int position, boolean animate) {
    method getTabCount (line 569) | public int getTabCount() {
    method getCurrentTab (line 576) | public BottomBarTab getCurrentTab() {
    method getTabAtPosition (line 583) | public BottomBarTab getTabAtPosition(int position) {
    method getCurrentTabId (line 596) | @IdRes
    method getCurrentTabPosition (line 604) | public int getCurrentTabPosition() {
    method findPositionForTabWithId (line 611) | public int findPositionForTabWithId(@IdRes int tabId) {
    method getTabWithId (line 618) | public BottomBarTab getTabWithId(@IdRes int tabId) {
    method setLongPressHintsEnabled (line 629) | public void setLongPressHintsEnabled(boolean enabled) {
    method setInActiveTabAlpha (line 636) | public void setInActiveTabAlpha(float alpha) {
    method setActiveTabAlpha (line 650) | public void setActiveTabAlpha(float alpha) {
    method setInActiveTabColor (line 661) | public void setInActiveTabColor(@ColorInt int color) {
    method setActiveTabColor (line 675) | public void setActiveTabColor(@ColorInt int color) {
    method setBadgeBackgroundColor (line 689) | public void setBadgeBackgroundColor(@ColorInt int color) {
    method setBadgesHideWhenActive (line 704) | public void setBadgesHideWhenActive(final boolean hideWhenSelected) {
    method setTabTitleTextAppearance (line 717) | public void setTabTitleTextAppearance(int textAppearance) {
    method setTabTitleTypeface (line 736) | public void setTabTitleTypeface(String fontPath) {
    method setTabTitleTypeface (line 744) | public void setTabTitleTypeface(Typeface typeface) {
    method onLayout (line 755) | @Override
    method updateTitleBottomPadding (line 776) | private void updateTitleBottomPadding() {
    method initializeShyBehavior (line 807) | private void initializeShyBehavior() {
    method updateShyHeight (line 829) | private void updateShyHeight(int height) {
    method resizeForDrawingUnderNavbar (line 834) | private void resizeForDrawingUnderNavbar() {
    method onSaveInstanceState (line 853) | @Override
    method saveState (line 860) | @VisibleForTesting
    method onRestoreInstanceState (line 868) | @Override
    method restoreState (line 879) | @VisibleForTesting
    method onClick (line 890) | @Override
    method onLongClick (line 896) | @Override
    method findTabInLayout (line 901) | private BottomBarTab findTabInLayout(ViewGroup child) {
    method handleClick (line 913) | private void handleClick(BottomBarTab newTab) {
    method handleLongClick (line 929) | private boolean handleLongClick(BottomBarTab longClickedTab) {
    method updateSelectedTab (line 944) | private void updateSelectedTab(int newPosition) {
    method shiftingMagic (line 962) | private void shiftingMagic(BottomBarTab oldTab, BottomBarTab newTab, b...
    method handleBackgroundColorChange (line 969) | private void handleBackgroundColorChange(BottomBarTab tab, boolean ani...
    method animateBGColorChange (line 991) | private void animateBGColorChange(View clickedView, final int newColor) {
    method prepareForBackgroundColorAnimation (line 1005) | private void prepareForBackgroundColorAnimation(int newColor) {
    method backgroundCircularRevealAnimation (line 1013) | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    method backgroundCrossfadeAnimation (line 1054) | private void backgroundCrossfadeAnimation(final int newColor) {

FILE: bottom-bar/src/main/java/com/roughike/bottombar/BottomBarBadge.java
  class BottomBarBadge (line 32) | class BottomBarBadge extends TextView {
    method BottomBarBadge (line 36) | BottomBarBadge(Context context) {
    method setCount (line 45) | void setCount(int count) {
    method getCount (line 55) | int getCount() {
    method show (line 62) | void show() {
    method hide (line 75) | void hide() {
    method isVisible (line 90) | boolean isVisible() {
    method attachToTab (line 94) | void attachToTab(BottomBarTab tab, int backgroundColor) {
    method setColoredCircleBackground (line 106) | void setColoredCircleBackground(int circleColor) {
    method wrapTabAndBadgeInSameContainer (line 113) | private void wrapTabAndBadgeInSameContainer(final BottomBarTab tab) {
    method removeFromTab (line 136) | void removeFromTab(BottomBarTab tab) {
    method adjustPositionAndSize (line 145) | void adjustPositionAndSize(BottomBarTab tab) {
    method setBackgroundCompat (line 162) | @SuppressWarnings("deprecation")

FILE: bottom-bar/src/main/java/com/roughike/bottombar/BottomBarTab.java
  class BottomBarTab (line 37) | public class BottomBarTab extends LinearLayout {
    method BottomBarTab (line 72) | BottomBarTab(Context context) {
    method setConfig (line 80) | void setConfig(@NonNull Config config) {
    method prepareLayout (line 92) | void prepareLayout() {
    method getLayoutResource (line 117) | @VisibleForTesting
    method updateTitle (line 137) | private void updateTitle() {
    method updateCustomTextAppearance (line 143) | @SuppressWarnings("deprecation")
    method updateCustomTypeface (line 158) | private void updateCustomTypeface() {
    method getType (line 164) | Type getType() {
    method setType (line 168) | void setType(Type type) {
    method isTitleless (line 172) | boolean isTitleless() {
    method setIsTitleless (line 176) | void setIsTitleless(boolean isTitleless) {
    method getOuterView (line 186) | public ViewGroup getOuterView() {
    method getIconView (line 190) | AppCompatImageView getIconView() {
    method getIconResId (line 194) | int getIconResId() {
    method setIconResId (line 198) | void setIconResId(int iconResId) {
    method getTitleView (line 202) | TextView getTitleView() {
    method getTitle (line 206) | public String getTitle() {
    method setTitle (line 210) | public void setTitle(String title) {
    method getInActiveAlpha (line 215) | public float getInActiveAlpha() {
    method setInActiveAlpha (line 219) | public void setInActiveAlpha(float inActiveAlpha) {
    method getActiveAlpha (line 227) | public float getActiveAlpha() {
    method setActiveAlpha (line 231) | public void setActiveAlpha(float activeAlpha) {
    method getInActiveColor (line 239) | public int getInActiveColor() {
    method setInActiveColor (line 243) | public void setInActiveColor(int inActiveColor) {
    method getActiveColor (line 251) | public int getActiveColor() {
    method setActiveColor (line 255) | public void setActiveColor(int activeIconColor) {
    method getBarColorWhenSelected (line 263) | public int getBarColorWhenSelected() {
    method setBarColorWhenSelected (line 267) | public void setBarColorWhenSelected(int barColorWhenSelected) {
    method getBadgeBackgroundColor (line 271) | public int getBadgeBackgroundColor() {
    method setBadgeBackgroundColor (line 275) | public void setBadgeBackgroundColor(int badgeBackgroundColor) {
    method getBadgeHidesWhenActive (line 283) | public boolean getBadgeHidesWhenActive() {
    method setBadgeHidesWhenActive (line 287) | public void setBadgeHidesWhenActive(boolean hideWhenActive) {
    method getCurrentDisplayedIconColor (line 291) | int getCurrentDisplayedIconColor() {
    method getCurrentDisplayedTitleColor (line 301) | int getCurrentDisplayedTitleColor() {
    method getCurrentDisplayedTextAppearance (line 309) | int getCurrentDisplayedTextAppearance() {
    method setBadgeCount (line 319) | public void setBadgeCount(int count) {
    method removeBadge (line 341) | public void removeBadge() {
    method isActive (line 345) | boolean isActive() {
    method hasActiveBadge (line 349) | boolean hasActiveBadge() {
    method getIndexInTabContainer (line 353) | int getIndexInTabContainer() {
    method setIndexInContainer (line 357) | void setIndexInContainer(int indexInContainer) {
    method setIconTint (line 361) | void setIconTint(int tint) {
    method getTitleTextAppearance (line 365) | public int getTitleTextAppearance() {
    method setTitleTextAppearance (line 369) | @SuppressWarnings("deprecation")
    method setTitleTypeface (line 375) | public void setTitleTypeface(Typeface typeface) {
    method getTitleTypeFace (line 380) | public Typeface getTitleTypeFace() {
    method select (line 384) | void select(boolean animate) {
    method deselect (line 406) | void deselect(boolean animate) {
    method animateColors (line 433) | private void animateColors(int previousColor, int color) {
    method setColors (line 448) | private void setColors(int color) {
    method setAlphas (line 459) | private void setAlphas(float alpha) {
    method updateWidth (line 469) | void updateWidth(float endWidth, boolean animated) {
    method updateBadgePosition (line 510) | private void updateBadgePosition() {
    method setTopPaddingAnimated (line 516) | private void setTopPaddingAnimated(int start, int end) {
    method animateTitle (line 538) | private void animateTitle(int padding, float scale, float alpha) {
    method animateIconScale (line 553) | private void animateIconScale(float scale) {
    method animateIcon (line 561) | private void animateIcon(float alpha, float scale) {
    method setTopPadding (line 572) | private void setTopPadding(int topPadding) {
    method setTitleScale (line 585) | private void setTitleScale(float scale) {
    method setIconScale (line 594) | private void setIconScale(float scale) {
    method onSaveInstanceState (line 601) | @Override
    method saveState (line 613) | @VisibleForTesting
    method onRestoreInstanceState (line 621) | @Override
    method restoreState (line 633) | @VisibleForTesting
    type Type (line 639) | enum Type {
    class Config (line 643) | public static class Config {
      method Config (line 654) | private Config(Builder builder) {
      class Builder (line 666) | public static class Builder {
        method inActiveTabAlpha (line 677) | public Builder inActiveTabAlpha(float alpha) {
        method activeTabAlpha (line 682) | public Builder activeTabAlpha(float alpha) {
        method inActiveTabColor (line 687) | public Builder inActiveTabColor(@ColorInt int color) {
        method activeTabColor (line 692) | public Builder activeTabColor(@ColorInt int color) {
        method barColorWhenSelected (line 697) | public Builder barColorWhenSelected(@ColorInt int color) {
        method badgeBackgroundColor (line 702) | public Builder badgeBackgroundColor(@ColorInt int color) {
        method hideBadgeWhenSelected (line 707) | public Builder hideBadgeWhenSelected(boolean hide) {
        method titleTextAppearance (line 712) | public Builder titleTextAppearance(int titleTextAppearance) {
        method titleTypeFace (line 717) | public Builder titleTypeFace(Typeface titleTypeFace) {
        method build (line 722) | public Config build() {

FILE: bottom-bar/src/main/java/com/roughike/bottombar/BottomNavigationBehavior.java
  class BottomNavigationBehavior (line 20) | class BottomNavigationBehavior<V extends View> extends VerticalScrolling...
    method BottomNavigationBehavior (line 32) | BottomNavigationBehavior(int bottomNavHeight, int defaultOffset, boole...
    method layoutDependsOn (line 38) | @Override
    method onNestedVerticalOverScroll (line 44) | @Override
    method onDependentViewRemoved (line 48) | @Override
    method updateScrollingForSnackbar (line 54) | private void updateScrollingForSnackbar(View dependency, boolean enabl...
    method onDependentViewChanged (line 60) | @Override
    method onDirectionNestedPreScroll (line 66) | @Override
    method handleDirection (line 71) | private void handleDirection(V child, int scrollDirection) {
    method onNestedDirectionFling (line 82) | @Override
    method animateOffset (line 88) | private void animateOffset(final V child, final int offset) {
    method ensureOrCancelAnimator (line 93) | private void ensureOrCancelAnimator(V child) {
    method setHidden (line 104) | void setHidden(@NonNull  V view, boolean bottomLayoutHidden) {
    method from (line 114) | static <V extends View> BottomNavigationBehavior<V> from(@NonNull V vi...
    type BottomNavigationWithSnackbar (line 132) | private interface BottomNavigationWithSnackbar {
      method updateSnackbar (line 133) | void updateSnackbar(CoordinatorLayout parent, View dependency, View ...
    class PreLollipopBottomNavWithSnackBarImpl (line 137) | private class PreLollipopBottomNavWithSnackBarImpl implements BottomNa...
      method updateSnackbar (line 139) | @Override
    class LollipopBottomNavWithSnackBarImpl (line 160) | private class LollipopBottomNavWithSnackBarImpl implements BottomNavig...
      method updateSnackbar (line 161) | @Override

FILE: bottom-bar/src/main/java/com/roughike/bottombar/MiscUtils.java
  class MiscUtils (line 36) | class MiscUtils {
    method getTypedValue (line 38) | @NonNull protected static TypedValue getTypedValue(@NonNull Context co...
    method getColor (line 44) | @ColorInt
    method getDrawableRes (line 49) | @DrawableRes
    method dpToPixel (line 61) | protected static int dpToPixel(@NonNull Context context, @Dimension(un...
    method pixelToDp (line 79) | protected static int pixelToDp(@NonNull Context context, @Px int px) {
    method getScreenWidth (line 90) | protected static int getScreenWidth(@NonNull Context context) {
    method setTextAppearance (line 101) | @SuppressWarnings("deprecation")
    method isNightMode (line 116) | protected static boolean isNightMode(@NonNull Context context) {

FILE: bottom-bar/src/main/java/com/roughike/bottombar/NavbarUtils.java
  class NavbarUtils (line 18) | final class NavbarUtils {
    method getNavbarHeight (line 21) | @IntRange(from = 0)
    method shouldDrawBehindNavbar (line 29) | static boolean shouldDrawBehindNavbar(@NonNull Context context) {
    method isPortrait (line 34) | private static boolean isPortrait(@NonNull Context context) {
    method hasSoftKeys (line 42) | private static boolean hasSoftKeys(@NonNull Context context) {

FILE: bottom-bar/src/main/java/com/roughike/bottombar/OnTabReselectListener.java
  type OnTabReselectListener (line 21) | public interface OnTabReselectListener {
    method onTabReSelected (line 29) | void onTabReSelected(@IdRes int tabId);

FILE: bottom-bar/src/main/java/com/roughike/bottombar/OnTabSelectListener.java
  type OnTabSelectListener (line 21) | public interface OnTabSelectListener {
    method onTabSelected (line 31) | void onTabSelected(@IdRes int tabId);

FILE: bottom-bar/src/main/java/com/roughike/bottombar/ShySettings.java
  class ShySettings (line 6) | public class ShySettings {
    method ShySettings (line 10) | ShySettings(BottomBar bottomBar) {
    method shyHeightCalculated (line 14) | void shyHeightCalculated() {
    method showBar (line 21) | public void showBar() {
    method hideBar (line 28) | public void hideBar() {
    method toggleIsVisibleInShyMode (line 32) | private void toggleIsVisibleInShyMode(boolean visible) {
    method updatePendingShyVisibility (line 49) | private void updatePendingShyVisibility() {

FILE: bottom-bar/src/main/java/com/roughike/bottombar/TabParser.java
  class TabParser (line 51) | class TabParser {
    method TabParser (line 69) | TabParser(@NonNull Context context, @NonNull BottomBarTab.Config defau...
    method parseTabs (line 75) | @CheckResult
    method parseNewTab (line 98) | @NonNull
    method tabWithDefaults (line 151) | @NonNull
    method getTitleValue (line 159) | @NonNull
    method getColorValue (line 166) | @ColorInt
    class TabParserException (line 206) | @SuppressWarnings("WeakerAccess")

FILE: bottom-bar/src/main/java/com/roughike/bottombar/TabSelectionInterceptor.java
  type TabSelectionInterceptor (line 21) | public interface TabSelectionInterceptor {
    method shouldInterceptTabSelection (line 32) | boolean shouldInterceptTabSelection(@IdRes int oldTabId, @IdRes int ne...

FILE: bottom-bar/src/main/java/com/roughike/bottombar/VerticalScrollingBehavior.java
  class VerticalScrollingBehavior (line 21) | abstract class VerticalScrollingBehavior<V extends View> extends Coordin...
    method VerticalScrollingBehavior (line 30) | VerticalScrollingBehavior(Context context, AttributeSet attrs) {
    method VerticalScrollingBehavior (line 34) | VerticalScrollingBehavior() {
    method getOverScrollDirection (line 50) | @ScrollDirection
    method getScrollDirection (line 60) | @ScrollDirection
    method onNestedVerticalOverScroll (line 73) | abstract void onNestedVerticalOverScroll(CoordinatorLayout coordinator...
    method onDirectionNestedPreScroll (line 78) | abstract void onDirectionNestedPreScroll(CoordinatorLayout coordinator...
    method onStartNestedScroll (line 80) | @Override
    method onNestedScrollAccepted (line 85) | @Override
    method onStopNestedScroll (line 90) | @Override
    method onNestedScroll (line 95) | @Override
    method onNestedPreScroll (line 109) | @Override
    method onNestedFling (line 124) | @Override
    method onNestedDirectionFling (line 131) | abstract boolean onNestedDirectionFling(CoordinatorLayout coordinatorL...
    method onNestedPreFling (line 133) | @Override
    method onApplyWindowInsets (line 138) | @NonNull
    method onSaveInstanceState (line 144) | @Override

FILE: bottom-bar/src/test/java/com/roughike/bottombar/ExampleUnitTest.java
  class ExampleUnitTest (line 10) | public class ExampleUnitTest {
    method addition_isCorrect (line 11) | @Test
Condensed preview — 105 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (623K chars).
[
  {
    "path": ".gitignore",
    "chars": 481,
    "preview": "# Built application files\n*.apk\n*.ap_\n\n# Files for the ART/Dalvik VM\n*.dex\n\n# Java class files\n*.class\n\n# Generated file"
  },
  {
    "path": ".idea/.name",
    "chars": 9,
    "preview": "BottomBar"
  },
  {
    "path": ".idea/compiler.xml",
    "chars": 686,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"CompilerConfiguration\">\n    <resourceExt"
  },
  {
    "path": ".idea/copyright/profiles_settings.xml",
    "chars": 74,
    "preview": "<component name=\"CopyrightManager\">\n  <settings default=\"\" />\n</component>"
  },
  {
    "path": ".idea/encodings.xml",
    "chars": 159,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"Encoding\">\n    <file url=\"PROJECT\" chars"
  },
  {
    "path": ".idea/gradle.xml",
    "chars": 682,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"GradleSettings\">\n    <option name=\"linke"
  },
  {
    "path": ".idea/kotlinc.xml",
    "chars": 232,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"KotlinCommonCompilerArguments\">\n    <opt"
  },
  {
    "path": ".idea/markdown-exported-files.xml",
    "chars": 185,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"MarkdownExportedFiles\">\n    <htmlFiles /"
  },
  {
    "path": ".idea/markdown-navigator/profiles_settings.xml",
    "chars": 90,
    "preview": "<component name=\"MarkdownNavigator.ProfileManager\">\n  <settings default=\"\" />\n</component>"
  },
  {
    "path": ".idea/markdown-navigator.xml",
    "chars": 3766,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"MarkdownProjectSettings\">\n    <PreviewSe"
  },
  {
    "path": ".idea/misc.xml",
    "chars": 2226,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"EntryPointsManager\">\n    <entry_points v"
  },
  {
    "path": ".idea/modules.xml",
    "chars": 480,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"ProjectModuleManager\">\n    <modules>\n   "
  },
  {
    "path": ".idea/runConfigurations/All_Tests.xml",
    "chars": 2221,
    "preview": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"All Tests\" type=\"AndroidTestRun"
  },
  {
    "path": ".idea/runConfigurations.xml",
    "chars": 564,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"RunConfigurationProducerService\">\n    <o"
  },
  {
    "path": ".idea/vcs.xml",
    "chars": 180,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"VcsDirectoryMappings\">\n    <mapping dire"
  },
  {
    "path": ".travis.yml",
    "chars": 428,
    "preview": "language: android\n\njdk: oraclejdk8\n\nandroid:\n  components:\n    - tools\n    - platform-tools\n    - build-tools-25.0.2\n   "
  },
  {
    "path": "CHANGELOG.md",
    "chars": 8614,
    "preview": "## Changelog\n\n### 2.3.1\n\n* [#749](https://github.com/roughike/BottomBar/pull/749): Quick fix for the issue where *bb_sho"
  },
  {
    "path": "LICENSE",
    "chars": 10172,
    "preview": "                                 Apache License\n                           Version 2.0, January 2004\n                   "
  },
  {
    "path": "NOTICE",
    "chars": 146,
    "preview": "BottomBar library for Android\nCopyright 2016 Iiro Krankka\n\nThis product includes software developed by\nIiro Krankka (htt"
  },
  {
    "path": "README.md",
    "chars": 16337,
    "preview": "# BottomBar (Deprecated)\n\nI don't have time to maintain this anymore. I basically wrote the whole library in a rush, wit"
  },
  {
    "path": "app/build.gradle",
    "chars": 1339,
    "preview": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion rootProject.ext.compileSdkVersion\n    buildTool"
  },
  {
    "path": "app/proguard-rules.pro",
    "chars": 670,
    "preview": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in C:"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "chars": 1159,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package="
  },
  {
    "path": "app/src/main/assets/fonts/GREAT-VIBES-LICENSE",
    "chars": 4459,
    "preview": "SIL Open Font License v1.10\n\nThis license can also be found at this permalink: https://www.fontsquirrel.com/license/grea"
  },
  {
    "path": "app/src/main/java/com/example/bottombar/sample/BadgeActivity.java",
    "chars": 1554,
    "preview": "package com.example.bottombar.sample;\n\nimport android.os.Bundle;\nimport android.support.annotation.IdRes;\nimport android"
  },
  {
    "path": "app/src/main/java/com/example/bottombar/sample/CustomColorAndFontActivity.java",
    "chars": 1400,
    "preview": "package com.example.bottombar.sample;\n\nimport android.os.Bundle;\nimport android.support.annotation.IdRes;\nimport android"
  },
  {
    "path": "app/src/main/java/com/example/bottombar/sample/FiveColorChangingTabsActivity.java",
    "chars": 1423,
    "preview": "package com.example.bottombar.sample;\n\nimport android.os.Bundle;\nimport android.support.annotation.IdRes;\nimport android"
  },
  {
    "path": "app/src/main/java/com/example/bottombar/sample/IconsOnlyActivity.java",
    "chars": 1333,
    "preview": "package com.example.bottombar.sample;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.support.ann"
  },
  {
    "path": "app/src/main/java/com/example/bottombar/sample/MainActivity.java",
    "chars": 1773,
    "preview": "package com.example.bottombar.sample;\n\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.support.v"
  },
  {
    "path": "app/src/main/java/com/example/bottombar/sample/SampleFragment.java",
    "chars": 1705,
    "preview": "/*\n * BottomBar library for Android\n * Copyright (c) 2016 Iiro Krankka (http://github.com/roughike).\n *\n * Licensed unde"
  },
  {
    "path": "app/src/main/java/com/example/bottombar/sample/TabMessage.java",
    "chars": 854,
    "preview": "package com.example.bottombar.sample;\n\n/**\n * Created by iiro on 7.6.2016.\n */\npublic class TabMessage {\n    public stat"
  },
  {
    "path": "app/src/main/java/com/example/bottombar/sample/ThreeTabsActivity.java",
    "chars": 1373,
    "preview": "package com.example.bottombar.sample;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.support.ann"
  },
  {
    "path": "app/src/main/java/com/example/bottombar/sample/ThreeTabsQRActivity.java",
    "chars": 983,
    "preview": "package com.example.bottombar.sample;\n\nimport android.os.Bundle;\nimport android.support.annotation.IdRes;\nimport android"
  },
  {
    "path": "app/src/main/res/layout/activity_color_changing_tabs.xml",
    "chars": 846,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    an"
  },
  {
    "path": "app/src/main/res/layout/activity_custom_color_and_font.xml",
    "chars": 1132,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    an"
  },
  {
    "path": "app/src/main/res/layout/activity_icons_only.xml",
    "chars": 825,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    an"
  },
  {
    "path": "app/src/main/res/layout/activity_main.xml",
    "chars": 1790,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmln"
  },
  {
    "path": "app/src/main/res/layout/activity_three_tabs.xml",
    "chars": 790,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    an"
  },
  {
    "path": "app/src/main/res/layout/activity_three_tabs_quick_return.xml",
    "chars": 1147,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<android.support.design.widget.CoordinatorLayout xmlns:android=\"http://schemas.an"
  },
  {
    "path": "app/src/main/res/layout-sw600dp/activity_color_changing_tabs.xml",
    "chars": 1095,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n  "
  },
  {
    "path": "app/src/main/res/layout-sw600dp/activity_three_tabs.xml",
    "chars": 932,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n  "
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "chars": 208,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorPrimary\">#3F51B5</color>\n    <color name=\"color"
  },
  {
    "path": "app/src/main/res/values/dimens.xml",
    "chars": 304,
    "preview": "<resources>\n    <!-- Default screen margins, per the Android Design guidelines. -->\n    <dimen name=\"activity_horizontal"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "chars": 4053,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"lorem_ipsum\">Lorem ipsum dolor sit amet, consectetu"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "chars": 601,
    "preview": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar"
  },
  {
    "path": "app/src/main/res/values-land-v21/styles.xml",
    "chars": 120,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <style name=\"AppTheme.TransNav\" parent=\"AppTheme\" />\n</resources>"
  },
  {
    "path": "app/src/main/res/values-sw600dp/styles.xml",
    "chars": 120,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <style name=\"AppTheme.TransNav\" parent=\"AppTheme\" />\n</resources>"
  },
  {
    "path": "app/src/main/res/values-v21/styles.xml",
    "chars": 619,
    "preview": "<resources>\n    <style name=\"AppTheme.NoActionBar\">\n        <item name=\"windowActionBar\">false</item>\n        <item name"
  },
  {
    "path": "app/src/main/res/values-w820dp/dimens.xml",
    "chars": 358,
    "preview": "<resources>\n    <!-- Example customization of dimensions originally defined in res/values/dimens.xml\n         (such as s"
  },
  {
    "path": "app/src/main/res/xml/bottombar_tabs_color_changing.xml",
    "chars": 770,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<tabs>\n    <tab\n        id=\"@+id/tab_recents\"\n        icon=\"@drawable/ic_recents\""
  },
  {
    "path": "app/src/main/res/xml/bottombar_tabs_five.xml",
    "chars": 564,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<tabs>\n    <tab\n        id=\"@+id/tab_recents\"\n        icon=\"@drawable/ic_recents\""
  },
  {
    "path": "app/src/main/res/xml/bottombar_tabs_three.xml",
    "chars": 362,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<tabs>\n    <tab\n        id=\"@+id/tab_favorites\"\n        icon=\"@drawable/ic_favori"
  },
  {
    "path": "bottom-bar/build.gradle",
    "chars": 5023,
    "preview": "apply plugin: 'com.android.library'\napply plugin: 'com.github.kt3k.coveralls'\n\next {\n    bintrayRepo = 'maven'\n    bintr"
  },
  {
    "path": "bottom-bar/src/androidTest/assets/fonts/GREAT-VIBES-LICENSE",
    "chars": 4459,
    "preview": "SIL Open Font License v1.10\n\nThis license can also be found at this permalink: https://www.fontsquirrel.com/license/grea"
  },
  {
    "path": "bottom-bar/src/androidTest/java/com/roughike/bottombar/BadgeTest.java",
    "chars": 3127,
    "preview": "package com.roughike.bottombar;\n\nimport android.os.Bundle;\nimport android.support.test.InstrumentationRegistry;\nimport a"
  },
  {
    "path": "bottom-bar/src/androidTest/java/com/roughike/bottombar/BottomBarTabTest.java",
    "chars": 2055,
    "preview": "package com.roughike.bottombar;\n\nimport android.os.Bundle;\nimport android.support.test.InstrumentationRegistry;\nimport a"
  },
  {
    "path": "bottom-bar/src/androidTest/java/com/roughike/bottombar/BottomBarTest.java",
    "chars": 26257,
    "preview": "package com.roughike.bottombar;\n\nimport android.content.Context;\nimport android.graphics.Color;\nimport android.graphics."
  },
  {
    "path": "bottom-bar/src/androidTest/java/com/roughike/bottombar/TabParserTest.java",
    "chars": 5584,
    "preview": "package com.roughike.bottombar;\n\nimport android.content.Context;\nimport android.graphics.Color;\nimport android.graphics."
  },
  {
    "path": "bottom-bar/src/androidTest/res/values/colors.xml",
    "chars": 171,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"test_random_color\">#FAB123</color>\n    <color name=\""
  },
  {
    "path": "bottom-bar/src/androidTest/res/values/strings.xml",
    "chars": 117,
    "preview": "<resources>\n    <string name=\"favorites\">Favorites</string>\n    <string name=\"friends\">Friends</string>\n</resources>\n"
  },
  {
    "path": "bottom-bar/src/androidTest/res/values/styles.xml",
    "chars": 106,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <style name=\"dummy_text_appearance\" />\n</resources>"
  },
  {
    "path": "bottom-bar/src/androidTest/res/xml/dummy_tabs_five.xml",
    "chars": 1151,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<tabs>\n    <tab inActiveColor=\"#00FF00\" activeColor=\"#FF0000\" id=\"@+id/tab_recent"
  },
  {
    "path": "bottom-bar/src/androidTest/res/xml/dummy_tabs_three.xml",
    "chars": 361,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<tabs>\n    <tab\n        id=\"@+id/tab_favorites\"\n        icon=\"@drawable/empty_ico"
  },
  {
    "path": "bottom-bar/src/main/AndroidManifest.xml",
    "chars": 46,
    "preview": "<manifest package=\"com.roughike.bottombar\" />\n"
  },
  {
    "path": "bottom-bar/src/main/java/com/roughike/bottombar/BadgeCircle.java",
    "chars": 2167,
    "preview": "/*\n * BottomBar library for Android\n * Copyright (c) 2016 Iiro Krankka (http://github.com/roughike).\n *\n * Licensed unde"
  },
  {
    "path": "bottom-bar/src/main/java/com/roughike/bottombar/BadgeContainer.java",
    "chars": 321,
    "preview": "package com.roughike.bottombar;\n\nimport android.content.Context;\nimport android.support.annotation.NonNull;\nimport andro"
  },
  {
    "path": "bottom-bar/src/main/java/com/roughike/bottombar/BatchTabPropertyApplier.java",
    "chars": 673,
    "preview": "package com.roughike.bottombar;\n\nimport android.support.annotation.NonNull;\n\nclass BatchTabPropertyApplier {\n    private"
  },
  {
    "path": "bottom-bar/src/main/java/com/roughike/bottombar/BottomBar.java",
    "chars": 36449,
    "preview": "package com.roughike.bottombar;\n\nimport android.animation.Animator;\nimport android.animation.AnimatorListenerAdapter;\nim"
  },
  {
    "path": "bottom-bar/src/main/java/com/roughike/bottombar/BottomBarBadge.java",
    "chars": 5521,
    "preview": "package com.roughike.bottombar;\n\nimport android.content.Context;\nimport android.graphics.drawable.Drawable;\nimport andro"
  },
  {
    "path": "bottom-bar/src/main/java/com/roughike/bottombar/BottomBarTab.java",
    "chars": 21413,
    "preview": "package com.roughike.bottombar;\n\nimport android.animation.ArgbEvaluator;\nimport android.animation.ValueAnimator;\nimport "
  },
  {
    "path": "bottom-bar/src/main/java/com/roughike/bottombar/BottomNavigationBehavior.java",
    "chars": 7108,
    "preview": "package com.roughike.bottombar;\n\nimport android.os.Build;\nimport android.support.annotation.NonNull;\nimport android.supp"
  },
  {
    "path": "bottom-bar/src/main/java/com/roughike/bottombar/MiscUtils.java",
    "chars": 4354,
    "preview": "package com.roughike.bottombar;\n\nimport android.content.Context;\nimport android.content.res.Configuration;\nimport androi"
  },
  {
    "path": "bottom-bar/src/main/java/com/roughike/bottombar/NavbarUtils.java",
    "chars": 2536,
    "preview": "package com.roughike.bottombar;\n\nimport android.content.Context;\nimport android.content.res.Resources;\nimport android.os"
  },
  {
    "path": "bottom-bar/src/main/java/com/roughike/bottombar/OnTabReselectListener.java",
    "chars": 1115,
    "preview": "package com.roughike.bottombar;\n\nimport android.support.annotation.IdRes;\n\n/*\n * BottomBar library for Android\n * Copyri"
  },
  {
    "path": "bottom-bar/src/main/java/com/roughike/bottombar/OnTabSelectListener.java",
    "chars": 1188,
    "preview": "package com.roughike.bottombar;\n\nimport android.support.annotation.IdRes;\n\n/*\n * BottomBar library for Android\n * Copyri"
  },
  {
    "path": "bottom-bar/src/main/java/com/roughike/bottombar/ShySettings.java",
    "chars": 1426,
    "preview": "package com.roughike.bottombar;\n\n/**\n * Settings specific for a shy BottomBar.\n */\npublic class ShySettings {\n    privat"
  },
  {
    "path": "bottom-bar/src/main/java/com/roughike/bottombar/TabParser.java",
    "chars": 8213,
    "preview": "package com.roughike.bottombar;\n\nimport android.content.Context;\nimport android.content.res.XmlResourceParser;\nimport an"
  },
  {
    "path": "bottom-bar/src/main/java/com/roughike/bottombar/TabSelectionInterceptor.java",
    "chars": 1387,
    "preview": "package com.roughike.bottombar;\n\nimport android.support.annotation.IdRes;\n\n/*\n * BottomBar library for Android\n * Copyri"
  },
  {
    "path": "bottom-bar/src/main/java/com/roughike/bottombar/VerticalScrollingBehavior.java",
    "chars": 6016,
    "preview": "package com.roughike.bottombar;\n\nimport android.content.Context;\nimport android.os.Parcelable;\nimport android.support.an"
  },
  {
    "path": "bottom-bar/src/main/res/drawable/bb_bottom_bar_top_shadow.xml",
    "chars": 264,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <gradient\n"
  },
  {
    "path": "bottom-bar/src/main/res/layout/bb_bottom_bar_item_container.xml",
    "chars": 1045,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<merge xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <View\n   "
  },
  {
    "path": "bottom-bar/src/main/res/layout/bb_bottom_bar_item_container_tablet.xml",
    "chars": 1135,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<merge xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <FrameLay"
  },
  {
    "path": "bottom-bar/src/main/res/layout/bb_bottom_bar_item_fixed.xml",
    "chars": 731,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<merge xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <android."
  },
  {
    "path": "bottom-bar/src/main/res/layout/bb_bottom_bar_item_fixed_tablet.xml",
    "chars": 480,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    andro"
  },
  {
    "path": "bottom-bar/src/main/res/layout/bb_bottom_bar_item_shifting.xml",
    "chars": 929,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<merge xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <android."
  },
  {
    "path": "bottom-bar/src/main/res/layout/bb_bottom_bar_item_titleless.xml",
    "chars": 343,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<merge xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <android."
  },
  {
    "path": "bottom-bar/src/main/res/layout-v21/bb_bottom_bar_item_container.xml",
    "chars": 784,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<merge xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <FrameLay"
  },
  {
    "path": "bottom-bar/src/main/res/values/attrs.xml",
    "chars": 1086,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <declare-styleable name=\"BottomBar\">\n        <attr name=\"bb_tabXm"
  },
  {
    "path": "bottom-bar/src/main/res/values/bools.xml",
    "chars": 182,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <bool name=\"bb_bottom_bar_is_tablet_mode\">false</bool>\n    <bool "
  },
  {
    "path": "bottom-bar/src/main/res/values/colors.xml",
    "chars": 243,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"bb_inActiveBottomBarItemColor\">#747474</color>\n    <"
  },
  {
    "path": "bottom-bar/src/main/res/values/dimensions.xml",
    "chars": 207,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <dimen name=\"bb_height\">56dp</dimen>\n    <dimen name=\"bb_default_"
  },
  {
    "path": "bottom-bar/src/main/res/values/ids.xml",
    "chars": 172,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <item name=\"bb_bottom_bar_color_id\" type=\"id\"/>\n    <item name=\"b"
  },
  {
    "path": "bottom-bar/src/main/res/values/strings.xml",
    "chars": 72,
    "preview": "<resources>\n    <string name=\"app_name\">BottomBar</string>\n</resources>\n"
  },
  {
    "path": "bottom-bar/src/main/res/values/styles.xml",
    "chars": 1439,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <style name=\"BB_BottomBarItem\">\n        <item name=\"android:backg"
  },
  {
    "path": "bottom-bar/src/main/res/values-land/bools.xml",
    "chars": 124,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <bool name=\"bb_bottom_bar_is_portrait_mode\">false</bool>\n</resour"
  },
  {
    "path": "bottom-bar/src/test/java/com/roughike/bottombar/ExampleUnitTest.java",
    "chars": 315,
    "preview": "package com.roughike.bottombar;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * To work on unit tests"
  },
  {
    "path": "build.gradle",
    "chars": 899,
    "preview": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    r"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "chars": 230,
    "preview": "#Sat Mar 25 00:47:49 IST 2017\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_"
  },
  {
    "path": "gradle.properties",
    "chars": 855,
    "preview": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will o"
  },
  {
    "path": "gradlew",
    "chars": 4971,
    "preview": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start "
  },
  {
    "path": "projectFilesBackup/.idea/workspace.xml",
    "chars": 341003,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"AndroidLayouts\">\n    <shared>\n      <con"
  },
  {
    "path": "settings.gradle",
    "chars": 30,
    "preview": "include ':app', ':bottom-bar'\n"
  }
]

// ... and 3 more files (download for full content)

About this extraction

This page contains the full source code of the roughike/BottomBar GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 105 files (576.4 KB), approximately 147.6k tokens, and a symbol index with 339 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!