├── .gitignore ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── es │ │ └── dmoral │ │ └── tinylist │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── es │ │ │ └── dmoral │ │ │ └── tinylist │ │ │ ├── TinyListApplication.java │ │ │ ├── activities │ │ │ ├── EditListActivity.java │ │ │ └── MainActivity.java │ │ │ ├── adapters │ │ │ ├── ArchivedListsAdapter.java │ │ │ ├── ItemListAdapter.java │ │ │ └── SavedListsAdapter.java │ │ │ ├── fragments │ │ │ ├── ArchivedListsFragment.java │ │ │ ├── BaseFragment.java │ │ │ └── SavedListsFragment.java │ │ │ ├── helpers │ │ │ └── TinyListSQLHelper.java │ │ │ ├── models │ │ │ ├── Task.java │ │ │ └── TaskList.java │ │ │ └── widgets │ │ │ └── FABScrollBehavior.java │ └── res │ │ ├── drawable-hdpi-v23 │ │ └── window_background_statusbar_toolbar_tab.9.png │ │ ├── drawable-hdpi │ │ ├── ic_archive_black_48dp.png │ │ ├── ic_close_black_48dp.png │ │ ├── ic_delete_black_48dp.png │ │ ├── ic_done_white_48dp.png │ │ ├── ic_palette_white_48dp.png │ │ ├── ic_playlist_add_white_48dp.png │ │ ├── ic_share_black_48dp.png │ │ ├── ic_unarchive_black_48dp.png │ │ └── window_background_statusbar_toolbar_tab.9.png │ │ ├── drawable-mdpi-v23 │ │ └── window_background_statusbar_toolbar_tab.9.png │ │ ├── drawable-mdpi │ │ ├── ic_archive_black_48dp.png │ │ ├── ic_close_black_48dp.png │ │ ├── ic_delete_black_48dp.png │ │ ├── ic_done_white_48dp.png │ │ ├── ic_palette_white_48dp.png │ │ ├── ic_playlist_add_white_48dp.png │ │ ├── ic_share_black_48dp.png │ │ ├── ic_unarchive_black_48dp.png │ │ └── window_background_statusbar_toolbar_tab.9.png │ │ ├── drawable-sw600dp-hdpi-v23 │ │ └── window_background_statusbar_toolbar_tab.9.png │ │ ├── drawable-sw600dp-hdpi │ │ └── window_background_statusbar_toolbar_tab.9.png │ │ ├── drawable-sw600dp-mdpi-v23 │ │ └── window_background_statusbar_toolbar_tab.9.png │ │ ├── drawable-sw600dp-mdpi │ │ └── window_background_statusbar_toolbar_tab.9.png │ │ ├── drawable-sw600dp-xhdpi-v23 │ │ └── window_background_statusbar_toolbar_tab.9.png │ │ ├── drawable-sw600dp-xhdpi │ │ └── window_background_statusbar_toolbar_tab.9.png │ │ ├── drawable-sw600dp-xxhdpi-v23 │ │ └── window_background_statusbar_toolbar_tab.9.png │ │ ├── drawable-sw600dp-xxhdpi │ │ └── window_background_statusbar_toolbar_tab.9.png │ │ ├── drawable-sw600dp-xxxhdpi-v23 │ │ └── window_background_statusbar_toolbar_tab.9.png │ │ ├── drawable-sw600dp-xxxhdpi │ │ └── window_background_statusbar_toolbar_tab.9.png │ │ ├── drawable-v21 │ │ └── add_item_bg.xml │ │ ├── drawable-xhdpi-v23 │ │ └── window_background_statusbar_toolbar_tab.9.png │ │ ├── drawable-xhdpi │ │ ├── ic_archive_black_48dp.png │ │ ├── ic_close_black_48dp.png │ │ ├── ic_delete_black_48dp.png │ │ ├── ic_done_white_48dp.png │ │ ├── ic_palette_white_48dp.png │ │ ├── ic_playlist_add_white_48dp.png │ │ ├── ic_share_black_48dp.png │ │ ├── ic_unarchive_black_48dp.png │ │ └── window_background_statusbar_toolbar_tab.9.png │ │ ├── drawable-xxhdpi-v23 │ │ └── window_background_statusbar_toolbar_tab.9.png │ │ ├── drawable-xxhdpi │ │ ├── ic_archive_black_48dp.png │ │ ├── ic_close_black_48dp.png │ │ ├── ic_delete_black_48dp.png │ │ ├── ic_done_white_48dp.png │ │ ├── ic_palette_white_48dp.png │ │ ├── ic_playlist_add_white_48dp.png │ │ ├── ic_share_black_48dp.png │ │ ├── ic_unarchive_black_48dp.png │ │ └── window_background_statusbar_toolbar_tab.9.png │ │ ├── drawable-xxxhdpi-v23 │ │ └── window_background_statusbar_toolbar_tab.9.png │ │ ├── drawable-xxxhdpi │ │ ├── ic_archive_black_48dp.png │ │ ├── ic_close_black_48dp.png │ │ ├── ic_delete_black_48dp.png │ │ ├── ic_done_white_48dp.png │ │ ├── ic_palette_white_48dp.png │ │ ├── ic_playlist_add_white_48dp.png │ │ ├── ic_share_black_48dp.png │ │ ├── ic_unarchive_black_48dp.png │ │ └── window_background_statusbar_toolbar_tab.9.png │ │ ├── drawable │ │ └── add_item_bg.xml │ │ ├── layout │ │ ├── activity_edit_list.xml │ │ ├── activity_main.xml │ │ ├── archived_lists_cardview.xml │ │ ├── fragment_archived_lists.xml │ │ ├── fragment_saved_lists.xml │ │ ├── item_list_layout.xml │ │ └── saved_list_cardview.xml │ │ ├── menu │ │ ├── edit_menu.xml │ │ └── menu_main.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-es │ │ └── strings.xml │ │ ├── values-v21 │ │ └── styles.xml │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── ids.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── es │ └── dmoral │ └── tinylist │ └── ExampleUnitTest.java ├── art ├── 512icon.png ├── big_image.png ├── device-2016-01-17-160529.png ├── device-2016-01-17-160641.png └── device-2016-01-17-160659.png ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .idea 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TinyList 2 | Get it on Google Play 3 |

Just a tiny list app.

4 | 5 | # Images 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.2" 6 | 7 | defaultConfig { 8 | applicationId "es.dmoral.tinylist" 9 | minSdkVersion 15 10 | targetSdkVersion 25 11 | versionCode 121 12 | versionName "1.2.1" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | 21 | lintOptions { 22 | disable 'InvalidPackage' 23 | } 24 | } 25 | 26 | dependencies { 27 | compile fileTree(dir: 'libs', include: ['*.jar']) 28 | testCompile 'junit:junit:4.12' 29 | compile 'com.android.support:appcompat-v7:25.1.0' 30 | compile 'com.android.support:design:25.1.0' 31 | compile 'com.android.support:support-v4:25.1.0' 32 | compile 'com.jakewharton:butterknife:7.0.1' 33 | compile 'com.android.support:cardview-v7:25.1.0' 34 | compile 'com.android.support:recyclerview-v7:25.1.0' 35 | compile 'com.thebluealliance:spectrum:0.2.1' 36 | compile 'com.afollestad.material-dialogs:core:0.9.1.0' 37 | } 38 | -------------------------------------------------------------------------------- /app/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 /home/grender/Software/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 | -------------------------------------------------------------------------------- /app/src/androidTest/java/es/dmoral/tinylist/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package es.dmoral.tinylist; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/java/es/dmoral/tinylist/TinyListApplication.java: -------------------------------------------------------------------------------- 1 | package es.dmoral.tinylist; 2 | 3 | import android.app.Application; 4 | 5 | /** 6 | * Created by grender on 13/01/16. 7 | *

