├── .gitignore
├── LICENSE
├── README.md
├── README_CN.md
├── Rabbits
├── .gitignore
├── annotations
│ ├── .gitignore
│ ├── build.gradle
│ └── src
│ │ └── main
│ │ └── java
│ │ └── com
│ │ └── kyleduo
│ │ └── rabbits
│ │ └── annotations
│ │ └── Page.java
├── app
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ │ ├── androidTest
│ │ └── java
│ │ │ └── com
│ │ │ └── kyleduo
│ │ │ └── rabbits
│ │ │ └── demo
│ │ │ └── ExampleInstrumentedTest.java
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── assets
│ │ │ └── web.html
│ │ ├── ic_launcher-web.png
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── kyleduo
│ │ │ │ └── rabbits
│ │ │ │ └── demo
│ │ │ │ ├── DemoApplication.java
│ │ │ │ ├── DumpFragment.java
│ │ │ │ ├── EmbeddedFragment.java
│ │ │ │ ├── FragmentContainerActivity.java
│ │ │ │ ├── InterceptorActivity.java
│ │ │ │ ├── ListingFragment.java
│ │ │ │ ├── MainActivity.java
│ │ │ │ ├── SecondFragment.java
│ │ │ │ ├── TestActivity.java
│ │ │ │ ├── TestFragment.java
│ │ │ │ ├── WebFragment.java
│ │ │ │ ├── base
│ │ │ │ ├── BaseActivity.java
│ │ │ │ └── BaseFragment.java
│ │ │ │ └── utils
│ │ │ │ ├── Constants.java
│ │ │ │ └── UriUtils.java
│ │ └── res
│ │ │ ├── anim
│ │ │ ├── fadein.xml
│ │ │ └── fadeout.xml
│ │ │ ├── layout
│ │ │ ├── activity_common.xml
│ │ │ ├── activity_main.xml
│ │ │ ├── activity_test.xml
│ │ │ ├── fragment_web.xml
│ │ │ ├── item.xml
│ │ │ ├── item_section_header.xml
│ │ │ └── toolbar.xml
│ │ │ ├── menu
│ │ │ └── menu_main.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_background.png
│ │ │ ├── ic_launcher_foreground.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_background.png
│ │ │ ├── ic_launcher_foreground.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_background.png
│ │ │ ├── ic_launcher_foreground.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_background.png
│ │ │ ├── ic_launcher_foreground.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_background.png
│ │ │ ├── ic_launcher_foreground.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── values-v23
│ │ │ └── styles.xml
│ │ │ ├── values-w820dp
│ │ │ └── dimens.xml
│ │ │ └── values
│ │ │ ├── colors.xml
│ │ │ ├── dimens.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ └── test
│ │ └── java
│ │ └── com
│ │ └── kyleduo
│ │ └── rabbits
│ │ └── ExampleUnitTest.java
├── build.gradle
├── compiler
│ ├── .gitignore
│ ├── build.gradle
│ └── src
│ │ └── main
│ │ └── java
│ │ └── com
│ │ └── kyleduo
│ │ └── rabbits
│ │ └── compiler
│ │ ├── PageInfo.java
│ │ └── RabbitsCompiler.java
├── gradle.properties
├── gradle
│ ├── gradle-bintray-upload.gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── rabbits
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ │ ├── androidTest
│ │ └── java
│ │ │ └── com
│ │ │ └── kyleduo
│ │ │ └── rabbits
│ │ │ └── ExampleInstrumentedTest.java
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── kyleduo
│ │ │ │ └── rabbits
│ │ │ │ ├── AbstractNavigation.java
│ │ │ │ ├── Action.java
│ │ │ │ ├── ActionParser.java
│ │ │ │ ├── ActivityNavigator.java
│ │ │ │ ├── CleanUpInterceptor.java
│ │ │ │ ├── Interceptor.java
│ │ │ │ ├── InternalInterceptor.java
│ │ │ │ ├── Logger.java
│ │ │ │ ├── Navigation.java
│ │ │ │ ├── NavigationImpl.java
│ │ │ │ ├── Navigator.java
│ │ │ │ ├── NavigatorInterceptor.java
│ │ │ │ ├── PatternInterceptor.java
│ │ │ │ ├── Rabbit.java
│ │ │ │ ├── RabbitConfig.java
│ │ │ │ ├── RabbitResult.java
│ │ │ │ ├── RealDispatcher.java
│ │ │ │ ├── RouteTable.java
│ │ │ │ ├── TargetAssembler.java
│ │ │ │ ├── TargetInfo.java
│ │ │ │ ├── URLEncodeUtils.java
│ │ │ │ └── rules
│ │ │ │ ├── DomainRule.java
│ │ │ │ ├── Element.java
│ │ │ │ ├── NotRule.java
│ │ │ │ ├── PathRule.java
│ │ │ │ ├── QueryRule.java
│ │ │ │ ├── Rule.java
│ │ │ │ ├── RuleSet.java
│ │ │ │ ├── Rules.java
│ │ │ │ ├── SchemeRule.java
│ │ │ │ ├── TargetFlagsRule.java
│ │ │ │ └── UriRule.java
│ │ └── res
│ │ │ └── values
│ │ │ └── strings.xml
│ │ └── test
│ │ └── java
│ │ └── com
│ │ └── kyleduo
│ │ └── rabbits
│ │ └── ExampleUnitTest.java
├── settings.gradle
├── submodule1
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ │ ├── androidTest
│ │ └── java
│ │ │ └── com
│ │ │ └── kyleduo
│ │ │ └── rabbits
│ │ │ └── demo
│ │ │ └── submodule1
│ │ │ └── ExampleInstrumentedTest.java
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── kyleduo
│ │ │ │ └── rabbits
│ │ │ │ └── demo
│ │ │ │ └── submodule1
│ │ │ │ ├── BaseActivity.java
│ │ │ │ ├── SMActivity.java
│ │ │ │ ├── SMActivity2.java
│ │ │ │ └── SMTestActivity.java
│ │ └── res
│ │ │ ├── layout
│ │ │ ├── act_sm1_test.xml
│ │ │ ├── act_sub_module_1.xml
│ │ │ ├── act_sub_module_2.xml
│ │ │ └── toolbar_sm1.xml
│ │ │ └── values
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ └── test
│ │ └── java
│ │ └── com
│ │ └── kyleduo
│ │ └── rabbits
│ │ └── demo
│ │ └── submodule1
│ │ └── ExampleUnitTest.java
└── submodule2
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── kyleduo
│ │ └── rabbits
│ │ └── demo
│ │ └── submodule2
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── kyleduo
│ │ │ └── rabbits
│ │ │ └── demo
│ │ │ └── submodule2
│ │ │ ├── BaseActivity.java
│ │ │ └── SM2Activity.java
│ └── res
│ │ ├── layout
│ │ ├── act_sm2.xml
│ │ └── toolbar_sm2.xml
│ │ └── values
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── kyleduo
│ └── rabbits
│ └── demo
│ └── submodule2
│ └── ExampleUnitTest.java
└── demo
├── demo.apk
└── preview.png
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.ap_
3 |
4 | # Files for the ART/Dalvik VM
5 | *.dex
6 |
7 | # Java class files
8 | *.class
9 |
10 | # Generated files
11 | bin/
12 | gen/
13 | out/
14 |
15 | # Gradle files
16 | .gradle/
17 | build/
18 |
19 | # Local configuration file (sdk path, etc)
20 | local.properties
21 |
22 | # Proguard folder generated by Eclipse
23 | proguard/
24 |
25 | # Log Files
26 | *.log
27 |
28 | # Android Studio Navigation editor temp files
29 | .navigation/
30 |
31 | # Android Studio captures folder
32 | captures/
33 |
34 | # Intellij
35 | *.iml
36 | .idea/workspace.xml
37 |
38 | # Keystore files
39 | *.jks
40 |
41 | .idea/
42 | #gradle.properties
43 |
44 | .DS_Store
45 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Rabbits
2 |
3 | [中文 README](./README_CN.md) [Blog](https://blog.kyleduo.com/2018/03/22/rabbits-100/)
4 |
5 | > **NOTICE** Rabbits has a lot of changes after version 1.0.0. If you’re using Rabbits, read wiki before update.
6 |
7 | 
8 |
9 | ### Demo
10 |
11 | [Download Link](./demo/demo.apk)
12 |
13 | 
14 |
15 |
16 |
17 | ### Quick Glance
18 |
19 | ```java
20 | @Page(value="/test", alias="TEST_ACT", variety=["/test_variety", "/test/{param}"])
21 | public class TestActivity extends AppCompatActivity {}
22 |
23 | // somewhere else
24 | public class MainActivity extends AppCompatActivity {
25 | public void onButtonClicked() {
26 | // every statement works same
27 | Rabbit.from(this).to(P.TEST_ACT).putExtra("param", "value").start();
28 | Rabbit.from(this).to("/test?param=value").start();
29 | Rabbit.from(this).to(P.P_TEST_PARAM("value")).start();
30 | }
31 | }
32 | ```
33 |
34 |
35 |
36 | ### Changes in version 1.0.0
37 |
38 | I released Rabbits last year and have been using it since then. Everything works fine and the “**From-To-Start**” pattern make me almost forget the `new Intent()` stuff. Rabbits simplify the navigation between pages hosted by Activities and Fragments and, with URI as the protocol, navigation between web pages and native pages becomes easier.
39 |
40 | Even though the usage is easier and intuitive, I found some which can be improved. There are 3 points which are more important.
41 |
42 | 1. `mappings.json`. When I first design Rabbits, I design by separating App client from the backend. But in practice, it’s much reasonable and flexible to let backend control the behavior of App.
43 | 2. `Interceptor`. Interceptor is important when you need to control the process of navigation, adding some params or redirecting to another page. It looks silly that you need check whether the url is the one you want when using Interceptor. The condition of url should be bound to the interceptor so only the urls you want can enter the process method, which is that logical.
44 | 3. `obtain`. It’s not consistent when you try to obtain a Fragment object using Rabbits since the towering `obtain` API.
45 |
46 | Therefore, I’ve rethought the design of Rabbits and browsed other router libraries to learn what is really needed of a router. However, my decision is to keep Rabbits simple and make it’s usage much more easier. Rabbits 1.0.0 focus just one thing, navigation through pages.
47 |
48 | Since I redesigned Rabbits from the bottom. Almost all of the APIs have changed. I’m going to list some significant and obvious change below, the others about the new version can be found in wiki.
49 |
50 | 1. `mappings.json` has been removed. You need to annotated name on every page’s class. `@Page()` has changed and you can use it like this: `@Page("/page/path")`.
51 | 2. Initialization API has changed a lot but the new API becomes more fluent and you can finish the initialization by a single link of invocations.
52 | 3. `P` class is still there. Since `mappings.json` is gone so Rabbits generate P using url path specified in `@Page` or you can provide a `alias` field to name that page a friendly name. Field generated from a path may looks like this: **P_PAGE_PATH**. Yes, there is a prefix, `P_`.
53 | 4. The execution chain has been redesigned and interceptors are recommended set during initialization phase.
54 | 5. Usage of in multiple-module become so easy and dependency between modules in different layers become less.
55 |
56 | ### Use with Gradle
57 |
58 | ```groovy
59 | dependencies {
60 | implementation "com.kyleduo.rabbits:rabbits:{latest-version}"
61 | annotationProcessor "com.kyleduo.rabbits:compiler:{latest-version}"
62 | }
63 | ```
64 |
65 | ### From-To-Start pattern
66 |
67 | The From-To-Start pattern is quite simple and intuitive since navigation is just from on page to another.
68 |
69 | ```java
70 | // MainActivity.java
71 | Rabbit.from(this).to(P.P_TEST).start();
72 | ```
73 | ### ProGuard rules
74 |
75 | Since Rabbits generate routing table in compile phase and no reflection during navigation, you just need add this line in your ProGuard rules file.
76 | ```
77 | -keep class com.kyleduo.rabbits.Router { *; }
78 | ```
79 | ### Migration
80 |
81 | If you have been using Rabbits before 1.0.0, read the wiki first before update.
82 |
83 | ### Thanks
84 |
85 | [OKHttp](https://github.com/square/okhttp): I learnt how to implement interceptor chain from OKHttp.
86 |
87 | [ARouter](https://github.com/alibaba/ARouter): The idea`flags` is from ARouter.
88 |
89 |
90 | License
91 | ---
92 |
93 | Licensed under the Apache License, Version 2.0 (the "License");
94 | you may not use this file except in compliance with the License.
95 | You may obtain a copy of the License at
96 |
97 | http://www.apache.org/licenses/LICENSE-2.0
98 |
99 | Unless required by applicable law or agreed to in writing, software
100 | distributed under the License is distributed on an "AS IS" BASIS,
101 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
102 | See the License for the specific language governing permissions and
103 | limitations under the License.
104 |
--------------------------------------------------------------------------------
/README_CN.md:
--------------------------------------------------------------------------------
1 | # Rabbits
2 |
3 | [EN](./README.md) [Blog](https://blog.kyleduo.com/2018/03/22/rabbits-100/)
4 |
5 |
6 | **注意:** Rabbits在1.0.0版本上有非常大的改变,如果你正在使用,请在更新之前阅读wiki。
7 |
8 |
9 |
10 | ### Demo
11 |
12 | [下载链接](./demo/demo.apk)
13 |
14 | 
15 |
16 |
17 |
18 | ### Quick Glance
19 |
20 | ```java
21 | @Page(value="/test", alias="TEST_ACT", variety=["/test_variety", "/test/{param}"])
22 | public class TestActivity extends AppCompatActivity {}
23 |
24 | // somewhere else
25 | public class MainActivity extends AppCompatActivity {
26 | public void onButtonClicked() {
27 | // every statement works same
28 | Rabbit.from(this).to(P.TEST_ACT).putExtra("param", "value").start();
29 | Rabbit.from(this).to("/test?param=value").start();
30 | Rabbit.from(this).to(P.P_TEST_PARAM("value")).start();
31 | }
32 | }
33 | ```
34 |
35 |
36 |
37 | ### 1.0.0版本的变化
38 |
39 | 我在去年开源了Rabbits并且一直使用到现在。期间运行良好并且符合项目的需求,“From-To-Start”模式让我几乎已经忘掉了`new Intent()`这种写法。Rabbits简化了页面之间的导航,不管是Activity之间还是Fragment之间。由于使用URI作为跳转协议,网页和原生页面之间的跳转页变得更加简单。
40 |
41 | 虽然使用上很简单并且符合直觉,我还是发现了一些需要改变的地方。下面是三个主要的切入点:
42 |
43 | 1. `mappings.json`。最开始设计Rabbits 的时候,我把客户端和服务端分开考量。但实际上由后端对App的行为进行控制更灵活也更合理。
44 | 2. `Interceptor`。如果你希望控制导航的流程,比如添加参数或者重定向到另一个页面,Interceptor就变得非常有用。在拦截器里面判断这个请求是否需要被拦截很傻。Url是否要被拦截的条件应该和拦截器绑定到一起,这样的话只有需要被拦截的请求才会执行到拦截器的处理方法。这才是合理的。
45 | 3. `obtain`。使用原有的`obtain`API获取目标Fragment或者Intent的调用过程并不连贯。
46 |
47 | 因此,我重新思考了Rabbits的设计,并且也参考了其他路由库来寻找有哪些其他特性也是一个路由库的必备特性。然而,我觉得让Rabbits继续保持简单小巧,并且让它的使用变得更加简单。Rabbits 1.0.0只专注一件事:页面间的导航。
48 |
49 | 因为我自底向上的重新设计了Rabbits,几乎所有的API都发生了改变。让我来列举一些比较明显且重要的改变,其他的请到wiki中查看。
50 |
51 | 1. `mappings.json`被移除。你现在需要在每个页面的类上设置页面的注解。`@Page`已经改变了,现在你可以这样使用:`@Page("/page/path")`.
52 | 2. 初始化API发生了很大的变化,但是新的API更加简单,一个链式调用就可以完成初始化。
53 | 3. `P`文件得以保留。因为`mappings.json`被移除所以Rabbits使用url生成P文件的字段。你也可以在`@Page`注解中提供`alias`属性来对页面进行友好的命名。从url生成的P文件的字段大概长这个样子:**P_PAGE_PATH**,有一个前缀`P_`。:)
54 | 4. 重新设计了路由的执行链,建议在初始化阶段设置拦截器。
55 | 5. 多Module中的使用更加简单,同时多个Module之间的耦合更少了。
56 |
57 | ### 在Gradle中使用
58 |
59 | ```groovy
60 | dependencies {
61 | implementation "com.kyleduo.rabbits:rabbits:{latest-version}"
62 | annotationProcessor "com.kyleduo.rabbits:compiler:{latest-version}"
63 | }
64 | ```
65 |
66 | ### From-To-Start 模式
67 |
68 | 因为路由就是从一个页面到另一个页面,所以From-To-Start模式非常符合直觉并且易用。
69 |
70 | ```java
71 | // MainActivity.java
72 | Rabbit.from(this).to(P.P_TEST).start();
73 | ```
74 |
75 | ### ProGuard 防混淆规则
76 |
77 | 因为Rabbits在编译阶段生成路由表并且在导航执行阶段没有类名加载,你只需要对路由表类进行防混淆,在ProGuard规则文件中添加如下内容:
78 |
79 | ```
80 | -keep class com.kyleduo.rabbits.Router { *; }
81 | ```
82 |
83 | ### 迁移
84 |
85 | 如果你在使用1.0.0之前的版本,升级前请阅读wiki。
86 |
87 | ### 感谢
88 |
89 | [OKHttp](https://github.com/square/okhttp): 我从OKHttp中学习了拦截器链条的实现。
90 |
91 | [ARouter](https://github.com/alibaba/ARouter): `flags`的想法来自于ARouter。
92 |
93 | ## License
94 |
95 | ```
96 | Licensed under the Apache License, Version 2.0 (the "License");
97 | you may not use this file except in compliance with the License.
98 | You may obtain a copy of the License at
99 |
100 | http://www.apache.org/licenses/LICENSE-2.0
101 |
102 | Unless required by applicable law or agreed to in writing, software
103 | distributed under the License is distributed on an "AS IS" BASIS,
104 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
105 | See the License for the specific language governing permissions and
106 | limitations under the License.
107 | ```
108 |
109 |
--------------------------------------------------------------------------------
/Rabbits/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 | /publish.sh
11 |
--------------------------------------------------------------------------------
/Rabbits/annotations/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/Rabbits/annotations/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'java'
2 |
3 | dependencies {
4 | implementation fileTree(dir: 'libs', include: ['*.jar'])
5 | }
6 |
7 | sourceCompatibility = "1.7"
8 | targetCompatibility = "1.7"
9 |
10 | ext {
11 | POM_ARTIFACT_ID='annotations'
12 | POM_NAME='Rabbits-Annotations'
13 | POM_PACKAGING='aar'
14 | }
15 |
16 | apply from: rootProject.file('gradle/gradle-bintray-upload.gradle')
--------------------------------------------------------------------------------
/Rabbits/annotations/src/main/java/com/kyleduo/rabbits/annotations/Page.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.annotations;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * Annotation for a single page.
10 | *
11 | * Created by kyle on 2016/12/07.
12 | */
13 |
14 | @Target({ElementType.TYPE})
15 | @Retention(RetentionPolicy.CLASS)
16 | public @interface Page {
17 | /**
18 | * Main url pattern, whole url or path.
19 | *
20 | * 1. /foo/bar
21 | * 2. foo://foo.bar/foo/bar
22 | * 3. /foo/{bar:i}
23 | *
24 | * @return url pattern
25 | */
26 | String value();
27 |
28 | /**
29 | * Used when this page can match multiple urls.
30 | * Exp.
31 | * /seg1/seg2/{id}
32 | * /seg1/seg2
33 | * /seg1/seg2/view
34 | * /seg1/seg2/detail
35 | *
36 | * @return other urls that can mark this page than the main one.
37 | */
38 | String[] variety() default {};
39 |
40 | /**
41 | * Flags related to this Page, like Intent flags. You can give meanings to each bit and
42 | * response to different flags in your interceptors.
43 | *
44 | * @return flags in int value
45 | */
46 | int flags() default 0;
47 |
48 | /**
49 | * Used to generate a constant you can use in your code against writing hard-coded urls.
50 | *
51 | * @return alias
52 | */
53 | String alias() default "";
54 | }
55 |
--------------------------------------------------------------------------------
/Rabbits/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/Rabbits/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 27
5 | buildToolsVersion buildToolsVersion
6 | defaultConfig {
7 | applicationId "com.kyleduo.rabbits.demo"
8 | minSdkVersion 14
9 | targetSdkVersion 27
10 | versionCode 1
11 | versionName "1.0"
12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
13 |
14 | javaCompileOptions {
15 | annotationProcessorOptions {
16 | arguments = [rabbits_submodules: "sm1"]
17 | }
18 | }
19 | }
20 |
21 | signingConfigs {
22 | releaseConfig {
23 | storeFile file("demo.jks")
24 | storePassword "12345678"
25 | keyAlias "demo"
26 | keyPassword "12345678"
27 | }
28 | }
29 |
30 | buildTypes {
31 | debug {
32 | minifyEnabled true
33 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
34 | }
35 |
36 | release {
37 | minifyEnabled false
38 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
39 | signingConfig signingConfigs.releaseConfig
40 | }
41 | }
42 | }
43 |
44 | dependencies {
45 | implementation fileTree(include: ['*.jar'], dir: 'libs')
46 | implementation project(':submodule1')
47 | // App's dependencies, including test
48 | implementation "com.android.support:support-annotations:$supportVersion"
49 | implementation "com.android.support:appcompat-v7:$supportVersion"
50 | implementation "com.android.support:recyclerview-v7:$supportVersion"
51 | androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
52 | exclude group: 'com.android.support', module: 'support-annotations'
53 | })
54 | androidTestImplementation('com.android.support.test:runner:0.5', {
55 | exclude group: 'com.android.support', module: 'support-annotations'
56 | })
57 | testImplementation 'junit:junit:4.12'
58 | // Rabbits
59 | implementation project(':rabbits')
60 | annotationProcessor project(':compiler')
61 | // Fragmentation
62 | implementation 'me.yokeyword:fragmentation:0.10.0'
63 | }
64 |
--------------------------------------------------------------------------------
/Rabbits/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/kyle/Documents/developer/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | -keep class com.kyleduo.rabbits.Router { *; }
--------------------------------------------------------------------------------
/Rabbits/app/src/androidTest/java/com/kyleduo/rabbits/demo/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.demo;
2 |
3 | import android.support.test.filters.LargeTest;
4 |
5 | /**
6 | * Instrumentation test, which will execute on an Android device.
7 | *
8 | * @see Testing documentation
9 | */
10 | @LargeTest
11 | public class ExampleInstrumentedTest {
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/Rabbits/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
13 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Rabbits/app/src/main/assets/web.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Test web page
5 |
19 |
20 |
21 |
22 |
23 | Used in a web page.
24 |
25 |
26 |
27 | Rabbits is quite useful when you need handle navigation between web and native.
28 |
29 |
30 |
31 | Load normally.
32 |
33 |
34 | https://kyleduo.com
35 |
36 |
37 |
38 | Additional allowed domains. These domains will be treated as the default one: rabbits.kyleduo.com
39 |
40 |
41 | https://blog.kyleduo.com
42 |
43 |
44 |
45 | Intercepted by RouteTable, navigating to native page.
46 |
47 |
48 | https://rabbits.kyleduo.com
49 |
50 |
51 | https://rabbits.kyleduo.com/test/this
52 | is a param
53 |
54 |
55 | https://rabbits.kyleduo.com/test/listing
56 |
57 |
58 | https://rabbits.kyleduo.com/test/listing?a=b#/sharp
59 |
60 |
61 | https://rabbits.kyleduo.com/sm1/activity
62 |
63 |
64 |
65 | Green channel. By default, this url will open the home page like the one above, but the exists of greenChannel forbid this happen.
66 |
67 |
68 | This feature should implemented by the user and the rule can be any.
69 |
70 |
71 | https://blog.kyleduo.com?greenChannel=1
72 |
73 |
74 | https://blog.kyleduo.com?ignore=1
75 |
76 |
77 |
78 | Custom control param.
79 |
80 |
81 | https://rabbits.kyleduo.com/test/customParam?redirect=1
82 |
83 |
84 |
85 | Special uri.
86 |
87 |
88 | tel:10010
89 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/Rabbits/app/src/main/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kyleduo/Rabbits/bacc53128b2acf700ea64d4729605121e61dd727/Rabbits/app/src/main/ic_launcher-web.png
--------------------------------------------------------------------------------
/Rabbits/app/src/main/java/com/kyleduo/rabbits/demo/DemoApplication.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.demo;
2 |
3 | import android.app.AlertDialog;
4 | import android.app.Application;
5 | import android.content.Context;
6 | import android.content.DialogInterface;
7 | import android.net.Uri;
8 | import android.text.TextUtils;
9 | import android.widget.Toast;
10 |
11 | import com.kyleduo.rabbits.Action;
12 | import com.kyleduo.rabbits.Interceptor;
13 | import com.kyleduo.rabbits.Navigator;
14 | import com.kyleduo.rabbits.P;
15 | import com.kyleduo.rabbits.Rabbit;
16 | import com.kyleduo.rabbits.RabbitConfig;
17 | import com.kyleduo.rabbits.RabbitResult;
18 | import com.kyleduo.rabbits.TargetInfo;
19 | import com.kyleduo.rabbits.demo.base.BaseActivity;
20 | import com.kyleduo.rabbits.demo.base.BaseFragment;
21 | import com.kyleduo.rabbits.demo.utils.Constants;
22 | import com.kyleduo.rabbits.rules.Rule;
23 | import com.kyleduo.rabbits.rules.RuleSet;
24 | import com.kyleduo.rabbits.rules.Rules;
25 |
26 | import me.yokeyword.fragmentation.SupportFragment;
27 |
28 | /**
29 | * Created by kyle on 2016/12/8.
30 | */
31 |
32 | public class DemoApplication extends Application {
33 | @SuppressWarnings("unused")
34 | private static final String TAG = "DemoApplication";
35 |
36 | private static DemoApplication sApp;
37 |
38 | public static DemoApplication get() {
39 | return sApp;
40 | }
41 |
42 | @Override
43 | public void onCreate() {
44 | super.onCreate();
45 | sApp = this;
46 |
47 | Rabbit.init(
48 | RabbitConfig.get()
49 | .schemes("demo", "http", "https")
50 | .domains("rabbits.kyleduo.com", "blog.kyleduo.com", "rabbits.kyleduo.com")
51 | .debug(true))
52 | // do not open any native pages when there is a query named 'greenChannel'
53 | // and it's value equals '1'. This useful when use Rabbit as a bridge between
54 | // native and web page.
55 | .addInterceptor(new Interceptor() {
56 | @Override
57 | public RabbitResult intercept(Dispatcher dispatcher) {
58 | dispatcher.action().discard();
59 | return dispatcher.dispatch(dispatcher.action());
60 | }
61 | }, Rules.set(RuleSet.Relation.OR, Rules.query("greenChannel").is("1"), Rules.query("ignore").is("1")))
62 | .addInterceptor(new Interceptor() {
63 | @Override
64 | public RabbitResult intercept(Dispatcher dispatcher) {
65 | dispatcher.action().setRedirect(true);
66 | return dispatcher.dispatch(dispatcher.action());
67 | }
68 | }, Rules.query("redirect").is("1"))
69 | .addInterceptor(new Interceptor() {
70 | @Override
71 | public RabbitResult intercept(Dispatcher dispatcher) {
72 | // ignore other following interceptors.
73 | dispatcher.action().getExtras().putString("param", "rules");
74 | dispatcher.action().setIgnoreInterceptors(true);
75 | Toast.makeText(DemoApplication.this, "Interceptor by Rules", Toast.LENGTH_SHORT).show();
76 | return dispatcher.dispatch(dispatcher.action());
77 | }
78 | }, Rules.set(RuleSet.Relation.AND, Rules.scheme().startsWith("demo"), Rules.domain().contains("kyleduo"), Rules.path().contains("/rules")))
79 | .addInterceptor(new Interceptor() {
80 | @Override
81 | public RabbitResult intercept(final Dispatcher dispatcher) {
82 | final Action action = dispatcher.action();
83 | if (action.getFrom() instanceof Context) {
84 | action.getExtras().putString("param", "set in interceptor");
85 | new AlertDialog.Builder((Context) action.getFrom())
86 | .setTitle("Intercepted")
87 | .setMessage("The navigation has been intercepted by interceptor. \n\nA param has been set in the interceptor.")
88 | .setPositiveButton("Go on", new DialogInterface.OnClickListener() {
89 | @Override
90 | public void onClick(DialogInterface dialog, int which) {
91 | dispatcher.dispatch(action);
92 | }
93 | })
94 | .setNegativeButton("Cancel", null).create().show();
95 | return null;
96 | }
97 | return dispatcher.dispatch(action);
98 | }
99 | }, Rules.flags().has(1))
100 | // add Interceptor with totally custom rules
101 | .addInterceptor(new Interceptor() {
102 | @Override
103 | public RabbitResult intercept(Dispatcher dispatcher) {
104 | return null;
105 | }
106 | }, new Rule() {
107 | @Override
108 | public boolean verify(Action action) {
109 | return false;
110 | }
111 | })
112 | .registerNavigator(TargetInfo.TYPE_FRAGMENT_V4, new FragmentNavigator())
113 | .registerFallbackNavigator(new FallbackNavigator());
114 | }
115 |
116 | public static class FragmentNavigator implements Navigator {
117 |
118 | @Override
119 | public RabbitResult perform(Action action) {
120 | Object from = action.getFrom();
121 | Object target = action.getTarget();
122 |
123 | boolean isBase = target instanceof BaseFragment;
124 |
125 | if (!isBase) {
126 | return RabbitResult.error("Target invalid");
127 | }
128 |
129 | if ((action.getTargetFlags() & Constants.FLAG_FRAG_EMBED) > 0) {
130 | if (from instanceof BaseFragment) {
131 | if (action.isRedirect()) {
132 | ((BaseFragment) from).replaceFragment((SupportFragment) target, false);
133 | } else {
134 | ((BaseFragment) from).start((SupportFragment) target);
135 | }
136 | return RabbitResult.success();
137 | } else if (from instanceof BaseActivity) {
138 | if (((BaseActivity) from).getTopFragment() != null) {
139 | if (action.isRedirect()) {
140 | ((BaseActivity) from).getTopFragment().replaceFragment((SupportFragment) target, false);
141 | } else {
142 | ((BaseActivity) from).start((SupportFragment) target);
143 | }
144 | }
145 | return RabbitResult.success();
146 | }
147 | }
148 |
149 | return Rabbit.from(from)
150 | .to(P.P_FRAGMENT_CONTAINER)
151 | .action(action)
152 | .putExtra(FragmentContainerActivity.KEY_FRAG_URL, action.getOriginUrl())
153 | .start();
154 | }
155 | }
156 |
157 | public static class FallbackNavigator implements Navigator {
158 |
159 | @Override
160 | public RabbitResult perform(Action action) {
161 | Uri uri = action.createUri();
162 | if (TextUtils.isEmpty(uri.getScheme()) || !uri.getScheme().startsWith("http")) {
163 | uri = uri.buildUpon().scheme("https").build();
164 | }
165 | if (TextUtils.isEmpty(uri.getAuthority())) {
166 | uri = uri.buildUpon().authority("kyleduo.com").build();
167 | }
168 | return Rabbit.from(action.getFrom())
169 | .to(P.P_WEB)
170 | .putExtra(WebFragment.KEY_URL, uri.toString())
171 | .start();
172 | }
173 | }
174 | }
175 |
--------------------------------------------------------------------------------
/Rabbits/app/src/main/java/com/kyleduo/rabbits/demo/DumpFragment.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.demo;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.Nullable;
5 | import android.util.TypedValue;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.widget.ScrollView;
10 | import android.widget.TextView;
11 |
12 | import com.kyleduo.rabbits.Rabbit;
13 | import com.kyleduo.rabbits.annotations.Page;
14 | import com.kyleduo.rabbits.demo.base.BaseFragment;
15 |
16 | /**
17 | * Created by kyle on 2016/12/12.
18 | */
19 | @Page("/dump")
20 | public class DumpFragment extends BaseFragment {
21 |
22 | @Override
23 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
24 | ScrollView sv = new ScrollView(getActivity());
25 | sv.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
26 |
27 | TextView tv = new TextView(getActivity());
28 | tv.setText(Rabbit.dump());
29 | tv.setTextColor(0xFFA6ABB0);
30 | tv.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
31 | int padding = (int) (getResources().getDisplayMetrics().density * 16);
32 | tv.setPadding(padding, padding, padding, padding);
33 | tv.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
34 | sv.addView(tv);
35 |
36 | return sv;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Rabbits/app/src/main/java/com/kyleduo/rabbits/demo/EmbeddedFragment.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.demo;
2 |
3 | import com.kyleduo.rabbits.annotations.Page;
4 | import com.kyleduo.rabbits.demo.base.BaseFragment;
5 | import com.kyleduo.rabbits.demo.utils.Constants;
6 |
7 | /**
8 | * Created by kyle on 28/02/2018.
9 | */
10 |
11 | @Page(value = "/test_embedded", flags = Constants.FLAG_FRAG_EMBED)
12 | public class EmbeddedFragment extends BaseFragment {
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/Rabbits/app/src/main/java/com/kyleduo/rabbits/demo/FragmentContainerActivity.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.demo;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.support.annotation.Nullable;
6 |
7 | import com.kyleduo.rabbits.RabbitResult;
8 | import com.kyleduo.rabbits.Rabbit;
9 | import com.kyleduo.rabbits.annotations.Page;
10 | import com.kyleduo.rabbits.demo.base.BaseActivity;
11 | import com.kyleduo.rabbits.demo.base.BaseFragment;
12 |
13 | import me.yokeyword.fragmentation.SupportFragment;
14 |
15 | /**
16 | * Created by kyle on 2016/12/12.
17 | */
18 | @Page("/fragment_container")
19 | public class FragmentContainerActivity extends BaseActivity {
20 | public static final String KEY_FRAG_URL = "frag_url";
21 |
22 | @Override
23 | protected void onCreate(@Nullable Bundle savedInstanceState) {
24 | super.onCreate(savedInstanceState);
25 | setContentView(R.layout.activity_common);
26 |
27 | if (getTopFragment() == null) {
28 | String uri = getIntent().getStringExtra(KEY_FRAG_URL);
29 | Bundle extras = getIntent().getExtras();
30 | if (extras != null) {
31 | extras.remove(KEY_FRAG_URL);
32 | }
33 | if (uri != null) {
34 | RabbitResult ret = Rabbit.from(this)
35 | .to(uri)
36 | .putExtras(extras)
37 | .obtain();
38 | Object target = ret.getTarget();
39 | if (target instanceof BaseFragment) {
40 | loadRootFragment(R.id.common_fragment_container, (SupportFragment) target);
41 | }
42 | }
43 | }
44 | }
45 |
46 | @Override
47 | protected void onNewIntent(Intent intent) {
48 | super.onNewIntent(intent);
49 | String pattern = intent.getStringExtra(Rabbit.KEY_PATTERN);
50 | Bundle extras = intent.getExtras();
51 | if (extras != null) {
52 | extras.remove(KEY_FRAG_URL);
53 | }
54 | BaseFragment fragment = (BaseFragment) Rabbit.from(this)
55 | .to(intent.getStringExtra(KEY_FRAG_URL))
56 | .putExtras(extras)
57 | .obtain().getTarget();
58 |
59 | if (fragment == null) {
60 | return;
61 | }
62 |
63 | SupportFragment topFragment = getTopFragment();
64 | if (topFragment != null && topFragment.getArguments().getString(Rabbit.KEY_PATTERN, "").equals(pattern)) {
65 | topFragment.replaceFragment(fragment, false);
66 | } else if (topFragment == null) {
67 | loadRootFragment(R.id.common_fragment_container, fragment);
68 | } else {
69 | start(fragment);
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/Rabbits/app/src/main/java/com/kyleduo/rabbits/demo/InterceptorActivity.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.demo;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.os.Bundle;
5 | import android.view.View;
6 | import android.widget.TextView;
7 |
8 | import com.kyleduo.rabbits.P;
9 | import com.kyleduo.rabbits.Rabbit;
10 | import com.kyleduo.rabbits.annotations.Page;
11 | import com.kyleduo.rabbits.demo.base.BaseActivity;
12 |
13 | @Page(value = "/test/interceptor", flags = 1, variety = {"/test/rules"})
14 | public class InterceptorActivity extends BaseActivity {
15 |
16 | @SuppressLint("SetTextI18n")
17 | @Override
18 | protected void onCreate(Bundle savedInstanceState) {
19 | super.onCreate(savedInstanceState);
20 | setContentView(R.layout.activity_test);
21 |
22 | TextView tv = (TextView) findViewById(R.id.params_tv);
23 | tv.setText("param = " + getIntent().getStringExtra("param"));
24 |
25 | findViewById(R.id.back_home_bt).setOnClickListener(new View.OnClickListener() {
26 | @Override
27 | public void onClick(View view) {
28 | Rabbit.from(InterceptorActivity.this)
29 | .to(P.P_)
30 | .clearTop()
31 | .singleTop()
32 | .start();
33 | }
34 | });
35 | }
36 |
37 | @Override
38 | protected void onResume() {
39 | super.onResume();
40 | setTitle(getIntent().getStringExtra(Rabbit.KEY_PATTERN) + "@" + this.getClass().getSimpleName());
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Rabbits/app/src/main/java/com/kyleduo/rabbits/demo/ListingFragment.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.demo;
2 |
3 | import com.kyleduo.rabbits.annotations.Page;
4 | import com.kyleduo.rabbits.demo.base.BaseFragment;
5 |
6 | /**
7 | * Created by kyle on 2016/12/12.
8 | */
9 | @Page("/test/listing")
10 | public class ListingFragment extends BaseFragment {
11 | }
12 |
--------------------------------------------------------------------------------
/Rabbits/app/src/main/java/com/kyleduo/rabbits/demo/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.demo;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.support.v7.widget.LinearLayoutManager;
7 | import android.support.v7.widget.RecyclerView;
8 | import android.view.LayoutInflater;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 | import android.widget.TextView;
12 | import android.widget.Toast;
13 |
14 | import com.kyleduo.rabbits.P;
15 | import com.kyleduo.rabbits.Rabbit;
16 | import com.kyleduo.rabbits.RabbitResult;
17 | import com.kyleduo.rabbits.annotations.Page;
18 | import com.kyleduo.rabbits.demo.base.BaseActivity;
19 |
20 | import java.lang.ref.WeakReference;
21 | import java.util.ArrayList;
22 | import java.util.List;
23 |
24 | @Page("/")
25 | public class MainActivity extends BaseActivity {
26 |
27 | @Override
28 | protected void onCreate(Bundle savedInstanceState) {
29 | super.onCreate(savedInstanceState);
30 | ViewGroup view = (ViewGroup) LayoutInflater.from(this).inflate(R.layout.activity_main, null);
31 | setContentView(view);
32 |
33 | List data = new ArrayList<>();
34 |
35 | data.add(new Section(
36 | "Standard Usages",
37 | "/test",
38 | "/test?param=value",
39 | "/test/value",
40 | "demo://rabbits.kyleduo.com/test/value",
41 | "/test_variety",
42 | "xxx://xxx.xxx/xxx?param=xxx"
43 | ));
44 | data.add(new Section("startForResult", "Result: " + P.P_SECOND_ID(1)));
45 | data.add(new Section("Interceptors", "/test/interceptor", "/test/rules"));
46 | data.add(new Section("Fallback", "https://kyleduo.com"));
47 | data.add(new Section("Control", "Redirect: /test/redirect", "Anim: /test/animation", "IgnoreInterceptor: /test/interceptor?not_intercepted", "IgnoreFallback: https://kyleduo.com"));
48 | data.add(new Section("Fragment", "/test_fragment", "/web"));
49 | data.add(new Section("Dump route table", "/dump"));
50 | data.add(new Section("Multiple modules", "/sm1/activity", "/sm2/activity"));
51 | data.add(new Section("copyright @kyleduo 2018"));
52 | data.add(new Section(""));
53 |
54 | RecyclerView rv = (RecyclerView) findViewById(R.id.recycler_view);
55 | rv.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
56 | rv.setAdapter(new TestAdapter(this, data));
57 | }
58 |
59 | @Override
60 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
61 | super.onActivityResult(requestCode, resultCode, data);
62 | if (requestCode == 100) {
63 | if (resultCode == Activity.RESULT_OK && data != null) {
64 | Toast.makeText(this, "Result: " + data.getStringExtra("result"), Toast.LENGTH_SHORT).show();
65 | } else {
66 | Toast.makeText(this, "Result: " + resultCode, Toast.LENGTH_SHORT).show();
67 | }
68 | }
69 | }
70 |
71 | static class Section {
72 | String name;
73 | List- items;
74 |
75 | Section(String name, String... items) {
76 | this.name = name;
77 | this.items = new ArrayList<>();
78 | if (items != null) {
79 | for (String item : items) {
80 | this.items.add(new Item(item));
81 | }
82 | }
83 | }
84 | }
85 |
86 | static class Item {
87 | String name;
88 |
89 | Item(String name) {
90 | this.name = name;
91 | }
92 | }
93 |
94 | static class IndexPath {
95 | int section;
96 | int index;
97 |
98 | IndexPath(int section, int index) {
99 | this.section = section;
100 | this.index = index;
101 | }
102 |
103 | static IndexPath create(int section, int index) {
104 | return new IndexPath(section, index);
105 | }
106 | }
107 |
108 | static class SectionViewHolder extends RecyclerView.ViewHolder {
109 |
110 | TextView titleTv;
111 |
112 | SectionViewHolder(View itemView) {
113 | super(itemView);
114 | titleTv = (TextView) itemView.findViewById(R.id.section_title);
115 | }
116 | }
117 |
118 | static class ItemViewHolder extends RecyclerView.ViewHolder {
119 |
120 | TextView titleTv;
121 |
122 | ItemViewHolder(View itemView) {
123 | super(itemView);
124 | titleTv = (TextView) itemView.findViewById(R.id.item_title);
125 | }
126 | }
127 |
128 | static class TestAdapter extends RecyclerView.Adapter
{
129 | List mData;
130 | List mIndexPaths;
131 | private WeakReference mActRef;
132 |
133 | TestAdapter(MainActivity activity, List data) {
134 | mActRef = new WeakReference<>(activity);
135 | mData = data;
136 | mIndexPaths = new ArrayList<>();
137 | for (int i = 0; i < mData.size(); i++) {
138 | mIndexPaths.add(IndexPath.create(i, -1));
139 | Section s = mData.get(i);
140 | for (int j = 0; j < s.items.size(); j++) {
141 | mIndexPaths.add(IndexPath.create(i, j));
142 | }
143 | }
144 | }
145 |
146 | @Override
147 | public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
148 | if (viewType == 0) {
149 | View view = LayoutInflater.from(DemoApplication.get()).inflate(R.layout.item_section_header, parent, false);
150 | return new SectionViewHolder(view);
151 | } else if (viewType == 1) {
152 | View view = LayoutInflater.from(DemoApplication.get()).inflate(R.layout.item, parent, false);
153 | final ItemViewHolder holder = new ItemViewHolder(view);
154 | view.setOnClickListener(new View.OnClickListener() {
155 | @Override
156 | public void onClick(View v) {
157 | int position = holder.getAdapterPosition();
158 | IndexPath indexPath = mIndexPaths.get(position);
159 | if (indexPath.index >= 0) {
160 | Item item = mData.get(indexPath.section).items.get(indexPath.index);
161 | String url = item.name;
162 | RabbitResult result;
163 | if (url.startsWith("Redirect: ")) {
164 | url = url.substring(10);
165 | result = Rabbit.from(mActRef.get()).to(url).redirect().start();
166 | } else if (url.startsWith("Anim: ")) {
167 | url = url.substring(6);
168 | result = Rabbit.from(mActRef.get()).to(url).setTransitionAnimations(new int[]{R.anim.fadein, R.anim.fadeout}).start();
169 | } else if (url.startsWith("IgnoreInterceptor: ")) {
170 | url = url.substring(19);
171 | result = Rabbit.from(mActRef.get()).to(url).ignoreInterceptors().start();
172 | } else if (url.startsWith("IgnoreFallback: ")) {
173 | url = url.substring(16);
174 | result = Rabbit.from(mActRef.get()).to(url).ignoreFallback().start();
175 | } else if (url.startsWith("Result: ")) {
176 | url = url.substring(8);
177 | result = Rabbit.from(mActRef.get()).to(url).startForResult(100);
178 | } else {
179 | result = Rabbit.from(mActRef.get()).to(url).start();
180 | }
181 | if (result.isFinished() && !result.isSuccess()) {
182 | Toast.makeText(mActRef.get(), "Navigation Fail", Toast.LENGTH_SHORT).show();
183 | }
184 | }
185 | }
186 | });
187 | return holder;
188 | }
189 | return null;
190 | }
191 |
192 | @Override
193 | public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
194 | int type = getItemViewType(position);
195 | IndexPath indexPath = mIndexPaths.get(position);
196 | if (type == 0) {
197 | ((SectionViewHolder) holder).titleTv.setText(mData.get(indexPath.section).name);
198 | } else if (type == 1) {
199 | ((ItemViewHolder) holder).titleTv.setText(mData.get(indexPath.section).items.get(indexPath.index).name);
200 | }
201 | }
202 |
203 | @Override
204 | public int getItemCount() {
205 | return mIndexPaths.size();
206 | }
207 |
208 | @Override
209 | public int getItemViewType(int position) {
210 | IndexPath indexPath = mIndexPaths.get(position);
211 | return indexPath.index == -1 ? 0 : 1;
212 | }
213 | }
214 | }
215 |
--------------------------------------------------------------------------------
/Rabbits/app/src/main/java/com/kyleduo/rabbits/demo/SecondFragment.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.demo;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.support.annotation.Nullable;
7 | import android.view.LayoutInflater;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 | import android.widget.Button;
11 | import android.widget.LinearLayout;
12 |
13 | import com.kyleduo.rabbits.P;
14 | import com.kyleduo.rabbits.Rabbit;
15 | import com.kyleduo.rabbits.annotations.Page;
16 | import com.kyleduo.rabbits.demo.base.BaseFragment;
17 |
18 | /**
19 | * Created by kyle on 2016/12/12.
20 | */
21 | @Page("/second/{id:l}")
22 | public class SecondFragment extends BaseFragment {
23 | @Nullable
24 | @Override
25 | public View onCreateView(final LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
26 | LinearLayout ll = (LinearLayout) super.onCreateView(inflater, container, savedInstanceState);
27 | assert ll != null;
28 |
29 | Button button = new Button(getActivity());
30 | button.setText("Open Embedded Fragment");
31 | button.setTextColor(0xFF49A1FF);
32 | button.setBackgroundDrawable(null);
33 | button.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
34 | button.setOnClickListener(new View.OnClickListener() {
35 | @Override
36 | public void onClick(View view) {
37 | Rabbit.from(SecondFragment.this)
38 | .to(P.P_TEST_EMBEDDED)
39 | .putExtra("param", "send to embedded fragment")
40 | .start();
41 | }
42 | });
43 | ll.addView(button);
44 |
45 |
46 | Button button1 = new Button(getActivity());
47 | button1.setText("Set Result");
48 | button1.setTextColor(0xFF49A1FF);
49 | button1.setBackgroundDrawable(null);
50 | button1.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
51 | button1.setOnClickListener(new View.OnClickListener() {
52 | @Override
53 | public void onClick(View view) {
54 | Intent intent = new Intent();
55 | intent.putExtra("result", "Result content");
56 | getActivity().setResult(Activity.RESULT_OK, intent);
57 | getActivity().finish();
58 | }
59 | });
60 | ll.addView(button1);
61 |
62 | return ll;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Rabbits/app/src/main/java/com/kyleduo/rabbits/demo/TestActivity.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.demo;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.os.Bundle;
5 | import android.view.View;
6 | import android.widget.TextView;
7 |
8 | import com.kyleduo.rabbits.P;
9 | import com.kyleduo.rabbits.Rabbit;
10 | import com.kyleduo.rabbits.annotations.Page;
11 | import com.kyleduo.rabbits.demo.base.BaseActivity;
12 |
13 | @Page(value = "/test", variety = {"/test_variety", "/test/{param}", "xxx://xxx.xxx/xxx"})
14 | public class TestActivity extends BaseActivity {
15 |
16 | @SuppressLint("SetTextI18n")
17 | @Override
18 | protected void onCreate(Bundle savedInstanceState) {
19 | super.onCreate(savedInstanceState);
20 | setContentView(R.layout.activity_test);
21 |
22 | TextView tv = (TextView) findViewById(R.id.params_tv);
23 | tv.setText("param = " + getIntent().getStringExtra("param"));
24 |
25 | findViewById(R.id.back_home_bt).setOnClickListener(new View.OnClickListener() {
26 | @Override
27 | public void onClick(View view) {
28 | Rabbit.from(TestActivity.this)
29 | .to(P.P_)
30 | .clearTop()
31 | .singleTop()
32 | .start();
33 | }
34 | });
35 | }
36 |
37 | @Override
38 | protected void onResume() {
39 | super.onResume();
40 | setTitle(getIntent().getStringExtra(Rabbit.KEY_PATTERN) + "@" + this.getClass().getSimpleName());
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Rabbits/app/src/main/java/com/kyleduo/rabbits/demo/TestFragment.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.demo;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.support.annotation.Nullable;
7 | import android.view.LayoutInflater;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 | import android.widget.Button;
11 | import android.widget.LinearLayout;
12 | import android.widget.Toast;
13 |
14 | import com.kyleduo.rabbits.P;
15 | import com.kyleduo.rabbits.Rabbit;
16 | import com.kyleduo.rabbits.annotations.Page;
17 | import com.kyleduo.rabbits.demo.base.BaseFragment;
18 |
19 | import java.util.Random;
20 |
21 | /**
22 | * Created by kyle on 2016/12/12.
23 | */
24 | @Page("/test_fragment")
25 | public class TestFragment extends BaseFragment {
26 | @Nullable
27 | @Override
28 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
29 | LinearLayout ll = (LinearLayout) super.onCreateView(inflater, container, savedInstanceState);
30 | assert ll != null;
31 |
32 | Button button = createButton("Start Second Fragment");
33 | button.setOnClickListener(new View.OnClickListener() {
34 | @Override
35 | public void onClick(View view) {
36 | Rabbit.from(TestFragment.this)
37 | .to(P.P_SECOND_ID(new Random().nextInt(10)))
38 | .start();
39 | }
40 | });
41 | ll.addView(button);
42 |
43 | Button button1 = createButton("Redirect to Second Fragment");
44 | button1.setOnClickListener(new View.OnClickListener() {
45 | @Override
46 | public void onClick(View view) {
47 | Rabbit.from(TestFragment.this)
48 | .to(P.P_SECOND_ID(new Random().nextInt(10)))
49 | .redirect()
50 | .start();
51 | }
52 | });
53 | ll.addView(button1);
54 |
55 | Button button2 = createButton("start for Result in Fragment");
56 | button2.setOnClickListener(new View.OnClickListener() {
57 | @Override
58 | public void onClick(View view) {
59 | Rabbit.from(TestFragment.this)
60 | .to(P.P_SECOND_ID(new Random().nextInt(10)))
61 | .startForResult(100);
62 | }
63 | });
64 | ll.addView(button2);
65 |
66 | return ll;
67 | }
68 |
69 | private Button createButton(String text) {
70 | Button button = new Button(getActivity());
71 | button.setText(text);
72 | button.setTextColor(0xFF49A1FF);
73 | button.setBackgroundDrawable(null);
74 | button.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
75 | return button;
76 | }
77 |
78 | @Override
79 | public void onActivityResult(int requestCode, int resultCode, Intent data) {
80 | super.onActivityResult(requestCode, resultCode, data);
81 | if (requestCode == 100) {
82 | if (resultCode == Activity.RESULT_OK && data != null) {
83 | Toast.makeText(getActivity(), "Result: " + data.getStringExtra("result"), Toast.LENGTH_SHORT).show();
84 | } else {
85 | Toast.makeText(getActivity(), "Result: " + resultCode, Toast.LENGTH_SHORT).show();
86 | }
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/Rabbits/app/src/main/java/com/kyleduo/rabbits/demo/WebFragment.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.demo;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.support.annotation.Nullable;
7 | import android.view.LayoutInflater;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 | import android.webkit.WebChromeClient;
11 | import android.webkit.WebResourceError;
12 | import android.webkit.WebResourceRequest;
13 | import android.webkit.WebView;
14 | import android.webkit.WebViewClient;
15 |
16 | import com.kyleduo.rabbits.Interceptor;
17 | import com.kyleduo.rabbits.Rabbit;
18 | import com.kyleduo.rabbits.RabbitResult;
19 | import com.kyleduo.rabbits.annotations.Page;
20 | import com.kyleduo.rabbits.demo.base.BaseFragment;
21 | import com.kyleduo.rabbits.rules.Rules;
22 |
23 | /**
24 | * Created by kyle on 2016/12/12.
25 | */
26 | @Page("/web")
27 | public class WebFragment extends BaseFragment {
28 | public static final String KEY_URL = "url";
29 |
30 | private WebView mWebView;
31 |
32 | @SuppressLint("SetJavaScriptEnabled")
33 | @Nullable
34 | @Override
35 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
36 | mWebView = (WebView) LayoutInflater.from(getActivity()).inflate(R.layout.fragment_web, container, false);
37 | mWebView.setWebViewClient(new DefaultWebViewClient());
38 | mWebView.setWebChromeClient(new WebChromeClient());
39 | mWebView.getSettings().setUseWideViewPort(false);
40 | mWebView.getSettings().setJavaScriptEnabled(true);
41 | return mWebView;
42 | }
43 |
44 | @Override
45 | public void onActivityCreated(@Nullable Bundle savedInstanceState) {
46 | super.onActivityCreated(savedInstanceState);
47 | Bundle extras = getArguments();
48 | String url = "file:///android_asset/web.html";
49 | if (extras != null) {
50 | url = extras.getString(KEY_URL, url);
51 | }
52 | mWebView.loadUrl(url);
53 | }
54 |
55 | @Override
56 | public void onDestroy() {
57 | if (mWebView != null) {
58 | mWebView.destroy();
59 | }
60 | super.onDestroy();
61 | }
62 |
63 | private class DefaultWebViewClient extends WebViewClient {
64 |
65 | @Override
66 | public boolean shouldOverrideUrlLoading(WebView view, String url) {
67 | boolean ret = Rabbit.from(WebFragment.this)
68 | .to(url)
69 | .addInterceptor(new Interceptor() {
70 | @Override
71 | public RabbitResult intercept(Dispatcher dispatcher) {
72 | Intent intent = new Intent(Intent.ACTION_DIAL, dispatcher.action().getUri());
73 | startActivity(intent);
74 | return RabbitResult.success();
75 | }
76 | }, Rules.scheme().is("tel"))
77 | .ignoreFallback()
78 | .start()
79 | .isSuccess();
80 | return ret || super.shouldOverrideUrlLoading(view, url);
81 | }
82 |
83 | @Override
84 | public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
85 | super.onReceivedError(view, request, error);
86 | }
87 | }
88 |
89 | @Override
90 | public boolean onBackPressedSupport() {
91 | if (mWebView.canGoBack()) {
92 | mWebView.goBack();
93 | return true;
94 | }
95 | return super.onBackPressedSupport();
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/Rabbits/app/src/main/java/com/kyleduo/rabbits/demo/base/BaseActivity.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.demo.base;
2 |
3 | import android.os.Bundle;
4 | import android.os.Handler;
5 | import android.support.annotation.Nullable;
6 | import android.support.v7.widget.Toolbar;
7 |
8 | import com.kyleduo.rabbits.demo.R;
9 |
10 | import me.yokeyword.fragmentation.SupportActivity;
11 | import me.yokeyword.fragmentation.anim.DefaultHorizontalAnimator;
12 | import me.yokeyword.fragmentation.anim.FragmentAnimator;
13 |
14 | /**
15 | * Created by kyle on 2016/12/12.
16 | */
17 |
18 | public class BaseActivity extends SupportActivity {
19 |
20 | @Override
21 | protected FragmentAnimator onCreateFragmentAnimator() {
22 | return new DefaultHorizontalAnimator();
23 | }
24 |
25 | @Override
26 | protected void onCreate(@Nullable Bundle savedInstanceState) {
27 | super.onCreate(savedInstanceState);
28 |
29 | new Handler().post(new Runnable() {
30 | @Override
31 | public void run() {
32 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
33 | setSupportActionBar(toolbar);
34 |
35 | toolbar.setTitleTextColor(0xFF7A8790);
36 | }
37 | });
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Rabbits/app/src/main/java/com/kyleduo/rabbits/demo/base/BaseFragment.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.demo.base;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.Nullable;
5 | import android.util.TypedValue;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.widget.LinearLayout;
10 | import android.widget.TextView;
11 |
12 | import com.kyleduo.rabbits.Rabbit;
13 | import com.kyleduo.rabbits.demo.R;
14 |
15 | import java.util.Set;
16 |
17 | import me.yokeyword.fragmentation.SupportFragment;
18 |
19 | /**
20 | * Created by kyle on 2016/12/12.
21 | */
22 |
23 | public class BaseFragment extends SupportFragment {
24 |
25 | @Nullable
26 | @Override
27 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
28 | LinearLayout ll = new LinearLayout(getActivity());
29 | ll.setBackgroundColor(0xFFFFFFFF);
30 | ll.setOrientation(LinearLayout.VERTICAL);
31 | int padding = (int) (getResources().getDisplayMetrics().density * 16);
32 | ll.setPadding(padding, padding, padding, padding);
33 | ll.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
34 | TextView tv = createInfoTextView();
35 | ll.addView(tv);
36 | return ll;
37 | }
38 |
39 | protected TextView createInfoTextView() {
40 | TextView tv = new TextView(getActivity());
41 | ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
42 | tv.setLayoutParams(lp);
43 | tv.setTextColor(getResources().getColor(R.color.textColor));
44 | tv.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
45 |
46 | StringBuilder params = new StringBuilder();
47 | params.append("These are the params being used when opening this page.\n\n");
48 | Set keys = getArguments().keySet();
49 | for (String key : keys) {
50 | Object value = getArguments().get(key);
51 | params.append(key).append("\n\t->\t").append(value == null ? "null" : value.toString()).append('\n').append('\n');
52 | }
53 | tv.setText(params.toString());
54 | return tv;
55 | }
56 |
57 | @Override
58 | public void onSupportVisible() {
59 | super.onSupportVisible();
60 | getActivity().setTitle(getArguments().getString(Rabbit.KEY_PATTERN) + "@" + this.getClass().getSimpleName());
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Rabbits/app/src/main/java/com/kyleduo/rabbits/demo/utils/Constants.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.demo.utils;
2 |
3 | /**
4 | * Created by kyle on 27/02/2018.
5 | */
6 |
7 | public class Constants {
8 | // indicate fragment can embed in other Activity than common.
9 | public static final int FLAG_FRAG_EMBED = 1 << 1;
10 | }
11 |
--------------------------------------------------------------------------------
/Rabbits/app/src/main/java/com/kyleduo/rabbits/demo/utils/UriUtils.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.demo.utils;
2 |
3 | import android.net.Uri;
4 |
5 | /**
6 | * Created by kyle on 2017/1/6.
7 | */
8 |
9 | public class UriUtils {
10 | public static boolean matchPath(Uri uri, String path) {
11 | return uri != null && uri.getPath() != null && uri.getPath().equals(path);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/anim/fadein.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/anim/fadeout.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/layout/activity_common.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
13 |
14 |
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/layout/activity_test.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 |
19 |
20 |
28 |
29 |
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/layout/fragment_web.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/layout/item.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
19 |
20 |
26 |
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/layout/item_section_header.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/layout/toolbar.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
15 |
16 |
20 |
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/menu/menu_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
8 |
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kyleduo/Rabbits/bacc53128b2acf700ea64d4729605121e61dd727/Rabbits/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/mipmap-hdpi/ic_launcher_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kyleduo/Rabbits/bacc53128b2acf700ea64d4729605121e61dd727/Rabbits/app/src/main/res/mipmap-hdpi/ic_launcher_background.png
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kyleduo/Rabbits/bacc53128b2acf700ea64d4729605121e61dd727/Rabbits/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kyleduo/Rabbits/bacc53128b2acf700ea64d4729605121e61dd727/Rabbits/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kyleduo/Rabbits/bacc53128b2acf700ea64d4729605121e61dd727/Rabbits/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/mipmap-mdpi/ic_launcher_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kyleduo/Rabbits/bacc53128b2acf700ea64d4729605121e61dd727/Rabbits/app/src/main/res/mipmap-mdpi/ic_launcher_background.png
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kyleduo/Rabbits/bacc53128b2acf700ea64d4729605121e61dd727/Rabbits/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kyleduo/Rabbits/bacc53128b2acf700ea64d4729605121e61dd727/Rabbits/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kyleduo/Rabbits/bacc53128b2acf700ea64d4729605121e61dd727/Rabbits/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kyleduo/Rabbits/bacc53128b2acf700ea64d4729605121e61dd727/Rabbits/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kyleduo/Rabbits/bacc53128b2acf700ea64d4729605121e61dd727/Rabbits/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kyleduo/Rabbits/bacc53128b2acf700ea64d4729605121e61dd727/Rabbits/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kyleduo/Rabbits/bacc53128b2acf700ea64d4729605121e61dd727/Rabbits/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kyleduo/Rabbits/bacc53128b2acf700ea64d4729605121e61dd727/Rabbits/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kyleduo/Rabbits/bacc53128b2acf700ea64d4729605121e61dd727/Rabbits/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kyleduo/Rabbits/bacc53128b2acf700ea64d4729605121e61dd727/Rabbits/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kyleduo/Rabbits/bacc53128b2acf700ea64d4729605121e61dd727/Rabbits/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kyleduo/Rabbits/bacc53128b2acf700ea64d4729605121e61dd727/Rabbits/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kyleduo/Rabbits/bacc53128b2acf700ea64d4729605121e61dd727/Rabbits/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kyleduo/Rabbits/bacc53128b2acf700ea64d4729605121e61dd727/Rabbits/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/values-v23/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 |
13 |
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 | #8F979F
8 |
9 |
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Rabbits
3 |
4 |
--------------------------------------------------------------------------------
/Rabbits/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/Rabbits/app/src/test/java/com/kyleduo/rabbits/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 |
18 |
19 | }
--------------------------------------------------------------------------------
/Rabbits/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | google()
7 | }
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:3.2.0'
10 | classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
11 | classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0'
12 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3'
13 | }
14 | }
15 |
16 | allprojects {
17 | repositories {
18 | jcenter()
19 | google()
20 | }
21 | }
22 |
23 | task clean(type: Delete) {
24 | delete rootProject.buildDir
25 | }
26 |
27 | ext {
28 | minSdkVersion = 14
29 | targetSdkVersion = 25
30 | compileSdkVersion = 27
31 | buildToolsVersion = '28.0.2'
32 |
33 | supportVersion = '27.0.2'
34 | }
35 |
--------------------------------------------------------------------------------
/Rabbits/compiler/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/Rabbits/compiler/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'java'
2 |
3 | dependencies {
4 | implementation fileTree(dir: 'libs', include: ['*.jar'])
5 | implementation project(":annotations")
6 | implementation 'com.google.auto.service:auto-service:1.0-rc2'
7 | implementation 'com.squareup:javapoet:1.9.0'
8 | implementation 'com.google.code.gson:gson:2.8.2'
9 | }
10 |
11 | sourceCompatibility = "1.7"
12 | targetCompatibility = "1.7"
13 |
14 |
15 | ext {
16 | POM_ARTIFACT_ID='compiler'
17 | POM_NAME='Rabbits-Compiler'
18 | POM_PACKAGING='aar'
19 | }
20 |
21 | apply from: rootProject.file('gradle/gradle-bintray-upload.gradle')
--------------------------------------------------------------------------------
/Rabbits/compiler/src/main/java/com/kyleduo/rabbits/compiler/PageInfo.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.compiler;
2 |
3 | import com.squareup.javapoet.ClassName;
4 |
5 | /**
6 | * Information used in compiler.
7 | *
8 | * Created by kyle on 30/01/2018.
9 | */
10 |
11 | class PageInfo {
12 | String url;
13 | ClassName target;
14 | int type;
15 | int flag;
16 | String alias;
17 | boolean main;
18 |
19 | PageInfo(String url, ClassName target, int type, int flag, String alias, boolean main) {
20 | this.url = url;
21 | this.target = target;
22 | this.type = type;
23 | this.flag = flag;
24 | this.alias = alias;
25 | this.main = main;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Rabbits/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
19 | # bintray
20 | GIT_URL=https://github.com/kyleduo/Rabbits.git
21 | PRJ_URL=https://github.com/kyleduo/Rabbits
22 | VERSION_NAME=1.0.2
23 | VERSION_CODE=32
24 | GROUP=com.kyleduo.rabbits
--------------------------------------------------------------------------------
/Rabbits/gradle/gradle-bintray-upload.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.github.dcendents.android-maven'
2 | apply plugin: 'com.jfrog.bintray'
3 |
4 | version = VERSION_NAME
5 | group = GROUP
6 |
7 | install {
8 | repositories.mavenInstaller {
9 | pom {
10 | //noinspection GroovyAssignabilityCheck
11 | project {
12 | packaging POM_PACKAGING
13 | groupId GROUP
14 | artifactId POM_ARTIFACT_ID
15 |
16 | // Add your description here
17 | name POM_NAME
18 | url PRJ_URL
19 |
20 | // Set your license
21 | licenses {
22 | license {
23 | name 'The Apache Software License, Version 2.0'
24 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
25 | }
26 | }
27 | developers {
28 | developer {
29 | id 'kyleduo'
30 | name 'kyleduo'
31 | email 'kyleduo@gmail.com'
32 | }
33 | }
34 | scm {
35 | connection GIT_URL
36 | developerConnection GIT_URL
37 | url PRJ_URL
38 |
39 | }
40 | }
41 | }
42 | }
43 | }
44 | if (project.getPlugins().hasPlugin('com.android.application') ||
45 | project.getPlugins().hasPlugin('com.android.library')) {
46 | task sourcesJar(type: Jar) {
47 | from android.sourceSets.main.java.srcDirs
48 | classifier = 'sources'
49 | }
50 |
51 | task javadoc(type: Javadoc) {
52 | source = android.sourceSets.main.java.srcDirs
53 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) + configurations.compile
54 | failOnError false
55 | }
56 | } else {
57 | task sourcesJar(type: Jar, dependsOn:classes) {
58 | classifier = 'sources'
59 | from sourceSets.main.allSource
60 | }
61 | }
62 |
63 | task javadocJar(type: Jar, dependsOn:javadoc) {
64 | classifier = 'javadoc'
65 | from javadoc.destinationDir
66 | }
67 |
68 | artifacts {
69 | archives javadocJar
70 | archives sourcesJar
71 | }
72 |
73 | Properties properties = new Properties()
74 | properties.load(project.rootProject.file('local.properties').newDataInputStream())
75 | bintray {
76 | user = properties.getProperty('bintray.user')
77 | key = properties.getProperty('bintray.apikey')
78 | configurations = ['archives']
79 | pkg {
80 | repo = 'maven'
81 | name = POM_NAME
82 | userOrg = 'kyleduo'
83 | licenses = ['Apache-2.0']
84 | websiteUrl = PRJ_URL
85 | vcsUrl = GIT_URL
86 | publicDownloadNumbers = true
87 | override = true
88 | publish = true
89 |
90 | version {
91 | name = VERSION_NAME
92 | desc = 'A router library for Android app.'
93 | released = new Date()
94 | vcsTag = version
95 | }
96 | }
97 | }
98 |
99 | bintrayUpload.dependsOn install
--------------------------------------------------------------------------------
/Rabbits/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kyleduo/Rabbits/bacc53128b2acf700ea64d4729605121e61dd727/Rabbits/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/Rabbits/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Dec 19 11:26:50 CST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
7 |
--------------------------------------------------------------------------------
/Rabbits/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/Rabbits/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion rootProject.ext.compileSdkVersion
5 | buildToolsVersion rootProject.ext.buildToolsVersion
6 |
7 | defaultConfig {
8 | minSdkVersion rootProject.ext.minSdkVersion
9 | targetSdkVersion rootProject.ext.targetSdkVersion
10 | versionCode Integer.parseInt(VERSION_CODE)
11 | versionName VERSION_NAME
12 |
13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
14 |
15 | }
16 | buildTypes {
17 | release {
18 | minifyEnabled false
19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
20 | }
21 | }
22 |
23 | lintOptions {
24 | abortOnError false
25 | }
26 | }
27 |
28 | dependencies {
29 | api project(':annotations')
30 |
31 | implementation fileTree(dir: 'libs', include: ['*.jar'])
32 | androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
33 | exclude group: 'com.android.support', module: 'support-annotations'
34 | })
35 | implementation "com.android.support:appcompat-v7:${rootProject.ext.supportVersion}"
36 | testImplementation 'junit:junit:4.12'
37 | }
38 |
39 | ext {
40 | POM_ARTIFACT_ID = 'rabbits'
41 | POM_NAME = 'Rabbits'
42 | POM_PACKAGING = 'aar'
43 | }
44 |
45 | apply from: rootProject.file('gradle/gradle-bintray-upload.gradle')
--------------------------------------------------------------------------------
/Rabbits/rabbits/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/kyle/Documents/developer/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/src/androidTest/java/com/kyleduo/rabbits/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.assertEquals;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.kyleduo.rabbits.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/src/main/java/com/kyleduo/rabbits/AbstractNavigation.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits;
2 |
3 | import android.content.Intent;
4 | import android.os.Build;
5 | import android.os.Bundle;
6 | import android.os.Parcelable;
7 | import android.support.annotation.NonNull;
8 | import android.text.ParcelableSpan;
9 | import android.util.Size;
10 | import android.util.SizeF;
11 | import android.util.SparseArray;
12 |
13 | import com.kyleduo.rabbits.rules.Rule;
14 |
15 | import java.io.Serializable;
16 | import java.lang.reflect.Type;
17 | import java.util.ArrayList;
18 | import java.util.List;
19 | import java.util.Map;
20 |
21 | /**
22 | * Abstract class implements {@link Navigation} interface. Used to assemble a navigation.
23 | *
24 | * Created by kyle on 26/01/2018.
25 | */
26 |
27 | public abstract class AbstractNavigation implements Navigation {
28 | private Action mAction;
29 |
30 | AbstractNavigation(Action action) {
31 | this.mAction = action;
32 | }
33 |
34 | @NonNull
35 | @Override
36 | public Action action() {
37 | return mAction;
38 | }
39 |
40 | @Override
41 | public Navigation addIntentFlags(int flags) {
42 | mAction.setIntentFlags(mAction.getIntentFlags() | flags);
43 | return this;
44 | }
45 |
46 | @Override
47 | public Navigation setIntentFlags(int flags) {
48 | mAction.setIntentFlags(flags);
49 | return this;
50 | }
51 |
52 | @Override
53 | public Navigation newTask() {
54 | addIntentFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
55 | return this;
56 | }
57 |
58 | @Override
59 | public Navigation clearTop() {
60 | addIntentFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
61 | return this;
62 | }
63 |
64 | @Override
65 | public Navigation singleTop() {
66 | addIntentFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
67 | return this;
68 | }
69 |
70 | @Override
71 | public Navigation redirect() {
72 | mAction.setRedirect(true);
73 | return this;
74 | }
75 |
76 | @Override
77 | public Navigation putExtra(@NonNull String key, Object value) {
78 | Bundle extras = mAction.getExtras();
79 | if (extras == null) {
80 | extras = new Bundle();
81 | mAction.setExtras(extras);
82 | }
83 | if (value == null) {
84 | extras.remove(key);
85 | }
86 | if (value instanceof Integer) {
87 | extras.putInt(key, (Integer) value);
88 | } else if (value instanceof String) {
89 | extras.putString(key, (String) value);
90 | } else if (value instanceof Boolean) {
91 | extras.putBoolean(key, (Boolean) value);
92 | } else if (value instanceof Long) {
93 | extras.putLong(key, (Long) value);
94 | } else if (value instanceof Double) {
95 | extras.putDouble(key, (Double) value);
96 | } else if (value instanceof double[]) {
97 | extras.putDoubleArray(key, (double[]) value);
98 | } else if (value instanceof int[]) {
99 | extras.putIntArray(key, (int[]) value);
100 | } else if (value instanceof long[]) {
101 | extras.putLongArray(key, (long[]) value);
102 | } else if (value instanceof boolean[]) {
103 | extras.putBooleanArray(key, (boolean[]) value);
104 | } else if (value instanceof String[]) {
105 | extras.putStringArray(key, (String[]) value);
106 | } else if (value instanceof Byte) {
107 | extras.putByte(key, (Byte) value);
108 | } else if (value instanceof Character) {
109 | extras.putChar(key, (char) value);
110 | } else if (value instanceof Short) {
111 | extras.putShort(key, (short) value);
112 | } else if (value instanceof Float) {
113 | extras.putFloat(key, (Float) value);
114 | } else if (value instanceof CharSequence) {
115 | extras.putCharSequence(key, (CharSequence) value);
116 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && value instanceof Size) {
117 | extras.putSize(key, (Size) value);
118 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && value instanceof SizeF) {
119 | extras.putSizeF(key, (SizeF) value);
120 | } else if (value instanceof Parcelable[]) {
121 | extras.putParcelableArray(key, (Parcelable[]) value);
122 | } else if (value instanceof ArrayList>) {
123 | // WARNING
124 | Type[] mySuperClass = value.getClass().getGenericInterfaces();
125 | Type type = mySuperClass[0];
126 | if (Parcelable.class.isAssignableFrom((Class>) type)) {
127 | //noinspection unchecked
128 | extras.putParcelableArrayList(key, (ArrayList extends Parcelable>) value);
129 | }
130 | } else if (value instanceof SparseArray>) {
131 | Type[] mySuperClass = value.getClass().getGenericInterfaces();
132 | Type type = mySuperClass[0];
133 | if (Parcelable.class.isAssignableFrom((Class>) type)) {
134 | //noinspection unchecked
135 | extras.putSparseParcelableArray(key, (SparseArray extends Parcelable>) value);
136 | }
137 | } else if (value instanceof byte[]) {
138 | extras.putByteArray(key, (byte[]) value);
139 | } else if (value instanceof short[]) {
140 | extras.putShortArray(key, (short[]) value);
141 | } else if (value instanceof char[]) {
142 | extras.putCharArray(key, (char[]) value);
143 | } else if (value instanceof float[]) {
144 | extras.putFloatArray(key, (float[]) value);
145 | } else if (value instanceof CharSequence[]) {
146 | extras.putCharSequenceArray(key, (CharSequence[]) value);
147 | } else if (value instanceof Serializable) {
148 | extras.putSerializable(key, (Serializable) value);
149 | } else if (value instanceof Parcelable) {
150 | extras.putParcelable(key, (Parcelable) value);
151 | }
152 |
153 | return this;
154 | }
155 |
156 | @Override
157 | public Navigation putExtras(Bundle bundle) {
158 | if (bundle == null) {
159 | mAction.setExtras(null);
160 | } else {
161 | Bundle extras = mAction.getExtras();
162 | if (extras == null) {
163 | extras = new Bundle();
164 | mAction.setExtras(extras);
165 | }
166 | extras.putAll(bundle);
167 | }
168 | return this;
169 | }
170 |
171 | @Override
172 | public Navigation putExtras(Map extras) {
173 | if (extras == null) {
174 | mAction.setExtras(null);
175 | } else {
176 | for (Map.Entry e : extras.entrySet()) {
177 | putExtra(e.getKey(), e.getValue());
178 | }
179 | }
180 | return this;
181 | }
182 |
183 | @Override
184 | public Navigation ignoreInterceptors() {
185 | mAction.setIgnoreInterceptors(true);
186 | return this;
187 | }
188 |
189 | @Override
190 | public Navigation ignoreFallback() {
191 | mAction.setIgnoreFallback(true);
192 | return this;
193 | }
194 |
195 | @Override
196 | public Navigation addInterceptor(Interceptor interceptor) {
197 | List l = action().getInterceptors();
198 | if (l == null) {
199 | l = new ArrayList<>();
200 | action().setInterceptors(l);
201 | }
202 | l.add(interceptor);
203 | return this;
204 | }
205 |
206 | @Override
207 | public Navigation addInterceptor(Interceptor interceptor, Rule rule) {
208 | List l = action().getInterceptors();
209 | if (l == null) {
210 | l = new ArrayList<>();
211 | action().setInterceptors(l);
212 | }
213 | l.add(new PatternInterceptor(interceptor, rule));
214 | return this;
215 | }
216 |
217 | @Override
218 | public Navigation justObtain() {
219 | mAction.setJustObtain(true);
220 | return this;
221 | }
222 |
223 | @Override
224 | public Navigation forResult(int requestCode) {
225 | mAction.setRequestCode(requestCode);
226 | return this;
227 | }
228 |
229 | @Override
230 | public Navigation setTransitionAnimations(int[] transitionAnimations) {
231 | mAction.setTransitionAnimations(transitionAnimations);
232 | return this;
233 | }
234 |
235 | @Override
236 | public Navigation action(Action action) {
237 | this.mAction.setAction(action);
238 | return this;
239 | }
240 | }
241 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/src/main/java/com/kyleduo/rabbits/Action.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits;
2 |
3 | import android.net.Uri;
4 | import android.os.Bundle;
5 | import android.support.annotation.Nullable;
6 | import android.text.TextUtils;
7 |
8 | import java.util.Arrays;
9 | import java.util.List;
10 | import java.util.Set;
11 |
12 | /**
13 | * Representing an navigation request.
14 | *
15 | * Created by kyle on 26/01/2018.
16 | */
17 |
18 | @SuppressWarnings({"WeakerAccess", "SameParameterValue", "unused"})
19 | public final class Action {
20 |
21 | /* *****************************
22 | * Set before dispatch
23 | *******************************/
24 |
25 | private Object mFrom;
26 | /**
27 | * Origin url witch the caller use.
28 | */
29 | private String mOriginUrl;
30 | /**
31 | * extras
32 | */
33 | private Bundle mExtras;
34 | /**
35 | * intent flags
36 | */
37 | private int mIntentFlags;
38 | /**
39 | * transition animations
40 | */
41 | private int[] mTransitionAnimations;
42 | /**
43 | * Whether ignore custom interceptors.
44 | * If false, all interceptors added to Rabbit and Navigation would not be
45 | * executed.
46 | */
47 | private boolean mIgnoreInterceptors;
48 | /**
49 | * Whether ignore fallback navigator.
50 | * If false and no page matched, fallback navigator would not be executed and a result with
51 | * status {@link RabbitResult#STATUS_NOT_FOUND} will be returned.
52 | */
53 | private boolean mIgnoreFallback;
54 | /**
55 | * Indicate just obtain the target or really perform a navigation. The target might be an
56 | * Intent instance for Activity or Corresponding instance for Fragment.
57 | */
58 | private boolean mJustObtain;
59 | /**
60 | * If set, {@link android.app.Activity#startActivityForResult} will be invoked rather than
61 | * {@link android.content.Context#startActivity}.
62 | */
63 | private int mRequestCode;
64 | /**
65 | * finish current parent after navigation
66 | */
67 | private boolean mRedirect;
68 |
69 | /**
70 | * Interceptors set by caller.
71 | */
72 | private List mInterceptors;
73 |
74 | /* *****************************
75 | * Generate during dispatch
76 | *******************************/
77 |
78 | /**
79 | * Uri used by Rabbit to find the target page.
80 | */
81 | private Uri mUri;
82 | /**
83 | * The pattern witch matches the uri.
84 | */
85 | private String mTargetPattern;
86 | /**
87 | * Flags of the target page.
88 | */
89 | private int mTargetFlags;
90 | /**
91 | * Type of the target page.
92 | */
93 | private int mTargetType;
94 | /**
95 | * Class of the target page.
96 | */
97 | private Class> mTargetClass;
98 | /**
99 | * Real navigation target, maybe an Intent or a Fragment instance.
100 | */
101 | private Object mTarget;
102 |
103 | /**
104 | * Discard the target page.
105 | * Invoke this method will lead current navigation to Fallback
106 | * (If {@link Navigation#ignoreFallback()}) has not been called and can not revert.
107 | * Probably used when Rabbit used as bridge between web pages and native pages.
108 | */
109 | public void discard() {
110 | setTarget(null);
111 | setTargetType(TargetInfo.TYPE_NONE);
112 | setTargetFlags(0);
113 | }
114 |
115 | /**
116 | * Used internally when navigate with an {@link Action} instance. This probably happens
117 | * in {@link Interceptor} or custom {@link Navigator} where redirecting from current mAction is needed.
118 | *
119 | * @param action origin mAction
120 | */
121 | void setAction(Action action) {
122 | this.setExtras(action.getExtras());
123 | this.setIntentFlags(action.getIntentFlags());
124 | this.setTransitionAnimations(action.getTransitionAnimations());
125 | this.setIgnoreInterceptors(action.isIgnoreInterceptors());
126 | this.setIgnoreFallback(action.isIgnoreFallback());
127 | this.setJustObtain(action.isJustObtain());
128 | this.setRequestCode(action.getRequestCode());
129 | this.setRedirect(action.isRedirect());
130 | }
131 |
132 | /**
133 | * Create uri from url and param. This will parse all param to String and append
134 | * them to the uri.
135 | *
136 | * @return Uri
137 | */
138 | public Uri createUri() {
139 | Uri uri = Uri.parse(getOriginUrl());
140 | Uri.Builder builder = uri.buildUpon();
141 | Bundle extras = getExtras();
142 | if (extras != null) {
143 | Set keys = extras.keySet();
144 | for (String key : keys) {
145 | if (TextUtils.equals(key, Rabbit.KEY_ORIGIN_URL) || TextUtils.equals(key, Rabbit.KEY_PATTERN)) {
146 | continue;
147 | }
148 | Object value = extras.get(key);
149 | if (value == null) {
150 | continue;
151 | }
152 | builder.appendQueryParameter(key, URLEncodeUtils.encode(value.toString()));
153 | }
154 | }
155 | uri = builder.build();
156 | return uri;
157 | }
158 |
159 | public Object getFrom() {
160 | return mFrom;
161 | }
162 |
163 | void setFrom(Object from) {
164 | mFrom = from;
165 | }
166 |
167 | @Nullable
168 | public Object getTarget() {
169 | return mTarget;
170 | }
171 |
172 | void setTarget(Object target) {
173 | mTarget = target;
174 | }
175 |
176 | public String getOriginUrl() {
177 | return mOriginUrl;
178 | }
179 |
180 | void setOriginUrl(String originUrl) {
181 | mOriginUrl = originUrl;
182 | }
183 |
184 | public Uri getUri() {
185 | return mUri;
186 | }
187 |
188 | void setUri(Uri uri) {
189 | mUri = uri;
190 | }
191 |
192 | public boolean isRedirect() {
193 | return mRedirect;
194 | }
195 |
196 | public void setRedirect(boolean redirect) {
197 | mRedirect = redirect;
198 | }
199 |
200 | public Bundle getExtras() {
201 | return mExtras;
202 | }
203 |
204 | void setExtras(Bundle extras) {
205 | mExtras = extras;
206 | }
207 |
208 | public int getIntentFlags() {
209 | return mIntentFlags;
210 | }
211 |
212 | public void setIntentFlags(int intentFlags) {
213 | mIntentFlags = intentFlags;
214 | }
215 |
216 | public int[] getTransitionAnimations() {
217 | return mTransitionAnimations;
218 | }
219 |
220 | public void setTransitionAnimations(int[] transitionAnimations) {
221 | mTransitionAnimations = transitionAnimations;
222 | }
223 |
224 | public int getRequestCode() {
225 | return mRequestCode;
226 | }
227 |
228 | public void setRequestCode(int requestCode) {
229 | mRequestCode = requestCode;
230 | }
231 |
232 | public boolean isIgnoreInterceptors() {
233 | return mIgnoreInterceptors;
234 | }
235 |
236 | public void setIgnoreInterceptors(boolean ignoreInterceptors) {
237 | mIgnoreInterceptors = ignoreInterceptors;
238 | }
239 |
240 | public boolean isIgnoreFallback() {
241 | return mIgnoreFallback;
242 | }
243 |
244 | public void setIgnoreFallback(boolean ignoreFallback) {
245 | mIgnoreFallback = ignoreFallback;
246 | }
247 |
248 | public boolean isJustObtain() {
249 | return mJustObtain;
250 | }
251 |
252 | public void setJustObtain(boolean justObtain) {
253 | mJustObtain = justObtain;
254 | }
255 |
256 | public int getTargetFlags() {
257 | return mTargetFlags;
258 | }
259 |
260 | void setTargetFlags(int targetFlags) {
261 | mTargetFlags = targetFlags;
262 | }
263 |
264 | public int getTargetType() {
265 | return mTargetType;
266 | }
267 |
268 | void setTargetType(int targetType) {
269 | mTargetType = targetType;
270 | }
271 |
272 | public Class> getTargetClass() {
273 | return mTargetClass;
274 | }
275 |
276 | void setTargetClass(Class> targetClass) {
277 | mTargetClass = targetClass;
278 | }
279 |
280 | public String getTargetPattern() {
281 | return mTargetPattern;
282 | }
283 |
284 | public void setTargetPattern(String targetPattern) {
285 | mTargetPattern = targetPattern;
286 | }
287 |
288 | List getInterceptors() {
289 | return mInterceptors;
290 | }
291 |
292 | void setInterceptors(List interceptors) {
293 | mInterceptors = interceptors;
294 | }
295 |
296 | @Override
297 | public String toString() {
298 | return "Action{" +
299 | "mFrom=" + mFrom +
300 | ", mOriginUrl='" + mOriginUrl + '\'' +
301 | ", mExtras=" + mExtras +
302 | ", mIntentFlags=" + mIntentFlags +
303 | ", mTransitionAnimations=" + Arrays.toString(mTransitionAnimations) +
304 | ", mIgnoreInterceptors=" + mIgnoreInterceptors +
305 | ", mIgnoreFallback=" + mIgnoreFallback +
306 | ", mJustObtain=" + mJustObtain +
307 | ", mRequestCode=" + mRequestCode +
308 | ", mRedirect=" + mRedirect +
309 | ", mUri=" + mUri +
310 | ", mTargetPattern='" + mTargetPattern + '\'' +
311 | ", mTargetFlags=" + mTargetFlags +
312 | ", mTargetType=" + mTargetType +
313 | ", mTargetClass=" + mTargetClass +
314 | ", mTarget=" + mTarget +
315 | '}';
316 | }
317 | }
318 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/src/main/java/com/kyleduo/rabbits/ActionParser.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits;
2 |
3 | import android.net.Uri;
4 | import android.os.Bundle;
5 |
6 | import java.util.Map;
7 | import java.util.Set;
8 |
9 | /**
10 | * Find TargetInfo matching the url.
11 | *
12 | * Created by kyle on 30/01/2018.
13 | */
14 |
15 | public class ActionParser implements InternalInterceptor {
16 | @Override
17 | public RabbitResult intercept(Dispatcher dispatcher) {
18 | Logger.i("[!] Parsing...");
19 | Action action = dispatcher.action();
20 | Uri uri = action.getUri();
21 |
22 | // generate uri
23 | if (uri == null) {
24 | String url = action.getOriginUrl();
25 | if (url.contains(":")) {
26 | uri = Uri.parse(url);
27 | } else {
28 | if (!url.startsWith("/")) {
29 | url = "/" + url;
30 | }
31 | url = Rabbit.get().getSchemes().get(0) + "://" + Rabbit.get().getDomains().get(0) + url;
32 | uri = Uri.parse(url);
33 | }
34 | action.setUri(uri);
35 | }
36 |
37 | TargetInfo target = RouteTable.match(action.getUri());
38 |
39 | action.setTargetType(target == null ? TargetInfo.TYPE_NONE : target.type);
40 | action.setTargetFlags(target == null ? 0 : target.flags);
41 | action.setTargetPattern(target == null ? null : target.pattern);
42 | action.setTargetClass(target == null ? null : target.target);
43 |
44 | if (target != null) {
45 | Bundle bundle = new Bundle();
46 |
47 | // params from REST url
48 | Map urlParams = target.params;
49 | if (urlParams != null) {
50 | for (Map.Entry entry : urlParams.entrySet()) {
51 | String key = entry.getKey();
52 | Object value = entry.getValue();
53 | if (value instanceof Integer) {
54 | bundle.putInt(key, (Integer) value);
55 | } else if (value instanceof Float) {
56 | bundle.putFloat(key, (Float) value);
57 | } else if (value instanceof Double) {
58 | bundle.putDouble(key, (Double) value);
59 | } else if (value instanceof String) {
60 | bundle.putString(key, (String) value);
61 | } else {
62 | bundle.putString(key, value.toString());
63 | }
64 | }
65 | }
66 |
67 | // query params
68 | Set keys;
69 | keys = uri.getQueryParameterNames();
70 | if (keys != null && keys.size() > 0) {
71 | for (String key : keys) {
72 | String params = uri.getQueryParameter(key);
73 | bundle.putString(key, params);
74 | }
75 | }
76 |
77 | if (action.getExtras() != null) {
78 | bundle.putAll(action.getExtras());
79 | }
80 |
81 | // Rabbits param, high priority.
82 | bundle.putString(Rabbit.KEY_ORIGIN_URL, action.getOriginUrl());
83 | bundle.putString(Rabbit.KEY_PATTERN, target.pattern);
84 |
85 | action.setExtras(bundle);
86 | } else {
87 | if (action.getExtras() == null) {
88 | action.setExtras(new Bundle());
89 | }
90 | }
91 |
92 | return dispatcher.dispatch(action);
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/src/main/java/com/kyleduo/rabbits/ActivityNavigator.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.support.v4.app.Fragment;
7 |
8 | /**
9 | * Navigate to an Activity.
10 | *
11 | * Created by kyle on 26/01/2018.
12 | */
13 |
14 | public class ActivityNavigator implements Navigator {
15 | @Override
16 | public RabbitResult perform(Action action) {
17 | Object target = action.getTarget();
18 | if (target == null) {
19 | return RabbitResult.notFound(action.getOriginUrl());
20 | }
21 | if (action.isJustObtain()) {
22 | return RabbitResult.success(target);
23 | }
24 |
25 | Object from = action.getFrom();
26 | if (from == null) {
27 | return RabbitResult.error("The \"from\" can not be null");
28 | }
29 | final int requestCode = action.getRequestCode();
30 | final Intent intent = (Intent) action.getTarget();
31 |
32 | Activity activity = null;
33 |
34 | if (requestCode > 0) {
35 | if (from instanceof Activity) {
36 | ((Activity) from).startActivityForResult(intent, requestCode);
37 | } else if (from instanceof Fragment) {
38 | ((Fragment) from).startActivityForResult(intent, requestCode);
39 | } else if (from instanceof android.app.Fragment) {
40 | ((android.app.Fragment) from).startActivityForResult(intent, requestCode);
41 | } else if (from instanceof Context) {
42 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
43 | ((Context) from).startActivity(intent);
44 | } else {
45 | return RabbitResult.error("Invalid from.");
46 | }
47 | } else {
48 | if (from instanceof Activity) {
49 | activity = (Activity) from;
50 | } else if (from instanceof Fragment) {
51 | activity = ((Fragment) from).getActivity();
52 | } else if (from instanceof android.app.Fragment) {
53 | activity = ((android.app.Fragment) from).getActivity();
54 | }
55 | if (activity != null) {
56 | activity.startActivity((Intent) action.getTarget());
57 | } else if (from instanceof Context) {
58 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
59 | ((Context) from).startActivity(intent);
60 | } else {
61 | return RabbitResult.error("Invalid from.");
62 | }
63 | }
64 |
65 | return RabbitResult.success();
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/src/main/java/com/kyleduo/rabbits/CleanUpInterceptor.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits;
2 |
3 | import android.app.Activity;
4 | import android.support.v4.app.Fragment;
5 |
6 | /**
7 | * Dealing with clean up staff like closing current page or setting transition animations.
8 | *
9 | * Created by kyle on 01/03/2018.
10 | */
11 |
12 | public class CleanUpInterceptor implements InternalInterceptor {
13 | @Override
14 | public RabbitResult intercept(Dispatcher dispatcher) {
15 | Action action = dispatcher.action();
16 | RabbitResult result = dispatcher.dispatch(action);
17 | if (result.isSuccess()) {
18 | if (action.getTargetType() == TargetInfo.TYPE_ACTIVITY) {
19 |
20 | Activity act = null;
21 | if (action.getFrom() instanceof Activity) {
22 | act = (Activity) action.getFrom();
23 | } else if (action.getFrom() instanceof Fragment) {
24 | act = ((Fragment) action.getFrom()).getActivity();
25 | } else if (action.getFrom() instanceof android.app.Fragment) {
26 | act = ((android.app.Fragment) action.getFrom()).getActivity();
27 | }
28 |
29 | if (action.isRedirect()) {
30 | if (act != null) {
31 | act.finish();
32 | }
33 | }
34 |
35 | int[] animations = action.getTransitionAnimations();
36 |
37 | if (act != null
38 | && animations != null
39 | && animations.length == 2) {
40 | act.overridePendingTransition(animations[0], animations[1]);
41 | }
42 | }
43 | }
44 | return result;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/src/main/java/com/kyleduo/rabbits/Interceptor.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits;
2 |
3 | /**
4 | * Abstract interceptor used in mAction process chain.
5 | * Every process in the chain is an interceptor.
6 | *
7 | * Created by kyle on 19/12/2017.
8 | */
9 |
10 | public interface Interceptor {
11 | RabbitResult intercept(Dispatcher dispatcher);
12 |
13 | interface Dispatcher {
14 | RabbitResult dispatch(Action action);
15 |
16 | Action action();
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/src/main/java/com/kyleduo/rabbits/InternalInterceptor.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits;
2 |
3 | /**
4 | * Mark an internal interceptor.
5 | *
6 | * Created by kyle on 28/02/2018.
7 | */
8 |
9 | interface InternalInterceptor extends Interceptor {
10 | }
11 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/src/main/java/com/kyleduo/rabbits/Logger.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits;
2 |
3 | import android.util.Log;
4 |
5 | /**
6 | * Internal logger.
7 | *
8 | * Created by kyle on 28/02/2018.
9 | */
10 |
11 | class Logger {
12 | private static final String TAG = "Rabbits";
13 |
14 | static void d(String message) {
15 | if (!Rabbit.sDebug) {
16 | return;
17 | }
18 | Log.d(TAG, message);
19 | }
20 |
21 | static void v(String message) {
22 | if (!Rabbit.sDebug) {
23 | return;
24 | }
25 | Log.v(TAG, message);
26 | }
27 |
28 | static void i(String message) {
29 | if (!Rabbit.sDebug) {
30 | return;
31 | }
32 | Log.i(TAG, message);
33 | }
34 |
35 | static void w(String message) {
36 | if (!Rabbit.sDebug) {
37 | return;
38 | }
39 | Log.w(TAG, message);
40 | }
41 |
42 | static void e(String message) {
43 | if (!Rabbit.sDebug) {
44 | return;
45 | }
46 | Log.e(TAG, message);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/src/main/java/com/kyleduo/rabbits/Navigation.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.NonNull;
5 |
6 | import com.kyleduo.rabbits.rules.Rule;
7 |
8 | import java.util.Map;
9 |
10 | /**
11 | * Represent a Navigation into which the caller put extras and configures and perform the navigation.
12 | *
13 | * Created by kyle on 26/01/2018.
14 | */
15 |
16 | @SuppressWarnings("unused")
17 | public interface Navigation {
18 | Navigation addIntentFlags(int flags);
19 |
20 | Navigation setIntentFlags(int flags);
21 |
22 | Navigation newTask();
23 |
24 | Navigation clearTop();
25 |
26 | Navigation singleTop();
27 |
28 | Navigation putExtra(@NonNull String key, Object value);
29 |
30 | Navigation putExtras(Bundle bundle);
31 |
32 | Navigation putExtras(Map extras);
33 |
34 | Navigation addInterceptor(Interceptor interceptor);
35 |
36 | Navigation addInterceptor(Interceptor interceptor, Rule rule);
37 |
38 | Navigation justObtain();
39 |
40 | Navigation forResult(int requestCode);
41 |
42 | Navigation redirect();
43 |
44 | Navigation ignoreInterceptors();
45 |
46 | Navigation ignoreFallback();
47 |
48 | Navigation setTransitionAnimations(int[] transitionAnimations);
49 |
50 | Navigation action(Action action);
51 |
52 | @NonNull
53 | RabbitResult start();
54 |
55 | @NonNull
56 | RabbitResult startForResult(int requestCode);
57 |
58 | @NonNull
59 | RabbitResult obtain();
60 |
61 | @NonNull
62 | Action action();
63 | }
64 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/src/main/java/com/kyleduo/rabbits/NavigationImpl.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits;
2 |
3 | import android.support.annotation.NonNull;
4 |
5 | /**
6 | * Implementation of navigation.
7 | *
8 | * Created by kyle on 26/01/2018.
9 | */
10 |
11 | public class NavigationImpl extends AbstractNavigation {
12 | protected Rabbit rabbit;
13 |
14 | NavigationImpl(Rabbit rabbit, Action action) {
15 | super(action);
16 | this.rabbit = rabbit;
17 | Logger.d("Navigation created. FROM: " + action.getFrom().toString() + " URL: " + action.getOriginUrl());
18 | }
19 |
20 | @NonNull
21 | @Override
22 | public RabbitResult start() {
23 | return rabbit.dispatch(this);
24 | }
25 |
26 | @NonNull
27 | @Override
28 | public RabbitResult startForResult(int requestCode) {
29 | forResult(requestCode);
30 | return start();
31 | }
32 |
33 | @NonNull
34 | @Override
35 | public RabbitResult obtain() {
36 | justObtain();
37 | return rabbit.dispatch(this);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/src/main/java/com/kyleduo/rabbits/Navigator.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits;
2 |
3 | /**
4 | * Created by kyle on 26/01/2018.
5 | */
6 |
7 | public interface Navigator {
8 |
9 | RabbitResult perform(Action action);
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/src/main/java/com/kyleduo/rabbits/NavigatorInterceptor.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits;
2 |
3 | import android.util.SparseArray;
4 |
5 | /**
6 | * Final phase of navigation.
7 | *
8 | * Created by kyle on 26/01/2018.
9 | */
10 |
11 | class NavigatorInterceptor implements InternalInterceptor {
12 |
13 | private SparseArray mNavigators;
14 |
15 | NavigatorInterceptor(SparseArray navigators) {
16 | mNavigators = navigators;
17 | }
18 |
19 | @Override
20 | public RabbitResult intercept(Dispatcher dispatcher) {
21 | Logger.i("[!] Navigating...");
22 | if (mNavigators == null) {
23 | throw new NullPointerException("No verify navigator");
24 | }
25 | Action action = dispatcher.action();
26 |
27 | // Just return the target when "justObtain" was specific.
28 | if (action.isJustObtain() && action.getTarget() != null) {
29 | return RabbitResult.success(action.getTarget());
30 | }
31 |
32 | boolean notFound = false;
33 | if (action.getTarget() == null || action.getTargetType() == TargetInfo.TYPE_NONE) {
34 | notFound = true;
35 | if (action.isIgnoreFallback()) {
36 | return RabbitResult.notFound(action.getOriginUrl());
37 | }
38 | }
39 |
40 | // Normal navigation or fallback navigation all handled here.
41 | Navigator navigator = mNavigators.get(action.getTargetType());
42 | if (navigator == null) {
43 | if (notFound) {
44 | // need to be handled by fallback.
45 | // fallback handler isn't set.
46 | return RabbitResult.notFound(action.getOriginUrl());
47 | } else {
48 | return RabbitResult.error("Navigator not found for type " + action.getTargetType());
49 | }
50 | }
51 | return navigator.perform(action);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/src/main/java/com/kyleduo/rabbits/PatternInterceptor.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits;
2 |
3 | import com.kyleduo.rabbits.rules.Rule;
4 |
5 | /**
6 | * Used for Interceptor which wants to be invoked when the url match the pattern
7 | *
8 | * Created by kyle on 30/01/2018.
9 | */
10 |
11 | final class PatternInterceptor implements Interceptor {
12 |
13 | private Interceptor mInterceptor;
14 | private Rule mRule;
15 |
16 | PatternInterceptor(Interceptor interceptor, Rule rule) {
17 | mInterceptor = interceptor;
18 | this.mRule = rule;
19 | }
20 |
21 | @Override
22 | public RabbitResult intercept(Dispatcher dispatcher) {
23 | final Action action = dispatcher.action();
24 | if (mRule != null && mRule.verify(action)) {
25 | return mInterceptor.intercept(dispatcher);
26 | }
27 | return dispatcher.dispatch(dispatcher.action());
28 | }
29 |
30 | @Override
31 | public String toString() {
32 | return "Rule: " + mRule.toString() + " Interceptor: " + mInterceptor.toString();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/src/main/java/com/kyleduo/rabbits/Rabbit.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.support.v4.app.Fragment;
6 | import android.util.SparseArray;
7 |
8 | import com.kyleduo.rabbits.rules.Rule;
9 |
10 | import java.lang.reflect.Method;
11 | import java.util.ArrayList;
12 | import java.util.List;
13 |
14 | /**
15 | * Normal usage likes this.
16 | *
17 | * Rabbit.from(activity)
18 | * .to("http://rabbits.kyleduo.com/some/path")
19 | * .start();
20 | *
21 | *
22 | * Created by kyle on 2016/12/7.
23 | */
24 |
25 | @SuppressWarnings({"WeakerAccess", "unused", "UnusedReturnValue"})
26 | public final class Rabbit {
27 | private static final String TAG = Rabbit.class.getSimpleName();
28 | private static final String PACKAGE = "com.kyleduo.rabbits";
29 | private static final String ROUTER_CLASS_NAME = PACKAGE + ".Router";
30 | private static final String GENERATE_METHOD_NAME = "generate";
31 | public static final String KEY_ORIGIN_URL = "rabbits_origin_url";
32 | public static final String KEY_PATTERN = "rabbits_pattern";
33 |
34 | private List mSchemes;
35 | private List mDomains;
36 | private List mInterceptors = new ArrayList<>();
37 | private SparseArray mNavigators = new SparseArray<>();
38 |
39 | private ActionParser mActionParser;
40 | private TargetAssembler mTargetAssembler;
41 | private CleanUpInterceptor mCleanUpInterceptor;
42 |
43 | private static Rabbit sInstance;
44 | static boolean sDebug;
45 |
46 | private Rabbit(RabbitConfig config) {
47 | this.registerNavigator(TargetInfo.TYPE_ACTIVITY, new ActivityNavigator());
48 | this.mSchemes = config.getSchemes();
49 | this.mDomains = config.getDomains();
50 |
51 | try {
52 | Class> routerClass = Class.forName(ROUTER_CLASS_NAME);
53 | Method generateMethod = routerClass.getMethod(GENERATE_METHOD_NAME);
54 | generateMethod.invoke(routerClass);
55 | } catch (Throwable e) {
56 | e.printStackTrace();
57 | throw new IllegalStateException("Generate route table failed.");
58 | }
59 |
60 | }
61 |
62 | public List getDomains() {
63 | return mDomains;
64 | }
65 |
66 | public List getSchemes() {
67 | return mSchemes;
68 | }
69 |
70 | public static Rabbit get() {
71 | if (sInstance == null) {
72 | throw new IllegalStateException("Rabbits has not been initialed.");
73 | }
74 | return sInstance;
75 | }
76 |
77 | public static synchronized Rabbit init(RabbitConfig config) {
78 | sDebug = config.isDebug();
79 | if (sInstance != null) {
80 | throw new IllegalStateException("Rabbits has already initialed.");
81 | }
82 | if (!config.valid()) {
83 | throw new IllegalArgumentException("Config object not verify");
84 | }
85 | if (sInstance == null) {
86 | sInstance = new Rabbit(config);
87 | }
88 | Logger.v("Rabbits has been initialized successfully.");
89 | return sInstance;
90 | }
91 |
92 | public static String dump() {
93 | return RouteTable.dump();
94 | }
95 |
96 | /**
97 | * Create a rabbit who has ability to navigate through your pages.
98 | *
99 | * @param from Whether an Activity or a Fragment instance.
100 | * @return Rabbit instance.
101 | */
102 | public static Builder from(Object from) {
103 | if (get() == null) {
104 | throw new IllegalStateException("Rabbit has not been initialized properly");
105 | }
106 | if (!(from instanceof Activity) && !(from instanceof Fragment || from instanceof android.app.Fragment) && !(from instanceof Context)) {
107 | throw new IllegalArgumentException("From object must be whether an Context or a Fragment instance.");
108 | }
109 | return new Builder(from);
110 | }
111 |
112 | /**
113 | * Add an interceptor used for this navigation. This is useful when you want to check whether a
114 | * uri matches a specific page using method.
115 | *
116 | * @param interceptor Interceptor instance.
117 | * @return Rabbit instance.
118 | */
119 | public Rabbit addInterceptor(Interceptor interceptor) {
120 | if (mInterceptors == null) {
121 | mInterceptors = new ArrayList<>();
122 | }
123 | mInterceptors.add(interceptor);
124 | return this;
125 | }
126 |
127 | public Rabbit registerNavigator(int type, Navigator navigator) {
128 | mNavigators.put(type, navigator);
129 | return this;
130 | }
131 |
132 | public Rabbit registerFallbackNavigator(Navigator navigator) {
133 | mNavigators.put(TargetInfo.TYPE_NONE, navigator);
134 | return this;
135 | }
136 |
137 | public Rabbit addInterceptor(Interceptor interceptor, Rule rule) {
138 | mInterceptors.add(new PatternInterceptor(interceptor, rule));
139 | return this;
140 | }
141 |
142 | private ActionParser getActionParser() {
143 | if (mActionParser == null) {
144 | mActionParser = new ActionParser();
145 | }
146 | return mActionParser;
147 | }
148 |
149 | public TargetAssembler getTargetAssembler() {
150 | if (mTargetAssembler == null) {
151 | mTargetAssembler = new TargetAssembler();
152 | }
153 | return mTargetAssembler;
154 | }
155 |
156 | public CleanUpInterceptor getCleanUpInterceptor() {
157 | if (mCleanUpInterceptor == null) {
158 | mCleanUpInterceptor = new CleanUpInterceptor();
159 | }
160 | return mCleanUpInterceptor;
161 | }
162 |
163 | public NavigatorInterceptor getNavigatorInterceptor() {
164 | return new NavigatorInterceptor(mNavigators);
165 | }
166 |
167 | RabbitResult dispatch(Navigation navigation) {
168 | Logger.i("[!] Dispatching...");
169 | final Action action = navigation.action();
170 |
171 | // interceptors
172 | List interceptors = new ArrayList<>();
173 | interceptors.add(getActionParser());
174 |
175 | if (!action.isIgnoreInterceptors()) {
176 | // custom interceptors
177 | interceptors.addAll(mInterceptors);
178 |
179 | // mAction specific interceptors
180 | if (action.getInterceptors() != null) {
181 | interceptors.addAll(action.getInterceptors());
182 | }
183 | }
184 |
185 | interceptors.add(getTargetAssembler());
186 | interceptors.add(getCleanUpInterceptor());
187 | interceptors.add(getNavigatorInterceptor());
188 |
189 | RealDispatcher dispatcher = new RealDispatcher(action, interceptors, 0);
190 |
191 | RabbitResult result = dispatcher.dispatch(action);
192 | if (result == null) {
193 | return RabbitResult.notFinished();
194 | }
195 | Logger.i("[!] Result: " + result);
196 | return result;
197 | }
198 |
199 | /**
200 | * Used to create Navigation instance.
201 | */
202 | public static class Builder {
203 | private Action mAction;
204 |
205 | Builder(Object from) {
206 | mAction = new Action();
207 | mAction.setFrom(from);
208 | }
209 |
210 | public Navigation to(String url) {
211 | mAction.setOriginUrl(url);
212 | return new NavigationImpl(Rabbit.get(), mAction);
213 | }
214 | }
215 | }
216 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/src/main/java/com/kyleduo/rabbits/RabbitConfig.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | /**
7 | * Config object for Rabbits
8 | * Created by kyleduo on 2017/5/10.
9 | */
10 |
11 | @SuppressWarnings("WeakerAccess")
12 | public final class RabbitConfig {
13 | private List mSchemes = new ArrayList<>();
14 | private List mDomains = new ArrayList<>();
15 | private boolean mDebug;
16 |
17 | public static RabbitConfig get() {
18 | return new RabbitConfig();
19 | }
20 |
21 | public RabbitConfig schemes(String... schemes) {
22 | mSchemes.clear();
23 | if (schemes != null) {
24 | for (String scheme : schemes) {
25 | if (!mSchemes.contains(scheme)) {
26 | mSchemes.add(scheme);
27 | }
28 | }
29 | }
30 | return this;
31 | }
32 |
33 | public RabbitConfig domains(String... domains) {
34 | mDomains.clear();
35 | if (domains != null) {
36 | for (String domain : domains) {
37 | if (!mDomains.contains(domain)) {
38 | mDomains.add(domain);
39 | }
40 | }
41 | }
42 | return this;
43 | }
44 |
45 | public RabbitConfig debug(boolean debug) {
46 | mDebug = debug;
47 | return this;
48 | }
49 |
50 | public boolean valid() {
51 | return mSchemes != null && mSchemes.size() > 0 && mDomains != null && mDomains.size() > 0;
52 | }
53 |
54 | public List getSchemes() {
55 | return mSchemes;
56 | }
57 |
58 | public List getDomains() {
59 | return mDomains;
60 | }
61 |
62 | public boolean isDebug() {
63 | return mDebug;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/src/main/java/com/kyleduo/rabbits/RabbitResult.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits;
2 |
3 | /**
4 | * 保存是否跳转成功,以及过程信息:拦截信息、参数、目标等
5 | *
6 | * Created by kyle on 19/12/2017.
7 | */
8 |
9 | public class RabbitResult {
10 | public static final int STATUS_ERROR = 0;
11 | public static final int STATUS_SUCCESS = 1;
12 | public static final int STATUS_NOT_FOUND = 2;
13 | public static final int STATUS_NOT_FINISH = 3;
14 |
15 | private int code;
16 | private String reason;
17 | private Object target;
18 |
19 | public RabbitResult(int code, String reason, Object target) {
20 | this.code = code;
21 | this.reason = reason;
22 | this.target = target;
23 | }
24 |
25 | public static RabbitResult error(String reason) {
26 | return new RabbitResult(STATUS_ERROR, reason, null);
27 | }
28 |
29 | public static RabbitResult notFound(String url) {
30 | return new RabbitResult(STATUS_NOT_FOUND, "Page not found: " + url, null);
31 | }
32 |
33 | public static RabbitResult success(Object target) {
34 | return new RabbitResult(STATUS_SUCCESS, "success", target);
35 | }
36 |
37 | public static RabbitResult success() {
38 | return success(null);
39 | }
40 |
41 | public static RabbitResult notFinished() {
42 | return new RabbitResult(STATUS_NOT_FINISH, "", null);
43 | }
44 |
45 | public int getCode() {
46 | return code;
47 | }
48 |
49 | public String getReason() {
50 | return reason;
51 | }
52 |
53 | public Object getTarget() {
54 | return target;
55 | }
56 |
57 | public boolean isSuccess() {
58 | return getCode() == STATUS_SUCCESS;
59 | }
60 |
61 | public boolean isFinished() {
62 | return getCode() != STATUS_NOT_FINISH;
63 | }
64 |
65 | @Override
66 | public boolean equals(Object obj) {
67 | if (obj instanceof RabbitResult) {
68 | return this.code == ((RabbitResult) obj).code && this.target == ((RabbitResult) obj).target;
69 | }
70 | return false;
71 | }
72 |
73 | @Override
74 | public String toString() {
75 | switch (code) {
76 | case STATUS_ERROR:
77 | return "Result: ERROR, " + reason;
78 | case STATUS_SUCCESS:
79 | if (target == null) {
80 | return "Result: SUCCESS";
81 | } else {
82 | return "Result: SUCCESS, " + target;
83 | }
84 | case STATUS_NOT_FOUND:
85 | return "Result: NOT_FOUND, " + reason;
86 | case STATUS_NOT_FINISH:
87 | return "Result: NOT_FINISH";
88 | }
89 | return "Result: UNKNOWN";
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/src/main/java/com/kyleduo/rabbits/RealDispatcher.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits;
2 |
3 | import java.util.List;
4 |
5 | /**
6 | * Real dispatcher.
7 | *
8 | * Created by kyle on 24/01/2018.
9 | */
10 |
11 | public class RealDispatcher implements Interceptor.Dispatcher {
12 |
13 | private Action mAction;
14 | private List mInterceptors;
15 | private int mIndex;
16 |
17 | RealDispatcher(Action action, List interceptors, int index) {
18 | mAction = action;
19 | mInterceptors = interceptors;
20 | mIndex = index;
21 | }
22 |
23 | @Override
24 | public RabbitResult dispatch(Action action) {
25 | if (mIndex >= mInterceptors.size()) {
26 | return RabbitResult.error("Action has not been performed.");
27 | }
28 |
29 | Interceptor interceptor = mInterceptors.get(mIndex);
30 | if (action.isIgnoreInterceptors()) {
31 | while (!(interceptor instanceof InternalInterceptor)) {
32 | interceptor = mInterceptors.get(++mIndex);
33 | }
34 | }
35 | if (!(interceptor instanceof InternalInterceptor)) {
36 | Logger.d("Interceptor: " + interceptor.toString());
37 | }
38 |
39 | RealDispatcher next = new RealDispatcher(mAction, mInterceptors, mIndex + 1);
40 |
41 | //noinspection UnnecessaryLocalVariable
42 | RabbitResult result = interceptor.intercept(next);
43 |
44 | return result;
45 | }
46 |
47 | @Override
48 | public Action action() {
49 | return mAction;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/src/main/java/com/kyleduo/rabbits/RouteTable.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits;
2 |
3 | import android.net.Uri;
4 | import android.support.annotation.NonNull;
5 | import android.text.TextUtils;
6 |
7 | import java.util.HashMap;
8 | import java.util.LinkedHashMap;
9 | import java.util.Map;
10 |
11 | /**
12 | * Storing the routes, this class is responsible for find the proper page that matches the uri.
13 | *
14 | * Created by kyle on 24/01/2018.
15 | */
16 |
17 | @SuppressWarnings("WeakerAccess")
18 | public class RouteTable {
19 | private static final String PARAM_KEY_INT = "i";
20 | private static final String PARAM_KEY_LONG = "l";
21 | private static final String PARAM_KEY_FLOAT = "f";
22 | private static final String PARAM_KEY_DOUBLE = "d";
23 | private static final String PARAM_KEY_BOOLEAN = "b";
24 | private static final String PARAM_KEY_STRING = "s";
25 |
26 | private static Map sMappings = new LinkedHashMap<>();
27 | private static boolean sHasCompletePattern = false;
28 |
29 | public static void map(String path, TargetInfo targetInfo) {
30 | if (sMappings.containsKey(path)) {
31 | Logger.e("Same pattern exists. Overwriting path: " + path + " target: \"" + sMappings.get(path) + "\" to \"" + targetInfo + "\"");
32 | }
33 | sMappings.put(path, targetInfo);
34 | if (path.contains("://")) {
35 | sHasCompletePattern = true;
36 | }
37 | Logger.d("Route rule inserted. PATTERN: " + path + " TARGET: " + targetInfo.target.getCanonicalName());
38 | }
39 |
40 | static TargetInfo match(Uri uri) {
41 | if (uri == null) {
42 | return null;
43 | }
44 | Logger.d("Start matching. URI: " + uri.toString());
45 | // 并不能先判断scheme 和 domain,因为可能有固定匹配的模式,特殊scheme和domain。
46 | TargetInfo to = null;
47 | if (valid(uri)) {
48 | Logger.d("[01] Matching use path.");
49 | to = sMappings.get(uri.getPath());
50 | }
51 | if (to == null) {
52 | Logger.d("[02] Matching use pure uri.");
53 | // 处理完全匹配的模式
54 | // 不能用完整uri来匹配,如果有参数,要先移除参数.
55 | Uri pure = uri.buildUpon().query(null).build();
56 | to = sMappings.get(pure.toString());
57 | }
58 | if (to == null) {
59 | Logger.d("[03] Deeply matching.");
60 | to = deepMatch(uri);
61 | }
62 | if (Rabbit.sDebug) {
63 | if (to != null) {
64 | Logger.d("[--] Target found: " + to.target.getCanonicalName());
65 | } else {
66 | Logger.d("[--] Not match.");
67 | }
68 | }
69 | return to;
70 | }
71 |
72 | private static TargetInfo deepMatch(Uri uri) {
73 | boolean valid = valid(uri);
74 | if (!sHasCompletePattern && !valid) {
75 | // Only execute deep match when there complete pattern and the uri is verify.
76 | return null;
77 | }
78 | String path = uri.getPath();
79 | if (TextUtils.isEmpty(path)) {
80 | return null;
81 | }
82 | String[] segs = path.split("/");
83 |
84 | outer:
85 | for (Map.Entry entry : sMappings.entrySet()) {
86 | String pattern = entry.getKey();
87 | String patternPath;
88 | if (pattern.contains("://")) {
89 | Uri patternUri = Uri.parse(pattern);
90 | if (TextUtils.equals(patternUri.getScheme(), uri.getScheme()) && TextUtils.equals(patternUri.getAuthority(), uri.getAuthority())) {
91 | // If one page has been annotated with a complete url, it can only be opened by
92 | // url with same scheme and domain.
93 | patternPath = Uri.parse(pattern).getPath();
94 | } else {
95 | continue;
96 | }
97 | } else if (valid) {
98 | patternPath = pattern;
99 | } else {
100 | break;
101 | }
102 | if (path.equals(patternPath)) {
103 | // scheme和domain不同,但是pattern相同,认为匹配
104 | return entry.getValue();
105 | }
106 | if (TextUtils.isEmpty(patternPath)) {
107 | continue;
108 | }
109 | if (!patternPath.contains("{")) {
110 | continue;
111 | }
112 | String[] pSegs = patternPath.split("/");
113 | if (segs.length != pSegs.length) {
114 | continue;
115 | }
116 |
117 | Map params = null;
118 |
119 | for (int i = 0; i < pSegs.length; i++) {
120 | String part = URLEncodeUtils.decode(segs[i]);
121 | String patternPart = pSegs[i];
122 |
123 | if (patternPart.startsWith("{")) {
124 | String[] paramFormat = patternPart.substring(1, patternPart.length() - 1).split(":");
125 | if (params == null) {
126 | params = new HashMap<>();
127 | }
128 | if (paramFormat.length == 1) {
129 | params.put(paramFormat[0], part);
130 | } else if (paramFormat.length == 2) {
131 | String type = paramFormat[1].toLowerCase();
132 | switch (type) {
133 | case PARAM_KEY_INT:
134 | params.put(paramFormat[0], part);
135 | break;
136 | case PARAM_KEY_LONG:
137 | params.put(paramFormat[0], Long.parseLong(part));
138 | break;
139 | case PARAM_KEY_FLOAT:
140 | params.put(paramFormat[0], Float.parseFloat(part));
141 | break;
142 | case PARAM_KEY_DOUBLE:
143 | params.put(paramFormat[0], Double.parseDouble(part));
144 | break;
145 | case PARAM_KEY_BOOLEAN:
146 | params.put(paramFormat[0], Boolean.parseBoolean(part));
147 | break;
148 | case PARAM_KEY_STRING:
149 | default:
150 | params.put(paramFormat[0], part);
151 | break;
152 | }
153 | }
154 | } else if (!patternPart.equals(part)) {
155 | continue outer;
156 | }
157 | }
158 |
159 | TargetInfo info = new TargetInfo(entry.getValue());
160 | info.params = params;
161 | return info;
162 | }
163 |
164 | return null;
165 | }
166 |
167 | private static boolean valid(@NonNull Uri uri) {
168 | String scheme = uri.getScheme();
169 | String domain = uri.getAuthority();
170 |
171 | return Rabbit.get().getSchemes().contains(scheme)
172 | && Rabbit.get().getDomains().contains(domain);
173 | }
174 |
175 | static String dump() {
176 | StringBuilder dump = new StringBuilder();
177 | for (Map.Entry e : sMappings.entrySet()) {
178 | dump.append("pattern: ").append(e.getKey()).append("\n")
179 | .append("class: ").append(e.getValue().target.getCanonicalName()).append("\n\n");
180 | }
181 | return dump.toString();
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/src/main/java/com/kyleduo/rabbits/TargetAssembler.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.support.v4.app.Fragment;
6 |
7 | /**
8 | * Assemble all the start information to target, which is an Intent or a Fragment object.
9 | *
10 | * Created by kyle on 26/01/2018.
11 | */
12 |
13 | public class TargetAssembler implements InternalInterceptor {
14 | @Override
15 | public RabbitResult intercept(Dispatcher dispatcher) {
16 | Logger.i("[!] Assembling...");
17 | Action action = dispatcher.action();
18 | int targetType = action.getTargetType();
19 |
20 | if (targetType == TargetInfo.TYPE_NONE) {
21 | // process on
22 | return dispatcher.dispatch(action);
23 | }
24 |
25 | // assemble Intent or Fragment instance.
26 | Object target = null;
27 | if (targetType == TargetInfo.TYPE_ACTIVITY) {
28 | Intent intent;
29 | if (action.getFrom() instanceof Context) {
30 | intent = new Intent((Context) action.getFrom(), action.getTargetClass());
31 | } else if (action.getFrom() instanceof Fragment) {
32 | intent = new Intent(((Fragment) action.getFrom()).getActivity(), action.getTargetClass());
33 | } else if (action.getFrom() instanceof android.app.Fragment) {
34 | intent = new Intent(((android.app.Fragment) action.getFrom()).getActivity(), action.getTargetClass());
35 | } else {
36 | return RabbitResult.error("From object must be whether an Context or a Fragment instance.");
37 | }
38 | intent.setFlags(action.getIntentFlags());
39 | intent.putExtras(action.getExtras());
40 | target = intent;
41 | } else if (targetType == TargetInfo.TYPE_FRAGMENT) {
42 | try {
43 | android.app.Fragment fragment = (android.app.Fragment) action.getTargetClass().newInstance();
44 | fragment.setArguments(action.getExtras());
45 | target = fragment;
46 | } catch (InstantiationException e) {
47 | e.printStackTrace();
48 | } catch (IllegalAccessException e) {
49 | e.printStackTrace();
50 | }
51 | } else if (targetType == TargetInfo.TYPE_FRAGMENT_V4) {
52 | try {
53 | Fragment fragment = (Fragment) action.getTargetClass().newInstance();
54 | fragment.setArguments(action.getExtras());
55 | target = fragment;
56 | } catch (InstantiationException e) {
57 | e.printStackTrace();
58 | } catch (IllegalAccessException e) {
59 | e.printStackTrace();
60 | }
61 | }
62 | action.setTarget(target);
63 |
64 | return dispatcher.dispatch(action);
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/src/main/java/com/kyleduo/rabbits/TargetInfo.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits;
2 |
3 | import java.util.Map;
4 |
5 | /**
6 | * Representing a page in route table.
7 | *
8 | * Created by kyle on 26/01/2018.
9 | */
10 | @SuppressWarnings("WeakerAccess")
11 | public class TargetInfo {
12 | static final int TYPE_NONE = 0;
13 | public static final int TYPE_ACTIVITY = 1;
14 | public static final int TYPE_FRAGMENT = 2;
15 | public static final int TYPE_FRAGMENT_V4 = 3;
16 |
17 | String pattern;
18 | Class> target;
19 | int type;
20 | int flags;
21 | //params from REST url.
22 | Map params;
23 |
24 | public TargetInfo(String pattern, Class> target, int type, int flags) {
25 | this.pattern = pattern;
26 | this.target = target;
27 | this.type = type;
28 | this.flags = flags;
29 | }
30 |
31 | TargetInfo(TargetInfo info) {
32 | this.pattern = info.pattern;
33 | this.target = info.target;
34 | this.type = info.type;
35 | this.flags = info.flags;
36 | }
37 |
38 | @Override
39 | public String toString() {
40 | return target.getCanonicalName();
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/src/main/java/com/kyleduo/rabbits/URLEncodeUtils.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits;
2 |
3 | import java.io.UnsupportedEncodingException;
4 | import java.net.URLDecoder;
5 | import java.net.URLEncoder;
6 |
7 | /**
8 | * Utility methods for url.
9 | *
10 | * Created by kyle on 26/02/2018.
11 | */
12 |
13 | class URLEncodeUtils {
14 | static String decode(String origin) {
15 | String out;
16 | try {
17 | out = URLDecoder.decode(origin, "utf-8");
18 | } catch (UnsupportedEncodingException e) {
19 | e.printStackTrace();
20 | out = origin;
21 | }
22 | return out;
23 | }
24 |
25 | static String encode(String origin) {
26 | String out;
27 | try {
28 | out = URLEncoder.encode(origin, "utf-8");
29 | } catch (UnsupportedEncodingException e) {
30 | e.printStackTrace();
31 | out = origin;
32 | }
33 | return out;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/src/main/java/com/kyleduo/rabbits/rules/DomainRule.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.rules;
2 |
3 | import android.net.Uri;
4 |
5 | /**
6 | * Rule for Domain.
7 | *
8 | * Created by kyle on 11/02/2018.
9 | */
10 |
11 | public class DomainRule extends UriRule {
12 | DomainRule() {
13 | }
14 |
15 | @Override
16 | public boolean verify(Uri uri) {
17 | if (uri.isOpaque()) {
18 | return false;
19 | }
20 | return verify(uri.getAuthority());
21 | }
22 |
23 | @Override
24 | public String toString() {
25 | return "(Domain " + super.toString();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/src/main/java/com/kyleduo/rabbits/rules/Element.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.rules;
2 |
3 | /**
4 | * Operators used when create a Rule.
5 | *
6 | * Created by kyle on 11/02/2018.
7 | */
8 |
9 | @SuppressWarnings("unused")
10 | public interface Element {
11 | /**
12 | * Check whether the source exits.
13 | *
14 | * @return a Rule instance
15 | */
16 | Rule exists();
17 |
18 | /**
19 | * Check whether the source equals to the {@param value}
20 | * using {@link java.lang.String#equals} method.
21 | *
22 | * @param value value
23 | * @return a Rule instance
24 | */
25 | Rule is(String value);
26 |
27 | /**
28 | * Check whether the source has the {@param value} as prefix
29 | * using {@link java.lang.String#startsWith} method.
30 | *
31 | * @param value value
32 | * @return a Rule instance
33 | */
34 | Rule startsWith(String value);
35 |
36 | /**
37 | * Check whether the source has the {@param value} as suffix
38 | * using {@link java.lang.String#endsWith} method.
39 | *
40 | * @param value value
41 | * @return a Rule instance
42 | */
43 | Rule endsWith(String value);
44 |
45 | /**
46 | * Check whether the source contains the {@param value} as a substring
47 | * using {@link java.lang.String#endsWith} method.
48 | *
49 | * @param value value
50 | * @return a Rule instance
51 | */
52 | Rule contains(String value);
53 |
54 | /**
55 | * Check whether the source is one of the {@param values}.
56 | *
57 | * @param values value
58 | * @return a Rule instance
59 | */
60 | Rule in(String... values);
61 | }
62 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/src/main/java/com/kyleduo/rabbits/rules/NotRule.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.rules;
2 |
3 | import com.kyleduo.rabbits.Action;
4 |
5 | /**
6 | * Not
7 | *
8 | * Created by kyle on 01/03/2018.
9 | */
10 |
11 | public class NotRule implements Rule {
12 |
13 | private Rule mRule;
14 |
15 | NotRule(Rule rule) {
16 | mRule = rule;
17 | }
18 |
19 | @Override
20 | public boolean verify(Action action) {
21 | return !mRule.verify(action);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/src/main/java/com/kyleduo/rabbits/rules/PathRule.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.rules;
2 |
3 | import android.net.Uri;
4 |
5 | /**
6 | * Rule for Path.
7 | *
8 | * Created by kyle on 11/02/2018.
9 | */
10 |
11 | @SuppressWarnings("unused")
12 | public class PathRule extends UriRule {
13 | PathRule() {
14 | }
15 |
16 | @Override
17 | public boolean verify(Uri uri) {
18 | if (uri.isOpaque()) {
19 | return false;
20 | }
21 | return verify(uri.getPath());
22 | }
23 |
24 | @Override
25 | public String toString() {
26 | return "(Path " + super.toString();
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/src/main/java/com/kyleduo/rabbits/rules/QueryRule.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.rules;
2 |
3 | import android.net.Uri;
4 |
5 | /**
6 | * Rule for query.
7 | *
8 | * Created by kyle on 11/02/2018.
9 | */
10 |
11 | public class QueryRule extends UriRule {
12 |
13 | private String mKey;
14 |
15 | QueryRule(String key) {
16 | mKey = key;
17 | }
18 |
19 | @Override
20 | public boolean verify(Uri uri) {
21 | if (uri.isOpaque()) {
22 | return false;
23 | }
24 | String q = uri.getQueryParameter(mKey);
25 | return super.verify(q);
26 | }
27 |
28 | @Override
29 | public String toString() {
30 | return "(q:" + mKey + " " + super.toString();
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/src/main/java/com/kyleduo/rabbits/rules/Rule.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.rules;
2 |
3 | import com.kyleduo.rabbits.Action;
4 |
5 | /**
6 | * Rules used when matching url.
7 | *
8 | * Created by kyle on 11/02/2018.
9 | */
10 |
11 | public interface Rule {
12 | enum Operator {
13 | IS, STARTS_WITH, ENDS_WITH, IN, CONTAINS, EXISTS
14 | }
15 |
16 | boolean verify(Action action);
17 | }
18 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/src/main/java/com/kyleduo/rabbits/rules/RuleSet.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.rules;
2 |
3 | import com.kyleduo.rabbits.Action;
4 |
5 | import java.util.List;
6 |
7 | /**
8 | * Represents a set of rules which should be validated in AND or OR relationship.
9 | *
10 | * Created by kyle on 11/02/2018.
11 | */
12 |
13 | public class RuleSet implements Rule {
14 | public enum Relation {
15 | AND, OR
16 | }
17 |
18 | private List mRules;
19 | private Relation mRelation;
20 |
21 | RuleSet(List rules, Relation relation) {
22 | mRules = rules;
23 | mRelation = relation;
24 | }
25 |
26 | @SuppressWarnings("ConstantConditions")
27 | @Override
28 | public boolean verify(Action action) {
29 | if (mRelation == Relation.AND) {
30 | for (Rule rule : mRules) {
31 | if (!rule.verify(action)) {
32 | return false;
33 | }
34 | }
35 | return true;
36 | } else if (mRelation == Relation.OR) {
37 | for (Rule rule : mRules) {
38 | if (rule.verify(action)) {
39 | return true;
40 | }
41 | }
42 | return false;
43 | }
44 | return false;
45 | }
46 |
47 | @Override
48 | public String toString() {
49 | StringBuilder sb = new StringBuilder();
50 | sb.append("[");
51 | for (Rule rule : mRules) {
52 | sb.append(rule.toString()).append(", ");
53 | }
54 | sb.append("]");
55 | return sb.toString();
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/src/main/java/com/kyleduo/rabbits/rules/Rules.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.rules;
2 |
3 | import java.util.Arrays;
4 |
5 | /**
6 | * Factory class of {@link Rule}
7 | *
8 | * Created by kyle on 11/02/2018.
9 | */
10 |
11 | public final class Rules {
12 |
13 | public static Element scheme() {
14 | return new SchemeRule();
15 | }
16 |
17 | public static Element domain() {
18 | return new DomainRule();
19 | }
20 |
21 | public static Element path() {
22 | return new PathRule();
23 | }
24 |
25 | public static Element query(String key) {
26 | return new QueryRule(key);
27 | }
28 |
29 | public static TargetFlagsRule flags() {
30 | return new TargetFlagsRule();
31 | }
32 |
33 | public static NotRule not(Rule rule) {
34 | return new NotRule(rule);
35 | }
36 |
37 | public static Rule set(RuleSet.Relation relation, Rule... rules) {
38 | return new RuleSet(Arrays.asList(rules), relation);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/src/main/java/com/kyleduo/rabbits/rules/SchemeRule.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.rules;
2 |
3 | import android.net.Uri;
4 |
5 | /**
6 | * Rule for Scheme.
7 | *
8 | * Created by kyle on 11/02/2018.
9 | */
10 |
11 | public class SchemeRule extends UriRule {
12 |
13 | SchemeRule() {
14 | }
15 |
16 | @Override
17 | public boolean verify(Uri uri) {
18 | return verify(uri.getScheme());
19 | }
20 |
21 | @Override
22 | public String toString() {
23 | return "(Scheme " + super.toString();
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/src/main/java/com/kyleduo/rabbits/rules/TargetFlagsRule.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.rules;
2 |
3 | import com.kyleduo.rabbits.Action;
4 |
5 | /**
6 | * Rule for targetFlags from Action
7 | *
8 | * Created by kyle on 01/03/2018.
9 | */
10 |
11 | public class TargetFlagsRule implements Rule {
12 |
13 | private int mMask;
14 |
15 | public Rule has(int mask) {
16 | this.mMask = mask;
17 | return this;
18 | }
19 |
20 | @Override
21 | public boolean verify(Action action) {
22 | return (action.getTargetFlags() & mMask) > 0;
23 | }
24 |
25 | @Override
26 | public String toString() {
27 | return "(flags HAS " + Integer.toHexString(mMask) + ")";
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/src/main/java/com/kyleduo/rabbits/rules/UriRule.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.rules;
2 |
3 | import android.net.Uri;
4 |
5 | import com.kyleduo.rabbits.Action;
6 |
7 | import java.util.Arrays;
8 | import java.util.List;
9 |
10 | /**
11 | * Implementation of Rule
12 | *
13 | * Created by kyle on 11/02/2018.
14 | */
15 |
16 | public abstract class UriRule implements Rule, Element {
17 |
18 | private Operator mOperator;
19 | private String mValue;
20 | private List mValues;
21 |
22 | UriRule() {
23 | }
24 |
25 | @Override
26 | public Rule exists() {
27 | return operator(Operator.EXISTS, null);
28 | }
29 |
30 | @Override
31 | public Rule is(String value) {
32 | return operator(Operator.IS, value);
33 | }
34 |
35 | @Override
36 | public Rule startsWith(String value) {
37 | return operator(Operator.STARTS_WITH, value);
38 | }
39 |
40 | @Override
41 | public Rule endsWith(String value) {
42 | return operator(Operator.STARTS_WITH, value);
43 | }
44 |
45 | @Override
46 | public Rule in(String... values) {
47 | mValues = Arrays.asList(values);
48 | mOperator = Operator.IN;
49 | return this;
50 | }
51 |
52 | @Override
53 | public Rule contains(String value) {
54 | return operator(Operator.CONTAINS, value);
55 | }
56 |
57 | private UriRule operator(Operator operator, String value) {
58 | mOperator = operator;
59 | mValue = value;
60 | return this;
61 | }
62 |
63 | @Override
64 | public boolean verify(Action action) {
65 | return verify(action.getUri());
66 | }
67 |
68 | public abstract boolean verify(Uri uri);
69 |
70 | boolean verify(String source) {
71 | switch (mOperator) {
72 | case EXISTS:
73 | return source != null && source.length() > 0;
74 | case IS:
75 | return source != null && source.equals(mValue);
76 | case STARTS_WITH:
77 | return source != null && source.startsWith(mValue);
78 | case ENDS_WITH:
79 | return source != null && source.endsWith(mValue);
80 | case CONTAINS:
81 | return source != null && source.contains(mValue);
82 | case IN:
83 | return source != null && mValues != null && mValues.contains(source);
84 | }
85 | return false;
86 | }
87 |
88 | @Override
89 | public String toString() {
90 | return mOperator + " " + mValue + ")";
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Rabbits
3 |
4 |
--------------------------------------------------------------------------------
/Rabbits/rabbits/src/test/java/com/kyleduo/rabbits/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.assertEquals;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 |
18 | private void print(Object obj) {
19 | System.out.println(obj instanceof String ? obj : obj.toString());
20 | }
21 |
22 | }
--------------------------------------------------------------------------------
/Rabbits/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':rabbits', ':annotations', ':compiler', ':submodule1', ':submodule2'
2 |
--------------------------------------------------------------------------------
/Rabbits/submodule1/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/Rabbits/submodule1/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion rootProject.ext.compileSdkVersion
5 | buildToolsVersion rootProject.ext.buildToolsVersion
6 |
7 | defaultConfig {
8 | minSdkVersion 14
9 | targetSdkVersion 27
10 | versionCode 1
11 | versionName "1.0"
12 |
13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
14 |
15 | javaCompileOptions {
16 | annotationProcessorOptions {
17 | arguments = [rabbits_moduleName: "sm1", rabbits_submodules: "sm2"]
18 | }
19 | }
20 | }
21 |
22 | buildTypes {
23 | release {
24 | minifyEnabled false
25 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
26 | }
27 | }
28 |
29 | lintOptions {
30 | abortOnError false
31 | }
32 | }
33 |
34 | dependencies {
35 | implementation fileTree(dir: 'libs', include: ['*.jar'])
36 | implementation project(':submodule2')
37 | androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
38 | exclude group: 'com.android.support', module: 'support-annotations'
39 | })
40 | implementation "com.android.support:appcompat-v7:$supportVersion"
41 | testImplementation 'junit:junit:4.12'
42 |
43 | // Rabbits
44 | implementation project(':rabbits')
45 | annotationProcessor project(':compiler')
46 |
47 | // Fragmentation
48 | implementation 'me.yokeyword:fragmentation:0.10.0'
49 | }
50 |
--------------------------------------------------------------------------------
/Rabbits/submodule1/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/kyle/Documents/developer/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/Rabbits/submodule1/src/androidTest/java/com/kyleduo/rabbits/demo/submodule1/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.demo.submodule1;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.kyleduo.rabbits.demo.submodule1.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Rabbits/submodule1/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
9 |
10 |
13 |
14 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Rabbits/submodule1/src/main/java/com/kyleduo/rabbits/demo/submodule1/BaseActivity.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.demo.submodule1;
2 |
3 | import android.graphics.Color;
4 | import android.os.Bundle;
5 | import android.os.Handler;
6 | import android.support.annotation.Nullable;
7 | import android.support.v7.app.AppCompatActivity;
8 | import android.support.v7.widget.Toolbar;
9 |
10 | import com.kyleduo.rabbits.Rabbit;
11 |
12 | /**
13 | * Created by kyle on 28/02/2018.
14 | */
15 |
16 | public class BaseActivity extends AppCompatActivity {
17 |
18 | @Override
19 | protected void onCreate(@Nullable Bundle savedInstanceState) {
20 | super.onCreate(savedInstanceState);
21 |
22 | new Handler().post(new Runnable() {
23 | @Override
24 | public void run() {
25 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
26 | setSupportActionBar(toolbar);
27 |
28 |
29 | toolbar.setBackgroundColor(0xFF7A8790);
30 | toolbar.setTitleTextColor(Color.WHITE);
31 | }
32 | });
33 | }
34 |
35 | @Override
36 | protected void onResume() {
37 | super.onResume();
38 | setTitle(getIntent().getStringExtra(Rabbit.KEY_PATTERN) + "@" + this.getClass().getSimpleName());
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Rabbits/submodule1/src/main/java/com/kyleduo/rabbits/demo/submodule1/SMActivity.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.demo.submodule1;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.Nullable;
5 | import android.view.View;
6 |
7 | import com.kyleduo.rabbits.Rabbit;
8 | import com.kyleduo.rabbits.annotations.Page;
9 | import com.kyleduo.rabbits.sm1.P;
10 |
11 | /**
12 | * for Module
13 | * Created by kyleduo on 2017/5/8.
14 | */
15 | @Page("/sm1/activity")
16 | public class SMActivity extends BaseActivity {
17 | @Override
18 | protected void onCreate(@Nullable Bundle savedInstanceState) {
19 | super.onCreate(savedInstanceState);
20 | setContentView(R.layout.act_sub_module_1);
21 |
22 | findViewById(R.id.open_main).setOnClickListener(new View.OnClickListener() {
23 | @Override
24 | public void onClick(View v) {
25 | Rabbit.from(SMActivity.this).to("/test/from%20submodule").start();
26 | }
27 | });
28 |
29 | findViewById(R.id.open_submodule2).setOnClickListener(new View.OnClickListener() {
30 | @Override
31 | public void onClick(View v) {
32 | Rabbit.from(SMActivity.this).to(P.ACT2).start();
33 | }
34 | });
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Rabbits/submodule1/src/main/java/com/kyleduo/rabbits/demo/submodule1/SMActivity2.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.demo.submodule1;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.Nullable;
5 | import android.view.View;
6 |
7 | import com.kyleduo.rabbits.Rabbit;
8 | import com.kyleduo.rabbits.annotations.Page;
9 |
10 | /**
11 | * for Rabbits
12 | * Created by kyleduo on 2017/5/10.
13 | */
14 |
15 | @Page(value = "/sm1/activity2", alias = "ACT2")
16 | public class SMActivity2 extends BaseActivity {
17 | @Override
18 | protected void onCreate(@Nullable Bundle savedInstanceState) {
19 | super.onCreate(savedInstanceState);
20 | setContentView(R.layout.act_sub_module_2);
21 |
22 | findViewById(R.id.back_home).setOnClickListener(new View.OnClickListener() {
23 | @Override
24 | public void onClick(View view) {
25 | Rabbit.from(SMActivity2.this)
26 | .to("/")
27 | .clearTop()
28 | .singleTop()
29 | .start();
30 | }
31 | });
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Rabbits/submodule1/src/main/java/com/kyleduo/rabbits/demo/submodule1/SMTestActivity.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.demo.submodule1;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.Nullable;
5 |
6 | import com.kyleduo.rabbits.annotations.Page;
7 |
8 | /**
9 | * Created by kyle on 28/02/2018.
10 | */
11 | @Page("/test")
12 | public class SMTestActivity extends BaseActivity {
13 |
14 | @Override
15 | protected void onCreate(@Nullable Bundle savedInstanceState) {
16 | super.onCreate(savedInstanceState);
17 | setContentView(R.layout.act_sm1_test);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Rabbits/submodule1/src/main/res/layout/act_sm1_test.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
15 |
16 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Rabbits/submodule1/src/main/res/layout/act_sub_module_1.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
14 |
15 |
23 |
24 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/Rabbits/submodule1/src/main/res/layout/act_sub_module_2.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
14 |
15 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Rabbits/submodule1/src/main/res/layout/toolbar_sm1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
15 |
16 |
20 |
--------------------------------------------------------------------------------
/Rabbits/submodule1/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | SubModule1
3 |
4 |
--------------------------------------------------------------------------------
/Rabbits/submodule1/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
--------------------------------------------------------------------------------
/Rabbits/submodule1/src/test/java/com/kyleduo/rabbits/demo/submodule1/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.demo.submodule1;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/Rabbits/submodule2/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/Rabbits/submodule2/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion rootProject.ext.compileSdkVersion
5 | buildToolsVersion rootProject.ext.buildToolsVersion
6 |
7 | defaultConfig {
8 | minSdkVersion 14
9 | targetSdkVersion 27
10 | versionCode 1
11 | versionName "1.0"
12 |
13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
14 |
15 | javaCompileOptions {
16 | annotationProcessorOptions {
17 | arguments = [rabbits_moduleName: "sm2"]
18 | }
19 | }
20 | }
21 |
22 | buildTypes {
23 | release {
24 | minifyEnabled false
25 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
26 | }
27 | }
28 |
29 | lintOptions {
30 | abortOnError false
31 | }
32 |
33 | }
34 |
35 | dependencies {
36 | implementation fileTree(dir: 'libs', include: ['*.jar'])
37 |
38 | implementation "com.android.support:appcompat-v7:$supportVersion"
39 | testImplementation 'junit:junit:4.12'
40 | androidTestImplementation 'com.android.support.test:runner:1.0.1'
41 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
42 |
43 | // Rabbits
44 | implementation project(':rabbits')
45 | annotationProcessor project(':compiler')
46 |
47 | // Fragmentation
48 | implementation 'me.yokeyword:fragmentation:0.10.0'
49 | }
50 |
--------------------------------------------------------------------------------
/Rabbits/submodule2/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/Rabbits/submodule2/src/androidTest/java/com/kyleduo/rabbits/demo/submodule2/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.demo.submodule2;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.kyleduo.rabbits.demo.submodule2.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Rabbits/submodule2/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Rabbits/submodule2/src/main/java/com/kyleduo/rabbits/demo/submodule2/BaseActivity.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.demo.submodule2;
2 |
3 | import android.graphics.Color;
4 | import android.os.Bundle;
5 | import android.os.Handler;
6 | import android.support.annotation.Nullable;
7 | import android.support.v7.app.AppCompatActivity;
8 | import android.support.v7.widget.Toolbar;
9 |
10 | import com.kyleduo.rabbits.Rabbit;
11 |
12 | /**
13 | * Created by kyle on 28/02/2018.
14 | */
15 |
16 | public class BaseActivity extends AppCompatActivity {
17 |
18 | @Override
19 | protected void onCreate(@Nullable Bundle savedInstanceState) {
20 | super.onCreate(savedInstanceState);
21 |
22 | new Handler().post(new Runnable() {
23 | @Override
24 | public void run() {
25 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
26 | setSupportActionBar(toolbar);
27 |
28 |
29 | toolbar.setBackgroundColor(0xFF857694);
30 | toolbar.setTitleTextColor(Color.WHITE);
31 | }
32 | });
33 | }
34 |
35 | @Override
36 | protected void onResume() {
37 | super.onResume();
38 | setTitle(getIntent().getStringExtra(Rabbit.KEY_PATTERN) + "@" + this.getClass().getSimpleName());
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Rabbits/submodule2/src/main/java/com/kyleduo/rabbits/demo/submodule2/SM2Activity.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.demo.submodule2;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.Nullable;
5 |
6 | import com.kyleduo.rabbits.annotations.Page;
7 |
8 | /**
9 | * Created by kyle on 28/02/2018.
10 | */
11 |
12 | @Page("/sm2/activity")
13 | public class SM2Activity extends BaseActivity {
14 | @Override
15 | protected void onCreate(@Nullable Bundle savedInstanceState) {
16 | super.onCreate(savedInstanceState);
17 | setContentView(R.layout.act_sm2);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Rabbits/submodule2/src/main/res/layout/act_sm2.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
14 |
15 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Rabbits/submodule2/src/main/res/layout/toolbar_sm2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
15 |
16 |
20 |
--------------------------------------------------------------------------------
/Rabbits/submodule2/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | submodule2
3 |
4 |
--------------------------------------------------------------------------------
/Rabbits/submodule2/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
--------------------------------------------------------------------------------
/Rabbits/submodule2/src/test/java/com/kyleduo/rabbits/demo/submodule2/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.kyleduo.rabbits.demo.submodule2;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/demo/demo.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kyleduo/Rabbits/bacc53128b2acf700ea64d4729605121e61dd727/demo/demo.apk
--------------------------------------------------------------------------------
/demo/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kyleduo/Rabbits/bacc53128b2acf700ea64d4729605121e61dd727/demo/preview.png
--------------------------------------------------------------------------------