├── .gitignore
├── .travis.yml
├── ISSUE_TEMPLATE.MD
├── LICENSE
├── README.MD
├── art
├── faboptions_demo.gif
├── faboptions_materialup_demo.gif
└── google-play-badge.png
├── build.gradle
├── faboptions
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── joaquimley
│ │ └── faboptions
│ │ ├── FabOptions.java
│ │ └── FabOptionsAnimationStateListener.java
│ └── res
│ ├── anim
│ └── faboptions_open_morph_animation.xml
│ ├── animator
│ ├── faboptions_close_to_overflow.xml
│ └── faboptions_overflow_to_close.xml
│ └── drawable
│ ├── faboptions_ic_close.xml
│ └── faboptions_ic_overflow.xml
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── library
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── joaquimley
│ │ └── faboptions
│ │ ├── FabOptions.java
│ │ ├── FabOptionsAnimationStateListener.java
│ │ ├── FabOptionsBehavior.java
│ │ └── FabOptionsButtonContainer.java
│ └── res
│ ├── anim
│ └── faboptions_open_morph_animation.xml
│ ├── animator
│ ├── faboptions_close_to_overflow.xml
│ └── faboptions_overflow_to_close.xml
│ ├── drawable
│ ├── faboptions_background.xml
│ ├── faboptions_ic_close.xml
│ ├── faboptions_ic_close_animatable.xml
│ ├── faboptions_ic_menu_animatable.xml
│ └── faboptions_ic_overflow.xml
│ ├── layout
│ ├── faboptions_button.xml
│ ├── faboptions_layout.xml
│ └── faboptions_separator.xml
│ └── values
│ ├── attrs.xml
│ ├── dimens.xml
│ └── strings.xml
├── sample
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── joaquimley
│ │ └── sample
│ │ ├── JavaSampleActivity.java
│ │ └── XmlSampleActivity.java
│ └── res
│ ├── drawable
│ ├── ic_favorite.xml
│ ├── ic_file_download.xml
│ ├── ic_share.xml
│ └── ic_textsms.xml
│ ├── layout
│ ├── activity_sample_java.xml
│ └── activity_sample_xml.xml
│ ├── menu
│ ├── menu_activity_main.xml
│ └── menu_faboptions.xml
│ ├── mipmap-hdpi
│ └── ic_launcher.png
│ ├── mipmap-mdpi
│ └── ic_launcher.png
│ ├── mipmap-xhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxxhdpi
│ └── ic_launcher.png
│ ├── values-v21
│ └── styles.xml
│ ├── values-w820dp
│ └── dimens.xml
│ └── values
│ ├── colors.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | # Local files
2 | /local.properties
3 | .DS_Store
4 | /build
5 | /captures
6 |
7 | # Built application files
8 | *.apk
9 | *.ap_
10 |
11 | # Files for the Dalvik VM
12 | *.dex
13 |
14 | # Java class files
15 | *.class
16 |
17 | # Generated files
18 | bin/
19 | gen/
20 |
21 | # Gradle files
22 | .gradle/
23 | ./gradlew.bat
24 | ./gradlew
25 | build/
26 |
27 | # Mirror files
28 | mirror/
29 |
30 | # Local configuration file (sdk path, etc)
31 | local.properties
32 | deploy.properties
33 | gradle.properties
34 |
35 | # Proguard folder generated by Eclipse
36 | proguard/
37 |
38 | # Intellij project files
39 | *.iws
40 | .idea/
41 | *.iml
42 |
43 | # OS
44 | .DS_Store
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: android
2 |
3 | jdk:
4 | # Jdk version used by your project
5 | - oraclejdk8
6 |
7 | sudo: false
8 |
9 | android:
10 | components:
11 | - platform-tools
12 | - tools
13 | # The BuildTools version used by your project
14 | - build-tools-24.0.3
15 | - android-25
16 | - extra-android-m2repository
17 | - extra-google-m2repository
18 | - extra-android-support
19 | - extra-google-google_play_services
20 | # Specify at least one system image, if you need to run emulator(s) during your tests
21 | - sys-img-armeabi-v7a-android-25
22 | - sys-img-x86-android-25
23 |
24 | licenses:
25 | - android-sdk-preview-license-.+
26 | - android-sdk-license-.+
27 | - google-gdk-license-.+
28 |
29 | notifications:
30 | email: false
31 |
32 | before_cache:
33 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
34 |
35 | # cache between builds
36 | cache:
37 | directories:
38 | - $HOME/.m2
39 | - $HOME/.gradle/caches/
40 | - $HOME/.gradle/wrapper/
41 |
42 | before_install:
43 | - chmod +x gradlew
44 |
45 | install:
46 | # Check install section: http://docs.travis-ci.com/user/build-configuration/#install
47 | # If you'd like to skip the install stage entirely, set it to true and nothing will be run.
48 | - true
49 |
50 | before_script:
51 | #- chmod +x gradlew
52 | - echo no | android create avd --force -n test -t android-25 --abi armeabi-v7a
53 | - emulator -avd test -no-skin -no-audio -no-window -no-boot-anim &
54 | - android-wait-for-emulator
55 | - adb shell input keyevent 82 &
56 |
57 | # Build, and run tests
58 | script:
59 | # Once tests written should run the gradle task for both Mobile and Wear
60 | # currently only doing a normal build to test Travis config
61 | #- ./gradlew mobile:functionalTests
62 | #- ./gradlew wear:functionalTests
63 | #- ./gradlew build core:connectedCheck
64 | - ./gradlew core:test --stacktrace
65 | #- ./gradlew core:jacocoTestReport --stacktrace
66 |
67 | # Codecov
68 | after_success:
69 | - bash <(curl -s https://codecov.io/bash)
--------------------------------------------------------------------------------
/ISSUE_TEMPLATE.MD:
--------------------------------------------------------------------------------
1 | [provide general introduction to the issue logging and why it is relevant to this repository]
2 |
3 | ## Context
4 |
5 | [provide more detailed introduction to the issue itself and why it is relevant]
6 |
7 | ## Process
8 |
9 | [ordered list the process to finding and recreating the issue, example below]
10 |
11 | 1. User goes to delete a dataset (to save space or whatever)
12 | 2. User gets popup modal warning
13 | 3. User deletes and it's lost forever
14 |
15 | ## Expected result
16 |
17 | [describe what you would expect to have resulted from this process]
18 |
19 | ## Current result
20 |
21 | [describe what you you currently experience from this process, and thereby explain the bug]
22 |
23 | ## Possible Fix
24 |
25 | [not obligatory, but suggest fixes or reasons for the bug]
26 |
27 | * Modal tells the user what dataset is being deleted, like “You are about to delete this dataset: car_crashes_2014.”
28 | * A temporary "Trashcan" where you can recover a just deleted dataset if you mess up (maybe it's only good for a few hours, and then it cleans the cache assuming you made the right decision).
29 |
30 | ## `name of issue` screenshot
31 |
32 | [if relevant, include a screenshot]
33 |
34 |
35 | ### Thanks!
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright 2014 The Android Open Source Project
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
--------------------------------------------------------------------------------
/README.MD:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # FabOptions
4 | [](http://android-arsenal.com/details/1/4734)
5 | [](https://material.uplabs.com/posts/faboptions)
6 | [](https://bintray.com/leyopensource/FabOptions/com.github.joaquimley%3Afaboptions/1.1.2)
7 | 
8 | 
9 |
10 | **Special thanks to [André Mion](https://github.com/andremion)** for the help provided on building this component.
11 | Original concept by **Praveen Bisht** posted on [MaterialUp](https://www.uplabs.com/posts/options-floating-interaction), turned into code into open source library.
12 |
13 |
14 | 
15 |
16 | Android implementation
17 |
18 | 
19 |
20 |
21 | ## How to use
22 | - Import gradle dependency:
23 |
24 | dependencies {
25 | compile 'com.github.joaquimley:faboptions:1.2.0'
26 | }
27 |
28 | - Add the component to your layout:
29 |
30 | ```xml
31 |
36 | ```
37 |
38 | - Define a `menu.xml` file with your buttons information **e.g.**
39 |
40 | ```xml
41 |
63 | ```
64 |
65 | **XML**:
66 |
67 | - Bind the buttons menu by adding the **custom attribute** `app:button_menu="@menu/your_fab_buttons"` to your component XML.
68 |
69 | **Programmatically**
70 |
71 | - Bind the buttons menu on your FabOptions instance with FabOptions#setMenu(Menu).
72 |
73 | ```java
74 | FabOptions fabOptions = (FabOptions) findViewById(R.id.fab_options);
75 | fabOptions.setButtonsMenu(R.menu.your_fab_buttons);
76 | ```
77 |
78 |
79 |
80 | **Listening for click events**
81 |
82 | - Set your FabOptions instance click listener.
83 |
84 | - Handle the click events for each button id defined on the `menu.xml`.
85 |
86 |
87 | ## Customizing
88 |
89 | You can change the color of the component, both the FAB and the "background" individually, **unless specified** the background will **always** adopt the same value as the ```app:fab_color``` attribute (default is the theme's accent color).
90 |
91 |
92 | ```xml
93 |
99 | ```
100 |
101 | You can also use **Java**
102 |
103 | ```java
104 | fabOptions.setFabColor(R.color.fabOptionsFabColor);
105 | fabOptions.setBackgroundColor(R.color.fabOptionsBackgroundColor);
106 | ```
107 |
108 | *Note: One is not dependent on the other, you can set individually.*
109 |
110 | **Changing button color**
111 | ```java
112 | fabOptions.setButtonColor(R.id.faboptions_favorite, R.color.colorAccent)
113 | ```
114 |
115 | This will return a boolean value if it's able to change the color.
116 |
117 |
118 | ### The sample is also available on the Playstore
119 |
120 | [](https://play.google.com/store/apps/details?id=com.joaquimley.faboptions.sample)
121 |
122 | **Issues:**
123 | Fell free to open a new issue. Follow the [ISSUE_TEMPLATE.MD](../development/ISSUE_TEMPLATE.MD)
124 |
125 | ## [Changelog](https://github.com/JoaquimLey/faboptions/releases)
126 |
127 | **1.2.0**
128 | - Ability to open and close the component with new exposed `open()`/`close()` methods. - #35
129 | - Change the background color `setBackgroundColor()` through `@ColorInt` - #41
130 |
131 | **1.1.2**
132 | - Fix a bug where buttons were clickable even when hidden - #25
133 |
134 | **1.1.1**
135 | - Fix a resurfaced issue with related to Snackbar behaviour - #8
136 |
137 | **1.1.0**
138 | - Backport to API 14 - #21
139 | - Change button color at runtime with the new #setButtonColor(int) - #22
140 | - Bug fix on Menu not displayed correctly - #17
141 | - Customize both background + fab colors. - #16
142 |
143 | **1.0.2**
144 | - Fix layout measure
145 | - The component now reacts when a snackbar dismissed by user - #8
146 |
147 |
148 | **1.0.1**
149 | - Fix slight vertical offset on the button's icon - #2
150 |
151 | ## Contributing
152 |
153 | Contributions are always welcome!
154 |
155 | Follow the "fork-and-pull" Git workflow.
156 |
157 | 1. **Fork** the repo on GitHub
158 | 2. **Clone** the project to your own machine
159 | 3. **Commit** changes to your own branch
160 | 4. **Merge** with current *development* branch
161 | 5. **Push** your work back up to your fork
162 | 7. Submit a **Pull request** your changes can be reviewed (please refere the issue if reported)
163 |
164 | **Prevent** code-style related changes (at least run Ctrl+⌥+O, ⌥+⌘+L) before commiting.
165 |
166 | ### License
167 |
168 | Copyright © 2016 Joaquim Ley
169 |
170 | Licensed under the Apache License, Version 2.0 (the "License");
171 | you may not use this file except in compliance with the License.
172 | You may obtain a copy of the License at
173 |
174 | http://www.apache.org/licenses/LICENSE-2.0
175 |
176 | Unless required by applicable law or agreed to in writing, software
177 | distributed under the License is distributed on an "AS IS" BASIS,
178 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
179 | implied.
180 | See the License for the specific language governing permissions and
181 | limitations under the License.
--------------------------------------------------------------------------------
/art/faboptions_demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JoaquimLey/faboptions/ac8a9f9dab7f5c8b757fba8f08cfe5bc7bd66fd9/art/faboptions_demo.gif
--------------------------------------------------------------------------------
/art/faboptions_materialup_demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JoaquimLey/faboptions/ac8a9f9dab7f5c8b757fba8f08cfe5bc7bd66fd9/art/faboptions_materialup_demo.gif
--------------------------------------------------------------------------------
/art/google-play-badge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JoaquimLey/faboptions/ac8a9f9dab7f5c8b757fba8f08cfe5bc7bd66fd9/art/google-play-badge.png
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) Joaquim Ley 2016. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | buildscript {
18 | repositories {
19 | jcenter()
20 | google()
21 | }
22 | dependencies {
23 | classpath 'com.android.tools.build:gradle:3.1.0'
24 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1'
25 | }
26 | }
27 | plugins {
28 | id "com.jfrog.bintray" version "1.7"
29 | }
30 | allprojects {
31 | repositories {
32 | jcenter()
33 | google()
34 | }
35 | }
--------------------------------------------------------------------------------
/faboptions/src/main/java/com/joaquimley/faboptions/FabOptions.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) Joaquim Ley 2016. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.joaquimley.faboptions;
18 |
19 | import android.animation.ObjectAnimator;
20 | import android.annotation.SuppressLint;
21 | import android.content.Context;
22 | import android.content.res.ColorStateList;
23 | import android.content.res.TypedArray;
24 | import android.graphics.PorterDuff;
25 | import android.graphics.drawable.AnimatedVectorDrawable;
26 | import android.graphics.drawable.Drawable;
27 | import android.graphics.drawable.VectorDrawable;
28 | import android.os.Build;
29 | import android.support.annotation.ColorInt;
30 | import android.support.annotation.ColorRes;
31 | import android.support.annotation.MenuRes;
32 | import android.support.annotation.Nullable;
33 | import android.support.annotation.RequiresApi;
34 | import android.support.design.widget.CoordinatorLayout;
35 | import android.support.design.widget.FloatingActionButton;
36 | import android.support.v4.content.ContextCompat;
37 | import android.support.v7.view.SupportMenuInflater;
38 | import android.support.v7.view.menu.MenuBuilder;
39 | import android.support.v7.widget.AppCompatImageView;
40 | import android.transition.ChangeBounds;
41 | import android.transition.ChangeTransform;
42 | import android.transition.Transition;
43 | import android.transition.TransitionManager;
44 | import android.transition.TransitionSet;
45 | import android.util.AttributeSet;
46 | import android.util.Log;
47 | import android.util.TypedValue;
48 | import android.view.Menu;
49 | import android.view.MenuItem;
50 | import android.view.View;
51 | import android.view.ViewGroup;
52 | import android.widget.FrameLayout;
53 |
54 | import static com.joaquimley.faboptions.R.drawable.faboptions_ic_overflow;
55 |
56 | /**
57 | * FabOptions component
58 | */
59 | @CoordinatorLayout.DefaultBehavior(FabOptionsBehavior.class)
60 | public class FabOptions extends FrameLayout implements View.OnClickListener {
61 |
62 | private static final String TAG = "FabOptions";
63 |
64 | private static final String SUPER_INSTANCE_STATE = "superInstanceState";
65 | private static final String FAB_OPTIONS_IS_OPEN = "fabOptionsIsOpen";
66 |
67 | private static final int NO_DIMENSION = 0;
68 | private static final long CLOSE_MORPH_TRANSFORM_DURATION = 70;
69 |
70 | private boolean mIsAnimating;
71 | private boolean mIsOpen;
72 | private View.OnClickListener mClickListener;
73 |
74 | private Menu mMenu;
75 | private FloatingActionButton mFab;
76 |
77 | private View mBackground;
78 | private View mSeparator;
79 | private FabOptionsButtonContainer mButtonContainer;
80 |
81 | public FabOptions(Context context) {
82 | this(context, null);
83 | }
84 |
85 | public FabOptions(Context context, AttributeSet attrs) {
86 | this(context, attrs, 0);
87 | }
88 |
89 | public FabOptions(Context context, AttributeSet attrs, int defStyleAttr) {
90 | super(context, attrs, defStyleAttr);
91 | initViews(context);
92 | setInitialFabIcon();
93 |
94 | TypedArray fabOptionsAttributes = context.getTheme().obtainStyledAttributes(attrs, R.styleable.FabOptions, 0, 0);
95 | styleComponent(context, fabOptionsAttributes);
96 | inflateButtonsFromAttrs(context, fabOptionsAttributes);
97 | }
98 |
99 | private void initViews(Context context) {
100 | inflate(context, R.layout.faboptions_layout, this);
101 | mBackground = findViewById(R.id.faboptions_background);
102 | mButtonContainer = findViewById(R.id.faboptions_button_container);
103 | mFab = findViewById(R.id.faboptions_fab);
104 | mFab.setOnClickListener(this);
105 | }
106 |
107 | public boolean isOpen() {
108 | return mIsOpen;
109 | }
110 |
111 | public void open(@Nullable final FabOptionsAnimationStateListener listener) {
112 | expand(listener);
113 | }
114 |
115 | public void close(@Nullable final FabOptionsAnimationStateListener listener) {
116 | collapse(listener);
117 | }
118 |
119 | // @Nullable
120 | // @Override
121 | // protected Parcelable onSaveInstanceState() {
122 | // Bundle bundle = new Bundle();
123 | // bundle.putParcelable(SUPER_INSTANCE_STATE, super.onSaveInstanceState());
124 | // bundle.putBoolean(FAB_OPTIONS_IS_OPEN, mIsOpen);
125 | // return bundle;
126 | // }
127 | //
128 | // @Override
129 | // protected void onRestoreInstanceState(Parcelable state) {
130 | // if (state instanceof Bundle) {
131 | // Bundle bundle = (Bundle) state;
132 | // mIsOpen = bundle.getBoolean(FAB_OPTIONS_IS_OPEN, false);
133 | // state = bundle.getParcelable(SUPER_INSTANCE_STATE);
134 | // }
135 | // super.onRestoreInstanceState(state);
136 | // }
137 |
138 | public void setFabColor(@ColorRes int fabColor) {
139 | Context context = getContext();
140 | if (context != null) {
141 | @ColorInt int colorId = ContextCompat.getColor(context, fabColor);
142 | mFab.setBackgroundTintList(ColorStateList.valueOf(colorId));
143 | }
144 | }
145 |
146 | public void setBackgroundColor(Context context, @ColorInt int backgroundColor) {
147 | Drawable backgroundShape = ContextCompat.getDrawable(context, R.drawable.faboptions_background);
148 | if (backgroundShape != null) {
149 | backgroundShape.setColorFilter(backgroundColor, PorterDuff.Mode.ADD);
150 | }
151 |
152 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
153 | mBackground.setBackground(backgroundShape);
154 | } else {
155 | mBackground.setBackgroundDrawable(backgroundShape);
156 | }
157 | }
158 |
159 | /**
160 | * @deprecated Prefer passing resolved color {@link #setBackgroundColor(Context, int)} for safe context
161 | */
162 | @Deprecated
163 | public void setBackgroundColor(@ColorRes int backgroundColor) {
164 | Context context = getContext();
165 | if (context != null) {
166 | setBackgroundColor(context, ContextCompat.getColor(context, backgroundColor));
167 | } else {
168 | Log.w(TAG, "Couldn't set background color, context is null");
169 | }
170 | }
171 |
172 | public boolean setButtonColor(int buttonId, @ColorRes int color) {
173 | for (int i = 0; i < mButtonContainer.getChildCount(); i++) {
174 | if (mMenu.getItem(i).getItemId() == buttonId) {
175 | return styleButton(i, color);
176 | }
177 | }
178 | Log.d(TAG, "setButtonColor(): Couldn't find button with id " + buttonId);
179 | return false;
180 | }
181 |
182 | public void setButtonsMenu(@MenuRes int menuId) {
183 | Context context = getContext();
184 | if (context != null) {
185 | setButtonsMenu(context, menuId);
186 | } else {
187 | Log.w(TAG, "Couldn't set buttons, context is null");
188 | }
189 | }
190 |
191 | /**
192 | * Deprecated use {@link #setButtonsMenu(int)}.
193 | */
194 | @Deprecated
195 | @SuppressLint("RestrictedApi")
196 | public void setButtonsMenu(Context context, @MenuRes int menuId) {
197 | mMenu = new MenuBuilder(context);
198 | SupportMenuInflater menuInf = new SupportMenuInflater(context);
199 | menuInf.inflate(menuId, mMenu);
200 | addButtonsFromMenu(context, mMenu);
201 | mSeparator = mButtonContainer.addSeparator(context);
202 | animateButtons(false);
203 | }
204 |
205 | private void setInitialFabIcon() {
206 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
207 | VectorDrawable drawable = (VectorDrawable) getResources().getDrawable(faboptions_ic_overflow, null);
208 | mFab.setImageDrawable(drawable);
209 | } else {
210 | mFab.setImageResource(R.drawable.faboptions_ic_overflow);
211 | }
212 | }
213 |
214 | /**
215 | * Styles the component via attributes R.styleable.FabOptions_fab_color
216 | * If not set, the background same colour as the FAB, which in turn if the later
217 | * is not not set the default accent color will be used
218 | */
219 | private void styleComponent(Context context, TypedArray attributes) {
220 | int fabColor = attributes.getColor(R.styleable.FabOptions_fab_color, getThemeAccentColor(context));
221 | int backgroundColor = attributes.getColor(R.styleable.FabOptions_background_color, fabColor);
222 |
223 | setBackgroundColor(context, backgroundColor);
224 | mFab.setBackgroundTintList(ColorStateList.valueOf(fabColor));
225 | }
226 |
227 | @ColorInt
228 | private int getThemeAccentColor(final Context context) {
229 | final TypedValue value = new TypedValue();
230 | context.getTheme().resolveAttribute(R.attr.colorAccent, value, true);
231 | return value.data;
232 | }
233 |
234 | private void inflateButtonsFromAttrs(Context context, TypedArray attributes) {
235 | if (attributes.hasValue(R.styleable.FabOptions_button_menu)) {
236 | setButtonsMenu(context, attributes.getResourceId(R.styleable.FabOptions_button_menu, 0));
237 | }
238 | }
239 |
240 | private void addButtonsFromMenu(Context context, Menu menu) {
241 | for (int i = 0; i < menu.size(); i++) {
242 | addButton(context, menu.getItem(i));
243 | }
244 | }
245 |
246 | private void addButton(Context context, MenuItem menuItem) {
247 | AppCompatImageView button = mButtonContainer.addButton(context, menuItem.getItemId(),
248 | menuItem.getTitle(), menuItem.getIcon());
249 | button.setOnClickListener(this);
250 | }
251 |
252 | private boolean styleButton(int buttonIndex, @ColorRes int color) {
253 | if (buttonIndex >= (mButtonContainer.getChildCount() / 2)) {
254 | // Hacky way to deal with the separator view index
255 | buttonIndex++;
256 | }
257 |
258 | if (buttonIndex >= mButtonContainer.getChildCount()) {
259 | Log.e(TAG, "Button at " + buttonIndex + " is null (index out of bounds)");
260 | return false;
261 | }
262 |
263 | AppCompatImageView imageView = (AppCompatImageView) mButtonContainer.getChildAt(buttonIndex);
264 | imageView.setColorFilter(ContextCompat.getColor(getContext(), color));
265 | return true;
266 | }
267 |
268 | @Override
269 | public void onClick(View v) {
270 | if (!mIsAnimating) {
271 | mIsAnimating = true;
272 | if (v.getId() == R.id.faboptions_fab) {
273 | if (mIsOpen) {
274 | collapse(null);
275 | } else {
276 | expand(null);
277 | }
278 | } else {
279 | if (mClickListener != null && mIsOpen) {
280 | mClickListener.onClick(v);
281 | collapse(null);
282 | }
283 | }
284 | }
285 | }
286 |
287 | @Override
288 | public void setOnClickListener(View.OnClickListener listener) {
289 | mClickListener = listener;
290 | }
291 |
292 | private void expand(@Nullable final FabOptionsAnimationStateListener listener) {
293 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
294 | AnimatedVectorDrawable drawable = (AnimatedVectorDrawable) getResources()
295 | .getDrawable(R.drawable.faboptions_ic_menu_animatable, null);
296 | mFab.setImageDrawable(drawable);
297 | drawable.start();
298 | } else {
299 | mFab.setImageResource(R.drawable.faboptions_ic_close);
300 | }
301 |
302 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
303 | final TransitionSet transitionSet = new OpenMorphTransition(mButtonContainer);
304 | transitionSet.addListener(new Transition.TransitionListener() {
305 | @Override
306 | public void onTransitionStart(final Transition transition) {
307 | }
308 |
309 | @Override
310 | public void onTransitionEnd(final Transition transition) {
311 | if (listener != null) {
312 | listener.onOpenAnimationEnd();
313 | }
314 | mIsAnimating = false;
315 | }
316 |
317 | @Override
318 | public void onTransitionCancel(final Transition transition) {
319 | }
320 |
321 | @Override
322 | public void onTransitionPause(final Transition transition) {
323 | }
324 |
325 | @Override
326 | public void onTransitionResume(final Transition transition) {
327 | }
328 | });
329 |
330 | TransitionManager.beginDelayedTransition(this, transitionSet);
331 | }
332 | animateBackground(true);
333 | animateButtons(true);
334 |
335 | mIsOpen = true;
336 | }
337 |
338 | private void collapse(@Nullable final FabOptionsAnimationStateListener listener) {
339 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
340 | AnimatedVectorDrawable drawable = (AnimatedVectorDrawable) getResources().getDrawable(R.drawable.faboptions_ic_close_animatable, null);
341 | mFab.setImageDrawable(drawable);
342 | drawable.start();
343 | } else {
344 | mFab.setImageResource(R.drawable.faboptions_ic_overflow);
345 | }
346 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
347 | final TransitionSet transitionSet = new CloseMorphTransition(mButtonContainer);
348 | transitionSet.addListener(new Transition.TransitionListener() {
349 | @Override
350 | public void onTransitionStart(final Transition transition) {
351 | }
352 |
353 | @Override
354 | public void onTransitionEnd(final Transition transition) {
355 | if (listener != null) {
356 | listener.onCloseAnimationEnd();
357 | }
358 | mIsAnimating = false;
359 | }
360 |
361 | @Override
362 | public void onTransitionCancel(final Transition transition) {
363 | }
364 |
365 | @Override
366 | public void onTransitionPause(final Transition transition) {
367 | }
368 |
369 | @Override
370 | public void onTransitionResume(final Transition transition) {
371 | }
372 | });
373 |
374 | TransitionManager.beginDelayedTransition(this, transitionSet);
375 | }
376 | animateButtons(false);
377 | animateBackground(false);
378 | mIsOpen = false;
379 | }
380 |
381 | @Override
382 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
383 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
384 | if (mSeparator != null) {
385 | ViewGroup.LayoutParams separatorLayoutParams = mSeparator.getLayoutParams();
386 | separatorLayoutParams.width = mFab.getMeasuredWidth();
387 | separatorLayoutParams.height = mFab.getMeasuredHeight();
388 | mSeparator.setLayoutParams(separatorLayoutParams);
389 | }
390 | }
391 |
392 | private void animateBackground(final boolean isOpen) {
393 | ViewGroup.LayoutParams backgroundLayoutParams = mBackground.getLayoutParams();
394 | backgroundLayoutParams.width = isOpen ? mButtonContainer.getMeasuredWidth() : NO_DIMENSION;
395 | mBackground.setLayoutParams(backgroundLayoutParams);
396 | }
397 |
398 | private void openCompatAnimation() {
399 | ObjectAnimator anim = ObjectAnimator.ofFloat(mBackground, "scaleX", 1.0f);
400 | anim.setDuration(30000); // duration 3 seconds
401 | anim.start();
402 | }
403 |
404 |
405 | private void closeCompatAnimation() {
406 | ObjectAnimator anim = ObjectAnimator.ofFloat(mBackground, "scaleX", 0.0f);
407 | anim.setDuration(3000);
408 | anim.start();
409 | animateButtons(false);
410 | }
411 |
412 | private void animateButtons(boolean isOpen) {
413 | for (int i = 0; i < mButtonContainer.getChildCount(); i++) {
414 | mButtonContainer.getChildAt(i).setScaleX(isOpen ? 1 : 0);
415 | mButtonContainer.getChildAt(i).setScaleY(isOpen ? 1 : 0);
416 | }
417 | }
418 |
419 | @RequiresApi(api = Build.VERSION_CODES.KITKAT)
420 | private class OpenMorphTransition extends TransitionSet {
421 | OpenMorphTransition(ViewGroup viewGroup) {
422 |
423 | ChangeBounds changeBound = new ChangeBounds();
424 | changeBound.excludeChildren(R.id.faboptions_button_container, true);
425 | addTransition(changeBound);
426 |
427 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
428 | ChangeTransform changeTransform = new ChangeTransform();
429 | for (int i = 0; i < viewGroup.getChildCount(); i++) {
430 | changeTransform.addTarget(viewGroup.getChildAt(i));
431 | }
432 | addTransition(changeTransform);
433 | }
434 |
435 | setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
436 | }
437 | }
438 |
439 | @RequiresApi(api = Build.VERSION_CODES.KITKAT)
440 | private class CloseMorphTransition extends TransitionSet {
441 | CloseMorphTransition(ViewGroup viewGroup) {
442 |
443 | ChangeBounds changeBound = new ChangeBounds();
444 | changeBound.excludeChildren(R.id.faboptions_button_container, true);
445 |
446 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
447 | ChangeTransform changeTransform = new ChangeTransform();
448 | for (int i = 0; i < viewGroup.getChildCount(); i++) {
449 | changeTransform.addTarget(viewGroup.getChildAt(i));
450 | }
451 | changeTransform.setDuration(CLOSE_MORPH_TRANSFORM_DURATION);
452 | addTransition(changeTransform);
453 | }
454 |
455 | addTransition(changeBound);
456 | setOrdering(TransitionSet.ORDERING_TOGETHER);
457 | }
458 | }
459 | }
--------------------------------------------------------------------------------
/faboptions/src/main/java/com/joaquimley/faboptions/FabOptionsAnimationStateListener.java:
--------------------------------------------------------------------------------
1 | package com.joaquimley.faboptions;
2 |
3 | /**
4 | * FabOptions exposed listener for the expand and collapse animations
5 | */
6 |
7 | public interface FabOptionsAnimationStateListener {
8 | void onOpenAnimationEnd();
9 |
10 | void onCloseAnimationEnd();
11 | }
--------------------------------------------------------------------------------
/faboptions/src/main/res/anim/faboptions_open_morph_animation.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
20 |
29 |
30 |
34 |
--------------------------------------------------------------------------------
/faboptions/src/main/res/animator/faboptions_close_to_overflow.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
19 |
20 |
26 |
27 |
--------------------------------------------------------------------------------
/faboptions/src/main/res/animator/faboptions_overflow_to_close.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
19 |
20 |
25 |
26 |
--------------------------------------------------------------------------------
/faboptions/src/main/res/drawable/faboptions_ic_close.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
22 |
23 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/faboptions/src/main/res/drawable/faboptions_ic_overflow.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
22 |
23 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JoaquimLey/faboptions/ac8a9f9dab7f5c8b757fba8f08cfe5bc7bd66fd9/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Apr 02 21:19:50 CEST 2018
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/library/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/library/build.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) Joaquim Ley 2016. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | apply plugin: 'com.android.library'
18 | apply plugin: 'com.github.dcendents.android-maven'
19 | apply plugin: 'com.jfrog.bintray'
20 |
21 | android {
22 | compileSdkVersion 27
23 | buildToolsVersion '28.0.0 rc2'
24 |
25 | defaultConfig {
26 | minSdkVersion 14
27 | targetSdkVersion 27
28 | versionCode 12
29 | versionName "1.2.0"
30 | vectorDrawables.useSupportLibrary = true
31 | }
32 |
33 | buildTypes {
34 | release {
35 | minifyEnabled false
36 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
37 | }
38 | }
39 | }
40 |
41 | dependencies {
42 | final DESIGN_LIBRARY_VERSION = '27.1.0'
43 | implementation "com.android.support:design:$DESIGN_LIBRARY_VERSION"
44 | }
45 | apply from: 'https://raw.githubusercontent.com/JoaquimLey/jcenter-config/master/deploy.gradle'
46 |
--------------------------------------------------------------------------------
/library/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/joaquimley/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/library/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/library/src/main/java/com/joaquimley/faboptions/FabOptions.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) Joaquim Ley 2016. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.joaquimley.faboptions;
18 |
19 | import android.animation.ObjectAnimator;
20 | import android.annotation.SuppressLint;
21 | import android.content.Context;
22 | import android.content.res.ColorStateList;
23 | import android.content.res.TypedArray;
24 | import android.graphics.PorterDuff;
25 | import android.graphics.drawable.AnimatedVectorDrawable;
26 | import android.graphics.drawable.Drawable;
27 | import android.graphics.drawable.VectorDrawable;
28 | import android.os.Build;
29 | import android.support.annotation.ColorInt;
30 | import android.support.annotation.ColorRes;
31 | import android.support.annotation.MenuRes;
32 | import android.support.annotation.Nullable;
33 | import android.support.annotation.RequiresApi;
34 | import android.support.design.widget.CoordinatorLayout;
35 | import android.support.design.widget.FloatingActionButton;
36 | import android.support.v4.content.ContextCompat;
37 | import android.support.v7.view.SupportMenuInflater;
38 | import android.support.v7.view.menu.MenuBuilder;
39 | import android.support.v7.widget.AppCompatImageView;
40 | import android.transition.ChangeBounds;
41 | import android.transition.ChangeTransform;
42 | import android.transition.Transition;
43 | import android.transition.TransitionManager;
44 | import android.transition.TransitionSet;
45 | import android.util.AttributeSet;
46 | import android.util.Log;
47 | import android.util.TypedValue;
48 | import android.view.Menu;
49 | import android.view.MenuItem;
50 | import android.view.View;
51 | import android.view.ViewGroup;
52 | import android.widget.FrameLayout;
53 |
54 | import static com.joaquimley.faboptions.R.drawable.faboptions_ic_overflow;
55 |
56 | /**
57 | * FabOptions component
58 | */
59 | @CoordinatorLayout.DefaultBehavior(FabOptionsBehavior.class)
60 | public class FabOptions extends FrameLayout implements View.OnClickListener {
61 |
62 | private static final String TAG = "FabOptions";
63 |
64 | private static final String SUPER_INSTANCE_STATE = "superInstanceState";
65 | private static final String FAB_OPTIONS_IS_OPEN = "fabOptionsIsOpen";
66 |
67 | private static final int NO_DIMENSION = 0;
68 | private static final long CLOSE_MORPH_TRANSFORM_DURATION = 70;
69 |
70 | private boolean mIsAnimating;
71 | private boolean mIsOpen;
72 | private View.OnClickListener mClickListener;
73 |
74 | private Menu mMenu;
75 | private FloatingActionButton mFab;
76 |
77 | private View mBackground;
78 | private View mSeparator;
79 | private FabOptionsButtonContainer mButtonContainer;
80 |
81 | public FabOptions(Context context) {
82 | this(context, null);
83 | }
84 |
85 | public FabOptions(Context context, AttributeSet attrs) {
86 | this(context, attrs, 0);
87 | }
88 |
89 | public FabOptions(Context context, AttributeSet attrs, int defStyleAttr) {
90 | super(context, attrs, defStyleAttr);
91 | initViews(context);
92 | setInitialFabIcon();
93 |
94 | TypedArray fabOptionsAttributes = context.getTheme().obtainStyledAttributes(attrs, R.styleable.FabOptions, 0, 0);
95 | styleComponent(context, fabOptionsAttributes);
96 | inflateButtonsFromAttrs(context, fabOptionsAttributes);
97 | }
98 |
99 | public boolean isOpen() {
100 | return mIsOpen;
101 | }
102 |
103 | public void open(@Nullable final FabOptionsAnimationStateListener listener) {
104 | expand(listener);
105 | }
106 |
107 | public void close(@Nullable final FabOptionsAnimationStateListener listener) {
108 | collapse(listener);
109 | }
110 |
111 | // @Nullable
112 | // @Override
113 | // protected Parcelable onSaveInstanceState() {
114 | // Bundle bundle = new Bundle();
115 | // bundle.putParcelable(SUPER_INSTANCE_STATE, super.onSaveInstanceState());
116 | // bundle.putBoolean(FAB_OPTIONS_IS_OPEN, mIsOpen);
117 | // return bundle;
118 | // }
119 | //
120 | // @Override
121 | // protected void onRestoreInstanceState(Parcelable state) {
122 | // if (state instanceof Bundle) {
123 | // Bundle bundle = (Bundle) state;
124 | // mIsOpen = bundle.getBoolean(FAB_OPTIONS_IS_OPEN, false);
125 | // state = bundle.getParcelable(SUPER_INSTANCE_STATE);
126 | // }
127 | // super.onRestoreInstanceState(state);
128 | // }
129 |
130 | public void setFabColor(@ColorRes int fabColor) {
131 | Context context = getContext();
132 | if (context != null) {
133 | @ColorInt int colorId = ContextCompat.getColor(context, fabColor);
134 | mFab.setBackgroundTintList(ColorStateList.valueOf(colorId));
135 | }
136 | }
137 |
138 | public void setBackgroundColor(Context context, @ColorInt int backgroundColor) {
139 | Drawable backgroundShape = ContextCompat.getDrawable(context, R.drawable.faboptions_background);
140 | if (backgroundShape != null) {
141 | backgroundShape.setColorFilter(backgroundColor, PorterDuff.Mode.ADD);
142 | }
143 |
144 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
145 | mBackground.setBackground(backgroundShape);
146 | } else {
147 | mBackground.setBackgroundDrawable(backgroundShape);
148 | }
149 | }
150 |
151 | /**
152 | * @deprecated Prefer passing resolved color {@link #setBackgroundColor(Context, int)} for safe context
153 | */
154 | @Deprecated
155 | public void setBackgroundColor(@ColorRes int backgroundColor) {
156 | Context context = getContext();
157 | if (context != null) {
158 | setBackgroundColor(context, ContextCompat.getColor(context, backgroundColor));
159 | } else {
160 | Log.w(TAG, "Couldn't set background color, context is null");
161 | }
162 | }
163 |
164 | public boolean setButtonColor(int buttonId, @ColorRes int color) {
165 | for (int i = 0; i < mButtonContainer.getChildCount(); i++) {
166 | if (mMenu.getItem(i).getItemId() == buttonId) {
167 | return styleButton(i, color);
168 | }
169 | }
170 | Log.d(TAG, "setButtonColor(): Couldn't find button with id " + buttonId);
171 | return false;
172 | }
173 |
174 | public void setButtonsMenu(@MenuRes int menuId) {
175 | Context context = getContext();
176 | if (context != null) {
177 | setButtonsMenu(context, menuId);
178 | } else {
179 | Log.w(TAG, "Couldn't set buttons, context is null");
180 | }
181 | }
182 |
183 | /**
184 | * Deprecated use {@link #setButtonsMenu(int)}.
185 | */
186 | @Deprecated
187 | @SuppressLint("RestrictedApi")
188 | public void setButtonsMenu(Context context, @MenuRes int menuId) {
189 | mMenu = new MenuBuilder(context);
190 | SupportMenuInflater menuInf = new SupportMenuInflater(context);
191 | menuInf.inflate(menuId, mMenu);
192 | addButtonsFromMenu(context, mMenu);
193 | mSeparator = mButtonContainer.addSeparator(context);
194 | animateButtons(false);
195 | }
196 |
197 | private void initViews(Context context) {
198 | inflate(context, R.layout.faboptions_layout, this);
199 | mBackground = findViewById(R.id.faboptions_background);
200 | mButtonContainer = (FabOptionsButtonContainer) findViewById(R.id.faboptions_button_container);
201 | mFab = (FloatingActionButton) findViewById(R.id.faboptions_fab);
202 | mFab.setOnClickListener(this);
203 | }
204 |
205 | private void setInitialFabIcon() {
206 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
207 | VectorDrawable drawable = (VectorDrawable) getResources().getDrawable(faboptions_ic_overflow, null);
208 | mFab.setImageDrawable(drawable);
209 | } else {
210 | mFab.setImageResource(R.drawable.faboptions_ic_overflow);
211 | }
212 | }
213 |
214 | /**
215 | * Styles the component via attributes R.styleable.FabOptions_fab_color
216 | * If not set, the background same colour as the FAB, which in turn if the later
217 | * is not not set the default accent color will be used
218 | */
219 | private void styleComponent(Context context, TypedArray attributes) {
220 | int fabColor = attributes.getColor(R.styleable.FabOptions_fab_color, getThemeAccentColor(context));
221 | int backgroundColor = attributes.getColor(R.styleable.FabOptions_background_color, fabColor);
222 |
223 | setBackgroundColor(context, backgroundColor);
224 | mFab.setBackgroundTintList(ColorStateList.valueOf(fabColor));
225 | }
226 |
227 | @ColorInt
228 | private int getThemeAccentColor(final Context context) {
229 | final TypedValue value = new TypedValue();
230 | context.getTheme().resolveAttribute(R.attr.colorAccent, value, true);
231 | return value.data;
232 | }
233 |
234 | private void inflateButtonsFromAttrs(Context context, TypedArray attributes) {
235 | if (attributes.hasValue(R.styleable.FabOptions_button_menu)) {
236 | setButtonsMenu(context, attributes.getResourceId(R.styleable.FabOptions_button_menu, 0));
237 | }
238 | }
239 |
240 | private void addButtonsFromMenu(Context context, Menu menu) {
241 | for (int i = 0; i < menu.size(); i++) {
242 | addButton(context, menu.getItem(i));
243 | }
244 | }
245 |
246 | private void addButton(Context context, MenuItem menuItem) {
247 | AppCompatImageView button = mButtonContainer.addButton(context, menuItem.getItemId(),
248 | menuItem.getTitle(), menuItem.getIcon());
249 | button.setOnClickListener(this);
250 | }
251 |
252 | private boolean styleButton(int buttonIndex, @ColorRes int color) {
253 | if (buttonIndex >= (mButtonContainer.getChildCount() / 2)) {
254 | // Hacky way to deal with the separator view index
255 | buttonIndex++;
256 | }
257 |
258 | if (buttonIndex >= mButtonContainer.getChildCount()) {
259 | Log.e(TAG, "Button at " + buttonIndex + " is null (index out of bounds)");
260 | return false;
261 | }
262 |
263 | AppCompatImageView imageView = (AppCompatImageView) mButtonContainer.getChildAt(buttonIndex);
264 | imageView.setColorFilter(ContextCompat.getColor(getContext(), color));
265 | return true;
266 | }
267 |
268 | @Override
269 | public void onClick(View v) {
270 | if (!mIsAnimating) {
271 | mIsAnimating = true;
272 | if (v.getId() == R.id.faboptions_fab) {
273 | if (mIsOpen) {
274 | collapse(null);
275 | } else {
276 | expand(null);
277 | }
278 | } else {
279 | if (mClickListener != null && mIsOpen) {
280 | mClickListener.onClick(v);
281 | collapse(null);
282 | }
283 | }
284 | }
285 | }
286 |
287 | @Override
288 | public void setOnClickListener(View.OnClickListener listener) {
289 | mClickListener = listener;
290 | }
291 |
292 | private void expand(@Nullable final FabOptionsAnimationStateListener listener) {
293 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
294 | AnimatedVectorDrawable drawable = (AnimatedVectorDrawable) getResources()
295 | .getDrawable(R.drawable.faboptions_ic_menu_animatable, null);
296 | mFab.setImageDrawable(drawable);
297 | drawable.start();
298 | } else {
299 | mFab.setImageResource(R.drawable.faboptions_ic_close);
300 | }
301 |
302 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
303 | final TransitionSet transitionSet = new OpenMorphTransition(mButtonContainer);
304 | transitionSet.addListener(new Transition.TransitionListener() {
305 | @Override
306 | public void onTransitionStart(final Transition transition) {
307 | }
308 |
309 | @Override
310 | public void onTransitionEnd(final Transition transition) {
311 | if (listener != null) {
312 | listener.onOpenAnimationEnd();
313 | }
314 | mIsAnimating = false;
315 | }
316 |
317 | @Override
318 | public void onTransitionCancel(final Transition transition) {
319 | }
320 |
321 | @Override
322 | public void onTransitionPause(final Transition transition) {
323 | }
324 |
325 | @Override
326 | public void onTransitionResume(final Transition transition) {
327 | }
328 | });
329 |
330 | TransitionManager.beginDelayedTransition(this, transitionSet);
331 | }
332 | animateBackground(true);
333 | animateButtons(true);
334 |
335 | mIsOpen = true;
336 | }
337 |
338 | private void collapse(@Nullable final FabOptionsAnimationStateListener listener) {
339 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
340 | AnimatedVectorDrawable drawable = (AnimatedVectorDrawable) getResources().getDrawable(R.drawable.faboptions_ic_close_animatable, null);
341 | mFab.setImageDrawable(drawable);
342 | drawable.start();
343 | } else {
344 | mFab.setImageResource(R.drawable.faboptions_ic_overflow);
345 | }
346 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
347 | final TransitionSet transitionSet = new CloseMorphTransition(mButtonContainer);
348 | transitionSet.addListener(new Transition.TransitionListener() {
349 | @Override
350 | public void onTransitionStart(final Transition transition) {
351 | }
352 |
353 | @Override
354 | public void onTransitionEnd(final Transition transition) {
355 | if (listener != null) {
356 | listener.onCloseAnimationEnd();
357 | }
358 | mIsAnimating = false;
359 | }
360 |
361 | @Override
362 | public void onTransitionCancel(final Transition transition) {
363 | }
364 |
365 | @Override
366 | public void onTransitionPause(final Transition transition) {
367 | }
368 |
369 | @Override
370 | public void onTransitionResume(final Transition transition) {
371 | }
372 | });
373 |
374 | TransitionManager.beginDelayedTransition(this, transitionSet);
375 | }
376 | animateButtons(false);
377 | animateBackground(false);
378 | mIsOpen = false;
379 | }
380 |
381 | @Override
382 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
383 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
384 | if (mSeparator != null) {
385 | ViewGroup.LayoutParams separatorLayoutParams = mSeparator.getLayoutParams();
386 | separatorLayoutParams.width = mFab.getMeasuredWidth();
387 | separatorLayoutParams.height = mFab.getMeasuredHeight();
388 | mSeparator.setLayoutParams(separatorLayoutParams);
389 | }
390 | }
391 |
392 | private void animateBackground(final boolean isOpen) {
393 | ViewGroup.LayoutParams backgroundLayoutParams = mBackground.getLayoutParams();
394 | backgroundLayoutParams.width = isOpen ? mButtonContainer.getMeasuredWidth() : NO_DIMENSION;
395 | mBackground.setLayoutParams(backgroundLayoutParams);
396 | }
397 |
398 | private void openCompatAnimation() {
399 | ObjectAnimator anim = ObjectAnimator.ofFloat(mBackground, "scaleX", 1.0f);
400 | anim.setDuration(30000); // duration 3 seconds
401 | anim.start();
402 | }
403 |
404 |
405 | private void closeCompatAnimation() {
406 | ObjectAnimator anim = ObjectAnimator.ofFloat(mBackground, "scaleX", 0.0f);
407 | anim.setDuration(3000);
408 | anim.start();
409 | animateButtons(false);
410 | }
411 |
412 | private void animateButtons(boolean isOpen) {
413 | for (int i = 0; i < mButtonContainer.getChildCount(); i++) {
414 | mButtonContainer.getChildAt(i).setScaleX(isOpen ? 1 : 0);
415 | mButtonContainer.getChildAt(i).setScaleY(isOpen ? 1 : 0);
416 | }
417 | }
418 |
419 | @RequiresApi(api = Build.VERSION_CODES.KITKAT)
420 | private class OpenMorphTransition extends TransitionSet {
421 | OpenMorphTransition(ViewGroup viewGroup) {
422 |
423 | ChangeBounds changeBound = new ChangeBounds();
424 | changeBound.excludeChildren(R.id.faboptions_button_container, true);
425 | addTransition(changeBound);
426 |
427 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
428 | ChangeTransform changeTransform = new ChangeTransform();
429 | for (int i = 0; i < viewGroup.getChildCount(); i++) {
430 | changeTransform.addTarget(viewGroup.getChildAt(i));
431 | }
432 | addTransition(changeTransform);
433 | }
434 |
435 | setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
436 | }
437 | }
438 |
439 | @RequiresApi(api = Build.VERSION_CODES.KITKAT)
440 | private class CloseMorphTransition extends TransitionSet {
441 | CloseMorphTransition(ViewGroup viewGroup) {
442 |
443 | ChangeBounds changeBound = new ChangeBounds();
444 | changeBound.excludeChildren(R.id.faboptions_button_container, true);
445 |
446 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
447 | ChangeTransform changeTransform = new ChangeTransform();
448 | for (int i = 0; i < viewGroup.getChildCount(); i++) {
449 | changeTransform.addTarget(viewGroup.getChildAt(i));
450 | }
451 | changeTransform.setDuration(CLOSE_MORPH_TRANSFORM_DURATION);
452 | addTransition(changeTransform);
453 | }
454 |
455 | addTransition(changeBound);
456 | setOrdering(TransitionSet.ORDERING_TOGETHER);
457 | }
458 | }
459 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/joaquimley/faboptions/FabOptionsAnimationStateListener.java:
--------------------------------------------------------------------------------
1 | package com.joaquimley.faboptions;
2 |
3 | /**
4 | * FabOptions exposed listener for the expand and collapse animations
5 | */
6 |
7 | public interface FabOptionsAnimationStateListener {
8 | void onOpenAnimationEnd();
9 |
10 | void onCloseAnimationEnd();
11 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/joaquimley/faboptions/FabOptionsBehavior.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) Joaquim Ley 2016. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.joaquimley.faboptions;
18 |
19 | import android.support.design.widget.CoordinatorLayout;
20 | import android.support.design.widget.Snackbar;
21 | import android.view.View;
22 | import android.widget.FrameLayout;
23 |
24 | /**
25 | * FabOptions component default CoordinatorLayout.Behavior to react Snackbar
26 | */
27 |
28 | public class FabOptionsBehavior extends CoordinatorLayout.Behavior {
29 |
30 | @Override
31 | public boolean layoutDependsOn(CoordinatorLayout parent, FrameLayout child, View dependency) {
32 | return dependency instanceof Snackbar.SnackbarLayout;
33 | }
34 |
35 | @Override
36 | public boolean onDependentViewChanged(CoordinatorLayout parent, FrameLayout child, View dependency) {
37 | float translationY = Math.min(0, dependency.getTranslationY() - dependency.getHeight());
38 | child.setTranslationY(translationY);
39 | return true;
40 | }
41 |
42 | @Override
43 | public void onDependentViewRemoved(CoordinatorLayout parent, FrameLayout child, View dependency) {
44 | super.onDependentViewRemoved(parent, child, dependency);
45 | child.setTranslationY(0);
46 | }
47 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/joaquimley/faboptions/FabOptionsButtonContainer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) Joaquim Ley 2016. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.joaquimley.faboptions;
18 |
19 | import android.content.Context;
20 | import android.graphics.drawable.Drawable;
21 | import android.support.v7.widget.AppCompatImageView;
22 | import android.util.AttributeSet;
23 | import android.view.LayoutInflater;
24 | import android.view.View;
25 | import android.widget.ImageView;
26 | import android.widget.LinearLayout;
27 |
28 | /**
29 | * Custom FabOptions buttons ({@link ImageView}) container, enables runtime view insertion
30 | */
31 |
32 | public class FabOptionsButtonContainer extends LinearLayout {
33 |
34 | public FabOptionsButtonContainer(Context context) {
35 | this(context, null);
36 | }
37 |
38 | public FabOptionsButtonContainer(Context context, AttributeSet attrs) {
39 | this(context, attrs, 0);
40 | }
41 |
42 | public FabOptionsButtonContainer(Context context, AttributeSet attrs, int defStyleAttr) {
43 | super(context, attrs, defStyleAttr);
44 | }
45 |
46 | public AppCompatImageView addButton(Context context, int buttonId, CharSequence title, Drawable drawableIcon) {
47 | return addButton(context, buttonId, title, drawableIcon, null);
48 | }
49 |
50 | public AppCompatImageView addButton(Context context, int buttonId, CharSequence title, Drawable drawableIcon, Integer index) {
51 | AppCompatImageView fabOptionButton =
52 | (AppCompatImageView) LayoutInflater.from(context).inflate(R.layout.faboptions_button, this, false);
53 |
54 | fabOptionButton.setImageDrawable(drawableIcon);
55 | fabOptionButton.setContentDescription(title);
56 | fabOptionButton.setId(buttonId);
57 |
58 | if (index == null) {
59 | addView(fabOptionButton);
60 | } else {
61 | addView(fabOptionButton, index);
62 | }
63 | return fabOptionButton;
64 | }
65 |
66 | public View addSeparator(Context context) {
67 | View separator = LayoutInflater.from(context).inflate(R.layout.faboptions_separator, this, false);
68 | addView(separator, (getChildCount() / 2));
69 | return separator;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/library/src/main/res/anim/faboptions_open_morph_animation.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
20 |
29 |
30 |
34 |
--------------------------------------------------------------------------------
/library/src/main/res/animator/faboptions_close_to_overflow.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
19 |
20 |
26 |
27 |
--------------------------------------------------------------------------------
/library/src/main/res/animator/faboptions_overflow_to_close.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
19 |
20 |
25 |
26 |
--------------------------------------------------------------------------------
/library/src/main/res/drawable/faboptions_background.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/library/src/main/res/drawable/faboptions_ic_close.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
22 |
23 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/library/src/main/res/drawable/faboptions_ic_close_animatable.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
21 |
24 |
--------------------------------------------------------------------------------
/library/src/main/res/drawable/faboptions_ic_menu_animatable.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
21 |
24 |
--------------------------------------------------------------------------------
/library/src/main/res/drawable/faboptions_ic_overflow.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
22 |
23 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/library/src/main/res/layout/faboptions_button.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
--------------------------------------------------------------------------------
/library/src/main/res/layout/faboptions_layout.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
21 |
22 |
27 |
28 |
38 |
39 |
44 |
45 |
--------------------------------------------------------------------------------
/library/src/main/res/layout/faboptions_separator.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
--------------------------------------------------------------------------------
/library/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/library/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 | 8dp
19 | 44dp
20 | 98dp
21 |
22 | 2dp
23 |
24 | 20dp
25 | 20dp
26 | 24dp
27 |
28 |
--------------------------------------------------------------------------------
/library/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 | M 6 12 L 6 12.1, M 12 12.1 L 12 12, M 18 12.1 L 18 12
20 |
21 | M 6 6 L 18 18, M 12 12 L 12 12, M 6 18 L 18 6
22 |
23 |
24 |
--------------------------------------------------------------------------------
/sample/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/sample/build.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) Joaquim Ley 2016. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | apply plugin: 'com.android.application'
18 |
19 | android {
20 | compileSdkVersion 'android-P'
21 | buildToolsVersion '28.0.0 rc2'
22 |
23 | defaultConfig {
24 | applicationId "com.joaquimley.faboptions.sample"
25 | minSdkVersion 14
26 | targetSdkVersion 27
27 | versionCode 7
28 | versionName "1.0.2"
29 | }
30 |
31 | buildTypes {
32 | release {
33 | minifyEnabled false
34 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
35 | }
36 | }
37 | }
38 |
39 | dependencies {
40 | def DESIGN_LIBRARY_VERSION = '27.1.0'
41 | def FABOPTIONS_VERSION = '1.2.0'
42 | implementation "com.android.support:design:$DESIGN_LIBRARY_VERSION"
43 | implementation "com.github.joaquimley:faboptions:$FABOPTIONS_VERSION"
44 | // implementation project(':library')
45 | }
46 |
--------------------------------------------------------------------------------
/sample/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/joaquimley/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/sample/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
20 |
21 |
27 |
28 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/joaquimley/sample/JavaSampleActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) Joaquim Ley 2016. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.joaquimley.sample;
18 |
19 | import android.content.Context;
20 | import android.content.Intent;
21 | import android.os.Bundle;
22 | import android.support.design.widget.Snackbar;
23 | import android.support.v4.content.ContextCompat;
24 | import android.support.v7.app.AppCompatActivity;
25 | import android.support.v7.widget.Toolbar;
26 | import android.view.Menu;
27 | import android.view.MenuInflater;
28 | import android.view.MenuItem;
29 | import android.view.View;
30 | import android.widget.Toast;
31 |
32 | import com.joaquimley.faboptions.FabOptions;
33 | import com.joaquimley.faboptions.sample.R;
34 |
35 | import static com.joaquimley.faboptions.sample.R.id.toolbar;
36 |
37 | /**
38 | * Faboptions sample via Java {@see FabOptions#setButtonMenu()}
39 | */
40 |
41 | public class JavaSampleActivity extends AppCompatActivity implements View.OnClickListener {
42 |
43 | private Toolbar mToolbar;
44 |
45 | public static void start(Context context) {
46 | context.startActivity(new Intent(context, JavaSampleActivity.class));
47 | }
48 |
49 | @Override
50 | protected void onCreate(Bundle savedInstanceState) {
51 | super.onCreate(savedInstanceState);
52 | setContentView(R.layout.activity_sample_java);
53 | mToolbar = findViewById(toolbar);
54 | mToolbar.setTitle(getString(R.string.title_activity_java));
55 | setSupportActionBar(mToolbar);
56 | // This is the important part
57 | customizingFabOptions();
58 | }
59 |
60 | private void customizingFabOptions() {
61 | FabOptions fabOptions = findViewById(R.id.fab_options);
62 | fabOptions.setButtonsMenu(R.menu.menu_faboptions);
63 | fabOptions.setBackgroundColor(this, ContextCompat.getColor(this, R.color.colorPrimaryDark));
64 | fabOptions.setFabColor(R.color.colorAccent);
65 | fabOptions.setOnClickListener(this);
66 | }
67 |
68 | @Override
69 | public void onClick(View view) {
70 | switch (view.getId()) {
71 | case R.id.faboptions_favorite:
72 | Toast.makeText(JavaSampleActivity.this, "Favorite", Toast.LENGTH_SHORT).show();
73 | break;
74 |
75 | case R.id.faboptions_textsms:
76 | Toast.makeText(JavaSampleActivity.this, "Message", Toast.LENGTH_SHORT).show();
77 | break;
78 |
79 |
80 | case R.id.faboptions_download:
81 | Toast.makeText(JavaSampleActivity.this, "Download", Toast.LENGTH_SHORT).show();
82 | break;
83 |
84 | case R.id.faboptions_share:
85 | Toast.makeText(JavaSampleActivity.this, "Share", Toast.LENGTH_SHORT).show();
86 | break;
87 |
88 | default:
89 | // no-op
90 | }
91 | }
92 |
93 | @Override
94 | public boolean onCreateOptionsMenu(Menu menu) {
95 | MenuInflater inflater = getMenuInflater();
96 | inflater.inflate(R.menu.menu_activity_main, menu);
97 | return true;
98 | }
99 |
100 | @Override
101 | public boolean onOptionsItemSelected(MenuItem item) {
102 | switch (item.getItemId()) {
103 | case R.id.action_snackbar_test:
104 | Snackbar.make(mToolbar, getString(R.string.action_snackbar_test_message),
105 | Snackbar.LENGTH_LONG).show();
106 | return true;
107 |
108 | case R.id.action_change_activity:
109 | XmlSampleActivity.start(JavaSampleActivity.this);
110 | finish();
111 | return true;
112 | default:
113 | return super.onOptionsItemSelected(item);
114 | }
115 | }
116 | }
--------------------------------------------------------------------------------
/sample/src/main/java/com/joaquimley/sample/XmlSampleActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) Joaquim Ley 2016. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.joaquimley.sample;
18 |
19 | import android.content.Context;
20 | import android.content.Intent;
21 | import android.os.Bundle;
22 | import android.support.design.widget.Snackbar;
23 | import android.support.v7.app.AppCompatActivity;
24 | import android.support.v7.app.AppCompatDelegate;
25 | import android.support.v7.widget.Toolbar;
26 | import android.view.Menu;
27 | import android.view.MenuInflater;
28 | import android.view.MenuItem;
29 | import android.view.View;
30 | import android.widget.Toast;
31 |
32 | import com.joaquimley.faboptions.FabOptions;
33 | import com.joaquimley.faboptions.sample.R;
34 |
35 | import static com.joaquimley.faboptions.sample.R.id.toolbar;
36 |
37 | /**
38 | * Faboptions sample via XML {@see R.layout.activity_sample_xml}
39 | */
40 |
41 | public class XmlSampleActivity extends AppCompatActivity implements View.OnClickListener {
42 |
43 | static {
44 | AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
45 | }
46 |
47 | private Toolbar mToolbar;
48 | private FabOptions mFabOptions;
49 |
50 | public static void start(Context context) {
51 | context.startActivity(new Intent(context, XmlSampleActivity.class));
52 | }
53 |
54 | @Override
55 | protected void onCreate(Bundle savedInstanceState) {
56 | super.onCreate(savedInstanceState);
57 | setContentView(R.layout.activity_sample_xml);
58 | mToolbar = findViewById(toolbar);
59 | mToolbar.setTitle(getString(R.string.title_activity_xml));
60 | setSupportActionBar(mToolbar);
61 |
62 | mFabOptions = findViewById(R.id.fab_options);
63 | mFabOptions.setOnClickListener(this);
64 | }
65 |
66 | @Override
67 | public void onClick(View view) {
68 | switch (view.getId()) {
69 | case R.id.faboptions_favorite:
70 | mFabOptions.setButtonColor(R.id.faboptions_favorite, R.color.colorAccent);
71 | Toast.makeText(XmlSampleActivity.this, "Favorite", Toast.LENGTH_SHORT).show();
72 | break;
73 |
74 | case R.id.faboptions_textsms:
75 | mFabOptions.setButtonColor(R.id.faboptions_textsms, R.color.colorAccent);
76 | Toast.makeText(XmlSampleActivity.this, "Message", Toast.LENGTH_SHORT).show();
77 | break;
78 |
79 |
80 | case R.id.faboptions_download:
81 | mFabOptions.setButtonColor(R.id.faboptions_download, R.color.colorAccent);
82 | Toast.makeText(XmlSampleActivity.this, "Download", Toast.LENGTH_SHORT).show();
83 | break;
84 |
85 |
86 | case R.id.faboptions_share:
87 | mFabOptions.setButtonColor(R.id.faboptions_share, R.color.colorAccent);
88 | Toast.makeText(XmlSampleActivity.this, "Share", Toast.LENGTH_SHORT).show();
89 | break;
90 |
91 | default:
92 | // no-op
93 | }
94 | }
95 |
96 | @Override
97 | public boolean onCreateOptionsMenu(Menu menu) {
98 | MenuInflater inflater = getMenuInflater();
99 | inflater.inflate(R.menu.menu_activity_main, menu);
100 | return true;
101 | }
102 |
103 | @Override
104 | public boolean onOptionsItemSelected(MenuItem item) {
105 | switch (item.getItemId()) {
106 | case R.id.action_snackbar_test:
107 | Snackbar.make(mToolbar, getString(R.string.action_snackbar_test_message),
108 | Snackbar.LENGTH_LONG).show();
109 | return true;
110 |
111 | case R.id.action_change_activity:
112 | JavaSampleActivity.start(XmlSampleActivity.this);
113 | finish();
114 | return true;
115 | default:
116 | return super.onOptionsItemSelected(item);
117 | }
118 | }
119 | }
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_favorite.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
22 |
25 |
26 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_file_download.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
22 |
25 |
26 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_share.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
22 |
25 |
26 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_textsms.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
22 |
25 |
26 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/activity_sample_java.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
25 |
26 |
30 |
31 |
37 |
38 |
39 |
47 |
48 |
53 |
54 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/activity_sample_xml.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
24 |
25 |
29 |
30 |
36 |
37 |
38 |
46 |
47 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/sample/src/main/res/menu/menu_activity_main.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
33 |
--------------------------------------------------------------------------------
/sample/src/main/res/menu/menu_faboptions.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JoaquimLey/faboptions/ac8a9f9dab7f5c8b757fba8f08cfe5bc7bd66fd9/sample/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JoaquimLey/faboptions/ac8a9f9dab7f5c8b757fba8f08cfe5bc7bd66fd9/sample/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JoaquimLey/faboptions/ac8a9f9dab7f5c8b757fba8f08cfe5bc7bd66fd9/sample/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JoaquimLey/faboptions/ac8a9f9dab7f5c8b757fba8f08cfe5bc7bd66fd9/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JoaquimLey/faboptions/ac8a9f9dab7f5c8b757fba8f08cfe5bc7bd66fd9/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
25 |
26 |
--------------------------------------------------------------------------------
/sample/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
21 | 64dp
22 |
23 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 | #673AB7
20 | #4527A0
21 | #E84F3E
22 |
23 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 | 180dp
19 | 16dp
20 | 16dp
21 |
22 | 16dp
23 | 16dp
24 |
25 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 | FabOptions sample
19 | FabOptions: XML
20 | FabOptions: Java
21 | joaquimley.com
22 |
23 | Change Activity
24 | FabOptions reacts to me
25 | Snackbar test
26 |
27 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 |
26 |
27 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) Joaquim Ley 2016. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | include ':sample', ':library'
18 |
--------------------------------------------------------------------------------