Repository: KaiXuan666/WindowTree
Branch: master
Commit: 3eb6054c0979
Files: 79
Total size: 116.4 KB
Directory structure:
gitextract_oo05fiuo/
├── .gitignore
├── .idea/
│ ├── codeStyles/
│ │ ├── Project.xml
│ │ └── codeStyleConfig.xml
│ ├── gradle.xml
│ ├── misc.xml
│ ├── runConfigurations.xml
│ └── vcs.xml
├── README.md
├── app/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── com/
│ │ └── kaixuan/
│ │ └── windowtree/
│ │ └── ExampleInstrumentedTest.kt
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── kaixuan/
│ │ │ └── windowtree/
│ │ │ ├── KotlinCommon.kt
│ │ │ ├── MainActivity.kt
│ │ │ ├── MyApp.kt
│ │ │ ├── WindowTag.kt
│ │ │ ├── activity/
│ │ │ │ ├── BaseActivity.kt
│ │ │ │ ├── EmptyActivity.kt
│ │ │ │ ├── NewsDetailActivity.kt
│ │ │ │ └── NewsListActivity.kt
│ │ │ └── fragment/
│ │ │ ├── ContactsFragment.kt
│ │ │ ├── DynamicFragment.kt
│ │ │ ├── MainFragment.kt
│ │ │ ├── VipFragment.kt
│ │ │ └── dynamic/
│ │ │ └── GoodFriendDynamicFragment.kt
│ │ └── res/
│ │ ├── drawable/
│ │ │ └── ic_launcher_background.xml
│ │ ├── drawable-v24/
│ │ │ └── ic_launcher_foreground.xml
│ │ ├── layout/
│ │ │ ├── activity_main.xml
│ │ │ ├── activity_news_detail.xml
│ │ │ ├── activity_news_list.xml
│ │ │ ├── fragment_msg.xml
│ │ │ └── fragment_test.xml
│ │ ├── mipmap-anydpi-v26/
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ └── values/
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test/
│ └── java/
│ └── com/
│ └── kaixuan/
│ └── windowtree/
│ └── ExampleUnitTest.kt
├── build.gradle
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
├── windowtree_annotation/
│ ├── .gitignore
│ ├── build.gradle
│ └── src/
│ └── main/
│ └── java/
│ └── com/
│ └── kaixuan/
│ └── windowtree_annotation/
│ ├── MyClass.java
│ ├── annotation/
│ │ ├── Window.java
│ │ └── WindowTypeAnnotation.java
│ ├── enums/
│ │ └── WindowType.java
│ └── model/
│ ├── WindowData.java
│ └── WindowMeta.java
├── windowtree_compiler/
│ ├── .gitignore
│ ├── build.gradle
│ └── src/
│ └── main/
│ └── java/
│ └── com/
│ └── kaixuan/
│ └── compiler/
│ └── WindowProcessor.java
└── windowtree_library/
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src/
├── androidTest/
│ └── java/
│ └── com/
│ └── kaixuan/
│ └── windowtreelibrary/
│ └── ExampleInstrumentedTest.java
├── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── com/
│ │ └── kaixuan/
│ │ └── windowtreelibrary/
│ │ ├── WindowInfo.kt
│ │ ├── WindowTree.kt
│ │ ├── adapter/
│ │ │ └── DefaultJumpAdapter.kt
│ │ ├── model/
│ │ │ └── UnReadCountEvent.kt
│ │ ├── template/
│ │ │ ├── IJumpAdapter.kt
│ │ │ ├── ILogger.java
│ │ │ ├── IMain.java
│ │ │ └── IWindowTreeLoad.java
│ │ ├── thread/
│ │ │ ├── DefaultPoolExecutor.java
│ │ │ └── DefaultThreadFactory.java
│ │ └── util/
│ │ ├── ClassUtils.java
│ │ ├── Consts.kt
│ │ ├── DefaultLogger.java
│ │ ├── TextUtils.java
│ │ └── WindowTreeUtil.kt
│ └── res/
│ └── values/
│ └── strings.xml
└── test/
└── java/
└── com/
└── kaixuan/
└── windowtreelibrary/
└── ExampleUnitTest.java
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
*.iml
.gradle
/local.properties
/.idea/caches/build_file_checksums.ser
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
.DS_Store
/build
/captures
.externalNativeBuild
================================================
FILE: .idea/codeStyles/Project.xml
================================================
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<Objective-C-extensions>
<file>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
</file>
<class>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
</class>
<extensions>
<pair source="cpp" header="h" fileNamingConvention="NONE" />
<pair source="c" header="h" fileNamingConvention="NONE" />
</extensions>
</Objective-C-extensions>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</codeStyleSettings>
</code_scheme>
</component>
================================================
FILE: .idea/codeStyles/codeStyleConfig.xml
================================================
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>
================================================
FILE: .idea/gradle.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
<option value="$PROJECT_DIR$/windowtree_annotation" />
<option value="$PROJECT_DIR$/windowtree_compiler" />
<option value="$PROJECT_DIR$/windowtree_library" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
</GradleProjectSettings>
</option>
</component>
</project>
================================================
FILE: .idea/misc.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="NullableNotNullManager">
<option name="myDefaultNullable" value="android.support.annotation.Nullable" />
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
<option name="myNullables">
<value>
<list size="7">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
<item index="2" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
<item index="3" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
<item index="4" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
<item index="5" class="java.lang.String" itemvalue="androidx.annotation.Nullable" />
<item index="6" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNullable" />
</list>
</value>
</option>
<option name="myNotNulls">
<value>
<list size="6">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
<item index="4" class="java.lang.String" itemvalue="androidx.annotation.NonNull" />
<item index="5" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNonNull" />
</list>
</value>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>
================================================
FILE: .idea/runConfigurations.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
</set>
</option>
</component>
</project>
================================================
FILE: .idea/vcs.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
================================================
FILE: README.md
================================================
# WindowTree
只需使用注解,帮助你轻松维护树形界面的层级关系,管理你的界面结构,当你处于界面的任何位置时,都可以知道,我在哪里,我的父界面是谁,我的子界面是谁。甚至能够自动构建你的界面结构。
## 我们经常会遇一些这样的问题:
home界面下面有三个子界面A,B,C,他们三个业务功能内都会出现一个或多个未读消息或者通知,那么我们的home界面需要展示的未读消息数就应该是三个子界面未读消息数之和。
刚看到这个问题的时候会感觉很简单,我只需要在每次触发未读消息增减变化的时候,将本级界面和上一级界面的未读数量重新统计即可。当然,简单的需求是可以这么做,但是你有没有考虑过:
1. 当子界面A下级又有三个子界面A1,A2,A3的时候该怎么办?
2. 当用户没有一些界面的查看权限时,缺少了B和A2界面的浏览权限时,该怎么办?
3. 在界面构建时,如果发现用户没有A1,A2,A3的权限,那么其实A界面也不需要被构建,这样的工作,框架可以帮助我完成吗?
考虑到以上问题,如果用面向过程的思考方式出现一个解决一个,必然是事倍功半的,所以WindowTree就这样诞生了。
# WindowTree结构

