205 Commits

Author SHA1 Message Date
Konstantin 9e1436a223 Merge pull request #178 from adpops/master
Edited Readme
2023-05-15 13:35:24 +02:00
Jaimin Nimavat bd9ac36efc Grammar changes
Fixed spelling and grammar, added formatting for text, changed Cicerone definition, changed some titles for clearer heading
2022-07-24 20:35:33 -07:00
Jaimin Nimavat 2dc6fca41c Capitalized bullet points 2022-07-24 20:07:53 -07:00
Konstantin e210e45c36 Update README.md 2021-08-06 13:49:23 +03:00
Konstantin f1a5881a9e Update README.md 2021-08-06 12:39:28 +03:00
terrakok af396eb974 Bump version to '7.1' 2021-07-20 20:58:44 +03:00
terrakok b37a6c979d Improvements for SemiTransparentFragment. 2021-07-20 20:43:42 +03:00
Konstantin 701feee394 Merge pull request #159 from phansier/clearContainer_sample
Add sample for ClearContainer = false property of FragmentScreen
2021-07-20 17:05:59 +03:00
Andrey Beryukhov b399dd3173 Add back button 2021-07-20 15:35:43 +03:00
Konstantin 2a4969d756 Merge pull request #164 from VitalyPeryatin/bug/action_after_on_save_instance_state
Fix bug with 'Can not perform this action after onSaveInstanceState'
2021-07-20 12:22:57 +03:00
VitalyPeryatin 8521b7e19d Fix bug with 'Can not perform this action after onSaveInstanceState' 2021-07-17 08:57:57 +03:00
Andrey Beryukhov 8b2eab847b Add sample for ClearContainer = false option 2021-06-10 21:02:28 +03:00
terrakok cbaabaf0bc Update README.md 2021-04-17 14:43:45 +03:00
terrakok 192e35ca71 Bump version to "7.0" and disable publication config for successful sample build without credentials. 2021-04-17 14:26:44 +03:00
terrakok ff77162654 Fix setupFragmentTransaction in sample app. 2021-04-17 14:10:00 +03:00
terrakok 82256a9785 Refactor FragmentScreen and ActivityScreen to interfaces for flexibility in some cases.
See https://github.com/terrakok/Cicerone/pull/144
2021-04-17 14:09:41 +03:00
Konstantin bbd27daee3 Merge pull request #152 from eduard1abdulmanov123/bug_fix_same_screen_key
Fix bugs with identical screen keys
2021-04-17 14:06:54 +03:00
eduard1abdulmanov123 4a54fc1dd4 Fix bugs with identical screen keys 2021-04-17 10:45:29 +03:00
terrakok 4fc5b26980 Add fragment screen to setupFragmentTransaction method. 2021-04-09 17:14:13 +03:00
terrakok daef586461 Move fragment transaction type to FragmentScreen property. 2021-04-09 17:09:21 +03:00
Konstantin 7a862cb061 Merge pull request #151 from VitalyPeryatin/handle_transactions_by_ui_handler
Add navigation transactions in message queue
2021-04-08 16:06:04 +03:00
VitalyPeryatin f2de564600 Add navigation transactions in message queue 2021-04-08 12:58:35 +03:00
Konstantin 4c674c2583 Update README.md 2021-02-14 21:15:57 +03:00
terrakok 22182bddb8 Change jCenter badge to mavenCentral 2021-02-14 21:08:57 +03:00
terrakok ae9f4aea70 Setup publication to mavenCentral. 2021-02-14 21:01:10 +03:00
Konstantin edfc5b5300 Merge pull request #140 from dmitrynerd/fix/license_information_in_pom_file
Add license information to POM file on maven repository.
2021-02-05 14:24:50 +03:00
Dmitry Beshenov ee2dd68f09 Add license information to POM file on maven repository. 2021-02-05 14:08:52 +03:00
terrakok c3873c6e9d Bump version to "6.6" 2020-11-26 13:31:05 +03:00
terrakok f44410fcfe Fix problem with loss of result listener.
Issue #130
2020-11-26 13:30:22 +03:00
Konstantin 608a32c3be Update README.md 2020-11-18 22:34:28 +03:00
Konstantin 17428f0189 Update README.md 2020-11-18 22:08:21 +03:00
terrakok 1701bc4f36 Fixed launching Activity via no concrete Intent. 2020-11-18 22:02:18 +03:00
terrakok 9c9c56bc9b Bump version to "6.4" 2020-11-05 17:24:22 +03:00
terrakok 3da8fe52ca Set fragmentReorderingAllowed=TRUE by default. 2020-11-05 17:21:01 +03:00
terrakok a943aab08e Move ResultListener to another file. 2020-11-05 16:15:28 +03:00
Konstantin 86ac421fb8 Merge pull request #128 from aradxxx/feature/java-interop
java interop
2020-11-05 16:06:50 +03:00
aradxxx f7009f53e8 java interop 2020-11-03 05:13:13 +04:00
Konstantin 994c497147 Update README.md 2020-11-02 20:55:34 +03:00
Konstantin bb13ace1c9 Update README.md 2020-11-02 15:29:24 +03:00
Konstantin dbe065275d Update README.md 2020-11-02 15:15:24 +03:00
Konstantin 2ebbc98a99 Update README.md 2020-11-02 15:10:17 +03:00
Konstantin ba7eea60f5 Update README.md 2020-11-02 10:10:17 +03:00
Konstantin 6347fe7d95 Update README.md 2020-11-01 21:30:28 +03:00
Konstantin 2b6e2ee874 Update README.md 2020-11-01 18:34:03 +03:00
terrakok f50a4d8106 Bump version to "6.3" 2020-11-01 17:07:08 +03:00
terrakok 3535f491f5 Refactor Screen class as interface. 2020-11-01 17:07:04 +03:00
Konstantin 1c5b89d2ec Update README.md 2020-11-01 14:54:59 +03:00
Konstantin 127cce1fe4 Update README.md 2020-11-01 14:06:53 +03:00
terrakok 3bea311400 Bump version to "6.2" 2020-10-30 12:31:15 +03:00
terrakok a0b3dfa71e Fix sample app design 2020-10-30 12:27:24 +03:00
Konstantin 0e9ff3f7f5 Merge pull request #126 from MonStar1/fix/newRootChain
fix newRootChain
2020-10-30 12:12:13 +03:00
Andrei Papko 004faf77c4 fix newRootChain 2020-10-28 17:21:57 +03:00
Konstantin 214a6eddf1 Update README.md 2020-10-21 17:32:59 +03:00
terrakok a4c94003ad Bump version to "6.1" 2020-10-21 17:14:18 +03:00
terrakok 4f7c1c1607 Add screenKey to fragment transaction tag. 2020-10-21 17:14:18 +03:00
terrakok 16f8fd833e Refactor sample screens names 2020-10-21 17:14:18 +03:00
terrakok 77c3ecc76e Rename ResultBus to ResultWire 2020-10-21 17:14:18 +03:00
terrakok 062073cb37 Add simple result messaging. 2020-10-21 17:14:18 +03:00
Konstantin b9cddc290e Update README.md 2020-10-19 15:52:39 +03:00
terrakok df860d2173 Change version name to "6.0" and update bintray setup. 2020-10-19 15:37:29 +03:00
terrakok dbfc6a4f72 Change "Tckhovrebov" -> "Tskhovrebov" everywhere. 2020-10-19 15:34:14 +03:00
terrakok 69e4632058 Change "all" -> "bin" for gradle wrapper. 2020-10-19 15:33:04 +03:00
Konstantin f38b5685a2 Merge pull request #124 from Javernaut/feature/updated_sample
Sample project reviving
2020-10-17 22:55:10 +03:00
Javernaut b81d6bc8e5 Removing redundant explicit dependency for Kotlin stdlib 2020-10-17 21:54:03 +03:00
Javernaut ab8036a13e Migrating to moxy-community 2020-10-17 21:52:27 +03:00
Javernaut a9688db7f5 Migrating the sample applicationId to 'com.github.terrakok.cicerone.sample' 2020-10-16 19:34:59 +03:00
Javernaut e63f6c2339 The Animation sample was migrated to Kotlin 2020-10-16 18:47:15 +03:00
Javernaut 1de7a50b58 Bottom navigation sample was migrated to Kotlin 2020-10-16 18:33:18 +03:00
Javernaut 0efab8731b Main functionality of the sample was migrated to Kotlin 2020-10-16 18:18:44 +03:00
Javernaut 39d043f5d7 StartActivity was migrated to Kotlin 2020-10-16 17:10:02 +03:00
Javernaut f5397994ab Migrating Dagger code to Kotlin 2020-10-16 17:00:14 +03:00
Javernaut af272bc587 Reviving the sample module 2020-10-16 16:22:44 +03:00
Javernaut 9e6c08d5c5 Build tools updating 2020-10-16 14:45:57 +03:00
Konstantin 9c67451d11 Update README.md 2020-10-16 10:58:22 +03:00
Konstantin 86674c0275 Update README.md 2020-10-14 23:45:46 +03:00
terrakok 70b8fcbf4f Rename version to 6.1 2020-10-14 22:51:54 +03:00
terrakok 8be84acb80 Update readme for version 6.0 2020-10-14 22:41:40 +03:00
terrakok 4872d10d6a Add screen keys and update version to 6.0.3-dev 2020-07-26 00:39:01 +03:00
terrakok ac5efa84ed Use default FragmentFactory and update version to 6.0.2-dev 2020-06-10 22:04:30 +03:00
terrakok 3e982794ec Update version to 6.0.1-dev 2020-06-10 18:07:44 +03:00
terrakok a748473697 Refactor FragmentFactory usage and remove outdated sample. 2020-06-10 17:11:28 +03:00
terrakok 0734a4eb22 Refactor AppNavigator. 2020-05-24 23:21:12 +03:00
terrakok 748f2f657c Setup new publication. 2020-05-24 12:48:08 +03:00
terrakok 88884ac51e Migrate to kotlin. 2020-05-23 16:25:32 +03:00
terrakok 0b35127778 Add new parameter for clear container or not during navigation to new screen. 2020-05-21 23:32:45 +03:00
terrakok 9aeb02cfae Update version to 5.1.1 2020-05-10 17:23:43 +03:00
Konstantin 7c31cdc1bd Merge pull request #112 from adolgiy/hotfix/fragment-factory-npe
Fix NPE when localStackCopy.size() == 0 and FragmentFactory is used
2020-05-10 17:11:14 +03:00
Aleksey Dolgiy 05a8ec8f44 Extract 'forward' to reuse in replace when localStackCopy.size > 0 2020-02-20 10:29:18 +03:00
Aleksey Dolgiy d08e6b1afe Code Review: Use internal replace for fragmentForward(Forward) and add explicit exception with null checks 2020-02-19 20:46:31 +03:00
Aleksey Dolgiy cea829ae88 Fix NPE when localStackCopy.size() == 0 and FragmentFactory is used 2020-02-19 17:37:25 +03:00
Konstantin Tskhovrebov dcf1532103 Merge branch 'develop' 2020-02-11 14:42:17 +03:00
Konstantin Tskhovrebov 046410e132 Update version of Cicerone to "5.1.0". 2020-02-11 14:41:24 +03:00
Konstantin e80b2d5432 Merge pull request #111 from terrakok/handle_command_error
Add errorOnApplyCommand for ability manual error handling.
2020-02-11 14:38:09 +03:00
Konstantin Tskhovrebov 3f1cf714df Add errorOnApplyCommand for ability manual error handling. 2020-02-11 14:36:13 +03:00
Konstantin febae878a4 Merge pull request #99 from asitnkova/feature/add_nullability_annotations
Add androidx nullability annotations
2020-02-11 12:45:10 +03:00
Konstantin 6cc2d3e474 Merge branch 'develop' into feature/add_nullability_annotations 2020-02-11 12:42:04 +03:00
Konstantin dc2f732ec6 Merge pull request #110 from asitnkova/feature/add_new_methods_fragment_1.2.0
Feature/add new methods fragment 1.2.0
2020-02-11 12:38:47 +03:00
asitnkova 5d6880c95f Add support for new FragmentTransaction.replace overload 2020-02-04 22:34:10 +07:00
Alexander e1e2dd8b9b Bump build tools 3.5.0-alpha01 -> 3.5.0, gradle 5.1-milestone-1 -> 5.6.4 2020-02-04 22:32:51 +07:00
Konstantin 4df0f34e34 Merge pull request #101 from Anton111111/develop
Made properties protected to add ability use them in overridden methods.
2019-11-18 12:52:07 +03:00
anton f36f6a1e44 Made properties protected to add ability use them in overridden methods. 2019-09-17 19:42:11 +03:00
Anton Potekhin be63c91abe Made properties protected to add ability use them in overridden methods. 2019-02-07 09:36:05 +03:00
Alexander Sitnikov 8e9bf90ee6 Add nullability annotations 2019-01-26 17:31:29 +07:00
terrakok 0ba13efe29 Merge branch 'develop' 2019-01-25 22:13:32 +03:00
terrakok d5737bb092 Update version of Cicerone to "5.0.0". 2019-01-25 22:13:14 +03:00
terrakok 07869ad6e7 Update gradle plugin. 2019-01-25 22:09:19 +03:00
Konstantin a57b8ecb3c Merge pull request #91 from pihariev/fix/sample_chain_representation
Fix sample chain representation for ordinary navigation.
2018-11-05 22:58:21 +03:00
Konstantin 8e3f41057d Merge pull request #88 from pihariev/refactor/androidx
Refactor library and sample to AndroidX packaging system.
2018-11-05 22:12:00 +03:00
Roman Pihariev 7823dd0658 Fix sample chain representation for ordinary navigation. 2018-11-05 16:10:43 +02:00
Roman Pihariev 6b195590de Refactor library and sample to AndroidX packaging system. 2018-11-02 22:44:01 +02:00
Konstantin Tskhovrebov 95f29e0143 Merge branch 'develop' 2018-10-03 23:13:07 +03:00
Konstantin Tskhovrebov dd4fdb5b83 Update version to 4.0.2. 2018-10-03 23:11:43 +03:00
Konstantin Tskhovrebov 5ea14dc30c Merge branch 'master' into develop 2018-10-03 23:11:16 +03:00
Konstantin 3da51271a1 Merge pull request #82 from qwert2603/patch-1
Fix error in Router.java
2018-10-03 23:05:59 +03:00
Alexander Zhdanov e2a7493549 Fix error in Router.java
I make call:
router.newRootChain(new ScreenOne(), new ScreenTwo());
and get a exception 
        java.lang.ArrayIndexOutOfBoundsException: length=2; index=2
        at ru.terrakok.cicerone.Router.newRootChain(Router.java:91)
        at ...
