├── .gitignore ├── .travis.yml ├── LICENSE ├── bintrayUpload.gradle ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── okdeeplink-annotation ├── .gitignore ├── build.gradle └── src │ └── main │ └── java │ └── okdeeplink │ ├── Action.java │ ├── Activity.java │ ├── AppLink.java │ ├── Intercept.java │ ├── Path.java │ ├── Query.java │ ├── RequestCode.java │ ├── Service.java │ ├── Uri.java │ └── UriReplace.java ├── okdeeplink-api ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── okdeeplink │ │ ├── Address.java │ │ ├── BundleCompact.java │ │ ├── DeepLinkActivity.java │ │ ├── DeepLinkClient.java │ │ ├── Interceptor.java │ │ ├── LogInterceptor.java │ │ ├── RealCall.java │ │ ├── RealCallChain.java │ │ ├── Request.java │ │ └── Response.java │ └── res │ └── values │ └── strings.xml ├── okdeeplink-gradle ├── .gitignore ├── build.gradle └── src │ └── main │ ├── groovy │ └── okdeeplink │ │ └── OkDeepLinkPlugin.groovy │ └── resources │ └── META-INF │ └── gradle-plugins │ └── okdeeplink.plugin.properties ├── okdeeplink-processor ├── .gitignore ├── build.gradle ├── libs │ └── android-support-v4.jar └── src │ └── main │ └── java │ └── okdeeplink │ ├── DeepLinkInjectProcessor.java │ ├── DeepLinkInterceptorProcessor.java │ ├── DeepLinkServiceProcessor.java │ ├── element │ ├── AddressElement.java │ ├── InterceptorElement.java │ └── ServiceElement.java │ └── util │ ├── ElementUtils.java │ ├── Logger.java │ └── MethodGenerator.java ├── pom-evaluator.gradle ├── readme.md ├── rxactivityresult ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── rxactivityresult │ │ ├── ActivityResult.java │ │ ├── IActivityObservable.java │ │ ├── RxActivityResult.java │ │ ├── RxResultHoldContext.java │ │ ├── RxResultHoldEmpty.java │ │ ├── RxResultHoldFragment.java │ │ └── RxResultHoldFragmentV4.java │ └── res │ └── values │ └── styles.xml ├── sample ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── okdeeplink │ │ └── sample │ │ ├── MainActivity.java │ │ ├── SampleService.java │ │ └── SecondInterceptor.java │ └── res │ ├── layout │ └── activity_main.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── second ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── okdeeplink │ │ └── second │ │ ├── SecondActivity.java │ │ └── SecondService.java │ └── res │ ├── layout │ └── activity_second.xml │ └── values │ ├── dimens.xml │ └── strings.xml ├── settings.gradle └── snapshot └── intercept_preview.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # Intellij 36 | *.iml 37 | .idea/workspace.xml 38 | .idea/tasks.xml 39 | .idea/gradle.xml 40 | .idea/dictionaries 41 | .idea/libraries 42 | 43 | # Keystore files 44 | *.jks 45 | 46 | # External native build folder generated in Android Studio 2.2 and later 47 | .externalNativeBuild 48 | 49 | # Google Services (e.g. APIs or Firebase) 50 | google-services.json 51 | 52 | # Freeline 53 | freeline.py 54 | freeline/ 55 | freeline_project_description.json 56 | .idea 57 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | jdk: 3 | - oraclejdk8 4 | notifications: # 每次构建的时候是否通知,如果不想收到通知邮箱(个人感觉邮件贼烦),那就设置false吧 5 | email: true 6 | android: 7 | components: 8 | - tools 9 | - build-tools-25.0.2 10 | - android-25 11 | - extra-android-m2repository # Android Support Repository 12 | - extra-android-support # Support Library 13 | before_install: 14 | - chmod +x gradlew # 改变gradlew的访问权限 15 | script: # 执行:下面的命令 16 | - ./gradlew assembleRelease -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /bintrayUpload.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.github.dcendents.android-maven' 2 | apply plugin: 'com.jfrog.bintray' 3 | 4 | // load properties 5 | Properties properties = new Properties() 6 | DataInputStream localDataInputStream = null; 7 | DataInputStream gradleDataInputStream = null; 8 | 9 | if (project.rootProject.file('local.properties').canRead()) { 10 | File localPropertiesFile = project.rootProject.file('local.properties'); 11 | localDataInputStream = localPropertiesFile.newDataInputStream(); 12 | properties.load(localDataInputStream); 13 | } 14 | 15 | if (project.rootProject.file("gradle.properties").canRead()) { 16 | File projectPropertiesFile = project.rootProject.file("gradle.properties"); 17 | gradleDataInputStream = projectPropertiesFile.newDataInputStream(); 18 | properties.load(gradleDataInputStream); 19 | } 20 | 21 | // read properties 22 | def projectDescription = properties.getProperty("description") 23 | def projectGroupId = properties.getProperty("groupId") 24 | def projectArtifactId = properties.getProperty("artifactId") 25 | def projectVersionName = properties.getProperty("versionName") 26 | def projectPackaging = properties.getProperty("packaging") 27 | def projectSiteUrl = properties.getProperty("siteUrl") 28 | def projectGitUrl = properties.getProperty("gitUrl") 29 | 30 | def developerId = properties.getProperty("developer.id") 31 | def developerName = properties.getProperty("developer.name") 32 | def developerEmail = properties.getProperty("developer.email") 33 | 34 | def bintrayUser = properties.getProperty("bintray.user") 35 | def bintrayApikey = properties.getProperty("bintray.apikey") 36 | if (localDataInputStream != null) { 37 | localDataInputStream.close(); 38 | } 39 | if (gradleDataInputStream != null) { 40 | gradleDataInputStream.close(); 41 | } 42 | 43 | version = projectVersionName 44 | group = projectGroupId 45 | 46 | 47 | 48 | install { 49 | repositories.mavenInstaller { 50 | // This generates POM.xml with proper parameters 51 | pom { 52 | project { 53 | packaging projectPackaging 54 | // Add your description here 55 | name projectDescription //项目描述 56 | url projectSiteUrl 57 | // Set your license 58 | licenses { 59 | license { 60 | name 'The Apache Software License, Version 2.0' 61 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt' 62 | } 63 | } 64 | developers { 65 | developer { 66 | id developerId //填写的一些基本信息 67 | name developerName 68 | email developerEmail 69 | } 70 | } 71 | scm { 72 | connection projectGitUrl 73 | developerConnection projectGitUrl 74 | url projectSiteUrl 75 | } 76 | } 77 | } 78 | } 79 | } 80 | 81 | if (project.hasProperty("android")) { // Android libraries 82 | task sourcesJar(type: Jar) { 83 | classifier = 'sources' 84 | from android.sourceSets.main.java.srcDirs 85 | } 86 | 87 | task javadoc(type: Javadoc) { 88 | source = android.sourceSets.main.java.srcDirs 89 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 90 | 91 | } 92 | } else { // Java libraries 93 | task sourcesJar(type: Jar, dependsOn: classes) { 94 | classifier = 'sources' 95 | from sourceSets.main.allSource 96 | } 97 | } 98 | 99 | task javadocJar(type: Jar, dependsOn: javadoc) { 100 | classifier = 'javadoc' 101 | from javadoc.destinationDir 102 | } 103 | 104 | javadoc { 105 | options { 106 | encoding "UTF-8" 107 | charSet 'UTF-8' 108 | } 109 | } 110 | 111 | 112 | artifacts { 113 | archives javadocJar 114 | archives sourcesJar 115 | } 116 | 117 | bintray { 118 | user = bintrayUser 119 | key = bintrayApikey 120 | configurations = ['archives'] 121 | pkg { 122 | repo = "maven" 123 | name = project.archivesBaseName //发布到JCenter上的项目名字 124 | websiteUrl = projectGitUrl 125 | vcsUrl = projectGitUrl 126 | licenses = ["Apache-2.0"] 127 | publish = true 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /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 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:2.2.2' 10 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7' 11 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' 12 | classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:1.0.10' 13 | } 14 | } 15 | 16 | allprojects { 17 | repositories { 18 | 19 | jcenter() 20 | 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | groupId=com.hongjun 15 | description=OkDeepLink provides a annotation-based api to manipulate app deep links. 16 | versionName=1.0.0 17 | packaging=aar 18 | siteUrl=https://github.com/HongJun2046/OkDeepLink 19 | gitUrl=https://github.com/HongJun2046/OkDeepLink.git 20 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjerry/OkDeepLink/9fa28eb3c04380970b76e0af9b0434170d82184a/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Dec 13 20:37:13 PST 2016 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-3.3-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn ( ) { 37 | echo "$*" 38 | } 39 | 40 | die ( ) { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /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 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 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 Windows 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 | -------------------------------------------------------------------------------- /okdeeplink-annotation/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /okdeeplink-annotation/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | sourceCompatibility = 1.7 3 | 4 | dependencies { 5 | } 6 | 7 | apply from: "${rootDir}/pom-evaluator.gradle" 8 | -------------------------------------------------------------------------------- /okdeeplink-annotation/src/main/java/okdeeplink/Action.java: -------------------------------------------------------------------------------- 1 | 2 | package okdeeplink; 3 | 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | 10 | @Target(ElementType.METHOD) 11 | @Retention(RetentionPolicy.CLASS) 12 | public @interface Action { 13 | String value(); 14 | } 15 | -------------------------------------------------------------------------------- /okdeeplink-annotation/src/main/java/okdeeplink/Activity.java: -------------------------------------------------------------------------------- 1 | package okdeeplink; 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 | @Target(ElementType.METHOD) 10 | @Retention(RetentionPolicy.RUNTIME) 11 | public @interface Activity { 12 | Class value(); 13 | } 14 | -------------------------------------------------------------------------------- /okdeeplink-annotation/src/main/java/okdeeplink/AppLink.java: -------------------------------------------------------------------------------- 1 | 2 | package okdeeplink; 3 | 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | 10 | @Target(ElementType.TYPE) 11 | @Retention(RetentionPolicy.CLASS) 12 | public @interface AppLink { 13 | String host(); 14 | String scheme(); 15 | } 16 | -------------------------------------------------------------------------------- /okdeeplink-annotation/src/main/java/okdeeplink/Intercept.java: -------------------------------------------------------------------------------- 1 | package okdeeplink; 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 | @Target({ElementType.TYPE, ElementType.METHOD}) 10 | @Retention(RetentionPolicy.CLASS) 11 | public @interface Intercept { 12 | Class[] value() default Object.class; 13 | 14 | String path() default ""; 15 | } 16 | -------------------------------------------------------------------------------- /okdeeplink-annotation/src/main/java/okdeeplink/Path.java: -------------------------------------------------------------------------------- 1 | 2 | package okdeeplink; 3 | 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | 10 | @Target(ElementType.METHOD) 11 | @Retention(RetentionPolicy.CLASS) 12 | public @interface Path { 13 | String[] value(); 14 | } 15 | -------------------------------------------------------------------------------- /okdeeplink-annotation/src/main/java/okdeeplink/Query.java: -------------------------------------------------------------------------------- 1 | package okdeeplink; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Inherited; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | 10 | @Inherited 11 | @Target({ElementType.FIELD,ElementType.PARAMETER}) 12 | @Retention(RetentionPolicy.RUNTIME) 13 | public @interface Query { 14 | String value(); 15 | } 16 | -------------------------------------------------------------------------------- /okdeeplink-annotation/src/main/java/okdeeplink/RequestCode.java: -------------------------------------------------------------------------------- 1 | package okdeeplink; 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 | * Created by zhangqijun on 2017/5/1. 10 | */ 11 | 12 | @Target(ElementType.PARAMETER) 13 | @Retention(RetentionPolicy.CLASS) 14 | public @interface RequestCode { 15 | } 16 | -------------------------------------------------------------------------------- /okdeeplink-annotation/src/main/java/okdeeplink/Service.java: -------------------------------------------------------------------------------- 1 | package okdeeplink; 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 | @Target({ElementType.FIELD}) 9 | @Retention(RetentionPolicy.CLASS) 10 | public @interface Service { 11 | } 12 | -------------------------------------------------------------------------------- /okdeeplink-annotation/src/main/java/okdeeplink/Uri.java: -------------------------------------------------------------------------------- 1 | 2 | package okdeeplink; 3 | 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | 10 | @Target(ElementType.METHOD) 11 | @Retention(RetentionPolicy.CLASS) 12 | public @interface Uri { 13 | String value(); 14 | } 15 | -------------------------------------------------------------------------------- /okdeeplink-annotation/src/main/java/okdeeplink/UriReplace.java: -------------------------------------------------------------------------------- 1 | package okdeeplink; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Inherited; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | 10 | @Inherited 11 | @Target({ElementType.PARAMETER}) 12 | @Retention(RetentionPolicy.RUNTIME) 13 | public @interface UriReplace { 14 | String value(); 15 | } 16 | -------------------------------------------------------------------------------- /okdeeplink-api/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /okdeeplink-api/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.2" 6 | 7 | defaultConfig { 8 | minSdkVersion 14 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | consumerProguardFiles 'proguard-rules.pro' 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 | 24 | dependencies { 25 | compile fileTree(dir: 'libs', include: ['*.jar']) 26 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 27 | exclude group: 'com.android.support', module: 'support-annotations' 28 | }) 29 | compile "com.android.support:appcompat-v7:25.0.0" 30 | compile project(":rxactivityresult") 31 | compile project(":okdeeplink-annotation") 32 | compile 'org.aspectj:aspectjrt:1.8.9' 33 | testCompile 'junit:junit:4.12' 34 | } 35 | apply from: "${rootDir}/pom-evaluator.gradle" 36 | -------------------------------------------------------------------------------- /okdeeplink-api/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/sunshine/Documents/AndroidStudio/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 | 20 | 21 | -keep class okdeeplink.** {*;} 22 | -dontwarn okdeeplink.** 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /okdeeplink-api/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /okdeeplink-api/src/main/java/okdeeplink/Address.java: -------------------------------------------------------------------------------- 1 | package okdeeplink; 2 | 3 | 4 | import android.net.Uri; 5 | 6 | public final class Address { 7 | 8 | private final Class activityClass; 9 | private final String path; 10 | 11 | public Address(String path, Class activityClass) { 12 | this.path = path; 13 | this.activityClass = activityClass; 14 | 15 | } 16 | 17 | public Class getActivityClass() { 18 | return activityClass; 19 | } 20 | 21 | 22 | public boolean isUri(){ 23 | boolean isUri = false; 24 | try { 25 | Uri uri = Uri.parse(path); 26 | isUri = true; 27 | return isUri; 28 | } catch (Exception e) { 29 | e.printStackTrace(); 30 | } 31 | return isUri; 32 | 33 | } 34 | 35 | public String getPath() { 36 | return path; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /okdeeplink-api/src/main/java/okdeeplink/BundleCompact.java: -------------------------------------------------------------------------------- 1 | package okdeeplink; 2 | 3 | import android.app.Activity; 4 | import android.app.Fragment; 5 | import android.os.Bundle; 6 | import android.text.TextUtils; 7 | 8 | /** 9 | * Created by zhangqijun on 2017/4/26. 10 | */ 11 | 12 | public class BundleCompact { 13 | 14 | 15 | public static T getValue(Bundle dataBundle, String key, Class valueClass) { 16 | Object value = getValue(dataBundle, key); 17 | if (value != null) { 18 | try { 19 | if (value instanceof String) { 20 | String strValue = (String) value; 21 | return (T) parseString(strValue, valueClass); 22 | } else { 23 | return valueClass.cast(value); 24 | } 25 | } catch (Exception e) { 26 | e.printStackTrace(); 27 | } 28 | 29 | } 30 | return (T) value; 31 | } 32 | 33 | public static Bundle getSupportBundle(Object target) { 34 | if (target instanceof Activity) { 35 | return getBundle((Activity) target); 36 | } 37 | if (target instanceof Fragment) { 38 | return getBundle((Fragment) target); 39 | } 40 | if (target instanceof android.support.v4.app.Fragment) { 41 | return getBundle((android.support.v4.app.Fragment) target); 42 | } 43 | return null; 44 | } 45 | 46 | 47 | public static Object getValue(Bundle bundle, String query) { 48 | if (bundle != null && !TextUtils.isEmpty(query)) { 49 | return bundle.get(query); 50 | } 51 | return null; 52 | } 53 | 54 | 55 | private static Bundle getBundle(Activity activity) { 56 | 57 | if (activity == null || activity.getIntent() == null) { 58 | return null; 59 | } 60 | return activity.getIntent().getExtras(); 61 | } 62 | 63 | private static Bundle getBundle(Fragment fragment) { 64 | 65 | if (fragment == null || fragment.getArguments() == null) { 66 | return null; 67 | } 68 | return fragment.getArguments(); 69 | 70 | } 71 | 72 | private static Bundle getBundle(android.support.v4.app.Fragment fragment) { 73 | if (fragment == null || fragment.getArguments() == null) { 74 | return null; 75 | } 76 | return fragment.getArguments(); 77 | } 78 | 79 | private static Object parseString(String value, Class clazz) { 80 | if (Boolean.class == clazz || Boolean.TYPE == clazz) return Boolean.parseBoolean(value); 81 | if (Byte.class == clazz || Byte.TYPE == clazz) return Byte.parseByte(value); 82 | if (Short.class == clazz || Short.TYPE == clazz) return Short.parseShort(value); 83 | if (Integer.class == clazz || Integer.TYPE == clazz) return Integer.parseInt(value); 84 | if (Long.class == clazz || Long.TYPE == clazz) return Long.parseLong(value); 85 | if (Float.class == clazz || Float.TYPE == clazz) return Float.parseFloat(value); 86 | if (Double.class == clazz || Double.TYPE == clazz) return Double.parseDouble(value); 87 | return value; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /okdeeplink-api/src/main/java/okdeeplink/DeepLinkActivity.java: -------------------------------------------------------------------------------- 1 | package okdeeplink; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | 7 | import rx.Subscriber; 8 | 9 | public class DeepLinkActivity extends Activity { 10 | 11 | 12 | @Override 13 | protected void onCreate(Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | dispatchFrom(getIntent()); 16 | } 17 | 18 | 19 | public void dispatchFrom(Intent intent) { 20 | new DeepLinkClient(this) 21 | .buildRequest(intent) 22 | .dispatch() 23 | .subscribe(new Subscriber() { 24 | @Override 25 | public void onCompleted() { 26 | finish(); 27 | } 28 | 29 | @Override 30 | public void onError(Throwable e) { 31 | finish(); 32 | } 33 | 34 | @Override 35 | public void onNext(Request request) { 36 | Intent dispatchIntent = request.getIntent(); 37 | startActivity(dispatchIntent); 38 | } 39 | }); 40 | } 41 | 42 | @Override 43 | protected void onNewIntent(Intent intent) { 44 | super.onNewIntent(intent); 45 | dispatchFrom(intent); 46 | } 47 | 48 | 49 | } 50 | -------------------------------------------------------------------------------- /okdeeplink-api/src/main/java/okdeeplink/DeepLinkClient.java: -------------------------------------------------------------------------------- 1 | package okdeeplink; 2 | 3 | import android.content.ComponentName; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.net.Uri; 7 | import android.os.Bundle; 8 | import android.support.v4.app.Fragment; 9 | import android.util.Log; 10 | 11 | import java.util.ArrayList; 12 | import java.util.HashMap; 13 | import java.util.Hashtable; 14 | import java.util.List; 15 | import java.util.Map; 16 | 17 | import rx.Observable; 18 | import rx.Subscriber; 19 | 20 | 21 | /** 22 | * Created by zhangqijun on 2017/3/8. 23 | */ 24 | 25 | public class DeepLinkClient { 26 | 27 | 28 | public static final String TAG = "OkDeepLink"; 29 | 30 | private static Map addresses = new Hashtable<>(); 31 | 32 | private List interceptors = new ArrayList<>(); 33 | 34 | 35 | private Context context; 36 | 37 | private int interceptTimeOut = 0; 38 | 39 | private static boolean debug = true; 40 | 41 | 42 | public DeepLinkClient(Context context) { 43 | this.context = context; 44 | } 45 | 46 | public DeepLinkClient(Fragment fragment) { 47 | this.context = fragment.getActivity(); 48 | } 49 | 50 | public static void setDebug(boolean debugFlag) { 51 | debug = debugFlag; 52 | } 53 | 54 | public static boolean isDebug() { 55 | return debug; 56 | } 57 | 58 | /** 59 | * 每个matcher都会通过aop往这里注册路由表 60 | */ 61 | public void init() { 62 | 63 | } 64 | 65 | public DeepLinkClient setInterceptTimeOut(int interceptTimeOut) { 66 | this.interceptTimeOut = interceptTimeOut; 67 | return this; 68 | } 69 | 70 | public int getInterceptTimeOut() { 71 | return interceptTimeOut; 72 | } 73 | 74 | public T build(Class service) { 75 | return null; 76 | } 77 | 78 | 79 | public Observable createResponse(Request request) { 80 | return new RealCall(request).createResponse(); 81 | } 82 | 83 | public Observable createRequest(Request request) { 84 | return new RealCall(request).createRequest(); 85 | } 86 | 87 | public void start(Request request) { 88 | createResponse(request) 89 | .subscribe(new Subscriber() { 90 | @Override 91 | public void onCompleted() { 92 | 93 | } 94 | 95 | @Override 96 | public void onError(Throwable e) { 97 | 98 | } 99 | 100 | @Override 101 | public void onNext(Response response) { 102 | 103 | } 104 | }); 105 | } 106 | 107 | public Observable dispatch(Request request) { 108 | return new RealCall(request).dispatchRequest(); 109 | } 110 | 111 | public Intent buildIntent(String url) { 112 | Intent intent = new Intent(); 113 | intent.setData(Uri.parse(url)); 114 | return buildRequest(intent).getIntent(); 115 | } 116 | 117 | public Request buildRequest(String url) { 118 | Intent intent = new Intent(); 119 | intent.setData(Uri.parse(url)); 120 | return buildRequest(intent); 121 | } 122 | 123 | public Request buildRequest(Intent sourceIntent) { 124 | if (sourceIntent == null) { 125 | return null; 126 | } 127 | Intent newIntent = new Intent(sourceIntent); 128 | Uri uri = newIntent.getData(); 129 | 130 | addNewTaskFlag(newIntent); 131 | 132 | if (uri != null) { 133 | addBundleQuery(newIntent, uri); 134 | 135 | Address entry = new DeepLinkClient(context).matchUrl(uri.toString()); 136 | if (entry == null || entry.getActivityClass() == null) { 137 | return new Request(newIntent, this).setDeepLink(false); 138 | } 139 | newIntent.setComponent(new ComponentName(context, entry.getActivityClass())); 140 | 141 | return new Request(newIntent, this); 142 | } 143 | return new Request(newIntent, this).setDeepLink(false); 144 | 145 | } 146 | 147 | private void addNewTaskFlag(Intent newIntent) { 148 | if (context.getClass() == Context.class) { 149 | newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 150 | } 151 | } 152 | 153 | private void addBundleQuery(Intent newIntent, Uri uri) { 154 | Map uriParams = new HashMap<>(); 155 | try { 156 | for (String queryParameter : uri.getQueryParameterNames()) { 157 | for (String queryParameterValue : uri.getQueryParameters(queryParameter)) { 158 | if (uriParams.containsKey(queryParameter)) { 159 | Log.w(TAG, "Duplicate parameter name in path and query param: " + queryParameter); 160 | } 161 | uriParams.put(queryParameter, queryParameterValue); 162 | } 163 | } 164 | } catch (Exception e) { 165 | e.printStackTrace(); 166 | } 167 | 168 | Bundle bundle = newIntent.getExtras(); 169 | if (bundle == null) { 170 | bundle = new Bundle(); 171 | } 172 | for (Map.Entry parameterEntry : uriParams.entrySet()) { 173 | String value = parameterEntry.getValue(); 174 | bundle.putString(parameterEntry.getKey(), value); 175 | } 176 | newIntent.putExtras(bundle); 177 | } 178 | 179 | 180 | public DeepLinkClient addInterceptor(Interceptor interceptor) { 181 | interceptors.add(interceptor); 182 | return this; 183 | } 184 | 185 | 186 | public List getInterceptors() { 187 | return interceptors; 188 | } 189 | 190 | public List getGlobalInterceptors() { 191 | return new ArrayList<>(); 192 | } 193 | 194 | public Map getPathInterceptors() { 195 | return new Hashtable<>(); 196 | } 197 | 198 | 199 | public Context getContext() { 200 | return context; 201 | } 202 | 203 | public DeepLinkClient setContext(Context context) { 204 | this.context = context; 205 | return this; 206 | } 207 | 208 | public Address matchUrl(String url) { 209 | if (DeepLinkClient.isEmpty()) { 210 | init(); 211 | } 212 | String path = url; 213 | try { 214 | Uri uri = Uri.parse(url); 215 | path = uri.getEncodedPath(); 216 | } catch (Exception e) { 217 | e.printStackTrace(); 218 | } 219 | for (Address entry : addresses.values()) { 220 | if (entry.getPath().equals(path)) { 221 | return entry; 222 | } 223 | } 224 | return null; 225 | } 226 | 227 | public static boolean isEmpty() { 228 | return addresses.isEmpty(); 229 | } 230 | 231 | 232 | public static void addAddress(Address address) { 233 | addresses.put(address.getPath(), address); 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /okdeeplink-api/src/main/java/okdeeplink/Interceptor.java: -------------------------------------------------------------------------------- 1 | package okdeeplink; 2 | 3 | 4 | /** 5 | * Created by zhangqijun on 2017/5/1. 6 | */ 7 | 8 | public abstract class Interceptor { 9 | 10 | 11 | public abstract void intercept(Call call); 12 | 13 | public void onCall(Request request) { 14 | 15 | } 16 | 17 | public void onNotFound(Request request) { 18 | 19 | } 20 | 21 | public void onError(Request request, Throwable throwable) { 22 | 23 | } 24 | 25 | public interface Call { 26 | Request getRequest(); 27 | 28 | void proceed(Request request); 29 | 30 | void proceed(); 31 | 32 | void cancel(); 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /okdeeplink-api/src/main/java/okdeeplink/LogInterceptor.java: -------------------------------------------------------------------------------- 1 | package okdeeplink; 2 | 3 | import android.util.Log; 4 | 5 | /** 6 | * Created by zhangqijun on 2017/5/7. 7 | */ 8 | 9 | public class LogInterceptor extends Interceptor { 10 | 11 | 12 | @Override 13 | public void intercept(Call call) { 14 | Request request = call.getRequest(); 15 | log("deep link is : " + request.getUrl()); 16 | call.proceed(); 17 | } 18 | 19 | @Override 20 | public void onNotFound(Request request) { 21 | super.onNotFound(request); 22 | log("deep link is not found : " + request.getUrl()); 23 | 24 | } 25 | 26 | @Override 27 | public void onError(Request request, Throwable throwable) { 28 | super.onError(request, throwable); 29 | log("deep link is throw throwable : [" + request.getUrl() + "] -------->" + throwable.getMessage()); 30 | } 31 | 32 | public void log(String msg) { 33 | if (DeepLinkClient.isDebug()) { 34 | Log.v(DeepLinkClient.TAG, msg); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /okdeeplink-api/src/main/java/okdeeplink/RealCall.java: -------------------------------------------------------------------------------- 1 | package okdeeplink; 2 | 3 | import android.app.Activity; 4 | import android.app.FragmentManager; 5 | import android.content.ActivityNotFoundException; 6 | import android.content.Context; 7 | import android.content.Intent; 8 | import android.os.Build; 9 | import android.support.v4.app.FragmentActivity; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | import rx.Observable; 16 | import rx.functions.Action1; 17 | import rx.functions.Func1; 18 | import rxactivityresult.ActivityResult; 19 | import rxactivityresult.RxActivityResult; 20 | 21 | /** 22 | * Created by zhangqijun on 2017/5/5. 23 | */ 24 | 25 | public final class RealCall { 26 | 27 | 28 | private Request request; 29 | private DeepLinkClient deepLinkClient; 30 | 31 | private List interceptors; 32 | private int interceptTimeOut; 33 | 34 | public RealCall(Request request) { 35 | this.request = request; 36 | this.deepLinkClient = request.getDeepLinkClient(); 37 | List interceptorList = new ArrayList<>(); 38 | 39 | interceptorList.addAll(request.getInterceptors()); 40 | interceptorList.addAll(deepLinkClient.getInterceptors()); 41 | interceptorList.addAll(getPathInterceptors(request)); 42 | interceptorList.addAll(deepLinkClient.getGlobalInterceptors()); 43 | 44 | interceptorList.add(new LogInterceptor()); 45 | this.interceptors = interceptorList; 46 | this.interceptTimeOut = deepLinkClient.getInterceptTimeOut(); 47 | } 48 | 49 | private List getPathInterceptors(Request request) { 50 | String path = request.getPath(); 51 | List list = new ArrayList<>(); 52 | for (Map.Entry pathInterceptors : deepLinkClient.getPathInterceptors().entrySet()) { 53 | String pathKey = pathInterceptors.getKey(); 54 | Interceptor interceptor = pathInterceptors.getValue(); 55 | if (pathKey.equals(path)) { 56 | list.add(interceptor); 57 | } 58 | } 59 | return list; 60 | } 61 | 62 | 63 | public Observable createRequest() { 64 | 65 | return buildRequest() 66 | .doOnError(new Action1() { 67 | @Override 68 | public void call(Throwable throwable) { 69 | callThrowable(throwable); 70 | } 71 | }); 72 | } 73 | 74 | 75 | private Observable buildRequest() { 76 | RealCallChain chain = new RealCallChain(interceptors, 0, request); 77 | chain.setTimeout(interceptTimeOut); 78 | chain.call(); 79 | return chain 80 | .getRequestObservable() 81 | .map(new Func1() { 82 | @Override 83 | public Request call(Request request) { 84 | if (interceptors != null) { 85 | for (Interceptor interceptor : interceptors) { 86 | interceptor.onCall(request); 87 | } 88 | } 89 | return request; 90 | } 91 | }); 92 | } 93 | 94 | 95 | public Observable createResponse() { 96 | 97 | return buildRequest() 98 | .flatMap(new Func1>() { 99 | @Override 100 | public Observable call(final Request request) { 101 | 102 | Context context = getStartContext(request.getContext()); 103 | 104 | return RxActivityResult 105 | .on(context) 106 | .startIntent(request.getIntent()) 107 | .map(new Func1() { 108 | @Override 109 | public Response call(ActivityResult activityActivityResult) { 110 | return new Response(request, activityActivityResult.getResultCode(), activityActivityResult.getData()); 111 | } 112 | }); 113 | } 114 | }).doOnError(new Action1() { 115 | @Override 116 | public void call(Throwable throwable) { 117 | callThrowable(throwable); 118 | } 119 | }); 120 | } 121 | 122 | 123 | private Context getStartContext(Context context) { 124 | if (context instanceof Activity) { 125 | Activity activity = (Activity) context; 126 | boolean isDestroyed = false; 127 | if (activity.isFinishing()) { 128 | isDestroyed = true; 129 | } 130 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { 131 | if (activity.isDestroyed()) { 132 | isDestroyed = true; 133 | } 134 | 135 | } 136 | FragmentManager fragmentManager = activity.getFragmentManager(); 137 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { 138 | if (fragmentManager.isDestroyed()) { 139 | isDestroyed = true; 140 | } 141 | } 142 | if (context instanceof FragmentActivity) { 143 | FragmentActivity fragmentActivity = (FragmentActivity) context; 144 | android.support.v4.app.FragmentManager fragmentManagerV4 = fragmentActivity.getSupportFragmentManager(); 145 | 146 | if (fragmentManagerV4.isDestroyed()) { 147 | isDestroyed = true; 148 | } 149 | } 150 | if (isDestroyed) { 151 | return context.getApplicationContext(); 152 | } 153 | } 154 | return context; 155 | } 156 | 157 | private void callThrowable(Throwable throwable) { 158 | if (throwable instanceof ActivityNotFoundException) { 159 | if (interceptors != null) { 160 | for (Interceptor interceptor : interceptors) { 161 | interceptor.onNotFound(request); 162 | } 163 | } 164 | } 165 | if (interceptors != null) { 166 | for (Interceptor interceptor : interceptors) { 167 | interceptor.onError(request, throwable); 168 | } 169 | } 170 | } 171 | 172 | public Observable dispatchRequest() { 173 | return buildRequest() 174 | .flatMap(new Func1>() { 175 | @Override 176 | public Observable call(Request request) { 177 | if (!request.isDeepLink()) { 178 | return Observable.error(new ActivityNotFoundException()); 179 | } 180 | Context context = request.getContext(); 181 | Intent intent = request.getIntent(); 182 | if (context instanceof android.app.Activity) { 183 | android.app.Activity activity = (android.app.Activity) context; 184 | if (activity.getCallingActivity() != null) { 185 | intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); 186 | } 187 | } 188 | if (context != null && context.getClass() == Context.class) { 189 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 190 | } 191 | return Observable.just(request); 192 | } 193 | }) 194 | .onErrorResumeNext(new Func1>() { 195 | @Override 196 | public Observable call(Throwable throwable) { 197 | callThrowable(throwable); 198 | return Observable.error(throwable); 199 | } 200 | }); 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /okdeeplink-api/src/main/java/okdeeplink/RealCallChain.java: -------------------------------------------------------------------------------- 1 | package okdeeplink; 2 | 3 | import android.app.PendingIntent; 4 | 5 | import java.util.List; 6 | import java.util.concurrent.TimeUnit; 7 | import java.util.concurrent.TimeoutException; 8 | 9 | import rx.Observable; 10 | import rx.Subscriber; 11 | import rx.android.schedulers.AndroidSchedulers; 12 | import rx.functions.Action1; 13 | import rx.schedulers.Schedulers; 14 | import rx.subjects.AsyncSubject; 15 | import rx.subjects.BehaviorSubject; 16 | 17 | /** 18 | * Created by zhangqijun on 2017/5/5. 19 | */ 20 | 21 | public class RealCallChain implements Interceptor.Call { 22 | 23 | 24 | private final List interceptors; 25 | private int index; 26 | private Request request; 27 | 28 | public final BehaviorSubject interceptorSubject = BehaviorSubject.create(); 29 | 30 | public final AsyncSubject requestSubject = AsyncSubject.create(); 31 | 32 | private int timeout = 0; 33 | 34 | public RealCallChain(List interceptors, int index, Request request) { 35 | this.interceptors = interceptors; 36 | this.index = index; 37 | this.request = request; 38 | } 39 | 40 | public RealCallChain setTimeout(int timeout) { 41 | this.timeout = timeout; 42 | return this; 43 | } 44 | 45 | public void call() { 46 | 47 | Observable.just(1) 48 | .observeOn(Schedulers.io()) 49 | .subscribe(new Action1() { 50 | @Override 51 | public void call(Integer integer) { 52 | proceed(); 53 | } 54 | }); 55 | 56 | 57 | Observable observable = interceptorSubject; 58 | if (timeout > 0) { 59 | observable = interceptorSubject.timeout(timeout, TimeUnit.SECONDS); 60 | } 61 | observable.subscribe(new Subscriber() { 62 | @Override 63 | public void onCompleted() { 64 | 65 | } 66 | 67 | @Override 68 | public void onError(Throwable e) { 69 | 70 | requestSubject.onError(new TimeoutException()); 71 | } 72 | 73 | @Override 74 | public void onNext(Interceptor interceptor) { 75 | 76 | } 77 | }); 78 | } 79 | 80 | 81 | @Override 82 | public Request getRequest() { 83 | return request; 84 | } 85 | 86 | 87 | @Override 88 | public void proceed(Request request) { 89 | this.request = request; 90 | proceed(); 91 | } 92 | 93 | public Observable getRequestObservable() { 94 | return requestSubject 95 | .asObservable() 96 | .subscribeOn(Schedulers.io()) 97 | .observeOn(AndroidSchedulers.mainThread()); 98 | } 99 | 100 | public void realCall() { 101 | requestSubject.onNext(request); 102 | requestSubject.onCompleted(); 103 | } 104 | 105 | @Override 106 | public void proceed() { 107 | 108 | 109 | if (index >= interceptors.size()) { 110 | realCall(); 111 | return; 112 | } 113 | final Interceptor interceptor = interceptors.get(index); 114 | Observable 115 | .just(1) 116 | .observeOn(AndroidSchedulers.mainThread()) 117 | .subscribe(new Action1() { 118 | @Override 119 | public void call(Integer integer) { 120 | interceptor.intercept(RealCallChain.this); 121 | } 122 | }); 123 | 124 | interceptorSubject.onNext(interceptor); 125 | index = index + 1; 126 | } 127 | 128 | @Override 129 | public void cancel() { 130 | requestSubject.onError(new PendingIntent.CanceledException()); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /okdeeplink-api/src/main/java/okdeeplink/Request.java: -------------------------------------------------------------------------------- 1 | package okdeeplink; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | 7 | import java.util.ArrayList; 8 | import java.util.HashMap; 9 | import java.util.Iterator; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.Set; 13 | 14 | import rx.Observable; 15 | 16 | /** 17 | * Created by zhangqijun on 2017/5/2. 18 | */ 19 | 20 | public class Request { 21 | 22 | 23 | private Intent intent; 24 | private Context context; 25 | private DeepLinkClient deepLinkClient; 26 | private int requestCode = -1; 27 | 28 | private boolean isDeepLink = true; 29 | 30 | private List interceptors = new ArrayList<>(); 31 | 32 | 33 | public Request(Intent intent, DeepLinkClient deepLinkClient) { 34 | this.intent = intent; 35 | this.context = deepLinkClient.getContext(); 36 | this.deepLinkClient = deepLinkClient; 37 | } 38 | 39 | 40 | public Request addInterceptor(Interceptor interceptor) { 41 | interceptors.add(interceptor); 42 | return this; 43 | } 44 | 45 | public List getInterceptors() { 46 | return interceptors; 47 | } 48 | 49 | public Request(Intent intent, DeepLinkClient deepLinkClient, int requestCode) { 50 | this.requestCode = requestCode; 51 | this.intent = intent; 52 | this.context = deepLinkClient.getContext(); 53 | this.deepLinkClient = deepLinkClient; 54 | } 55 | 56 | 57 | public Request setDeepLink(boolean deepLink) { 58 | isDeepLink = deepLink; 59 | return this; 60 | } 61 | 62 | public boolean isDeepLink() { 63 | return isDeepLink; 64 | } 65 | 66 | public Request setIntent(Intent intent) { 67 | this.intent = intent; 68 | return this; 69 | } 70 | 71 | public Request setRequestCode(int requestCode) { 72 | this.requestCode = requestCode; 73 | return this; 74 | } 75 | 76 | public int getRequestCode() { 77 | return requestCode; 78 | } 79 | 80 | public Intent getIntent() { 81 | return intent; 82 | } 83 | 84 | 85 | public String getUrl() { 86 | return getUrl(intent); 87 | } 88 | 89 | public String getScheme() { 90 | if (intent != null && intent.getData() != null) { 91 | return intent.getData().getScheme(); 92 | } 93 | return ""; 94 | } 95 | 96 | public String getHost() { 97 | if (intent != null && intent.getData() != null) { 98 | return intent.getData().getHost(); 99 | } 100 | return ""; 101 | } 102 | 103 | public String getPath() { 104 | if (intent != null && intent.getData() != null) { 105 | return intent.getData().getEncodedPath(); 106 | } 107 | return ""; 108 | } 109 | 110 | 111 | public static String getUrl(Intent intent) { 112 | if (intent != null && intent.getData() != null) { 113 | return intent.getData().toString(); 114 | } 115 | return ""; 116 | } 117 | 118 | public Map getQuery() { 119 | return getQuery(intent); 120 | } 121 | 122 | 123 | public static Map getQuery(Intent intent) { 124 | Map query = new HashMap<>(); 125 | if (intent != null) { 126 | Bundle bundle = intent.getExtras(); 127 | if (bundle != null) { 128 | Set keys = bundle.keySet(); 129 | Iterator it = keys.iterator(); 130 | while (it.hasNext()) { 131 | String key = it.next(); 132 | 133 | bundle.get(key); 134 | query.put(key, bundle.get(key)); 135 | 136 | } 137 | } 138 | } 139 | return query; 140 | } 141 | 142 | public T build(Class service) { 143 | if (deepLinkClient != null && context != null) { 144 | return deepLinkClient.build(service); 145 | } 146 | return null; 147 | } 148 | 149 | public Context getContext() { 150 | return context; 151 | } 152 | 153 | 154 | public Request setContext(Context context) { 155 | this.context = context; 156 | if (deepLinkClient != null) { 157 | deepLinkClient.setContext(context); 158 | } 159 | return this; 160 | } 161 | 162 | public DeepLinkClient getDeepLinkClient() { 163 | return deepLinkClient; 164 | } 165 | 166 | 167 | public Observable createResponse() { 168 | return deepLinkClient.createResponse(this); 169 | } 170 | 171 | public Observable createRequest() { 172 | return deepLinkClient.createRequest(this); 173 | } 174 | 175 | public void start() { 176 | deepLinkClient.start(this); 177 | } 178 | 179 | public Observable dispatch() { 180 | return deepLinkClient.dispatch(this); 181 | } 182 | 183 | } 184 | -------------------------------------------------------------------------------- /okdeeplink-api/src/main/java/okdeeplink/Response.java: -------------------------------------------------------------------------------- 1 | package okdeeplink; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | 6 | /** 7 | * Created by zhangqijun on 2017/5/2. 8 | */ 9 | 10 | public class Response { 11 | 12 | private Request request; 13 | private int resultCode = Activity.RESULT_CANCELED; 14 | private Intent data; 15 | 16 | 17 | public Response(Request request, int resultCode, Intent data) { 18 | this.request = request; 19 | this.resultCode = resultCode; 20 | this.data = data; 21 | } 22 | 23 | public Request getRequest() { 24 | return request; 25 | } 26 | 27 | public int getResultCode() { 28 | return resultCode; 29 | } 30 | 31 | 32 | public Intent getData() { 33 | return data; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /okdeeplink-api/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Router 3 | 4 | -------------------------------------------------------------------------------- /okdeeplink-gradle/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /okdeeplink-gradle/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'groovy' 2 | apply from: "${rootDir}/pom-evaluator.gradle" 3 | 4 | dependencies { 5 | compile gradleApi() 6 | compile localGroovy() 7 | compile 'com.android.tools.build:gradle:2.1.3' 8 | compile 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:1.0.10' 9 | 10 | } 11 | sourceCompatibility = "1.7" 12 | targetCompatibility = "1.7" 13 | //compileGroovy { 14 | // sourceCompatibility = 1.6 15 | // targetCompatibility = 1.6 16 | // options.encoding = 'UTF-8' 17 | //} 18 | 19 | sourceSets { 20 | main { 21 | groovy { 22 | srcDirs = ['src/main/groovy'] 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /okdeeplink-gradle/src/main/groovy/okdeeplink/OkDeepLinkPlugin.groovy: -------------------------------------------------------------------------------- 1 | package okdeeplink 2 | 3 | import com.android.build.gradle.AppPlugin 4 | import com.android.build.gradle.LibraryPlugin 5 | import org.gradle.api.Plugin 6 | import org.gradle.api.Project 7 | 8 | /** 9 | * Created by zhangqijun on 2017/5/16. 10 | */ 11 | 12 | public class OkDeepLinkPlugin implements Plugin { 13 | 14 | @Override 15 | public void apply(Project project) { 16 | 17 | 18 | def hasApp = project.plugins.withType(AppPlugin) 19 | def hasLib = project.plugins.withType(LibraryPlugin) 20 | def hasApt = project.plugins.hasPlugin('com.neenbedankt.android-apt') 21 | def usesAndroidAspectJxPlugin = project.plugins.hasPlugin('android-aspectjx') 22 | if (hasApp || hasLib) { 23 | if (hasApp && !usesAndroidAspectJxPlugin) { 24 | project.pluginManager.apply(com.hujiang.gradle.plugin.android.aspectjx.AndroidAspectJXPlugin); 25 | } 26 | project.dependencies { 27 | compile 'org.aspectj:aspectjrt:1.8.9' 28 | compile 'com.hongjun:okdeeplink-api:1.0.0' 29 | if (hasApt){ 30 | apt 'com.hongjun:okdeeplink-processor:1.0.0' 31 | }else { 32 | annotationProcessor 'com.hongjun:okdeeplink-processor:1.0.0' 33 | } 34 | 35 | } 36 | } 37 | project.configurations.all { 38 | resolutionStrategy.cacheChangingModulesFor 0, 'seconds' 39 | } 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /okdeeplink-gradle/src/main/resources/META-INF/gradle-plugins/okdeeplink.plugin.properties: -------------------------------------------------------------------------------- 1 | implementation-class=okdeeplink.OkDeepLinkPlugin -------------------------------------------------------------------------------- /okdeeplink-processor/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /okdeeplink-processor/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | sourceCompatibility = 1.7 4 | 5 | repositories { 6 | 7 | jcenter() 8 | 9 | } 10 | 11 | dependencies { 12 | compile project(':okdeeplink-annotation') 13 | compile 'org.aspectj:aspectjrt:1.8.9' 14 | compile 'com.google.auto.service:auto-service:1.0-rc2' 15 | compile 'com.squareup:javapoet:1.8.0' 16 | compile 'com.google.auto:auto-common:0.6' 17 | 18 | } 19 | 20 | apply from: "${rootDir}/pom-evaluator.gradle" 21 | -------------------------------------------------------------------------------- /okdeeplink-processor/libs/android-support-v4.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjerry/OkDeepLink/9fa28eb3c04380970b76e0af9b0434170d82184a/okdeeplink-processor/libs/android-support-v4.jar -------------------------------------------------------------------------------- /okdeeplink-processor/src/main/java/okdeeplink/DeepLinkInjectProcessor.java: -------------------------------------------------------------------------------- 1 | 2 | package okdeeplink; 3 | 4 | import com.google.auto.service.AutoService; 5 | import com.squareup.javapoet.AnnotationSpec; 6 | import com.squareup.javapoet.ClassName; 7 | import com.squareup.javapoet.CodeBlock; 8 | import com.squareup.javapoet.JavaFile; 9 | import com.squareup.javapoet.MethodSpec; 10 | import com.squareup.javapoet.TypeName; 11 | import com.squareup.javapoet.TypeSpec; 12 | 13 | import org.aspectj.lang.JoinPoint; 14 | import org.aspectj.lang.ProceedingJoinPoint; 15 | import org.aspectj.lang.annotation.After; 16 | import org.aspectj.lang.annotation.Around; 17 | import org.aspectj.lang.annotation.Aspect; 18 | 19 | import java.io.IOException; 20 | import java.util.ArrayList; 21 | import java.util.HashMap; 22 | import java.util.HashSet; 23 | import java.util.List; 24 | import java.util.Map; 25 | import java.util.Set; 26 | 27 | import javax.annotation.processing.AbstractProcessor; 28 | import javax.annotation.processing.Filer; 29 | import javax.annotation.processing.ProcessingEnvironment; 30 | import javax.annotation.processing.Processor; 31 | import javax.annotation.processing.RoundEnvironment; 32 | import javax.lang.model.SourceVersion; 33 | import javax.lang.model.element.Element; 34 | import javax.lang.model.element.ElementKind; 35 | import javax.lang.model.element.Modifier; 36 | import javax.lang.model.element.PackageElement; 37 | import javax.lang.model.element.TypeElement; 38 | import javax.lang.model.type.TypeMirror; 39 | import javax.lang.model.util.Elements; 40 | import javax.lang.model.util.Types; 41 | 42 | import okdeeplink.util.ElementUtils; 43 | import okdeeplink.util.Logger; 44 | 45 | import static com.squareup.javapoet.MethodSpec.methodBuilder; 46 | import static javax.lang.model.element.ElementKind.CLASS; 47 | import static javax.lang.model.element.Modifier.PUBLIC; 48 | 49 | @AutoService(Processor.class) 50 | public class DeepLinkInjectProcessor extends AbstractProcessor { 51 | 52 | private static final String PACKAGE_NAME = DeepLinkInjectProcessor.class.getPackage().getName(); 53 | 54 | private static final ClassName BUNDLE = ClassName.get("android.os", "Bundle"); 55 | private static final ClassName INTENT = ClassName.get("android.content", "Intent"); 56 | private static final ClassName BUNDLE_COMPACT = ClassName.get(PACKAGE_NAME, "BundleCompact"); 57 | 58 | private static final String ACTIVITY = "android.app.Activity"; 59 | 60 | 61 | private static final String INJECTOR_SUFFIX = "$$Injector"; 62 | 63 | private static final List SUPPORT_INJECT = new ArrayList<>(); 64 | 65 | static { 66 | SUPPORT_INJECT.add(ACTIVITY); 67 | SUPPORT_INJECT.add("android.app.Fragment"); 68 | SUPPORT_INJECT.add("android.support.v4.app.Fragment"); 69 | } 70 | 71 | 72 | private Filer filer; 73 | private Logger logger; 74 | 75 | private Map> targetInjectElements; 76 | private Elements elements; 77 | private Types types; 78 | 79 | @Override 80 | public synchronized void init(ProcessingEnvironment processingEnv) { 81 | super.init(processingEnv); 82 | logger = new Logger(processingEnv.getMessager()); 83 | filer = processingEnv.getFiler(); 84 | elements = processingEnv.getElementUtils(); 85 | types = processingEnv.getTypeUtils(); 86 | } 87 | 88 | @Override 89 | public Set getSupportedAnnotationTypes() { 90 | Set ret = new HashSet<>(); 91 | ret.add(Query.class.getCanonicalName()); 92 | ret.add(Service.class.getCanonicalName()); 93 | return ret; 94 | } 95 | 96 | @Override 97 | public SourceVersion getSupportedSourceVersion() { 98 | return SourceVersion.latestSupported(); 99 | } 100 | 101 | @Override 102 | public boolean process(Set annotations, RoundEnvironment roundEnv) { 103 | 104 | List injectElements = new ArrayList<>(); 105 | List queryElements = generateQueryElements(roundEnv); 106 | List serviceElements = generateServiceElements(roundEnv); 107 | injectElements.addAll(queryElements); 108 | injectElements.addAll(serviceElements); 109 | if (ElementUtils.isEmpty(injectElements)) { 110 | return false; 111 | } 112 | targetInjectElements = findInjectElements(injectElements); 113 | if (ElementUtils.isEmpty(targetInjectElements)) { 114 | return false; 115 | } 116 | for (Map.Entry> injectElementEntrySet : targetInjectElements.entrySet()) { 117 | TypeElement targetElement = injectElementEntrySet.getKey(); 118 | List fieldElements = injectElementEntrySet.getValue(); 119 | 120 | MethodSpec injectQueryMethod = geneOnCreateQueryMethod(targetElement, fieldElements); 121 | 122 | MethodSpec injectServiceMethod = geneInjectServiceMethod(targetElement, fieldElements); 123 | 124 | MethodSpec saveInstanceMethod = geneSaveInstanceMethod(targetElement, fieldElements); 125 | 126 | 127 | MethodSpec newIntentMethod = geneOnNewIntentQueryMethod(targetElement, fieldElements); 128 | 129 | String fileName = targetElement.getSimpleName() + INJECTOR_SUFFIX; 130 | TypeSpec.Builder helper = TypeSpec.classBuilder(fileName) 131 | .addModifiers(PUBLIC) 132 | .addAnnotation(AnnotationSpec.builder(Aspect.class).build()) 133 | .addMethod(injectQueryMethod) 134 | .addMethod(injectServiceMethod) 135 | .addMethod(saveInstanceMethod); 136 | 137 | TypeMirror typeMirror = elements.getTypeElement(ACTIVITY).asType(); 138 | if (types.isSubtype(targetElement.asType(), typeMirror)) { 139 | helper.addMethod(newIntentMethod); 140 | } 141 | 142 | try { 143 | PackageElement packageElement = (PackageElement) targetElement.getEnclosingElement(); 144 | JavaFile.builder(packageElement.getQualifiedName().toString(), helper.build()).build().writeTo(filer); 145 | } catch (IOException e) { 146 | logger.error("Error creating inject file", targetElement); 147 | } 148 | 149 | } 150 | return true; 151 | } 152 | 153 | private MethodSpec geneOnCreateQueryMethod(TypeElement targetElement, List fieldElements) { 154 | 155 | MethodSpec.Builder injectMethodBuilder = MethodSpec.methodBuilder("onCreate") 156 | .addModifiers(PUBLIC) 157 | .addException(Throwable.class) 158 | .addAnnotation(AnnotationSpec.builder(Around.class).addMember("value", "$S", "execution(* " + targetElement.getQualifiedName() + ".onCreate(..))").build()) 159 | .addParameter(ProceedingJoinPoint.class, "joinPoint"); 160 | 161 | CodeBlock.Builder injectQueryCodeBuilder = geneOnCreateCodeBuilder(targetElement); 162 | 163 | List queryKeys = new ArrayList<>(); 164 | 165 | for (Element queryElement : fieldElements) { 166 | if (queryElement.getAnnotation(Query.class) != null) { 167 | String queryKey = queryElement.getAnnotation(Query.class).value(); 168 | if (queryKeys.contains(queryKey)) { 169 | logger.error("The inject query key cannot be Duplicate" + Query.class.getCanonicalName(), queryElement); 170 | } 171 | queryKeys.add(queryKey); 172 | 173 | injectQueryCodeBuilder 174 | .beginControlFlow("try") 175 | .add("target.$L= $T.getValue(dataBundle,$S,$T.class);\n", queryElement, BUNDLE_COMPACT, queryKey, queryElement) 176 | .nextControlFlow("catch ($T e)", Exception.class) 177 | .addStatement("e.printStackTrace()") 178 | .endControlFlow(); 179 | 180 | } 181 | } 182 | injectQueryCodeBuilder.add("joinPoint.proceed();\n"); 183 | injectMethodBuilder.addCode(injectQueryCodeBuilder.build()); 184 | 185 | return injectMethodBuilder.build(); 186 | } 187 | 188 | 189 | private MethodSpec geneOnNewIntentQueryMethod(TypeElement targetElement, List fieldElements) { 190 | 191 | MethodSpec.Builder injectMethodBuilder = MethodSpec.methodBuilder("onNewIntent") 192 | .addModifiers(PUBLIC) 193 | .addException(Throwable.class) 194 | .addAnnotation(AnnotationSpec.builder(Around.class).addMember("value", "$S", "execution(* " + targetElement.getQualifiedName() + ".onNewIntent(..))").build()) 195 | .addParameter(ProceedingJoinPoint.class, "joinPoint"); 196 | 197 | CodeBlock.Builder injectQueryCodeBuilder = geneOnNewIntentCodeBuilder(targetElement); 198 | 199 | List queryKeys = new ArrayList<>(); 200 | 201 | for (Element queryElement : fieldElements) { 202 | if (queryElement.getAnnotation(Query.class) != null) { 203 | String queryKey = queryElement.getAnnotation(Query.class).value(); 204 | if (queryKeys.contains(queryKey)) { 205 | logger.error("The inject query key cannot be Duplicate" + Query.class.getCanonicalName(), queryElement); 206 | } 207 | queryKeys.add(queryKey); 208 | 209 | injectQueryCodeBuilder 210 | .beginControlFlow("try") 211 | .add("target.$L= $T.getValue(dataBundle,$S,$T.class);\n", queryElement, BUNDLE_COMPACT, queryKey, queryElement) 212 | .nextControlFlow("catch ($T e)", Exception.class) 213 | .addStatement("e.printStackTrace()") 214 | .endControlFlow(); 215 | 216 | } 217 | } 218 | injectQueryCodeBuilder.add("joinPoint.proceed();\n"); 219 | injectMethodBuilder.addCode(injectQueryCodeBuilder.build()); 220 | 221 | return injectMethodBuilder.build(); 222 | } 223 | 224 | 225 | private MethodSpec geneInjectServiceMethod(TypeElement targetElement, List fieldElements) { 226 | 227 | MethodSpec.Builder injectMethodBuilder = MethodSpec.methodBuilder("onCreateService") 228 | .addModifiers(PUBLIC) 229 | .addException(Throwable.class) 230 | .addAnnotation(AnnotationSpec.builder(Around.class).addMember("value", "$S", "execution(* " + targetElement.getQualifiedName() + ".onCreate(..))").build()) 231 | .addParameter(ProceedingJoinPoint.class, "joinPoint"); 232 | 233 | CodeBlock.Builder injectServiceCodeBuilder = CodeBlock.builder(); 234 | injectServiceCodeBuilder.add("$T target = ($T)joinPoint.getTarget();\n", targetElement, targetElement); 235 | List serviceTypeNames = new ArrayList<>(); 236 | 237 | for (Element queryElement : fieldElements) { 238 | if (queryElement.getAnnotation(Service.class) != null) { 239 | TypeName className = TypeName.get(queryElement.asType()); 240 | if (serviceTypeNames.contains(className)) { 241 | logger.error("The inject " + className + " cannot be Duplicate", queryElement); 242 | } 243 | serviceTypeNames.add(className); 244 | injectServiceCodeBuilder.add("target.$L= new $T(target).build($T.class);\n ", queryElement, DeepLinkServiceProcessor.DEEP_LINK_CLIENT, queryElement); 245 | 246 | } 247 | } 248 | injectServiceCodeBuilder.add("joinPoint.proceed();\n"); 249 | injectMethodBuilder.addCode(injectServiceCodeBuilder.build()); 250 | 251 | return injectMethodBuilder.build(); 252 | } 253 | 254 | 255 | private MethodSpec geneSaveInstanceMethod(TypeElement targetElement, List fieldElements) { 256 | 257 | MethodSpec.Builder saveInstanceMethodBuilder = methodBuilder("onSaveInstanceState") 258 | .addModifiers(PUBLIC) 259 | .addException(Throwable.class) 260 | .addAnnotation(AnnotationSpec 261 | .builder(After.class) 262 | .addMember("value", "$S", "execution(* " + targetElement.getQualifiedName() + ".onSaveInstanceState(..))") 263 | .build()) 264 | .addParameter(JoinPoint.class, "joinPoint"); 265 | 266 | CodeBlock.Builder saveInstanceCodeBuilder = geneSaveInstanceCodeBuilder(targetElement); 267 | 268 | List queryKeys = new ArrayList<>(); 269 | 270 | for (Element queryElement : fieldElements) { 271 | if (queryElement.getAnnotation(Query.class) != null) { 272 | 273 | String queryKey = queryElement.getAnnotation(Query.class).value(); 274 | if (queryKeys.contains(queryKey)) { 275 | logger.error("The inject query key cannot be Duplicate" + Query.class.getCanonicalName(), queryElement); 276 | } 277 | queryKeys.add(queryKey); 278 | saveInstanceCodeBuilder.add("intent.putExtra($S,target.$L);\n", queryKey, queryElement); 279 | } 280 | } 281 | 282 | saveInstanceCodeBuilder.add("saveBundle.putAll(intent.getExtras());\n"); 283 | 284 | saveInstanceMethodBuilder.addCode(saveInstanceCodeBuilder.build()); 285 | 286 | return saveInstanceMethodBuilder.build(); 287 | } 288 | 289 | private CodeBlock.Builder geneSaveInstanceCodeBuilder(TypeElement targetElement) { 290 | CodeBlock.Builder blockBuilderSave = CodeBlock.builder(); 291 | blockBuilderSave.add("$T target = ($T)joinPoint.getTarget();\n", targetElement, targetElement); 292 | blockBuilderSave.add("$T saveBundle = ($T)joinPoint.getArgs()[0];\n", BUNDLE, BUNDLE); 293 | blockBuilderSave.add("$T intent = new $T();\n", INTENT, INTENT); 294 | return blockBuilderSave; 295 | } 296 | 297 | private CodeBlock.Builder geneOnCreateCodeBuilder(TypeElement targetElement) { 298 | CodeBlock.Builder injectQueryCodeBuilder = CodeBlock.builder(); 299 | injectQueryCodeBuilder.add("$T target = ($T)joinPoint.getTarget();\n", targetElement, targetElement); 300 | injectQueryCodeBuilder.add("$T dataBundle = new $T();\n", BUNDLE, BUNDLE); 301 | injectQueryCodeBuilder.add("$T saveBundle = ($T)joinPoint.getArgs()[0];\n", BUNDLE, BUNDLE); 302 | injectQueryCodeBuilder.add("$T targetBundle = $T.getSupportBundle(target);\n", BUNDLE, BUNDLE_COMPACT); 303 | injectQueryCodeBuilder.beginControlFlow("if(targetBundle != null)"); 304 | injectQueryCodeBuilder.add("dataBundle.putAll(targetBundle);\n"); 305 | injectQueryCodeBuilder.endControlFlow(); 306 | injectQueryCodeBuilder.beginControlFlow("if(saveBundle != null)"); 307 | injectQueryCodeBuilder.add("dataBundle.putAll(saveBundle);\n"); 308 | injectQueryCodeBuilder.endControlFlow(); 309 | return injectQueryCodeBuilder; 310 | } 311 | 312 | private CodeBlock.Builder geneOnNewIntentCodeBuilder(TypeElement targetElement) { 313 | CodeBlock.Builder injectQueryCodeBuilder = CodeBlock.builder(); 314 | injectQueryCodeBuilder.add("$T target = ($T)joinPoint.getTarget();\n", targetElement, targetElement); 315 | injectQueryCodeBuilder.add("$T targetIntent = ($T)joinPoint.getArgs()[0];\n", INTENT, INTENT); 316 | injectQueryCodeBuilder.add("$T dataBundle = targetIntent.getExtras();\n", BUNDLE); 317 | 318 | return injectQueryCodeBuilder; 319 | } 320 | 321 | 322 | private List generateQueryElements(RoundEnvironment roundEnv) { 323 | Set deepLinkPathElements = roundEnv.getElementsAnnotatedWith(Query.class); 324 | List queryElements = new ArrayList<>(); 325 | for (Element element : deepLinkPathElements) { 326 | Query deepLinkPathAnnotation = element.getAnnotation(Query.class); 327 | ElementKind kind = element.getKind(); 328 | if (kind != ElementKind.PARAMETER && kind != ElementKind.FIELD) { 329 | logger.error("Only classes and methods can be annotated with @" + Query.class.getCanonicalName(), element); 330 | } 331 | String queryKey = deepLinkPathAnnotation.value(); 332 | if (queryKey == null || queryKey.length() == 0) { 333 | logger.error("The inject query cannot be null @" + Query.class.getCanonicalName(), element); 334 | } 335 | 336 | if (kind == ElementKind.FIELD) { 337 | Element enclosingElement = element.getEnclosingElement(); 338 | if (enclosingElement.getKind() != CLASS) { 339 | logger.error("@" + Query.class.getCanonicalName() + "only be contained in classes", element); 340 | } 341 | TypeElement typeElement = (TypeElement) enclosingElement; 342 | boolean support = isSupportInject(typeElement); 343 | if (!support) { 344 | logger.error("@" + Query.class.getCanonicalName() + "only support inject in activity or fragment", element); 345 | } 346 | if (element.getModifiers().contains(Modifier.PRIVATE)) { 347 | logger.error("The inject query fields can not be private, please check field @" + Query.class.getCanonicalName() + "in class" + typeElement.getQualifiedName(), element); 348 | } 349 | queryElements.add(element); 350 | } 351 | } 352 | return queryElements; 353 | } 354 | 355 | 356 | private List generateServiceElements(RoundEnvironment roundEnv) { 357 | Set deepLinkServiceElements = roundEnv.getElementsAnnotatedWith(Service.class); 358 | List serviceElements = new ArrayList<>(); 359 | for (Element element : deepLinkServiceElements) { 360 | serviceElements.add(element); 361 | } 362 | return serviceElements; 363 | } 364 | 365 | private Map> findInjectElements(List queryInjectElements) { 366 | 367 | Map> map = new HashMap<>(); 368 | 369 | for (Element queryInjectElement : queryInjectElements) { 370 | TypeElement enclosingElement = (TypeElement) queryInjectElement.getEnclosingElement(); 371 | List builder = map.get(enclosingElement); 372 | if (builder == null) { 373 | builder = new ArrayList<>(); 374 | map.put(enclosingElement, builder); 375 | } 376 | builder.add(queryInjectElement); 377 | } 378 | return map; 379 | } 380 | 381 | 382 | private boolean isSupportInject(TypeElement typeElement) { 383 | for (String supportClass : SUPPORT_INJECT) { 384 | TypeMirror typeMirror = elements.getTypeElement(supportClass).asType(); 385 | if (types.isSubtype(typeElement.asType(), typeMirror)) { 386 | return true; 387 | } 388 | } 389 | return false; 390 | } 391 | 392 | 393 | } 394 | -------------------------------------------------------------------------------- /okdeeplink-processor/src/main/java/okdeeplink/DeepLinkInterceptorProcessor.java: -------------------------------------------------------------------------------- 1 | 2 | package okdeeplink; 3 | 4 | import com.google.auto.service.AutoService; 5 | import com.squareup.javapoet.AnnotationSpec; 6 | import com.squareup.javapoet.ClassName; 7 | import com.squareup.javapoet.CodeBlock; 8 | import com.squareup.javapoet.JavaFile; 9 | import com.squareup.javapoet.MethodSpec; 10 | import com.squareup.javapoet.ParameterizedTypeName; 11 | import com.squareup.javapoet.TypeName; 12 | import com.squareup.javapoet.TypeSpec; 13 | 14 | import org.aspectj.lang.ProceedingJoinPoint; 15 | import org.aspectj.lang.annotation.Around; 16 | import org.aspectj.lang.annotation.Aspect; 17 | 18 | import java.io.IOException; 19 | import java.util.ArrayList; 20 | import java.util.HashSet; 21 | import java.util.List; 22 | import java.util.Map; 23 | import java.util.Set; 24 | 25 | import javax.annotation.processing.AbstractProcessor; 26 | import javax.annotation.processing.Filer; 27 | import javax.annotation.processing.ProcessingEnvironment; 28 | import javax.annotation.processing.Processor; 29 | import javax.annotation.processing.RoundEnvironment; 30 | import javax.lang.model.SourceVersion; 31 | import javax.lang.model.element.Element; 32 | import javax.lang.model.element.Modifier; 33 | import javax.lang.model.element.TypeElement; 34 | 35 | import okdeeplink.element.InterceptorElement; 36 | import okdeeplink.util.ElementUtils; 37 | import okdeeplink.util.Logger; 38 | 39 | import static javax.lang.model.element.ElementKind.CLASS; 40 | import static okdeeplink.DeepLinkServiceProcessor.DEEP_LINK_CLIENT; 41 | import static okdeeplink.DeepLinkServiceProcessor.PACKAGE_NAME; 42 | 43 | 44 | @AutoService(Processor.class) 45 | public class DeepLinkInterceptorProcessor extends AbstractProcessor { 46 | 47 | public static final String GLOBAL_INTERCEPTORS_METHOD_NAME = ElementUtils.getName(DEEP_LINK_CLIENT) + ".getGlobalInterceptors(..)"; 48 | 49 | public static final String PATH_INTERCEPTORS_METHOD_NAME = ElementUtils.getName(DEEP_LINK_CLIENT) + ".getPathInterceptors(..)"; 50 | 51 | public static final ClassName INTERCEPTOR = ClassName.get(PACKAGE_NAME, "Interceptor"); 52 | 53 | 54 | private Filer filer; 55 | private Logger logger; 56 | 57 | @Override 58 | public synchronized void init(ProcessingEnvironment processingEnv) { 59 | super.init(processingEnv); 60 | logger = new Logger(processingEnv.getMessager()); 61 | filer = processingEnv.getFiler(); 62 | } 63 | 64 | @Override 65 | public Set getSupportedAnnotationTypes() { 66 | Set ret = new HashSet<>(); 67 | ret.add(Intercept.class.getCanonicalName()); 68 | return ret; 69 | } 70 | 71 | @Override 72 | public SourceVersion getSupportedSourceVersion() { 73 | return SourceVersion.latestSupported(); 74 | } 75 | 76 | @Override 77 | public boolean process(Set annotations, RoundEnvironment roundEnv) { 78 | 79 | List interceptorElements = generateInterceptorElements(roundEnv); 80 | if (ElementUtils.isEmpty(interceptorElements)) { 81 | return false; 82 | } 83 | 84 | for (InterceptorElement interceptorElement : interceptorElements) { 85 | String path = interceptorElement.getPath(); 86 | MethodSpec.Builder methodBuilder; 87 | if (path == null || path.length() == 0) { 88 | methodBuilder = geneGlobalInterceptorsBuilder(interceptorElement); 89 | }else { 90 | methodBuilder = genePathInterceptorsBuilder(interceptorElement); 91 | } 92 | 93 | try { 94 | TypeSpec.Builder interceptorInitBuilder = TypeSpec.classBuilder(interceptorElement.getInitClassName()) 95 | .addModifiers(Modifier.PUBLIC, Modifier.FINAL) 96 | .addAnnotation(AnnotationSpec 97 | .builder(Aspect.class) 98 | .build()) 99 | .addMethod(methodBuilder.build()); 100 | JavaFile.builder(interceptorElement.getPackageName(), interceptorInitBuilder.build()) 101 | .build() 102 | .writeTo(filer); 103 | } catch (IOException e) { 104 | logger.error("Error creating matcher file", interceptorElement.getElement()); 105 | } 106 | } 107 | 108 | 109 | return true; 110 | } 111 | 112 | 113 | private MethodSpec.Builder genePathInterceptorsBuilder(InterceptorElement interceptorElement) { 114 | CodeBlock.Builder builder = CodeBlock.builder(); 115 | TypeName typeName = ParameterizedTypeName.get(ClassName.get(Map.class), ClassName.get(String.class), INTERCEPTOR); 116 | builder.add("$T result = ($T)joinPoint.proceed();\n", typeName, typeName); 117 | String path = interceptorElement.getPath(); 118 | builder.add("result.put($S,new $T());\n", 119 | path, 120 | interceptorElement.getClassName()); 121 | builder.add("return result;\n"); 122 | return MethodSpec.methodBuilder("getPathInterceptors") 123 | .addModifiers(Modifier.PUBLIC) 124 | .addParameter(ProceedingJoinPoint.class, "joinPoint") 125 | .returns(typeName) 126 | .addException(Throwable.class) 127 | .addAnnotation(AnnotationSpec 128 | .builder(Around.class) 129 | .addMember("value", "$S", "execution(* " + PATH_INTERCEPTORS_METHOD_NAME + ")").build()) 130 | .addCode(builder.build()); 131 | } 132 | 133 | 134 | private MethodSpec.Builder geneGlobalInterceptorsBuilder(InterceptorElement interceptorElement) { 135 | CodeBlock.Builder builder = CodeBlock.builder(); 136 | TypeName typeName = ParameterizedTypeName.get(ClassName.get(List.class), INTERCEPTOR); 137 | builder.add("$T result = ($T)joinPoint.proceed();\n", typeName, typeName); 138 | builder.add("result.add(new $T());\n", 139 | interceptorElement.getClassName()); 140 | builder.add("return result;\n"); 141 | return MethodSpec.methodBuilder("getGlobalInterceptors") 142 | .addModifiers(Modifier.PUBLIC) 143 | .addParameter(ProceedingJoinPoint.class, "joinPoint") 144 | .returns(typeName) 145 | .addException(Throwable.class) 146 | .addAnnotation(AnnotationSpec 147 | .builder(Around.class) 148 | .addMember("value", "$S", "execution(* " + GLOBAL_INTERCEPTORS_METHOD_NAME + ")").build()) 149 | .addCode(builder.build()); 150 | } 151 | 152 | 153 | private List generateInterceptorElements(RoundEnvironment roundEnv) { 154 | Set interceptorElements = roundEnv.getElementsAnnotatedWith(Intercept.class); 155 | List serviceElements = new ArrayList<>(); 156 | for (Element element : interceptorElements) { 157 | if (element.getKind() == CLASS) { 158 | InterceptorElement serviceElement = new InterceptorElement(element); 159 | String name = serviceElement.getClassName().simpleName(); 160 | if (!name.endsWith("Interceptor")) { 161 | logger.error(name + "must be end with Interceptor", element); 162 | } 163 | serviceElements.add(serviceElement); 164 | } 165 | } 166 | return serviceElements; 167 | } 168 | 169 | 170 | } 171 | -------------------------------------------------------------------------------- /okdeeplink-processor/src/main/java/okdeeplink/DeepLinkServiceProcessor.java: -------------------------------------------------------------------------------- 1 | 2 | package okdeeplink; 3 | 4 | import com.google.auto.service.AutoService; 5 | import com.squareup.javapoet.AnnotationSpec; 6 | import com.squareup.javapoet.ClassName; 7 | import com.squareup.javapoet.CodeBlock; 8 | import com.squareup.javapoet.FieldSpec; 9 | import com.squareup.javapoet.JavaFile; 10 | import com.squareup.javapoet.MethodSpec; 11 | import com.squareup.javapoet.ParameterizedTypeName; 12 | import com.squareup.javapoet.TypeName; 13 | import com.squareup.javapoet.TypeSpec; 14 | 15 | import org.aspectj.lang.ProceedingJoinPoint; 16 | import org.aspectj.lang.annotation.After; 17 | import org.aspectj.lang.annotation.Around; 18 | import org.aspectj.lang.annotation.Aspect; 19 | 20 | import java.io.IOException; 21 | import java.util.ArrayList; 22 | import java.util.HashMap; 23 | import java.util.HashSet; 24 | import java.util.List; 25 | import java.util.Map; 26 | import java.util.Set; 27 | 28 | import javax.annotation.processing.AbstractProcessor; 29 | import javax.annotation.processing.Filer; 30 | import javax.annotation.processing.ProcessingEnvironment; 31 | import javax.annotation.processing.Processor; 32 | import javax.annotation.processing.RoundEnvironment; 33 | import javax.lang.model.SourceVersion; 34 | import javax.lang.model.element.Element; 35 | import javax.lang.model.element.ElementKind; 36 | import javax.lang.model.element.ExecutableElement; 37 | import javax.lang.model.element.Modifier; 38 | import javax.lang.model.element.TypeElement; 39 | import javax.lang.model.element.VariableElement; 40 | import javax.lang.model.type.TypeMirror; 41 | import javax.lang.model.util.Elements; 42 | import javax.lang.model.util.Types; 43 | 44 | import okdeeplink.element.AddressElement; 45 | import okdeeplink.util.ElementUtils; 46 | import okdeeplink.util.Logger; 47 | import okdeeplink.util.MethodGenerator; 48 | 49 | import static okdeeplink.DeepLinkInterceptorProcessor.INTERCEPTOR; 50 | 51 | 52 | @AutoService(Processor.class) 53 | public class DeepLinkServiceProcessor extends AbstractProcessor { 54 | 55 | private static final String INTERNAL_HOST = "app://deeplink"; 56 | 57 | private static final ClassName INTENT = ClassName.get("android.content", "Intent"); 58 | private static final ClassName URI = ClassName.get("android.net", "Uri"); 59 | 60 | 61 | public static final String PACKAGE_NAME = DeepLinkServiceProcessor.class.getPackage().getName(); 62 | 63 | private static final ClassName DEEP_LINK_ENTRY = ClassName.get(PACKAGE_NAME, "Address"); 64 | 65 | public static final ClassName DEEP_LINK_CLIENT = ClassName.get(PACKAGE_NAME, "DeepLinkClient"); 66 | 67 | public static final String INIT_METHOD_NAME = ElementUtils.getName(DEEP_LINK_CLIENT) + ".init(..)"; 68 | 69 | 70 | public static final String BUILD_METHOD_NAME = ElementUtils.getName(DEEP_LINK_CLIENT) + ".build(..)"; 71 | 72 | public static final String BUILD_REQUEST_METHOD_NAME = "buildRequest"; 73 | 74 | private static final String PROVIDER_SUFFIX = "$$Provider"; 75 | 76 | 77 | private static final ClassName DEEP_LINK_REQUEST = ClassName.get(PACKAGE_NAME, "Request"); 78 | 79 | private static final TypeName DEEP_LINK_OBSERVABLE = ParameterizedTypeName.get(ClassName.get("rx", "Observable"), ClassName.get(PACKAGE_NAME, "Response")); 80 | 81 | 82 | private static final List SUPPORT_RETURN_TYPE = new ArrayList<>(); 83 | 84 | private static final String ACTIVITY = "android.app.Activity"; 85 | 86 | 87 | static { 88 | SUPPORT_RETURN_TYPE.add(DEEP_LINK_REQUEST); 89 | SUPPORT_RETURN_TYPE.add(TypeName.VOID); 90 | SUPPORT_RETURN_TYPE.add(DEEP_LINK_OBSERVABLE); 91 | } 92 | 93 | 94 | private List addresses = new ArrayList<>(); 95 | 96 | private Filer filer; 97 | private Logger logger; 98 | private Types types; 99 | private Elements elements; 100 | 101 | @Override 102 | public synchronized void init(ProcessingEnvironment processingEnv) { 103 | super.init(processingEnv); 104 | logger = new Logger(processingEnv.getMessager()); 105 | filer = processingEnv.getFiler(); 106 | types = processingEnv.getTypeUtils(); 107 | elements = processingEnv.getElementUtils(); 108 | } 109 | 110 | @Override 111 | public Set getSupportedAnnotationTypes() { 112 | Set ret = new HashSet<>(); 113 | ret.add(Path.class.getCanonicalName()); 114 | ret.add(Action.class.getCanonicalName()); 115 | ret.add(Activity.class.getCanonicalName()); 116 | ret.add(Uri.class.getCanonicalName()); 117 | return ret; 118 | } 119 | 120 | @Override 121 | public SourceVersion getSupportedSourceVersion() { 122 | return SourceVersion.latestSupported(); 123 | } 124 | 125 | @Override 126 | public boolean process(Set annotations, RoundEnvironment roundEnv) { 127 | 128 | 129 | List addressElements = generateAddressElements(roundEnv); 130 | 131 | if (ElementUtils.isEmpty(addressElements)) { 132 | return false; 133 | } 134 | Map> serviceElements = findServiceElements(addressElements); 135 | if (ElementUtils.isEmpty(serviceElements)) { 136 | return false; 137 | } 138 | for (Map.Entry> serviceElementEntrySet : serviceElements.entrySet()) { 139 | TypeElement serviceElement = serviceElementEntrySet.getKey(); 140 | List serviceAddressElements = serviceElementEntrySet.getValue(); 141 | if (ElementUtils.isEmpty(serviceAddressElements)) { 142 | break; 143 | } 144 | try { 145 | generateDeepLinkService(serviceElement, serviceAddressElements); 146 | } catch (IOException e) { 147 | logger.error("Error creating matcher file", serviceElement); 148 | } 149 | } 150 | 151 | return true; 152 | } 153 | 154 | 155 | private List generateAddressElements(RoundEnvironment roundEnv) { 156 | List annotationElements = new ArrayList<>(); 157 | for (String annotationType : getSupportedAnnotationTypes()) { 158 | TypeElement typeElement = processingEnv.getElementUtils().getTypeElement(annotationType); 159 | Set annotatedElements = roundEnv.getElementsAnnotatedWith(typeElement); 160 | for (Element annotatedElement : annotatedElements) { 161 | if (!annotationElements.contains(annotatedElement)) { 162 | annotationElements.add(annotatedElement); 163 | } 164 | } 165 | } 166 | 167 | List serviceElements = new ArrayList<>(); 168 | for (Element element : annotationElements) { 169 | ElementKind kind = element.getKind(); 170 | if (kind != ElementKind.METHOD) { 171 | logger.error("Only classes and methods can be with " + getSupportedAnnotationTypes(), element); 172 | } 173 | if (!isSupportReturnType((ExecutableElement) element)) { 174 | logger.error("method only support return type is " + SUPPORT_RETURN_TYPE.toString(), element); 175 | } 176 | 177 | Element enclosingElement = element.getEnclosingElement(); 178 | String name = enclosingElement.getSimpleName().toString(); 179 | if (!name.endsWith("Service")) { 180 | logger.error(name + "this class must be in end with Service", enclosingElement); 181 | } 182 | 183 | if (enclosingElement.getKind() != ElementKind.INTERFACE) { 184 | 185 | logger.error(name + "this class must be interface", enclosingElement); 186 | } 187 | serviceElements.add(new AddressElement(element)); 188 | } 189 | return serviceElements; 190 | } 191 | 192 | 193 | private Map> findServiceElements(List queryInjectElements) { 194 | 195 | Map> map = new HashMap<>(); 196 | 197 | for (AddressElement queryInjectElement : queryInjectElements) { 198 | TypeElement enclosingElement = (TypeElement) queryInjectElement.getElement().getEnclosingElement(); 199 | List builder = map.get(enclosingElement); 200 | if (builder == null) { 201 | builder = new ArrayList<>(); 202 | map.put(enclosingElement, builder); 203 | } 204 | builder.add(queryInjectElement); 205 | } 206 | return map; 207 | } 208 | 209 | 210 | private void generateDeepLinkService(TypeElement serviceElements, 211 | List deepLinkMatchElements) 212 | throws IOException { 213 | 214 | ClassName providerClassName = getServiceProviderClassName(serviceElements); 215 | 216 | MethodSpec initMethod = generateInitMethod(deepLinkMatchElements); 217 | 218 | 219 | FieldSpec activity = FieldSpec 220 | .builder(DEEP_LINK_CLIENT, "deepLinkClient", 221 | Modifier.PUBLIC) 222 | .build(); 223 | 224 | 225 | MethodSpec activityConstructor = MethodSpec.constructorBuilder() 226 | .addModifiers(Modifier.PUBLIC) 227 | .addParameter(DEEP_LINK_CLIENT, "deepLinkClient") 228 | .addCode("this.deepLinkClient= deepLinkClient;\n") 229 | .build(); 230 | TypeSpec.Builder serviceProviderBuilder = TypeSpec.classBuilder(providerClassName) 231 | .addModifiers(Modifier.PUBLIC, Modifier.FINAL) 232 | .addAnnotation(AnnotationSpec.builder(Aspect.class).build()) 233 | .addSuperinterface(ClassName.get(serviceElements)) 234 | .addField(activity) 235 | .addMethod(activityConstructor) 236 | .addMethod(initMethod); 237 | 238 | 239 | for (AddressElement matchElement : deepLinkMatchElements) { 240 | ExecutableElement element = (ExecutableElement) matchElement.getElement(); 241 | serviceProviderBuilder.addMethod(geneOverServiceMethod(element)); 242 | } 243 | 244 | MethodSpec buildMethodSpec = generateBuildMethod(serviceElements); 245 | serviceProviderBuilder.addMethod(buildMethodSpec); 246 | JavaFile.builder(ClassName.get(serviceElements).packageName(), serviceProviderBuilder.build()) 247 | .build() 248 | .writeTo(filer); 249 | } 250 | 251 | 252 | private MethodSpec generateInitMethod(List addressElements) { 253 | 254 | CodeBlock.Builder initializer = CodeBlock.builder(); 255 | for (AddressElement element : addressElements) { 256 | List deepLinkPaths = element.getDeepLinkPaths(); 257 | if (!element.isPathsEmpty()) { 258 | for (String deepLinkPath : deepLinkPaths) { 259 | TypeMirror activityTypeMirror = element.getActivityTypeMirror(); 260 | TypeMirror supportTypeMirror = elements.getTypeElement(ACTIVITY).asType(); 261 | if (activityTypeMirror != null) { 262 | if (!types.isSubtype(activityTypeMirror, supportTypeMirror)) { 263 | logger.error(Activity.class.getName() + " only support class which extends " + ACTIVITY, element.getElement()); 264 | } 265 | } 266 | ClassName activityClassName = element.getActivityClassName(); 267 | if (activityClassName != null) { 268 | initializer.add("$T.addAddress(new $T($S, $T.class));\n", 269 | DEEP_LINK_CLIENT, DEEP_LINK_ENTRY, deepLinkPath, activityClassName); 270 | } 271 | } 272 | } 273 | 274 | } 275 | MethodSpec.Builder initMethod = MethodSpec.methodBuilder("init") 276 | .addModifiers(Modifier.PUBLIC) 277 | .addAnnotation(AnnotationSpec.builder(After.class).addMember("value", "$S", "execution(* " + INIT_METHOD_NAME + ")").build()) 278 | .addCode(initializer.build()); 279 | 280 | return initMethod.build(); 281 | } 282 | 283 | 284 | private MethodSpec geneOverServiceMethod(ExecutableElement element) { 285 | 286 | TypeName returnType = ClassName.get(element.getReturnType()); 287 | 288 | MethodSpec.Builder serviceMethodBuilder = new MethodGenerator(element) 289 | .overMethod(element.getSimpleName().toString()); 290 | CodeBlock.Builder serviceStartMethodBuilder = CodeBlock 291 | .builder() 292 | .add("$T intent = new $T();\n", INTENT, INTENT); 293 | 294 | 295 | List params = element.getParameters(); 296 | 297 | String requestCode = null; 298 | 299 | Map uriReplaces = new HashMap<>(); 300 | 301 | if (params != null) { 302 | for (int i = 0; i < params.size(); i++) { 303 | VariableElement elem = params.get(i); 304 | Query query = elem.getAnnotation(Query.class); 305 | RequestCode requestCodeAnnotation = elem.getAnnotation(RequestCode.class); 306 | UriReplace uriReplaceAnnotation = elem.getAnnotation(UriReplace.class); 307 | if (uriReplaceAnnotation != null) { 308 | 309 | if (!ClassName.get(elem.asType()).equals(ClassName.get(String.class))) { 310 | logger.error(UriReplace.class + "must be annotation " + String.class, elem); 311 | } 312 | uriReplaces.put(uriReplaceAnnotation.value(), elem); 313 | } 314 | if (query != null) { 315 | serviceStartMethodBuilder.add("intent.putExtra($S,$L);\n", query.value(), elem); 316 | } else if (requestCodeAnnotation != null) { 317 | if (requestCode != null) { 318 | logger.error("Method Must have only one @" + RequestCode.class, elem); 319 | } 320 | TypeName requestCodeTypeName = TypeName.get(elem.asType()); 321 | if (TypeName.INT.equals(requestCodeTypeName)) { 322 | requestCode = elem.getSimpleName().toString(); 323 | } else { 324 | logger.error("@" + RequestCode.class + " must be annotation int variable", elem); 325 | } 326 | } 327 | } 328 | } 329 | 330 | AddressElement addressElement = new AddressElement(element); 331 | 332 | List pathList = addressElement.getDeepLinkPaths(); 333 | List interceptorList = addressElement.getInterceptors(); 334 | 335 | if (!addressElement.isPathsEmpty()) { 336 | if (!addressElement.isUriEmpty()) { 337 | logger.error("path or uri not use in same time ", element); 338 | } 339 | for (String path : pathList) { 340 | if (!path.startsWith("/")) { 341 | if (addressElement.getActivityClassName() == null) { 342 | logger.error("if you what start activity may be use " + Uri.class + " replace " + Path.class, element); 343 | } 344 | logger.error("path must be start with / ", element); 345 | } 346 | if (addresses.contains(path)) { 347 | logger.error("Duplicate path: " + path, element); 348 | } 349 | } 350 | addresses.addAll(pathList); 351 | String path = pathList.get(0); 352 | String url = path.startsWith("/") ? INTERNAL_HOST + path : path; 353 | serviceStartMethodBuilder.add(String.format("intent.setData($T.parse(\"%s\"));\n", url), URI); 354 | } 355 | if (!addressElement.isUriEmpty()) { 356 | if (addressElement.getActivityClassName() != null) { 357 | logger.error("uri not need activity annotation", element); 358 | } 359 | serviceStartMethodBuilder.add("$T uri = $S;\n", String.class, addressElement.getUri()); 360 | for (String uriReplace : uriReplaces.keySet()) { 361 | serviceStartMethodBuilder.add("uri = uri.replace($S,$L);\n", "{" + uriReplace + "}", uriReplaces.get(uriReplace)); 362 | } 363 | serviceStartMethodBuilder.add("intent.setData($T.parse(uri));\n", URI); 364 | } 365 | 366 | 367 | if (!addressElement.isActionEmpty()) { 368 | serviceStartMethodBuilder.add("intent.setAction($S);\n", addressElement.getIntentAction()); 369 | } 370 | serviceStartMethodBuilder.add("$T request = deepLinkClient.$L(intent);\n", DEEP_LINK_REQUEST, BUILD_REQUEST_METHOD_NAME); 371 | serviceStartMethodBuilder.beginControlFlow("if (request != null)"); 372 | if (requestCode != null) { 373 | serviceStartMethodBuilder.add("request.setRequestCode($L);\n", requestCode); 374 | } 375 | if (!ElementUtils.isEmpty(interceptorList)) { 376 | for (TypeMirror interceptorTypeMirror : interceptorList) { 377 | TypeMirror supportTypeMirror = elements.getTypeElement(ElementUtils.getName(INTERCEPTOR)).asType(); 378 | TypeName typeName = ElementUtils.getClassName(interceptorTypeMirror); 379 | if (!types.isSubtype(interceptorTypeMirror, supportTypeMirror) || typeName.equals(TypeName.OBJECT)) { 380 | logger.error(Intercept.class.getName() + " only support class which extends " + INTERCEPTOR, element); 381 | } 382 | serviceStartMethodBuilder.add("request.addInterceptor(new $T());\n", ElementUtils.getClassName(interceptorTypeMirror)); 383 | } 384 | } 385 | if (DEEP_LINK_REQUEST.equals(returnType)) { 386 | serviceStartMethodBuilder.endControlFlow(); 387 | serviceStartMethodBuilder.add("return request;\n"); 388 | 389 | 390 | } else if (TypeName.VOID.equals(returnType)) { 391 | serviceStartMethodBuilder.add("request.start();\n"); 392 | serviceStartMethodBuilder.endControlFlow(); 393 | 394 | } else if (DEEP_LINK_OBSERVABLE.equals(returnType)) { 395 | 396 | if (requestCode != null) { 397 | logger.error(returnType + " not need " + RequestCode.class, element); 398 | } 399 | serviceStartMethodBuilder.endControlFlow(); 400 | serviceStartMethodBuilder.add("return request.createResponse();\n"); 401 | } 402 | 403 | 404 | serviceMethodBuilder.addCode(serviceStartMethodBuilder.build()); 405 | return serviceMethodBuilder.build(); 406 | } 407 | 408 | private ClassName getServiceProviderClassName(TypeElement serviceElements) { 409 | String className = serviceElements.getSimpleName().toString(); 410 | String packageName = ClassName.get(serviceElements).packageName(); 411 | String serviceProviderName = className + PROVIDER_SUFFIX; 412 | return ClassName.get(packageName, serviceProviderName); 413 | } 414 | 415 | 416 | private MethodSpec generateBuildMethod(TypeElement deepLinkServiceElement) { 417 | 418 | CodeBlock.Builder codeBlockBuilder = CodeBlock.builder(); 419 | codeBlockBuilder.add("$T target = ($T)joinPoint.getTarget();\n", DEEP_LINK_CLIENT, DEEP_LINK_CLIENT); 420 | codeBlockBuilder.beginControlFlow("if (joinPoint.getArgs() == null || joinPoint.getArgs().length != 1)"); 421 | codeBlockBuilder.add("return joinPoint.proceed();\n"); 422 | codeBlockBuilder.endControlFlow(); 423 | codeBlockBuilder.add("$T arg = joinPoint.getArgs()[0];\n", Object.class); 424 | codeBlockBuilder.beginControlFlow("if (arg instanceof Class)"); 425 | codeBlockBuilder.add("$T buildClass = ($T) arg;\n", Class.class, Class.class); 426 | codeBlockBuilder.beginControlFlow("if (buildClass.isAssignableFrom(getClass()))"); 427 | codeBlockBuilder.add("return new $T(target);\n", getServiceProviderClassName(deepLinkServiceElement)); 428 | codeBlockBuilder.endControlFlow(); 429 | codeBlockBuilder.endControlFlow(); 430 | codeBlockBuilder.add("return joinPoint.proceed();\n"); 431 | 432 | MethodSpec.Builder initMethod = MethodSpec.methodBuilder("aroundBuildMethod") 433 | .addModifiers(Modifier.PUBLIC) 434 | .addParameter(ProceedingJoinPoint.class, "joinPoint") 435 | .returns(Object.class) 436 | .addException(Throwable.class) 437 | .addAnnotation(AnnotationSpec.builder(Around.class).addMember("value", "$S", "execution(* " + BUILD_METHOD_NAME + ")").build()) 438 | .addCode(codeBlockBuilder.build()); 439 | 440 | return initMethod.build(); 441 | } 442 | 443 | 444 | private boolean isSupportReturnType(ExecutableElement executableElement) { 445 | if (executableElement == null) { 446 | return false; 447 | } 448 | TypeName matchReturnType = ClassName.get(executableElement.getReturnType()); 449 | for (TypeName supportClass : SUPPORT_RETURN_TYPE) { 450 | if (supportClass.equals(matchReturnType)) { 451 | return true; 452 | } 453 | } 454 | return false; 455 | } 456 | 457 | } 458 | -------------------------------------------------------------------------------- /okdeeplink-processor/src/main/java/okdeeplink/element/AddressElement.java: -------------------------------------------------------------------------------- 1 | 2 | package okdeeplink.element; 3 | 4 | import com.squareup.javapoet.ClassName; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Arrays; 8 | import java.util.List; 9 | 10 | import javax.lang.model.element.Element; 11 | import javax.lang.model.type.MirroredTypeException; 12 | import javax.lang.model.type.MirroredTypesException; 13 | import javax.lang.model.type.TypeMirror; 14 | 15 | import okdeeplink.Action; 16 | import okdeeplink.Activity; 17 | import okdeeplink.Intercept; 18 | import okdeeplink.Path; 19 | import okdeeplink.Uri; 20 | import okdeeplink.util.ElementUtils; 21 | 22 | public final class AddressElement { 23 | 24 | private TypeMirror activityTypeMirror; 25 | List deepLinkPaths = new ArrayList<>(); 26 | List interceptorClassNames = new ArrayList<>(); 27 | 28 | ClassName activityClassName; 29 | String intentAction; 30 | String uri; 31 | 32 | Element element; 33 | 34 | 35 | public AddressElement(Element element) { 36 | this.element = element; 37 | Path deepLinkPathAnnotation = element.getAnnotation(Path.class); 38 | Activity deepLinkActivityAnnotation = element.getAnnotation(Activity.class); 39 | Intercept interceptAnnotation = element.getAnnotation(Intercept.class); 40 | Action actionAnnotation = element.getAnnotation(Action.class); 41 | Uri uriAnnotation = element.getAnnotation(Uri.class); 42 | if (deepLinkPathAnnotation != null) { 43 | deepLinkPaths.addAll(Arrays.asList(deepLinkPathAnnotation.value())); 44 | } 45 | if (deepLinkActivityAnnotation != null) { 46 | try { 47 | deepLinkActivityAnnotation.value(); 48 | } catch (MirroredTypeException mte) { 49 | activityTypeMirror = mte.getTypeMirror(); 50 | activityClassName = ElementUtils.getClassName(activityTypeMirror); 51 | } 52 | } 53 | if (actionAnnotation != null) { 54 | intentAction = actionAnnotation.value(); 55 | } 56 | 57 | if (uriAnnotation != null) { 58 | uri = uriAnnotation.value(); 59 | } 60 | 61 | if (interceptAnnotation != null) { 62 | try { 63 | interceptAnnotation.value(); 64 | } catch (MirroredTypesException mte) { 65 | List typeMirrors = mte.getTypeMirrors(); 66 | if (!ElementUtils.isEmpty(typeMirrors)) { 67 | for (TypeMirror typeMirror : typeMirrors) { 68 | interceptorClassNames.add(typeMirror); 69 | } 70 | } 71 | 72 | } 73 | } 74 | 75 | } 76 | 77 | public TypeMirror getActivityTypeMirror() { 78 | return activityTypeMirror; 79 | } 80 | 81 | public List getDeepLinkPaths() { 82 | return deepLinkPaths; 83 | } 84 | 85 | public boolean isPathsEmpty() { 86 | return ElementUtils.isEmpty(getDeepLinkPaths()); 87 | } 88 | 89 | public boolean isActionEmpty() { 90 | return intentAction == null || intentAction.length() == 0; 91 | } 92 | 93 | public boolean isUriEmpty() { 94 | return uri == null || uri.length() == 0; 95 | } 96 | 97 | public List getInterceptors() { 98 | return interceptorClassNames; 99 | } 100 | 101 | public ClassName getActivityClassName() { 102 | return activityClassName; 103 | } 104 | 105 | public Element getElement() { 106 | return element; 107 | } 108 | 109 | public String getIntentAction() { 110 | return intentAction; 111 | } 112 | 113 | public String getUri() { 114 | return uri; 115 | } 116 | 117 | 118 | } 119 | -------------------------------------------------------------------------------- /okdeeplink-processor/src/main/java/okdeeplink/element/InterceptorElement.java: -------------------------------------------------------------------------------- 1 | 2 | package okdeeplink.element; 3 | 4 | import com.squareup.javapoet.ClassName; 5 | 6 | import javax.lang.model.element.Element; 7 | import javax.lang.model.element.PackageElement; 8 | 9 | import okdeeplink.Intercept; 10 | 11 | public final class InterceptorElement { 12 | 13 | 14 | private final ClassName initClassName; 15 | ClassName interceptorClassName; 16 | String packageName; 17 | Element element; 18 | String path; 19 | 20 | public InterceptorElement(Element serviceElement) { 21 | element = serviceElement; 22 | String className = serviceElement.getSimpleName().toString(); 23 | PackageElement packageElement = (PackageElement) serviceElement.getEnclosingElement(); 24 | packageName = packageElement.getQualifiedName().toString(); 25 | interceptorClassName = ClassName.get(packageName, className); 26 | String serviceProviderName = className + "$$Injector"; 27 | initClassName = ClassName.get(packageName, serviceProviderName); 28 | Intercept intercept = serviceElement.getAnnotation(Intercept.class); 29 | if (intercept != null) { 30 | path = intercept.path(); 31 | } 32 | } 33 | 34 | 35 | public ClassName getInitClassName() { 36 | return initClassName; 37 | } 38 | 39 | public String getPackageName() { 40 | return packageName; 41 | } 42 | 43 | 44 | public ClassName getClassName() { 45 | return interceptorClassName; 46 | } 47 | 48 | public Element getElement() { 49 | return element; 50 | } 51 | 52 | public String getPath() { 53 | return path; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /okdeeplink-processor/src/main/java/okdeeplink/element/ServiceElement.java: -------------------------------------------------------------------------------- 1 | 2 | package okdeeplink.element; 3 | 4 | import com.squareup.javapoet.ClassName; 5 | 6 | import java.util.List; 7 | 8 | import javax.lang.model.element.Element; 9 | import javax.lang.model.element.PackageElement; 10 | 11 | public final class ServiceElement { 12 | 13 | 14 | ClassName serviceClassName; 15 | String packageName; 16 | ClassName providerClassName; 17 | List enclosedElements; 18 | Element element; 19 | 20 | public ServiceElement(Element serviceElement) { 21 | element = serviceElement; 22 | String className = serviceElement.getSimpleName().toString(); 23 | PackageElement packageElement = (PackageElement) serviceElement.getEnclosingElement(); 24 | packageName = packageElement.getQualifiedName().toString(); 25 | serviceClassName = ClassName.get(packageName, className); 26 | enclosedElements = serviceElement.getEnclosedElements(); 27 | String serviceProviderName = className +"$$Provider"; 28 | providerClassName = ClassName.get(packageName, serviceProviderName); 29 | 30 | } 31 | 32 | 33 | public String getPackageName() { 34 | return packageName; 35 | } 36 | 37 | public ClassName getProviderClassName() { 38 | return providerClassName; 39 | } 40 | 41 | public ClassName getClassName() { 42 | return serviceClassName; 43 | } 44 | 45 | public List getEnclosedElements() { 46 | return enclosedElements; 47 | } 48 | 49 | public Element getElement() { 50 | return element; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /okdeeplink-processor/src/main/java/okdeeplink/util/ElementUtils.java: -------------------------------------------------------------------------------- 1 | package okdeeplink.util; 2 | 3 | import com.squareup.javapoet.ClassName; 4 | 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | import javax.lang.model.type.TypeMirror; 9 | 10 | /** 11 | * Created by zhangqijun on 2017/4/24. 12 | */ 13 | 14 | public class ElementUtils { 15 | 16 | 17 | public static ClassName getClassName(TypeMirror typeMirror) { 18 | String fullyQualifiedClassName = typeMirror.toString(); 19 | String simpleClassName = getSimpleName(fullyQualifiedClassName); 20 | String packageName = getPackageName(fullyQualifiedClassName); 21 | return ClassName.get(packageName, simpleClassName); 22 | } 23 | 24 | 25 | public static String getName(ClassName className) { 26 | return className.packageName()+"."+ className.simpleName(); 27 | } 28 | 29 | 30 | 31 | private static String getPackageName(String fullyQualifiedClassName) { 32 | int dotIndex = fullyQualifiedClassName.lastIndexOf("."); 33 | return fullyQualifiedClassName.substring(0, dotIndex); 34 | } 35 | 36 | 37 | private static String getSimpleName(String fullyQualifiedClassName) { 38 | int dotIndex = fullyQualifiedClassName.lastIndexOf("."); 39 | return fullyQualifiedClassName.substring(dotIndex + 1, fullyQualifiedClassName.length()); 40 | } 41 | 42 | public static boolean isEmpty(List list) { 43 | return list == null || list.size() == 0; 44 | } 45 | 46 | public static boolean isEmpty(Map map) { 47 | return map == null || map.size() == 0; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /okdeeplink-processor/src/main/java/okdeeplink/util/Logger.java: -------------------------------------------------------------------------------- 1 | package okdeeplink.util; 2 | 3 | import javax.annotation.processing.Messager; 4 | import javax.lang.model.element.Element; 5 | import javax.tools.Diagnostic; 6 | 7 | /** 8 | * Simplify the messager. 9 | * 10 | * @author Alex Contact me. 11 | * @version 1.0 12 | * @since 16/8/22 上午11:48 13 | */ 14 | public class Logger { 15 | private Messager msg; 16 | 17 | public Logger(Messager messager) { 18 | msg = messager; 19 | } 20 | 21 | 22 | public void info(CharSequence info, Element element) { 23 | msg.printMessage(Diagnostic.Kind.NOTE, info, element); 24 | } 25 | 26 | public void error(CharSequence error, Element element) { 27 | msg.printMessage(Diagnostic.Kind.ERROR, error, element); 28 | } 29 | 30 | 31 | public void error(Throwable error, Element element) { 32 | msg.printMessage(Diagnostic.Kind.ERROR, "An exception is encountered, [" + error.getMessage() + "]" + "\n" + formatStackTrace(error.getStackTrace()), element); 33 | 34 | } 35 | 36 | public void warning(CharSequence warning, Element element) { 37 | msg.printMessage(Diagnostic.Kind.WARNING, warning, element); 38 | 39 | } 40 | 41 | private String formatStackTrace(StackTraceElement[] stackTrace) { 42 | StringBuilder sb = new StringBuilder(); 43 | for (StackTraceElement element : stackTrace) { 44 | sb.append(" at ").append(element.toString()); 45 | sb.append("\n"); 46 | } 47 | return sb.toString(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /okdeeplink-processor/src/main/java/okdeeplink/util/MethodGenerator.java: -------------------------------------------------------------------------------- 1 | package okdeeplink.util; 2 | 3 | import com.squareup.javapoet.AnnotationSpec; 4 | import com.squareup.javapoet.ClassName; 5 | import com.squareup.javapoet.MethodSpec; 6 | 7 | import java.util.List; 8 | 9 | import javax.lang.model.element.ExecutableElement; 10 | import javax.lang.model.element.Modifier; 11 | import javax.lang.model.element.VariableElement; 12 | import javax.lang.model.type.TypeMirror; 13 | 14 | /** 15 | * Created by zhangqijun on 2017/4/28. 16 | */ 17 | 18 | public class MethodGenerator { 19 | 20 | private ExecutableElement executableElement; 21 | 22 | public MethodGenerator(ExecutableElement executableElement) { 23 | this.executableElement = executableElement; 24 | } 25 | 26 | public MethodSpec.Builder overMethod(String name) { 27 | 28 | 29 | MethodSpec.Builder builder = MethodSpec.methodBuilder(getValidMethodName(name)); 30 | 31 | addModifiers(builder); 32 | 33 | addAnnotation(builder); 34 | 35 | checkParameters(executableElement, builder); 36 | 37 | addReturnType(executableElement, builder); 38 | 39 | addExceptions(executableElement, builder); 40 | 41 | return builder; 42 | } 43 | 44 | 45 | 46 | MethodGenerator addModifiers(MethodSpec.Builder builder) { 47 | builder.addModifiers(Modifier.PUBLIC); 48 | return this; 49 | } 50 | 51 | void addAnnotation(MethodSpec.Builder builder) { 52 | builder.addAnnotation(AnnotationSpec.builder(Override.class).build()); 53 | } 54 | 55 | String getValidMethodName(String name) { 56 | return name.replace(' ', '_'); 57 | } 58 | 59 | void addExceptions(ExecutableElement annotatedMtd, MethodSpec.Builder builder) { 60 | List exceptions = annotatedMtd.getThrownTypes(); 61 | if (exceptions != null){ 62 | for (TypeMirror exc : exceptions) { 63 | builder.addException(ClassName.get(exc)); 64 | } 65 | } 66 | } 67 | 68 | void addReturnType(ExecutableElement annotatedMtd, MethodSpec.Builder builder) { 69 | builder.returns(ClassName.get(annotatedMtd.getReturnType())); 70 | } 71 | 72 | void checkParameters(ExecutableElement annotatedMtd, MethodSpec.Builder builder) { 73 | List params = annotatedMtd.getParameters(); 74 | if (params != null){ 75 | for (int i = 0; i < params.size(); i++) { 76 | VariableElement elem = params.get(i); 77 | builder.addParameter(ClassName.get(elem.asType()), elem.getSimpleName().toString()); 78 | } 79 | } 80 | 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /pom-evaluator.gradle: -------------------------------------------------------------------------------- 1 | apply from: "${rootDir}/bintrayUpload.gradle" 2 | afterEvaluate { 3 | install { 4 | repositories.mavenInstaller { 5 | pom.groupId = rootProject.groupId 6 | pom.version = rootProject.versionName 7 | 8 | pom.whenConfigured { pom -> 9 | pom.dependencies.findAll { dep -> dep.groupId == rootProject.name }.collect { dep -> 10 | dep.groupId = pom.groupId = rootProject.groupId 11 | dep.version = pom.version = rootProject.versionName 12 | } 13 | } 14 | } 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # OkDeepLink 2 | [![Build Status](https://travis-ci.org/HongJun2046/OkDeepLink.svg?branch=master)](https://travis-ci.org/HongJun2046/OkDeepLink) 3 | [ ![Download](https://api.bintray.com/packages/zmanchina/maven/okdeeplink-gradle/images/download.svg) ](https://bintray.com/zmanchina/maven/okdeeplink-gradle/_latestVersion) 4 | 5 | OkDeepLink provides a annotation-based api to manipulate app deep links. 6 | 7 | - register deep links with annotation `@Path`、`@Activity` 8 | - start deep links by service which generates an implementation by `Annotation-processor`, auto inject to activity with annotation `@Service` 9 | - url or bundle parameters auto inject to activity , restore when activity recreate by annotation `@Query` 10 | - async intercept deep links in ui thread with annotation `@Intercept` 11 | - support activity result with `rxJava` 12 | 13 | 14 | ### Config 15 | **In Root Gradle** 16 | 17 | ```groovy 18 | repositories { 19 | jcenter() 20 | } 21 | dependencies { 22 | classpath 'com.hongjun:okdeeplink-gradle:1.0.0' 23 | } 24 | ``` 25 | 26 | **In App Or Lib Gradle** 27 | 28 | ```groovy 29 | apply plugin: 'okdeeplink.plugin' 30 | ``` 31 | if use multiple apt lib, you must use 32 | 33 | ```groovy 34 | packagingOptions { 35 | exclude 'META-INF/services/javax.annotation.processing.Processor' 36 | } 37 | ``` 38 | 39 | ### Example 40 | 41 | If you want define `old://app/main?key=value` uri, you can do like this 42 | 43 | #### Define Host And Scheme 44 | 45 | you can define dispatch activity which receive deep links in your `AndroidManifest.xml` file (using `odl` as an example): 46 | 47 | **In App AndroidManifest** 48 | ```xml 49 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 61 | 62 | 63 | ``` 64 | In the future, I will use annotation `@AppLink` which define scheme and host instead of `AndroidManifest.xml`. I already have a simple way. 65 | 66 | **In Service File** 67 | 68 | ```java 69 | public interface SampleService { 70 | 71 | 72 | @Path("/main") 73 | @Activity(MainActivity.class) 74 | void startMainActivity(@Query("key") String key); 75 | } 76 | ``` 77 | path must start with "/" 78 | when app receive uri like `old://app/main?key=value`, `DeepLinkActivity` will start ,then dispatch the deep link to the appropriate `Activity`. 79 | 80 | 81 | **In Activity Or Fragment File** 82 | 83 | You also start MainActivity by service in app. 84 | ```java 85 | public class SecondActivity extends AppCompatActivity { 86 | 87 | @Service 88 | SampleService sampleService; 89 | @Query("key") 90 | String key; 91 | 92 | 93 | sampleService.startMainActivity("value"); 94 | } 95 | 96 | ``` 97 | 98 | ### Intercept 99 | If you want Intercept `old://app/second`, you can use `@Intercept` 100 | 101 | ```java 102 | @Intercept(path = "/second") 103 | public class SecondInterceptor extends Interceptor { 104 | @Override 105 | public void intercept(final Call call) { 106 | Request request = call.getRequest(); 107 | final Intent intent = request.getIntent(); 108 | Context context = request.getContext(); 109 | 110 | StringBuffer stringBuffer = new StringBuffer(); 111 | stringBuffer.append("Intercept\n"); 112 | stringBuffer.append("URL: " + request.getUrl() + "\n"); 113 | 114 | AlertDialog.Builder builder = new AlertDialog.Builder(context,R.style.Theme_AppCompat_Dialog_Alert); 115 | builder.setTitle("Notice"); 116 | builder.setMessage(stringBuffer); 117 | builder.setNegativeButton("cancel", new DialogInterface.OnClickListener() { 118 | @Override 119 | public void onClick(DialogInterface dialog, int which) { 120 | call.cancel(); 121 | } 122 | }); 123 | builder.setPositiveButton("ok", new DialogInterface.OnClickListener() { 124 | @Override 125 | public void onClick(DialogInterface dialog, int which) { 126 | intent.putExtra("key1", "value3"); 127 | call.proceed(); 128 | } 129 | }); 130 | builder.setOnDismissListener(new DialogInterface.OnDismissListener() { 131 | @Override 132 | public void onDismiss(DialogInterface dialog) { 133 | call.cancel(); 134 | } 135 | }); 136 | builder.show(); 137 | } 138 | } 139 | ``` 140 | 141 | ![intercept`old://app/second` ](https://raw.githubusercontent.com/HongJun2046/OkDeepLink/master/snapshot/intercept_preview.png) 142 | 143 | 144 | ### Log 145 | I define `LogInterceptor`, this can log deep links which start、notFound、error , log tag is `OkDeepLink` 146 | 147 | ### Other Way 148 | You can start intent action by service 149 | 150 | ```java 151 | public interface SampleService { 152 | 153 | @Action(MediaStore.ACTION_IMAGE_CAPTURE) 154 | Observable startImageCapture(); 155 | 156 | @Action(Intent.ACTION_DIAL) 157 | @Uri("tel:{phone}") 158 | void startTel(@UriReplace("phone") String phone); 159 | } 160 | 161 | ``` 162 | ```java 163 | public void startImageCapture(){ 164 | sampleService 165 | .startImageCapture() 166 | .subscribe(new Action1() { 167 | @Override 168 | public void call(Response response) { 169 | Intent data = response.getData(); 170 | int resultCode = response.getResultCode(); 171 | if (resultCode == RESULT_OK) { 172 | Bitmap imageBitmap = (Bitmap) data.getExtras().get("data"); 173 | ImageView imageView = (ImageView) findViewById(R.id.ImageView_Capture_Image); 174 | imageView.setImageBitmap(imageBitmap); 175 | } 176 | } 177 | }); 178 | } 179 | 180 | public void startTel(String phone){ 181 | sampleService 182 | .startTel(phone); 183 | } 184 | ``` 185 | 186 | ### Words 187 | 188 | It is so simple,then you just do it like `sample` 189 | 190 | License 191 | ------- 192 | 193 | Copyright 2017 Hongjun2046 194 | 195 | Licensed under the Apache License, Version 2.0 (the "License"); 196 | you may not use this file except in compliance with the License. 197 | You may obtain a copy of the License at 198 | 199 | http://www.apache.org/licenses/LICENSE-2.0 200 | 201 | Unless required by applicable law or agreed to in writing, software 202 | distributed under the License is distributed on an "AS IS" BASIS, 203 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 204 | See the License for the specific language governing permissions and 205 | limitations under the License. 206 | 207 | 208 | -------------------------------------------------------------------------------- /rxactivityresult/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /rxactivityresult/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.2" 6 | 7 | defaultConfig { 8 | minSdkVersion 14 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | consumerProguardFiles 'proguard-rules.pro' 13 | 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | } 22 | 23 | dependencies { 24 | compile fileTree(dir: 'libs', include: ['*.jar']) 25 | compile 'com.android.support:appcompat-v7:25.0.0' 26 | compile 'io.reactivex:rxjava:1.1.5' 27 | compile 'io.reactivex:rxandroid:1.2.1' 28 | testCompile 'junit:junit:4.12' 29 | compile 'com.android.support:support-fragment:25.0.0' 30 | } 31 | 32 | apply from: "${rootDir}/pom-evaluator.gradle" 33 | -------------------------------------------------------------------------------- /rxactivityresult/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/sunshine/Documents/AndroidStudio/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 | 27 | 28 | #--- rxjava start 29 | 30 | -keep class sun.misc.Unsafe { *; } 31 | -keepattributes SourceFile,LineNumberTable 32 | -dontwarn sun.misc.** 33 | 34 | -keep class rx.schedulers.Schedulers { 35 | public static ; 36 | } 37 | -keep class rx.schedulers.ImmediateScheduler { 38 | public ; 39 | } 40 | -keep class rx.schedulers.TestScheduler { 41 | public ; 42 | } 43 | -keep class rx.schedulers.Schedulers { 44 | public static ** test(); 45 | } 46 | -keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* { 47 | long producerIndex; 48 | long consumerIndex; 49 | } 50 | -keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef { 51 | long producerNode; 52 | long consumerNode; 53 | } 54 | -dontnote rx.internal.util.PlatformDependent 55 | 56 | #--- rxjava end -------------------------------------------------------------------------------- /rxactivityresult/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /rxactivityresult/src/main/java/rxactivityresult/ActivityResult.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Copyright 2016 Víctor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package rxactivityresult; 18 | 19 | import android.content.Intent; 20 | 21 | public class ActivityResult { 22 | private final int resultCode; 23 | private final Intent data; 24 | 25 | public ActivityResult(int resultCode, Intent data) { 26 | this.resultCode = resultCode; 27 | this.data = data; 28 | } 29 | 30 | public int getResultCode() { 31 | return resultCode; 32 | } 33 | 34 | public Intent getData() { 35 | return data; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /rxactivityresult/src/main/java/rxactivityresult/IActivityObservable.java: -------------------------------------------------------------------------------- 1 | package rxactivityresult; 2 | 3 | import android.content.Intent; 4 | 5 | import rx.Observable; 6 | 7 | /** 8 | * Created by zhangqijun on 2017/6/9. 9 | */ 10 | 11 | interface IActivityObservable { 12 | 13 | Observable getAttachedObservable(); 14 | 15 | Observable getResultObservable(); 16 | 17 | void startIntent(Intent intent); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /rxactivityresult/src/main/java/rxactivityresult/RxActivityResult.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017. nekocode (nekocode.cn@gmail.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package rxactivityresult; 18 | 19 | import android.app.Activity; 20 | import android.app.Fragment; 21 | import android.app.FragmentManager; 22 | import android.content.Context; 23 | import android.content.Intent; 24 | import android.support.v4.app.FragmentActivity; 25 | 26 | import java.lang.ref.WeakReference; 27 | 28 | import rx.Observable; 29 | import rx.functions.Func1; 30 | 31 | 32 | public final class RxActivityResult { 33 | 34 | 35 | private RxActivityResult() { 36 | } 37 | 38 | 39 | public static RxActivityResult.Builder on(T context) { 40 | return new RxActivityResult.Builder<>(context); 41 | } 42 | 43 | public static RxActivityResult.Builder on(T fragment) { 44 | return new RxActivityResult.Builder<>(fragment); 45 | } 46 | 47 | public static RxActivityResult.Builder on(T fragment) { 48 | return new RxActivityResult.Builder<>(fragment); 49 | } 50 | 51 | 52 | public static class Builder { 53 | 54 | WeakReference targetWeak; 55 | 56 | private Builder(T t) { 57 | targetWeak = new WeakReference(t); 58 | } 59 | 60 | 61 | public Observable startIntent(final Intent intent) { 62 | IActivityObservable activityObservable = buildActivityObservable(); 63 | Observable intentObservable = startActivity(activityObservable, intent); 64 | Observable.Transformer transformer = getActivityResultTransformer(); 65 | return intentObservable.compose(transformer); 66 | } 67 | 68 | 69 | 70 | private Observable startActivity(final IActivityObservable activityObservable, final Intent intent) { 71 | return activityObservable 72 | .getAttachedObservable() 73 | .filter(new Func1() { 74 | @Override 75 | public Boolean call(Boolean attach) { 76 | return attach; 77 | } 78 | }) 79 | .take(1) 80 | .map(new Func1() { 81 | @Override 82 | public IActivityObservable call(Boolean aBoolean) { 83 | activityObservable.startIntent(intent); 84 | return activityObservable; 85 | } 86 | }); 87 | } 88 | 89 | private IActivityObservable buildActivityObservable() { 90 | 91 | T target = targetWeak.get(); 92 | 93 | if (target instanceof FragmentActivity) { 94 | FragmentActivity activity = (FragmentActivity) target; 95 | android.support.v4.app.FragmentManager fragmentManager = activity.getSupportFragmentManager(); 96 | IActivityObservable activityObservable = RxResultHoldFragmentV4.getHoldFragment(fragmentManager); 97 | return activityObservable; 98 | } 99 | 100 | if (target instanceof Activity) { 101 | Activity activity = (Activity) target; 102 | FragmentManager fragmentManager = activity.getFragmentManager(); 103 | IActivityObservable activityObservable = RxResultHoldFragment.getHoldFragment(fragmentManager); 104 | return activityObservable; 105 | } 106 | if (target instanceof Context) { 107 | final Context context = (Context) target; 108 | IActivityObservable activityObservable = new RxResultHoldContext(context); 109 | return activityObservable; 110 | } 111 | 112 | if (target instanceof Fragment) { 113 | Fragment fragment = (Fragment) target; 114 | FragmentManager fragmentManager = fragment.getFragmentManager(); 115 | if (fragmentManager != null) { 116 | IActivityObservable activityObservable = RxResultHoldFragment.getHoldFragment(fragmentManager); 117 | return activityObservable; 118 | } 119 | } 120 | if (target instanceof android.support.v4.app.Fragment) { 121 | android.support.v4.app.Fragment fragment = (android.support.v4.app.Fragment) target; 122 | android.support.v4.app.FragmentManager fragmentManager = fragment.getFragmentManager(); 123 | if (fragmentManager != null) { 124 | IActivityObservable activityObservable = RxResultHoldFragmentV4.getHoldFragment(fragmentManager); 125 | return activityObservable; 126 | } 127 | } 128 | return new RxResultHoldEmpty(); 129 | } 130 | 131 | 132 | public Observable.Transformer getActivityResultTransformer() { 133 | return new Observable.Transformer() { 134 | @Override 135 | public Observable call(Observable intentObservable) { 136 | return intentObservable 137 | .flatMap(new Func1>() { 138 | @Override 139 | public Observable call(IActivityObservable activityObservable) { 140 | return activityObservable.getResultObservable(); 141 | } 142 | }) 143 | .filter(new Func1() { 144 | @Override 145 | public Boolean call(ActivityResult result) { 146 | T target = targetWeak.get(); 147 | return target != null; 148 | } 149 | }); 150 | } 151 | }; 152 | } 153 | 154 | 155 | } 156 | 157 | 158 | } 159 | -------------------------------------------------------------------------------- /rxactivityresult/src/main/java/rxactivityresult/RxResultHoldContext.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017. nekocode (nekocode.cn@gmail.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package rxactivityresult; 18 | 19 | import android.app.Activity; 20 | import android.content.Context; 21 | import android.content.Intent; 22 | 23 | import rx.Observable; 24 | import rx.subjects.BehaviorSubject; 25 | import rx.subjects.PublishSubject; 26 | 27 | 28 | /** 29 | * @author nekocode (nekocode.cn@gmail.com) 30 | */ 31 | class RxResultHoldContext implements IActivityObservable { 32 | 33 | private Context context; 34 | 35 | public final PublishSubject resultPublisher = PublishSubject.create(); 36 | public final BehaviorSubject attachedPublisher = BehaviorSubject.create(true); 37 | 38 | 39 | public RxResultHoldContext(Context context) { 40 | this.context = context; 41 | attachedPublisher.onNext(true); 42 | } 43 | 44 | public Observable getResultObservable() { 45 | return resultPublisher.asObservable(); 46 | } 47 | 48 | public Observable getAttachedObservable() { 49 | return attachedPublisher.asObservable(); 50 | } 51 | 52 | 53 | public void startIntent(Intent intent) { 54 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 55 | context.startActivity(intent); 56 | 57 | resultPublisher.onNext(new ActivityResult(Activity.RESULT_OK, null)); 58 | resultPublisher.onCompleted(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /rxactivityresult/src/main/java/rxactivityresult/RxResultHoldEmpty.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017. nekocode (nekocode.cn@gmail.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package rxactivityresult; 18 | 19 | import android.content.Intent; 20 | 21 | import rx.Observable; 22 | 23 | 24 | /** 25 | * @author nekocode (nekocode.cn@gmail.com) 26 | */ 27 | class RxResultHoldEmpty implements IActivityObservable { 28 | 29 | 30 | public Observable getResultObservable() { 31 | return Observable.empty(); 32 | } 33 | 34 | public Observable getAttachedObservable() { 35 | return Observable.empty(); 36 | } 37 | 38 | 39 | public void startIntent(Intent intent) { 40 | 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /rxactivityresult/src/main/java/rxactivityresult/RxResultHoldFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017. nekocode (nekocode.cn@gmail.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package rxactivityresult; 18 | 19 | import android.app.Activity; 20 | import android.app.Fragment; 21 | import android.app.FragmentManager; 22 | import android.app.FragmentTransaction; 23 | import android.content.Context; 24 | import android.content.Intent; 25 | import android.os.Build; 26 | import android.os.Bundle; 27 | import android.support.annotation.Nullable; 28 | 29 | import rx.Observable; 30 | import rx.subjects.PublishSubject; 31 | 32 | 33 | /** 34 | * @author nekocode (nekocode.cn@gmail.com) 35 | */ 36 | public class RxResultHoldFragment extends Fragment implements IActivityObservable { 37 | 38 | public static final String TAG = RxResultHoldFragment.class.getName(); 39 | private ActivityResult activityResult; 40 | public final PublishSubject resultPublisher = PublishSubject.create(); 41 | public final PublishSubject attachedPublisher = PublishSubject.create(); 42 | 43 | public RxResultHoldFragment() { 44 | 45 | } 46 | 47 | @Override 48 | public void onCreate(@Nullable Bundle savedInstanceState) { 49 | super.onCreate(savedInstanceState); 50 | setRetainInstance(true); 51 | } 52 | 53 | public Observable getResultObservable() { 54 | return resultPublisher.asObservable(); 55 | } 56 | 57 | public Observable getAttachedObservable() { 58 | return attachedPublisher.asObservable(); 59 | } 60 | 61 | @Override 62 | public void onAttach(Context context) { 63 | super.onAttach(context); 64 | onAttachToContext(context); 65 | } 66 | 67 | @SuppressWarnings("deprecation") 68 | @Override 69 | public void onAttach(Activity activity) { 70 | super.onAttach(activity); 71 | if (Build.VERSION.SDK_INT < 23) { 72 | onAttachToContext(activity); 73 | } 74 | } 75 | 76 | 77 | private void onAttachToContext(Context context) { 78 | attachedPublisher.onNext(true); 79 | } 80 | 81 | 82 | public void startIntent(Intent intent) { 83 | startActivityForResult(intent, 100); 84 | } 85 | 86 | 87 | @Override 88 | public void onActivityResult(int requestCode, int resultCode, Intent data) { 89 | super.onActivityResult(requestCode, resultCode, data); 90 | activityResult = new ActivityResult(resultCode, data); 91 | 92 | 93 | } 94 | 95 | @Override 96 | public void onResume() { 97 | super.onResume(); 98 | if (activityResult != null) { 99 | resultPublisher.onNext(activityResult); 100 | activityResult = null; 101 | resultPublisher.onCompleted(); 102 | FragmentTransaction transaction = getFragmentManager().beginTransaction(); 103 | transaction.remove(this); 104 | transaction.commit(); 105 | } 106 | } 107 | 108 | public static RxResultHoldFragment getHoldFragment(FragmentManager fragmentManager) { 109 | RxResultHoldFragment holdFragment = new RxResultHoldFragment(); 110 | FragmentTransaction transaction = fragmentManager.beginTransaction(); 111 | transaction.add(holdFragment, RxResultHoldFragment.TAG); 112 | transaction.commit(); 113 | return holdFragment; 114 | } 115 | 116 | 117 | @Override 118 | public void onSaveInstanceState(Bundle outState) { 119 | super.onSaveInstanceState(outState); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /rxactivityresult/src/main/java/rxactivityresult/RxResultHoldFragmentV4.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017. nekocode (nekocode.cn@gmail.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package rxactivityresult; 18 | 19 | import android.content.Context; 20 | import android.content.Intent; 21 | import android.os.Bundle; 22 | import android.support.annotation.Nullable; 23 | import android.support.v4.app.Fragment; 24 | import android.support.v4.app.FragmentManager; 25 | import android.support.v4.app.FragmentTransaction; 26 | 27 | import rx.Observable; 28 | import rx.subjects.PublishSubject; 29 | 30 | 31 | /** 32 | * @author nekocode (nekocode.cn@gmail.com) 33 | */ 34 | public class RxResultHoldFragmentV4 extends Fragment implements IActivityObservable { 35 | 36 | public static final String TAG = RxResultHoldFragmentV4.class.getName(); 37 | public final PublishSubject resultPublisher = PublishSubject.create(); 38 | public final PublishSubject attachedPublisher = PublishSubject.create(); 39 | private ActivityResult activityResult; 40 | 41 | 42 | public RxResultHoldFragmentV4() { 43 | } 44 | 45 | @Override 46 | public void onCreate(@Nullable Bundle savedInstanceState) { 47 | super.onCreate(savedInstanceState); 48 | setRetainInstance(true); 49 | } 50 | 51 | @Override 52 | public void onSaveInstanceState(Bundle outState) { 53 | super.onSaveInstanceState(outState); 54 | } 55 | 56 | public Observable getResultObservable() { 57 | return resultPublisher.asObservable(); 58 | } 59 | 60 | public Observable getAttachedObservable() { 61 | return attachedPublisher.asObservable(); 62 | } 63 | 64 | @Override 65 | public void onAttach(Context context) { 66 | super.onAttach(context); 67 | onAttachToContext(context); 68 | 69 | } 70 | 71 | 72 | private void onAttachToContext(Context context) { 73 | attachedPublisher.onNext(true); 74 | } 75 | 76 | 77 | public void startIntent(Intent intent) { 78 | startActivityForResult(intent, 100); 79 | 80 | } 81 | 82 | 83 | @Override 84 | public void onActivityResult(int requestCode, int resultCode, Intent data) { 85 | super.onActivityResult(requestCode, resultCode, data); 86 | activityResult = new ActivityResult(resultCode, data); 87 | 88 | 89 | } 90 | 91 | @Override 92 | public void onResume() { 93 | super.onResume(); 94 | if (activityResult != null) { 95 | resultPublisher.onNext(activityResult); 96 | activityResult = null; 97 | resultPublisher.onCompleted(); 98 | FragmentTransaction transaction = getFragmentManager().beginTransaction(); 99 | transaction.remove(this); 100 | transaction.commit(); 101 | } 102 | } 103 | 104 | 105 | public static RxResultHoldFragmentV4 getHoldFragment(FragmentManager fragmentManager) { 106 | RxResultHoldFragmentV4 holdFragment = new RxResultHoldFragmentV4(); 107 | FragmentTransaction transaction = fragmentManager.beginTransaction(); 108 | transaction.add(holdFragment, RxResultHoldFragmentV4.TAG); 109 | transaction.commit(); 110 | return holdFragment; 111 | } 112 | 113 | 114 | } 115 | -------------------------------------------------------------------------------- /rxactivityresult/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | -------------------------------------------------------------------------------- /sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /sample/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'android-aspectjx' 3 | 4 | 5 | android { 6 | compileSdkVersion 25 7 | buildToolsVersion "25.0.2" 8 | 9 | defaultConfig { 10 | applicationId "okdeeplink.sample" 11 | minSdkVersion 14 12 | targetSdkVersion 25 13 | versionCode 1 14 | versionName "1.0" 15 | 16 | 17 | } 18 | packagingOptions { 19 | exclude 'META-INF/services/javax.annotation.processing.Processor' 20 | } 21 | lintOptions { 22 | disable 'InvalidPackage' 23 | } 24 | 25 | 26 | 27 | // android.applicationVariants.all { variant -> 28 | // if (variant.install) { 29 | // variant.install.doLast { 30 | // description "Installs and Runs the APK for ${variant.description}." 31 | // def getMainActivity = { file -> 32 | // new XmlSlurper().parse(file).application.activity.find { 33 | // it.'intent-filter'.find { filter -> 34 | // return filter.action.find { 35 | // it.'@android:name'.text() == 'android.intent.action.MAIN' 36 | // } \ 37 | // && filter.category.find { 38 | // it.'@android:name'.text() == 'android.intent.category.LAUNCHER' 39 | // } 40 | // } 41 | // }.'@android:name' 42 | // } 43 | // exec { 44 | // def activityClass = 45 | // getMainActivity(variant.outputs.processManifest.manifestOutputFile) 46 | // commandLine android.adbExe, 'shell', 'am', 'start', '-n', 47 | // "${variant.applicationId}/${activityClass}" 48 | // 49 | // } 50 | // } 51 | // } 52 | // } 53 | } 54 | 55 | configurations.all { 56 | resolutionStrategy.cacheChangingModulesFor 0, 'seconds' 57 | } 58 | 59 | dependencies { 60 | compile project(":second") 61 | compile project(':okdeeplink-api') 62 | annotationProcessor project(':okdeeplink-processor') 63 | compile 'com.android.support:appcompat-v7:25.0.0' 64 | compile 'com.android.support:support-v4:25.0.0' 65 | } 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /sample/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/christian/Environment/android-sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 14 | 15 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /sample/src/main/java/okdeeplink/sample/MainActivity.java: -------------------------------------------------------------------------------- 1 | 2 | package okdeeplink.sample; 3 | 4 | import android.content.Intent; 5 | import android.graphics.Bitmap; 6 | import android.os.Bundle; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.view.View; 9 | import android.widget.ImageView; 10 | 11 | import okdeeplink.Query; 12 | import okdeeplink.Response; 13 | import okdeeplink.Service; 14 | import rx.functions.Action1; 15 | 16 | 17 | public class MainActivity extends AppCompatActivity { 18 | 19 | @Service 20 | SampleService sampleService; 21 | @Query("key") 22 | String key; 23 | 24 | @Override 25 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 26 | super.onActivityResult(requestCode, resultCode, data); 27 | 28 | 29 | } 30 | 31 | @Override 32 | protected void onCreate(Bundle savedInstanceState) { 33 | super.onCreate(savedInstanceState); 34 | setContentView(R.layout.activity_main); 35 | 36 | findViewById(R.id.Second).setOnClickListener(new View.OnClickListener() { 37 | @Override 38 | public void onClick(View v) { 39 | sampleService.startSecondActivity("value1",2); 40 | } 41 | }); 42 | 43 | findViewById(R.id.Capture_Image).setOnClickListener(new View.OnClickListener() { 44 | @Override 45 | public void onClick(View v) { 46 | sampleService 47 | .startImageCapture() 48 | .subscribe(new Action1() { 49 | @Override 50 | public void call(Response response) { 51 | Intent data = response.getData(); 52 | int resultCode = response.getResultCode(); 53 | if (resultCode == RESULT_OK) { 54 | Bitmap imageBitmap = (Bitmap) data.getExtras().get("data"); 55 | ImageView imageView = (ImageView) findViewById(R.id.ImageView_Capture_Image); 56 | imageView.setImageBitmap(imageBitmap); 57 | } 58 | } 59 | }); 60 | } 61 | }); 62 | 63 | findViewById(R.id.Tel_Phone).setOnClickListener(new View.OnClickListener() { 64 | @Override 65 | public void onClick(View v) { 66 | sampleService.startTel("10000"); 67 | } 68 | }); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /sample/src/main/java/okdeeplink/sample/SampleService.java: -------------------------------------------------------------------------------- 1 | package okdeeplink.sample; 2 | 3 | import android.content.Intent; 4 | import android.provider.MediaStore; 5 | 6 | import okdeeplink.Action; 7 | import okdeeplink.Activity; 8 | import okdeeplink.Path; 9 | import okdeeplink.Query; 10 | import okdeeplink.Response; 11 | import okdeeplink.Uri; 12 | import okdeeplink.UriReplace; 13 | import rx.Observable; 14 | 15 | /** 16 | * Created by zhangqijun on 2017/4/24. 17 | */ 18 | public interface SampleService { 19 | 20 | 21 | @Path("/main") 22 | @Activity(MainActivity.class) 23 | void startMainActivity(@Query("key") String key); 24 | 25 | @Path("/second") 26 | void startSecondActivity(@Query("key1") String value1, @Query("key2") int value2); 27 | 28 | 29 | @Action(MediaStore.ACTION_IMAGE_CAPTURE) 30 | Observable startImageCapture(); 31 | 32 | 33 | @Action(Intent.ACTION_DIAL) 34 | @Uri("tel:{phone}") 35 | void startTel(@UriReplace("phone") String phone); 36 | } 37 | -------------------------------------------------------------------------------- /sample/src/main/java/okdeeplink/sample/SecondInterceptor.java: -------------------------------------------------------------------------------- 1 | package okdeeplink.sample; 2 | 3 | import android.content.Context; 4 | import android.content.DialogInterface; 5 | import android.content.Intent; 6 | import android.support.v7.app.AlertDialog; 7 | 8 | import okdeeplink.Intercept; 9 | import okdeeplink.Interceptor; 10 | import okdeeplink.Request; 11 | 12 | /** 13 | * Created by zhangqijun on 2017/5/6. 14 | */ 15 | 16 | @Intercept(path = "/second") 17 | public class SecondInterceptor extends Interceptor { 18 | @Override 19 | public void intercept(final Call call) { 20 | 21 | Request request = call.getRequest(); 22 | final Intent intent = request.getIntent(); 23 | Context context = request.getContext(); 24 | 25 | StringBuffer stringBuffer = new StringBuffer(); 26 | stringBuffer.append("Intercept\n"); 27 | stringBuffer.append("URL: " + request.getUrl() + "\n"); 28 | 29 | AlertDialog.Builder builder = new AlertDialog.Builder(context,R.style.Theme_AppCompat_Dialog_Alert); 30 | builder.setTitle("Notice"); 31 | builder.setMessage(stringBuffer); 32 | builder.setNegativeButton("cancel", new DialogInterface.OnClickListener() { 33 | @Override 34 | public void onClick(DialogInterface dialog, int which) { 35 | call.cancel(); 36 | } 37 | }); 38 | builder.setPositiveButton("ok", new DialogInterface.OnClickListener() { 39 | @Override 40 | public void onClick(DialogInterface dialog, int which) { 41 | intent.putExtra("key1", "value3"); 42 | call.proceed(); 43 | } 44 | }); 45 | builder.setOnDismissListener(new DialogInterface.OnDismissListener() { 46 | @Override 47 | public void onDismiss(DialogInterface dialog) { 48 | call.cancel(); 49 | } 50 | }); 51 | builder.show(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 11 | 12 |