8 | * This class represents the entire Application's context (Android best practices) 9 | */ 10 | public class TinyListApplication extends Application { 11 | 12 | @Override 13 | public void onCreate() { 14 | super.onCreate(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/es/dmoral/tinylist/activities/EditListActivity.java: -------------------------------------------------------------------------------- 1 | package es.dmoral.tinylist.activities; 2 | 3 | import android.graphics.Color; 4 | import android.os.Bundle; 5 | import android.support.annotation.ColorInt; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.support.v7.widget.LinearLayoutManager; 8 | import android.support.v7.widget.RecyclerView; 9 | import android.text.Editable; 10 | import android.text.TextWatcher; 11 | import android.view.Menu; 12 | import android.view.MenuItem; 13 | import android.view.View; 14 | import android.widget.Button; 15 | import android.widget.EditText; 16 | import android.widget.LinearLayout; 17 | 18 | import com.thebluealliance.spectrum.SpectrumDialog; 19 | 20 | import java.util.Calendar; 21 | 22 | import butterknife.Bind; 23 | import butterknife.ButterKnife; 24 | import es.dmoral.tinylist.R; 25 | import es.dmoral.tinylist.adapters.ItemListAdapter; 26 | import es.dmoral.tinylist.helpers.TinyListSQLHelper; 27 | import es.dmoral.tinylist.models.Task; 28 | import es.dmoral.tinylist.models.TaskList; 29 | 30 | public class EditListActivity extends AppCompatActivity { 31 | 32 | /* Intent to handle edit list action. */ 33 | public static final String INTENT_EDIT = "INTENT_EDIT"; 34 | @Bind(R.id.main_layout) 35 | LinearLayout mainLayout; 36 | @Bind(R.id.et_task_list_title) 37 | EditText etTaskTitle; 38 | @Bind(R.id.list_item_recyclerview) 39 | RecyclerView listItemRecyclerview; 40 | @Bind(R.id.add_item) 41 | Button addItem; 42 | private int selectedColor; 43 | private TaskList editingTaskList; 44 | 45 | @Override 46 | protected void onCreate(Bundle savedInstanceState) { 47 | super.onCreate(savedInstanceState); 48 | setContentView(R.layout.activity_edit_list); 49 | 50 | /* Getting the TaskList to edit */ 51 | if (getIntent().getExtras() != null) { 52 | this.editingTaskList = (TaskList) getIntent().getSerializableExtra(INTENT_EDIT); 53 | } 54 | 55 | setupViews(); 56 | setListeners(); 57 | } 58 | 59 | /** 60 | * Method used to set up the entire view, here we check if the user is adding 61 | * a new TaskList or editing an existing one. 62 | */ 63 | private void setupViews() { 64 | ButterKnife.bind(this); 65 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 66 | getSupportActionBar().setTitle(R.string.new_shopping_list); 67 | 68 | this.listItemRecyclerview.setLayoutManager(new LinearLayoutManager(this)); 69 | RecyclerView.Adapter mAdapter; 70 | if (this.editingTaskList == null) { 71 | mAdapter = new ItemListAdapter(this); 72 | this.listItemRecyclerview.setAdapter(mAdapter); 73 | this.selectedColor = Color.parseColor("#FFFFFF"); 74 | } else { 75 | mAdapter = new ItemListAdapter(editingTaskList.getTasks(), this); 76 | this.listItemRecyclerview.setAdapter(mAdapter); 77 | this.selectedColor = this.editingTaskList.getBackgroundColor(); 78 | this.etTaskTitle.setText(this.editingTaskList.getTitle()); 79 | this.mainLayout.setBackgroundColor(this.selectedColor); 80 | getSupportActionBar().setTitle(this.etTaskTitle.getText().toString()); 81 | } 82 | 83 | } 84 | 85 | private void setListeners() { 86 | this.etTaskTitle.addTextChangedListener(new TextWatcher() { 87 | @Override 88 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { 89 | 90 | } 91 | 92 | @Override 93 | public void onTextChanged(CharSequence s, int start, int before, int count) { 94 | /* Updating activity title as the user writes TaskList title. */ 95 | getSupportActionBar().setTitle(s.toString()); 96 | } 97 | 98 | @Override 99 | public void afterTextChanged(Editable s) { 100 | 101 | } 102 | }); 103 | 104 | this.addItem.setOnClickListener(new View.OnClickListener() { 105 | @Override 106 | public void onClick(View v) { 107 | /* Adding new task, scrolling and focusing it.*/ 108 | if (editingTaskList == null) 109 | ((ItemListAdapter) listItemRecyclerview.getAdapter()).addItem(new Task()); 110 | else 111 | ((ItemListAdapter) listItemRecyclerview.getAdapter()).addItem(new Task(editingTaskList.getTask_list_id())); 112 | listItemRecyclerview.scrollToPosition(listItemRecyclerview.getAdapter().getItemCount() - 1); 113 | ((ItemListAdapter) listItemRecyclerview.getAdapter()).setFocusedItem(listItemRecyclerview.getAdapter().getItemCount() - 1); 114 | } 115 | }); 116 | } 117 | 118 | @Override 119 | public boolean onCreateOptionsMenu(Menu menu) { 120 | getMenuInflater().inflate(R.menu.edit_menu, menu); 121 | return true; 122 | } 123 | 124 | @Override 125 | public boolean onOptionsItemSelected(MenuItem item) { 126 | 127 | switch (item.getItemId()) { 128 | case android.R.id.home: 129 | onBackPressed(); 130 | return true; 131 | case R.id.action_palette: 132 | handlePaletteAction(); 133 | return true; 134 | case R.id.action_done: 135 | saveTaskList(); 136 | return true; 137 | } 138 | 139 | return super.onOptionsItemSelected(item); 140 | } 141 | 142 | @Override 143 | public void onBackPressed() { 144 | super.onBackPressed(); 145 | } 146 | 147 | /** 148 | * When the user touch palette menu icon in order to change background color of the 149 | * future CardView. 150 | */ 151 | private void handlePaletteAction() { 152 | new SpectrumDialog.Builder(this) 153 | .setColors(R.array.card_colors) 154 | .setSelectedColor(this.selectedColor) 155 | .setDismissOnColorSelected(true) 156 | .setOnColorSelectedListener(new SpectrumDialog.OnColorSelectedListener() { 157 | @Override 158 | public void onColorSelected(boolean positiveResult, @ColorInt int color) { 159 | if (positiveResult) { 160 | selectedColor = color; 161 | mainLayout.setBackgroundColor(selectedColor); 162 | } 163 | 164 | } 165 | }).build().show(getSupportFragmentManager(), "SpectrumDialog"); 166 | } 167 | 168 | /** 169 | * Method created to save a new TaskList or editing an existing one. 170 | */ 171 | private void saveTaskList() { 172 | if (this.editingTaskList == null) { 173 | TinyListSQLHelper.getSqlHelper(this).addOrUpdateTaskList( 174 | new TaskList(-1, this.etTaskTitle.getText().toString(), Calendar.getInstance().getTime(), 175 | ((ItemListAdapter) listItemRecyclerview.getAdapter()).getTasks(), this.selectedColor, false) 176 | ); 177 | } else { 178 | this.editingTaskList.setTasks(((ItemListAdapter) listItemRecyclerview.getAdapter()).getTasks()); 179 | this.editingTaskList.setBackgroundColor(this.selectedColor); 180 | this.editingTaskList.setCreationDate(Calendar.getInstance().getTime()); 181 | this.editingTaskList.setTitle(this.etTaskTitle.getText().toString()); 182 | TinyListSQLHelper.getSqlHelper(this).addOrUpdateTaskList(this.editingTaskList); 183 | } 184 | 185 | onBackPressed(); 186 | } 187 | 188 | } 189 | -------------------------------------------------------------------------------- /app/src/main/java/es/dmoral/tinylist/activities/MainActivity.java: -------------------------------------------------------------------------------- 1 | package es.dmoral.tinylist.activities; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.annotation.NonNull; 6 | import android.support.design.widget.AppBarLayout; 7 | import android.support.design.widget.FloatingActionButton; 8 | import android.support.design.widget.TabLayout; 9 | import android.support.v4.app.Fragment; 10 | import android.support.v4.app.FragmentManager; 11 | import android.support.v4.app.FragmentPagerAdapter; 12 | import android.support.v4.view.ViewPager; 13 | import android.support.v7.app.AppCompatActivity; 14 | import android.support.v7.widget.Toolbar; 15 | import android.text.InputType; 16 | import android.text.SpannableString; 17 | import android.text.util.Linkify; 18 | import android.util.SparseArray; 19 | import android.view.Menu; 20 | import android.view.MenuItem; 21 | import android.view.View; 22 | 23 | import com.afollestad.materialdialogs.MaterialDialog; 24 | 25 | import java.util.ArrayList; 26 | 27 | import butterknife.Bind; 28 | import butterknife.ButterKnife; 29 | import es.dmoral.tinylist.R; 30 | import es.dmoral.tinylist.fragments.ArchivedListsFragment; 31 | import es.dmoral.tinylist.fragments.BaseFragment; 32 | import es.dmoral.tinylist.fragments.SavedListsFragment; 33 | import es.dmoral.tinylist.helpers.TinyListSQLHelper; 34 | import es.dmoral.tinylist.models.Task; 35 | import es.dmoral.tinylist.models.TaskList; 36 | 37 | public class MainActivity extends AppCompatActivity { 38 | 39 | @Bind(R.id.container) 40 | ViewPager viewPagerContainer; 41 | @Bind(R.id.fab) 42 | FloatingActionButton fab; 43 | @Bind(R.id.toolbar) 44 | Toolbar toolbar; 45 | @Bind(R.id.tabs) 46 | TabLayout tabLayout; 47 | @Bind(R.id.appbar) 48 | AppBarLayout appBarLayout; 49 | 50 | @Override 51 | protected void onCreate(Bundle savedInstanceState) { 52 | /* Changing to main theme (avoiding cold start). 53 | Thanks to "https://github.com/DreaminginCodeZH/MaterialColdStart" */ 54 | setTheme(R.style.AppTheme_NoActionBar); 55 | 56 | super.onCreate(savedInstanceState); 57 | setContentView(R.layout.activity_main); 58 | 59 | setupView(); 60 | setListeners(); 61 | } 62 | 63 | /** 64 | * Method used to set up the entire view, here we check if the user is adding 65 | * a new TaskList or editing an existing one. 66 | */ 67 | private void setupView() { 68 | ButterKnife.bind(this); 69 | setSupportActionBar(toolbar); 70 | getSupportActionBar().setLogo(R.mipmap.ic_launcher); 71 | 72 | viewPagerContainer.setAdapter(new SectionsPagerAdapter(getSupportFragmentManager())); 73 | 74 | tabLayout.setupWithViewPager(viewPagerContainer); 75 | 76 | } 77 | 78 | private void setListeners() { 79 | /* FAB handling the main action, adding a new TaskList */ 80 | fab.setOnClickListener(new View.OnClickListener() { 81 | @Override 82 | public void onClick(View view) { 83 | Intent intent = new Intent(MainActivity.this, EditListActivity.class); 84 | startActivity(intent); 85 | } 86 | }); 87 | appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() { 88 | @Override 89 | public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) { 90 | if (verticalOffset == 0) { //expanded 91 | fab.show(); 92 | } else if (Math.abs(verticalOffset) >= appBarLayout.getTotalScrollRange()) { //collapsed 93 | fab.hide(); 94 | } 95 | } 96 | }); 97 | viewPagerContainer.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { 98 | @Override 99 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 100 | 101 | } 102 | 103 | @Override 104 | public void onPageSelected(int position) { 105 | appBarLayout.setExpanded(true, true); 106 | } 107 | 108 | @Override 109 | public void onPageScrollStateChanged(int state) { 110 | /* If the user starts scrolling or dragging the ViewPager, the adapters will redraw 111 | * themselves in order to show recent changes. */ 112 | if (state != ViewPager.SCROLL_STATE_IDLE) { 113 | try { 114 | ((SectionsPagerAdapter) viewPagerContainer.getAdapter()) 115 | .getRegisteredFragment(viewPagerContainer.getCurrentItem()).redrawItems(); 116 | } catch (NullPointerException ignored) { 117 | } 118 | fab.show(); 119 | } 120 | } 121 | }); 122 | } 123 | 124 | public BaseFragment getCurrentVisibleFragment() { 125 | return (((SectionsPagerAdapter) 126 | viewPagerContainer.getAdapter()).getRegisteredFragment(viewPagerContainer.getCurrentItem())); 127 | } 128 | 129 | @Override 130 | public boolean onCreateOptionsMenu(Menu menu) { 131 | // Inflate the menu; 132 | getMenuInflater().inflate(R.menu.menu_main, menu); 133 | return true; 134 | } 135 | 136 | @Override 137 | public boolean onOptionsItemSelected(MenuItem item) { 138 | int id = item.getItemId(); 139 | 140 | switch (id) { 141 | case R.id.action_about: 142 | showAboutDialog(); 143 | break; 144 | case R.id.action_import: 145 | showImportListDialog(); 146 | break; 147 | } 148 | 149 | return super.onOptionsItemSelected(item); 150 | } 151 | 152 | /** 153 | * Just info about the app :-) 154 | */ 155 | private void showAboutDialog() { 156 | final SpannableString spannableString = new SpannableString(getString(R.string.about_msg)); 157 | Linkify.addLinks(spannableString, Linkify.ALL); 158 | 159 | new MaterialDialog.Builder(this) 160 | .positiveText(android.R.string.ok) 161 | .title(getString(R.string.app_name) + " " + getString(R.string.app_version)) 162 | .content(spannableString) 163 | .show(); 164 | } 165 | 166 | /** 167 | * Prompt to import a list. 168 | */ 169 | private void showImportListDialog() { 170 | final MaterialDialog importDialog = new MaterialDialog.Builder(this) 171 | .title(getString(R.string.action_import)) 172 | .content(R.string.import_list_msg) 173 | .negativeText(android.R.string.cancel) 174 | .inputRange(1, -1) 175 | .input(getString(R.string.import_list_hint), null, new MaterialDialog.InputCallback() { 176 | @Override 177 | public void onInput(@NonNull MaterialDialog dialog, CharSequence input) { 178 | importList(input.toString()); 179 | if (viewPagerContainer.getCurrentItem() == 0) 180 | ((SectionsPagerAdapter) 181 | viewPagerContainer.getAdapter()).getRegisteredFragment(0).redrawItems(); 182 | } 183 | }).build(); 184 | importDialog.getInputEditText().setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_LONG_MESSAGE); 185 | importDialog.getInputEditText().setSingleLine(false); 186 | importDialog.show(); 187 | } 188 | 189 | private void importList(String listToImport) { 190 | TaskList taskListToImport = new TaskList(); 191 | ArrayList tasksToImport = new ArrayList<>(); 192 | final String[] lines = listToImport.split("[\r\n]+"); 193 | for (int i = 0; i < lines.length; i++) { 194 | if (i == 0) 195 | if (!lines[0].contains(Task.DONE_TASK_MARK) 196 | && !lines[0].contains(Task.UNDONE_TASK_MARK)) { 197 | taskListToImport.setTitle(lines[0].trim()); 198 | continue; 199 | } 200 | Task taskToImport = new Task(); 201 | taskToImport.setIsChecked(lines[i].contains(Task.DONE_TASK_MARK)); 202 | lines[i] = lines[i].replace(Task.DONE_TASK_MARK, ""); 203 | lines[i] = lines[i].replace(Task.UNDONE_TASK_MARK, ""); 204 | taskToImport.setTask(lines[i].trim()); 205 | tasksToImport.add(taskToImport); 206 | 207 | } 208 | taskListToImport.setTasks(tasksToImport); 209 | TinyListSQLHelper.getSqlHelper(this).addOrUpdateTaskList(taskListToImport); 210 | } 211 | 212 | /** 213 | * A {@link FragmentPagerAdapter} that returns a fragment corresponding to 214 | * one of the sections/tabs/pages. 215 | */ 216 | public class SectionsPagerAdapter extends FragmentPagerAdapter { 217 | private SparseArray registeredFragments = new SparseArray<>(); 218 | 219 | public SectionsPagerAdapter(FragmentManager fm) { 220 | super(fm); 221 | } 222 | 223 | @Override 224 | public Fragment getItem(int position) { 225 | /* getItem is called to instantiate the fragment for the given page. */ 226 | switch (position) { 227 | case 0: 228 | SavedListsFragment savedListsFragment = SavedListsFragment.getInstance(); 229 | registeredFragments.put(0, savedListsFragment); 230 | return savedListsFragment; 231 | case 1: 232 | ArchivedListsFragment archivedListsFragment = ArchivedListsFragment.getInstance(); 233 | registeredFragments.put(1, archivedListsFragment); 234 | return archivedListsFragment; 235 | default: 236 | return new Fragment(); 237 | } 238 | 239 | } 240 | 241 | @Override 242 | public int getCount() { 243 | // Show 2 total pages (Saved lists and Archived lists). 244 | return 2; 245 | } 246 | 247 | public BaseFragment getRegisteredFragment(int position) { 248 | return registeredFragments.get(position); 249 | } 250 | 251 | @Override 252 | public CharSequence getPageTitle(int position) { 253 | switch (position) { 254 | case 0: 255 | return getString(R.string.saved_lists); 256 | case 1: 257 | return getString(R.string.archived_lists); 258 | } 259 | return null; 260 | } 261 | 262 | } 263 | 264 | } 265 | -------------------------------------------------------------------------------- /app/src/main/java/es/dmoral/tinylist/adapters/ArchivedListsAdapter.java: -------------------------------------------------------------------------------- 1 | package es.dmoral.tinylist.adapters; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.graphics.Paint; 6 | import android.support.annotation.NonNull; 7 | import android.support.v7.widget.CardView; 8 | import android.support.v7.widget.RecyclerView; 9 | import android.view.LayoutInflater; 10 | import android.view.View; 11 | import android.view.ViewGroup; 12 | import android.widget.ImageView; 13 | import android.widget.LinearLayout; 14 | import android.widget.TextView; 15 | 16 | import com.afollestad.materialdialogs.DialogAction; 17 | import com.afollestad.materialdialogs.MaterialDialog; 18 | 19 | import java.text.SimpleDateFormat; 20 | import java.util.ArrayList; 21 | 22 | import butterknife.Bind; 23 | import butterknife.ButterKnife; 24 | import es.dmoral.tinylist.R; 25 | import es.dmoral.tinylist.helpers.TinyListSQLHelper; 26 | import es.dmoral.tinylist.models.Task; 27 | import es.dmoral.tinylist.models.TaskList; 28 | 29 | /** 30 | * Created by grend on 13/01/2016. 31 | */ 32 | public class ArchivedListsAdapter extends RecyclerView.Adapter { 33 | 34 | private final ArrayList taskLists; 35 | private final Context context; 36 | @SuppressLint("SimpleDateFormat") 37 | private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 38 | 39 | public ArchivedListsAdapter(ArrayList taskLists, Context context) { 40 | this.taskLists = taskLists; 41 | this.context = context; 42 | } 43 | 44 | @Override 45 | public ArchivedListsAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 46 | return new ArchivedListsAdapter.ViewHolder( 47 | LayoutInflater.from(parent.getContext()).inflate(R.layout.archived_lists_cardview, parent, false)); 48 | } 49 | 50 | @Override 51 | public void onBindViewHolder(final ViewHolder holder, final int position) { 52 | holder.cardView.setCardBackgroundColor(taskLists.get(holder.getAdapterPosition()).getBackgroundColor()); 53 | holder.taskListTitle.setVisibility(View.VISIBLE); 54 | if (taskLists.get(holder.getAdapterPosition()).getTitle().isEmpty()) 55 | holder.taskListTitle.setVisibility(View.GONE); 56 | else 57 | holder.taskListTitle.setText(taskLists.get(holder.getAdapterPosition()).getTitle()); 58 | 59 | /* Removing existing views (to prevent weird behaviours as the RecyclerView recycle views, 60 | * and adding Tasks (List items) to the CardView. */ 61 | holder.taskContainer.removeAllViews(); 62 | for (Task task : taskLists.get(position).getTasks()) { 63 | TextView taskDescription = new TextView(context); 64 | taskDescription.setText(task.getTask()); 65 | taskDescription.setTextSize(18f); 66 | if (task.isChecked()) { 67 | taskDescription.setPaintFlags(taskDescription.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); 68 | taskDescription.setAlpha(0.3f); 69 | } 70 | holder.taskContainer.addView(taskDescription); 71 | } 72 | 73 | holder.taskListDate.setText(dateFormat.format(taskLists.get(holder.getAdapterPosition()).getCreationDate())); 74 | holder.unarchiveButton.setOnClickListener(new View.OnClickListener() { 75 | @Override 76 | public void onClick(View v) { 77 | final TaskList taskListToArchive = taskLists.get(holder.getAdapterPosition()); 78 | taskListToArchive.setIsArchived(false); 79 | TinyListSQLHelper.getSqlHelper(context).addOrUpdateTaskList(taskListToArchive); 80 | removeItem(holder.getAdapterPosition()); 81 | } 82 | }); 83 | 84 | /* Handling delete action and ensuring the user really wants to delete that archived list. */ 85 | holder.deleteButton.setOnClickListener(new View.OnClickListener() { 86 | @Override 87 | public void onClick(View v) { 88 | new MaterialDialog.Builder(context) 89 | .positiveText(android.R.string.ok) 90 | .onPositive(new MaterialDialog.SingleButtonCallback() { 91 | @Override 92 | public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { 93 | TinyListSQLHelper.getSqlHelper(context).deleteTaskList(taskLists.get(holder.getAdapterPosition()).getTask_list_id()); 94 | removeItem(holder.getAdapterPosition()); 95 | } 96 | }) 97 | .negativeText(android.R.string.cancel) 98 | .title(context.getString(R.string.delete_title) + taskLists.get(holder.getAdapterPosition()).getTitle()) 99 | .content(context.getString(R.string.delete_msg_1) + taskLists.get(holder.getAdapterPosition()).getTitle() + context.getString(R.string.delete_msg_2)) 100 | .show(); 101 | } 102 | }); 103 | } 104 | 105 | @Override 106 | public int getItemCount() { 107 | return this.taskLists.size(); 108 | } 109 | 110 | /** 111 | * This method removes an item and notifies their removal and notifies the change of 112 | * the views below the removed item. 113 | * 114 | * @param position position of the item to remove 115 | */ 116 | public void removeItem(int position) { 117 | this.taskLists.remove(position); 118 | notifyItemRemoved(position); 119 | notifyItemRangeChanged(position, getItemCount() - position); 120 | } 121 | 122 | /** 123 | * Replaces all items with new ones. 124 | * 125 | * @param newTaskLists items to replace. 126 | */ 127 | public void replaceWith(ArrayList newTaskLists) { 128 | this.taskLists.clear(); 129 | this.taskLists.addAll(newTaskLists); 130 | notifyDataSetChanged(); 131 | } 132 | 133 | /** 134 | * This method returns the item at the desired position. 135 | * 136 | * @param position position of the item to retrieve 137 | * @return retrieved item 138 | */ 139 | public TaskList getItem(int position) { 140 | return this.taskLists.get(position); 141 | } 142 | 143 | /** 144 | * Using ViewHolder pattern (combined with ButterKnife library 145 | * as in the rest of the app (http://jakewharton.github.io/butterknife/)) 146 | */ 147 | public static class ViewHolder extends RecyclerView.ViewHolder { 148 | 149 | @Bind(R.id.task_archived_list_title) 150 | TextView taskListTitle; 151 | @Bind(R.id.task_archived_container) 152 | LinearLayout taskContainer; 153 | @Bind(R.id.task_archived_list_date) 154 | TextView taskListDate; 155 | @Bind(R.id.card_view_archived_lists) 156 | CardView cardView; 157 | @Bind(R.id.unarchive) 158 | ImageView unarchiveButton; 159 | @Bind(R.id.delete) 160 | ImageView deleteButton; 161 | 162 | public ViewHolder(View itemView) { 163 | super(itemView); 164 | ButterKnife.bind(this, itemView); 165 | } 166 | } 167 | 168 | } 169 | -------------------------------------------------------------------------------- /app/src/main/java/es/dmoral/tinylist/adapters/ItemListAdapter.java: -------------------------------------------------------------------------------- 1 | package es.dmoral.tinylist.adapters; 2 | 3 | import android.content.Context; 4 | import android.graphics.Paint; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.text.Editable; 7 | import android.text.TextWatcher; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | import android.widget.CheckBox; 12 | import android.widget.CompoundButton; 13 | import android.widget.EditText; 14 | import android.widget.ImageView; 15 | 16 | import java.util.ArrayList; 17 | 18 | import butterknife.Bind; 19 | import butterknife.ButterKnife; 20 | import es.dmoral.tinylist.R; 21 | import es.dmoral.tinylist.helpers.TinyListSQLHelper; 22 | import es.dmoral.tinylist.models.Task; 23 | 24 | /** 25 | * Created by grend on 14/01/2016. 26 | */ 27 | public class ItemListAdapter extends RecyclerView.Adapter { 28 | 29 | private final ArrayList tasks; 30 | /* This is the position of the last added item, which needs to be focused. */ 31 | private int focusedItem; 32 | private Context context; 33 | 34 | public ItemListAdapter(Context context) { 35 | this.context = context; 36 | this.tasks = new ArrayList<>(); 37 | } 38 | 39 | public ItemListAdapter(ArrayList tasks, Context context) { 40 | this.context = context; 41 | this.tasks = tasks; 42 | } 43 | 44 | @Override 45 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 46 | return new ItemListAdapter.ViewHolder( 47 | LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list_layout, parent, false), new CustomEditTextListener(), 48 | new CustomCheckboxListener()); 49 | } 50 | 51 | @Override 52 | public void onBindViewHolder(final ViewHolder holder, final int position) { 53 | holder.customEditTextListener.updatePosition(holder.getAdapterPosition()); 54 | holder.customCheckboxListener.updatePosition(holder.getAdapterPosition()); 55 | holder.customCheckboxListener.updatePaintFlags(holder.itemDesc); 56 | holder.cbItem.setChecked(this.tasks.get(position).isChecked()); 57 | holder.itemDesc.setText(this.tasks.get(position).getTask()); 58 | holder.removeItem.setOnClickListener(new View.OnClickListener() { 59 | @Override 60 | public void onClick(View v) { 61 | removeItem(holder.getAdapterPosition()); 62 | } 63 | }); 64 | if (this.focusedItem == position) 65 | holder.itemDesc.requestFocus(); 66 | } 67 | 68 | @Override 69 | public int getItemCount() { 70 | return this.tasks.size(); 71 | } 72 | 73 | /** 74 | * This method sets the focusedItem (see its description to know more) 75 | * 76 | * @param position position of the focused item. 77 | */ 78 | public void setFocusedItem(int position) { 79 | this.focusedItem = position; 80 | } 81 | 82 | /** 83 | * Adds a new item to the list. 84 | * 85 | * @param task item to add 86 | */ 87 | public void addItem(Task task) { 88 | this.tasks.add(task); 89 | notifyItemInserted(tasks.size() - 1); 90 | } 91 | 92 | /** 93 | * This method removes an item and notifies their removal and notifies the change of 94 | * the views below the removed item. 95 | * 96 | * @param position position of the item to remove 97 | */ 98 | public void removeItem(int position) { 99 | TinyListSQLHelper.getSqlHelper(context).deleteTask(this.tasks.get(position).getTask_id()); 100 | this.tasks.remove(position); 101 | notifyItemRemoved(position); 102 | notifyItemRangeChanged(position, getItemCount() - position); 103 | } 104 | 105 | /** 106 | * This method returns all tasks. 107 | * 108 | * @return tasks. 109 | */ 110 | public ArrayList getTasks() { 111 | return this.tasks; 112 | } 113 | 114 | public static class ViewHolder extends RecyclerView.ViewHolder { 115 | 116 | public CustomEditTextListener customEditTextListener; 117 | public CustomCheckboxListener customCheckboxListener; 118 | @Bind(R.id.cb_item) 119 | CheckBox cbItem; 120 | @Bind(R.id.item_desc) 121 | EditText itemDesc; 122 | @Bind(R.id.remove_item) 123 | ImageView removeItem; 124 | 125 | public ViewHolder(View itemView, CustomEditTextListener customEditTextListener, 126 | CustomCheckboxListener customCheckboxListener) { 127 | super(itemView); 128 | ButterKnife.bind(this, itemView); 129 | this.customEditTextListener = customEditTextListener; 130 | this.itemDesc.addTextChangedListener(customEditTextListener); 131 | this.customCheckboxListener = customCheckboxListener; 132 | this.cbItem.setOnCheckedChangeListener(customCheckboxListener); 133 | } 134 | } 135 | 136 | /** 137 | * This class is used to maintain updated the Task's ArrayList as the users changes the information. 138 | */ 139 | private class CustomEditTextListener implements TextWatcher { 140 | 141 | private int position; 142 | 143 | public void updatePosition(int position) { 144 | this.position = position; 145 | } 146 | 147 | @Override 148 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { 149 | 150 | } 151 | 152 | @Override 153 | public void onTextChanged(CharSequence s, int start, int before, int count) { 154 | tasks.get(position).setTask(s.toString()); 155 | } 156 | 157 | @Override 158 | public void afterTextChanged(Editable s) { 159 | 160 | } 161 | } 162 | 163 | /** 164 | * This class is used to maintain updated the Task's ArrayList as the users changes the information. 165 | */ 166 | private class CustomCheckboxListener implements CompoundButton.OnCheckedChangeListener { 167 | 168 | private int position; 169 | private EditText currentEditText; 170 | 171 | public void updatePosition(int position) { 172 | this.position = position; 173 | } 174 | 175 | public void updatePaintFlags(EditText currentEditText) { 176 | this.currentEditText = currentEditText; 177 | } 178 | 179 | @Override 180 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 181 | tasks.get(position).setIsChecked(isChecked); 182 | 183 | if (isChecked) 184 | this.currentEditText.setPaintFlags(this.currentEditText.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); 185 | else 186 | this.currentEditText.setPaintFlags(this.currentEditText.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG)); 187 | } 188 | } 189 | 190 | } 191 | -------------------------------------------------------------------------------- /app/src/main/java/es/dmoral/tinylist/adapters/SavedListsAdapter.java: -------------------------------------------------------------------------------- 1 | package es.dmoral.tinylist.adapters; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.graphics.Paint; 7 | import android.support.v7.widget.CardView; 8 | import android.support.v7.widget.RecyclerView; 9 | import android.view.ContextMenu; 10 | import android.view.LayoutInflater; 11 | import android.view.Menu; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | import android.widget.ImageView; 15 | import android.widget.LinearLayout; 16 | import android.widget.TextView; 17 | 18 | import java.text.SimpleDateFormat; 19 | import java.util.ArrayList; 20 | 21 | import butterknife.Bind; 22 | import butterknife.ButterKnife; 23 | import es.dmoral.tinylist.R; 24 | import es.dmoral.tinylist.activities.EditListActivity; 25 | import es.dmoral.tinylist.activities.MainActivity; 26 | import es.dmoral.tinylist.fragments.SavedListsFragment; 27 | import es.dmoral.tinylist.helpers.TinyListSQLHelper; 28 | import es.dmoral.tinylist.models.Task; 29 | import es.dmoral.tinylist.models.TaskList; 30 | 31 | /** 32 | * Created by grend on 13/01/2016. 33 | */ 34 | public class SavedListsAdapter extends RecyclerView.Adapter { 35 | 36 | private final ArrayList taskLists; 37 | private final Context context; 38 | @SuppressLint("SimpleDateFormat") 39 | private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 40 | /* Cached item to restore (if user wants to), used when an item is removed. */ 41 | private TaskList cachedItem; 42 | private int contextMenuSelectedPosition; 43 | 44 | public SavedListsAdapter(ArrayList taskLists, Context context) { 45 | this.taskLists = taskLists; 46 | this.context = context; 47 | } 48 | 49 | @Override 50 | public SavedListsAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 51 | return new SavedListsAdapter.ViewHolder( 52 | LayoutInflater.from(parent.getContext()).inflate(R.layout.saved_list_cardview, parent, false)); 53 | } 54 | 55 | @Override 56 | public void onBindViewHolder(final ViewHolder holder, final int position) { 57 | holder.cardView.setCardBackgroundColor(taskLists.get(holder.getAdapterPosition()).getBackgroundColor()); 58 | holder.taskListTitle.setVisibility(View.VISIBLE); 59 | if (taskLists.get(holder.getAdapterPosition()).getTitle().isEmpty()) 60 | holder.taskListTitle.setVisibility(View.GONE); 61 | else 62 | holder.taskListTitle.setText(taskLists.get(holder.getAdapterPosition()).getTitle()); 63 | 64 | holder.taskContainer.removeAllViews(); 65 | for (Task task : taskLists.get(holder.getAdapterPosition()).getTasks()) { 66 | TextView taskDescription = new TextView(context); 67 | taskDescription.setText(task.getTask()); 68 | taskDescription.setTextSize(18f); 69 | if (task.isChecked()) { 70 | taskDescription.setPaintFlags(taskDescription.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); 71 | taskDescription.setAlpha(0.3f); 72 | } 73 | holder.taskContainer.addView(taskDescription); 74 | } 75 | holder.taskListDate.setText(dateFormat.format(taskLists.get(holder.getAdapterPosition()).getCreationDate())); 76 | holder.cardView.setOnClickListener(new View.OnClickListener() { 77 | @Override 78 | public void onClick(View v) { 79 | Intent intent = new Intent(context, EditListActivity.class); 80 | intent.putExtra(EditListActivity.INTENT_EDIT, taskLists.get(holder.getAdapterPosition())); 81 | context.startActivity(intent); 82 | } 83 | }); 84 | holder.cardView.setOnLongClickListener(new View.OnLongClickListener() { 85 | @Override 86 | public boolean onLongClick(View view) { 87 | setContextMenuSelectedPosition(holder.getAdapterPosition()); 88 | return false; 89 | } 90 | }); 91 | holder.archiveButton.setOnClickListener(new View.OnClickListener() { 92 | @Override 93 | public void onClick(View v) { 94 | final TaskList taskListToArchive = taskLists.get(holder.getAdapterPosition()); 95 | taskListToArchive.setIsArchived(true); 96 | TinyListSQLHelper.getSqlHelper(context).addOrUpdateTaskList(taskListToArchive); 97 | removeItem(holder.getAdapterPosition()); 98 | ((SavedListsFragment) ((MainActivity) context).getCurrentVisibleFragment()).undoSnackbar(); 99 | } 100 | }); 101 | holder.shareButton.setOnClickListener(new View.OnClickListener() { 102 | @Override 103 | public void onClick(View view) { 104 | String shareIntentText = ""; 105 | Intent shareIntent = new Intent(Intent.ACTION_SEND); 106 | shareIntentText += taskLists.get(holder.getAdapterPosition()).getTitle() + "\n\n"; 107 | for (Task task : taskLists.get(holder.getAdapterPosition()).getTasks()) { 108 | shareIntentText += (task.isChecked() ? Task.DONE_TASK_MARK : Task.UNDONE_TASK_MARK) 109 | + " " + task.getTask() + "\n"; 110 | } 111 | shareIntent.setType("text/plain"); 112 | shareIntent.putExtra(Intent.EXTRA_TEXT, shareIntentText.trim()); 113 | context.startActivity(Intent.createChooser(shareIntent, context.getString(R.string.share_with))); 114 | } 115 | }); 116 | } 117 | 118 | @Override 119 | public int getItemCount() { 120 | return this.taskLists.size(); 121 | } 122 | 123 | /** 124 | * This method removes an item and notifies their removal and notifies the change of 125 | * the views below the removed item. 126 | * 127 | * @param position position of the item to remove 128 | */ 129 | public void removeItem(int position) { 130 | this.cachedItem = this.taskLists.get(position); 131 | this.taskLists.remove(position); 132 | notifyItemRemoved(position); 133 | notifyItemRangeChanged(position, getItemCount() - position); 134 | } 135 | 136 | /** 137 | * Replaces all items with new ones. 138 | * 139 | * @param newTaskLists items to replace. 140 | */ 141 | public void replaceWith(ArrayList newTaskLists) { 142 | this.taskLists.clear(); 143 | this.taskLists.addAll(newTaskLists); 144 | notifyDataSetChanged(); 145 | } 146 | 147 | /** 148 | * Get item at the desired position. 149 | * 150 | * @param position desired position 151 | * @return item at the desired position. 152 | */ 153 | public TaskList getItem(int position) { 154 | return this.taskLists.get(position); 155 | } 156 | 157 | /** 158 | * This method returns the cached item (see its description to know more) 159 | * 160 | * @return cached item. 161 | */ 162 | public TaskList getCachedItem() { 163 | return this.cachedItem; 164 | } 165 | 166 | /** 167 | * Sets the cached item (see its description to know more) 168 | * 169 | * @param item cached item. 170 | */ 171 | public void setCachedItem(TaskList item) { 172 | this.cachedItem = item; 173 | } 174 | 175 | public int getContextMenuSelectedPosition() { 176 | return contextMenuSelectedPosition; 177 | } 178 | 179 | public void setContextMenuSelectedPosition(int contextMenuSelectedPosition) { 180 | this.contextMenuSelectedPosition = contextMenuSelectedPosition; 181 | } 182 | 183 | @Override 184 | public void onViewRecycled(ViewHolder holder) { 185 | holder.cardView.setOnLongClickListener(null); 186 | super.onViewRecycled(holder); 187 | } 188 | 189 | public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnCreateContextMenuListener { 190 | 191 | @Bind(R.id.task_list_title) 192 | TextView taskListTitle; 193 | @Bind(R.id.task_container) 194 | LinearLayout taskContainer; 195 | @Bind(R.id.task_list_date) 196 | TextView taskListDate; 197 | @Bind(R.id.card_view_saved_lists) 198 | CardView cardView; 199 | @Bind(R.id.archive) 200 | ImageView archiveButton; 201 | @Bind(R.id.share) 202 | ImageView shareButton; 203 | 204 | public ViewHolder(View itemView) { 205 | super(itemView); 206 | ButterKnife.bind(this, itemView); 207 | itemView.setOnCreateContextMenuListener(this); 208 | } 209 | 210 | @Override 211 | public void onCreateContextMenu(ContextMenu contextMenu, View view, ContextMenu.ContextMenuInfo contextMenuInfo) { 212 | contextMenu.add(Menu.NONE, R.id.contextual_delete_menu, Menu.NONE, R.string.delete_list_cont_menu); 213 | } 214 | } 215 | 216 | } 217 | -------------------------------------------------------------------------------- /app/src/main/java/es/dmoral/tinylist/fragments/ArchivedListsFragment.java: -------------------------------------------------------------------------------- 1 | package es.dmoral.tinylist.fragments; 2 | 3 | 4 | import android.os.Bundle; 5 | import android.support.v7.widget.LinearLayoutManager; 6 | import android.support.v7.widget.RecyclerView; 7 | import android.support.v7.widget.helper.ItemTouchHelper; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | 12 | import butterknife.Bind; 13 | import butterknife.ButterKnife; 14 | import es.dmoral.tinylist.R; 15 | import es.dmoral.tinylist.adapters.ArchivedListsAdapter; 16 | import es.dmoral.tinylist.helpers.TinyListSQLHelper; 17 | import es.dmoral.tinylist.models.TaskList; 18 | 19 | public class ArchivedListsFragment extends BaseFragment { 20 | 21 | @Bind(R.id.archived_list_recycler_view) 22 | RecyclerView archivedListsRecyclerView; 23 | 24 | public ArchivedListsFragment() { 25 | // Required empty public constructor 26 | } 27 | 28 | /** 29 | * Using Singleton pattern (Android best practices) 30 | * 31 | * @return an instance of the fragment 32 | */ 33 | public static ArchivedListsFragment getInstance() { 34 | return new ArchivedListsFragment(); 35 | } 36 | 37 | @Override 38 | public void onCreate(Bundle savedInstanceState) { 39 | super.onCreate(savedInstanceState); 40 | } 41 | 42 | @Override 43 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 44 | Bundle savedInstanceState) { 45 | // Inflate the layout for this fragment 46 | View view = inflater.inflate(R.layout.fragment_archived_lists, container, false); 47 | ButterKnife.bind(this, view); 48 | return view; 49 | } 50 | 51 | @Override 52 | public void onViewCreated(View view, Bundle savedInstanceState) { 53 | super.onViewCreated(view, savedInstanceState); 54 | setupViews(); 55 | } 56 | 57 | /** 58 | * Method used to set up the entire view, here we check if the user is adding 59 | * a new TaskList or editing an existing one. 60 | */ 61 | @Override 62 | void setupViews() { 63 | archivedListsRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); 64 | RecyclerView.Adapter mAdapter = new ArchivedListsAdapter(TinyListSQLHelper.getSqlHelper(getActivity()).getTaskLists(true), getActivity()); 65 | archivedListsRecyclerView.setAdapter(mAdapter); 66 | ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) { 67 | @Override 68 | public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { 69 | return false; 70 | } 71 | 72 | @Override 73 | public void onSwiped(final RecyclerView.ViewHolder viewHolder, int direction) { 74 | /* Unarchiving the swiped item. */ 75 | final TaskList taskListToArchive = ((ArchivedListsAdapter) archivedListsRecyclerView.getAdapter()).getItem(viewHolder.getAdapterPosition()); 76 | taskListToArchive.setIsArchived(false); 77 | TinyListSQLHelper.getSqlHelper(getActivity()).addOrUpdateTaskList(taskListToArchive); 78 | ((ArchivedListsAdapter) archivedListsRecyclerView.getAdapter()).removeItem(viewHolder.getAdapterPosition()); 79 | } 80 | }; 81 | new ItemTouchHelper(simpleItemTouchCallback).attachToRecyclerView(archivedListsRecyclerView); 82 | } 83 | 84 | /** 85 | * Method used to redraw the items inside the main RecyclerView 86 | */ 87 | @Override 88 | public void redrawItems() { 89 | ((ArchivedListsAdapter) archivedListsRecyclerView.getAdapter()).replaceWith(TinyListSQLHelper.getSqlHelper(getActivity()).getTaskLists(true)); 90 | } 91 | 92 | @Override 93 | public void onStart() { 94 | super.onStart(); 95 | /* Redraw items when this fragment is first viewed by the user. */ 96 | redrawItems(); 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /app/src/main/java/es/dmoral/tinylist/fragments/BaseFragment.java: -------------------------------------------------------------------------------- 1 | package es.dmoral.tinylist.fragments; 2 | 3 | import android.support.v4.app.Fragment; 4 | 5 | /** 6 | * Created by grender on 16/12/16. 7 | */ 8 | 9 | public abstract class BaseFragment extends Fragment { 10 | abstract void setupViews(); 11 | 12 | public abstract void redrawItems(); 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/es/dmoral/tinylist/fragments/SavedListsFragment.java: -------------------------------------------------------------------------------- 1 | package es.dmoral.tinylist.fragments; 2 | 3 | 4 | import android.os.Bundle; 5 | import android.support.design.widget.Snackbar; 6 | import android.support.v7.widget.LinearLayoutManager; 7 | import android.support.v7.widget.RecyclerView; 8 | import android.support.v7.widget.helper.ItemTouchHelper; 9 | import android.view.LayoutInflater; 10 | import android.view.MenuItem; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | 14 | import butterknife.Bind; 15 | import butterknife.ButterKnife; 16 | import es.dmoral.tinylist.R; 17 | import es.dmoral.tinylist.activities.MainActivity; 18 | import es.dmoral.tinylist.adapters.SavedListsAdapter; 19 | import es.dmoral.tinylist.helpers.TinyListSQLHelper; 20 | import es.dmoral.tinylist.models.TaskList; 21 | import es.dmoral.tinylist.widgets.FABScrollBehavior; 22 | 23 | public class SavedListsFragment extends BaseFragment { 24 | 25 | @Bind(R.id.saved_list_recycler_view) 26 | RecyclerView savedListsRecyclerView; 27 | 28 | public SavedListsFragment() { 29 | // Required empty public constructor 30 | } 31 | 32 | /** 33 | * Using Singleton pattern (Android best practices) 34 | * 35 | * @return an instance of the fragment 36 | */ 37 | public static SavedListsFragment getInstance() { 38 | return new SavedListsFragment(); 39 | } 40 | 41 | @Override 42 | public void onCreate(Bundle savedInstanceState) { 43 | super.onCreate(savedInstanceState); 44 | } 45 | 46 | @Override 47 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 48 | Bundle savedInstanceState) { 49 | // Inflate the layout for this fragment 50 | View view = inflater.inflate(R.layout.fragment_saved_lists, container, false); 51 | ButterKnife.bind(this, view); 52 | return view; 53 | } 54 | 55 | @Override 56 | public void onViewCreated(View view, Bundle savedInstanceState) { 57 | super.onViewCreated(view, savedInstanceState); 58 | setupViews(); 59 | } 60 | 61 | /** 62 | * Method used to set up the entire view, here we check if the user is adding 63 | * a new TaskList or editing an existing one. 64 | */ 65 | @Override 66 | void setupViews() { 67 | savedListsRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); 68 | RecyclerView.Adapter mAdapter = new SavedListsAdapter(TinyListSQLHelper.getSqlHelper(getActivity()).getTaskLists(false), getActivity()); 69 | savedListsRecyclerView.setAdapter(mAdapter); 70 | ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.RIGHT) { 71 | @Override 72 | public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { 73 | return false; 74 | } 75 | 76 | @Override 77 | public void onSwiped(final RecyclerView.ViewHolder viewHolder, int direction) { 78 | /* Archiving the swiped item and showing an Snackbar with "UNDO" option. */ 79 | final TaskList taskListToArchive = ((SavedListsAdapter) savedListsRecyclerView.getAdapter()).getItem(viewHolder.getAdapterPosition()); 80 | taskListToArchive.setIsArchived(true); 81 | TinyListSQLHelper.getSqlHelper(getActivity()).addOrUpdateTaskList(taskListToArchive); 82 | ((SavedListsAdapter) savedListsRecyclerView.getAdapter()).removeItem(viewHolder.getAdapterPosition()); 83 | undoSnackbar(); 84 | } 85 | }; 86 | new ItemTouchHelper(simpleItemTouchCallback).attachToRecyclerView(savedListsRecyclerView); 87 | registerForContextMenu(savedListsRecyclerView); 88 | } 89 | 90 | @Override 91 | public boolean onContextItemSelected(MenuItem item) { 92 | if (item.getItemId() == R.id.contextual_delete_menu) { 93 | final int position = ((SavedListsAdapter) this.savedListsRecyclerView.getAdapter()).getContextMenuSelectedPosition(); 94 | TinyListSQLHelper.getSqlHelper(getActivity()) 95 | .deleteTaskList(((SavedListsAdapter) this.savedListsRecyclerView.getAdapter()).getItem(position).getTask_list_id()); 96 | ((SavedListsAdapter) this.savedListsRecyclerView.getAdapter()).removeItem(position); 97 | } 98 | return super.onContextItemSelected(item); 99 | } 100 | 101 | /** 102 | * Method used to redraw the items inside the main RecyclerView 103 | */ 104 | @Override 105 | public void redrawItems() { 106 | ((SavedListsAdapter) savedListsRecyclerView.getAdapter()).replaceWith(TinyListSQLHelper.getSqlHelper(getActivity()).getTaskLists(false)); 107 | } 108 | 109 | /** 110 | * This method shows an Snackbar informing the item has been archived and giving the user the 111 | * option to revert that. 112 | */ 113 | public void undoSnackbar() { 114 | Snackbar undoSnackbar = Snackbar 115 | .make(this.savedListsRecyclerView, R.string.list_archived, Snackbar.LENGTH_LONG) 116 | .setAction(R.string.undo, new View.OnClickListener() { 117 | @Override 118 | public void onClick(View view) { 119 | if (((SavedListsAdapter) savedListsRecyclerView.getAdapter()).getCachedItem() != null) { 120 | final TaskList taskListToArchive = ((SavedListsAdapter) savedListsRecyclerView.getAdapter()).getCachedItem(); 121 | taskListToArchive.setIsArchived(false); 122 | TinyListSQLHelper.getSqlHelper(getActivity()).addOrUpdateTaskList(taskListToArchive); 123 | clearCachedItem(); 124 | ((MainActivity) getActivity()).getCurrentVisibleFragment().redrawItems(); 125 | } 126 | } 127 | }) 128 | /* Controlling if the FAB can hide or not to avoid weird behaviours 129 | * with its final position after hiding while the Snackbar is still 130 | * showing. */ 131 | .addCallback(new Snackbar.Callback() { 132 | public void onShown(Snackbar snackbar) { 133 | super.onShown(snackbar); 134 | FABScrollBehavior.setCanHideChild(false); 135 | } 136 | 137 | @Override 138 | public void onDismissed(Snackbar snackbar, int event) { 139 | super.onDismissed(snackbar, event); 140 | FABScrollBehavior.setCanHideChild(true); 141 | } 142 | }); 143 | 144 | undoSnackbar.show(); 145 | } 146 | 147 | /** 148 | * Clears the cached item, this method is called when the user deletes an archived item. 149 | * (to prevent the UNDO of that item). 150 | */ 151 | public void clearCachedItem() { 152 | ((SavedListsAdapter) this.savedListsRecyclerView.getAdapter()).setCachedItem(null); 153 | } 154 | 155 | @Override 156 | public void onStart() { 157 | super.onStart(); 158 | /* Redraw items when this fragment is first viewed by the user. */ 159 | redrawItems(); 160 | } 161 | 162 | } 163 | -------------------------------------------------------------------------------- /app/src/main/java/es/dmoral/tinylist/helpers/TinyListSQLHelper.java: -------------------------------------------------------------------------------- 1 | package es.dmoral.tinylist.helpers; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.ContentValues; 5 | import android.content.Context; 6 | import android.database.Cursor; 7 | import android.database.sqlite.SQLiteDatabase; 8 | import android.database.sqlite.SQLiteOpenHelper; 9 | 10 | import java.text.ParseException; 11 | import java.text.SimpleDateFormat; 12 | import java.util.ArrayList; 13 | 14 | import es.dmoral.tinylist.models.Task; 15 | import es.dmoral.tinylist.models.TaskList; 16 | 17 | /** 18 | * Created by grend on 13/01/2016. 19 | *

20 | * This class is a set of useful database actions, threads or AsyncTask are not used 21 | * because of the small size of the database and small complexity of the operations. 22 | */ 23 | public class TinyListSQLHelper extends SQLiteOpenHelper { 24 | 25 | private static final String TABLE_NAME_TASK_LISTS = "task_lists"; 26 | private static final String TABLE_NAME_TASKS = "tasks"; 27 | private static final String CREATE_TABLE_TASK_LISTS = "CREATE TABLE task_lists " + 28 | "(task_list_id INTEGER PRIMARY KEY, " + 29 | "title VARCHAR, " + 30 | "background_color INTEGER, " + 31 | "is_archived, " + // 0 or 1 32 | "creation_date DATE)"; 33 | private static final String CREATE_TABLE_TASKS = "CREATE TABLE tasks " + 34 | "(task_id INTEGER PRIMARY KEY AUTOINCREMENT, " + 35 | "task VARCHAR, " + 36 | "is_checked INTEGER," + // 0 or 1 37 | "task_list_id INTEGER," + 38 | "FOREIGN KEY(task_list_id) REFERENCES task_lists(task_list_id))"; 39 | private static TinyListSQLHelper sqlHelper = null; 40 | @SuppressLint("SimpleDateFormat") 41 | private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 42 | 43 | /*Avoiding instantiation of this class*/ 44 | private TinyListSQLHelper(Context context) { 45 | super(context, "tinylistdb.sqlite", null, 1); 46 | } 47 | 48 | /* Using Singleton pattern*/ 49 | public static TinyListSQLHelper getSqlHelper(Context context) { 50 | if (sqlHelper == null) 51 | sqlHelper = new TinyListSQLHelper(context); 52 | return sqlHelper; 53 | } 54 | 55 | @Override 56 | public void onCreate(SQLiteDatabase db) { 57 | db.execSQL(CREATE_TABLE_TASK_LISTS); 58 | db.execSQL(CREATE_TABLE_TASKS); 59 | } 60 | 61 | @Override 62 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 63 | 64 | } 65 | 66 | /** 67 | * This method adds or updates a TaskList (depending of this id, a new TaskList will 68 | * always have -1 as it's id. 69 | * 70 | * @param taskList TaskList to add or update. 71 | */ 72 | public void addOrUpdateTaskList(TaskList taskList) { 73 | SQLiteDatabase db = sqlHelper.getWritableDatabase(); 74 | ContentValues values = new ContentValues(); 75 | values.put("title", taskList.getTitle()); 76 | values.put("background_color", taskList.getBackgroundColor()); 77 | values.put("is_archived", taskList.isArchived() ? 1 : 0); 78 | values.put("creation_date", dateFormat.format(taskList.getCreationDate())); 79 | 80 | for (Task task : taskList.getTasks()) { 81 | addOrUpdateTask(task, db); 82 | } 83 | 84 | if (taskList.getTask_list_id() == -1) { 85 | values.put("task_list_id", getNextUniqueId(db)); 86 | db.insert(TABLE_NAME_TASK_LISTS, null, values); 87 | } else { 88 | db.update(TABLE_NAME_TASK_LISTS, values, "task_list_id = ?", 89 | new String[]{String.valueOf(taskList.getTask_list_id())}); 90 | } 91 | 92 | db.close(); 93 | 94 | } 95 | 96 | /** 97 | * This method adds or updates a TaskList (depending of this id, a new TaskList will 98 | * always have -1 as it's id. 99 | * 100 | * @param task Task to add or update 101 | * @param db db to use 102 | */ 103 | private void addOrUpdateTask(Task task, SQLiteDatabase db) { 104 | ContentValues values = new ContentValues(); 105 | values.put("task", task.getTask()); 106 | values.put("is_checked", task.isChecked() ? 1 : 0); 107 | 108 | if (task.getTask_id() == -1) { 109 | values.put("task_list_id", 110 | task.getTask_list_id() == -1 ? getNextUniqueId(db) : task.getTask_list_id()); 111 | db.insert(TABLE_NAME_TASKS, null, values); 112 | } else { 113 | db.update(TABLE_NAME_TASKS, values, "task_id = ?", 114 | new String[]{String.valueOf(task.getTask_id())}); 115 | } 116 | 117 | } 118 | 119 | /** 120 | * Deletes the desired TaskList based on the given id 121 | * 122 | * @param id id of the TaskList to remove 123 | */ 124 | public void deleteTaskList(int id) { 125 | SQLiteDatabase db = sqlHelper.getWritableDatabase(); 126 | TaskList taskList = getTaskList(db, id); 127 | 128 | deleteTask(taskList.getTask_list_id(), db); 129 | 130 | db.delete(TABLE_NAME_TASK_LISTS, "task_list_id = ?", 131 | new String[]{String.valueOf(id)}); 132 | db.close(); 133 | 134 | } 135 | 136 | /** 137 | * Deletes the desired Task based on the given task_list_id 138 | * 139 | * @param task_list_id id of the Task to remove 140 | * @param db database to use 141 | */ 142 | private void deleteTask(int task_list_id, SQLiteDatabase db) { 143 | db.delete(TABLE_NAME_TASKS, "task_list_id = ?", 144 | new String[]{String.valueOf(task_list_id)}); 145 | } 146 | 147 | /** 148 | * Deletes the desired Task based on the given task_id 149 | * but without an existing db 150 | * 151 | * @param task_id id of the Task to remove 152 | */ 153 | public void deleteTask(int task_id) { 154 | SQLiteDatabase db = sqlHelper.getWritableDatabase(); 155 | db.delete(TABLE_NAME_TASKS, "task_id = ?", 156 | new String[]{String.valueOf(task_id)}); 157 | db.close(); 158 | } 159 | 160 | /** 161 | * Returns all of the TaskLists based on if they are archived or not 162 | * 163 | * @param isArchived to know what kind of TaskLists are requested 164 | * @return the desired TaskLists 165 | */ 166 | public ArrayList getTaskLists(boolean isArchived) { 167 | ArrayList taskLists = new ArrayList<>(); 168 | SQLiteDatabase db = sqlHelper.getReadableDatabase(); 169 | /* Getting the TaskLists ordered by date (in descendant order) */ 170 | Cursor c = db.query(TABLE_NAME_TASK_LISTS, null, "is_archived = " + (isArchived ? "1" : "0"), null, null, null, "datetime(creation_date) DESC"); 171 | 172 | while (c.moveToNext()) { 173 | TaskList taskList = new TaskList(); 174 | taskList.setTask_list_id(c.getInt(c.getColumnIndex("task_list_id"))); 175 | taskList.setTitle(c.getString(c.getColumnIndex("title"))); 176 | taskList.setBackgroundColor(c.getInt(c.getColumnIndex("background_color"))); 177 | taskList.setIsArchived(c.getInt(c.getColumnIndex("is_archived")) == 1); 178 | try { 179 | taskList.setCreationDate(dateFormat.parse(c.getString(c.getColumnIndex("creation_date")))); 180 | } catch (ParseException e) { 181 | e.printStackTrace(); 182 | } 183 | taskList.setTasks(getTasks(taskList.getTask_list_id())); 184 | taskLists.add(taskList); 185 | } 186 | 187 | c.close(); 188 | db.close(); 189 | return taskLists; 190 | } 191 | 192 | /** 193 | * This method is used to retrieve all tasks of a given TaskList id. 194 | * 195 | * @param task_list_id the id of the TaskList to retrieve Tasks 196 | * @return the desired Tasks 197 | */ 198 | private ArrayList getTasks(int task_list_id) { 199 | ArrayList tasks = new ArrayList<>(); 200 | SQLiteDatabase db = sqlHelper.getReadableDatabase(); 201 | Cursor c = db.query(TABLE_NAME_TASKS, null, "task_list_id = ?", new String[]{String.valueOf(task_list_id)}, null, null, null); 202 | 203 | while (c.moveToNext()) { 204 | Task task = new Task(); 205 | task.setTask(c.getString(c.getColumnIndex("task"))); 206 | task.setIsChecked(c.getInt(c.getColumnIndex("is_checked")) == 1); 207 | task.setTask_list_id(c.getInt(c.getColumnIndex("task_list_id"))); 208 | task.setTask_id(c.getInt(c.getColumnIndex("task_id"))); 209 | tasks.add(task); 210 | } 211 | 212 | c.close(); 213 | return tasks; 214 | } 215 | 216 | /** 217 | * Returns the desired TaskList 218 | * 219 | * @param db db to use 220 | * @param task_list_id desired TaskList id 221 | * @return the desired TaskList 222 | */ 223 | private TaskList getTaskList(SQLiteDatabase db, int task_list_id) { 224 | Cursor c = db.query(TABLE_NAME_TASK_LISTS, null, "task_list_id = ?", new String[]{String.valueOf(task_list_id)}, null, null, null); 225 | c.moveToFirst(); 226 | 227 | TaskList taskList = new TaskList(); 228 | taskList.setTask_list_id(c.getInt(c.getColumnIndex("task_list_id"))); 229 | taskList.setTitle(c.getString(c.getColumnIndex("title"))); 230 | taskList.setBackgroundColor(c.getInt(c.getColumnIndex("background_color"))); 231 | try { 232 | taskList.setCreationDate(dateFormat.parse(c.getString(c.getColumnIndex("creation_date")))); 233 | } catch (ParseException e) { 234 | e.printStackTrace(); 235 | } 236 | taskList.setTasks(getTasks(taskList.getTask_list_id())); 237 | 238 | c.close(); 239 | return taskList; 240 | } 241 | 242 | /** 243 | * This method calculates the UNIQUE id the next TaskList would have when it's created, used 244 | * to manage correct behaviour of the added Tasks 245 | * 246 | * @param db db to use 247 | * @return the next UNIQUE id 248 | */ 249 | private int getNextUniqueId(SQLiteDatabase db) { 250 | int nextUniqueId; 251 | Cursor c = db.rawQuery("SELECT MAX(task_list_id) FROM " + TABLE_NAME_TASK_LISTS, null); 252 | c.moveToFirst(); 253 | nextUniqueId = c.getInt(0) + 1; 254 | c.close(); 255 | 256 | return nextUniqueId; 257 | } 258 | 259 | } 260 | -------------------------------------------------------------------------------- /app/src/main/java/es/dmoral/tinylist/models/Task.java: -------------------------------------------------------------------------------- 1 | package es.dmoral.tinylist.models; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * Created by grend on 13/01/2016. 7 | *

8 | * This class represents every single item/task of a TaskList. 9 | */ 10 | public class Task implements Serializable { 11 | 12 | public static final String DONE_TASK_MARK = "[✔]"; 13 | public static final String UNDONE_TASK_MARK = "[❌]"; 14 | private int task_id; 15 | private int task_list_id; 16 | private String task; 17 | private boolean isChecked; 18 | 19 | public Task() { 20 | this.task_id = -1; 21 | this.isChecked = false; 22 | this.task_list_id = -1; 23 | this.task = ""; 24 | } 25 | 26 | public Task(int task_list_id) { 27 | this.task_list_id = task_list_id; 28 | this.task_id = -1; 29 | this.isChecked = false; 30 | this.task = ""; 31 | } 32 | 33 | public Task(int task_id, int task_list_id, String task, boolean isChecked) { 34 | this.task_id = task_id; 35 | this.task_list_id = task_list_id; 36 | this.task = task; 37 | this.isChecked = isChecked; 38 | } 39 | 40 | public boolean isChecked() { 41 | return isChecked; 42 | } 43 | 44 | public void setIsChecked(boolean isChecked) { 45 | this.isChecked = isChecked; 46 | } 47 | 48 | public String getTask() { 49 | return task; 50 | } 51 | 52 | public void setTask(String task) { 53 | this.task = task; 54 | } 55 | 56 | public int getTask_id() { 57 | return task_id; 58 | } 59 | 60 | public void setTask_id(int task_id) { 61 | this.task_id = task_id; 62 | } 63 | 64 | public int getTask_list_id() { 65 | return task_list_id; 66 | } 67 | 68 | public void setTask_list_id(int task_list_id) { 69 | this.task_list_id = task_list_id; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /app/src/main/java/es/dmoral/tinylist/models/TaskList.java: -------------------------------------------------------------------------------- 1 | package es.dmoral.tinylist.models; 2 | 3 | import android.graphics.Color; 4 | 5 | import java.io.Serializable; 6 | import java.util.ArrayList; 7 | import java.util.Calendar; 8 | import java.util.Date; 9 | 10 | /** 11 | * Created by grend on 13/01/2016. 12 | *

13 | * This class represents a Task (or shopping...) List, containing all items/tasks. 14 | */ 15 | public class TaskList implements Serializable { 16 | 17 | private int task_list_id; 18 | private String title; 19 | private Date creationDate; 20 | private ArrayList tasks; 21 | private int backgroundColor; 22 | private boolean isArchived; 23 | 24 | public TaskList() { 25 | this.task_list_id = -1; 26 | this.title = ""; 27 | this.isArchived = false; 28 | this.backgroundColor = Color.WHITE; 29 | this.creationDate = Calendar.getInstance().getTime(); 30 | } 31 | 32 | public TaskList(int task_list_id, String title, Date creationDate, ArrayList tasks, int backgroundColor, boolean isArchived) { 33 | this.task_list_id = task_list_id; 34 | this.title = title; 35 | this.creationDate = creationDate; 36 | this.tasks = tasks; 37 | this.backgroundColor = backgroundColor; 38 | this.isArchived = isArchived; 39 | } 40 | 41 | public String getTitle() { 42 | return title; 43 | } 44 | 45 | public void setTitle(String title) { 46 | this.title = title; 47 | } 48 | 49 | public Date getCreationDate() { 50 | return creationDate; 51 | } 52 | 53 | public void setCreationDate(Date creationDate) { 54 | this.creationDate = creationDate; 55 | } 56 | 57 | public ArrayList getTasks() { 58 | return tasks; 59 | } 60 | 61 | public void setTasks(ArrayList tasks) { 62 | this.tasks = tasks; 63 | } 64 | 65 | public int getTask_list_id() { 66 | return task_list_id; 67 | } 68 | 69 | public void setTask_list_id(int task_list_id) { 70 | this.task_list_id = task_list_id; 71 | } 72 | 73 | public int getBackgroundColor() { 74 | return backgroundColor; 75 | } 76 | 77 | public void setBackgroundColor(int backgroundColor) { 78 | this.backgroundColor = backgroundColor; 79 | } 80 | 81 | public boolean isArchived() { 82 | return isArchived; 83 | } 84 | 85 | public void setIsArchived(boolean isArchived) { 86 | this.isArchived = isArchived; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /app/src/main/java/es/dmoral/tinylist/widgets/FABScrollBehavior.java: -------------------------------------------------------------------------------- 1 | package es.dmoral.tinylist.widgets; 2 | 3 | import android.content.Context; 4 | import android.support.design.widget.CoordinatorLayout; 5 | import android.support.design.widget.FloatingActionButton; 6 | import android.support.v4.view.ViewCompat; 7 | import android.util.AttributeSet; 8 | import android.view.View; 9 | 10 | /** 11 | * Created by grend on 16/01/2016. 12 | *

13 | * This class is used to give a custom behaviour to the Floating Action Button. 14 | * (Hide when scrolling down and show when scrolling up) 15 | */ 16 | public class FABScrollBehavior extends FloatingActionButton.Behavior { 17 | 18 | private static boolean canHideChild = true; 19 | 20 | public FABScrollBehavior(Context context, AttributeSet attributeSet) { 21 | super(); 22 | } 23 | 24 | public static void setCanHideChild(boolean canHideChild) { 25 | FABScrollBehavior.canHideChild = canHideChild; 26 | } 27 | 28 | @Override 29 | public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child, View directTargetChild, View target, int nestedScrollAxes) { 30 | return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL; 31 | } 32 | 33 | @Override 34 | public void onNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) { 35 | super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed); 36 | if ((dyConsumed > 0 || dyUnconsumed > 0) && child.getVisibility() == View.VISIBLE) { 37 | if (FABScrollBehavior.canHideChild) 38 | child.hide(); 39 | } else if ((dyConsumed < 0 || dyUnconsumed < 0) && child.getVisibility() == View.GONE) { 40 | child.show(); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi-v23/window_background_statusbar_toolbar_tab.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-hdpi-v23/window_background_statusbar_toolbar_tab.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_archive_black_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-hdpi/ic_archive_black_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_close_black_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-hdpi/ic_close_black_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_delete_black_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-hdpi/ic_delete_black_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_done_white_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-hdpi/ic_done_white_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_palette_white_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-hdpi/ic_palette_white_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_playlist_add_white_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-hdpi/ic_playlist_add_white_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_share_black_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-hdpi/ic_share_black_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_unarchive_black_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-hdpi/ic_unarchive_black_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/window_background_statusbar_toolbar_tab.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-hdpi/window_background_statusbar_toolbar_tab.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi-v23/window_background_statusbar_toolbar_tab.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-mdpi-v23/window_background_statusbar_toolbar_tab.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_archive_black_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-mdpi/ic_archive_black_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_close_black_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-mdpi/ic_close_black_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_delete_black_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-mdpi/ic_delete_black_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_done_white_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-mdpi/ic_done_white_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_palette_white_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-mdpi/ic_palette_white_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_playlist_add_white_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-mdpi/ic_playlist_add_white_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_share_black_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-mdpi/ic_share_black_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_unarchive_black_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-mdpi/ic_unarchive_black_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/window_background_statusbar_toolbar_tab.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-mdpi/window_background_statusbar_toolbar_tab.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-sw600dp-hdpi-v23/window_background_statusbar_toolbar_tab.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-sw600dp-hdpi-v23/window_background_statusbar_toolbar_tab.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-sw600dp-hdpi/window_background_statusbar_toolbar_tab.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-sw600dp-hdpi/window_background_statusbar_toolbar_tab.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-sw600dp-mdpi-v23/window_background_statusbar_toolbar_tab.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-sw600dp-mdpi-v23/window_background_statusbar_toolbar_tab.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-sw600dp-mdpi/window_background_statusbar_toolbar_tab.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-sw600dp-mdpi/window_background_statusbar_toolbar_tab.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-sw600dp-xhdpi-v23/window_background_statusbar_toolbar_tab.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-sw600dp-xhdpi-v23/window_background_statusbar_toolbar_tab.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-sw600dp-xhdpi/window_background_statusbar_toolbar_tab.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-sw600dp-xhdpi/window_background_statusbar_toolbar_tab.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-sw600dp-xxhdpi-v23/window_background_statusbar_toolbar_tab.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-sw600dp-xxhdpi-v23/window_background_statusbar_toolbar_tab.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-sw600dp-xxhdpi/window_background_statusbar_toolbar_tab.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-sw600dp-xxhdpi/window_background_statusbar_toolbar_tab.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-sw600dp-xxxhdpi-v23/window_background_statusbar_toolbar_tab.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-sw600dp-xxxhdpi-v23/window_background_statusbar_toolbar_tab.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-sw600dp-xxxhdpi/window_background_statusbar_toolbar_tab.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-sw600dp-xxxhdpi/window_background_statusbar_toolbar_tab.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/add_item_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi-v23/window_background_statusbar_toolbar_tab.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-xhdpi-v23/window_background_statusbar_toolbar_tab.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_archive_black_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-xhdpi/ic_archive_black_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_close_black_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-xhdpi/ic_close_black_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_delete_black_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-xhdpi/ic_delete_black_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_done_white_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-xhdpi/ic_done_white_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_palette_white_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-xhdpi/ic_palette_white_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_playlist_add_white_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-xhdpi/ic_playlist_add_white_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_share_black_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-xhdpi/ic_share_black_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_unarchive_black_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-xhdpi/ic_unarchive_black_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/window_background_statusbar_toolbar_tab.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-xhdpi/window_background_statusbar_toolbar_tab.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi-v23/window_background_statusbar_toolbar_tab.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-xxhdpi-v23/window_background_statusbar_toolbar_tab.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_archive_black_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-xxhdpi/ic_archive_black_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_close_black_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-xxhdpi/ic_close_black_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_delete_black_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-xxhdpi/ic_delete_black_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_done_white_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-xxhdpi/ic_done_white_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_palette_white_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-xxhdpi/ic_palette_white_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_playlist_add_white_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-xxhdpi/ic_playlist_add_white_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_share_black_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-xxhdpi/ic_share_black_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_unarchive_black_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-xxhdpi/ic_unarchive_black_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/window_background_statusbar_toolbar_tab.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-xxhdpi/window_background_statusbar_toolbar_tab.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi-v23/window_background_statusbar_toolbar_tab.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-xxxhdpi-v23/window_background_statusbar_toolbar_tab.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_archive_black_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-xxxhdpi/ic_archive_black_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_close_black_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-xxxhdpi/ic_close_black_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_delete_black_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-xxxhdpi/ic_delete_black_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_done_white_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-xxxhdpi/ic_done_white_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_palette_white_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-xxxhdpi/ic_palette_white_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_playlist_add_white_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-xxxhdpi/ic_playlist_add_white_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_share_black_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-xxxhdpi/ic_share_black_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_unarchive_black_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-xxxhdpi/ic_unarchive_black_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/window_background_statusbar_toolbar_tab.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrenderG/TinyList/a5e2553ae0e7d030a9ef3c2a155342e34846c1ec/app/src/main/res/drawable-xxxhdpi/window_background_statusbar_toolbar_tab.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/add_item_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_edit_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 16 | 17 | 27 | 28 | 34 | 35 |