I suppose, iteration bound must be equal to length of array "screens" (not "commands").
2018-10-03 17:56:05 +07:00
Konstantin Tskhovrebov f03ea7f60e Merge branch 'develop' 2018-10-01 22:59:26 +03:00
Konstantin Tskhovrebov 9919f6a73f Update version to 4.0.1. 2018-10-01 22:57:51 +03:00
Konstantin Tskhovrebov 6ff618272d Fix newRootChain command. 2018-10-01 22:49:25 +03:00
Konstantin Tskhovrebov 301f3f3db8 Merge branch 'develop' 2018-09-27 22:15:56 +03:00
Konstantin Tskhovrebov de961bcd24 Update version to 4.0.0. 2018-09-27 22:11:02 +03:00
Konstantin Tskhovrebov bccb7ffdce Add newRootChain method to Router. 2018-09-27 22:10:20 +03:00
Konstantin Tskhovrebov b82196ce8c Update readme and fix java docs. 2018-09-27 22:03:58 +03:00
Konstantin Tskhovrebov 1d733906c8 Add media for readme. 2018-09-27 21:57:08 +03:00
Konstantin Tskhovrebov 39305091b9 Update readme for new version. 2018-09-27 18:03:12 +03:00
Konstantin Tskhovrebov 8cbd987c7c Add media for documentation. 2018-09-27 17:11:52 +03:00
Konstantin Tskhovrebov 6b9b8ba992 Update gradle plugin. 2018-09-27 16:21:56 +03:00
Konstantin Tskhovrebov ef05b43b0d Add java docs for public methods. 2018-09-27 16:21:22 +03:00
Konstantin Tskhovrebov 7e316fa7ef Delete unused code. 2018-09-27 16:20:41 +03:00
Konstantin Tskhovrebov f7f45ad493 Change screen default id to "getCanonicalName". 2018-09-27 16:19:53 +03:00
Konstantin Tskhovrebov 62520744cb Fix "back to root" crash. 2018-09-22 21:31:48 +03:00
Konstantin Tskhovrebov 4a09e0083c Merge branch 'feature/screen_chain' into develop 2018-09-22 21:23:50 +03:00
Konstantin Tskhovrebov 74e9681a48 Add router method for opening screen chain. 2018-09-22 21:23:33 +03:00
Konstantin Tskhovrebov 3ef18015ee Fix library android dependencies. 2018-09-22 21:22:33 +03:00
Konstantin Tskhovrebov 163fc90fcd Merge branch 'feature/result_changes' into develop 2018-09-22 20:45:05 +03:00
Konstantin Tskhovrebov a920505915 Update sample for manual result listening. 2018-09-22 20:43:40 +03:00
Konstantin Tskhovrebov 88427b0271 Change pure android stubs on android source dependency. 2018-09-22 20:43:08 +03:00
Konstantin Tskhovrebov 3b8954b993 Remove result listener from Router. 2018-09-22 20:42:22 +03:00
Konstantin Tskhovrebov d9fd28fb2c Merge branch 'feature/screen_class' into develop 2018-09-20 15:29:10 +03:00
Konstantin Tskhovrebov f2977d599d Update sample for new library version. 2018-09-20 15:27:42 +03:00
Konstantin Tskhovrebov ccc8dd626c Update gradle plugin. 2018-09-20 12:48:51 +03:00
Konstantin Tskhovrebov c9f0b413cb Remove FragmentNavigator and create common AppNavigator. 2018-09-20 12:48:32 +03:00
Konstantin Tskhovrebov 47f5197f90 Add screen class (contains key and creation logic). 2018-09-09 02:51:03 +03:00
Konstantin Tskhovrebov d0b4e5c119 Update gradle plugin. 2018-09-09 01:11:56 +03:00
Konstantin f036217eb0 Merge pull request #69 from Aleksander3007/changed_pop
Changed pop() to removeLast().
2018-07-12 12:24:20 +03:00
aaermakov 29fcd3c834 Changed pop() to removeLast(). 2018-06-19 09:15:11 +03:00
Konstantin Tskhovrebov 92600e69b2 rename setupFragmentTransactionAnimation to setupFragmentTransaction. because it can be used not for only animations 2018-06-06 16:06:30 +03:00
Konstantin Tskhovrebov 9f41ec0d8f remove unused field MainActivity.screenNames 2018-06-06 14:30:25 +03:00
Konstantin Tskhovrebov 56fb9e82e9 fix: switch on main thread for forward command with delay 2018-06-06 14:28:56 +03:00
Konstantin Tskhovrebov 96c1ec0a25 fix invalid sorting for chain view on main activity 2018-06-06 14:27:52 +03:00
Konstantin Tskhovrebov d90f4ab3a1 Merge branch 'feature/remove_system_msg' into develop 2018-06-06 14:26:03 +03:00
Konstantin Tskhovrebov 07a956be3a remove SystemMessage command 2018-06-06 14:09:00 +03:00
Konstantin ad68431834 Update README.md 2018-01-21 14:53:35 +03:00
Konstantin Tskhovrebov 98b1ff265f Merge branch 'develop' 2018-01-08 13:40:26 +03:00
Konstantin Tskhovrebov 9464fae539 updated library version to 3.0.0 2018-01-08 13:24:26 +03:00
Konstantin db0061a416 Merge pull request #58 from Jeevuz/small_refactor_and_docs
Small refactor and docs
2017-12-27 11:31:04 +03:00
Konstantin 8b2b1e0137 Merge pull request #59 from Jeevuz/feature/screen_name_for_unexisting
Add a screen key to the backToUnexisting method.
2017-12-27 11:29:40 +03:00
Vasili Chyrvon 2ac7848bbb Add a screen key to the backToUnexisting method. 2017-12-25 00:41:43 +03:00
Vasili Chyrvon 29bc51db6f Add the licence file 2017-12-25 00:31:00 +03:00
Vasili Chyrvon e2f78e8185 Change readme 2017-12-25 00:27:43 +03:00
Vasili Chyrvon 3eb0302046 Made executeCommands package-private 2017-12-25 00:25:40 +03:00
Vasili Chyrvon 933fdd2715 Add readability improvements and javadocs. 2017-12-25 00:14:23 +03:00
Konstantin Tskhovrebov 143b144cbf updated gradle version 2017-12-23 00:47:17 +03:00
Konstantin Tskhovrebov eeb5d20b8c Merge branch 'feature/commands_batch' into develop 2017-12-23 00:28:56 +03:00
Konstantin Tskhovrebov fe79f30c25 removed blocking navigation (popBackStackImmediate) 2017-12-21 17:18:45 +03:00
Konstantin Tskhovrebov 0ee0df0bcc moved auth comment to file header for library package 2017-12-19 23:42:57 +03:00
Konstantin Tskhovrebov f665c263ee changed navigator logic for apply commands batch 2017-12-19 22:56:01 +03:00
Konstantin 835d6425be Merge pull request #51 from Jeevuz/feature/context_for_activity_intents
Add Context to the activity creation method.
2017-12-01 14:28:14 +03:00
Vasili Chyrvon 851535ab28 Fix signature of the createActivityIntent() method in the sample and readme. 2017-11-27 02:13:34 +03:00
Vasili Chyrvon e4bb13b025 Add Context to the activity creation method. 2017-11-24 14:45:11 +03:00
Konstantin 4e0f36d57a Merge pull request #49 from Jeevuz/check_activity_exists
Added check for the activity existence before opening.
2017-10-24 09:22:05 +03:00
Vasili Chyrvon 752c22c0b9 Added check for the activity existence before opening. 2017-10-23 17:25:23 +03:00
terrakok ced094ea55 Merge branch 'develop' 2017-08-21 23:02:21 +03:00
terrakok ecd214fa36 update readme.md for new version 2017-08-21 22:58:35 +03:00
Konstantin dcfe1a32b8 Merge pull request #41 from Jeevuz/small_javadocs_fix
Changed the javadocs of the animation allowing methods to look similar.
2017-08-02 11:02:52 +03:00
Vasili Chyrvon 4120ce878d Changed the javadocs of the animation allowing methods to look similar. 2017-08-02 01:28:34 +03:00
Konstantin c9ca3f76da Merge pull request #37 from Jeevuz/patch-2
Update FragmentNavigator.java
2017-08-01 23:10:45 +03:00
Konstantin 9a7b4421c2 Merge pull request #38 from Jeevuz/patch-3
Update SupportFragmentNavigator.java
2017-08-01 23:09:47 +03:00
Konstantin 81774d2d12 Merge pull request #39 from Popalay/feature/activity-animations
Feature/activity animations
2017-08-01 23:09:04 +03:00
Denis Nikiforov 30f2f736c0 Update docs 2017-07-29 22:12:40 +03:00
Denis Nikiforov 0f59f588b8 Rename setupActivityTransactionAnimation to createStartActivityOptions 2017-07-29 22:09:50 +03:00
Denis Nikiforov 68bc8c809c Create activity transition only if activity intent is not null 2017-07-28 12:34:26 +03:00
Denis Nikiforov 37d5818413 Add support of activity transitions 2017-07-27 16:04:40 +03:00
Vasili Chyrvon 7344926349 Update SupportFragmentNavigator.java
Small change in docs
2017-07-18 01:03:58 +03:00
Vasili Chyrvon 357f8d9670 Update FragmentNavigator.java
Small change in docs
2017-07-18 01:01:59 +03:00
terrakok d8266b02b8 Merge branch 'develop' 2017-07-18 00:51:17 +03:00
terrakok 7337901f19 updated version to 2.0.0 AND fixed java docs 2017-07-18 00:50:45 +03:00
terrakok 2e647aec0e updated README 2017-07-18 00:37:38 +03:00
terrakok e10725955b update README file for new version 2017-07-18 00:35:09 +03:00
terrakok f357e9db3a Merge branch 'feature/fragment_animations' into develop 2017-07-17 23:26:57 +03:00
terrakok 5ed9df5409 added javaDocs for new setupFragmentTransactionAnimation navigator method AND update sample 2017-07-17 23:20:13 +03:00
Konstantin Tskhovrebov b493cf5a7b fix after merge 2017-07-15 13:47:29 +03:00
Konstantin Tskhovrebov 2be8ac0db2 Merge branch 'develop' into feature/fragment_animations 2017-07-15 13:45:15 +03:00
Konstantin Tskhovrebov b317498d95 changed weak references on manual set/remove result listener for Router 2017-07-15 13:44:53 +03:00
Konstantin Tskhovrebov 230d7ba064 added sample for present fragment transaction animation and returning result from screen 2017-07-15 01:37:59 +03:00
Konstantin Tskhovrebov b5c2d20ea3 added next fragment instance to applyFragmentAnimations method 2017-07-15 01:35:19 +03:00
Konstantin Tskhovrebov 1647377fb1 Merge branch 'develop' into feature/fragment_animations 2017-07-14 22:43:43 +03:00
Konstantin f6b8c0bfe1 Merge pull request #36 from terrakok/feature/result_bus
Feature/result_bus
2017-07-12 12:55:18 +03:00
terrakok 5a4eeef49f added javaDocs 2017-07-12 01:16:55 +03:00
terrakok 340e659502 removed ResultData marker 2017-07-12 01:05:10 +03:00
terrakok f88ab56ee9 changed call ordering for exitWithResult method 2017-07-12 01:03:16 +03:00
terrakok 3e26610157 renamed requestCode -> resultCode 2017-07-12 01:01:45 +03:00
terrakok 906baccdfe Added the ability to apply animation to a fragment transaction 2017-07-05 00:36:34 +03:00
terrakok 72d100197a added simple "bus" to Router for listen result data 2017-07-04 23:57:27 +03:00
terrakok 10918fd759 added warning for bad sample code 2017-05-31 01:10:44 +03:00
terrakok c02d5a1ff8 optimized navigator backToRoot implementation 2017-05-31 01:04:04 +03:00
132 changed files with 2615 additions and 2876 deletions
+4 -1
View File
@@ -10,4 +10,7 @@ local.properties
**/*.iml
# Mac OS
.DS_Store
.DS_Store
# GPG keys
*.gpg
+22
View File
@@ -0,0 +1,22 @@
MIT License
Copyright (c) 2017 Konstantin Tskhovrebov (@terrakok)
and Vasili Chyrvon (@Jeevuz)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+185 -120
View File
@@ -1,177 +1,242 @@
# Cicerone
[![jCenter](https://api.bintray.com/packages/terrakok/terramaven/cicerone/images/download.svg)](https://bintray.com/terrakok/terramaven/cicerone/_latestVersion)
[![Maven Central](https://img.shields.io/maven-central/v/com.github.terrakok/cicerone)](https://repo1.maven.org/maven2/com/github/terrakok/cicerone/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-Cicerone-green.svg?style=true)](https://android-arsenal.com/details/1/4700)
[![Android Weekly](https://img.shields.io/badge/Android%20Weekly-250-lightgrey.svg)](http://androidweekly.net/issues/issue-250)
[![Telegram](https://img.shields.io/badge/Telegram-ENG-blue.svg)](https://t.me/Cicerone_ENG)
[![Telegram](https://img.shields.io/badge/Telegram-RUS-blue.svg)](https://t.me/Cicerone_RUS)
[![Android Weekly](https://img.shields.io/badge/Android%20Weekly-250-green.svg)](http://androidweekly.net/issues/issue-250)
[![Android Weekly](https://img.shields.io/badge/Android%20Weekly-271-green.svg)](http://androidweekly.net/issues/issue-271)
![](https://habrastorage.org/files/644/32e/9eb/64432e9eb3664723b3ee438449dab3b0.png)
<table>
<tr>
<td>
<img src="https://github.com/terrakok/Cicerone/raw/master/media/navigation.gif" width="256"/>
</td>
<td>
<img src="https://github.com/terrakok/Cicerone/raw/master/media/insta_tabs.gif" width="256"/>
</td>
<td>
<img src="https://github.com/terrakok/Cicerone/raw/master/media/animations.gif" width="256"/>
</td>
</tr>
<tr>
<td>
Power navigation
</td>
<td>
Multibackstack
</td>
<td>
Result listeners
</td>
</tr>
</table>
Cicerone (a guide, one who conducts sightseers) is a lightweight library that makes the navigation in an Android app easy.
It was designed to be used with the MVP pattern (try [Moxy](https://github.com/Arello-Mobile/Moxy)), but will work great with any architecture.
[Russian version readme](https://github.com/terrakok/Cicerone/blob/develop/README_RUS.md)
Cicerone (a guide who gives information about antiquities and places of interest to sightseers) is a lightweight library that makes the navigation in an Android app easy.
It was designed to be used with the MVP/MVVM/MVI patterns but will work great with any architecture.
## Main advantages
+ is not tied to Fragments
+ not a framework
+ short navigation calls (no builders)
+ lifecycle-safe!
+ functionality is simple to extend
+ suitable for Unit Testing
+ Is not tied to Fragments
+ Not a framework (very lightweight)
+ Short navigation calls (no builders)
+ Static typed checks for screen parameters!
+ Lifecycle-safe!
+ Functionality is simple to extend
+ Suitable for Unit Testing
## How to add
## Additional features
+ Opening several screens inside single call (for example: deeplink)
+ Provides `FragmentFactory` if it needed
+ `add` or `replace` strategy for opening next screen (see `router.navigateTo` last parameter)
+ Implementation of parallel navigation (Instagram like)
+ Predefined navigator ready for Single-Activity apps
+ Predefined navigator ready for setup transition animation
## How to add Cicerone to your application
Add the dependency in your build.gradle:
```groovy
```kotlin
dependencies {
//Cicerone
compile 'ru.terrakok.cicerone:cicerone:X.X.X'
implementation("com.github.terrakok:cicerone:X.X.X")
}
```
Initialize the library (for example in your Application class):
```java
public class SampleApplication extends MvpApplication {
public static SampleApplication INSTANCE;
private Cicerone<Router> cicerone;
```kotlin
class App : Application() {
private val cicerone = Cicerone.create()
val router get() = cicerone.router
val navigatorHolder get() = cicerone.getNavigatorHolder()
@Override
public void onCreate() {
super.onCreate();
INSTANCE = this;
initCicerone();
override fun onCreate() {
super.onCreate()
INSTANCE = this
}
private void initCicerone() {
cicerone = Cicerone.create();
}
public NavigatorHolder getNavigatorHolder() {
return cicerone.getNavigatorHolder();
}
public Router getRouter() {
return cicerone.getRouter();
companion object {
internal lateinit var INSTANCE: App
private set
}
}
```
## How it works?
![](https://habrastorage.org/files/4df/45d/973/4df45d9733fc4ee0a2f0be933de475b1.png)
## How does it work?
<img src="https://github.com/terrakok/Cicerone/blob/master/media/CiceroneDiagram.png" alt="CiceroneDiagram.png" width="800"/>
Presenter calls navigation method of Router.
The `Presenter` calls the navigation method of `Router`.
```java
public class SamplePresenter extends Presenter<SampleView> {
private Router router;
```kotlin
class SamplePresenter(
private val router: Router
) : Presenter<SampleView>() {
public SamplePresenter() {
router = SampleApplication.INSTANCE.getRouter();
fun onOpenNewScreen() {
router.navigateTo(SomeScreen())
}
public void onBackCommandClick() {
router.exit();
}
public void onForwardCommandClick() {
router.navigateTo("Some screen");
fun onBackPressed() {
router.exit()
}
}
```
Router converts the navigation call to the set of Commands and sends them to CommandBuffer.
`Router` converts the navigation call to the set of commands and sends them to `CommandBuffer`.
CommandBuffer checks whether there are _"active"_ Navigator:
If yes, it passes the commands to the Navigator. Navigator will process them to achive the desired transition.
If no, then CommandBuffer saves the commands in a queue, and will apply them as soon as new _"active"_ Navigator will appear.
`CommandBuffer` checks whether there are the `_"active"_ Navigator`:
- If yes, it passes the commands to the Navigator. `Navigator` will process them to achive the desired transition.
- If no, then `CommandBuffer` saves the commands in a queue, and will apply them as soon as a new `_"active"_ Navigator` will appear.
```java
protected void executeCommand(Command command) {
if (navigator != null) {
navigator.applyCommand(command);
} else {
pendingCommands.add(command);
}
```kotlin
fun executeCommands(commands: Array<out Command>) {
navigator?.applyCommands(commands) ?: pendingCommands.add(commands)
}
```
Navigator processes the navigation commands. Usually it is an anonymous class inside the Activity.
Activity provides Navigator to the CommandBuffer in _onResume_ and removes it in _onPause_.
`Navigator` processes the navigation commands. Usually it is an anonymous class inside `Activity`.
`Activity` provides `Navigator` to the `CommandBuffer` in `_onResume_` and removes it in `_onPause_`.
**Attention**: Use _onResumeFragments()_ with FragmentActivity ([more info](https://developer.android.com/reference/android/support/v4/app/FragmentActivity.html#onResume()))
**Attention**: Use `_onResumeFragments()_` with `FragmentActivity` ([more info](https://developer.android.com/reference/android/support/v4/app/FragmentActivity.html#onResume()))
```java
@Override
protected void onResume() {
super.onResume();
SampleApplication.INSTANCE.getNavigatorHolder().setNavigator(navigator);
```kotlin
private val navigator = AppNavigator(this, R.id.container)
override fun onResumeFragments() {
super.onResumeFragments()
navigatorHolder.setNavigator(navigator)
}
@Override
protected void onPause() {
super.onPause();
SampleApplication.INSTANCE.getNavigatorHolder().removeNavigator();
override fun onPause() {
navigatorHolder.removeNavigator()
super.onPause()
}
private Navigator navigator = new Navigator() {
@Override
public void applyCommand(Command command) {
//implement commands logic
}
};
```
## Navigation commands
This commands set will fulfill the needs of the most applications. But if you need something special - just add it!
+ Forward - Opens new screen
![](https://habrastorage.org/files/862/77e/b20/86277eb20b574dae8307ac4f64b0f090.png)
+ Back - Rolls back the last transition
![](https://habrastorage.org/files/059/b63/2d3/059b632d3a7c4515a534b9e5e881c8f0.png)
+ BackTo - Rolls back to the needed screen in the screens chain
![](https://habrastorage.org/files/a45/4f4/c34/a454f4c340764632ad0669014ad5550d.png)
+ Replace - Replaces the current screen
![](https://habrastorage.org/files/4ae/95c/fee/4ae95cfee4c04f038ad17d358ab08d07.png)
+ SystemMessage - Shows system message (Alert, Toast, Snack, etc.)
![](https://habrastorage.org/files/6e7/1a6/4ed/6e71a64edec04079bf33faa7ab39606f.png)
These commands will fulfill the needs of the most applications. But if you need something special - just add it!
+ Forward - Opens new screen
![](https://github.com/terrakok/Cicerone/raw/master/media/forward_img.png)
+ Back - Rolls back the last transition
![](https://github.com/terrakok/Cicerone/raw/master/media/back_img.png)
+ BackTo - Rolls back to the needed screen in the screens chain
![](https://github.com/terrakok/Cicerone/raw/master/media/backTo_img.png)
+ Replace - Replaces the current screen
![](https://github.com/terrakok/Cicerone/raw/master/media/replace_img.png)
## Predefined navigators
The library provides predefined navigators for _Fragments_ to use inside _Activity_.
To use, just provide it with the container and _FragmentManager_ and override few simple methods.
```java
private Navigator navigator = new SupportFragmentNavigator(
getSupportFragmentManager(), R.id.main_container) {
@Override
protected Fragment createFragment(String screenKey, Object data) {
return SampleFragment.getNewInstance((int) data);
}
@Override
protected void showSystemMessage(String message) {
Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT).show();
}
@Override
protected void exit() {
finish();
}
};
## Predefined navigator
The library provides predefined navigator for _Fragments_ and _Activity_.
To use, just provide it with the container and _FragmentManager_.
```kotlin
private val navigator = AppNavigator(this, R.id.container)
```
A custom navigator can be useful sometimes:
```kotlin
private val navigator = object : AppNavigator(this, R.id.container) {
override fun setupFragmentTransaction(
screen: FragmentScreen,
fragmentTransaction: FragmentTransaction,
currentFragment: Fragment?,
nextFragment: Fragment
) {
//setup your animation
}
override fun applyCommands(commands: Array<out Command>) {
hideKeyboard()
super.applyCommands(commands)
}
}
```
## Screens
Describe your screens as you like e.g. create a Kotlin `object` with all application screens:
```kotlin
object Screens {
fun Main() = FragmentScreen { MainFragment() }
fun AddressSearch() = FragmentScreen { AddressSearchFragment() }
fun Profile(userId: Long) = FragmentScreen("Profile_$userId") { ProfileFragment(userId) }
fun Browser(url: String) = ActivityScreen { Intent(Intent.ACTION_VIEW, Uri.parse(url)) }
}
```
Additional you can use `FragmentFactory` for creating your screens:
```kotlin
fun SomeScreen() = FragmentScreen { factory: FragmentFactory -> ... }
```
## Screen parameters and result listener
```kotlin
//you have to specify screen parameters via new FragmentScreen creation
fun SelectPhoto(resultKey: String) = FragmentScreen {
SelectPhotoFragment.getNewInstance(resultKey)
}
```
```kotlin
//listen result
fun onSelectPhotoClicked() {
router.setResultListener(RESULT_KEY) { data ->
view.showPhoto(data as Bitmap)
}
router.navigateTo(SelectPhoto(RESULT_KEY))
}
//send result
fun onPhotoClick(photo: Bitmap) {
router.sendResult(resultKey, photoRes)
router.exit()
}
```
## Sample
To see how to add, initialize and use the library and predefined navigators check out the sample.
To see how to add, initialize and use the library and predefined navigators see the **sample project**
(thank you [@Javernaut](https://github.com/Javernaut) for support new library version and migrate sample project to Kotlin!)
![](https://habrastorage.org/files/16d/2ee/6e3/16d2ee6e33a0428eb4f0dcab8ce6b294.gif)
For more complex use case check out the [GitFox (Android GitLab client)](https://gitlab.com/terrakok/gitlab-client)
![](https://hsto.org/files/867/638/c33/867638c338704489b3107a6d7cb28c2d.gif)
## Applications that use Cicerone
<a href="https://play.google.com/store/apps/details?id=ru.foodfox.client"><img src="https://play-lh.googleusercontent.com/gWYedIqy8QujCQOn0kzEIBEkGLBSpuKvFm-fMcfkWnJ1Oirtv847xAE4OyhAaohdcp5V=s360" width="64" /> Яндекс.Еда — доставка еды/продуктов. Food delivery</a><br>
<a href="https://play.google.com/store/apps/details?id=com.kms.me"><img src="https://play-lh.googleusercontent.com/IBzu0tlHd_amw2HbjBLOZiCfK-0tn0CnwkMdOd1toP23rdHUV-i7L2ViNKgIg687=s360" width="64" /> Kaspersky Internet Security</a><br>
<a href="https://play.google.com/store/apps/details?id=com.deliveryclub"><img src="https://play-lh.googleusercontent.com/m6-gFunvj7aQD5fdv8EdJZBN5M4REIobTaPZPYS0K5Td7CNYnazN7fOKiPwwaY3hJw=s360" width="64" /> Delivery Club Доставка еды и продуктов</a><br>
<a href="https://play.google.com/store/apps/details?id=ru.hh.android"><img src="https://play-lh.googleusercontent.com/YpAV7Q-ZJhI5tzFk_wEX-7-x2BydtnCtFTVUrmq0zAO6jLCLA4nNcfem3p_Pyowg9w=s360" width="64" /> Поиск работы на hh. Вакансии рядом с домом</a><br>
<a href="https://play.google.com/store/apps/details?id=com.foodient.whisk"><img src="https://play-lh.googleusercontent.com/eKotZjJcZOU2_L9t2l34EEY7aGl5zhvKVuEbF0Kc4MRs_pAC2SJgOnWMkMTFjR_e9EY=s360" width="64" /> Whisk: Recipe Saver, Meal Planner & Grocery List</a><br>
<a href="https://play.google.com/store/apps/details?id=kz.beeline.odp"><img src="https://play-lh.googleusercontent.com/hzgjpQQpy6Z-Byye0aVKSv9P7h8yx58i6pVkQtiM6jB99iWFXjYfKeaPqJ3wm6Rtb38=s360" width="64" /> Мой Beeline (Казахстан)</a><br>
<a href="https://play.google.com/store/apps/details?id=com.mercuryo.app"><img src="https://play-lh.googleusercontent.com/FKulXdc15r5PWX6hTZi2i3iaJjcQHwd9xParp6YPiQ2KiBqza7jwEt_b_tqLwXpyEHg=s360" width="64" /> Mercuryo Bitcoin Cryptowallet</a><br>
<a href="https://play.google.com/store/apps/details?id=com.warefly.checkscan"><img src="https://play-lh.googleusercontent.com/2c2uuiSl2vwGgp-vdI-VArQEMdSSXk1neUK5A-Udc0WANPcvp5kBJFEugrFiXnxUc7k=s360" width="64" /> ЧекСкан - кэшбэк за чеки, цены и акции в магазинах</a><br>
<a href="https://github.com/eduard1abdulmanov123/News"><img src="https://raw.githubusercontent.com/eduard1abdulmanov123/News/dev/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png" width="64" /> RSS Reader для Вести.Ru</a><br>
<a href="https://play.google.com/store/apps/details?id=com.epam.connect.android"><img src="https://play-lh.googleusercontent.com/aN7R6BiR7yt7b3oEoBI30pVwzsdzaWe3TWpw8c9igqoOj79Pm2xVh4_C4qwjSKwjVio=s360" width="64" /> EPAM Connect</a><br>
<a href="https://play.google.com/store/apps/details?id=org.consumerreports.ratings"><img src="https://play-lh.googleusercontent.com/dEdOwZOjXAdamytxY1TgY8LS-Hc9FKCcit5HP1RyaKqRAWjDJEyFSQS1XlqQPpeY5UI=s360" width="64" /> Consumer Reports: Product Reviews & Ratings</a><br>
<a href="https://play.google.com/store/apps/details?id=ru.zakaz.android"><img src="https://play-lh.googleusercontent.com/jj18yK2dB2MHZ_QdO21aXyznGXteIF2q4mgxY4ubLhFv9gwZqHVDeu1i2FmanS-0Furm=s360" width="64" /> Zakaz.ru</a><br>
## Participants
+ idea and code - Konstantin Tskhovrebov (@terrakok)
+ architecture advice, documentation and publication - Vasili Chyrvon (@Jeevuz)
+ Idea and code - Konstantin Tskhovrebov (@terrakok)
+ Architecture advice, documentation and publication - Vasili Chyrvon (@Jeevuz)
## License
```
The MIT License (MIT)
MIT License
Copyright (c) 2017 Konstantin Tskhovrebov (@terrakok)
and Vasili Chyrvon (@Jeevuz)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
-174
View File
@@ -1,174 +0,0 @@
# Cicerone
Cicerone (_"чи-че-ро́-не"_ - устар. гид) - легкая библиотека для простой реализации навигации в андроид приложении.
Разработана для использования в MVP архитектуре (попробуйте [Moxy](https://github.com/Arello-Mobile/Moxy)), но легко встраивается в любые решения.
## Основные преимущества
+ не завязана на Fragment'ы
+ не фреймворк
+ короткие вызовы навигации (никаких билдеров)
+ lifecycle-безопасна!
+ простое расширение функционала
+ приспособлена для Unit тестов
## Как подключить?
Добавьте в build.gradle зависимость:
```groovy
dependencies {
//Cicerone
compile 'ru.terrakok.cicerone:cicerone:X.X.X'
}
```
И инициализируйте библиотеку, например, так:
```java
public class SampleApplication extends MvpApplication {
public static SampleApplication INSTANCE;
private Cicerone<Router> cicerone;
@Override
public void onCreate() {
super.onCreate();
INSTANCE = this;
initCicerone();
}
private void initCicerone() {
cicerone = Cicerone.create();
}
public NavigatorHolder getNavigatorHolder() {
return cicerone.getNavigatorHolder();
}
public Router getRouter() {
return cicerone.getRouter();
}
}
```
## Как это работает?
![](https://habrastorage.org/files/4df/45d/973/4df45d9733fc4ee0a2f0be933de475b1.png)
Presenter вызывает у Router метод навигации.
```java
public class SamplePresenter extends Presenter<SampleView> {
private Router router;
public SamplePresenter() {
router = SampleApplication.INSTANCE.getRouter();
}
public void onBackCommandClick() {
router.exit();
}
public void onForwardCommandClick() {
router.navigateTo("Some screen");
}
}
```
Router - класс, который реализует вызванный метод навигации набором Command и отправляет их в CommandBuffer.
CommandBuffer проверяет есть ли _"активный"_ Navigator.
Если да, то на нем вызываются необходимые команды для осуществления запрошенного перехода.
Если нет, то команды добавляются в очередь, которая будет применена, как только появится _"активный"_ Navigator.
```java
protected void executeCommand(Command command) {
if (navigator != null) {
navigator.applyCommand(command);
} else {
pendingCommands.add(command);
}
}
```
Navigator - (например) анонимный класс внутри Activity, который реализует команды навигации.
Activity предоставляет свой Navigator для CommandBuffer в методе _onResume_ и очищает в _onPause_
**Внимание**: В FragmentActivity следует использовать _onResumeFragments()_. ([подробнее](https://developer.android.com/reference/android/support/v4/app/FragmentActivity.html#onResume()))
```java
@Override
protected void onResume() {
super.onResume();
SampleApplication.INSTANCE.getNavigatorHolder().setNavigator(navigator);
}
@Override
protected void onPause() {
super.onPause();
SampleApplication.INSTANCE.getNavigatorHolder().removeNavigator();
}
private Navigator navigator = new Navigator() {
@Override
public void applyCommand(Command command) {
//implements commands logic
}
};
```
Навигатор не обязан быть в Activity. Он может быть и внутри Fragment'а, который переключает внутри себя View.
## Команды навигатора
Для большинства задач предоставленных в библиотеке команд должно хватить, но их всегда можно дополнить собственными!
+ Forward - переход на новый экран
![](https://habrastorage.org/files/862/77e/b20/86277eb20b574dae8307ac4f64b0f090.png)
+ Back - возврат на предыдущий экран
![](https://habrastorage.org/files/059/b63/2d3/059b632d3a7c4515a534b9e5e881c8f0.png)
+ BackTo - возврат к конкретному экрану в цепочке, либо на корневой, если указанный экран не найден
![](https://habrastorage.org/files/a45/4f4/c34/a454f4c340764632ad0669014ad5550d.png)
+ Replace - замена текущего экрана на новый
![](https://habrastorage.org/files/4ae/95c/fee/4ae95cfee4c04f038ad17d358ab08d07.png)
+ SystemMessage - показ системного сообщения (Alert, Toast, Snack и тд)
![](https://habrastorage.org/files/6e7/1a6/4ed/6e71a64edec04079bf33faa7ab39606f.png)
## Готовые навигаторы
Библиотека предоставляет готовые навигаторы для _Activity_.
Чтобы их использовать, достаточно указать контейнер и передать _FragmentManager_.
```java
private Navigator navigator = new SupportFragmentNavigator(
getSupportFragmentManager(), R.id.main_container) {
@Override
protected Fragment createFragment(String screenKey, Object data) {
return SampleFragment.getNewInstance((int) data);
}
@Override
protected void showSystemMessage(String message) {
Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT).show();
}
@Override
protected void exit() {
finish();
}
};
```
## Sample
Работу библиотеки, готовых навигаторов и другое можно посмотреть в _sample_ приложении.
![](https://habrastorage.org/files/16d/2ee/6e3/16d2ee6e33a0428eb4f0dcab8ce6b294.gif)
![](https://hsto.org/files/867/638/c33/867638c338704489b3107a6d7cb28c2d.gif)
## Участники
+ идея и реализация - Константин Цховребов (@terrakok)
+ архитектурные советы, документация и публикация - Василий Чирвон (@Jeevuz)
## Лицензия
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+7 -6
View File
@@ -1,21 +1,22 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.5.21'
repositories {
jcenter()
mavenCentral()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.0'
// For the library uploading to the Bintray
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.1'
classpath 'com.android.tools.build:gradle:4.2.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5'
}
}
allprojects {
repositories {
jcenter()
mavenCentral()
google()
}
}
+2 -1
View File
@@ -12,7 +12,8 @@
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=1024m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
android.enableJetifier=true
android.useAndroidX=true
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+2 -2
View File
@@ -1,6 +1,6 @@
#Fri Mar 10 17:09:31 MSK 2017
#Fri Jan 25 22:04:15 MSK 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip
-14
View File
@@ -1,14 +0,0 @@
apply plugin: 'com.github.dcendents.android-maven'
group = publishedGroupId // Maven Group ID for the artifact
install {
repositories.mavenInstaller {
// This generates POM.xml with proper parameters
pom.project {
packaging 'jar'
groupId publishedGroupId
artifactId artifact
}
}
}
-24
View File
@@ -1,24 +0,0 @@
apply plugin: 'com.jfrog.bintray'
version = libraryVersion // Library version
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
bintray {
// User and ApiKey stored in local.properties
user = properties.getProperty("bintrayUser")
key = properties.getProperty("bintrayApiKey")
configurations = ['archives']
// Package info for Bintray
pkg {
repo = bintrayRepo
name = bintrayName
licenses = allLicenses
vcsUrl = gitUrl
publish = true
}
}
// Dependency to call only bintrayUpload task
bintrayUpload.dependsOn install
+8 -28
View File
@@ -1,4 +1,4 @@
apply plugin: 'java'
apply plugin: 'kotlin'
// This is important even if Android Studio claims it isn't
// used. Android can't interpret Java 8 byte code.
@@ -7,37 +7,17 @@ targetCompatibility = JavaVersion.VERSION_1_7
dependencies {
compileOnly project(':stub-android')
compileOnly 'com.google.android:android:4.1.1.4'
implementation "org.jetbrains.kotlin:kotlin-stdlib"
}
ext {
// This params is for the library uploading to the Bintray
bintrayRepo = 'terramaven'
bintrayName = 'cicerone'
publishedGroupId = 'ru.terrakok.cicerone'
publishedGroupId = 'com.github.terrakok'
artifact = 'cicerone'
libraryVersion = '1.2.1'
gitUrl = 'https://github.com/terrakok/Cicerone'
allLicenses = ['MIT']
libraryVersion = '7.1'
}
// Configuration of the library uploading to the Bintray
// Note: Call 'bintrayUpload' task (it will execute 'install' task first)
// I try to include as little properties as possible in these files
project.archivesBaseName ='cicerone' // to fix that project name different from artifact name
apply from: 'androidmaven.gradle'
apply from: 'bintray.gradle'
project.archivesBaseName = artifact // to fix that project name different from artifact name
// Tasks for sources and javadocs jars
task sourcesJar(type: Jar) {
from sourceSets.main.java.srcDirs
classifier = 'sources'
}
task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
from javadoc.destinationDir
}
artifacts {
archives javadocJar
archives sourcesJar
}
//apply from: 'publish-mavencentral.gradle'
//for publication use './gradlew publishReleasePublicationToSonatypeRepository'
+94
View File
@@ -0,0 +1,94 @@
apply plugin: 'maven-publish'
apply plugin: 'signing'
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
properties.each { name, value -> ext[name] = value }
group = publishedGroupId
version = libraryVersion
task sourcesJar(type: Jar) {
from sourceSets.main.kotlin.srcDirs
classifier = 'sources'
}
task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
from javadoc.destinationDir
}
artifacts {
archives javadocJar
archives sourcesJar
}
publishing {
publications {
release(MavenPublication) {
groupId = publishedGroupId
artifactId = artifact
version = libraryVersion
artifact("$buildDir/libs/${artifact}-${version}.jar")
artifact sourcesJar
artifact javadocJar
pom {
name = artifact
description = 'Cicerone is a lightweight library that makes the navigation in an Android app easy.'
url = 'https://github.com/terrakok/Cicerone'
licenses {
license {
name = "MIT"
url = "https://opensource.org/licenses/MIT"
}
}
developers {
developer {
id = 'terrakok'
name = 'Konstantin Tskhovrebov'
email = 'terrakok@gmail.com'
}
}
scm {
developerConnection = 'scm:git:ssh://github.com/terrakok/Cicerone.git'
url = 'https://github.com/terrakok/Cicerone'
}
// A slightly hacky fix so that your POM will include any transitive dependencies
// that your library builds upon
withXml {
def dependenciesNode = asNode().appendNode('dependencies')
project.configurations.implementation.allDependencies.each {
def dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId', it.group)
dependencyNode.appendNode('artifactId', it.name)
dependencyNode.appendNode('version', it.version)
}
}
}
}
}
repositories {
maven {
name = "sonatype"
url = "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
credentials {
username = ossrhUsername
password = ossrhPassword
}
}
}
}
signing {
sign publishing.publications
}
tasks.getByName("publishReleasePublicationToSonatypeRepository").dependsOn("assemble", "sourcesJar", "javadocJar")
@@ -1,33 +0,0 @@
package ru.terrakok.cicerone;
import ru.terrakok.cicerone.commands.Command;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 12.10.16
*/
/**
* BaseRouter is an abstract class to implement high-level navigation.
* Extend it to add needed transition methods.
*/
public abstract class BaseRouter {
private CommandBuffer commandBuffer;
public BaseRouter() {
this.commandBuffer = new CommandBuffer();
}
CommandBuffer getCommandBuffer() {
return commandBuffer;
}
/**
* Sends navigation command to {@link CommandBuffer}.
*
* @param command navigation command to execute
*/
protected void executeCommand(Command command) {
commandBuffer.executeCommand(command);
}
}
@@ -1,45 +0,0 @@
package ru.terrakok.cicerone;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 11.10.16
*/
/**
* Cicerone is the holder for other library components.
* To use it, instantiate it using one of the {@link #create()} methods.
* When you need a {@link NavigatorHolder navigation holder} or router, get it here.
*
* @param <T> type of router. You can use the default {@link Router} or pass your own
* {@link BaseRouter} implementation.
*/
public class Cicerone<T extends BaseRouter> {
private T router;
private Cicerone(T router) {
this.router = router;
}
public NavigatorHolder getNavigatorHolder() {
return router.getCommandBuffer();
}
public T getRouter() {
return router;
}
/**
* Creates the Cicerone instance with the default {@link Router router}
*/
public static Cicerone<Router> create() {
return create(new Router());
}
/**
* Creates the Cicerone instance with the custom router.
* @param customRouter the custom router extending {@link BaseRouter}
*/
public static <T extends BaseRouter> Cicerone<T> create(T customRouter) {
return new Cicerone<>(customRouter);
}
}
@@ -1,48 +0,0 @@
package ru.terrakok.cicerone;
import java.util.LinkedList;
import java.util.Queue;
import ru.terrakok.cicerone.commands.Command;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 12.10.16
*/
/**
* Passes navigation command to an active {@link Navigator}
* or stores it in the pending commands queue to pass it later.
*/
class CommandBuffer implements NavigatorHolder {
private Navigator navigator;
private Queue<Command> pendingCommands = new LinkedList<>();
@Override
public void setNavigator(Navigator navigator) {
this.navigator = navigator;
while (!pendingCommands.isEmpty()) {
if (navigator != null) {
executeCommand(pendingCommands.poll());
} else break;
}
}
@Override
public void removeNavigator() {
this.navigator = null;
}
/**
* Passes {@code command} to the {@link Navigator} if it available.
* Else puts it to the pending commands queue to pass it later.
* @param command navigation command
*/
public void executeCommand(Command command) {
if (navigator != null) {
navigator.applyCommand(command);
} else {
pendingCommands.add(command);
}
}
}
@@ -1,22 +0,0 @@
package ru.terrakok.cicerone;
import ru.terrakok.cicerone.commands.Command;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 11.10.16
*/
/**
* The low-level navigation interface.
* Navigator is the one who actually performs any transition.
*/
public interface Navigator {
/**
* Performs transition described by the navigation command
*
* @param command the navigation command to apply
*/
void applyCommand(Command command);
}
@@ -1,25 +0,0 @@
package ru.terrakok.cicerone;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 11.10.16
*/
/**
* Navigator holder interface.
* Use it to connect a {@link Navigator} to the {@link Cicerone}.
*/
public interface NavigatorHolder {
/**
* Set an active Navigator for the Cicerone and start receive commands.
*
* @param navigator new active Navigator
*/
void setNavigator(Navigator navigator);
/**
* Remove the current Navigator and stop receive commands.
*/
void removeNavigator();
}
@@ -1,159 +0,0 @@
package ru.terrakok.cicerone;
import ru.terrakok.cicerone.commands.Back;
import ru.terrakok.cicerone.commands.BackTo;
import ru.terrakok.cicerone.commands.Forward;
import ru.terrakok.cicerone.commands.Replace;
import ru.terrakok.cicerone.commands.SystemMessage;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 12.10.16
*/
/**
* Router is the class for high-level navigation.
* Use it to perform needed transitions.<br>
* This implementation covers almost all cases needed for the average app.
* Extend it if you need some tricky navigation.
*/
public class Router extends BaseRouter {
public Router() {
super();
}
/**
* Open new screen and add it to the screens chain.
*
* @param screenKey screen key
*/
public void navigateTo(String screenKey) {
navigateTo(screenKey, null);
}
/**
* Open new screen and add it to screens chain.
*
* @param screenKey screen key
* @param data initialisation parameters for the new screen
*/
public void navigateTo(String screenKey, Object data) {
executeCommand(new Forward(screenKey, data));
}
/**
* Clear the current screens chain and start new one
* by opening a new screen right after the root.
*
* @param screenKey screen key
*/
public void newScreenChain(String screenKey) {
newScreenChain(screenKey, null);
}
/**
* Clear the current screens chain and start new one
* by opening a new screen right after the root.
*
* @param screenKey screen key
* @param data initialisation parameters for the new screen
*/
public void newScreenChain(String screenKey, Object data) {
executeCommand(new BackTo(null));
executeCommand(new Forward(screenKey, data));
}
/**
* Clear all screens and open new one as root.
*
* @param screenKey screen key
*/
public void newRootScreen(String screenKey) {
newRootScreen(screenKey, null);
}
/**
* Clear all screens and open new one as root.
*
* @param screenKey screen key
* @param data initialisation parameters for the root
*/
public void newRootScreen(String screenKey, Object data) {
executeCommand(new BackTo(null));
executeCommand(new Replace(screenKey, data));
}
/**
* Replace current screen.
* By replacing the screen, you alters the backstack,
* so by going back you will return to the previous screen
* and not to the replaced one.
*
* @param screenKey screen key
*/
public void replaceScreen(String screenKey) {
replaceScreen(screenKey, null);
}
/**
* Replace current screen.
* By replacing the screen, you alters the backstack,
* so by going back you will return to the previous screen
* and not to the replaced one.
*
* @param screenKey screen key
* @param data initialisation parameters for the new screen
*/
public void replaceScreen(String screenKey, Object data) {
executeCommand(new Replace(screenKey, data));
}
/**
* Return back to the needed screen from the chain.
* Behavior in the case when no needed screens found depends on
* the processing of the {@link BackTo} command in a {@link Navigator} implementation.
*
* @param screenKey screen key
*/
public void backTo(String screenKey) {
executeCommand(new BackTo(screenKey));
}
/**
* Remove all screens from the chain and exit.
* It's mostly used to finish the application or close a supplementary navigation chain.
*/
public void finishChain() {
executeCommand(new BackTo(null));
executeCommand(new Back());
}
/**
* Return to the previous screen in the chain.
* Behavior in the case when the current screen is the root depends on
* the processing of the {@link Back} command in a {@link Navigator} implementation.
*/
public void exit() {
executeCommand(new Back());
}
/**
* Return to the previous screen in the chain and show system message.
*
* @param message message to show
*/
public void exitWithMessage(String message) {
executeCommand(new Back());
executeCommand(new SystemMessage(message));
}
/**
* Show system message.
*
* @param message message to show
*/
public void showSystemMessage(String message) {
executeCommand(new SystemMessage(message));
}
}
@@ -1,88 +0,0 @@
package ru.terrakok.cicerone.android;
import android.app.Activity;
import android.app.FragmentManager;
import android.content.Intent;
import android.widget.Toast;
import ru.terrakok.cicerone.commands.BackTo;
import ru.terrakok.cicerone.commands.Command;
import ru.terrakok.cicerone.commands.Forward;
import ru.terrakok.cicerone.commands.Replace;
/**
* Extends {@link FragmentNavigator} to allow
* open new or replace current activity.
* <p>
* This navigator DOESN'T provide full featured Activity navigation,
* but can ease Activity start or replace from current navigator.
* </p>
*
* @author Vasili Chyrvon (vasili.chyrvon@gmail.com)
*/
public abstract class AppNavigator extends FragmentNavigator {
private Activity activity;
public AppNavigator(Activity activity, int containerId) {
super(activity.getFragmentManager(), containerId);
this.activity = activity;
}
public AppNavigator(Activity activity, FragmentManager fragmentManager, int containerId) {
super(fragmentManager, containerId);
this.activity = activity;
}
@Override
public void applyCommand(Command command) {
if (command instanceof Forward) {
Forward forward = (Forward) command;
Intent activityIntent = createActivityIntent(forward.getScreenKey(), forward.getTransitionData());
// Start activity
if (activityIntent != null) {
activity.startActivity(activityIntent);
return;
}
} else if (command instanceof Replace) {
Replace replace = (Replace) command;
Intent activityIntent = createActivityIntent(replace.getScreenKey(), replace.getTransitionData());
// Replace activity
if (activityIntent != null) {
activity.startActivity(activityIntent);
activity.finish();
return;
}
}
// Use default fragments navigation
super.applyCommand(command);
}
/**
* Creates Intent to start Activity for {@code screenKey}.
* <p>
* <b>Warning:</b> This method does not work with {@link BackTo} command.
* </p>
*
* @param screenKey screen key
* @param data initialization data, can be null
* @return intent to start Activity for the passed screen key
*/
protected abstract Intent createActivityIntent(String screenKey, Object data);
@Override
protected void showSystemMessage(String message) {
// Toast by default
Toast.makeText(activity, message, Toast.LENGTH_SHORT).show();
}
@Override
protected void exit() {
// Finish by default
activity.finish();
}
}
@@ -1,150 +0,0 @@
package ru.terrakok.cicerone.android;
import android.app.Fragment;
import android.app.FragmentManager;
import ru.terrakok.cicerone.Navigator;
import ru.terrakok.cicerone.commands.Back;
import ru.terrakok.cicerone.commands.BackTo;
import ru.terrakok.cicerone.commands.Command;
import ru.terrakok.cicerone.commands.Forward;
import ru.terrakok.cicerone.commands.Replace;
import ru.terrakok.cicerone.commands.SystemMessage;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 11.10.16
*/
/**
* {@link Navigator} implementation based on the fragments.
* <p>
* {@link BackTo} navigation command will return to the root if
* needed screen isn't found in the screens chain.
* To change this behavior override {@link #backToUnexisting()} method.
* </p>
* <p>
* {@link Back} command will call {@link #exit()} method if current screen is the root.
* </p>
*/
public abstract class FragmentNavigator implements Navigator {
private FragmentManager fragmentManager;
private int containerId;
/**
* Creates FragmentNavigator.
*
* @param fragmentManager fragment manager
* @param containerId id of the fragments container layout
*/
public FragmentNavigator(FragmentManager fragmentManager, int containerId) {
this.fragmentManager = fragmentManager;
this.containerId = containerId;
}
@Override
public void applyCommand(Command command) {
if (command instanceof Forward) {
Forward forward = (Forward) command;
Fragment fragment = createFragment(forward.getScreenKey(), forward.getTransitionData());
if (fragment == null) {
unknownScreen(command);
return;
}
fragmentManager
.beginTransaction()
.replace(containerId, fragment)
.addToBackStack(forward.getScreenKey())
.commit();
} else if (command instanceof Back) {
if (fragmentManager.getBackStackEntryCount() > 0) {
fragmentManager.popBackStackImmediate();
} else {
exit();
}
} else if (command instanceof Replace) {
Replace replace = (Replace) command;
Fragment fragment = createFragment(replace.getScreenKey(), replace.getTransitionData());
if (fragment == null) {
unknownScreen(command);
return;
}
if (fragmentManager.getBackStackEntryCount() > 0) {
fragmentManager.popBackStackImmediate();
fragmentManager
.beginTransaction()
.replace(containerId, fragment)
.addToBackStack(replace.getScreenKey())
.commit();
} else {
fragmentManager
.beginTransaction()
.replace(containerId, fragment)
.commit();
}
} else if (command instanceof BackTo) {
String key = ((BackTo) command).getScreenKey();
if (key == null) {
backToRoot();
} else {
boolean hasScreen = false;
for (int i = 0; i < fragmentManager.getBackStackEntryCount(); i++) {
if (key.equals(fragmentManager.getBackStackEntryAt(i).getName())) {
fragmentManager.popBackStackImmediate(key, 0);
hasScreen = true;
break;
}
}
if (!hasScreen) {
backToUnexisting();
}
}
} else if (command instanceof SystemMessage) {
showSystemMessage(((SystemMessage) command).getMessage());
}
}
private void backToRoot() {
for (int i = 0; i < fragmentManager.getBackStackEntryCount(); i++) {
fragmentManager.popBackStack();
}
fragmentManager.executePendingTransactions();
}
/**
* Creates Fragment matching {@code screenKey}.
*
* @param screenKey screen key
* @param data initialization data
* @return instantiated fragment for the passed screen key
*/
protected abstract Fragment createFragment(String screenKey, Object data);
/**
* Shows system message.
*
* @param message message to show
*/
protected abstract void showSystemMessage(String message);
/**
* Called when we try to back from the root.
*/
protected abstract void exit();
/**
* Called when we tried to back to some specific screen, but didn't found it.
*/
protected void backToUnexisting() {
backToRoot();
}
/**
* Called if we can't create a screen.
*/
protected void unknownScreen(Command command) {
throw new RuntimeException("Can't create a screen for passed screenKey.");
}
}
@@ -1,89 +0,0 @@
package ru.terrakok.cicerone.android;
import android.app.Activity;
import android.content.Intent;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.widget.Toast;
import ru.terrakok.cicerone.commands.BackTo;
import ru.terrakok.cicerone.commands.Command;
import ru.terrakok.cicerone.commands.Forward;
import ru.terrakok.cicerone.commands.Replace;
/**
* Extends {@link SupportFragmentNavigator} to allow
* open new or replace current activity.
* <p>
* This navigator DOESN'T provide full featured Activity navigation,
* but can ease Activity start or replace from current navigator.
* </p>
*
* @author Vasili Chyrvon (vasili.chyrvon@gmail.com)
*/
public abstract class SupportAppNavigator extends SupportFragmentNavigator {
private Activity activity;
public SupportAppNavigator(FragmentActivity activity, int containerId) {
super(activity.getSupportFragmentManager(), containerId);
this.activity = activity;
}
public SupportAppNavigator(FragmentActivity activity, FragmentManager fragmentManager, int containerId) {
super(fragmentManager, containerId);
this.activity = activity;
}
@Override
public void applyCommand(Command command) {
if (command instanceof Forward) {
Forward forward = (Forward) command;
Intent activityIntent = createActivityIntent(forward.getScreenKey(), forward.getTransitionData());
// Start activity
if (activityIntent != null) {
activity.startActivity(activityIntent);
return;
}
} else if (command instanceof Replace) {
Replace replace = (Replace) command;
Intent activityIntent = createActivityIntent(replace.getScreenKey(), replace.getTransitionData());
// Replace activity
if (activityIntent != null) {
activity.startActivity(activityIntent);
activity.finish();
return;
}
}
// Use default fragments navigation
super.applyCommand(command);
}
/**
* Creates Intent to start Activity for {@code screenKey}.
* <p>
* <b>Warning:</b> This method does not work with {@link BackTo} command.
* </p>
*
* @param screenKey screen key
* @param data initialization data, can be null
* @return intent to start Activity for the passed screen key
*/
protected abstract Intent createActivityIntent(String screenKey, Object data);
@Override
protected void showSystemMessage(String message) {
// Toast by default
Toast.makeText(activity, message, Toast.LENGTH_SHORT).show();
}
@Override
protected void exit() {
// Finish by default
activity.finish();
}
}
@@ -1,149 +0,0 @@
package ru.terrakok.cicerone.android;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import ru.terrakok.cicerone.Navigator;
import ru.terrakok.cicerone.commands.Back;
import ru.terrakok.cicerone.commands.BackTo;
import ru.terrakok.cicerone.commands.Command;
import ru.terrakok.cicerone.commands.Forward;
import ru.terrakok.cicerone.commands.Replace;
import ru.terrakok.cicerone.commands.SystemMessage;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 11.10.16
*/
/**
* {@link Navigator} implementation based on the support fragments.
* <p>
* {@link BackTo} navigation command will return to the root if
* needed screen isn't found in the screens chain.
* To change this behavior override {@link #backToUnexisting()} method.
* </p>
* <p>
* {@link Back} command will call {@link #exit()} method if current screen is the root.
* </p>
*/
public abstract class SupportFragmentNavigator implements Navigator {
private FragmentManager fragmentManager;
private int containerId;
/**
* Creates SupportFragmentNavigator.
*
* @param fragmentManager support fragment manager
* @param containerId id of the fragments container layout
*/
public SupportFragmentNavigator(FragmentManager fragmentManager, int containerId) {
this.fragmentManager = fragmentManager;
this.containerId = containerId;
}
@Override
public void applyCommand(Command command) {
if (command instanceof Forward) {
Forward forward = (Forward) command;
Fragment fragment = createFragment(forward.getScreenKey(), forward.getTransitionData());
if (fragment == null) {
unknownScreen(command);
return;
}
fragmentManager
.beginTransaction()
.replace(containerId, fragment)
.addToBackStack(forward.getScreenKey())
.commit();
} else if (command instanceof Back) {
if (fragmentManager.getBackStackEntryCount() > 0) {
fragmentManager.popBackStackImmediate();
} else {
exit();
}
} else if (command instanceof Replace) {
Replace replace = (Replace) command;
Fragment fragment = createFragment(replace.getScreenKey(), replace.getTransitionData());
if (fragment == null) {
unknownScreen(command);
return;
}
if (fragmentManager.getBackStackEntryCount() > 0) {
fragmentManager.popBackStackImmediate();
fragmentManager
.beginTransaction()
.replace(containerId, fragment)
.addToBackStack(replace.getScreenKey())
.commit();
} else {
fragmentManager
.beginTransaction()
.replace(containerId, fragment)
.commit();
}
} else if (command instanceof BackTo) {
String key = ((BackTo) command).getScreenKey();
if (key == null) {
backToRoot();
} else {
boolean hasScreen = false;
for (int i = 0; i < fragmentManager.getBackStackEntryCount(); i++) {
if (key.equals(fragmentManager.getBackStackEntryAt(i).getName())) {
fragmentManager.popBackStackImmediate(key, 0);
hasScreen = true;
break;
}
}
if (!hasScreen) {
backToUnexisting();
}
}
} else if (command instanceof SystemMessage) {
showSystemMessage(((SystemMessage) command).getMessage());
}
}
private void backToRoot() {
for (int i = 0; i < fragmentManager.getBackStackEntryCount(); i++) {
fragmentManager.popBackStack();
}
fragmentManager.executePendingTransactions();
}
/**
* Creates Fragment matching {@code screenKey}.
*
* @param screenKey screen key
* @param data initialization data
* @return instantiated fragment for the passed screen key
*/
protected abstract Fragment createFragment(String screenKey, Object data);
/**
* Shows system message.
*
* @param message message to show
*/
protected abstract void showSystemMessage(String message);
/**
* Called when we try to back from the root.
*/
protected abstract void exit();
/**
* Called when we tried to back to some specific screen, but didn't found it.
*/
protected void backToUnexisting() {
backToRoot();
}
/**
* Called if we can't create a screen.
*/
protected void unknownScreen(Command command) {
throw new RuntimeException("Can't create a screen for passed screenKey.");
}
}
@@ -1,18 +0,0 @@
package ru.terrakok.cicerone.commands;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 11.10.16
*/
/**
* Rolls back the last transition from the screens chain.
*/
public class Back implements Command {
/**
* Creates a {@link Back} navigation command.
*/
public Back() {
}
}
@@ -1,30 +0,0 @@
package ru.terrakok.cicerone.commands;
import ru.terrakok.cicerone.Navigator;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 11.10.16
*/
/**
* Rolls back to the needed screen from the screens chain.
* Behavior in the case when no needed screens found depends on an implementation of the {@link Navigator}.
* But the recommended behavior is to return to the root.
*/
public class BackTo implements Command {
private String screenKey;
/**
* Creates a {@link BackTo} navigation command.
*
* @param screenKey screen key
*/
public BackTo(String screenKey) {
this.screenKey = screenKey;
}
public String getScreenKey() {
return screenKey;
}
}
@@ -1,13 +0,0 @@
package ru.terrakok.cicerone.commands;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 11.10.16
*/
/**
* Navigation command describes screens transition.
* that can be processed by {@link ru.terrakok.cicerone.Navigator}.
*/
public interface Command {
}
@@ -1,33 +0,0 @@
package ru.terrakok.cicerone.commands;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 11.10.16
*/
/**
* Opens new screen.
*/
public class Forward implements Command {
private String screenKey;
private Object transitionData;
/**
* Creates a {@link Forward} navigation command.
*
* @param screenKey screen key
* @param transitionData initial data
*/
public Forward(String screenKey, Object transitionData) {
this.screenKey = screenKey;
this.transitionData = transitionData;
}
public String getScreenKey() {
return screenKey;
}
public Object getTransitionData() {
return transitionData;
}
}
@@ -1,33 +0,0 @@
package ru.terrakok.cicerone.commands;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 11.10.16
*/
/**
* Replaces the current screen.
*/
public class Replace implements Command {
private String screenKey;
private Object transitionData;
/**
* Creates a {@link Replace} navigation command.
*
* @param screenKey screen key
* @param transitionData initial data
*/
public Replace(String screenKey, Object transitionData) {
this.screenKey = screenKey;
this.transitionData = transitionData;
}
public String getScreenKey() {
return screenKey;
}
public Object getTransitionData() {
return transitionData;
}
}
@@ -1,26 +0,0 @@
package ru.terrakok.cicerone.commands;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 11.10.16
*/
/**
* Shows system message.
*/
public class SystemMessage implements Command {
private String message;
/**
* Creates a {@link SystemMessage} command.
*
* @param message message text
*/
public SystemMessage(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}
@@ -0,0 +1,40 @@
package com.github.terrakok.cicerone
/**
* BaseRouter is an abstract class to implement high-level navigation.
*
* Extend it to add needed transition methods.
*/
abstract class BaseRouter {
internal val commandBuffer = CommandBuffer()
private val resultWire = ResultWire()
/**
* Sets data listener with given key
* and returns [ResultListenerHandler] for availability to dispose subscription.
*
* After first call listener will be removed.
*/
fun setResultListener(
key: String,
listener: ResultListener
): ResultListenerHandler {
return resultWire.setResultListener(key, listener)
}
/**
* Sends data to listener with given key.
*/
fun sendResult(key: String, data: Any) {
resultWire.sendResult(key, data)
}
/**
* Sends navigation command array to [CommandBuffer].
*
* @param commands navigation command array to execute
*/
protected fun executeCommands(vararg commands: Command) {
commandBuffer.executeCommands(commands)
}
}
@@ -0,0 +1,30 @@
package com.github.terrakok.cicerone
/**
* Cicerone is the holder for other library components.
*
* To use it, instantiate it using one of the {@link #create()} methods.
*
* When you need a [NavigatorHolder] or router, get it here.
*
* @param router type of router. You can use the default [Router] or pass your own [BaseRouter] implementation.
*/
class Cicerone<T : BaseRouter> private constructor(val router: T) {
fun getNavigatorHolder(): NavigatorHolder = router.commandBuffer
companion object {
/**
* Creates the Cicerone instance with the default [Router]
*/
@JvmStatic
fun create() = create(Router())
/**
* Creates the Cicerone instance with the custom router.
* @param customRouter the custom router extending [BaseRouter]
*/
@JvmStatic
fun <T : BaseRouter> create(customRouter: T) = Cicerone(customRouter)
}
}
@@ -0,0 +1,35 @@
package com.github.terrakok.cicerone
import android.os.Handler
import android.os.Looper
/**
* Passes navigation command to an active [Navigator]
* or stores it in the pending commands queue to pass it later.
*/
internal class CommandBuffer : NavigatorHolder {
private var navigator: Navigator? = null
private val pendingCommands = mutableListOf<Array<out Command>>()
private val mainHandler = Handler(Looper.getMainLooper())
override fun setNavigator(navigator: Navigator) {
this.navigator = navigator
pendingCommands.forEach { navigator.applyCommands(it) }
pendingCommands.clear()
}
override fun removeNavigator() {
navigator = null
}
/**
* Passes `commands` to the [Navigator] if it available.
* Else puts it to the pending commands queue to pass it later.
* @param commands navigation command array
*/
fun executeCommands(commands: Array<out Command>) {
mainHandler.post {
navigator?.applyCommands(commands) ?: pendingCommands.add(commands)
}
}
}
@@ -0,0 +1,14 @@
package com.github.terrakok.cicerone
/**
* The low-level navigation interface.
* Navigator is the one who actually performs any transition.
*/
interface Navigator {
/**
* Performs transition described by the navigation command
*
* @param commands the navigation command array to apply per single transaction
*/
fun applyCommands(commands: Array<out Command>)
}
@@ -0,0 +1,20 @@
package com.github.terrakok.cicerone
/**
* Navigator holder interface.
*
* Use it to connect a [Navigator] to the [Cicerone].
*/
interface NavigatorHolder {
/**
* Set an active Navigator for the Cicerone and start receive commands.
*
* @param navigator new active Navigator
*/
fun setNavigator(navigator: Navigator)
/**
* Remove the current Navigator and stop receive commands.
*/
fun removeNavigator()
}
@@ -0,0 +1,30 @@
package com.github.terrakok.cicerone
/**
* Interface definition for a result callback.
*/
fun interface ResultListener {
fun onResult(data: Any)
}
/**
* Handler for manual delete subscription and avoid leak
*/
fun interface ResultListenerHandler {
fun dispose()
}
internal class ResultWire {
private val listeners = mutableMapOf<String, ResultListener>()
fun setResultListener(key: String, listener: ResultListener): ResultListenerHandler {
listeners[key] = listener
return ResultListenerHandler {
listeners.remove(key)
}
}
fun sendResult(key: String, data: Any) {
listeners.remove(key)?.onResult(data)
}
}
@@ -0,0 +1,97 @@
package com.github.terrakok.cicerone
/**
* Router is the class for high-level navigation.
*
* Use it to perform needed transitions.
* This implementation covers almost all cases needed for the average app.
* Extend it if you need some tricky navigation.
*/
open class Router : BaseRouter() {
/**
* Open new screen and add it to the screens chain.
*
* @param screen screen
*/
fun navigateTo(screen: Screen) {
executeCommands(Forward(screen))
}
/**
* Clear all screens and open new one as root.
*
* @param screen screen
*/
fun newRootScreen(screen: Screen) {
executeCommands(BackTo(null), Replace(screen))
}
/**
* Replace current screen.
*
* By replacing the screen, you alters the backstack,
* so by going fragmentBack you will return to the previous screen
* and not to the replaced one.
*
* @param screen screen
*/
fun replaceScreen(screen: Screen) {
executeCommands(Replace(screen))
}
/**
* Return fragmentBack to the needed screen from the chain.
*
* Behavior in the case when no needed screens found depends on
* the processing of the [BackTo] command in a [Navigator] implementation.
*
* @param screen screen
*/
fun backTo(screen: Screen?) {
executeCommands(BackTo(screen))
}
/**
* Opens several screens inside single transaction.
*
* @param screens
*/
fun newChain(vararg screens: Screen) {
val commands = screens.map { Forward(it) }
executeCommands(*commands.toTypedArray())
}
/**
* Clear current stack and open several screens inside single transaction.
*
* @param screens
*/
fun newRootChain(vararg screens: Screen) {
val commands = screens.mapIndexed { index, screen ->
if (index == 0)
Replace(screen)
else
Forward(screen)
}
executeCommands(BackTo(null), *commands.toTypedArray())
}
/**
* Remove all screens from the chain and exit.
*
* It's mostly used to finish the application or close a supplementary navigation chain.
*/
fun finishChain() {
executeCommands(BackTo(null), Back())
}
/**
* Return to the previous screen in the chain.
*
* Behavior in the case when the current screen is the root depends on
* the processing of the [Back] command in a [Navigator] implementation.
*/
fun exit() {
executeCommands(Back())
}
}
@@ -0,0 +1,8 @@
package com.github.terrakok.cicerone
/**
* Screen is interface for description application screen.
*/
interface Screen {
val screenKey: String get() = this::class.java.name
}
@@ -0,0 +1,214 @@
package com.github.terrakok.cicerone.androidx
import android.content.ActivityNotFoundException
import android.content.Intent
import androidx.fragment.app.*
import com.github.terrakok.cicerone.*
/**
* Navigator implementation for launch fragments and activities.
*
* Feature [BackTo] works only for fragments.
*
* Recommendation: most useful for Single-Activity application.
*/
open class AppNavigator @JvmOverloads constructor(
protected val activity: FragmentActivity,
protected val containerId: Int,
protected val fragmentManager: FragmentManager = activity.supportFragmentManager,
protected val fragmentFactory: FragmentFactory = fragmentManager.fragmentFactory
) : Navigator {
protected val localStackCopy = mutableListOf<String>()
override fun applyCommands(commands: Array<out Command>) {
fragmentManager.executePendingTransactions()
//copy stack before apply commands
copyStackToLocal()
for (command in commands) {
try {
applyCommand(command)
} catch (e: RuntimeException) {
errorOnApplyCommand(command, e)
}
}
}
private fun copyStackToLocal() {
localStackCopy.clear()
for (i in 0 until fragmentManager.backStackEntryCount) {
localStackCopy.add(fragmentManager.getBackStackEntryAt(i).name)
}
}
/**
* Perform transition described by the navigation command
*
* @param command the navigation command to apply
*/
protected open fun applyCommand(command: Command) {
when (command) {
is Forward -> forward(command)
is Replace -> replace(command)
is BackTo -> backTo(command)
is Back -> back()
}
}
protected open fun forward(command: Forward) {
when (val screen = command.screen) {
is ActivityScreen -> {
checkAndStartActivity(screen)
}
is FragmentScreen -> {
commitNewFragmentScreen(screen, true)
}
}
}
protected open fun replace(command: Replace) {
when (val screen = command.screen) {
is ActivityScreen -> {
checkAndStartActivity(screen)
activity.finish()
}
is FragmentScreen -> {
if (localStackCopy.isNotEmpty()) {
fragmentManager.popBackStack()
localStackCopy.removeAt(localStackCopy.lastIndex)
commitNewFragmentScreen(screen, true)
} else {
commitNewFragmentScreen(screen, false)
}
}
}
}
protected open fun back() {
if (localStackCopy.isNotEmpty()) {
fragmentManager.popBackStack()
localStackCopy.removeAt(localStackCopy.lastIndex)
} else {
activityBack()
}
}
protected open fun activityBack() {
activity.finish()
}
protected open fun commitNewFragmentScreen(
screen: FragmentScreen,
addToBackStack: Boolean
) {
val fragment = screen.createFragment(fragmentFactory)
val transaction = fragmentManager.beginTransaction()
transaction.setReorderingAllowed(true)
setupFragmentTransaction(
screen,
transaction,
fragmentManager.findFragmentById(containerId),
fragment
)
if (screen.clearContainer) {
transaction.replace(containerId, fragment, screen.screenKey)
} else {
transaction.add(containerId, fragment, screen.screenKey)
}
if (addToBackStack) {
transaction.addToBackStack(screen.screenKey)
localStackCopy.add(screen.screenKey)
}
transaction.commit()
}
/**
* Performs [BackTo] command transition
*/
protected open fun backTo(command: BackTo) {
if (command.screen == null) {
backToRoot()
} else {
val screenKey = command.screen.screenKey
val index = localStackCopy.indexOfFirst { it == screenKey }
if (index != -1) {
val forRemove = localStackCopy.subList(index, localStackCopy.size)
fragmentManager.popBackStack(forRemove.first().toString(), 0)
forRemove.clear()
} else {
backToUnexisting(command.screen)
}
}
}
private fun backToRoot() {
localStackCopy.clear()
fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
}
/**
* Override this method to setup fragment transaction [FragmentTransaction].
* For example: setCustomAnimations(...), addSharedElement(...) or setReorderingAllowed(...)
*
* @param fragmentTransaction fragment transaction
* @param currentFragment current fragment in container
* (for [Replace] command it will be screen previous in new chain, NOT replaced screen)
* @param nextFragment next screen fragment
*/
protected open fun setupFragmentTransaction(
screen: FragmentScreen,
fragmentTransaction: FragmentTransaction,
currentFragment: Fragment?,
nextFragment: Fragment
) {
// Do nothing by default
}
private fun checkAndStartActivity(screen: ActivityScreen) {
// Check if we can start activity
val activityIntent = screen.createIntent(activity)
try {
activity.startActivity(activityIntent, screen.startActivityOptions)
} catch (e: ActivityNotFoundException) {
unexistingActivity(screen, activityIntent)
}
}
/**
* Called when there is no activity to open `screenKey`.
*
* @param screen screen
* @param activityIntent intent passed to start Activity for the `screenKey`
*/
protected open fun unexistingActivity(
screen: ActivityScreen,
activityIntent: Intent
) {
// Do nothing by default
}
/**
* Called when we tried to fragmentBack to some specific screen (via [BackTo] command),
* but didn't found it.
*
* @param screen screen
*/
protected open fun backToUnexisting(screen: Screen) {
backToRoot()
}
/**
* Override this method if you want to handle apply command error.
*
* @param command command
* @param error error
*/
protected open fun errorOnApplyCommand(
command: Command,
error: RuntimeException
) {
throw error
}
}
@@ -0,0 +1,46 @@
package com.github.terrakok.cicerone.androidx
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentFactory
import com.github.terrakok.cicerone.Screen
fun interface Creator<A, R> {
fun create(argument: A): R
}
interface FragmentScreen : Screen {
val clearContainer: Boolean get() = true
fun createFragment(factory: FragmentFactory): Fragment
companion object {
operator fun invoke(
key: String? = null,
clearContainer: Boolean = true,
fragmentCreator: Creator<FragmentFactory, Fragment>
) = object : FragmentScreen {
override val screenKey = key ?: fragmentCreator::class.java.name
override val clearContainer = clearContainer
override fun createFragment(factory: FragmentFactory) = fragmentCreator.create(factory)
}
}
}
interface ActivityScreen : Screen {
val startActivityOptions: Bundle? get() = null
fun createIntent(context: Context): Intent
companion object {
operator fun invoke(
key: String? = null,
startActivityOptions: Bundle? = null,
intentCreator: Creator<Context, Intent>
) = object : ActivityScreen {
override val screenKey = key ?: intentCreator::class.java.name
override val startActivityOptions = startActivityOptions
override fun createIntent(context: Context) = intentCreator.create(context)
}
}
}
@@ -0,0 +1,31 @@
package com.github.terrakok.cicerone
/**
* Navigation command describes screens transition.
*
* That can be processed by [com.github.terrakok.cicerone.Navigator]
*/
interface Command
/**
* Opens new screen.
*/
data class Forward(val screen: Screen) : Command
/**
* Replaces the current screen.
*/
data class Replace(val screen: Screen) : Command
/**
* Rolls fragmentBack the last transition from the screens chain.
*/
class Back : Command
/**
* Rolls fragmentBack to the needed screen from the screens chain.
*
* Behavior in the case when no needed screens found depends on an implementation of the [com.github.terrakok.cicerone.Navigator]
* But the recommended behavior is to return to the root.
*/
data class BackTo(val screen: Screen?) : Command
+4
View File
@@ -2,3 +2,7 @@ apply plugin: 'java'
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
dependencies {
compileOnly 'com.google.android:android:4.1.1.4'
}
@@ -1,21 +0,0 @@
package android.app;
import android.content.Context;
import android.content.Intent;
/***
* Created by laomo on 2016-11-21.
*/
public class Activity extends Context {
public void startActivity(Intent intent) {
throw new RuntimeException("Stub!");
}
public FragmentManager getFragmentManager() {
throw new RuntimeException("Stub!");
}
public void finish() {
throw new RuntimeException("Stub!");
}
}
@@ -1,36 +0,0 @@
package android.app;
import android.os.Bundle;
/***
* Created by laomo on 2016-11-21.
*/
public class Fragment {
public void onCreate(Bundle savedInstanceState) {
throw new RuntimeException("Stub!");
}
public void onStart() {
throw new RuntimeException("Stub!");
}
public void onDestroyView() {
throw new RuntimeException("Stub!");
}
public void onDestroy() {
throw new RuntimeException("Stub!");
}
public void onSaveInstanceState(Bundle outState) {
throw new RuntimeException("Stub!");
}
final public boolean isRemoving() {
throw new RuntimeException("Stub!");
}
public final Activity getActivity() {
throw new RuntimeException("Stub!");
}
}
@@ -1,41 +0,0 @@
package android.app;
/***
* Created by laomo on 2016-11-21.
*/
public class FragmentManager {
public FragmentTransaction beginTransaction() {
throw new RuntimeException("Stub!");
}
public boolean executePendingTransactions() {
throw new RuntimeException("Stub!");
}
public void popBackStack() {
throw new RuntimeException("Stub!");
}
public boolean popBackStackImmediate() {
throw new RuntimeException("Stub!");
}
public boolean popBackStackImmediate(String name, int flags) {
throw new RuntimeException("Stub!");
}
public int getBackStackEntryCount() {
throw new RuntimeException("Stub!");
}
public BackStackEntry getBackStackEntryAt(int index) {
throw new RuntimeException("Stub!");
}
public interface BackStackEntry {
int getId();
String getName();
}
}
@@ -1,18 +0,0 @@
package android.app;
/***
* Created by laomo on 2016-11-21.
*/
public class FragmentTransaction {
public FragmentTransaction replace(int containerViewId, Fragment fragment) {
throw new RuntimeException("Stub!");
}
public FragmentTransaction addToBackStack(String name) {
throw new RuntimeException("Stub!");
}
public int commit() {
throw new RuntimeException("Stub!");
}
}
@@ -1,8 +0,0 @@
package android.content;
/**
* @author Konstantin Tskhovrebov (aka terrakok). Date: 10.03.17
*/
public class Context {
}
@@ -1,9 +0,0 @@
package android.content;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 22.02.17
*/
public class Intent {
}
@@ -1,7 +0,0 @@
package android.os;
/***
* Created by laomo on 2016-11-21.
*/
public class Bundle {
}
@@ -1,20 +0,0 @@
package android.support.v4.app;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 11.10.16
*/
public class FragmentTransaction {
public FragmentTransaction replace(int containerViewId, Fragment fragment) {
throw new RuntimeException("Stub!");
}
public FragmentTransaction addToBackStack(String name) {
throw new RuntimeException("Stub!");
}
public int commit() {
throw new RuntimeException("Stub!");
}
}
@@ -1,20 +0,0 @@
package android.widget;
import android.content.Context;
/**
* @author Konstantin Tskhovrebov (aka terrakok). Date: 10.03.17
*/
public class Toast {
public static final int LENGTH_SHORT = 0;
public void show() {
throw new RuntimeException("Stub!");
}
public static Toast makeText(Context context, CharSequence text, int duration) {
throw new RuntimeException("Stub!");
}
}
@@ -1,9 +1,10 @@
package android.support.v4.app;
package androidx.fragment.app;
import android.os.Bundle;
import androidx.fragment.app.FragmentActivity;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* Created by Konstantin Tskhovrebov (aka @terrakok)
* on 11.10.16
*/
@@ -1,9 +1,9 @@
package android.support.v4.app;
package androidx.fragment.app;
import android.app.Activity;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* Created by Konstantin Tskhovrebov (aka @terrakok)
* on 11.10.16
*/
@@ -0,0 +1,9 @@
package androidx.fragment.app;
/**
* Created by Konstantin Tskhovrebov (aka @terrakok)
* on 11.10.16
*/
public class FragmentFactory {
}
@@ -1,11 +1,12 @@
package android.support.v4.app;
package androidx.fragment.app;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* Created by Konstantin Tskhovrebov (aka @terrakok)
* on 11.10.16
*/
public class FragmentManager {
public static final int POP_BACK_STACK_INCLUSIVE = 1<<0;
public FragmentTransaction beginTransaction() {
throw new RuntimeException("Stub!");
@@ -19,11 +20,7 @@ public class FragmentManager {
throw new RuntimeException("Stub!");
}
public boolean popBackStackImmediate() {
throw new RuntimeException("Stub!");
}
public boolean popBackStackImmediate(String name, int flags) {
public void popBackStack(String name, int flags) {
throw new RuntimeException("Stub!");
}
@@ -35,6 +32,13 @@ public class FragmentManager {
throw new RuntimeException("Stub!");
}
public Fragment findFragmentById(int id) {
throw new RuntimeException("Stub!");
}
public FragmentFactory getFragmentFactory() {
throw new RuntimeException("Stub!");
}
public interface BackStackEntry {
int getId();
@@ -0,0 +1,28 @@
package androidx.fragment.app;
/**
* Created by Konstantin Tskhovrebov (aka @terrakok)
* on 11.10.16
*/
public class FragmentTransaction {
public FragmentTransaction setReorderingAllowed(boolean reorderingAllowed) {
throw new RuntimeException("Stub!");
}
public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
throw new RuntimeException("Stub!");
}
public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) {
throw new RuntimeException("Stub!");
}
public FragmentTransaction addToBackStack(String name) {
throw new RuntimeException("Stub!");
}
public int commit() {
throw new RuntimeException("Stub!");
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 754 KiB

+1
View File
@@ -0,0 +1 @@
<mxfile userAgent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:62.0) Gecko/20100101 Firefox/62.0" version="9.1.8" editor="www.draw.io" type="device"><diagram id="7e88c67f-9661-f316-02ff-f99b9ebf4599" name="Page-1">7Vpbc6M2FP41nu4+JMPFYPsxdpLtw26bNjvt7lNHBgHqYkSF8GV/fY9A4iZwnBon0zbxTEY+Erp859O54Ym92uw/MJRGn6iP44ll+PuJfTuxrNnChP9CcCgF07lTCkJG/FJk1oJH8h1LoSGlOfFx1hrIKY05SdtCjyYJ9nhLhhiju/awgMbtVVMUYk3w6KFYl/5OfB6VUsswjLrjR0zCiHd71sj7FjKaJ3LBiWUHxV/ZvUFqMjk+i5BPdw2RfTexV4xSXrY2+xWOBbYKt/K5+4HeauMMJ/yUByy3fGKL4hyrLRcb4weFhjhPKodhxvG+TwdorYYb+h7M6mTAGEw3mLMDDNm3tS7J4kiu7GrkFwreqAH61JZCJLUdVjPXB4aGPPPA+Z2nz1+oE4sHjIm93EWE48cUeaJ3B/QHWcQ3sMCtCc2MM/oNr2hMWfG0bRR/VY/ikxgbkDhujJREATlNeN8MgzpoYl1qVAf7ZeDsQdONYYWlT7bQDEXzgeEMtouZ6oNZG93n4d/B1Hfw3J/26WVurW3XlWg/ytXMsyBW5q/NZ1vdiIYGTKtPAyMowNYU8CvNC6jPArUFkQ4ywvPA6wPZ9eZ4HZxL3X5cLeMFcTVNDUHsg8+QXynjEQ1pguK7WrpsY9zAE+8J/yLE14789lUOAizYodElvn6VT/2JOT9IZ4lyTkFUr/uR0lSOK3cqtvcE0nAcmjMPt+4uRyzEvMEmXR8Mx4iTbXv6c9Cdaqxd0c0GJf4yD4LXJ29EGfkOsyA1peAxgXDhJiZhAjIusB+F47bTJrk57SG520PySniOHuaXILnZpHjN6i9Nxtd9Fya5rZN8+kIk1yMNSXLlBtdMucBfcgxjurqAWDEVTe8QE4CcPU39dambj+tKUIWnP+ccZhm4E9gEvznruxMLd2YjdySyT40O2Rc62fu4Ph+B6nrc+xPakhBx+urmZgxo3dd0lov/vBmZ6mZkQB+jm5GZRtx7hsINbPcTSiAvZbo5EZQ2VsBLvOcXJTc4a9frJbc/W6zPz2EGyP2iEbbKVi8ZCZpHIkHj8ux2dXbPXojdpp5B3niwAuEwzb1iOjR/I3j3oS5MNLTxjAQxocIDLn2URYVy6gRepelWH5lXU/EZjAM76Xs1egzqO4un48PKPzapb40RH5p6oK6sjTh0UQ1S53b/ymmZztvGvfg0RSrTl5ZKCZLSBePP9F3mMYyT940Swbr7GMjKVQfqBoW5O1KckdrvIQSS+vTKMoWu6A3x/eJi95Gtfdlb5LokNUy74/JnJ1pFawxm9NQRnaVXBrjZxLn9F2qnqruNXiGynKejs+mlNKUHEG+aGra37itqajFobSNTWT1V3qtMYt2lhKkSQAgo0MjEXmGrVzHeFu9tpNklNBEWHMWxGAFRBbAAC6NOA9FMsUdQXNh4yRUt0GxsI72AWc6AHyQJQeDU3z4X0c6VNRS9UjhyEBfvWCJgG07+GbPGIJPttLjUZ58XF0p3FR+PcqlTdDuFUo+QL+OsRQqDBDWnitTDpzhLfuBFvIh8AUhQyFu8exeTAF95By/G76/fqPVcanVeopk9YaHpXIhbZg+3OprxcratgMCJfyPe0grPEKMsI56eZQ4DdUI6c/Ttl9NzxZTs5HxGrvBASRH2Km8xbWth1kG3TLLkQzXA+jz2QJSvJipTM20iQBUdGsNSMSA7sl+3y5rW22JolDPWNKggPY0Zejb3P2VGV6HdbOxUZnQIVt3zkYnRtSbzkXmhv6bUvVGjHnvEE4GpTuqZVYYJPimP+R9rum9mnQZHa1E537fGXpmtMTEq3EAtA7M+mLmWqytXiMrYCu+xl0uvVoZOZJMC3buBVu0xi19YwCWAcyRF3LVG3Itwdt04enOpQfGbP1QlQqcda/WVSS7mD/uqJJeweo0SoSjNtIrg5sJSggfMCBxCJGC3zzKW7RJgWZp7LQPqTvtd1fN9a7eCppgytm/trmOdZUPha/2rsHJ4/dM7++5v</diagram></mxfile>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

+34 -18
View File
@@ -1,19 +1,20 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
repositories {
jcenter()
mavenCentral()
}
android {
compileSdkVersion 25
buildToolsVersion '25.0.2'
compileSdkVersion 28
defaultConfig {
minSdkVersion 16
targetSdkVersion 25
minSdkVersion 21
targetSdkVersion 28
versionCode 1
versionName "1.0.0"
applicationId "ru.terrakok.cicerone.sample"
applicationId "com.github.terrakok.cicerone.sample"
}
buildTypes {
@@ -25,33 +26,48 @@ android {
debug {
}
}
buildFeatures {
viewBinding true
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
}
ext {
supportLibraryVersion = "25.3.0"
moxyVersion = "1.4.6"
daggerVersion = "2.10"
androidXVersion = "1.3.0"
materialVersion = "1.4.0"
moxyVersion = "2.2.2"
daggerVersion = "2.38"
}
dependencies {
// Support libraries
compile "com.android.support:appcompat-v7:$supportLibraryVersion"
compile "com.android.support:design:$supportLibraryVersion"
implementation "androidx.appcompat:appcompat:$androidXVersion"
implementation "com.google.android.material:material:$materialVersion"
//MVP Moxy
compile "com.arello-mobile:moxy:$moxyVersion"
compile "com.arello-mobile:moxy-app-compat:$moxyVersion"
provided "com.arello-mobile:moxy-compiler:$moxyVersion"
implementation "com.github.moxy-community:moxy:$moxyVersion"
implementation "com.github.moxy-community:moxy-androidx:$moxyVersion"
kapt "com.github.moxy-community:moxy-compiler:$moxyVersion"
//Cicerone
compile project(':library')
implementation project(':library')
//DI
compile "com.google.dagger:dagger:$daggerVersion"
annotationProcessor "com.google.dagger:dagger-compiler:$daggerVersion"
implementation "com.google.dagger:dagger:$daggerVersion"
kapt "com.google.dagger:dagger-compiler:$daggerVersion"
//Bottom Navigation Bar
compile ('com.ashokvarma.android:bottom-navigation-bar:1.3.0') {
implementation ('com.ashokvarma.android:bottom-navigation-bar:1.3.0') {
exclude group: "com.android.support", module: "design"
}
implementation "androidx.core:core-ktx:1.6.0"
}
+2 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest
package="ru.terrakok.cicerone.sample"
package="com.github.terrakok.cicerone.sample"
xmlns:android="http://schemas.android.com/apk/res/android">
<application
@@ -20,5 +20,6 @@
<activity android:name=".ui.main.MainActivity"/>
<activity android:name=".ui.bottom.BottomNavigationActivity"/>
<activity android:name=".ui.animations.ProfileActivity"/>
</application>
</manifest>
@@ -0,0 +1,25 @@
package com.github.terrakok.cicerone.sample
import android.app.Application
import com.github.terrakok.cicerone.sample.dagger.AppComponent
import com.github.terrakok.cicerone.sample.dagger.DaggerAppComponent
/**
* Created by Konstantin Tskhovrebov (aka @terrakok)
* on 11.10.16
*/
class SampleApplication : Application() {
val appComponent: AppComponent by lazy {
DaggerAppComponent.builder().build()
}
override fun onCreate() {
super.onCreate()
INSTANCE = this
}
companion object {
lateinit var INSTANCE: SampleApplication
}
}
@@ -0,0 +1,67 @@
package com.github.terrakok.cicerone.sample
import android.content.Intent
import android.net.Uri
import com.github.terrakok.cicerone.androidx.ActivityScreen
import com.github.terrakok.cicerone.androidx.FragmentScreen
import com.github.terrakok.cicerone.sample.ui.animations.ProfileActivity
import com.github.terrakok.cicerone.sample.ui.animations.photos.SelectPhotoFragment
import com.github.terrakok.cicerone.sample.ui.animations.profile.ProfileFragment
import com.github.terrakok.cicerone.sample.ui.bottom.BottomNavigationActivity
import com.github.terrakok.cicerone.sample.ui.bottom.ForwardFragment
import com.github.terrakok.cicerone.sample.ui.bottom.TabContainerFragment
import com.github.terrakok.cicerone.sample.ui.main.MainActivity
import com.github.terrakok.cicerone.sample.ui.main.SampleFragment
import com.github.terrakok.cicerone.sample.ui.main.SemiTransparentFragment
import com.github.terrakok.cicerone.sample.ui.start.StartActivity
/**
* Created by Konstantin Tskhovrebov (aka @terrakok)
* on 11.10.16
*/
object Screens {
fun Sample(number: Int) = FragmentScreen("Sample($number)") {
SampleFragment.getNewInstance(number)
}
fun Start() = ActivityScreen {
Intent(it, StartActivity::class.java)
}
fun Main() = ActivityScreen {
Intent(it, MainActivity::class.java)
}
fun BottomNavigation() = ActivityScreen {
Intent(it, BottomNavigationActivity::class.java)
}
fun Tab(tabName: String) = FragmentScreen {
TabContainerFragment.getNewInstance(tabName)
}
fun Forward(containerName: String, number: Int) = FragmentScreen {
ForwardFragment.getNewInstance(containerName, number)
}
fun Github() = ActivityScreen {
Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/terrakok/Cicerone"))
}
fun Profile() = ActivityScreen {
Intent(it, ProfileActivity::class.java)
}
fun ProfileInfo() = FragmentScreen {
ProfileFragment()
}
fun SelectPhoto(resultKey: String) = FragmentScreen {
SelectPhotoFragment.getNewInstance(resultKey)
}
fun SemiTransparent() = FragmentScreen(clearContainer = false) {
SemiTransparentFragment()
}
}
@@ -0,0 +1,43 @@
package com.github.terrakok.cicerone.sample.dagger
import com.github.terrakok.cicerone.sample.dagger.module.LocalNavigationModule
import com.github.terrakok.cicerone.sample.dagger.module.NavigationModule
import com.github.terrakok.cicerone.sample.ui.animations.ProfileActivity
import com.github.terrakok.cicerone.sample.ui.animations.photos.SelectPhotoFragment
import com.github.terrakok.cicerone.sample.ui.animations.profile.ProfileFragment
import com.github.terrakok.cicerone.sample.ui.bottom.BottomNavigationActivity
import com.github.terrakok.cicerone.sample.ui.bottom.TabContainerFragment
import com.github.terrakok.cicerone.sample.ui.main.MainActivity
import com.github.terrakok.cicerone.sample.ui.main.SampleFragment
import com.github.terrakok.cicerone.sample.ui.main.SemiTransparentFragment
import com.github.terrakok.cicerone.sample.ui.start.StartActivity
import dagger.Component
import javax.inject.Singleton
/**
* Created by terrakok 24.11.16
*/
@Singleton
@Component(modules = [
NavigationModule::class,
LocalNavigationModule::class]
)
interface AppComponent {
fun inject(activity: StartActivity)
fun inject(activity: MainActivity)
fun inject(fragment: SampleFragment)
fun inject(activity: BottomNavigationActivity)
fun inject(fragment: TabContainerFragment)
fun inject(fragment: ProfileFragment)
fun inject(fragment: SelectPhotoFragment)
fun inject(activity: ProfileActivity)
fun inject(fragment: SemiTransparentFragment)
}
@@ -0,0 +1,17 @@
package com.github.terrakok.cicerone.sample.dagger.module
import com.github.terrakok.cicerone.sample.subnavigation.LocalCiceroneHolder
import dagger.Module
import dagger.Provides
import javax.inject.Singleton
/**
* Created by terrakok 24.11.16
*/
@Module
object LocalNavigationModule {
@Provides
@Singleton
fun provideLocalNavigationHolder(): LocalCiceroneHolder = LocalCiceroneHolder()
}
@@ -0,0 +1,29 @@
package com.github.terrakok.cicerone.sample.dagger.module
import com.github.terrakok.cicerone.Cicerone
import com.github.terrakok.cicerone.Cicerone.Companion.create
import com.github.terrakok.cicerone.NavigatorHolder
import com.github.terrakok.cicerone.Router
import dagger.Module
import dagger.Provides
import javax.inject.Singleton
/**
* Created by terrakok 24.11.16
*/
@Module
class NavigationModule {
private val cicerone: Cicerone<Router> = create()
@Provides
@Singleton
fun provideRouter(): Router {
return cicerone.router
}
@Provides
@Singleton
fun provideNavigatorHolder(): NavigatorHolder {
return cicerone.getNavigatorHolder()
}
}
@@ -0,0 +1,35 @@
package com.github.terrakok.cicerone.sample.mvp.animation.photos
import com.github.terrakok.cicerone.Router
import com.github.terrakok.cicerone.sample.R
import moxy.InjectViewState
import moxy.MvpPresenter
/**
* Created by Konstantin Tskhovrebov (aka @terrakok) on 14.07.17.
*/
@InjectViewState
class SelectPhotoPresenter(
private val resultKey: String,
private val router: Router
) : MvpPresenter<SelectPhotoView>() {
override fun onFirstViewAttach() {
super.onFirstViewAttach()
viewState!!.showPhotos(intArrayOf(
R.drawable.ava_1,
R.drawable.ava_2,
R.drawable.ava_3,
R.drawable.ava_4
))
}
fun onPhotoClick(photoRes: Int) {
router.sendResult(resultKey, photoRes)
router.exit()
}
fun onBackPressed() {
router.exit()
}
}
@@ -0,0 +1,13 @@
package com.github.terrakok.cicerone.sample.mvp.animation.photos
import moxy.MvpView
import moxy.viewstate.strategy.AddToEndSingleStrategy
import moxy.viewstate.strategy.StateStrategyType
/**
* Created by Konstantin Tskhovrebov (aka @terrakok) on 14.07.17.
*/
@StateStrategyType(AddToEndSingleStrategy::class)
interface SelectPhotoView : MvpView {
fun showPhotos(resourceIds: IntArray)
}
@@ -0,0 +1,44 @@
package com.github.terrakok.cicerone.sample.mvp.animation.profile
import com.github.terrakok.cicerone.ResultListenerHandler
import com.github.terrakok.cicerone.Router
import com.github.terrakok.cicerone.sample.R
import com.github.terrakok.cicerone.sample.Screens.SelectPhoto
import moxy.InjectViewState
import moxy.MvpPresenter
/**
* Created by Konstantin Tskhovrebov (aka @terrakok) on 14.07.17.
*/
@InjectViewState
class ProfilePresenter(
private val router: Router
) : MvpPresenter<ProfileView>() {
private var resultListenerHandler: ResultListenerHandler? = null
companion object {
private const val RESULT_KEY = "photo_result"
private const val DEFAULT_PHOTO = R.drawable.ava_1
}
override fun onFirstViewAttach() {
super.onFirstViewAttach()
viewState!!.showPhoto(DEFAULT_PHOTO)
}
fun onPhotoClicked() {
resultListenerHandler = router.setResultListener(RESULT_KEY) { data ->
viewState!!.showPhoto(data as Int)
}
router.navigateTo(SelectPhoto(RESULT_KEY))
}
override fun onDestroy() {
resultListenerHandler?.dispose()
super.onDestroy()
}
fun onBackPressed() {
router.exit()
}
}
@@ -0,0 +1,13 @@
package com.github.terrakok.cicerone.sample.mvp.animation.profile
import moxy.MvpView
import moxy.viewstate.strategy.AddToEndSingleStrategy
import moxy.viewstate.strategy.StateStrategyType
/**
* Created by Konstantin Tskhovrebov (aka @terrakok) on 14.07.17.
*/
@StateStrategyType(AddToEndSingleStrategy::class)
interface ProfileView : MvpView {
fun showPhoto(resId: Int)
}
@@ -0,0 +1,18 @@
package com.github.terrakok.cicerone.sample.mvp.bottom
import com.github.terrakok.cicerone.Router
import moxy.InjectViewState
import moxy.MvpPresenter
/**
* Created by terrakok 25.11.16
*/
@InjectViewState
class BottomNavigationPresenter(
private val router: Router
) : MvpPresenter<BottomNavigationView>() {
fun onBackPressed() {
router.exit()
}
}
@@ -0,0 +1,11 @@
package com.github.terrakok.cicerone.sample.mvp.bottom
import moxy.MvpView
import moxy.viewstate.strategy.AddToEndSingleStrategy
import moxy.viewstate.strategy.StateStrategyType
/**
* Created by terrakok 25.11.16
*/
@StateStrategyType(AddToEndSingleStrategy::class)
interface BottomNavigationView : MvpView
@@ -0,0 +1,42 @@
package com.github.terrakok.cicerone.sample.mvp.bottom.forward
import com.github.terrakok.cicerone.Router
import com.github.terrakok.cicerone.sample.Screens.Forward
import com.github.terrakok.cicerone.sample.Screens.Github
import moxy.InjectViewState
import moxy.MvpPresenter
/**
* Created by terrakok 26.11.16
*/
@InjectViewState
class ForwardPresenter(
private val container: String,
private val router: Router,
private val number: Int
) : MvpPresenter<ForwardView>() {
private fun createChain(number: Int): String {
var chain = "[0]"
for (i in 0 until number) {
chain += "" + (i + 1)
}
return chain
}
fun onForwardPressed() {
router.navigateTo(Forward(container, number + 1))
}
fun onGithubPressed() {
router.navigateTo(Github())
}
fun onBackPressed() {
router.exit()
}
init {
viewState?.setChainText(createChain(number))
}
}
@@ -0,0 +1,13 @@
package com.github.terrakok.cicerone.sample.mvp.bottom.forward
import moxy.MvpView
import moxy.viewstate.strategy.AddToEndSingleStrategy
import moxy.viewstate.strategy.StateStrategyType
/**
* Created by terrakok 26.11.16
*/
@StateStrategyType(AddToEndSingleStrategy::class)
interface ForwardView : MvpView {
fun setChainText(chainText: String)
}
@@ -0,0 +1,84 @@
package com.github.terrakok.cicerone.sample.mvp.main
import android.os.Handler
import android.os.Looper
import com.github.terrakok.cicerone.Router
import com.github.terrakok.cicerone.sample.Screens
import com.github.terrakok.cicerone.sample.Screens.Sample
import moxy.InjectViewState
import moxy.MvpPresenter
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledFuture
import java.util.concurrent.TimeUnit
/**
* Created by Konstantin Tskhovrebov (aka @terrakok)
* on 11.10.16
*/
@InjectViewState
class SamplePresenter(
private val router: Router,
private val screenNumber: Int
) : MvpPresenter<SampleView>() {
private val executorService = Executors.newSingleThreadScheduledExecutor()
private var future: ScheduledFuture<*>? = null
fun onBackCommandClick() {
router.exit()
}
fun onForwardCommandClick() {
router.navigateTo(Sample(screenNumber + 1))
}
fun onReplaceCommandClick() {
router.replaceScreen(Sample(screenNumber + 1))
}
fun onNewChainCommandClick() {
router.newChain(
Sample(screenNumber + 1),
Sample(screenNumber + 2),
Sample(screenNumber + 3)
)
}
fun onNewRootChainCommandClick() {
router.newRootChain(
Sample(screenNumber + 1),
Sample(screenNumber + 2),
Sample(screenNumber + 3)
)
}
fun onFinishChainCommandClick() {
router.finishChain()
}
fun onForwardNccCommandClick() {
router.navigateTo(Screens.SemiTransparent())
}
fun onNewRootCommandClick() {
router.newRootScreen(Sample(screenNumber + 1))
}
fun onForwardWithDelayCommandClick() {
future?.cancel(true)
future = executorService.schedule({ //WARNING! Navigation must be only in UI thread.
Handler(Looper.getMainLooper()).post {
router.navigateTo(Sample(screenNumber + 1))
}
}, 5, TimeUnit.SECONDS)
}
fun onBackToCommandClick() {
router.backTo(Sample(3))
}
init {
viewState?.setTitle("Screen $screenNumber")
}
}
@@ -0,0 +1,14 @@
package com.github.terrakok.cicerone.sample.mvp.main
import moxy.MvpView
import moxy.viewstate.strategy.AddToEndSingleStrategy
import moxy.viewstate.strategy.StateStrategyType
/**
* Created by Konstantin Tskhovrebov (aka @terrakok)
* on 11.10.16
*/
@StateStrategyType(AddToEndSingleStrategy::class)
interface SampleView : MvpView {
fun setTitle(title: String)
}
@@ -0,0 +1,30 @@
package com.github.terrakok.cicerone.sample.mvp.start
import com.github.terrakok.cicerone.Router
import com.github.terrakok.cicerone.sample.Screens.BottomNavigation
import com.github.terrakok.cicerone.sample.Screens.Main
import com.github.terrakok.cicerone.sample.Screens.Profile
import moxy.MvpPresenter
/**
* Created by terrakok 21.11.16
*/
class StartActivityPresenter(private val router: Router) : MvpPresenter<StartActivityView>() {
fun onOrdinaryPressed() {
router.navigateTo(Main())
}
fun onMultiPressed() {
router.navigateTo(BottomNavigation())
}
fun onResultWithAnimationPressed() {
router.navigateTo(Profile())
}
fun onBackPressed() {
router.exit()
}
}
@@ -0,0 +1,8 @@
package com.github.terrakok.cicerone.sample.mvp.start
import moxy.MvpView
/**
* Created by terrakok 21.11.16
*/
interface StartActivityView : MvpView
@@ -0,0 +1,18 @@
package com.github.terrakok.cicerone.sample.subnavigation
import com.github.terrakok.cicerone.Cicerone
import com.github.terrakok.cicerone.Cicerone.Companion.create
import com.github.terrakok.cicerone.Router
import java.util.*
/**
* Created by terrakok 27.11.16
*/
class LocalCiceroneHolder {
private val containers = HashMap<String, Cicerone<Router>>()
fun getCicerone(containerTag: String): Cicerone<Router> =
containers.getOrPut(containerTag) {
create()
}
}
@@ -0,0 +1,94 @@
package com.github.terrakok.cicerone.sample.ui.animations
import android.os.Bundle
import android.transition.ChangeBounds
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentTransaction
import com.github.terrakok.cicerone.Command
import com.github.terrakok.cicerone.Navigator
import com.github.terrakok.cicerone.NavigatorHolder
import com.github.terrakok.cicerone.Replace
import com.github.terrakok.cicerone.androidx.AppNavigator
import com.github.terrakok.cicerone.androidx.FragmentScreen
import com.github.terrakok.cicerone.sample.R
import com.github.terrakok.cicerone.sample.SampleApplication
import com.github.terrakok.cicerone.sample.Screens.ProfileInfo
import com.github.terrakok.cicerone.sample.ui.animations.photos.SelectPhotoFragment
import com.github.terrakok.cicerone.sample.ui.animations.profile.ProfileFragment
import com.github.terrakok.cicerone.sample.ui.common.BackButtonListener
import javax.inject.Inject
/**
* Created by Konstantin Tskhovrebov (aka @terrakok) on 14.07.17.
*/
class ProfileActivity : AppCompatActivity() {
@Inject
lateinit var navigatorHolder: NavigatorHolder
override fun onCreate(savedInstanceState: Bundle?) {
SampleApplication.INSTANCE.appComponent.inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_container)
if (savedInstanceState == null) {
navigator.applyCommands(arrayOf<Command>(Replace(ProfileInfo())))
}
}
override fun onResumeFragments() {
super.onResumeFragments()
navigatorHolder.setNavigator(navigator)
}
override fun onPause() {
navigatorHolder.removeNavigator()
super.onPause()
}
private val navigator: Navigator = object : AppNavigator(this, R.id.container) {
override fun setupFragmentTransaction(
screen: FragmentScreen,
fragmentTransaction: FragmentTransaction,
currentFragment: Fragment?,
nextFragment: Fragment
) {
if (currentFragment is ProfileFragment
&& nextFragment is SelectPhotoFragment) {
setupSharedElementForProfileToSelectPhoto(
currentFragment,
nextFragment,
fragmentTransaction
)
}
}
}
private fun setupSharedElementForProfileToSelectPhoto(profileFragment: ProfileFragment,
selectPhotoFragment: SelectPhotoFragment,
fragmentTransaction: FragmentTransaction) {
val changeBounds = ChangeBounds()
selectPhotoFragment.sharedElementEnterTransition = changeBounds
selectPhotoFragment.sharedElementReturnTransition = changeBounds
profileFragment.sharedElementEnterTransition = changeBounds
profileFragment.sharedElementReturnTransition = changeBounds
val view = profileFragment.avatarViewForAnimation
fragmentTransaction.addSharedElement(view!!, PHOTO_TRANSITION)
selectPhotoFragment.setAnimationDestinationId((view.tag as Int))
}
override fun onBackPressed() {
val fragment = supportFragmentManager.findFragmentById(R.id.container)
if (fragment != null && fragment is BackButtonListener
&& (fragment as BackButtonListener).onBackPressed()) {
return
} else {
super.onBackPressed()
}
}
companion object {
const val PHOTO_TRANSITION = "photo_trasition"
}
}
@@ -0,0 +1,112 @@
package com.github.terrakok.cicerone.sample.ui.animations.photos
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import com.github.terrakok.cicerone.Router
import com.github.terrakok.cicerone.sample.SampleApplication
import com.github.terrakok.cicerone.sample.databinding.FragmentSelectPhotoBinding
import com.github.terrakok.cicerone.sample.mvp.animation.photos.SelectPhotoPresenter
import com.github.terrakok.cicerone.sample.mvp.animation.photos.SelectPhotoView
import com.github.terrakok.cicerone.sample.ui.animations.ProfileActivity
import com.github.terrakok.cicerone.sample.ui.common.BackButtonListener
import moxy.MvpAppCompatFragment
import moxy.presenter.InjectPresenter
import moxy.presenter.ProvidePresenter
import javax.inject.Inject
/**
* Created by Konstantin Tskhovrebov (aka @terrakok) on 14.07.17.
*/
class SelectPhotoFragment : MvpAppCompatFragment(), SelectPhotoView, BackButtonListener {
private lateinit var binding: FragmentSelectPhotoBinding
private lateinit var allImages: Array<ImageView>
@Inject
lateinit var router: Router
@InjectPresenter
lateinit var presenter: SelectPhotoPresenter
fun setAnimationDestinationId(resId: Int) {
var arguments = arguments
if (arguments == null) arguments = Bundle()
arguments.putInt(ARG_ANIM_DESTINATION, resId)
setArguments(arguments)
}
private val animationDestionationId: Int
get() = arguments!!.getInt(ARG_ANIM_DESTINATION)
private val resultKey: String
get() = arguments!!.getString(EXTRA_RESULT_KEY)!!
@ProvidePresenter
fun providePresenter() = SelectPhotoPresenter(resultKey, router)
override fun onCreate(savedInstanceState: Bundle?) {
SampleApplication.INSTANCE.appComponent.inject(this)
super.onCreate(savedInstanceState)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = FragmentSelectPhotoBinding.inflate(inflater, container, false)
allImages = arrayOf(
binding.selectImage1,
binding.selectImage2,
binding.selectImage3,
binding.selectImage4
)
return binding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
allImages.forEach {
it.setOnClickListener(clickListener)
}
}
private val clickListener = View.OnClickListener { v ->
allImages.forEach {
it.transitionName = null
}
v.transitionName = ProfileActivity.PHOTO_TRANSITION
presenter.onPhotoClick((v.tag as Int))
}
override fun showPhotos(resourceIds: IntArray) {
if (resourceIds.size >= 4) {
//for shared element animation
val animRes = animationDestionationId
allImages.forEachIndexed {
index, imageView ->
imageView.setImageResource(resourceIds[index])
imageView.tag = resourceIds[index]
imageView.transitionName = if (animRes == resourceIds[index]) ProfileActivity.PHOTO_TRANSITION else null
}
}
}
override fun onBackPressed(): Boolean {
presenter.onBackPressed()
return true
}
companion object {
private const val ARG_ANIM_DESTINATION = "arg_anim_dest"
private const val EXTRA_RESULT_KEY = "extra_result_key"
fun getNewInstance(resultKey: String): SelectPhotoFragment {
return SelectPhotoFragment().apply {
arguments = Bundle().apply {
putString(EXTRA_RESULT_KEY, resultKey)
}
}
}
}
}
@@ -0,0 +1,70 @@
package com.github.terrakok.cicerone.sample.ui.animations.profile
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import com.github.terrakok.cicerone.Router
import com.github.terrakok.cicerone.sample.R
import com.github.terrakok.cicerone.sample.SampleApplication
import com.github.terrakok.cicerone.sample.mvp.animation.profile.ProfilePresenter
import com.github.terrakok.cicerone.sample.mvp.animation.profile.ProfileView
import com.github.terrakok.cicerone.sample.ui.animations.ProfileActivity
import com.github.terrakok.cicerone.sample.ui.common.BackButtonListener
import moxy.MvpAppCompatFragment
import moxy.presenter.InjectPresenter
import moxy.presenter.ProvidePresenter
import javax.inject.Inject
/**
* Created by Konstantin Tskhovrebov (aka @terrakok) on 14.07.17.
*/
class ProfileFragment : MvpAppCompatFragment(), ProfileView, BackButtonListener {
private lateinit var avatar: ImageView
@Inject
lateinit var router: Router
@InjectPresenter
lateinit var presenter: ProfilePresenter
@ProvidePresenter
fun providePresenter() = ProfilePresenter(router)
override fun onCreate(savedInstanceState: Bundle?) {
SampleApplication.INSTANCE.appComponent.inject(this)
super.onCreate(savedInstanceState)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_profile, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
avatar = view.findViewById<View>(R.id.avatar_imageView) as ImageView
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
avatar.transitionName = ProfileActivity.PHOTO_TRANSITION
avatar.setOnClickListener { presenter.onPhotoClicked() }
}
override fun showPhoto(resId: Int) {
avatar.setImageResource(resId)
//for shared element animation
avatar.tag = resId
}
val avatarViewForAnimation: View?
get() = avatar
override fun onBackPressed(): Boolean {
presenter.onBackPressed()
return true
}
}
@@ -0,0 +1,119 @@
package com.github.terrakok.cicerone.sample.ui.bottom
import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import com.ashokvarma.bottomnavigation.BottomNavigationBar
import com.ashokvarma.bottomnavigation.BottomNavigationItem
import com.github.terrakok.cicerone.Router
import com.github.terrakok.cicerone.sample.R
import com.github.terrakok.cicerone.sample.SampleApplication
import com.github.terrakok.cicerone.sample.Screens.Tab
import com.github.terrakok.cicerone.sample.mvp.bottom.BottomNavigationPresenter
import com.github.terrakok.cicerone.sample.mvp.bottom.BottomNavigationView
import com.github.terrakok.cicerone.sample.ui.common.BackButtonListener
import com.github.terrakok.cicerone.sample.ui.common.RouterProvider
import moxy.MvpAppCompatActivity
import moxy.presenter.InjectPresenter
import moxy.presenter.ProvidePresenter
import javax.inject.Inject
/**
* Created by terrakok 25.11.16
*/
class BottomNavigationActivity : MvpAppCompatActivity(), BottomNavigationView, RouterProvider {
private lateinit var bottomNavigationBar: BottomNavigationBar
@Inject
override lateinit var router: Router
@InjectPresenter
lateinit var presenter: BottomNavigationPresenter
@ProvidePresenter
fun createBottomNavigationPresenter() = BottomNavigationPresenter(router)
override fun onCreate(savedInstanceState: Bundle?) {
SampleApplication.INSTANCE.appComponent.inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_bottom)
bottomNavigationBar = findViewById<View>(R.id.ab_bottom_navigation_bar) as BottomNavigationBar
initViews()
if (savedInstanceState == null) {
bottomNavigationBar.selectTab(0, true)
}
}
private fun initViews() {
bottomNavigationBar
.addItem(BottomNavigationItem(R.drawable.ic_android_white_24dp, R.string.tab_android))
.addItem(BottomNavigationItem(R.drawable.ic_bug_report_white_24dp, R.string.tab_bug))
.addItem(BottomNavigationItem(R.drawable.ic_pets_white_24dp, R.string.tab_dog))
.initialise()
bottomNavigationBar.setTabSelectedListener(object : BottomNavigationBar.OnTabSelectedListener {
override fun onTabSelected(position: Int) {
when (position) {
0 -> selectTab("ANDROID")
1 -> selectTab("BUG")
2 -> selectTab("DOG")
}
bottomNavigationBar.selectTab(position, false)
}
override fun onTabUnselected(position: Int) {}
override fun onTabReselected(position: Int) {
onTabSelected(position)
}
})
}
private fun selectTab(tab: String) {
val fm = supportFragmentManager
var currentFragment: Fragment? = null
val fragments = fm.fragments
for (f in fragments) {
if (f.isVisible) {
currentFragment = f
break
}
}
val newFragment = fm.findFragmentByTag(tab)
if (currentFragment != null && newFragment != null && currentFragment === newFragment) return
val transaction = fm.beginTransaction()
if (newFragment == null) {
transaction.add(
R.id.ab_container,
Tab(tab).createFragment(fm.fragmentFactory), tab
)
}
if (currentFragment != null) {
transaction.hide(currentFragment)
}
if (newFragment != null) {
transaction.show(newFragment)
}
transaction.commitNow()
}
override fun onBackPressed() {
val fm = supportFragmentManager
var fragment: Fragment? = null
val fragments = fm.fragments
for (f in fragments) {
if (f.isVisible) {
fragment = f
break
}
}
if (fragment != null && fragment is BackButtonListener
&& (fragment as BackButtonListener).onBackPressed()) {
return
} else {
presenter.onBackPressed()
}
}
}
@@ -0,0 +1,69 @@
package com.github.terrakok.cicerone.sample.ui.bottom
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.github.terrakok.cicerone.sample.databinding.FragmentForwardBinding
import com.github.terrakok.cicerone.sample.mvp.bottom.forward.ForwardPresenter
import com.github.terrakok.cicerone.sample.mvp.bottom.forward.ForwardView
import com.github.terrakok.cicerone.sample.ui.common.BackButtonListener
import com.github.terrakok.cicerone.sample.ui.common.RouterProvider
import moxy.MvpAppCompatFragment
import moxy.presenter.InjectPresenter
import moxy.presenter.ProvidePresenter
/**
* Created by terrakok 26.11.16
*/
class ForwardFragment : MvpAppCompatFragment(), ForwardView, BackButtonListener {
private lateinit var binding: FragmentForwardBinding
@InjectPresenter
lateinit var presenter: ForwardPresenter
@ProvidePresenter
fun provideForwardPresenter(): ForwardPresenter {
return ForwardPresenter(
arguments!!.getString(EXTRA_NAME),
(parentFragment as RouterProvider).router,
arguments!!.getInt(EXTRA_NUMBER)
)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = FragmentForwardBinding.inflate(inflater)
return binding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
binding.toolbar.title = arguments!!.getString(EXTRA_NAME)
binding.toolbar.setNavigationOnClickListener { presenter.onBackPressed() }
binding.forwardButton.setOnClickListener { presenter.onForwardPressed() }
binding.githubButton.setOnClickListener { presenter.onGithubPressed() }
}
override fun setChainText(chainText: String) {
binding.chainText.text = chainText
}
override fun onBackPressed(): Boolean {
presenter.onBackPressed()
return true
}
companion object {
private const val EXTRA_NAME = "extra_name"
private const val EXTRA_NUMBER = "extra_number"
fun getNewInstance(name: String?, number: Int): ForwardFragment {
return ForwardFragment().apply {
arguments = Bundle().apply {
putString(EXTRA_NAME, name)
putInt(EXTRA_NUMBER, number)
}
}
}
}
}
@@ -0,0 +1,88 @@
package com.github.terrakok.cicerone.sample.ui.bottom
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.github.terrakok.cicerone.Cicerone
import com.github.terrakok.cicerone.Navigator
import com.github.terrakok.cicerone.Router
import com.github.terrakok.cicerone.androidx.AppNavigator
import com.github.terrakok.cicerone.sample.R
import com.github.terrakok.cicerone.sample.SampleApplication
import com.github.terrakok.cicerone.sample.Screens.Forward
import com.github.terrakok.cicerone.sample.subnavigation.LocalCiceroneHolder
import com.github.terrakok.cicerone.sample.ui.common.BackButtonListener
import com.github.terrakok.cicerone.sample.ui.common.RouterProvider
import javax.inject.Inject
/**
* Created by terrakok 25.11.16
*/
class TabContainerFragment : Fragment(), RouterProvider, BackButtonListener {
private val navigator: Navigator by lazy {
AppNavigator(activity!!, R.id.ftc_container, childFragmentManager)
}
@Inject
lateinit var ciceroneHolder: LocalCiceroneHolder
private val containerName: String
get() = arguments!!.getString(EXTRA_NAME)
override fun onCreate(savedInstanceState: Bundle?) {
SampleApplication.INSTANCE.appComponent.inject(this)
super.onCreate(savedInstanceState)
}
private val cicerone: Cicerone<Router>
get() = ciceroneHolder.getCicerone(containerName)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_tab_container, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
if (childFragmentManager.findFragmentById(R.id.ftc_container) == null) {
cicerone.router.replaceScreen(Forward(containerName, 0))
}
}
override fun onResume() {
super.onResume()
cicerone.getNavigatorHolder().setNavigator(navigator)
}
override fun onPause() {
cicerone.getNavigatorHolder().removeNavigator()
super.onPause()
}
override val router: Router
get() = cicerone.router
override fun onBackPressed(): Boolean {
val fragment = childFragmentManager.findFragmentById(R.id.ftc_container)
return if (fragment != null && fragment is BackButtonListener
&& (fragment as BackButtonListener).onBackPressed()) {
true
} else {
(activity as RouterProvider?)!!.router.exit()
true
}
}
companion object {
private const val EXTRA_NAME = "tcf_extra_name"
fun getNewInstance(name: String?) =
TabContainerFragment().apply {
arguments = Bundle().apply {
putString(EXTRA_NAME, name)
}
}
}
}
@@ -0,0 +1,8 @@
package com.github.terrakok.cicerone.sample.ui.common
/**
* Created by terrakok 26.11.16
*/
interface BackButtonListener {
fun onBackPressed(): Boolean
}
@@ -0,0 +1,10 @@
package com.github.terrakok.cicerone.sample.ui.common
import com.github.terrakok.cicerone.Router
/**
* Created by terrakok 25.11.16
*/
interface RouterProvider {
val router: Router
}
@@ -0,0 +1,34 @@
package com.github.terrakok.cicerone.sample.ui.main
import android.content.Context
import androidx.fragment.app.Fragment
import moxy.MvpAppCompatFragment
import java.lang.ref.WeakReference
abstract class BaseFragment : MvpAppCompatFragment(), ChainScreen {
override fun onAttach(context: Context) {
super.onAttach(context)
val activity = activity
if (activity is ChainHolder) {
(activity as ChainHolder).chain.add(WeakReference<Fragment>(this))
}
}
override fun onDetach() {
val activity = activity
if (activity is ChainHolder) {
val chain = (activity as ChainHolder).chain
val it = chain.iterator()
while (it.hasNext()) {
val fragmentReference = it.next()
val fragment = fragmentReference.get()
if (fragment != null && fragment === this) {
it.remove()
break
}
}
}
super.onDetach()
}
}
@@ -0,0 +1,13 @@
package com.github.terrakok.cicerone.sample.ui.main
import androidx.fragment.app.Fragment
import java.lang.ref.WeakReference
interface ChainHolder {
val chain: MutableList<WeakReference<Fragment>>
}
interface ChainScreen {
val name: String
val creationTime: Long
}
@@ -0,0 +1,95 @@
package com.github.terrakok.cicerone.sample.ui.main
import android.os.Bundle
import android.view.View
import android.widget.TextView
import androidx.fragment.app.Fragment
import com.github.terrakok.cicerone.Command
import com.github.terrakok.cicerone.Navigator
import com.github.terrakok.cicerone.NavigatorHolder
import com.github.terrakok.cicerone.Replace
import com.github.terrakok.cicerone.androidx.AppNavigator
import com.github.terrakok.cicerone.sample.R
import com.github.terrakok.cicerone.sample.SampleApplication
import com.github.terrakok.cicerone.sample.Screens.Sample
import com.github.terrakok.cicerone.sample.ui.common.BackButtonListener
import moxy.MvpAppCompatActivity
import java.lang.ref.WeakReference
import java.util.*
import javax.inject.Inject
/**
* Created by Konstantin Tskhovrebov (aka @terrakok)
* on 11.10.16
*/
class MainActivity : MvpAppCompatActivity(), ChainHolder {
private lateinit var screensSchemeTV: TextView
override val chain = ArrayList<WeakReference<Fragment>>()
@Inject
lateinit var navigatorHolder: NavigatorHolder
private val navigator: Navigator = object : AppNavigator(this, R.id.main_container) {
override fun applyCommands(commands: Array<out Command>) {
super.applyCommands(commands)
supportFragmentManager.executePendingTransactions()
printScreensScheme()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
SampleApplication.INSTANCE.appComponent.inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
screensSchemeTV = findViewById<View>(R.id.screens_scheme) as TextView
if (savedInstanceState == null) {
navigator.applyCommands(arrayOf<Command>(Replace(Sample(1))))
} else {
printScreensScheme()
}
}
override fun onResumeFragments() {
super.onResumeFragments()
navigatorHolder.setNavigator(navigator)
}
override fun onPause() {
navigatorHolder.removeNavigator()
super.onPause()
}
override fun onBackPressed() {
val fragment = supportFragmentManager.findFragmentById(R.id.main_container)
if (fragment != null && fragment is BackButtonListener
&& (fragment as BackButtonListener).onBackPressed()) {
return
} else {
super.onBackPressed()
}
}
private fun printScreensScheme() {
val fragments = ArrayList<ChainScreen>()
for (fragmentReference in chain) {
val fragment = fragmentReference.get()
if (fragment != null && fragment is ChainScreen) {
fragments.add(fragment)
}
}
fragments.sortWith { f1, f2 ->
val t = f1.creationTime - f2.creationTime
if (t > 0) 1 else if (t < 0) -1 else 0
}
val keys = ArrayList<String>()
for (fragment in fragments) {
keys.add(fragment.name)
}
screensSchemeTV.text = "Chain: $keys"
}
}
@@ -0,0 +1,89 @@
package com.github.terrakok.cicerone.sample.ui.main
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.github.terrakok.cicerone.Router
import com.github.terrakok.cicerone.sample.SampleApplication
import com.github.terrakok.cicerone.sample.databinding.FragmentSampleBinding
import com.github.terrakok.cicerone.sample.mvp.main.SamplePresenter
import com.github.terrakok.cicerone.sample.mvp.main.SampleView
import com.github.terrakok.cicerone.sample.ui.common.BackButtonListener
import moxy.presenter.InjectPresenter
import moxy.presenter.ProvidePresenter
import javax.inject.Inject
/**
* Created by Konstantin Tskhovrebov (aka @terrakok)
* on 11.10.16
*/
class SampleFragment : BaseFragment(), SampleView, BackButtonListener {
lateinit var binding: FragmentSampleBinding
@Inject
lateinit var router: Router
@InjectPresenter
lateinit var presenter: SamplePresenter
@ProvidePresenter
fun createSamplePresenter() = SamplePresenter(router, arguments!!.getInt(EXTRA_NUMBER))
override val name: String
get() = arguments!!.getInt(EXTRA_NUMBER).toString()
override val creationTime: Long
get() = arguments!!.getLong(EXTRA_TIME, 0L)
override fun onCreate(savedInstanceState: Bundle?) {
SampleApplication.INSTANCE.appComponent.inject(this)
super.onCreate(savedInstanceState)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = FragmentSampleBinding.inflate(inflater)
return binding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
binding.toolbar.setNavigationOnClickListener { presenter.onBackCommandClick() }
binding.backCommand.setOnClickListener { presenter.onBackCommandClick() }
binding.forwardCommand.setOnClickListener { presenter.onForwardCommandClick() }
binding.replaceCommand.setOnClickListener { presenter.onReplaceCommandClick() }
binding.newChainCommand.setOnClickListener { presenter.onNewChainCommandClick() }
binding.newRootChainCommand.setOnClickListener { presenter.onNewRootChainCommandClick() }
binding.newRootCommand.setOnClickListener { presenter.onNewRootCommandClick() }
binding.forwardDelayCommand.setOnClickListener { presenter.onForwardWithDelayCommandClick() }
binding.backToCommand.setOnClickListener { presenter.onBackToCommandClick() }
binding.finishChainCommand.setOnClickListener { presenter.onFinishChainCommandClick() }
binding.forwardNccCommand.setOnClickListener { presenter.onForwardNccCommandClick() }
}
override fun setTitle(title: String) {
binding.toolbar.title = title
}
override fun onBackPressed(): Boolean {
presenter.onBackCommandClick()
return true
}
companion object {
private const val EXTRA_NUMBER = "extra_number"
private const val EXTRA_TIME = "extra_time"
fun getNewInstance(number: Int): SampleFragment {
return SampleFragment().apply {
arguments = Bundle().apply {
putInt(EXTRA_NUMBER, number)
putLong(EXTRA_TIME, System.currentTimeMillis())
}
}
}
}
}
@@ -0,0 +1,46 @@
package com.github.terrakok.cicerone.sample.ui.main
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import com.github.terrakok.cicerone.Router
import com.github.terrakok.cicerone.sample.R
import com.github.terrakok.cicerone.sample.SampleApplication
import com.github.terrakok.cicerone.sample.databinding.FragmentSampleBinding
import com.github.terrakok.cicerone.sample.databinding.FragmentSemitransparentBinding
import com.github.terrakok.cicerone.sample.ui.common.BackButtonListener
import moxy.MvpAppCompatFragment
import javax.inject.Inject
class SemiTransparentFragment : BaseFragment(), BackButtonListener {
lateinit var binding: FragmentSemitransparentBinding
@Inject
lateinit var router: Router
override val name: String = "D"
override val creationTime: Long = System.currentTimeMillis()
override fun onCreate(savedInstanceState: Bundle?) {
SampleApplication.INSTANCE.appComponent.inject(this)
super.onCreate(savedInstanceState)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = FragmentSemitransparentBinding.inflate(inflater)
return binding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
binding.background.setOnClickListener { onBackPressed() }
binding.backButton.setOnClickListener { onBackPressed() }
}
override fun onBackPressed(): Boolean {
router.exit()
return true
}
}
@@ -0,0 +1,70 @@
package com.github.terrakok.cicerone.sample.ui.start
import android.os.Bundle
import android.view.View
import com.github.terrakok.cicerone.Navigator
import com.github.terrakok.cicerone.NavigatorHolder
import com.github.terrakok.cicerone.Router
import com.github.terrakok.cicerone.androidx.AppNavigator
import com.github.terrakok.cicerone.sample.R
import com.github.terrakok.cicerone.sample.SampleApplication
import com.github.terrakok.cicerone.sample.mvp.start.StartActivityPresenter
import com.github.terrakok.cicerone.sample.mvp.start.StartActivityView
import moxy.MvpAppCompatActivity
import moxy.presenter.InjectPresenter
import moxy.presenter.ProvidePresenter
import javax.inject.Inject
/**
* Created by terrakok 21.11.16
*/
class StartActivity : MvpAppCompatActivity(), StartActivityView {
@Inject
lateinit var router: Router
@Inject
lateinit var navigatorHolder: NavigatorHolder
@InjectPresenter
lateinit var presenter: StartActivityPresenter
private val navigator: Navigator = AppNavigator(this, -1)
@ProvidePresenter
fun createStartActivityPresenter() = StartActivityPresenter(router)
override fun onCreate(savedInstanceState: Bundle?) {
SampleApplication.INSTANCE.appComponent.inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_start)
initViews()
}
private fun initViews() {
findViewById<View>(R.id.ordinary_nav_button).setOnClickListener {
presenter.onOrdinaryPressed()
}
findViewById<View>(R.id.multi_nav_button).setOnClickListener {
presenter.onMultiPressed()
}
findViewById<View>(R.id.result_and_anim_button).setOnClickListener {
presenter.onResultWithAnimationPressed()
}
}
override fun onResume() {
super.onResume()
navigatorHolder.setNavigator(navigator)
}
override fun onPause() {
navigatorHolder.removeNavigator()
super.onPause()
}
override fun onBackPressed() {
presenter.onBackPressed()
}
}
@@ -1,29 +0,0 @@
package ru.terrakok.cicerone.sample;
import android.app.Application;
import ru.terrakok.cicerone.sample.dagger.AppComponent;
import ru.terrakok.cicerone.sample.dagger.DaggerAppComponent;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 11.10.16
*/
public class SampleApplication extends Application {
public static SampleApplication INSTANCE;
private AppComponent appComponent;
@Override
public void onCreate() {
super.onCreate();
INSTANCE = this;
}
public AppComponent getAppComponent() {
if (appComponent == null) {
appComponent = DaggerAppComponent.builder().build();
}
return appComponent;
}
}
@@ -1,21 +0,0 @@
package ru.terrakok.cicerone.sample;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 11.10.16
*/
public class Screens {
public static final String SAMPLE_SCREEN = "sample_screen_";
public static final String START_ACTIVITY_SCREEN = "start activity screen";
public static final String MAIN_ACTIVITY_SCREEN = "main activity screen";
public static final String BOTTOM_NAVIGATION_ACTIVITY_SCREEN = "bna screen";
public static final String ANDROID_SCREEN = "android screen";
public static final String BUG_SCREEN = "bug screen";
public static final String DOG_SCREEN = "dog screen";
public static final String FORWARD_SCREEN = "forward screen";
public static final String GITHUB_SCREEN = "github screen";
}
@@ -1,34 +0,0 @@
package ru.terrakok.cicerone.sample.dagger;
import javax.inject.Singleton;
import dagger.Component;
import ru.terrakok.cicerone.sample.dagger.module.LocalNavigationModule;
import ru.terrakok.cicerone.sample.dagger.module.NavigationModule;
import ru.terrakok.cicerone.sample.ui.bottom.BottomNavigationActivity;
import ru.terrakok.cicerone.sample.ui.bottom.TabContainerFragment;
import ru.terrakok.cicerone.sample.ui.main.MainActivity;
import ru.terrakok.cicerone.sample.ui.main.SampleFragment;
import ru.terrakok.cicerone.sample.ui.start.StartActivity;
/**
* Created by terrakok 24.11.16
*/
@Singleton
@Component(modules = {
NavigationModule.class,
LocalNavigationModule.class
})
public interface AppComponent {
void inject(StartActivity activity);
void inject(MainActivity activity);
void inject(SampleFragment fragment);
void inject(BottomNavigationActivity activity);
void inject(TabContainerFragment fragment);
}
@@ -1,21 +0,0 @@
package ru.terrakok.cicerone.sample.dagger.module;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import ru.terrakok.cicerone.sample.subnavigation.LocalCiceroneHolder;
/**
* Created by terrakok 24.11.16
*/
@Module
public class LocalNavigationModule {
@Provides
@Singleton
LocalCiceroneHolder provideLocalNavigationHolder() {
return new LocalCiceroneHolder();
}
}

Some files were not shown because too many files have changed in this diff Show More