windowTree将应用内的所有界面都视为一个Window,每个window都拥有与之关联的WindowInfo,借助WindowInfo,你可以完成灵活、强大的工作。
目前支持的功能:
1. 维护了父子界面的层级关系,你在应用的任意位置,都可以知道我的父界面是谁,我的子界面有几个等
2. 灵活的消息通讯,你可以在任意位置发送消息给任意界面,支持双向通讯,你可以知道接收者处理该消息的结果
3. 支持贯穿全局的未读消息小红点,一个父节点能够智能计算出他所拥有的所有子节点的未读消息之和,借助Kotlin的委托属性实现了数量变化实时通知更新!
4. 智能的权限管理,当用户未拥有a的子界面a1,a2的权限时,也将失去a的界面权限
5. 界面自动化搭建,你只需要使用注解定义好界面的层级关系,便可以调用api获取所有子界面,一个循环全部建立
6. 统一管理界面跳转,便于管理界面和埋点统计,减少重复代码
7. 支持Activity、Fragment、View、Dialog、PopupWindow...
<img src="https://img-blog.csdnimg.cn/20190302194910186.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2thaXh1YW5fZGFzaGVu,size_16,color_FFFFFF,t_70" width="400" hegiht="867" align=center />
# WindowTree使用
添加依赖项:
```
apply plugin: 'kotlin-kapt'
...
dependencies {
kapt 'com.kaixuan:windowtree-compiler:1.0.0'
implementation 'com.kaixuan:windowtree-library:1.0.0'
...
}
```
1. 在你应用的所有界面添加注解@Window,参数parentClass指定该界面的父节点(如当前是顶级节点,则不需要设置该属性),可选添加其他属性,index表示当前界面是父界面的第几个同类界面,name表示当前节点名字
```
@Window(parentClass = MainActivity::class,index = 2,name = "联系人")
class ContactsFragment : Fragment()
```
<br/>
2. 在应用启动时,初始化WindowTree
<br/>
```
WindowTree.init(MyApp.instance)
```
<br/>
3.
- 使用kotlin时,可直接在界面类(Activity、Fragment等)中使用扩展属性mWindowInfo拿到当前界面对应的WindowInfo
- 使用java时,使用WindowTree.with(this)获取当前界面对应的WindowInfo
<br/>
4. 如需接收消息,需要给当前节点设置setEventListener
```
mWindowInfo.setEventListener { sender, sendData ->
if (sendData is UnReadCountEvent){
tv_log.append("未读消息:${sender.name}的未读消息发生变化,数量变化=${sendData.change}\n")
updateUnReadCount()
return@setEventListener "ok 我已收到并处理完毕" // 支持返回给消息发送者一个回信
}
return@setEventListener null
}
```
windowTree支持消息接收者返回一个结果的应答,告知事件处理结果
<br/>
5. 拿到我的父节点或子节点对象,使用当前的WindowInfo对象可以轻松找到父节点和子节点,如:
```
mWindowInfo.parent
mWindowInfo.child
```
<br/>
6. 界面自动构建,如果你使用TabLayout管理你的界面,只需要以下代码即可将当前界面的所有子界面加入到Tab
```
tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener{
override fun onTabReselected(tab: TabLayout.Tab?) {
}
override fun onTabUnselected(tab: TabLayout.Tab?) {
}
override fun onTabSelected(tab: TabLayout.Tab?) {
mWindowInfo.jump(tab!!.position,WindowType.FRAGMENTV4)
}
})
mWindowInfo.filterChildByWindowType(WindowType.FRAGMENTV4).forEach { window ->
tabLayout.addTab(tabLayout.newTab().setText(window.name).setTag(window))
}
```
<br/>
7. 界面跳转控制
```
mWindowInfo.jump(0,WindowType.FRAGMENTV4)
```
这段代码表示,跳转到我的第1个Fragment类型的子界面
DefaultJumpAdapter中默认实现了一些跳转逻辑,你可以继承它实现自己的特殊逻辑
<br/>
<br/>
如果你有更好的建议欢迎与我联系!thank you!kaixuanapp@163.com
github:https://github.com/KaiXuan666/WindowTree
csdn:https://blog.csdn.net/kaixuan_dashen
================================================
FILE: app/.gitignore
================================================
/build
================================================
FILE: app/build.gradle
================================================
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.kaixuan.windowtree"
minSdkVersion 15
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation project(path: ':windowtree_annotation')
kapt project(':windowtree_compiler')
implementation project(':windowtree_library')
implementation 'com.android.support:design:28.0.0'
// kapt 'com.kaixuan:windowtree-compiler:1.0.0'
// implementation 'com.kaixuan:windowtree-library:1.0.0'
}
================================================
FILE: app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
================================================
FILE: app/src/androidTest/java/com/kaixuan/windowtree/ExampleInstrumentedTest.kt
================================================
package com.kaixuan.windowtree
import android.support.test.InstrumentationRegistry
import android.support.test.runner.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getTargetContext()
assertEquals("com.kaixuan.windowtree", appContext.packageName)
}
}
================================================
FILE: app/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.kaixuan.windowtree">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:name=".MyApp"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".activity.NewsListActivity">
</activity>
<activity android:name=".activity.EmptyActivity"/>
<activity android:name=".activity.NewsDetailActivity"/>
</application>
</manifest>
================================================
FILE: app/src/main/java/com/kaixuan/windowtree/KotlinCommon.kt
================================================
package com.kaixuan.windowtree
import android.widget.Toast
fun showToast(msg : String){
Toast.makeText(MyApp.instance,msg,Toast.LENGTH_SHORT).show()
}
================================================
FILE: app/src/main/java/com/kaixuan/windowtree/MainActivity.kt
================================================
package com.kaixuan.windowtree
import android.os.Bundle
import android.support.design.widget.TabLayout
import android.text.method.ScrollingMovementMethod
import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import com.kaixuan.windowtree.activity.BaseActivity
import com.kaixuan.windowtree_annotation.annotation.Window
import com.kaixuan.windowtree_annotation.enums.WindowType
import com.kaixuan.windowtreelibrary.WindowInfo
import com.kaixuan.windowtreelibrary.WindowTree
import com.kaixuan.windowtreelibrary.mWindowInfo
import com.kaixuan.windowtreelibrary.model.UnReadCountEvent
import kotlinx.android.synthetic.main.activity_main.*
@Window
class MainActivity : BaseActivity() {
lateinit var with: WindowInfo<*>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
tv_log.movementMethod = ScrollingMovementMethod.getInstance();
btnInit.setOnClickListener {
init()
}
btnCreateLayout.setOnClickListener { _ ->
WindowTree.instance.hasPageAuthorityFun = { it == -1L }
if (WindowTree.hasInit) {
bindTabLayout()
initActivityButton()
} else {
showToast("请先初始化")
}
}
btnCreateLayout.setOnLongClickListener { _ ->
// 用户的页面权限,一般通过登录接口获取
val userAuthority = listOf(-1L, 1L, 2L, 3L, 4L)
WindowTree.instance.hasPageAuthorityFun = { userAuthority.contains(it) }
if (WindowTree.hasInit) {
bindTabLayout()
initActivityButton()
} else {
showToast("请先初始化")
}
true
}
btnGc.setOnClickListener { System.gc() }
btnDestroy.setOnClickListener {
release()
}
}
fun init() {
if (WindowTree.hasInit) {
showToast("已经初始化过")
return
}
WindowTree.init(MyApp.instance)
with = mWindowInfo
mWindowInfo.frameLayoutId = frameLayout.id
initEventListener()
}
fun initEventListener() {
with.setEventListener { sender, sendData ->
if (sendData is UnReadCountEvent) {
tv_log.append("未读消息:${sender.name}的未读消息发生变化,数量变化=${sendData.change}\n")
updateUnReadCount()
return@setEventListener null
}
when (sender) {
// 1、判断是自己的孩子发来的消息
in with.child -> {
when (sendData) {
// 2、判断发来消息的数据类型,你也可以定义msgCode或其他数据类型来进行判断,此处我为了偷懒
is String -> {
tv_log.append("子模块${sender.name}发来了消息,内容=${sendData}\n")
}
is Int -> {
}
}
}
}
return@setEventListener "ok 我已收到并处理完毕" // 支持返回给消息发送者一个回信
}
}
fun bindTabLayout() {
if (tabLayout.tabCount != 0) {
showToast("不能重复添加tab")
return
}
tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabReselected(tab: TabLayout.Tab?) {
}
override fun onTabUnselected(tab: TabLayout.Tab?) {
}
override fun onTabSelected(tab: TabLayout.Tab?) {
Log.e("onTabSelected", tab!!.tag.toString())
mWindowInfo.jump(tab!!.position, WindowType.FRAGMENTV4)
}
})
// 自动布局到TabLayout,两种方式都可以实现
// with.child.forEach {
// if (it.windowType == WindowType.FRAGMENTV4){
// tabLayout.addTab(tabLayout.newTab().setText(it.name))
// }
// }
mWindowInfo.filterChildByWindowType(WindowType.FRAGMENTV4).filter { WindowTree.hasAuthority(it.pageAuthority) }
.forEach { window ->
tabLayout.addTab(tabLayout.newTab().setText(window.name).setTag(window))
}
}
fun initActivityButton() {
llActivity.visibility = View.VISIBLE
if (llActivity.childCount == 0) {
// 过滤子Window自动进行布局
mWindowInfo.filterChildByWindowType(WindowType.ACTIVITY).forEach { window ->
llActivity.addView(Button(this).apply {
text = "打开 ${window.name}"
setOnClickListener { with.jump(window) } // 注:此处不能使用mWindowInfo获取当前windowInfo对象,因为此处的this指代的是View Button
}, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
}
}
}
/**
* 更新未读消息显示
*/
fun updateUnReadCount() {
val calcChildUnReadCount = mWindowInfo.calcChildUnReadCount()
supportActionBar!!.title = if (calcChildUnReadCount != 0) "新消息:$calcChildUnReadCount" else "WindowTree"
(0 until tabLayout.tabCount).forEach {
val windowInfo = tabLayout.getTabAt(it)!!.tag as WindowInfo<*>
val count = windowInfo.calcChildUnReadCount()
tabLayout.getTabAt(it)!!.text = windowInfo.name + if (count == 0) "" else "($count)"
}
(0 until llActivity.childCount).forEach {
val findChildByIndex = mWindowInfo.findChildByIndex<Any>(it, WindowType.ACTIVITY)!!
(llActivity.getChildAt(it) as Button).apply {
text = "打开 ${findChildByIndex.name}"
val count = findChildByIndex.calcChildUnReadCount()
if (count != 0) {
append("($count)")
}
}
}
}
override fun onBackPressed() {
super.onBackPressed()
}
fun release() {
supportActionBar!!.title = "WindowTree"
WindowTree.destroy()
llActivity.visibility = View.GONE
llActivity.removeAllViews()
tabLayout.removeAllTabs()
tabLayout.clearOnTabSelectedListeners()
frameLayout.removeAllViews()
System.gc()
tv_log.text = ""
}
override fun onDestroy() {
release()
super.onDestroy()
}
}
================================================
FILE: app/src/main/java/com/kaixuan/windowtree/MyApp.kt
================================================
package com.kaixuan.windowtree
import android.app.Application
class MyApp : Application() {
companion object {
lateinit var instance : MyApp;
}
override fun onCreate() {
super.onCreate()
instance = this
}
}
================================================
FILE: app/src/main/java/com/kaixuan/windowtree/WindowTag.kt
================================================
package com.kaixuan.windowtree
class WindowTag(
var unReadMsgCount : Int = 0,
var pageAuthority : Long = 0
){
}
================================================
FILE: app/src/main/java/com/kaixuan/windowtree/activity/BaseActivity.kt
================================================
package com.kaixuan.windowtree.activity
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.util.Log
open class BaseActivity : AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.i(this.javaClass.simpleName,"onCreate")
}
override fun onStart() {
super.onStart()
Log.i(this.javaClass.simpleName,"onStart")
}
override fun onResume() {
super.onResume()
Log.i(this.javaClass.simpleName,"onResume")
}
override fun onPause() {
super.onPause()
Log.i(this.javaClass.simpleName,"onPause")
}
override fun onStop() {
super.onStop()
Log.i(this.javaClass.simpleName,"onStop")
}
override fun onDestroy() {
super.onDestroy()
Log.i(this.javaClass.simpleName,"onDestroy")
}
}
================================================
FILE: app/src/main/java/com/kaixuan/windowtree/activity/EmptyActivity.kt
================================================
package com.kaixuan.windowtree.activity
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import com.kaixuan.windowtree.R
import com.kaixuan.windowtree_annotation.annotation.Window
import com.kaixuan.windowtreelibrary.mWindowInfo
@Window(parentClassName = "com.kaixuan.windowtree.MainActivity",name = "空白界面",index = 2)
class EmptyActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.fragment_test)
supportActionBar!!.title = mWindowInfo.name
}
}
================================================
FILE: app/src/main/java/com/kaixuan/windowtree/activity/NewsDetailActivity.kt
================================================
package com.kaixuan.windowtree.activity
import android.os.Bundle
import com.kaixuan.windowtree.R
import com.kaixuan.windowtree_annotation.annotation.Window
import com.kaixuan.windowtreelibrary.mWindowInfo
import com.kaixuan.windowtreelibrary.model.UnReadCountEvent
import kotlinx.android.synthetic.main.activity_news_detail.*
@Window(parentClass = NewsListActivity::class,name = "新闻详情",index = 3)
class NewsDetailActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_news_detail)
supportActionBar!!.title = mWindowInfo.name + mWindowInfo.getTag()
tv_title.text = mWindowInfo.getTag() as String
mWindowInfo.unReadMsgCount = mWindowInfo.bundle.getInt("unReadCount")
tv_unReadCount.text = "未读数量:${mWindowInfo.unReadMsgCount}"
mWindowInfo.setEventListener { sender, sendData ->
if (sendData is UnReadCountEvent){
tv_unReadCount.text = "未读数量:${mWindowInfo.unReadMsgCount}"
}
}
btn_unReadAdd.setOnClickListener { mWindowInfo.unReadMsgCount ++ }
btn_dealUnRead.setOnClickListener { mWindowInfo.unReadMsgCount = 0 }
}
override fun onDestroy() {
mWindowInfo.release()
super.onDestroy()
}
}
================================================
FILE: app/src/main/java/com/kaixuan/windowtree/activity/NewsListActivity.kt
================================================
package com.kaixuan.windowtree.activity
import android.os.Bundle
import android.support.v7.recyclerview.extensions.ListAdapter
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.TextView
import com.kaixuan.windowtree.MainActivity
import com.kaixuan.windowtree.R
import com.kaixuan.windowtree_annotation.annotation.Window
import com.kaixuan.windowtreelibrary.mWindowInfo
import kotlinx.android.synthetic.main.activity_news_list.*
@Window(parentClass = MainActivity::class,name = "新闻列表",index = 1)
class NewsListActivity : BaseActivity() {
val newsList = 0..200
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_news_list)
supportActionBar!!.title = mWindowInfo.name
recyclerView.layoutManager = LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false)
recyclerView.adapter = object : RecyclerView.Adapter<RecyclerView.ViewHolder>(){
override fun onCreateViewHolder(p0: ViewGroup, p1: Int): RecyclerView.ViewHolder {
return object : RecyclerView.ViewHolder(LayoutInflater.from(this@NewsListActivity).inflate(R.layout.support_simple_spinner_dropdown_item,p0,false)){}
}
override fun getItemCount(): Int = newsList.count()
override fun onBindViewHolder(p0: RecyclerView.ViewHolder, p1: Int) {
(p0.itemView as TextView).let {textView ->
textView.text = "Hello,这里是新闻啊啊啊啊啊啊啊啊 ${p1}"
textView.setOnClickListener {
val childByIndex = mWindowInfo.findChildByIndex<String>(0)!!
childByIndex.setTag(textView.text.toString())
childByIndex.bundle.putInt("unReadCount",p1)
mWindowInfo.jump(childByIndex) }
}
}
}
}
override fun onDestroy() {
mWindowInfo.release()
super.onDestroy()
}
}
================================================
FILE: app/src/main/java/com/kaixuan/windowtree/fragment/ContactsFragment.kt
================================================
package com.kaixuan.windowtree.fragment
import android.os.Bundle
import android.support.v4.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.kaixuan.windowtree.MainActivity
import com.kaixuan.windowtree.R
import com.kaixuan.windowtree_annotation.annotation.Window
import com.kaixuan.windowtreelibrary.mWindowInfo
import kotlinx.android.synthetic.main.fragment_test.*
@Window(parentClass = MainActivity::class,index = 2,name = "联系人")
class ContactsFragment : Fragment() {
var mView : View? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
mView ?: inflater.inflate(R.layout.fragment_test,container,false).apply { mView = this }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
tv_title.text = "我是" + mWindowInfo.getClazz()!!.simpleName
btn_send.setOnClickListener {
mWindowInfo.unReadMsgCount ++
val response = mWindowInfo.sendData("hello,我是${javaClass.simpleName}", mWindowInfo.parent!!)
tv_log.append("收到了回信:${mWindowInfo.parent!!.getClazz()!!.simpleName}:$response\n")
}
btn_resetUnReadCount.setOnClickListener { mWindowInfo.unReadMsgCount = 0 }
}
override fun onDestroyView() {
mWindowInfo.release()
super.onDestroyView()
}
}
================================================
FILE: app/src/main/java/com/kaixuan/windowtree/fragment/DynamicFragment.kt
================================================
package com.kaixuan.windowtree.fragment
import android.os.Bundle
import android.support.v4.app.Fragment
import android.text.method.ScrollingMovementMethod
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.kaixuan.windowtree.MainActivity
import com.kaixuan.windowtree.R
import com.kaixuan.windowtree_annotation.annotation.Window
import com.kaixuan.windowtreelibrary.mWindowInfo
import kotlinx.android.synthetic.main.fragment_test.*
@Window(parentClass = MainActivity::class,index = 3,name = "动态")
class DynamicFragment : Fragment() {
var mView : View? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
mView ?: inflater.inflate(R.layout.fragment_test,container,false).apply { mView = this }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
tv_log.movementMethod = ScrollingMovementMethod.getInstance();
tv_title.text = "我是" + mWindowInfo.getClazz()!!.simpleName
btn_send.setOnClickListener {
mWindowInfo.unReadMsgCount ++
val response = mWindowInfo.sendData("hello,我是${javaClass.simpleName}", mWindowInfo.parent!!)
tv_log.append("收到了回信:${mWindowInfo.parent!!.getClazz()!!.simpleName}:$response\n")
}
btn_resetUnReadCount.setOnClickListener { mWindowInfo.unReadMsgCount = 0 }
}
}
================================================
FILE: app/src/main/java/com/kaixuan/windowtree/fragment/MainFragment.kt
================================================
package com.kaixuan.windowtree.fragment
import android.os.Bundle
import android.support.v4.app.Fragment
import android.text.method.ScrollingMovementMethod
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.kaixuan.windowtree.MainActivity
import com.kaixuan.windowtree.R
import com.kaixuan.windowtree_annotation.annotation.Window
import com.kaixuan.windowtreelibrary.WindowInfo
import com.kaixuan.windowtreelibrary.WindowTree
import com.kaixuan.windowtreelibrary.mWindowInfo
import kotlinx.android.synthetic.main.fragment_test.*
@Window(parentClass = MainActivity::class,index = 1,name = "主页")
class MainFragment : Fragment() {
lateinit var with : WindowInfo<String>
var mView : View? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
mView ?: inflater.inflate(R.layout.fragment_test,container,false).apply { mView = this }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
with = WindowTree.with(this)!!
val t = with.setTag("")
tv_log.movementMethod = ScrollingMovementMethod.getInstance();
tv_title.text = "我是" + mWindowInfo!!.getClazz()!!.simpleName
btn_send.setOnClickListener {
mWindowInfo.unReadMsgCount ++
val response = with.sendData("hello,我是${javaClass.simpleName}", with.parent!!)
tv_log.append("收到了回信:${with.parent!!.getClazz()!!.simpleName}:${response}\n")
}
btn_resetUnReadCount.setOnClickListener { mWindowInfo.unReadMsgCount = 0 }
}
}
================================================
FILE: app/src/main/java/com/kaixuan/windowtree/fragment/VipFragment.kt
================================================
package com.kaixuan.windowtree.fragment
import android.os.Bundle
import android.support.v4.app.Fragment
import android.text.method.ScrollingMovementMethod
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.kaixuan.windowtree.MainActivity
import com.kaixuan.windowtree.R
import com.kaixuan.windowtree_annotation.annotation.Window
import com.kaixuan.windowtreelibrary.mWindowInfo
import kotlinx.android.synthetic.main.fragment_test.*
@Window(parentClass = MainActivity::class,index = 3,name = "Vip隐藏界面",pageAuthority = 1)
class VipFragment : Fragment() {
var mView : View? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
mView ?: inflater.inflate(R.layout.fragment_test,container,false).apply { mView = this }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
tv_log.movementMethod = ScrollingMovementMethod.getInstance();
tv_title.text = "我是Vip界面,一般人没有权限打开我"
btn_send.setOnClickListener {
mWindowInfo.unReadMsgCount ++
val response = mWindowInfo.sendData("hello,我是${javaClass.simpleName},pageAuthority=${mWindowInfo.pageAuthority}", mWindowInfo.parent!!)
tv_log.append( "收到了回信:${mWindowInfo.parent!!.getClazz()!!.simpleName}:$response\n")
}
btn_resetUnReadCount.setOnClickListener { mWindowInfo.unReadMsgCount = 0 }
}
}
================================================
FILE: app/src/main/java/com/kaixuan/windowtree/fragment/dynamic/GoodFriendDynamicFragment.kt
================================================
package com.kaixuan.windowtree.fragment.dynamic
import android.os.Bundle
import android.support.v4.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.kaixuan.windowtree.R
import com.kaixuan.windowtreelibrary.mWindowInfo
import kotlinx.android.synthetic.main.fragment_msg.*
class GoodFriendDynamicFragment : Fragment() {
var mView : View? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
mView ?: inflater.inflate(R.layout.fragment_msg,container,false).apply { mView = this }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
updateMsgCount()
mWindowInfo.setEventListener { sender, sendData ->
mWindowInfo.unReadMsgCount++
updateMsgCount()
}
}
fun updateMsgCount(){
tv_msg_tips.text = "我有${mWindowInfo.unReadMsgCount}条未读消息"
}
}
================================================
FILE: app/src/main/res/drawable/ic_launcher_background.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:height="108dp"
android:width="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path android:fillColor="#008577"
android:pathData="M0,0h108v108h-108z"/>
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,0L79,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M99,0L99,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,9L108,9"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,29L108,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,39L108,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,49L108,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,69L108,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,79L108,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,99L108,99"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,29L89,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
</vector>
================================================
FILE: app/src/main/res/drawable-v24/ic_launcher_foreground.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path
android:fillType="evenOdd"
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
android:strokeColor="#00000000"
android:strokeWidth="1">
<aapt:attr name="android:fillColor">
<gradient
android:endX="78.5885"
android:endY="90.9159"
android:startX="48.7653"
android:startY="61.0927"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0"/>
<item
android:color="#00000000"
android:offset="1.0"/>
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
android:strokeColor="#00000000"
android:strokeWidth="1"/>
</vector>
================================================
FILE: app/src/main/res/layout/activity_main.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout android:layout_width="0dp" android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_weight="1">
<Button
android:id="@+id/btnInit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/colorAccent"
android:text="初始化WindowTree"/>
<Button
android:id="@+id/btnCreateLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="WindowTree自动布局\n(长按使用会员身份构建)"/>
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btnDestroy"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="销毁"/>
<Button
android:id="@+id/btnGc"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="GC"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/llActivity"
android:layout_width="0dp" android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone"
android:layout_weight="1">
</LinearLayout>
</LinearLayout>
<TextView
android:id="@+id/tv_log"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:scrollbars="vertical"
android:lines="10"/>
<View android:layout_width="match_parent" android:layout_height="2px"
android:background="@color/design_default_color_primary"
/>
<FrameLayout
android:id="@+id/frameLayout"
android:layout_width="match_parent" android:layout_height="0dp"
android:layout_weight="1">
</FrameLayout>
<android.support.design.widget.TabLayout
android:layout_alignParentBottom="true"
android:id="@+id/tabLayout"
android:layout_width="match_parent" android:layout_height="wrap_content"
app:tabIndicatorColor="@color/colorAccent"
app:tabIndicatorGravity="top">
</android.support.design.widget.TabLayout>
</LinearLayout>
================================================
FILE: app/src/main/res/layout/activity_news_detail.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
tools:context=".activity.NewsListActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent" android:layout_height="match_parent"
android:gravity="center"
android:textSize="18sp"
android:text=""/>
<TextView
android:id="@+id/tv_unReadCount"
android:layout_width="match_parent" android:layout_height="match_parent"
android:gravity="center"
android:textSize="26sp"
android:text=""/>
<Button
android:id="@+id/btn_unReadAdd"
android:layout_width="match_parent" android:layout_height="match_parent"
android:gravity="center"
android:textSize="26sp"
android:text="未读消息+1"/>
<Button
android:id="@+id/btn_dealUnRead"
android:layout_width="match_parent" android:layout_height="match_parent"
android:gravity="center"
android:textSize="26sp"
android:text="全部已读"/>
</LinearLayout>
</LinearLayout>
================================================
FILE: app/src/main/res/layout/activity_news_list.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
tools:context=".activity.NewsListActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"></android.support.v7.widget.RecyclerView>
</LinearLayout>
================================================
FILE: app/src/main/res/layout/fragment_msg.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
tools:context=".activity.TestActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<TextView
android:id="@+id/tv_msg_tips"
android:layout_width="match_parent" android:layout_height="match_parent"
android:gravity="center"
android:textSize="26sp"
android:text=""/>
<Button
android:id="@+id/btn_add_msg"
android:layout_width="match_parent" android:layout_height="match_parent"
android:gravity="center"
android:textSize="26sp"
android:text="本模块消息+1"/>
</LinearLayout>
</LinearLayout>
================================================
FILE: app/src/main/res/layout/fragment_test.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
tools:context=".activity.NewsListActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent" android:layout_height="match_parent"
android:gravity="center"
android:textSize="26sp"
android:text="我是"/>
<Button
android:id="@+id/btn_send"
android:layout_width="match_parent" android:layout_height="match_parent"
android:gravity="center"
android:textSize="26sp"
android:text="点我发消息"/>
<Button
android:id="@+id/btn_resetUnReadCount"
android:layout_width="match_parent" android:layout_height="match_parent"
android:gravity="center"
android:textSize="26sp"
android:text="重置消息数量"/>
<TextView
android:id="@+id/tv_log"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:scrollbars="vertical"
android:lines="5"/>
</LinearLayout>
</LinearLayout>
================================================
FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>
================================================
FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>
================================================
FILE: app/src/main/res/values/colors.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#008577</color>
<color name="colorPrimaryDark">#00574B</color>
<color name="colorAccent">#D81B60</color>
</resources>
================================================
FILE: app/src/main/res/values/strings.xml
================================================
<resources>
<string name="app_name">WindowTree</string>
</resources>
================================================
FILE: app/src/main/res/values/styles.xml
================================================
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>
================================================
FILE: app/src/test/java/com/kaixuan/windowtree/ExampleUnitTest.kt
================================================
package com.kaixuan.windowtree
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}
================================================
FILE: build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.2.71'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.novoda:bintray-release:0.9'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
// maven { url "https://dl.bintray.com/kaixuan666/maven" }
}
tasks.withType(Javadoc) {
options.addStringOption('Xdoclint:none', '-quiet')
options.addStringOption('encoding', 'UTF-8')
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
//统一声明配置
ext {
repoName = 'maven'
userOrg = 'kaixuan666'
groupId = 'com.kaixuan'
uploadName = 'windowtree'
publishVersion = '1.0.0'
desc = 'WindowTree'
website = 'https://github.com/KaiXuan666/WindowTree'
licences = ['Apache-2.0']
}
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
================================================
FILE: gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
================================================
FILE: gradlew
================================================
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: settings.gradle
================================================
include ':app', ':windowtree_library', ':windowtree_compiler', ':windowtree_annotation'
================================================
FILE: windowtree_annotation/.gitignore
================================================
/build
================================================
FILE: windowtree_annotation/build.gradle
================================================
apply plugin: 'java-library'
apply plugin: 'com.novoda.bintray-release'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
}
sourceCompatibility = "7"
targetCompatibility = "7"
tasks.withType(Javadoc) {
options.encoding = "UTF-8"
}
//添加
publish {
repoName = rootProject.repoName
artifactId = 'windowtree-annotation'
userOrg = rootProject.userOrg
groupId = rootProject.groupId
uploadName = rootProject.uploadName
publishVersion = rootProject.publishVersion
desc = rootProject.desc
website = rootProject.website
licences = rootProject.licences
}
================================================
FILE: windowtree_annotation/src/main/java/com/kaixuan/windowtree_annotation/MyClass.java
================================================
package com.kaixuan.windowtree_annotation;
public class MyClass {
}
================================================
FILE: windowtree_annotation/src/main/java/com/kaixuan/windowtree_annotation/annotation/Window.java
================================================
package com.kaixuan.windowtree_annotation.annotation;
import com.kaixuan.windowtree_annotation.model.WindowData;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Window {
/**
*
* @return 父class
*/
Class parentClass() default Window.class;
/**
*
* @return 父class完整类名,该属性和parentClass只需要任选实现一个即可,该属性便于模块化场景下无法直接引用目标类的情况
*/
String parentClassName() default "";
/**
* 功能名称 可空
* @return
*/
String name() default "";
/**
* 该窗口是父节点的第几个子窗口
* @return
*/
int index() default 0;
/**
* 该窗口在你的项目中,页面权限id是多少
* 该值默认为-1时,即该页面无权限控制,任何人都允许加载
* @return
*/
long pageAuthority() default -1;
}
================================================
FILE: windowtree_annotation/src/main/java/com/kaixuan/windowtree_annotation/annotation/WindowTypeAnnotation.java
================================================
package com.kaixuan.windowtree_annotation.annotation;
public @interface WindowTypeAnnotation {
}
================================================
FILE: windowtree_annotation/src/main/java/com/kaixuan/windowtree_annotation/enums/WindowType.java
================================================
package com.kaixuan.windowtree_annotation.enums;
public enum WindowType {
ACTIVITY(0, "android.app.Activity"),
FRAGMENT(1, "android.app.Fragment"),
FRAGMENTV4(2, "android.support.v4.app.Fragment"),
VIEW(3, "android.view.View"),
DIALOG(4, "android.app.Dialog"),
POPUPWINDOW(5, "android.widget.PopupWindow"),
UNKNOWN(-1, "Unknown route type");
int id;
String className;
WindowType(int id, String className) {
this.id = id;
this.className = className;
}
public int getId() {
return id;
}
public WindowType setId(int id) {
this.id = id;
return this;
}
public String getClassName() {
return className;
}
public WindowType setClassName(String className) {
this.className = className;
return this;
}
public static WindowType parse(String name) {
for (WindowType windowType : WindowType.values()) {
if (windowType.getClassName().equals(name)) {
return windowType;
}
}
return UNKNOWN;
}
}
================================================
FILE: windowtree_annotation/src/main/java/com/kaixuan/windowtree_annotation/model/WindowData.java
================================================
package com.kaixuan.windowtree_annotation.model;
public class WindowData {
}
================================================
FILE: windowtree_annotation/src/main/java/com/kaixuan/windowtree_annotation/model/WindowMeta.java
================================================
package com.kaixuan.windowtree_annotation.model;
import com.kaixuan.windowtree_annotation.enums.WindowType;
import java.util.ArrayList;
import java.util.List;
public class WindowMeta<T> {
private Class<?> clazz = null;
private String clazzName = "";
public WindowMeta parent = null;
public List<WindowMeta> child = new ArrayList<>();
public String name;
public int index;
private WindowType windowType;
public long pageAuthority;
public WindowMeta(Class<?> clazz, String clazzName, WindowMeta parent, String name, int index) {
this(clazz,clazzName,parent,name,index,WindowType.UNKNOWN);
}
public WindowMeta(Class<?> clazz, String clazzName, WindowMeta parent, String name, int index, WindowType windowType) {
this(clazz,clazzName,parent,name,index,WindowType.UNKNOWN,-1);
}
public WindowMeta(Class<?> clazz, String clazzName, WindowMeta parent, String name, int index, WindowType windowType,long pageAuthority) {
this.index = index;
this.name = name;
this.clazz = clazz;
this.clazzName = clazzName;
this.parent = parent;
this.windowType = windowType;
this.pageAuthority = pageAuthority;
}
public void addChild(String clazzName,String name,int index,WindowType windowType){
try {
child.add(new WindowMeta(Class.forName(clazzName),clazzName,this,name,index,windowType));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public Class<?> getClazz() {
return clazz;
}
public WindowMeta setClazz(Class<?> clazz) {
this.clazz = clazz;
return this;
}
public String getClazzName() {
return clazzName;
}
public WindowMeta setClazzName(String clazzName) {
this.clazzName = clazzName;
return this;
}
public WindowType getWindowType() {
return windowType;
}
public WindowMeta<T> setWindowType(WindowType windowType) {
this.windowType = windowType;
return this;
}
@Override
public String toString() {
return "WindowMeta{" +
"clazz=" + clazz +
", clazzName='" + clazzName + '\'' +
", name='" + name + '\'' +
", index=" + index +
", windowType=" + windowType +
", pageAuthority=" + pageAuthority +
'}';
}
}
================================================
FILE: windowtree_compiler/.gitignore
================================================
/build
================================================
FILE: windowtree_compiler/build.gradle
================================================
apply plugin: 'java-library'
apply plugin:'java'
apply plugin: 'com.novoda.bintray-release'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.google.auto.service:auto-service:1.0-rc3'
implementation project(path: ':windowtree_annotation')
implementation 'com.squareup:javapoet:1.8.0'
}
sourceCompatibility = "7"
targetCompatibility = "7"
tasks.withType(Javadoc) {
options.encoding = "UTF-8"
}
//添加
publish {
repoName = rootProject.repoName
artifactId = 'windowtree-compiler'
userOrg = rootProject.userOrg
groupId = rootProject.groupId
uploadName = rootProject.uploadName
publishVersion = rootProject.publishVersion
desc = rootProject.desc
website = rootProject.website
licences = rootProject.licences
}
================================================
FILE: windowtree_compiler/src/main/java/com/kaixuan/compiler/WindowProcessor.java
================================================
package com.kaixuan.compiler;
import com.google.auto.service.AutoService;
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.MirroredTypeException;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
import com.kaixuan.windowtree_annotation.enums.WindowType;
import com.kaixuan.windowtree_annotation.model.WindowMeta;
import com.kaixuan.windowtree_annotation.annotation.Window;
import com.squareup.javapoet.*;
import java.io.IOException;
import java.util.*;
@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes("com.kaixuan.windowtree_annotation.annotation.Window")
public class WindowProcessor extends AbstractProcessor {
/**
* 文件相关的辅助类
*/
private Filer mFiler;
/**
* 元素相关的辅助类
*/
private Elements mElementUtils;
/**
* 日志相关的辅助类
*/
private Messager mMessager;
/**
* 日志相关的辅助类
*/
private Types types;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
mFiler = processingEnvironment.getFiler();
types = processingEnvironment.getTypeUtils();
mElementUtils = processingEnvironment.getElementUtils();
mMessager = processingEnvironment.getMessager();
mMessager.printMessage(Diagnostic.Kind.WARNING, "init : ");
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
Map<String, List<WindowMeta>> stringListMap = parseWindows(roundEnvironment.getElementsAnnotatedWith(Window.class));
List<String> main = new ArrayList<>();
for (String s : stringListMap.keySet()) {
MethodSpec.Builder builder = MethodSpec.methodBuilder("loadWindowTree")//定义方面名
.addModifiers(Modifier.PUBLIC)//定义修饰符
.addAnnotation(Override.class)
.returns(void.class)//定义返回结果
.addParameter(ClassName.get("com.kaixuan.windowtreelibrary","WindowInfo"), "currentWindowMeta");//添加方法参数
List<WindowMeta> windowMetas = stringListMap.get(s);
windowMetas.sort(new Comparator<WindowMeta>() {
@Override
public int compare(WindowMeta windowMeta, WindowMeta t1) {
return windowMeta.index - t1.index;
}
});
mMessager.printMessage(Diagnostic.Kind.WARNING, "windowMetas windowMetas : " + windowMetas.toString());
for (WindowMeta meta : windowMetas) {
mMessager.printMessage(Diagnostic.Kind.WARNING, "windowMetas meta.index : " + meta.index);
builder.addStatement("currentWindowMeta.addChild($S,$S,$L,$L,$L)", meta.getClazzName(),meta.name,meta.index,meta.getWindowType(),meta.pageAuthority);//添加方法内容
}
MethodSpec methodSpec = builder.addException(ClassName.get(ClassNotFoundException.class))
.build();
String tempClass = s.substring(s.lastIndexOf(".") + 1) + "$Gen";
TypeSpec finderClass = TypeSpec.classBuilder(tempClass)
.addSuperinterface(ClassName.get(mElementUtils.getTypeElement("com.kaixuan.windowtreelibrary.template.IWindowTreeLoad")))
.addModifiers(Modifier.PUBLIC)
// .addSuperinterface(ParameterizedTypeName.get(TypeUtil.INJECTOR, TypeName.get(mClassElement.asType())))
.addMethod(methodSpec)
.build();
// 创建Java文件
main.add("com.kaixuan.windowtree.windows." + tempClass );
JavaFile javaFile = JavaFile.builder("com.kaixuan.windowtree.windows", finderClass)
.addStaticImport(WindowType.class, "*").build();
try {
javaFile.writeTo(mFiler);
} catch (IOException e) {
e.printStackTrace();
}
}
ParameterizedTypeName map = ParameterizedTypeName.get(ClassName.get(Map.class)
, ClassName.get(String.class)
, ParameterizedTypeName.get(
ClassName.get(Class.class),
WildcardTypeName.subtypeOf(ClassName.get(mElementUtils.getTypeElement("com.kaixuan.windowtreelibrary.template.IWindowTreeLoad")))
));
MethodSpec.Builder builder = MethodSpec.methodBuilder("getAllGeneratedFile")
.addModifiers(Modifier.PUBLIC)//定义修饰符
.addAnnotation(Override.class)
.returns(ParameterizedTypeName.get(ClassName.get(Map.class)
, ClassName.get(String.class)
, ParameterizedTypeName.get(
ClassName.get(Class.class),
WildcardTypeName.subtypeOf(ClassName.get(mElementUtils.getTypeElement("com.kaixuan.windowtreelibrary.template.IWindowTreeLoad")))
)));//定义返回结果
ClassName mapNew = ClassName.get("java.util", "HashMap");
builder.addStatement("$T map = new $T()",map,mapNew);
for (String s : main) {
builder.addStatement("map.put($S,$L.class)",s,s);
}
builder.addStatement("return map");
TypeSpec mainClass = TypeSpec.classBuilder( "Main$Gen")
.addSuperinterface(ClassName.get(mElementUtils.getTypeElement("com.kaixuan.windowtreelibrary.template.IMain")))
.addModifiers(Modifier.PUBLIC)
// .addSuperinterface(ParameterizedTypeName.get(TypeUtil.INJECTOR, TypeName.get(mClassElement.asType())))
.addMethod(builder.build())
.build();
JavaFile javaFile = JavaFile.builder("com.kaixuan.windowtree.windows", mainClass).build();
try {
javaFile.writeTo(mFiler);
} catch (IOException e) {
e.printStackTrace();
}
return true;
}
private Map<String, List<WindowMeta>> parseWindows(Set<? extends Element> routeElements) {
// 以父节点类名为key平铺存储所有WindowMeta
Map<String, List<WindowMeta>> map = new HashMap<>();
TypeMirror type_Activity = mElementUtils.getTypeElement(WindowType.ACTIVITY.getClassName()).asType();
TypeMirror type_Fragment = mElementUtils.getTypeElement(WindowType.FRAGMENT.getClassName()).asType();
TypeMirror type_Fragmentv4 = mElementUtils.getTypeElement(WindowType.FRAGMENTV4.getClassName()).asType();
TypeMirror type_View = mElementUtils.getTypeElement(WindowType.VIEW.getClassName()).asType();
TypeMirror type_Dialog = mElementUtils.getTypeElement(WindowType.DIALOG.getClassName()).asType();
TypeMirror type_PopupWindow = mElementUtils.getTypeElement(WindowType.POPUPWINDOW.getClassName()).asType();
for (Element routeElement : routeElements) {
Window annotation = routeElement.getAnnotation(Window.class);
if (routeElement.getKind() == ElementKind.CLASS) {
mMessager.printMessage(Diagnostic.Kind.WARNING, "annotation : " + annotation.toString());
mMessager.printMessage(Diagnostic.Kind.WARNING, "annotation info : " + annotation.name() + annotation.index());
String parentName = annotation.parentClassName();
if (parentName.isEmpty()) {
try {
mMessager.printMessage(Diagnostic.Kind.WARNING, "parentClass : " + annotation.parentClass());
} catch (MirroredTypeException e) {
parentName = e.getTypeMirror().toString();
// mMessager.printMessage(Diagnostic.Kind.WARNING,"e.getTypeMirror() : " + e.getTypeMirror().toString());
}
}
List<WindowMeta> windowMetas = map.get(parentName);
if (windowMetas == null) {
windowMetas = new ArrayList<>();
map.put(parentName, windowMetas);
}
// routeElement.toString()
WindowMeta windowMeta = new WindowMeta<Object>(null, routeElement.toString(), null, annotation.name(), annotation.index(),WindowType.UNKNOWN,annotation.pageAuthority());
windowMetas.add(windowMeta);
// 判断类型
TypeMirror tm = routeElement.asType();
if (types.isSubtype(tm,type_Activity)){
windowMeta.setWindowType(WindowType.ACTIVITY);
}else if (types.isSubtype(tm,type_Fragment)){
windowMeta.setWindowType(WindowType.FRAGMENT);
}else if (types.isSubtype(tm,type_Fragmentv4)){
windowMeta.setWindowType(WindowType.FRAGMENTV4);
}else if (types.isSubtype(tm,type_View)){
windowMeta.setWindowType(WindowType.VIEW);
}else if (types.isSubtype(tm,type_Dialog)){
windowMeta.setWindowType(WindowType.DIALOG);
}else if (types.isSubtype(tm,type_PopupWindow)){
windowMeta.setWindowType(WindowType.POPUPWINDOW);
}else {
windowMeta.setWindowType(WindowType.UNKNOWN);
}
mMessager.printMessage(Diagnostic.Kind.WARNING, "windowMeta : " + windowMeta.toString());
} else {
mMessager.printMessage(Diagnostic.Kind.WARNING, "not class : " + routeElement.toString());
}
}
mMessager.printMessage(Diagnostic.Kind.WARNING, "map : " + map.toString());
return map;
}
}
================================================
FILE: windowtree_library/.gitignore
================================================
/build
================================================
FILE: windowtree_library/build.gradle
================================================
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'com.novoda.bintray-release'
android {
compileSdkVersion 28
defaultConfig {
minSdkVersion 15
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:28.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation project(':windowtree_annotation')
}
repositories {
mavenCentral()
}
tasks.withType(Javadoc) {
options.encoding = "UTF-8"
}
//添加
publish {
repoName = rootProject.repoName
artifactId = 'windowtree-library'
userOrg = rootProject.userOrg
groupId = rootProject.groupId
uploadName = rootProject.uploadName
publishVersion = rootProject.publishVersion
desc = rootProject.desc
website = rootProject.website
licences = rootProject.licences
}
================================================
FILE: windowtree_library/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
================================================
FILE: windowtree_library/src/androidTest/java/com/kaixuan/windowtreelibrary/ExampleInstrumentedTest.java
================================================
package com.kaixuan.windowtreelibrary;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.kaixuan.windowtreelibrary.test", appContext.getPackageName());
}
}
================================================
FILE: windowtree_library/src/main/AndroidManifest.xml
================================================
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.kaixuan.windowtreelibrary"/>
================================================
FILE: windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/WindowInfo.kt
================================================
package com.kaixuan.windowtreelibrary
import android.content.Context
import android.os.Bundle
import com.kaixuan.windowtree_annotation.enums.WindowType
import com.kaixuan.windowtreelibrary.model.UnReadCountEvent
import com.kaixuan.windowtreelibrary.util.WindowTreeUtil
import java.util.*
import kotlin.properties.Delegates
class WindowInfo<T> @JvmOverloads constructor(
clazz: Class<*>,
clazzName: String,
parent: WindowInfo<*>?,
var name: String? = null,
var index: Int = 0,
var windowType: WindowType = WindowType.UNKNOWN
) {
companion object {
const val TAG = "WindowInfo"
}
private var clazz: Class<*>? = null
private var clazzName = ""
var parent: WindowInfo<*>? = null
val child: MutableList<WindowInfo<*>> = ArrayList()
val bundle: Bundle by lazy { Bundle() }
private var tag: T? = null
/**
* 如当前节点需跳转到Fragment或填充View,则需设置该属性
*/
var frameLayoutId = -1
/**
* 当前节点需要接收其他窗口发来的消息时,请设置该监听
*/
private var onEventListener: ((
sender: WindowInfo<*>,
sendData: Any?
) -> Any?)? = null
/**
* 该值发生任意变化时,都会通知到当前窗口以及当前窗口的所有父节点
*/
var unReadMsgCount: Int by Delegates.observable(0) { prop, old, new ->
if (!WindowTree.hasInit) return@observable // WindowTree销毁时不再发送未读消息事件
var notifyTaget: WindowInfo<*>?
notifyTaget = this
while (notifyTaget != null) {
this.sendData(UnReadCountEvent(this, new - old), notifyTaget)
notifyTaget = notifyTaget.parent
}
}
var pageAuthority: Long = -1
init {
this.clazz = clazz
this.clazzName = clazzName
this.parent = parent
}
fun addChild(clazzName: String, name: String, index: Int, windowType: WindowType, pageAuthority: Long) {
try {
child.add(
WindowInfo<Any>(
Class.forName(clazzName),
clazzName,
this,
name,
index,
windowType
).apply { this.pageAuthority = pageAuthority })
} catch (e: ClassNotFoundException) {
WindowTree.logger.error("addChild", e.toString())
e.printStackTrace()
}
}
fun getClazz(): Class<*>? {
return clazz
}
fun setClazz(clazz: Class<*>): WindowInfo<*> {
this.clazz = clazz
return this
}
fun getClazzName(): String {
return clazzName
}
fun setClazzName(clazzName: String): WindowInfo<*> {
this.clazzName = clazzName
return this
}
fun getTag(): T? {
return tag
}
fun setTag(tag: T): WindowInfo<T> {
this.tag = tag
return this
}
fun sendData(data: Any, receiverClass: Class<*>): Any? {
val findWindowInfoByClass =
WindowTree.instance.windowMeta!!.findWindowInfoByClass(receiverClass) ?: WindowTree.logger.error(
TAG,
"receiverClass,找不到$receiverClass"
).run { return null }
findWindowInfoByClass.onEventListener ?: WindowTree.logger.error(
TAG,
"发送失败,目标${receiverClass}未设置监听"
).run { return null }
return findWindowInfoByClass.onEventListener!!.invoke(this, data)
}
fun sendData(data: Any, receiverClazzName: String): Any? {
val findWindowInfoByClass =
WindowTree.instance.windowMeta!!.findWindowInfoByClass(receiverClazzName) ?: WindowTree.logger.error(
TAG,
"发送失败,找不到$receiverClazzName"
).run { return null }
findWindowInfoByClass.onEventListener ?: WindowTree.logger.error(
TAG,
"发送失败,目标${receiverClazzName}未设置监听"
).run { return null }
return findWindowInfoByClass.onEventListener!!.invoke(this, data)
}
fun sendData(data: Any, receiver: WindowInfo<*>): Any? {
receiver.onEventListener ?: WindowTree.logger.info(TAG, "发送失败,目标${receiver}未设置监听").run { return null }
WindowTree.logger.info("sendData", "this = ${this.clazzName}, receiver = ${receiver.clazzName}, data = $data")
return receiver.onEventListener!!.invoke(this, data)
}
fun setEventListener(a: ((sender: WindowInfo<*>, sendData: Any?) -> Any?)?) {
onEventListener = a
}
fun getEventListener() = onEventListener
/**
* 跳转至第几个什么类型的子界面
* @param index 第几个子界面,该索引值受windowType影响
* @param windowType 什么类型的子界面,如不传入windowType,默认以所有类型的子界面获取index
*/
fun jump(index: Int, windowType: WindowType = WindowType.UNKNOWN): Boolean {
val tempAdapter = WindowTree.instance.getJumpAdapter() ?: WindowTree.instance.defaultJumpAdapter
val taget =
findChildByIndex<Any>(index, windowType) ?: throw RuntimeException("找不到${windowType}类型的第${index}个窗口")
val context = WindowTreeUtil.findContextByInfo(this)
?: throw RuntimeException("当前${this}对应的window未处于打开状态,无法从当前节点获取context,无法跳转")
return tempAdapter.jump(context, taget)
}
fun jump(windowInfo: WindowInfo<*>): Boolean {
val tempAdapter = WindowTree.instance.getJumpAdapter() ?: WindowTree.instance.defaultJumpAdapter
val context = WindowTreeUtil.findContextByInfo(this)
?: throw RuntimeException("当前${this}对应的window未处于打开状态,无法从当前节点获取context,无法跳转")
return tempAdapter.jump(context, windowInfo)
}
fun <T> findChildByIndex(index: Int, windowType: WindowType = WindowType.UNKNOWN): WindowInfo<T>? {
val filter = filterChildByWindowType(windowType)
return if (index >= filter.size) null else filter[index] as WindowInfo<T>
}
fun filterChildByWindowType(windowType: WindowType): List<WindowInfo<*>> {
return child.filter { windowType == WindowType.UNKNOWN || it.windowType == windowType }
}
fun findWindowInfoByClass(clazz: Class<*>?): WindowInfo<*>? {
return findWindowInfoByClass(clazz!!.name)
}
/**
* 计算当前的所有子节点的未读消息数量(包含当前节点)
*/
fun calcChildUnReadCount(): Int {
var count = 0
findWindowInfoByCondition {
count += it.unReadMsgCount
return@findWindowInfoByCondition false
}
return count
}
fun getContext(): Context? = WindowTreeUtil.findContextByInfo(this)
fun findWindowInfoByClass(clazzName: String): WindowInfo<*>? {
return findWindowInfoByCondition {
return@findWindowInfoByCondition it.clazzName == clazzName
}
}
tailrec fun findWindowInfoByCondition(condition: (WindowInfo<*>) -> Boolean): WindowInfo<*>? {
WindowTree.logger.info("findWindowInfoByCondition", this.toString())
if (condition(this)) {
return this
}
for (windowMeta in child) {
val findWindowInfoByClass = windowMeta.findWindowInfoByCondition(condition)
if (findWindowInfoByClass == null) {
} else {
return findWindowInfoByClass
}
}
return null
}
tailrec fun findWindowInfoByConditionUp(condition: (WindowInfo<*>) -> Boolean): WindowInfo<*>? {
WindowTree.logger.info("findWindowInfoByCondition", this.toString())
if (condition(this)) {
return this
}
if (parent == null) {
return null
} else {
return parent!!.findWindowInfoByConditionUp(condition)
}
}
/**
* 如果当前节点有设置过onEventListener则需要手动调用该方法释放资源
*
* @param clearReadCount 是否清理未读消息数量,可根据业务需求传参
*/
fun release(clearReadCount: Boolean = false) {
if (clearReadCount) unReadMsgCount = 0
setEventListener(null)
}
/**
* 切忌不要打印this或父或子节点,否则将造成
*/
override fun toString(): String {
var parentClazzName = "null str"
parent?.run { parentClazzName = this.clazzName }
return "WindowMeta{" +
"clazz=" + clazz +
", clazzName='" + clazzName + '\''.toString() +
", parent=" + parentClazzName +
", child.size=" + child.size +
", index=" + index +
", windowType=" + windowType +
", tag=" + tag +
'}'.toString()
}
}
================================================
FILE: windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/WindowTree.kt
================================================
package com.kaixuan.windowtreelibrary
import android.app.Activity
import android.app.Application
import android.app.Dialog
import android.content.Context
import android.support.v4.app.Fragment
import android.view.View
import android.widget.PopupWindow
import com.kaixuan.windowtree_annotation.annotation.Window
import com.kaixuan.windowtreelibrary.adapter.DefaultJumpAdapter
import com.kaixuan.windowtreelibrary.template.IJumpAdapter
import com.kaixuan.windowtreelibrary.template.IMain
import com.kaixuan.windowtreelibrary.template.IWindowTreeLoad
import com.kaixuan.windowtreelibrary.util.Consts
import com.kaixuan.windowtreelibrary.util.DefaultLogger
import java.util.*
class WindowTree {
lateinit var allGeneratedFile:
Map<String, Class<out IWindowTreeLoad>>;
val defaultJumpAdapter: DefaultJumpAdapter by lazy { DefaultJumpAdapter() }
private var jumpAdapter: IJumpAdapter? = null
var hasPageAuthorityFun: ((Long) -> Boolean)? = null
/**
* 最顶级的WindowInfo
*/
var windowMeta: WindowInfo<Any>? = null;
val weakHashMap: WeakHashMap<Any, WindowInfo<*>> = WeakHashMap()
companion object {
lateinit var mContext: Context
@JvmField
@Volatile
var debuggable = false
@Volatile
var hasInit = false
@JvmField
val logger = DefaultLogger(Consts.TAG)
val instance: WindowTree by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { WindowTree() }
fun init(application: Application) {
if (!hasInit) {
hasInit = true
mContext = application.applicationContext
logger.showLog(true)
val newInstance =
Class.forName(Consts.GENERATE_FILE_PATH + ".Main" + Consts.GENERATE_FILE_NAME_END).getConstructor()
.newInstance()
instance.allGeneratedFile = (newInstance as IMain).allGeneratedFile
val rootWindow =
instance.allGeneratedFile[Consts.GENERATE_FILE_PATH + ".Window" + Consts.GENERATE_FILE_NAME_END]
instance.windowMeta = WindowInfo<Any>(
Window::class.java,
Window::class.java.name,
null
);
rootWindow!!.getConstructor().newInstance().loadWindowTree(instance.windowMeta)
bindWindowTree(instance.windowMeta!!)
logger.info(Consts.TAG, instance.allGeneratedFile.toString())
}
}
fun bindWindowTree(windowMeta: WindowInfo<*>) {
windowMeta.child.forEach { forItem ->
val clazz =
instance.allGeneratedFile[Consts.GENERATE_FILE_PATH + "." + forItem.getClazz()!!.simpleName + Consts.GENERATE_FILE_NAME_END]
if (clazz == null) {
// 找不到,跳过
} else {
// 找到,继续递归
clazz.getConstructor().newInstance().loadWindowTree(forItem)
bindWindowTree(forItem)
}
}
}
fun destroy() {
if (hasInit) {
hasInit = false
instance.windowMeta!!.findWindowInfoByCondition {
it.release(true)
return@findWindowInfoByCondition false
}
instance.weakHashMap.clear()
instance.windowMeta = null
instance.hasPageAuthorityFun = null
instance.defaultJumpAdapter.clear()
instance.setJumpAdapter(null)
}
}
@JvmStatic
fun <T> with(obj: Any): WindowInfo<T>? {
if (!hasInit) {
throw RuntimeException("WindowTree未初始化")
}
return instance.weakHashMap[obj] as WindowInfo<T>?
?: instance.windowMeta!!.findWindowInfoByClass(obj.javaClass)?.apply {
instance.weakHashMap[obj] = this
} as WindowInfo<T>?
}
@JvmStatic
fun hasAuthority(pageAuthorityId: Long): Boolean {
if (!hasInit) {
throw RuntimeException("WindowTree未初始化")
}
return instance.hasPageAuthorityFun?.invoke(pageAuthorityId)
?: (pageAuthorityId == -1L) // 不设置权限判断器的情况下,只拥有默认权限的页面
}
}
fun setJumpAdapter(jumpAdapter: IJumpAdapter?) {
this.jumpAdapter = jumpAdapter
}
fun setJumpAdapter(jumpAdapter: (Context, WindowInfo<*>) -> Boolean) {
this.jumpAdapter = jumpAdapter.toJumpAdapter()
}
fun getJumpAdapter(): IJumpAdapter? = this.jumpAdapter
}
/**
* 简化调用者写法,不需要object :
*/
fun <T : ((Context, WindowInfo<*>) -> Boolean)> T.toJumpAdapter(): IJumpAdapter = object : IJumpAdapter {
override fun jump(formContext: Context, to: WindowInfo<*>): Boolean = this@toJumpAdapter.invoke(formContext, to)
}
fun getWindowInfo(any: Any): WindowInfo<Any> = WindowTree.with<Any>(any)
?: throw RuntimeException("找不到与${any}对应的WindowInfo,请检查。在匿名内部类调用本方法时应谨慎检查this关键字的指代对象。")
val Activity.mWindowInfo: WindowInfo<Any>
get() = getWindowInfo(this)
val View.mWindowInfo: WindowInfo<Any>
get() = getWindowInfo(this)
val Fragment.mWindowInfo: WindowInfo<Any>
get() = getWindowInfo(this)
val android.app.Fragment.mWindowInfo: WindowInfo<Any>
get() = getWindowInfo(this)
val Dialog.mWindowInfo: WindowInfo<Any>
get() = getWindowInfo(this)
val PopupWindow.mWindowInfo: WindowInfo<Any>
get() = getWindowInfo(this)
================================================
FILE: windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/adapter/DefaultJumpAdapter.kt
================================================
package com.kaixuan.windowtreelibrary.adapter
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.support.v4.app.Fragment
import android.support.v4.app.FragmentActivity
import com.kaixuan.windowtree_annotation.enums.WindowType
import com.kaixuan.windowtreelibrary.WindowInfo
import com.kaixuan.windowtreelibrary.WindowTree
import com.kaixuan.windowtreelibrary.template.IJumpAdapter
import java.lang.ref.WeakReference
class DefaultJumpAdapter : IJumpAdapter {
// val weakHashMapFrag : WeakHashMap<String,Any> by lazy { WeakHashMap<String,Any>() }
val weakReferenceMap: HashMap<String, WeakReference<Any>> by lazy { HashMap<String, WeakReference<Any>>() }
fun <T> getCache(className: String, create: () -> T): T {
val weakReference = weakReferenceMap[className]
if (weakReference?.get() == null) {
weakReferenceMap[className] = WeakReference(create()) as WeakReference<Any>
}
return weakReferenceMap[className]!!.get() as T
}
override fun jump(formContext: Context, to: WindowInfo<*>): Boolean {
val with = WindowTree.with<Any>(formContext)
?: throw RuntimeException("找不到与该FormContext对应的WindowInfo; WindowInfo corresponding to FormContext could not be found")
when (to.windowType) {
WindowType.ACTIVITY -> {
formContext.startActivity(Intent(formContext, to.getClazz()).apply { putExtras(to.bundle) })
return true
}
WindowType.FRAGMENT -> {
(formContext as Activity).fragmentManager.beginTransaction().replace(with.frameLayoutId,
getCache(to.getClazzName()) { to.getClazz()!!.newInstance() } as android.app.Fragment
).commit()
return true
}
WindowType.FRAGMENTV4 -> {
(formContext as FragmentActivity).supportFragmentManager.run {
beginTransaction().replace(with.frameLayoutId,
getCache(to.getClazzName()) { to.getClazz()!!.newInstance() } as Fragment
).commit()
}
return true
}
else -> {
throw RuntimeException("暂时不支持自动跳转至${to.windowType}类型窗口,您可以自己实现IJumpAdapter接口,自定义跳转方法")
}
}
return false
}
fun clear() {
weakReferenceMap.clear()
}
}
================================================
FILE: windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/model/UnReadCountEvent.kt
================================================
package com.kaixuan.windowtreelibrary.model
import com.kaixuan.windowtreelibrary.WindowInfo
data class UnReadCountEvent(val fromWindowInfo: WindowInfo<*>,val change : Int)
================================================
FILE: windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/template/IJumpAdapter.kt
================================================
package com.kaixuan.windowtreelibrary.template
import android.content.Context
import com.kaixuan.windowtreelibrary.WindowInfo
interface IJumpAdapter{
fun jump(formContext: Context, to: WindowInfo<*>) : Boolean
}
================================================
FILE: windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/template/ILogger.java
================================================
package com.kaixuan.windowtreelibrary.template;
import com.kaixuan.windowtreelibrary.util.Consts;
/**
* Logger
*
* @author 正纬 <a href="mailto:zhilong.liu@aliyun.com">Contact me.</a>
* @version 1.0
* @since 16/5/16 下午5:39
*/
public interface ILogger {
boolean isShowLog = false;
boolean isShowStackTrace = false;
String defaultTag = Consts.TAG;
void showLog(boolean isShowLog);
void showStackTrace(boolean isShowStackTrace);
void debug(String tag, String message);
void info(String tag, String message);
void warning(String tag, String message);
void error(String tag, String message);
void monitor(String message);
boolean isMonitorMode();
String getDefaultTag();
}
================================================
FILE: windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/template/IMain.java
================================================
package com.kaixuan.windowtreelibrary.template;
import java.util.Map;
public interface IMain {
public Map<String, Class<? extends IWindowTreeLoad>> getAllGeneratedFile();
}
================================================
FILE: windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/template/IWindowTreeLoad.java
================================================
package com.kaixuan.windowtreelibrary.template;
import com.kaixuan.windowtreelibrary.WindowInfo;
public interface IWindowTreeLoad {
public void loadWindowTree(WindowInfo currentWindowInfo) throws ClassNotFoundException;
}
================================================
FILE: windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/thread/DefaultPoolExecutor.java
================================================
package com.kaixuan.windowtreelibrary.thread;
import com.kaixuan.windowtreelibrary.WindowTree;
import com.kaixuan.windowtreelibrary.util.Consts;
import com.kaixuan.windowtreelibrary.util.TextUtils;
import java.util.concurrent.*;
/**
* Executors
*
* @author 正纬 <a href="mailto:zhilong.liu@aliyun.com">Contact me.</a>
* @version 1.0
* @since 16/4/28 下午4:07
*/
public class DefaultPoolExecutor extends ThreadPoolExecutor {
// Thread args
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int INIT_THREAD_COUNT = CPU_COUNT + 1;
private static final int MAX_THREAD_COUNT = INIT_THREAD_COUNT;
private static final long SURPLUS_THREAD_LIFE = 30L;
private static volatile DefaultPoolExecutor instance;
public static DefaultPoolExecutor getInstance() {
if (null == instance) {
synchronized (DefaultPoolExecutor.class) {
if (null == instance) {
instance = new DefaultPoolExecutor(
INIT_THREAD_COUNT,
MAX_THREAD_COUNT,
SURPLUS_THREAD_LIFE,
TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(64),
new DefaultThreadFactory());
}
}
}
return instance;
}
private DefaultPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
WindowTree.logger.error(Consts.TAG, "Task rejected, too many task!");
}
});
}
/*
* 线程执行结束,顺便看一下有么有什么乱七八糟的异常
*
* @param r the runnable that has completed
* @param t the exception that caused termination, or null if
*/
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if (t == null && r instanceof Future<?>) {
try {
((Future<?>) r).get();
} catch (CancellationException ce) {
t = ce;
} catch (ExecutionException ee) {
t = ee.getCause();
} catch (InterruptedException ie) {
Thread.currentThread().interrupt(); // ignore/reset
}
}
if (t != null) {
WindowTree.logger.warning(Consts.TAG, "Running task appeared exception! Thread [" + Thread.currentThread().getName() + "], because [" + t.getMessage() + "]\n" + TextUtils.formatStackTrace(t.getStackTrace()));
}
}
}
================================================
FILE: windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/thread/DefaultThreadFactory.java
================================================
package com.kaixuan.windowtreelibrary.thread;
import android.support.annotation.NonNull;
import com.kaixuan.windowtreelibrary.WindowTree;
import com.kaixuan.windowtreelibrary.util.Consts;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 线程池工厂类
*
* @author zhilong <a href="mailto:zhilong.liu@aliyun.com">Contact me.</a>
* @version 1.0
* @since 15/12/25 上午10:51
*/
public class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final String namePrefix;
public DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "ARouter task pool No." + poolNumber.getAndIncrement() + ", thread No.";
}
public Thread newThread(@NonNull Runnable runnable) {
String threadName = namePrefix + threadNumber.getAndIncrement();
WindowTree.logger.info(Consts.TAG, "Thread production, name is [" + threadName + "]");
Thread thread = new Thread(group, runnable, threadName, 0);
if (thread.isDaemon()) { //设为非后台线程
thread.setDaemon(false);
}
if (thread.getPriority() != Thread.NORM_PRIORITY) { //优先级为normal
thread.setPriority(Thread.NORM_PRIORITY);
}
// 捕获多线程处理中的异常
thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread thread, Throwable ex) {
WindowTree.logger.info(Consts.TAG, "Running task appeared exception! Thread [" + thread.getName() + "], because [" + ex.getMessage() + "]");
}
});
return thread;
}
}
================================================
FILE: windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/util/ClassUtils.java
================================================
package com.kaixuan.windowtreelibrary.util;
// Copy from galaxy sdk ${com.alibaba.android.galaxy.utils.ClassUtils}
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.util.Log;
import com.kaixuan.windowtreelibrary.WindowTree;
import com.kaixuan.windowtreelibrary.thread.DefaultPoolExecutor;
import dalvik.system.DexFile;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Scanner, find out class with any conditions, copy from google source code.
*
* @author 正纬 <a href="mailto:zhilong.liu@aliyun.com">Contact me.</a>
* @version 1.0
* @since 16/6/27 下午10:58
*/
public class ClassUtils {
private static final String EXTRACTED_NAME_EXT = ".classes";
private static final String EXTRACTED_SUFFIX = ".zip";
private static final String SECONDARY_FOLDER_NAME = "code_cache" + File.separator + "secondary-dexes";
private static final String PREFS_FILE = "multidex.version";
private static final String KEY_DEX_NUMBER = "dex.number";
private static final int VM_WITH_MULTIDEX_VERSION_MAJOR = 2;
private static final int VM_WITH_MULTIDEX_VERSION_MINOR = 1;
private static SharedPreferences getMultiDexPreferences(Context context) {
return context.getSharedPreferences(PREFS_FILE, Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB ? Context.MODE_PRIVATE : Context.MODE_PRIVATE | Context.MODE_MULTI_PROCESS);
}
// public static Set<String> getFileNameByPackage(Context context, final String packageName) {
// final Set<String> classNames = new HashSet<>();
// }
/**
* 通过指定包名,扫描包下面包含的所有的ClassName
*
* @param context U know
* @param packageName 包名
* @return 所有class的集合
*/
public static Set<String> getFileNameByPackageName(Context context, final String packageName) throws PackageManager.NameNotFoundException, IOException, InterruptedException {
final Set<String> classNames = new HashSet<>();
List<String> paths = getSourcePaths(context);
final CountDownLatch parserCtl = new CountDownLatch(paths.size());
for (final String path : paths) {
DefaultPoolExecutor.getInstance().execute(new Runnable() {
@Override
public void run() {
DexFile dexfile = null;
try {
if (path.endsWith(EXTRACTED_SUFFIX)) {
//NOT use new DexFile(path), because it will throw "permission error in /data/dalvik-cache"
dexfile = DexFile.loadDex(path, path + ".tmp", 0);
} else {
dexfile = new DexFile(path);
}
Enumeration<String> dexEntries = dexfile.entries();
while (dexEntries.hasMoreElements()) {
String className = dexEntries.nextElement();
if (className.startsWith(packageName)) {
classNames.add(className);
}
}
} catch (Throwable ignore) {
Log.e("ARouter", "Scan map file in dex files made error.", ignore);
} finally {
if (null != dexfile) {
try {
dexfile.close();
} catch (Throwable ignore) {
}
}
parserCtl.countDown();
}
}
});
}
parserCtl.await();
Log.d(Consts.TAG, "Filter " + classNames.size() + " classes by packageName <" + packageName + ">");
return classNames;
}
/**
* get all the dex path
*
* @param context the application context
* @return all the dex path
* @throws PackageManager.NameNotFoundException
* @throws IOException
*/
public static List<String> getSourcePaths(Context context) throws PackageManager.NameNotFoundException, IOException {
ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0);
File sourceApk = new File(applicationInfo.sourceDir);
List<String> sourcePaths = new ArrayList<>();
sourcePaths.add(applicationInfo.sourceDir); //add the default apk path
//the prefix of extracted file, ie: test.classes
String extractedFilePrefix = sourceApk.getName() + EXTRACTED_NAME_EXT;
// 如果VM已经支持了MultiDex,就不要去Secondary Folder加载 Classesx.zip了,那里已经么有了
// 通过是否存在sp中的multidex.version是不准确的,因为从低版本升级上来的用户,是包含这个sp配置的
if (!isVMMultidexCapable()) {
//the total dex numbers
int totalDexNumber = getMultiDexPreferences(context).getInt(KEY_DEX_NUMBER, 1);
File dexDir = new File(applicationInfo.dataDir, SECONDARY_FOLDER_NAME);
for (int secondaryNumber = 2; secondaryNumber <= totalDexNumber; secondaryNumber++) {
//for each dex file, ie: test.classes2.zip, test.classes3.zip...
String fileName = extractedFilePrefix + secondaryNumber + EXTRACTED_SUFFIX;
File extractedFile = new File(dexDir, fileName);
if (extractedFile.isFile()) {
sourcePaths.add(extractedFile.getAbsolutePath());
//we ignore the verify zip part
} else {
throw new IOException("Missing extracted secondary dex file '" + extractedFile.getPath() + "'");
}
}
}
if (WindowTree.debuggable) { // Search instant run support only debuggable
sourcePaths.addAll(tryLoadInstantRunDexFile(applicationInfo));
}
return sourcePaths;
}
/**
* Get instant run dex path, used to catch the branch usingApkSplits=false.
*/
private static List<String> tryLoadInstantRunDexFile(ApplicationInfo applicationInfo) {
List<String> instantRunSourcePaths = new ArrayList<>();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && null != applicationInfo.splitSourceDirs) {
// add the split apk, normally for InstantRun, and newest version.
instantRunSourcePaths.addAll(Arrays.asList(applicationInfo.splitSourceDirs));
Log.d(Consts.TAG, "Found InstantRun support");
} else {
try {
// This man is reflection from Google instant run sdk, he will tell me where the dex files go.
Class pathsByInstantRun = Class.forName("com.android.tools.fd.runtime.Paths");
Method getDexFileDirectory = pathsByInstantRun.getMethod("getDexFileDirectory", String.class);
String instantRunDexPath = (String) getDexFileDirectory.invoke(null, applicationInfo.packageName);
File instantRunFilePath = new File(instantRunDexPath);
if (instantRunFilePath.exists() && instantRunFilePath.isDirectory()) {
File[] dexFile = instantRunFilePath.listFiles();
for (File file : dexFile) {
if (null != file && file.exists() && file.isFile() && file.getName().endsWith(".dex")) {
instantRunSourcePaths.add(file.getAbsolutePath());
}
}
Log.d(Consts.TAG, "Found InstantRun support");
}
} catch (Exception e) {
Log.e(Consts.TAG, "InstantRun support error, " + e.getMessage());
}
}
return instantRunSourcePaths;
}
/**
* Identifies if the current VM has a native support for multidex, meaning there is no need for
* additional installation by this library.
*
* @return true if the VM handles multidex
*/
private static boolean isVMMultidexCapable() {
boolean isMultidexCapable = false;
String vmName = null;
try {
if (isYunOS()) { // YunOS需要特殊判断
vmName = "'YunOS'";
isMultidexCapable = Integer.valueOf(System.getProperty("ro.build.version.sdk")) >= 21;
} else { // 非YunOS原生Android
vmName = "'Android'";
String versionString = System.getProperty("java.vm.version");
if (versionString != null) {
Matcher matcher = Pattern.compile("(\\d+)\\.(\\d+)(\\.\\d+)?").matcher(versionString);
if (matcher.matches()) {
try {
int major = Integer.parseInt(matcher.group(1));
int minor = Integer.parseInt(matcher.group(2));
isMultidexCapable = (major > VM_WITH_MULTIDEX_VERSION_MAJOR)
|| ((major == VM_WITH_MULTIDEX_VERSION_MAJOR)
&& (minor >= VM_WITH_MULTIDEX_VERSION_MINOR));
} catch (NumberFormatException ignore) {
// let isMultidexCapable be false
}
}
}
}
} catch (Exception ignore) {
}
Log.i(Consts.TAG, "VM with name " + vmName + (isMultidexCapable ? " has multidex support" : " does not have multidex support"));
return isMultidexCapable;
}
/**
* 判断系统是否为YunOS系统
*/
private static boolean isYunOS() {
try {
String version = System.getProperty("ro.yunos.version");
String vmName = System.getProperty("java.vm.name");
return (vmName != null && vmName.toLowerCase().contains("lemur"))
|| (version != null && version.trim().length() > 0);
} catch (Exception ignore) {
return false;
}
}
}
================================================
FILE: windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/util/Consts.kt
================================================
package com.kaixuan.windowtreelibrary.util
class Consts{
companion object {
@JvmField
val TAG = "WindowTree"
val GENERATE_FILE_PATH = "com.kaixuan.windowtree.windows"
val GENERATE_FILE_NAME_END = "\$Gen"
}
}
================================================
FILE: windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/util/DefaultLogger.java
================================================
package com.kaixuan.windowtreelibrary.util;
import android.text.TextUtils;
import android.util.Log;
import com.kaixuan.windowtreelibrary.template.ILogger;
/**
* Default logger
*
* @author zhilong <a href="mailto:zhilong.liu@aliyun.com">Contact me.</a>
* @version 1.0
* @since 2015-12-08 21:44:10
*/
public class DefaultLogger implements ILogger {
private static boolean isShowLog = false;
private static boolean isShowStackTrace = false;
private static boolean isMonitorMode = false;
private String defaultTag = "WindowTree";
public void showLog(boolean showLog) {
isShowLog = showLog;
}
public void showStackTrace(boolean showStackTrace) {
isShowStackTrace = showStackTrace;
}
public void showMonitor(boolean showMonitor) {
isMonitorMode = showMonitor;
}
public DefaultLogger() {
}
public DefaultLogger(String defaultTag) {
this.defaultTag = defaultTag;
}
@Override
public void debug(String tag, String message) {
if (isShowLog) {
StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[3];
Log.d(TextUtils.isEmpty(tag) ? getDefaultTag() : tag, message + getExtInfo(stackTraceElement));
}
}
@Override
public void info(String tag, String message) {
if (isShowLog) {
StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[3];
Log.i(TextUtils.isEmpty(tag) ? getDefaultTag() : tag, message + getExtInfo(stackTraceElement));
}
}
@Override
public void warning(String tag, String message) {
if (isShowLog) {
StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[3];
Log.w(TextUtils.isEmpty(tag) ? getDefaultTag() : tag, message + getExtInfo(stackTraceElement));
}
}
@Override
public void error(String tag, String message) {
if (isShowLog) {
StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[3];
Log.e(TextUtils.isEmpty(tag) ? getDefaultTag() : tag, message + getExtInfo(stackTraceElement));
}
}
@Override
public void monitor(String message) {
if (isShowLog && isMonitorMode()) {
StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[3];
Log.d(defaultTag + "::monitor", message + getExtInfo(stackTraceElement));
}
}
@Override
public boolean isMonitorMode() {
return isMonitorMode;
}
@Override
public String getDefaultTag() {
return defaultTag;
}
public static String getExtInfo(StackTraceElement stackTraceElement) {
String separator = " & ";
StringBuilder sb = new StringBuilder("[");
if (isShowStackTrace) {
String threadName = Thread.currentThread().getName();
String fileName = stackTraceElement.getFileName();
String className = stackTraceElement.getClassName();
String methodName = stackTraceElement.getMethodName();
long threadID = Thread.currentThread().getId();
int lineNumber = stackTraceElement.getLineNumber();
sb.append("ThreadId=").append(threadID).append(separator);
sb.append("ThreadName=").append(threadName).append(separator);
sb.append("FileName=").append(fileName).append(separator);
sb.append("ClassName=").append(className).append(separator);
sb.append("MethodName=").append(methodName).append(separator);
sb.append("LineNumber=").append(lineNumber);
}
sb.append(" ] ");
return sb.toString();
}
}
================================================
FILE: windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/util/TextUtils.java
================================================
package com.kaixuan.windowtreelibrary.util;
import android.net.Uri;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Text utils
*
* @author Alex <a href="mailto:zhilong.liu@aliyun.com">Contact me.</a>
* @version 1.0
* @since 16/9/9 14:40
*/
public class TextUtils {
public static boolean isEmpty(final CharSequence cs) {
return cs == null || cs.length() == 0;
}
/**
* Print thread stack
*/
public static String formatStackTrace(StackTraceElement[] stackTrace) {
StringBuilder sb = new StringBuilder();
for (StackTraceElement element : stackTrace) {
sb.append(" at ").append(element.toString());
sb.append("\n");
}
return sb.toString();
}
/**
* Split query parameters
* @param rawUri raw uri
* @return map with params
*/
public static Map<String, String> splitQueryParameters(Uri rawUri) {
String query = rawUri.getEncodedQuery();
if (query == null) {
return Collections.emptyMap();
}
Map<String, String> paramMap = new LinkedHashMap<>();
int start = 0;
do {
int next = query.indexOf('&', start);
int end = (next == -1) ? query.length() : next;
int separator = query.indexOf('=', start);
if (separator > end || separator == -1) {
separator = end;
}
String name = query.substring(start, separator);
if (!android.text.TextUtils.isEmpty(name)) {
String value = (separator == end ? "" : query.substring(separator + 1, end));
paramMap.put(Uri.decode(name), Uri.decode(value));
}
// Move start to end of name.
start = end + 1;
} while (start < query.length());
return Collections.unmodifiableMap(paramMap);
}
/**
* Split key with |
*
* @param key raw key
* @return left key
*/
public static String getLeft(String key) {
if (key.contains("|") && !key.endsWith("|")) {
return key.substring(0, key.indexOf("|"));
} else {
return key;
}
}
/**
* Split key with |
*
* @param key raw key
* @return right key
*/
public static String getRight(String key) {
if (key.contains("|") && !key.startsWith("|")) {
return key.substring(key.indexOf("|") + 1);
} else {
return key;
}
}
/**
* Split key with |
*
* @param key raw key
* @return right key
*/
public static String getRight(String key,String sub) {
if (key.contains(sub) && !key.startsWith(sub)) {
return key.substring(key.indexOf(sub) + 1);
} else {
return key;
}
}
}
================================================
FILE: windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/util/WindowTreeUtil.kt
================================================
package com.kaixuan.windowtreelibrary.util
import android.content.Context
import com.kaixuan.windowtreelibrary.WindowInfo
import com.kaixuan.windowtreelibrary.WindowTree
class WindowTreeUtil{
companion object {
fun <K,V> findKeyByValue(map: Map<K,V>,value : V): K? {
return map.entries.find { it.value == value }?.key
}
fun findContextByInfo(windowInfo: WindowInfo<*>): Context? {
val findKeyByValue = findKeyByValue(WindowTree.instance.weakHashMap, windowInfo) ?: return null
return getContext(findKeyByValue)
}
fun getContext(any: Any): Context? {
when(any){
is android.app.Activity -> return any
is android.app.Fragment -> return any.activity
is android.support.v4.app.Fragment -> return any.context
is android.view.View -> return any.context
is android.app.Dialog -> return any.context
is android.widget.PopupWindow -> return any.contentView.context
else -> return null
}
}
}
}
================================================
FILE: windowtree_library/src/main/res/values/strings.xml
================================================
<resources>
<string name="app_name">WindowTreeLibrary</string>
</resources>
================================================
FILE: windowtree_library/src/test/java/com/kaixuan/windowtreelibrary/ExampleUnitTest.java
================================================
package com.kaixuan.windowtreelibrary;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}
gitextract_oo05fiuo/
├── .gitignore
├── .idea/
│ ├── codeStyles/
│ │ ├── Project.xml
│ │ └── codeStyleConfig.xml
│ ├── gradle.xml
│ ├── misc.xml
│ ├── runConfigurations.xml
│ └── vcs.xml
├── README.md
├── app/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── com/
│ │ └── kaixuan/
│ │ └── windowtree/
│ │ └── ExampleInstrumentedTest.kt
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── kaixuan/
│ │ │ └── windowtree/
│ │ │ ├── KotlinCommon.kt
│ │ │ ├── MainActivity.kt
│ │ │ ├── MyApp.kt
│ │ │ ├── WindowTag.kt
│ │ │ ├── activity/
│ │ │ │ ├── BaseActivity.kt
│ │ │ │ ├── EmptyActivity.kt
│ │ │ │ ├── NewsDetailActivity.kt
│ │ │ │ └── NewsListActivity.kt
│ │ │ └── fragment/
│ │ │ ├── ContactsFragment.kt
│ │ │ ├── DynamicFragment.kt
│ │ │ ├── MainFragment.kt
│ │ │ ├── VipFragment.kt
│ │ │ └── dynamic/
│ │ │ └── GoodFriendDynamicFragment.kt
│ │ └── res/
│ │ ├── drawable/
│ │ │ └── ic_launcher_background.xml
│ │ ├── drawable-v24/
│ │ │ └── ic_launcher_foreground.xml
│ │ ├── layout/
│ │ │ ├── activity_main.xml
│ │ │ ├── activity_news_detail.xml
│ │ │ ├── activity_news_list.xml
│ │ │ ├── fragment_msg.xml
│ │ │ └── fragment_test.xml
│ │ ├── mipmap-anydpi-v26/
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ └── values/
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test/
│ └── java/
│ └── com/
│ └── kaixuan/
│ └── windowtree/
│ └── ExampleUnitTest.kt
├── build.gradle
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
├── windowtree_annotation/
│ ├── .gitignore
│ ├── build.gradle
│ └── src/
│ └── main/
│ └── java/
│ └── com/
│ └── kaixuan/
│ └── windowtree_annotation/
│ ├── MyClass.java
│ ├── annotation/
│ │ ├── Window.java
│ │ └── WindowTypeAnnotation.java
│ ├── enums/
│ │ └── WindowType.java
│ └── model/
│ ├── WindowData.java
│ └── WindowMeta.java
├── windowtree_compiler/
│ ├── .gitignore
│ ├── build.gradle
│ └── src/
│ └── main/
│ └── java/
│ └── com/
│ └── kaixuan/
│ └── compiler/
│ └── WindowProcessor.java
└── windowtree_library/
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src/
├── androidTest/
│ └── java/
│ └── com/
│ └── kaixuan/
│ └── windowtreelibrary/
│ └── ExampleInstrumentedTest.java
├── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── com/
│ │ └── kaixuan/
│ │ └── windowtreelibrary/
│ │ ├── WindowInfo.kt
│ │ ├── WindowTree.kt
│ │ ├── adapter/
│ │ │ └── DefaultJumpAdapter.kt
│ │ ├── model/
│ │ │ └── UnReadCountEvent.kt
│ │ ├── template/
│ │ │ ├── IJumpAdapter.kt
│ │ │ ├── ILogger.java
│ │ │ ├── IMain.java
│ │ │ └── IWindowTreeLoad.java
│ │ ├── thread/
│ │ │ ├── DefaultPoolExecutor.java
│ │ │ └── DefaultThreadFactory.java
│ │ └── util/
│ │ ├── ClassUtils.java
│ │ ├── Consts.kt
│ │ ├── DefaultLogger.java
│ │ ├── TextUtils.java
│ │ └── WindowTreeUtil.kt
│ └── res/
│ └── values/
│ └── strings.xml
└── test/
└── java/
└── com/
└── kaixuan/
└── windowtreelibrary/
└── ExampleUnitTest.java
SYMBOL INDEX (78 symbols across 15 files)
FILE: windowtree_annotation/src/main/java/com/kaixuan/windowtree_annotation/MyClass.java
class MyClass (line 3) | public class MyClass {
FILE: windowtree_annotation/src/main/java/com/kaixuan/windowtree_annotation/enums/WindowType.java
type WindowType (line 3) | public enum WindowType {
method WindowType (line 16) | WindowType(int id, String className) {
method getId (line 21) | public int getId() {
method setId (line 25) | public WindowType setId(int id) {
method getClassName (line 30) | public String getClassName() {
method setClassName (line 34) | public WindowType setClassName(String className) {
method parse (line 39) | public static WindowType parse(String name) {
FILE: windowtree_annotation/src/main/java/com/kaixuan/windowtree_annotation/model/WindowData.java
class WindowData (line 3) | public class WindowData {
FILE: windowtree_annotation/src/main/java/com/kaixuan/windowtree_annotation/model/WindowMeta.java
class WindowMeta (line 10) | public class WindowMeta<T> {
method WindowMeta (line 21) | public WindowMeta(Class<?> clazz, String clazzName, WindowMeta parent,...
method WindowMeta (line 24) | public WindowMeta(Class<?> clazz, String clazzName, WindowMeta parent,...
method WindowMeta (line 27) | public WindowMeta(Class<?> clazz, String clazzName, WindowMeta parent,...
method addChild (line 37) | public void addChild(String clazzName,String name,int index,WindowType...
method getClazz (line 45) | public Class<?> getClazz() {
method setClazz (line 49) | public WindowMeta setClazz(Class<?> clazz) {
method getClazzName (line 54) | public String getClazzName() {
method setClazzName (line 58) | public WindowMeta setClazzName(String clazzName) {
method getWindowType (line 63) | public WindowType getWindowType() {
method setWindowType (line 67) | public WindowMeta<T> setWindowType(WindowType windowType) {
method toString (line 72) | @Override
FILE: windowtree_compiler/src/main/java/com/kaixuan/compiler/WindowProcessor.java
class WindowProcessor (line 26) | @AutoService(Processor.class)
method init (line 49) | @Override
method process (line 59) | @Override
method parseWindows (line 141) | private Map<String, List<WindowMeta>> parseWindows(Set<? extends Eleme...
FILE: windowtree_library/src/androidTest/java/com/kaixuan/windowtreelibrary/ExampleInstrumentedTest.java
class ExampleInstrumentedTest (line 17) | @RunWith(AndroidJUnit4.class)
method useAppContext (line 19) | @Test
FILE: windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/template/ILogger.java
type ILogger (line 12) | public interface ILogger {
method showLog (line 18) | void showLog(boolean isShowLog);
method showStackTrace (line 20) | void showStackTrace(boolean isShowStackTrace);
method debug (line 22) | void debug(String tag, String message);
method info (line 24) | void info(String tag, String message);
method warning (line 26) | void warning(String tag, String message);
method error (line 28) | void error(String tag, String message);
method monitor (line 30) | void monitor(String message);
method isMonitorMode (line 32) | boolean isMonitorMode();
method getDefaultTag (line 34) | String getDefaultTag();
FILE: windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/template/IMain.java
type IMain (line 5) | public interface IMain {
method getAllGeneratedFile (line 7) | public Map<String, Class<? extends IWindowTreeLoad>> getAllGeneratedFi...
FILE: windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/template/IWindowTreeLoad.java
type IWindowTreeLoad (line 5) | public interface IWindowTreeLoad {
method loadWindowTree (line 7) | public void loadWindowTree(WindowInfo currentWindowInfo) throws ClassN...
FILE: windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/thread/DefaultPoolExecutor.java
class DefaultPoolExecutor (line 16) | public class DefaultPoolExecutor extends ThreadPoolExecutor {
method getInstance (line 25) | public static DefaultPoolExecutor getInstance() {
method DefaultPoolExecutor (line 42) | private DefaultPoolExecutor(int corePoolSize, int maximumPoolSize, lon...
method afterExecute (line 57) | @Override
FILE: windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/thread/DefaultThreadFactory.java
class DefaultThreadFactory (line 17) | public class DefaultThreadFactory implements ThreadFactory {
method DefaultThreadFactory (line 24) | public DefaultThreadFactory() {
method newThread (line 31) | public Thread newThread(@NonNull Runnable runnable) {
FILE: windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/util/ClassUtils.java
class ClassUtils (line 30) | public class ClassUtils {
method getMultiDexPreferences (line 42) | private static SharedPreferences getMultiDexPreferences(Context contex...
method getFileNameByPackageName (line 57) | public static Set<String> getFileNameByPackageName(Context context, fi...
method getSourcePaths (line 114) | public static List<String> getSourcePaths(Context context) throws Pack...
method tryLoadInstantRunDexFile (line 153) | private static List<String> tryLoadInstantRunDexFile(ApplicationInfo a...
method isVMMultidexCapable (line 192) | private static boolean isVMMultidexCapable() {
method isYunOS (line 229) | private static boolean isYunOS() {
FILE: windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/util/DefaultLogger.java
class DefaultLogger (line 14) | public class DefaultLogger implements ILogger {
method showLog (line 22) | public void showLog(boolean showLog) {
method showStackTrace (line 26) | public void showStackTrace(boolean showStackTrace) {
method showMonitor (line 30) | public void showMonitor(boolean showMonitor) {
method DefaultLogger (line 34) | public DefaultLogger() {
method DefaultLogger (line 37) | public DefaultLogger(String defaultTag) {
method debug (line 41) | @Override
method info (line 49) | @Override
method warning (line 57) | @Override
method error (line 65) | @Override
method monitor (line 73) | @Override
method isMonitorMode (line 81) | @Override
method getDefaultTag (line 86) | @Override
method getExtInfo (line 91) | public static String getExtInfo(StackTraceElement stackTraceElement) {
FILE: windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/util/TextUtils.java
class TextUtils (line 16) | public class TextUtils {
method isEmpty (line 18) | public static boolean isEmpty(final CharSequence cs) {
method formatStackTrace (line 25) | public static String formatStackTrace(StackTraceElement[] stackTrace) {
method splitQueryParameters (line 39) | public static Map<String, String> splitQueryParameters(Uri rawUri) {
method getLeft (line 77) | public static String getLeft(String key) {
method getRight (line 91) | public static String getRight(String key) {
method getRight (line 104) | public static String getRight(String key,String sub) {
FILE: windowtree_library/src/test/java/com/kaixuan/windowtreelibrary/ExampleUnitTest.java
class ExampleUnitTest (line 12) | public class ExampleUnitTest {
method addition_isCorrect (line 13) | @Test
Condensed preview — 79 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (136K chars).
[
{
"path": ".gitignore",
"chars": 176,
"preview": "*.iml\n.gradle\n/local.properties\n/.idea/caches/build_file_checksums.ser\n/.idea/libraries\n/.idea/modules.xml\n/.idea/worksp"
},
{
"path": ".idea/codeStyles/Project.xml",
"chars": 2033,
"preview": "<component name=\"ProjectCodeStyleConfiguration\">\n <code_scheme name=\"Project\" version=\"173\">\n <JetCodeStyleSettings>"
},
{
"path": ".idea/codeStyles/codeStyleConfig.xml",
"chars": 142,
"preview": "<component name=\"ProjectCodeStyleConfiguration\">\n <state>\n <option name=\"USE_PER_PROJECT_SETTINGS\" value=\"true\" />\n "
},
{
"path": ".idea/gradle.xml",
"chars": 822,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n <component name=\"GradleSettings\">\n <option name=\"linke"
},
{
"path": ".idea/misc.xml",
"chars": 2116,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n <component name=\"NullableNotNullManager\">\n <option nam"
},
{
"path": ".idea/runConfigurations.xml",
"chars": 564,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n <component name=\"RunConfigurationProducerService\">\n <o"
},
{
"path": ".idea/vcs.xml",
"chars": 180,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n <component name=\"VcsDirectoryMappings\">\n <mapping dire"
},
{
"path": "README.md",
"chars": 3453,
"preview": "# WindowTree\n只需使用注解,帮助你轻松维护树形界面的层级关系,管理你的界面结构,当你处于界面的任何位置时,都可以知道,我在哪里,我的父界面是谁,我的子界面是谁。甚至能够自动构建你的界面结构。\n\n## 我们经常会遇一些这样的问题:"
},
{
"path": "app/.gitignore",
"chars": 7,
"preview": "/build\n"
},
{
"path": "app/build.gradle",
"chars": 1426,
"preview": "apply plugin: 'com.android.application'\n\napply plugin: 'kotlin-android'\napply plugin: 'kotlin-kapt'\napply plugin: 'kotli"
},
{
"path": "app/proguard-rules.pro",
"chars": 751,
"preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar"
},
{
"path": "app/src/androidTest/java/com/kaixuan/windowtree/ExampleInstrumentedTest.kt",
"chars": 646,
"preview": "package com.kaixuan.windowtree\n\nimport android.support.test.InstrumentationRegistry\nimport android.support.test.runner.A"
},
{
"path": "app/src/main/AndroidManifest.xml",
"chars": 982,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n pa"
},
{
"path": "app/src/main/java/com/kaixuan/windowtree/KotlinCommon.kt",
"chars": 158,
"preview": "package com.kaixuan.windowtree\n\nimport android.widget.Toast\n\n\nfun showToast(msg : String){\n Toast.makeText(MyApp.inst"
},
{
"path": "app/src/main/java/com/kaixuan/windowtree/MainActivity.kt",
"chars": 6231,
"preview": "package com.kaixuan.windowtree\n\nimport android.os.Bundle\nimport android.support.design.widget.TabLayout\nimport android.t"
},
{
"path": "app/src/main/java/com/kaixuan/windowtree/MyApp.kt",
"chars": 250,
"preview": "package com.kaixuan.windowtree\n\nimport android.app.Application\n\nclass MyApp : Application() {\n\n companion object {\n "
},
{
"path": "app/src/main/java/com/kaixuan/windowtree/WindowTag.kt",
"chars": 123,
"preview": "package com.kaixuan.windowtree\n\n\nclass WindowTag(\n var unReadMsgCount : Int = 0,\n var pageAuthority : Long = 0\n){\n"
},
{
"path": "app/src/main/java/com/kaixuan/windowtree/activity/BaseActivity.kt",
"chars": 907,
"preview": "package com.kaixuan.windowtree.activity\n\nimport android.os.Bundle\nimport android.support.v7.app.AppCompatActivity\nimport"
},
{
"path": "app/src/main/java/com/kaixuan/windowtree/activity/EmptyActivity.kt",
"chars": 592,
"preview": "package com.kaixuan.windowtree.activity\n\nimport android.support.v7.app.AppCompatActivity\nimport android.os.Bundle\nimport"
},
{
"path": "app/src/main/java/com/kaixuan/windowtree/activity/NewsDetailActivity.kt",
"chars": 1333,
"preview": "package com.kaixuan.windowtree.activity\n\nimport android.os.Bundle\nimport com.kaixuan.windowtree.R\nimport com.kaixuan.win"
},
{
"path": "app/src/main/java/com/kaixuan/windowtree/activity/NewsListActivity.kt",
"chars": 2115,
"preview": "package com.kaixuan.windowtree.activity\n\nimport android.os.Bundle\nimport android.support.v7.recyclerview.extensions.List"
},
{
"path": "app/src/main/java/com/kaixuan/windowtree/fragment/ContactsFragment.kt",
"chars": 1463,
"preview": "package com.kaixuan.windowtree.fragment\n\nimport android.os.Bundle\nimport android.support.v4.app.Fragment\nimport android."
},
{
"path": "app/src/main/java/com/kaixuan/windowtree/fragment/DynamicFragment.kt",
"chars": 1425,
"preview": "package com.kaixuan.windowtree.fragment\n\nimport android.os.Bundle\nimport android.support.v4.app.Fragment\nimport android."
},
{
"path": "app/src/main/java/com/kaixuan/windowtree/fragment/MainFragment.kt",
"chars": 1618,
"preview": "package com.kaixuan.windowtree.fragment\n\nimport android.os.Bundle\nimport android.support.v4.app.Fragment\nimport android."
},
{
"path": "app/src/main/java/com/kaixuan/windowtree/fragment/VipFragment.kt",
"chars": 1466,
"preview": "package com.kaixuan.windowtree.fragment\n\nimport android.os.Bundle\nimport android.support.v4.app.Fragment\nimport android."
},
{
"path": "app/src/main/java/com/kaixuan/windowtree/fragment/dynamic/GoodFriendDynamicFragment.kt",
"chars": 974,
"preview": "package com.kaixuan.windowtree.fragment.dynamic\n\nimport android.os.Bundle\nimport android.support.v4.app.Fragment\nimport "
},
{
"path": "app/src/main/res/drawable/ic_launcher_background.xml",
"chars": 4887,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector\n xmlns:android=\"http://schemas.android.com/apk/res/android\"\n "
},
{
"path": "app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
"chars": 1969,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmlns:aapt=\"http://schemas.android.com/aapt\"\n"
},
{
"path": "app/src/main/res/layout/activity_main.xml",
"chars": 3313,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
},
{
"path": "app/src/main/res/layout/activity_news_detail.xml",
"chars": 1726,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
},
{
"path": "app/src/main/res/layout/activity_news_list.xml",
"chars": 691,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
},
{
"path": "app/src/main/res/layout/fragment_msg.xml",
"chars": 1180,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
},
{
"path": "app/src/main/res/layout/fragment_test.xml",
"chars": 1688,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
},
{
"path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
"chars": 270,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <b"
},
{
"path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
"chars": 270,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <b"
},
{
"path": "app/src/main/res/values/colors.xml",
"chars": 208,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <color name=\"colorPrimary\">#008577</color>\n <color name=\"color"
},
{
"path": "app/src/main/res/values/strings.xml",
"chars": 73,
"preview": "<resources>\n <string name=\"app_name\">WindowTree</string>\n</resources>\n"
},
{
"path": "app/src/main/res/values/styles.xml",
"chars": 383,
"preview": "<resources>\n\n <!-- Base application theme. -->\n <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar"
},
{
"path": "app/src/test/java/com/kaixuan/windowtree/ExampleUnitTest.kt",
"chars": 347,
"preview": "package com.kaixuan.windowtree\n\nimport org.junit.Test\n\nimport org.junit.Assert.*\n\n/**\n * Example local unit test, which "
},
{
"path": "build.gradle",
"chars": 1172,
"preview": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n e"
},
{
"path": "gradle/wrapper/gradle-wrapper.properties",
"chars": 200,
"preview": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributi"
},
{
"path": "gradle.properties",
"chars": 817,
"preview": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will ov"
},
{
"path": "gradlew",
"chars": 5296,
"preview": "#!/usr/bin/env sh\n\n##############################################################################\n##\n## Gradle start up"
},
{
"path": "gradlew.bat",
"chars": 2176,
"preview": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem "
},
{
"path": "settings.gradle",
"chars": 88,
"preview": "include ':app', ':windowtree_library', ':windowtree_compiler', ':windowtree_annotation'\n"
},
{
"path": "windowtree_annotation/.gitignore",
"chars": 7,
"preview": "/build\n"
},
{
"path": "windowtree_annotation/build.gradle",
"chars": 609,
"preview": "apply plugin: 'java-library'\napply plugin: 'com.novoda.bintray-release'\ndependencies {\n implementation fileTree(dir: "
},
{
"path": "windowtree_annotation/src/main/java/com/kaixuan/windowtree_annotation/MyClass.java",
"chars": 69,
"preview": "package com.kaixuan.windowtree_annotation;\n\npublic class MyClass {\n}\n"
},
{
"path": "windowtree_annotation/src/main/java/com/kaixuan/windowtree_annotation/annotation/Window.java",
"chars": 891,
"preview": "package com.kaixuan.windowtree_annotation.annotation;\n\nimport com.kaixuan.windowtree_annotation.model.WindowData;\n\nimpor"
},
{
"path": "windowtree_annotation/src/main/java/com/kaixuan/windowtree_annotation/annotation/WindowTypeAnnotation.java",
"chars": 99,
"preview": "package com.kaixuan.windowtree_annotation.annotation;\n\n\npublic @interface WindowTypeAnnotation {\n}\n"
},
{
"path": "windowtree_annotation/src/main/java/com/kaixuan/windowtree_annotation/enums/WindowType.java",
"chars": 1102,
"preview": "package com.kaixuan.windowtree_annotation.enums;\n\npublic enum WindowType {\n\n ACTIVITY(0, \"android.app.Activity\"),\n "
},
{
"path": "windowtree_annotation/src/main/java/com/kaixuan/windowtree_annotation/model/WindowData.java",
"chars": 78,
"preview": "package com.kaixuan.windowtree_annotation.model;\n\npublic class WindowData {\n}\n"
},
{
"path": "windowtree_annotation/src/main/java/com/kaixuan/windowtree_annotation/model/WindowMeta.java",
"chars": 2455,
"preview": "package com.kaixuan.windowtree_annotation.model;\n\n\nimport com.kaixuan.windowtree_annotation.enums.WindowType;\n\nimport ja"
},
{
"path": "windowtree_compiler/.gitignore",
"chars": 7,
"preview": "/build\n"
},
{
"path": "windowtree_compiler/build.gradle",
"chars": 801,
"preview": "apply plugin: 'java-library'\napply plugin:'java'\napply plugin: 'com.novoda.bintray-release'\ndependencies {\n implement"
},
{
"path": "windowtree_compiler/src/main/java/com/kaixuan/compiler/WindowProcessor.java",
"chars": 9914,
"preview": "package com.kaixuan.compiler;\n\n\nimport com.google.auto.service.AutoService;\n\nimport javax.annotation.processing.*;\nimpor"
},
{
"path": "windowtree_library/.gitignore",
"chars": 7,
"preview": "/build\n"
},
{
"path": "windowtree_library/build.gradle",
"chars": 1450,
"preview": "apply plugin: 'com.android.library'\napply plugin: 'kotlin-android'\napply plugin: 'com.novoda.bintray-release'\n\nandroid {"
},
{
"path": "windowtree_library/proguard-rules.pro",
"chars": 751,
"preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar"
},
{
"path": "windowtree_library/src/androidTest/java/com/kaixuan/windowtreelibrary/ExampleInstrumentedTest.java",
"chars": 747,
"preview": "package com.kaixuan.windowtreelibrary;\n\nimport android.content.Context;\nimport android.support.test.InstrumentationRegis"
},
{
"path": "windowtree_library/src/main/AndroidManifest.xml",
"chars": 121,
"preview": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package=\"com.kaixuan.windowtreelibrary\"/>"
},
{
"path": "windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/WindowInfo.kt",
"chars": 8315,
"preview": "package com.kaixuan.windowtreelibrary\n\n\nimport android.content.Context\nimport android.os.Bundle\nimport com.kaixuan.windo"
},
{
"path": "windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/WindowTree.kt",
"chars": 5549,
"preview": "package com.kaixuan.windowtreelibrary\n\nimport android.app.Activity\nimport android.app.Application\nimport android.app.Dia"
},
{
"path": "windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/adapter/DefaultJumpAdapter.kt",
"chars": 2449,
"preview": "package com.kaixuan.windowtreelibrary.adapter\n\nimport android.app.Activity\nimport android.content.Context\nimport android"
},
{
"path": "windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/model/UnReadCountEvent.kt",
"chars": 173,
"preview": "package com.kaixuan.windowtreelibrary.model\n\nimport com.kaixuan.windowtreelibrary.WindowInfo\n\ndata class UnReadCountEven"
},
{
"path": "windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/template/IJumpAdapter.kt",
"chars": 219,
"preview": "package com.kaixuan.windowtreelibrary.template\n\nimport android.content.Context\nimport com.kaixuan.windowtreelibrary.Wind"
},
{
"path": "windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/template/ILogger.java",
"chars": 732,
"preview": "package com.kaixuan.windowtreelibrary.template;\n\nimport com.kaixuan.windowtreelibrary.util.Consts;\n\n/**\n * Logger\n *\n * "
},
{
"path": "windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/template/IMain.java",
"chars": 179,
"preview": "package com.kaixuan.windowtreelibrary.template;\n\nimport java.util.Map;\n\npublic interface IMain {\n\n public Map<String, "
},
{
"path": "windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/template/IWindowTreeLoad.java",
"chars": 228,
"preview": "package com.kaixuan.windowtreelibrary.template;\n\nimport com.kaixuan.windowtreelibrary.WindowInfo;\n\npublic interface IWin"
},
{
"path": "windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/thread/DefaultPoolExecutor.java",
"chars": 2871,
"preview": "package com.kaixuan.windowtreelibrary.thread;\n\nimport com.kaixuan.windowtreelibrary.WindowTree;\nimport com.kaixuan.windo"
},
{
"path": "windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/thread/DefaultThreadFactory.java",
"chars": 1943,
"preview": "package com.kaixuan.windowtreelibrary.thread;\n\nimport android.support.annotation.NonNull;\nimport com.kaixuan.windowtreel"
},
{
"path": "windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/util/ClassUtils.java",
"chars": 10215,
"preview": "package com.kaixuan.windowtreelibrary.util;\n\n// Copy from galaxy sdk ${com.alibaba.android.galaxy.utils.ClassUtils}\n\nimp"
},
{
"path": "windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/util/Consts.kt",
"chars": 248,
"preview": "package com.kaixuan.windowtreelibrary.util\n\nclass Consts{\n companion object {\n @JvmField\n val TAG = \"Wi"
},
{
"path": "windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/util/DefaultLogger.java",
"chars": 3742,
"preview": "package com.kaixuan.windowtreelibrary.util;\n\nimport android.text.TextUtils;\nimport android.util.Log;\nimport com.kaixuan."
},
{
"path": "windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/util/TextUtils.java",
"chars": 2893,
"preview": "package com.kaixuan.windowtreelibrary.util;\n\nimport android.net.Uri;\n\nimport java.util.Collections;\nimport java.util.Lin"
},
{
"path": "windowtree_library/src/main/java/com/kaixuan/windowtreelibrary/util/WindowTreeUtil.kt",
"chars": 1115,
"preview": "package com.kaixuan.windowtreelibrary.util\n\nimport android.content.Context\nimport com.kaixuan.windowtreelibrary.WindowIn"
},
{
"path": "windowtree_library/src/main/res/values/strings.xml",
"chars": 80,
"preview": "<resources>\n <string name=\"app_name\">WindowTreeLibrary</string>\n</resources>\n"
},
{
"path": "windowtree_library/src/test/java/com/kaixuan/windowtreelibrary/ExampleUnitTest.java",
"chars": 390,
"preview": "package com.kaixuan.windowtreelibrary;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * Example local "
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the KaiXuan666/WindowTree GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 79 files (116.4 KB), approximately 32.0k tokens, and a symbol index with 78 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.