├── .github └── workflows │ └── gradle.yml ├── .gitignore ├── .idea ├── .gitignore ├── .name ├── compiler.xml ├── deploymentTargetSelector.xml ├── gradle.xml ├── inspectionProfiles │ └── Project_Default.xml ├── migrations.xml ├── misc.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── dark │ │ └── androidbox │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ └── Main.java │ ├── java │ │ └── com │ │ │ └── dark │ │ │ └── androidbox │ │ │ ├── Lexer.java │ │ │ ├── MainActivity.java │ │ │ ├── adapter │ │ │ ├── NodeAdderAdapter.java │ │ │ ├── NodeSelector.java │ │ │ ├── NodeViewAdapter.java │ │ │ └── SnippetAdapter.java │ │ │ ├── codeView │ │ │ └── Editor.java │ │ │ ├── fragments │ │ │ ├── EditorFragment.java │ │ │ └── TerminalFragment.java │ │ │ ├── helper │ │ │ └── StoragePermissionHelper.java │ │ │ ├── model │ │ │ └── NodeData.java │ │ │ ├── nodes │ │ │ ├── BaseNode.java │ │ │ ├── ClassNode.java │ │ │ └── MethodNode.java │ │ │ └── types │ │ │ └── NodeTypes.java │ └── res │ │ ├── anim │ │ ├── slide_in.xml │ │ └── slide_out.xml │ │ ├── drawable │ │ ├── bg_list.xml │ │ ├── bg_node_list_view.xml │ │ ├── bg_node_view.xml │ │ ├── bg_node_view_bottom.xml │ │ ├── bg_node_view_top.xml │ │ ├── drop_down.png │ │ ├── ic_launcher_background.xml │ │ ├── ic_launcher_foreground.xml │ │ └── settings.png │ │ ├── font │ │ └── jaldi.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── base_node.xml │ │ ├── class_node.xml │ │ ├── fragment_editor.xml │ │ ├── method_node.xml │ │ ├── methods_list.xml │ │ ├── node.xml │ │ ├── nodeview.xml │ │ └── terminal_fragment.xml │ │ ├── mipmap-anydpi │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── font_certs.xml │ │ ├── integers.xml │ │ ├── preloaded_fonts.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ ├── backup_rules.xml │ │ └── data_extraction_rules.xml │ └── test │ └── java │ └── com │ └── dark │ └── androidbox │ └── ExampleUnitTest.java ├── build.gradle ├── codeview ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── amrdeveloper │ │ └── codeview │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ └── java │ │ └── com │ │ └── amrdeveloper │ │ └── codeview │ │ ├── Code.java │ │ ├── CodeView.java │ │ ├── CodeViewAdapter.java │ │ ├── Findable.java │ │ ├── Keyword.java │ │ ├── KeywordTokenizer.java │ │ ├── Replaceable.java │ │ ├── Snippet.java │ │ └── Token.java │ └── test │ └── java │ └── com │ └── amrdeveloper │ └── codeview │ └── ExampleUnitTest.java ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── node ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── gyso │ │ └── treeview │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── gyso │ │ │ └── treeview │ │ │ ├── GysoTreeView.java │ │ │ ├── TreeViewContainer.java │ │ │ ├── TreeViewEditor.java │ │ │ ├── adapter │ │ │ ├── DrawInfo.java │ │ │ ├── TreeViewAdapter.java │ │ │ └── TreeViewHolder.java │ │ │ ├── algorithm │ │ │ ├── force │ │ │ │ ├── FLink.java │ │ │ │ ├── FNode.java │ │ │ │ ├── Force.java │ │ │ │ ├── ForceListener.java │ │ │ │ ├── ForceView.java │ │ │ │ └── QuadTree.java │ │ │ ├── ring │ │ │ │ ├── Ring.java │ │ │ │ ├── RingForCompact.java │ │ │ │ └── RingForSimple.java │ │ │ ├── table │ │ │ │ ├── Table.java │ │ │ │ └── TableKey.java │ │ │ └── tight_table │ │ │ │ └── TightTable.java │ │ │ ├── cache_pool │ │ │ ├── HolderPool.java │ │ │ └── PointPool.java │ │ │ ├── layout │ │ │ ├── BoxDownTreeLayoutManager.java │ │ │ ├── BoxHorizonLeftAndRightLayoutManager.java │ │ │ ├── BoxLeftTreeLayoutManager.java │ │ │ ├── BoxRightTreeLayoutManager.java │ │ │ ├── BoxUpTreeLayoutManager.java │ │ │ ├── BoxVerticalUpAndDownLayoutManager.java │ │ │ ├── CompactDownTreeLayoutManager.java │ │ │ ├── CompactHorizonLeftAndRightLayoutManager.java │ │ │ ├── CompactLeftTreeLayoutManager.java │ │ │ ├── CompactRightTreeLayoutManager.java │ │ │ ├── CompactRingTreeLayoutManager.java │ │ │ ├── CompactUpTreeLayoutManager.java │ │ │ ├── CompactVerticalUpAndDownLayoutManager.java │ │ │ ├── ForceDirectedTreeLayoutManager.java │ │ │ ├── SimpleRingTreeLayoutManager.java │ │ │ ├── TableDownTreeLayoutManager.java │ │ │ ├── TableHorizonLeftAndRightLayoutManager.java │ │ │ ├── TableLeftTreeLayoutManager.java │ │ │ ├── TableRightTreeLayoutManager.java │ │ │ ├── TableUpTreeLayoutManagerTable.java │ │ │ ├── TableVerticalUpAndTableDownLayoutManager.java │ │ │ └── TreeLayoutManager.java │ │ │ ├── line │ │ │ ├── AngledLine.java │ │ │ ├── BaseLine.java │ │ │ ├── DashLine.java │ │ │ ├── SmoothLine.java │ │ │ └── StraightLine.java │ │ │ ├── listener │ │ │ ├── TreeViewControlListener.java │ │ │ ├── TreeViewItemClick.java │ │ │ ├── TreeViewItemLongClick.java │ │ │ └── TreeViewNotifier.java │ │ │ ├── model │ │ │ ├── ITraversal.java │ │ │ ├── NodeModel.java │ │ │ └── TreeModel.java │ │ │ ├── onNodeEvents.java │ │ │ ├── touch │ │ │ ├── DragBlock.java │ │ │ ├── TouchEventHandler.java │ │ │ └── onDoubleTapListener.java │ │ │ └── util │ │ │ ├── DensityUtils.java │ │ │ ├── Interpolators.java │ │ │ ├── TreeViewLog.java │ │ │ └── ViewBox.java │ └── res │ │ └── values │ │ └── values.xml │ └── test │ └── java │ └── com │ └── gyso │ └── treeview │ └── ExampleUnitTest.java └── settings.gradle /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | name: Java CI with Gradle 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | permissions: 14 | contents: read 15 | 16 | steps: 17 | - uses: actions/checkout@v4 18 | - name: Set up JDK 17 19 | uses: actions/setup-java@v4 20 | with: 21 | java-version: '17' 22 | distribution: 'temurin' 23 | 24 | # Setup Gradle 25 | - name: Setup Gradle 26 | uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 27 | 28 | # Build Debug APK 29 | - name: Build Debug APK 30 | run: ./gradlew assembleDebug 31 | 32 | # Build Release APK 33 | - name: Build Release APK 34 | run: ./gradlew assembleRelease 35 | 36 | # Archive Debug APK 37 | - name: Upload Debug APK 38 | uses: actions/upload-artifact@v3 39 | with: 40 | name: app-debug.apk 41 | path: app/build/outputs/apk/debug/app-debug.apk 42 | 43 | # Archive Release APK 44 | - name: Upload Release APK 45 | uses: actions/upload-artifact@v3 46 | with: 47 | name: app-release.apk 48 | path: app/build/outputs/apk/release/app-release.apk 49 | 50 | dependency-submission: 51 | 52 | runs-on: ubuntu-latest 53 | permissions: 54 | contents: write 55 | 56 | steps: 57 | - uses: actions/checkout@v4 58 | - name: Set up JDK 17 59 | uses: actions/setup-java@v4 60 | with: 61 | java-version: '17' 62 | distribution: 'temurin' 63 | 64 | # Generates and submits a dependency graph, enabling Dependabot Alerts for all project dependencies. 65 | - name: Generate and submit dependency graph 66 | uses: gradle/actions/dependency-submission@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 67 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | AndroidBox -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/deploymentTargetSelector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 20 | 21 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | -------------------------------------------------------------------------------- /.idea/migrations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AndroidBox - A Node-Based IDE 2 | 3 | AndroidBox is an innovative IDE that utilizes a node-based editor concept, allowing for dynamic conversion between nodes and text code. It aims to provide an enhanced coding experience by combining the best features of block-based and text-based coding methods. 4 | ## See The Progress 5 | - **LinkedIn** : [Siddhesh Soanr](https://www.linkedin.com/in/siddhesh-sonar-7840a7260/) 6 | ## Key Features 7 | 8 | - **Node-to-Text Conversion**: Convert nodes to text code at runtime. 9 | - **Text-to-Node Conversion**: Seamlessly convert text code back into nodes. 10 | - **Enhanced Functionality**: Unlike Sketchware, AndroidBox supports text-to-block conversion and offers a versatile coding environment. 11 | 12 | ## Why AndroidBox? 13 | 14 | While tools like Sketchware and Sketchware Pro offer a block-based approach to coding, they lack the ability to convert text into blocks. AndroidBox addresses this limitation by providing: 15 | 16 | 1. Conversion between text code and nodes. 17 | 2. Integration of both block-based and text-based coding methods. 18 | 19 | ## Future Plans 20 | 21 | AndroidBox will include a plugin section where users can download compilers, themes, icons, and syntax highlighters similar to those available in VS Code. 22 | 23 | ## Build Information 24 | 25 | You can view the current build and updates at [AndroidBox GitHub Actions](https://github.com/Siddhesh2377/AndroidBox/actions). 26 | 27 | ## Design Preview 28 | 29 | ![Design Preview](https://user-images.githubusercontent.com/67579112/228865415-c6c67ebc-e862-4c76-9454-ffa3f5f23c05.svg) 30 | 31 | ## Services 32 | 33 | - **Java Code Parser** 34 | - **Local Path**: Available in the app's `build.gradle` 35 | - **Credits**: [JavaParser](https://github.com/javaparser/javaparser) 36 | 37 | - **Custom Views** 38 | - **Node View** 39 | - **Local Path**: `AndroidBox/node` 40 | - **Credits**: [android-thinkmap-treeview](https://github.com/guaishouN/android-thinkmap-treeview) 41 | 42 | - **Code View** 43 | - **Local Path**: `AndroidBox/codeview` 44 | - **Credits**: [Code View](https://amrdeveloper.github.io/CodeView/)) 45 | 46 | --- 47 | 48 | Feel free to make any additional modifications based on your specific needs or project updates!, But Give me credit Thoo ;) 49 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | alias(libs.plugins.android.application) 3 | } 4 | 5 | android { 6 | namespace 'com.dark.androidbox' 7 | compileSdk 34 8 | 9 | defaultConfig { 10 | applicationId "com.dark.androidbox" 11 | minSdk 27 12 | targetSdk 34 13 | versionCode 1 14 | versionName "1.0" 15 | 16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | compileOptions { 26 | sourceCompatibility JavaVersion.VERSION_1_8 27 | targetCompatibility JavaVersion.VERSION_1_8 28 | } 29 | 30 | buildFeatures { 31 | dataBinding = true 32 | viewBinding = true 33 | } 34 | } 35 | 36 | dependencies { 37 | 38 | implementation libs.javaparser.core 39 | 40 | implementation libs.janino 41 | 42 | implementation project(":node") 43 | implementation project(":codeview") 44 | 45 | implementation(libs.android.code.view) { 46 | exclude group: 'com.android.support', module: 'support-compat' 47 | } 48 | 49 | implementation libs.appcompat 50 | implementation libs.material 51 | implementation libs.activity 52 | implementation libs.constraintlayout 53 | testImplementation libs.junit 54 | androidTestImplementation libs.ext.junit 55 | androidTestImplementation libs.espresso.core 56 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/androidTest/java/com/dark/androidbox/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.dark.androidbox; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import android.content.Context; 6 | 7 | import androidx.test.ext.junit.runners.AndroidJUnit4; 8 | import androidx.test.platform.app.InstrumentationRegistry; 9 | 10 | import org.junit.Test; 11 | import org.junit.runner.RunWith; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | assertEquals("com.dark.androidbox", appContext.getPackageName()); 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 11 | 12 | 22 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/assets/Main.java: -------------------------------------------------------------------------------- 1 | public class Main{ 2 | public static void main(String[] args) { 3 | System.out.println("Hello World!"); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/dark/androidbox/Lexer.java: -------------------------------------------------------------------------------- 1 | package com.dark.androidbox; 2 | 3 | import com.github.javaparser.JavaParser; 4 | import com.github.javaparser.ast.CompilationUnit; 5 | import com.github.javaparser.ast.Modifier; 6 | import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; 7 | import com.github.javaparser.ast.body.FieldDeclaration; 8 | import com.github.javaparser.ast.body.MethodDeclaration; 9 | import com.github.javaparser.ast.body.Parameter; 10 | import com.github.javaparser.ast.expr.MethodCallExpr; 11 | import com.github.javaparser.ast.expr.VariableDeclarationExpr; 12 | import com.github.javaparser.ast.stmt.BlockStmt; 13 | import com.github.javaparser.ast.stmt.ExpressionStmt; 14 | 15 | import java.io.StringReader; 16 | import java.util.List; 17 | import java.util.Optional; 18 | 19 | public class Lexer { 20 | 21 | public CompilationUnit unit; 22 | public JavaParser parser; 23 | 24 | public Lexer(StringBuilder code) { 25 | parser = new JavaParser(); 26 | unit = parser.parse(new StringReader(code.toString())).getResult().orElse(null); 27 | } 28 | 29 | public List getClasses() { 30 | return unit.findAll(ClassOrInterfaceDeclaration.class); 31 | } 32 | 33 | public List getMethods() { 34 | return unit.findAll(MethodDeclaration.class); 35 | } 36 | 37 | public List getFields() { 38 | return unit.findAll(FieldDeclaration.class); 39 | } 40 | 41 | public List getMethodCalls() { 42 | return unit.findAll(MethodCallExpr.class); 43 | } 44 | 45 | public void addClass(String className, String extendedClass, List implementedInterfaces) { 46 | // Create a new class 47 | ClassOrInterfaceDeclaration newClass = unit.addClass(className); 48 | 49 | // Add the extended class (parent class) if provided 50 | if (extendedClass != null && !extendedClass.isEmpty()) { 51 | newClass.addExtendedType(extendedClass); // Add the "extends" clause 52 | } 53 | 54 | // Add implemented interfaces if provided 55 | if (implementedInterfaces != null && !implementedInterfaces.isEmpty()) { 56 | for (String interfaceName : implementedInterfaces) { 57 | newClass.addImplementedType(interfaceName); // Add the "implements" clause 58 | } 59 | } 60 | } 61 | 62 | 63 | // Add a field to a specific class 64 | public void addFieldToClass(String className, String fieldType, String fieldName) { 65 | Optional classOpt = unit.findAll(ClassOrInterfaceDeclaration.class).stream() 66 | .filter(c -> c.getNameAsString().equals(className)) 67 | .findFirst(); 68 | 69 | classOpt.ifPresent(classDeclaration -> { 70 | FieldDeclaration newField = classDeclaration.addField(fieldType, fieldName, Modifier.Keyword.PRIVATE); 71 | classDeclaration.addMember(newField); // Add the field to the class 72 | }); 73 | } 74 | 75 | // Add a method to a specific class 76 | public void addMethodToClass(String className, String methodName, String returnType) { 77 | Optional classOpt = unit.findAll(ClassOrInterfaceDeclaration.class).stream() 78 | .filter(c -> c.getNameAsString().equals(className)) 79 | .findFirst(); 80 | 81 | classOpt.ifPresent(classDeclaration -> { 82 | MethodDeclaration newMethod = classDeclaration.addMethod(methodName, Modifier.Keyword.PUBLIC); 83 | newMethod.setType(returnType); // Set the return type 84 | newMethod.setBody(new BlockStmt()); // Add an empty body 85 | classDeclaration.addMember(newMethod); // Add the method to the class 86 | }); 87 | } 88 | 89 | // Add local variables to a method body 90 | public void addLocalVariableToMethod(String methodName, String varType, String varName) { 91 | Optional methodOpt = unit.findAll(MethodDeclaration.class).stream() 92 | .filter(method -> method.getNameAsString().equals(methodName)) 93 | .findFirst(); 94 | 95 | methodOpt.ifPresent(method -> { 96 | VariableDeclarationExpr varDecl = new VariableDeclarationExpr(parser.parseParameter(varType).getResult().get().getType(), varName); 97 | ExpressionStmt varStmt = new ExpressionStmt(varDecl); // Create a statement from the variable declaration 98 | method.getBody().ifPresent(body -> body.addStatement(varStmt)); // Add the variable declaration to the method body 99 | }); 100 | } 101 | 102 | // Add parameters to a method signature 103 | public void addParameterToMethod(String className, String methodName, String paramType, String paramName) { 104 | Optional methodOpt = unit.findAll(MethodDeclaration.class).stream() 105 | .filter(method -> method.getNameAsString().equals(methodName)) 106 | .findFirst(); 107 | 108 | methodOpt.ifPresent(method -> { 109 | Parameter parameter = new Parameter(parser.parseParameter(paramType).getResult().get().getType(), paramName); 110 | method.addParameter(parameter); // Add the parameter to the method 111 | }); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /app/src/main/java/com/dark/androidbox/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.dark.androidbox; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.widget.Toast; 7 | 8 | import androidx.activity.result.ActivityResultLauncher; 9 | import androidx.activity.result.contract.ActivityResultContracts; 10 | import androidx.appcompat.app.AppCompatActivity; 11 | 12 | import com.dark.androidbox.databinding.ActivityMainBinding; 13 | import com.dark.androidbox.fragments.EditorFragment; 14 | import com.dark.androidbox.fragments.TerminalFragment; 15 | import com.dark.androidbox.helper.StoragePermissionHelper; 16 | 17 | @SuppressLint("SetTextI18n") 18 | public class MainActivity extends AppCompatActivity { 19 | 20 | private ActivityMainBinding binding; 21 | private StoragePermissionHelper storagePermissionHelper; 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | // Inflate layout using ViewBinding 27 | binding = ActivityMainBinding.inflate(getLayoutInflater()); 28 | setContentView(binding.getRoot()); 29 | 30 | // Check and manage storage permissions 31 | handleStoragePermissions(); 32 | } 33 | 34 | /** 35 | * Method to manage storage permissions and initialize storage-related operations. 36 | */ 37 | private void handleStoragePermissions() { 38 | // Launcher for requesting multiple permissions 39 | ActivityResultLauncher requestPermissionsLauncher = registerForActivityResult( 40 | new ActivityResultContracts.RequestMultiplePermissions(), 41 | result -> { 42 | boolean[] grantResults = new boolean[result.size()]; 43 | int i = 0; 44 | for (Boolean granted : result.values()) { 45 | grantResults[i++] = granted; 46 | } 47 | storagePermissionHelper.handlePermissionsResult(); 48 | }); 49 | 50 | // Launcher for managing all files access (Android 11+) 51 | ActivityResultLauncher manageAllFilesAccessLauncher = registerForActivityResult( 52 | new ActivityResultContracts.StartActivityForResult(), 53 | result -> storagePermissionHelper.handleManageFilesPermissionResult() 54 | ); 55 | 56 | // Initialize StoragePermissionHelper with appropriate callbacks 57 | storagePermissionHelper = new StoragePermissionHelper( 58 | this, 59 | new StoragePermissionHelper.PermissionCallback() { 60 | @Override 61 | public void onPermissionGranted() { 62 | processFiles(); 63 | } 64 | 65 | @Override 66 | public void onPermissionDenied() { 67 | Toast.makeText(MainActivity.this, "Allow permission for storage access!", Toast.LENGTH_SHORT).show(); 68 | } 69 | }, 70 | requestPermissionsLauncher, 71 | manageAllFilesAccessLauncher 72 | ); 73 | 74 | // Check and request permissions 75 | storagePermissionHelper.checkAndRequestPermission(); 76 | } 77 | 78 | /** 79 | * Initialize EditorFragment after permission handling. 80 | */ 81 | private void initEditorFragment() { 82 | getSupportFragmentManager().beginTransaction() 83 | .add(R.id.frame, new EditorFragment()) 84 | .commit(); 85 | } 86 | 87 | /** 88 | * Process files after storage permission is granted. 89 | */ 90 | private void processFiles() { 91 | initEditorFragment(); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /app/src/main/java/com/dark/androidbox/adapter/NodeAdderAdapter.java: -------------------------------------------------------------------------------- 1 | package com.dark.androidbox.adapter; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.graphics.Color; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.BaseAdapter; 10 | 11 | import com.dark.androidbox.R; 12 | import com.dark.androidbox.databinding.MethodsListBinding; 13 | import com.dark.androidbox.model.NodeData; 14 | import com.gyso.treeview.model.NodeModel; 15 | 16 | import java.util.List; 17 | 18 | public class NodeAdderAdapter extends BaseAdapter { 19 | 20 | private final List> data; 21 | private final LayoutInflater inflater; 22 | 23 | public NodeAdderAdapter(Context context, List> data) { 24 | this.data = data; 25 | this.inflater = LayoutInflater.from(context); 26 | } 27 | 28 | @Override 29 | public int getCount() { 30 | return data.size(); 31 | } 32 | 33 | @Override 34 | public Object getItem(int i) { 35 | return data.get(i); 36 | } 37 | 38 | @Override 39 | public long getItemId(int i) { 40 | return i; 41 | } 42 | 43 | @SuppressLint("ViewHolder") 44 | @Override 45 | public View getView(int i, View convertView, ViewGroup viewGroup) { 46 | ViewHolder holder; 47 | if (convertView == null) { 48 | MethodsListBinding binding = MethodsListBinding.inflate(inflater, viewGroup, false); 49 | holder = new ViewHolder(binding); 50 | convertView = binding.getRoot(); 51 | convertView.setTag(holder); 52 | } else { 53 | holder = (ViewHolder) convertView.getTag(); 54 | } 55 | convertView.setBackgroundResource(R.drawable.bg_node_list_view); 56 | if (data.get(i).value.title.equals("Var")) { 57 | holder.binding.title.setTextColor(Color.parseColor("#ee596e")); 58 | } else if (data.get(i).value.title.equals("Methods")) { 59 | holder.binding.title.setTextColor(Color.parseColor("#E49D33")); 60 | } else { 61 | holder.binding.title.setTextColor(Color.DKGRAY); 62 | } 63 | holder.binding.title.setText(data.get(i).value.title); 64 | 65 | return convertView; 66 | } 67 | 68 | static class ViewHolder { 69 | MethodsListBinding binding; 70 | 71 | ViewHolder(MethodsListBinding binding) { 72 | this.binding = binding; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /app/src/main/java/com/dark/androidbox/adapter/NodeSelector.java: -------------------------------------------------------------------------------- 1 | package com.dark.androidbox.adapter; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.graphics.Color; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.BaseAdapter; 10 | 11 | import com.dark.androidbox.R; 12 | import com.dark.androidbox.databinding.MethodsListBinding; 13 | import com.dark.androidbox.model.NodeData; 14 | import com.gyso.treeview.model.NodeModel; 15 | 16 | import java.util.List; 17 | 18 | public class NodeSelector extends BaseAdapter { 19 | 20 | private final List> data; 21 | private final Context context; 22 | private final LayoutInflater inflater; 23 | 24 | public NodeSelector(Context context, List> data) { 25 | this.context = context; 26 | this.data = data; 27 | this.inflater = LayoutInflater.from(context); 28 | } 29 | 30 | @Override 31 | public int getCount() { 32 | return data.size(); 33 | } 34 | 35 | @Override 36 | public Object getItem(int i) { 37 | return data.get(i); 38 | } 39 | 40 | @Override 41 | public long getItemId(int i) { 42 | return i; 43 | } 44 | 45 | @SuppressLint("ViewHolder") 46 | @Override 47 | public View getView(int i, View convertView, ViewGroup viewGroup) { 48 | ViewHolder holder; 49 | if (convertView == null) { 50 | MethodsListBinding binding = MethodsListBinding.inflate(inflater, viewGroup, false); 51 | holder = new ViewHolder(binding); 52 | convertView = binding.getRoot(); 53 | convertView.setTag(holder); 54 | } else { 55 | holder = (ViewHolder) convertView.getTag(); 56 | } 57 | convertView.setBackgroundResource(R.drawable.bg_node_list_view); 58 | if (data.get(i).value.title.equals("Var")) 59 | holder.binding.title.setTextColor(Color.parseColor("#ee596e")); 60 | else if (data.get(i).value.title.equals("Methods")) 61 | holder.binding.title.setTextColor(Color.parseColor("#E49D33")); 62 | else holder.binding.title.setTextColor(Color.DKGRAY); 63 | holder.binding.title.setText(data.get(i).value.title); 64 | return convertView; 65 | } 66 | 67 | static class ViewHolder { 68 | MethodsListBinding binding; 69 | 70 | ViewHolder(MethodsListBinding binding) { 71 | this.binding = binding; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /app/src/main/java/com/dark/androidbox/adapter/NodeViewAdapter.java: -------------------------------------------------------------------------------- 1 | package com.dark.androidbox.adapter; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | 8 | import androidx.annotation.NonNull; 9 | 10 | import com.dark.androidbox.databinding.NodeviewBinding; 11 | import com.dark.androidbox.model.NodeData; 12 | import com.dark.androidbox.types.NodeTypes; 13 | import com.gyso.treeview.adapter.DrawInfo; 14 | import com.gyso.treeview.adapter.TreeViewAdapter; 15 | import com.gyso.treeview.adapter.TreeViewHolder; 16 | import com.gyso.treeview.line.BaseLine; 17 | import com.gyso.treeview.model.NodeModel; 18 | 19 | import java.util.Locale; 20 | 21 | 22 | public class NodeViewAdapter extends TreeViewAdapter { 23 | 24 | private int nextId = 0; 25 | 26 | @Override 27 | public TreeViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, NodeModel model) { 28 | return new TreeViewHolder<>(NodeviewBinding.inflate(LayoutInflater.from(viewGroup.getContext()), viewGroup, false).getRoot(), model); 29 | } 30 | 31 | @SuppressLint("SetTextI18n") 32 | @Override 33 | public void onBindViewHolder(@NonNull TreeViewHolder holder) { 34 | View root = holder.getView(); 35 | NodeModel data = holder.getNode(); 36 | NodeviewBinding binding = NodeviewBinding.bind(root); 37 | data.value.nodeId = nextId++; 38 | binding.mainView.removeAllViews(); 39 | binding.mainView.addView(data.value.node.getNode(data)); 40 | String originalText = data.value.types.toString().toLowerCase(Locale.ROOT); 41 | String capitalizedText = originalText.substring(0, 1).toUpperCase(Locale.ROOT) + originalText.substring(1); 42 | if (data.value.types == NodeTypes.CLASS) binding.title.setText(data.value.title); 43 | else binding.title.setText(capitalizedText); 44 | binding.id.setText("Node ID: " + data.value.nodeId); 45 | } 46 | 47 | @Override 48 | public BaseLine onDrawLine(DrawInfo drawInfo) { 49 | return null; 50 | } 51 | 52 | 53 | } 54 | -------------------------------------------------------------------------------- /app/src/main/java/com/dark/androidbox/adapter/SnippetAdapter.java: -------------------------------------------------------------------------------- 1 | package com.dark.androidbox.adapter; 2 | 3 | import android.content.Context; 4 | import android.widget.ArrayAdapter; 5 | import android.widget.Filter; 6 | import java.util.List; 7 | 8 | public class SnippetAdapter extends ArrayAdapter { 9 | 10 | private List snippets; 11 | 12 | public SnippetAdapter(Context context, int resource, List snippets) { 13 | super(context, resource, snippets); 14 | this.snippets = snippets; 15 | } 16 | 17 | @Override 18 | public Filter getFilter() { 19 | return new Filter() { 20 | @Override 21 | protected FilterResults performFiltering(CharSequence constraint) { 22 | FilterResults results = new FilterResults(); 23 | results.values = snippets; 24 | results.count = snippets.size(); 25 | return results; 26 | } 27 | 28 | @Override 29 | protected void publishResults(CharSequence constraint, FilterResults results) { 30 | notifyDataSetChanged(); 31 | } 32 | }; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/dark/androidbox/codeView/Editor.java: -------------------------------------------------------------------------------- 1 | package com.dark.androidbox.codeView; 2 | 3 | import android.content.Context; 4 | import android.graphics.Color; 5 | 6 | import com.amrdeveloper.codeview.CodeView; 7 | import com.amrdeveloper.codeview.KeywordTokenizer; 8 | import com.dark.androidbox.adapter.SnippetAdapter; 9 | 10 | import java.util.ArrayList; 11 | import java.util.HashMap; 12 | import java.util.List; 13 | import java.util.Map; 14 | import java.util.regex.Pattern; 15 | 16 | public class Editor { 17 | // Define new color constants for white background 18 | private static final int KEYWORD_COLOR = Color.parseColor("#0077AA"); // Dark Blue for keywords 19 | private static final int CLASS_COLOR = Color.parseColor("#006600"); // Dark Green for classes 20 | private static final int METHOD_COLOR = Color.parseColor("#AA00FF"); // Violet for methods 21 | private static final int VARIABLE_COLOR = Color.parseColor("#AA5500"); // Brownish for variables 22 | private static final int PACKAGE_COLOR = Color.parseColor("#0055AA"); // Blue for package imports 23 | private static final int COMMENT_COLOR = Color.parseColor("#808080"); // Gray for comments 24 | private static final int TEXT_COLOR = Color.parseColor("#000000"); // Black for default text 25 | 26 | // Define patterns for Java syntax highlighting 27 | private final Pattern keywordsPattern = Pattern.compile( 28 | "\\b(abstract|assert|boolean|break|byte|case|catch|char|class|const|continue|" + 29 | "default|do|double|else|enum|extends|final|finally|float|for|goto|if|" + 30 | "implements|import|instanceof|int|interface|long|native|new|null|package|" + 31 | "private|protected|public|return|short|static|strictfp|super|switch|" + 32 | "synchronized|this|throw|throws|transient|try|void|volatile|while)\\b"); 33 | private final Pattern classPattern = Pattern.compile( 34 | "\\b([A-Z][a-zA-Z0-9_]*)\\b"); // Captures class names starting with capital letters 35 | private final Pattern methodPattern = Pattern.compile( 36 | "\\b([a-zA-Z_$][a-zA-Z\\d_$]*)\\s*\\("); // Methods followed by parentheses 37 | private final Pattern variablePattern = Pattern.compile( 38 | "\\b([a-zA-Z_$][a-zA-Z\\d_$]*)\\b(?=\\s*=|;)"); // Variable assignment or declaration 39 | private final Pattern packagePattern = Pattern.compile( 40 | "\\b([a-z]+(?:\\.[a-z]+)*)\\b"); // Package names in `import` statements 41 | private final Pattern commentPattern = Pattern.compile( 42 | "//[^\n]*|/\\*(.|\\R)*?\\*/"); // Single-line and multi-line comments 43 | 44 | public CodeView txtCode; 45 | private final Context context; 46 | 47 | public Editor(Context context, CodeView codeView) { 48 | this.context = context; 49 | this.txtCode = codeView; 50 | setUpEditor(); 51 | } 52 | 53 | private void setUpEditor() { 54 | // Setup line numbering 55 | txtCode.setEnableLineNumber(true); 56 | txtCode.setLineNumberTextColor(Color.parseColor("#B0B0B0")); // Light gray for line numbers 57 | txtCode.setLineNumberTextSize(30); 58 | 59 | // Setup auto-complete for code pairs 60 | Map pairCompleteMap = new HashMap<>(); 61 | pairCompleteMap.put('{', '}'); 62 | pairCompleteMap.put('[', ']'); 63 | pairCompleteMap.put('(', ')'); 64 | pairCompleteMap.put('<', '>'); 65 | pairCompleteMap.put('"', '"'); 66 | txtCode.enablePairComplete(true); 67 | txtCode.setPairCompleteMap(pairCompleteMap); 68 | 69 | // Set editor background to white and default text color to black 70 | txtCode.setBackgroundColor(Color.parseColor("#FFFFFF")); // White background 71 | txtCode.setTextColor(TEXT_COLOR); // Default text color is black 72 | 73 | // Apply syntax highlighting patterns 74 | applySyntaxHighlighting(); 75 | 76 | // Configure auto-complete 77 | txtCode.setThreshold(1); // Start suggesting after typing 1 character 78 | txtCode.setEnableAutoIndentation(true); 79 | txtCode.setTokenizer(new KeywordTokenizer()); 80 | 81 | // Set up auto-complete suggestions 82 | setUpAutoComplete(); 83 | } 84 | 85 | private void applySyntaxHighlighting() { 86 | // Apply syntax highlighting patterns with their respective colors 87 | txtCode.addSyntaxPattern(keywordsPattern, KEYWORD_COLOR); // Java keywords 88 | txtCode.addSyntaxPattern(classPattern, CLASS_COLOR); // Class names 89 | txtCode.addSyntaxPattern(methodPattern, METHOD_COLOR); // Method names 90 | txtCode.addSyntaxPattern(variablePattern, VARIABLE_COLOR); // Variables 91 | txtCode.addSyntaxPattern(packagePattern, PACKAGE_COLOR); // Package names 92 | txtCode.addSyntaxPattern(commentPattern, COMMENT_COLOR); // Comments 93 | } 94 | 95 | private void setUpAutoComplete() { 96 | // Define the snippets to suggest 97 | List snippets = new ArrayList<>(); 98 | snippets.add("public static void main(String[] args) {\n System.out.println(\"Hello World\");\n}"); 99 | 100 | // Create the custom adapter 101 | SnippetAdapter snippetAdapter = new SnippetAdapter(context, android.R.layout.simple_list_item_1, snippets); 102 | 103 | // Set the adapter to the CodeView 104 | txtCode.setAdapter(snippetAdapter); 105 | txtCode.setTokenizer(new KeywordTokenizer()); 106 | txtCode.setThreshold(1); // Show suggestions after 1 character is typed 107 | } 108 | 109 | // Method to dynamically set color for specific target strings 110 | public void setTxtColor(String targetString, int color) { 111 | Pattern pattern = Pattern.compile(Pattern.quote(targetString)); 112 | txtCode.addSyntaxPattern(pattern, color); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /app/src/main/java/com/dark/androidbox/fragments/TerminalFragment.java: -------------------------------------------------------------------------------- 1 | package com.dark.androidbox.fragments; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.os.Bundle; 6 | import android.os.Environment; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.Toast; 11 | 12 | import androidx.annotation.NonNull; 13 | import androidx.annotation.Nullable; 14 | import androidx.fragment.app.Fragment; 15 | 16 | import com.dark.androidbox.databinding.TerminalFragmentBinding; 17 | 18 | import org.codehaus.commons.compiler.CompileException; 19 | import org.codehaus.janino.SimpleCompiler; 20 | 21 | import java.io.BufferedReader; 22 | import java.io.ByteArrayOutputStream; 23 | import java.io.File; 24 | import java.io.FileOutputStream; 25 | import java.io.FileReader; 26 | import java.io.IOException; 27 | import java.io.PrintStream; 28 | import java.lang.reflect.Method; 29 | 30 | @SuppressLint("SetTextI18n") 31 | public class TerminalFragment extends Fragment { 32 | 33 | private Context ctx; 34 | private TerminalFragmentBinding binding; 35 | 36 | public TerminalFragment() { 37 | } 38 | 39 | @Nullable 40 | @Override 41 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 42 | binding = TerminalFragmentBinding.inflate(inflater, container, false); 43 | return binding.getRoot(); 44 | } 45 | 46 | @Override 47 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 48 | super.onViewCreated(view, savedInstanceState); 49 | ctx = requireContext(); 50 | init(); 51 | } 52 | 53 | private void init() { 54 | try { 55 | compileAndRunWithJanino(readJavaFile("/storage/emulated/0/AndroidBox/Main.java")); 56 | } catch (IOException e) { 57 | throw new RuntimeException(e); 58 | } 59 | } 60 | 61 | private String readJavaFile(String filePath) throws IOException { 62 | StringBuilder javaCode = new StringBuilder(); 63 | BufferedReader reader = new BufferedReader(new FileReader(filePath)); 64 | String line; 65 | while ((line = reader.readLine()) != null) { 66 | javaCode.append(line).append("\n"); 67 | } 68 | reader.close(); 69 | return javaCode.toString(); 70 | } 71 | 72 | private void compileAndRunWithJanino(String javaCode) { 73 | try { 74 | SimpleCompiler compiler = new SimpleCompiler(); 75 | compiler.cook(javaCode); 76 | 77 | Class clazz = compiler.getClassLoader().loadClass("Main"); 78 | Method method = clazz.getMethod("main", String[].class); 79 | 80 | // Redirect output 81 | ByteArrayOutputStream outContent = new ByteArrayOutputStream(); 82 | PrintStream ps = new PrintStream(outContent); 83 | PrintStream oldOut = System.out; 84 | System.setOut(ps); 85 | 86 | // Execute main method 87 | method.invoke(null, (Object) new String[]{}); 88 | 89 | // Restore output 90 | System.out.flush(); 91 | System.setOut(oldOut); 92 | 93 | // Display output 94 | String output = outContent.toString(); 95 | binding.output.setText(output); 96 | 97 | } catch (CompileException e) { 98 | binding.output.setText("Compilation Error: " + e); 99 | } catch (Exception e) { 100 | binding.output.setText("Execution Error: " + e); 101 | } 102 | } 103 | 104 | 105 | } 106 | -------------------------------------------------------------------------------- /app/src/main/java/com/dark/androidbox/helper/StoragePermissionHelper.java: -------------------------------------------------------------------------------- 1 | package com.dark.androidbox.helper; 2 | 3 | import android.Manifest; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.content.pm.PackageManager; 7 | import android.net.Uri; 8 | import android.os.Build; 9 | import android.provider.Settings; 10 | 11 | import androidx.activity.result.ActivityResultLauncher; 12 | import androidx.core.content.ContextCompat; 13 | 14 | public class StoragePermissionHelper { 15 | 16 | private final Context context; 17 | private final ActivityResultLauncher requestPermissionsLauncher; 18 | private final ActivityResultLauncher manageAllFilesAccessLauncher; 19 | 20 | private final PermissionCallback callback; 21 | 22 | public StoragePermissionHelper(Context context, PermissionCallback callback, 23 | ActivityResultLauncher requestPermissionsLauncher, 24 | ActivityResultLauncher manageAllFilesAccessLauncher) { 25 | this.context = context; 26 | this.callback = callback; 27 | this.requestPermissionsLauncher = requestPermissionsLauncher; 28 | this.manageAllFilesAccessLauncher = manageAllFilesAccessLauncher; 29 | } 30 | 31 | public void checkAndRequestPermission() { 32 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { 33 | // For Android 11 and above 34 | if (!android.os.Environment.isExternalStorageManager()) { 35 | Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION); 36 | intent.setData(Uri.parse("package:" + context.getPackageName())); 37 | manageAllFilesAccessLauncher.launch(intent); 38 | } else { 39 | callback.onPermissionGranted(); 40 | } 41 | } else { 42 | // For Android 10 and below 43 | requestPermissionsLauncher.launch(new String[]{ 44 | Manifest.permission.READ_EXTERNAL_STORAGE, 45 | Manifest.permission.WRITE_EXTERNAL_STORAGE 46 | }); 47 | } 48 | } 49 | 50 | public void handlePermissionsResult() { 51 | boolean readGranted = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; 52 | boolean writeGranted = ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; 53 | 54 | if (readGranted && writeGranted) { 55 | callback.onPermissionGranted(); 56 | } else { 57 | callback.onPermissionDenied(); 58 | } 59 | } 60 | 61 | public void handleManageFilesPermissionResult() { 62 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { 63 | if (android.os.Environment.isExternalStorageManager()) { 64 | callback.onPermissionGranted(); 65 | } else { 66 | callback.onPermissionDenied(); 67 | } 68 | } 69 | } 70 | 71 | public interface PermissionCallback { 72 | void onPermissionGranted(); 73 | 74 | void onPermissionDenied(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/java/com/dark/androidbox/model/NodeData.java: -------------------------------------------------------------------------------- 1 | package com.dark.androidbox.model; 2 | 3 | import com.dark.androidbox.nodes.BaseNode; 4 | import com.dark.androidbox.types.NodeTypes; 5 | import com.github.javaparser.ast.Node; 6 | 7 | public class NodeData { 8 | 9 | 10 | public final String title; 11 | public final String code; 12 | public final NodeTypes types; 13 | public final BaseNode node; 14 | public final Node data; 15 | public int nodeId; 16 | 17 | 18 | public NodeData(String title, String code, NodeTypes types, BaseNode node, Node data) { 19 | this.title = title; 20 | this.code = code; 21 | this.types = types; 22 | this.node = node; 23 | this.data = data; 24 | } 25 | 26 | 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/dark/androidbox/nodes/BaseNode.java: -------------------------------------------------------------------------------- 1 | package com.dark.androidbox.nodes; 2 | 3 | import android.content.Context; 4 | import android.graphics.Color; 5 | import android.util.AttributeSet; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.widget.FrameLayout; 9 | 10 | import androidx.annotation.Nullable; 11 | 12 | import com.dark.androidbox.databinding.BaseNodeBinding; 13 | import com.dark.androidbox.model.NodeData; 14 | import com.gyso.treeview.model.NodeModel; 15 | 16 | public class BaseNode extends FrameLayout { 17 | 18 | NodeModel data; 19 | private BaseNodeBinding binding; 20 | 21 | public BaseNode(Context context) { 22 | super(context); 23 | init(); 24 | } 25 | 26 | public BaseNode(Context context, @Nullable AttributeSet attrs) { 27 | super(context, attrs); 28 | init(); 29 | } 30 | 31 | public BaseNode(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 32 | super(context, attrs, defStyleAttr); 33 | init(); 34 | } 35 | 36 | public BaseNode(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { 37 | super(context, attrs, defStyleAttr, defStyleRes); 38 | init(); 39 | } 40 | 41 | public void init() { 42 | binding = BaseNodeBinding.inflate(LayoutInflater.from(getContext()), this, true); 43 | setBackgroundColor(Color.WHITE); 44 | } 45 | 46 | public View getNode(NodeModel data) { 47 | this.data = data; 48 | postInit(); 49 | return this; 50 | } 51 | 52 | public void postInit() { 53 | 54 | 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/com/dark/androidbox/nodes/ClassNode.java: -------------------------------------------------------------------------------- 1 | package com.dark.androidbox.nodes; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.graphics.Color; 6 | import android.text.Spannable; 7 | import android.text.SpannableString; 8 | import android.text.style.ForegroundColorSpan; 9 | import android.util.AttributeSet; 10 | import android.view.LayoutInflater; 11 | 12 | import androidx.annotation.Nullable; 13 | 14 | import com.dark.androidbox.databinding.ClassNodeBinding; 15 | import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; 16 | 17 | public class ClassNode extends BaseNode { 18 | 19 | private ClassNodeBinding binding; 20 | private ClassOrInterfaceDeclaration declaration; 21 | 22 | public ClassNode(Context context) { 23 | super(context); 24 | } 25 | 26 | public ClassNode(Context context, @Nullable AttributeSet attrs) { 27 | super(context, attrs); 28 | } 29 | 30 | public ClassNode(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 31 | super(context, attrs, defStyleAttr); 32 | } 33 | 34 | public ClassNode(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { 35 | super(context, attrs, defStyleAttr, defStyleRes); 36 | } 37 | 38 | @Override 39 | public void init() { 40 | super.init(); 41 | binding = ClassNodeBinding.inflate(LayoutInflater.from(getContext()), this, true); 42 | setBackgroundColor(Color.WHITE); 43 | } 44 | 45 | @SuppressLint("SetTextI18n") 46 | @Override 47 | public void postInit() { 48 | super.postInit(); 49 | if (data.value.data instanceof ClassOrInterfaceDeclaration) { 50 | declaration = (ClassOrInterfaceDeclaration) data.value.data; 51 | binding.ctx.setText(buildSpannableText()); 52 | } else { 53 | binding.ctx.setText(data.value.title); 54 | } 55 | } 56 | 57 | private SpannableString buildSpannableText() { 58 | // Construct the text 59 | String typeText = "Type : class\n\n"; 60 | String implementationText = "Implementation : " + formatList(declaration.getImplementedTypes().toString()) + "\n\n"; 61 | String extendedText = "Extended : " + formatList(declaration.getExtendedTypes().toString()); 62 | 63 | String completeText = typeText + implementationText + extendedText; 64 | SpannableString spannableString = new SpannableString(completeText); 65 | 66 | // Apply spans (use the complete text instead of individual segments) 67 | applySpan(spannableString, completeText, "class", Color.parseColor("#B276FF")); 68 | applySpan(spannableString, completeText, formatList(declaration.getImplementedTypes().toString()), Color.parseColor("#E49D33")); 69 | applySpan(spannableString, completeText, formatList(declaration.getExtendedTypes().toString()), Color.parseColor("#E49D33")); 70 | 71 | return spannableString; 72 | } 73 | 74 | private String formatList(String list) { 75 | return list.replace("[", "").replace("]", ""); 76 | } 77 | 78 | private void applySpan(SpannableString spannable, String fullText, String subText, int color) { 79 | int start = fullText.indexOf(subText); 80 | if (start != -1) { 81 | spannable.setSpan(new ForegroundColorSpan(color), start, start + subText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 82 | } 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /app/src/main/java/com/dark/androidbox/nodes/MethodNode.java: -------------------------------------------------------------------------------- 1 | package com.dark.androidbox.nodes; 2 | 3 | 4 | import android.content.Context; 5 | import android.graphics.Color; 6 | import android.graphics.drawable.GradientDrawable; 7 | import android.util.AttributeSet; 8 | import android.view.LayoutInflater; 9 | 10 | import androidx.annotation.Nullable; 11 | 12 | import com.dark.androidbox.databinding.MethodNodeBinding; 13 | 14 | public class MethodNode extends BaseNode { 15 | 16 | private MethodNodeBinding binding; 17 | 18 | public MethodNode(Context context) { 19 | super(context); 20 | } 21 | 22 | public MethodNode(Context context, @Nullable AttributeSet attrs) { 23 | super(context, attrs); 24 | } 25 | 26 | public MethodNode(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 27 | super(context, attrs, defStyleAttr); 28 | } 29 | 30 | public MethodNode(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { 31 | super(context, attrs, defStyleAttr, defStyleRes); 32 | } 33 | 34 | @Override 35 | public void init() { 36 | super.init(); 37 | binding = MethodNodeBinding.inflate(LayoutInflater.from(getContext()), this, true); 38 | GradientDrawable background = new GradientDrawable(); 39 | background.setColor(Color.parseColor("#b4defe")); // Set the background color 40 | background.setCornerRadius(16f); // Set the corner radius (in pixels) 41 | 42 | // Set the drawable as the background for the view 43 | setBackground(background); 44 | 45 | } 46 | 47 | @Override 48 | public void postInit() { 49 | super.postInit(); 50 | binding.title.setVisibility(GONE); 51 | 52 | // setup(binding.choose); 53 | 54 | } 55 | 56 | 57 | // void setup(AppCompatSpinner spinner) { 58 | // 59 | // MethodSelectAdapter adapter = new MethodSelectAdapter(getContext(), (List) data.getParentNode().value.data); 60 | // 61 | // 62 | // 63 | // // Bind the adapter to the Spinner 64 | // spinner.setAdapter(adapter); 65 | // 66 | // // Set a listener to handle item selection 67 | // spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { 68 | // @Override 69 | // public void onItemSelected(AdapterView parent, View view, int position, long id) { 70 | // String selectedItem = parent.getItemAtPosition(position).toString(); 71 | // binding.title.setText(selectedItem); 72 | // MainActivity.treeViewStatic.getEditor().setWantEdit(false); 73 | // } 74 | // 75 | // @Override 76 | // public void onNothingSelected(AdapterView parent) { 77 | // // Handle the case where no item is selected 78 | // } 79 | // }); 80 | // } 81 | } 82 | -------------------------------------------------------------------------------- /app/src/main/java/com/dark/androidbox/types/NodeTypes.java: -------------------------------------------------------------------------------- 1 | package com.dark.androidbox.types; 2 | 3 | public enum NodeTypes { 4 | 5 | CLASS, 6 | METHOD, 7 | VARIABLE, 8 | CONSTRUCTOR; 9 | 10 | NodeTypes() { 11 | 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_node_list_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_node_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_node_view_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_node_view_top.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/drop_down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Siddhesh2377/AndroidBox/4cc1be686a75fa46cda216f4e3b74e04e6d4a2fc/app/src/main/res/drawable/drop_down.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Siddhesh2377/AndroidBox/4cc1be686a75fa46cda216f4e3b74e04e6d4a2fc/app/src/main/res/drawable/settings.png -------------------------------------------------------------------------------- /app/src/main/res/font/jaldi.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/layout/base_node.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/layout/class_node.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_editor.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 11 | 12 | 20 | 21 | 28 | 29 | 36 | 37 | 48 | 49 | -------------------------------------------------------------------------------- /app/src/main/res/layout/method_node.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/methods_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/node.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/layout/nodeview.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 18 | 19 | 29 | 30 | 31 | 38 | 39 | 40 | 41 | 48 | 49 | 57 | 58 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /app/src/main/res/layout/terminal_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Siddhesh2377/AndroidBox/4cc1be686a75fa46cda216f4e3b74e04e6d4a2fc/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Siddhesh2377/AndroidBox/4cc1be686a75fa46cda216f4e3b74e04e6d4a2fc/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Siddhesh2377/AndroidBox/4cc1be686a75fa46cda216f4e3b74e04e6d4a2fc/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Siddhesh2377/AndroidBox/4cc1be686a75fa46cda216f4e3b74e04e6d4a2fc/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Siddhesh2377/AndroidBox/4cc1be686a75fa46cda216f4e3b74e04e6d4a2fc/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Siddhesh2377/AndroidBox/4cc1be686a75fa46cda216f4e3b74e04e6d4a2fc/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Siddhesh2377/AndroidBox/4cc1be686a75fa46cda216f4e3b74e04e6d4a2fc/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Siddhesh2377/AndroidBox/4cc1be686a75fa46cda216f4e3b74e04e6d4a2fc/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Siddhesh2377/AndroidBox/4cc1be686a75fa46cda216f4e3b74e04e6d4a2fc/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Siddhesh2377/AndroidBox/4cc1be686a75fa46cda216f4e3b74e04e6d4a2fc/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FF000000 4 | #FFFFFFFF 5 | #9C32821B 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12dp 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/font_certs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | @array/com_google_android_gms_fonts_certs_dev 5 | @array/com_google_android_gms_fonts_certs_prod 6 | 7 | 8 | 9 | MIIEqDCCA5CgAwIBAgIJANWFuGx90071MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAeFw0wODA0MTUyMzM2NTZaFw0zNTA5MDEyMzM2NTZaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBANbOLggKv+IxTdGNs8/TGFy0PTP6DHThvbbR24kT9ixcOd9W+EaBPWW+wPPKQmsHxajtWjmQwWfna8mZuSeJS48LIgAZlKkpFeVyxW0qMBujb8X8ETrWy550NaFtI6t9+u7hZeTfHwqNvacKhp1RbE6dBRGWynwMVX8XW8N1+UjFaq6GCJukT4qmpN2afb8sCjUigq0GuMwYXrFVee74bQgLHWGJwPmvmLHC69EH6kWr22ijx4OKXlSIx2xT1AsSHee70w5iDBiK4aph27yH3TxkXy9V89TDdexAcKk/cVHYNnDBapcavl7y0RiQ4biu8ymM8Ga/nmzhRKya6G0cGw8CAQOjgfwwgfkwHQYDVR0OBBYEFI0cxb6VTEM8YYY6FbBMvAPyT+CyMIHJBgNVHSMEgcEwgb6AFI0cxb6VTEM8YYY6FbBMvAPyT+CyoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJANWFuGx90071MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBABnTDPEF+3iSP0wNfdIjIz1AlnrPzgAIHVvXxunW7SBrDhEglQZBbKJEk5kT0mtKoOD1JMrSu1xuTKEBahWRbqHsXclaXjoBADb0kkjVEJu/Lh5hgYZnOjvlba8Ld7HCKePCVePoTJBdI4fvugnL8TsgK05aIskyY0hKI9L8KfqfGTl1lzOv2KoWD0KWwtAWPoGChZxmQ+nBli+gwYMzM1vAkP+aayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUWEv9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs= 10 | 11 | 12 | 13 | 14 | MIIEQzCCAyugAwIBAgIJAMLgh0ZkSjCNMA0GCSqGSIb3DQEBBAUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAeFw0wODA4MjEyMzEzMzRaFw0zNjAxMDcyMzEzMzRaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAKtWLgDYO6IIrgqWbxJOKdoR8qtW0I9Y4sypEwPpt1TTcvZApxsdyxMJZ2JORland2qSGT2y5b+3JKkedxiLDmpHpDsz2WCbdxgxRczfey5YZnTJ4VZbH0xqWVW/8lGmPav5xVwnIiJS6HXk+BVKZF+JcWjAsb/GEuq/eFdpuzSqeYTcfi6idkyugwfYwXFU1+5fZKUaRKYCwkkFQVfcAs1fXA5V+++FGfvjJ/CxURaSxaBvGdGDhfXE28LWuT9ozCl5xw4Yq5OGazvV24mZVSoOO0yZ31j7kYvtwYK6NeADwbSxDdJEqO4k//0zOHKrUiGYXtqw/A0LFFtqoZKFjnkCAQOjgdkwgdYwHQYDVR0OBBYEFMd9jMIhF1Ylmn/Tgt9r45jk14alMIGmBgNVHSMEgZ4wgZuAFMd9jMIhF1Ylmn/Tgt9r45jk14aloXikdjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLR29vZ2xlIEluYy4xEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMTB0FuZHJvaWSCCQDC4IdGZEowjTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IBAQBt0lLO74UwLDYKqs6Tm8/yzKkEu116FmH4rkaymUIE0P9KaMftGlMexFlaYjzmB2OxZyl6euNXEsQH8gjwyxCUKRJNexBiGcCEyj6z+a1fuHHvkiaai+KL8W1EyNmgjmyy8AW7P+LLlkR+ho5zEHatRbM/YAnqGcFh5iZBqpknHf1SKMXFh4dd239FJ1jWYfbMDMy3NS5CTMQ2XFI1MvcyUTdZPErjQfTbQe3aDQsQcafEQPD+nqActifKZ0Np0IS9L9kR/wbNvyz6ENwPiTrjV2KRkEjH78ZMcUQXg0L3BYHJ3lc69Vs5Ddf9uUGGMYldX3WfMBEmh/9iFBDAaTCK 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/values/integers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 1000 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/preloaded_fonts.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | @font/jaldi 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | AndroidBox 3 | Node Title 4 | Node ID : 001 5 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 21 | 22 | 23 | 24 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /app/src/test/java/com/dark/androidbox/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.dark.androidbox; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.junit.Test; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | plugins { 3 | alias(libs.plugins.android.application) apply false 4 | } -------------------------------------------------------------------------------- /codeview/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /codeview/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdk 34 5 | 6 | defaultConfig { 7 | minSdkVersion 27 8 | targetSdk 34 9 | 10 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 11 | } 12 | 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | compileOptions { 20 | sourceCompatibility JavaVersion.VERSION_1_8 21 | targetCompatibility JavaVersion.VERSION_1_8 22 | } 23 | namespace 'com.amrdeveloper.codeview' 24 | } 25 | 26 | dependencies { 27 | implementation fileTree(dir: "libs", include: ["*.jar"]) 28 | implementation libs.appcompat.v161 29 | testImplementation libs.junit 30 | androidTestImplementation libs.ext.junit 31 | androidTestImplementation libs.espresso.core.v351 32 | } -------------------------------------------------------------------------------- /codeview/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Siddhesh2377/AndroidBox/4cc1be686a75fa46cda216f4e3b74e04e6d4a2fc/codeview/consumer-rules.pro -------------------------------------------------------------------------------- /codeview/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /codeview/src/androidTest/java/com/amrdeveloper/codeview/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.amrdeveloper.codeview; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import android.content.Context; 6 | 7 | import androidx.test.ext.junit.runners.AndroidJUnit4; 8 | import androidx.test.platform.app.InstrumentationRegistry; 9 | 10 | import org.junit.Test; 11 | import org.junit.runner.RunWith; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | assertEquals("com.amrdeveloper.codeview.test", appContext.getPackageName()); 25 | } 26 | } -------------------------------------------------------------------------------- /codeview/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /codeview/src/main/java/com/amrdeveloper/codeview/Code.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 AmrDeveloper (Amr Hesham) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.amrdeveloper.codeview; 26 | 27 | /** 28 | * Interface to represent a different types of code such as keywords or snippets 29 | * 30 | * @since 1.1.0 31 | */ 32 | public interface Code { 33 | 34 | /** 35 | * @return The title of code 36 | */ 37 | String getCodeTitle(); 38 | 39 | /** 40 | * @return The prefix value of the code 41 | */ 42 | String getCodePrefix(); 43 | 44 | /** 45 | * @return The body of the code to insert it 46 | */ 47 | String getCodeBody(); 48 | } 49 | -------------------------------------------------------------------------------- /codeview/src/main/java/com/amrdeveloper/codeview/CodeViewAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 AmrDeveloper (Amr Hesham) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.amrdeveloper.codeview; 26 | 27 | import android.content.Context; 28 | import android.view.LayoutInflater; 29 | import android.view.View; 30 | import android.view.ViewGroup; 31 | import android.widget.BaseAdapter; 32 | import android.widget.Filter; 33 | import android.widget.Filterable; 34 | import android.widget.TextView; 35 | 36 | import androidx.annotation.NonNull; 37 | 38 | import java.util.ArrayList; 39 | import java.util.List; 40 | 41 | /** 42 | * Custom base adapter that to use it in CodeView auto complete and snippets feature 43 | *

44 | * CodeViewAdapter supports to take a list of code which can include Keywords and snippets 45 | * 46 | * @since 1.1.0 47 | */ 48 | public class CodeViewAdapter extends BaseAdapter implements Filterable { 49 | 50 | private final LayoutInflater layoutInflater; 51 | private final int codeViewLayoutId; 52 | private final int codeViewTextViewId; 53 | private List codeList; 54 | private List originalCodes; 55 | private final Filter codeFilter = new Filter() { 56 | @Override 57 | protected FilterResults performFiltering(CharSequence constraint) { 58 | FilterResults results = new FilterResults(); 59 | List suggestions = new ArrayList<>(); 60 | 61 | if (originalCodes == null) { 62 | originalCodes = new ArrayList<>(codeList); 63 | } 64 | 65 | 66 | if (constraint == null || constraint.length() == 0) { 67 | results.values = originalCodes; 68 | results.count = originalCodes.size(); 69 | } else { 70 | String filterPattern = constraint.toString().toLowerCase().trim(); 71 | 72 | for (Code item : originalCodes) { 73 | if (item.getCodePrefix().toLowerCase().contains(filterPattern)) { 74 | suggestions.add(item); 75 | } 76 | } 77 | results.values = suggestions; 78 | results.count = suggestions.size(); 79 | } 80 | 81 | return results; 82 | } 83 | 84 | @Override 85 | protected void publishResults(CharSequence constraint, FilterResults results) { 86 | codeList = (List) results.values; 87 | notifyDataSetChanged(); 88 | } 89 | 90 | @Override 91 | public CharSequence convertResultToString(Object resultValue) { 92 | return ((Code) resultValue).getCodeBody(); 93 | } 94 | }; 95 | 96 | public CodeViewAdapter(@NonNull Context context, int resource, int textViewResourceId, @NonNull List codes) { 97 | this.codeList = codes; 98 | this.layoutInflater = LayoutInflater.from(context); 99 | this.codeViewLayoutId = resource; 100 | this.codeViewTextViewId = textViewResourceId; 101 | } 102 | 103 | @Override 104 | public View getView(int position, View convertView, ViewGroup parent) { 105 | if (convertView == null) { 106 | convertView = layoutInflater.inflate(codeViewLayoutId, parent, false); 107 | } 108 | 109 | TextView textViewName = convertView.findViewById(codeViewTextViewId); 110 | 111 | Code currentCode = codeList.get(position); 112 | if (currentCode != null) { 113 | textViewName.setText(currentCode.getCodeTitle()); 114 | } 115 | 116 | return convertView; 117 | } 118 | 119 | @Override 120 | public int getCount() { 121 | return codeList.size(); 122 | } 123 | 124 | @Override 125 | public Object getItem(int position) { 126 | return codeList.get(position); 127 | } 128 | 129 | @Override 130 | public long getItemId(int position) { 131 | return position; 132 | } 133 | 134 | /** 135 | * Update the current code list with new list 136 | * 137 | * @param newCodeList The new code list 138 | */ 139 | public void updateCodes(List newCodeList) { 140 | codeList.clear(); 141 | codeList.addAll(newCodeList); 142 | notifyDataSetChanged(); 143 | } 144 | 145 | /** 146 | * Clear the current code list and notify data set changed 147 | */ 148 | public void clearCodes() { 149 | codeList.clear(); 150 | notifyDataSetChanged(); 151 | } 152 | 153 | @Override 154 | public Filter getFilter() { 155 | return codeFilter; 156 | } 157 | 158 | } 159 | -------------------------------------------------------------------------------- /codeview/src/main/java/com/amrdeveloper/codeview/Findable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 AmrDeveloper (Amr Hesham) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.amrdeveloper.codeview; 26 | 27 | import java.util.List; 28 | 29 | /** 30 | * Interface used to support find and match features 31 | * 32 | * @since 1.2.1 33 | */ 34 | public interface Findable { 35 | 36 | /** 37 | * Find all the the tokens that matches the regex string and save them on a list 38 | * 39 | * @param regex The regex used to find tokens 40 | * @return List of the matches Tokens 41 | */ 42 | List findMatches(String regex); 43 | 44 | /** 45 | * Highlight and return the next token 46 | * 47 | * @return The next matched token, {@code null} if not found 48 | */ 49 | Token findNextMatch(); 50 | 51 | /** 52 | * Highlight and return the previous token 53 | * 54 | * @return The previous matched token, {@code null} if not found 55 | */ 56 | Token findPrevMatch(); 57 | 58 | /** 59 | * Clear all the matches tokens 60 | */ 61 | void clearMatches(); 62 | } 63 | -------------------------------------------------------------------------------- /codeview/src/main/java/com/amrdeveloper/codeview/Keyword.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 AmrDeveloper (Amr Hesham) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.amrdeveloper.codeview; 26 | 27 | /** 28 | * Keyword is used to save information to provide auto complete features 29 | * 30 | * @since 1.1.0 31 | */ 32 | public class Keyword implements Code { 33 | 34 | private final String title; 35 | private final String prefix; 36 | 37 | public Keyword(String title) { 38 | this.title = title; 39 | this.prefix = title; 40 | } 41 | 42 | public Keyword(String title, String prefix) { 43 | this.title = title; 44 | this.prefix = prefix; 45 | } 46 | 47 | @Override 48 | public String getCodeTitle() { 49 | return title; 50 | } 51 | 52 | @Override 53 | public String getCodePrefix() { 54 | return prefix; 55 | } 56 | 57 | @Override 58 | public String getCodeBody() { 59 | return prefix; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /codeview/src/main/java/com/amrdeveloper/codeview/KeywordTokenizer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 AmrDeveloper (Amr Hesham) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.amrdeveloper.codeview; 26 | 27 | import android.widget.MultiAutoCompleteTextView; 28 | 29 | /** 30 | * The default tokenizer that used for CodeView auto complete feature 31 | */ 32 | public class KeywordTokenizer implements MultiAutoCompleteTextView.Tokenizer { 33 | 34 | @Override 35 | public int findTokenStart(CharSequence charSequence, int cursor) { 36 | // All text until the current cursor position 37 | final String sequenceStr = charSequence.toString().substring(0, cursor); 38 | 39 | // Iterate until find space, newline or (, starting from the current cursor position 40 | for (int i = cursor - 1; i >= 0; i--) { 41 | // Return the next position after the prefix character 42 | final char c = sequenceStr.charAt(i); 43 | if (c == ' ' || c == '\n' || c == '(') return i + 1; 44 | } 45 | 46 | // If no prefix character found then token start is the start of text 47 | return 0; 48 | } 49 | 50 | @Override 51 | public int findTokenEnd(CharSequence charSequence, int cursor) { 52 | return charSequence.length(); 53 | } 54 | 55 | @Override 56 | public CharSequence terminateToken(CharSequence charSequence) { 57 | return charSequence; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /codeview/src/main/java/com/amrdeveloper/codeview/Replaceable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 AmrDeveloper (Amr Hesham) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.amrdeveloper.codeview; 26 | 27 | /** 28 | * Interface used to support find and replacement feature 29 | * 30 | * @since 1.2.1 31 | */ 32 | public interface Replaceable { 33 | 34 | /** 35 | * Replace the first string that matched by the regex with new string 36 | * 37 | * @param regex regex Regex used to find the first target string 38 | * @param replacement Text to replace that matched string by it 39 | */ 40 | void replaceFirstMatch(String regex, String replacement); 41 | 42 | /** 43 | * Replace all strings that matched by the regex with new string 44 | * 45 | * @param regex Regex used to find the target string 46 | * @param replacement Text to replace that matched string by it 47 | */ 48 | void replaceAllMatches(String regex, String replacement); 49 | } 50 | -------------------------------------------------------------------------------- /codeview/src/main/java/com/amrdeveloper/codeview/Snippet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 AmrDeveloper (Amr Hesham) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.amrdeveloper.codeview; 26 | 27 | /** 28 | * Snippet is used to save information to provide snippets features 29 | * 30 | * @since 1.1.0 31 | */ 32 | public class Snippet implements Code { 33 | 34 | private final String title; 35 | private final String prefix; 36 | private final String body; 37 | 38 | public Snippet(String title, String body) { 39 | this.title = title; 40 | this.prefix = title; 41 | this.body = body; 42 | } 43 | 44 | public Snippet(String title, String prefix, String body) { 45 | this.title = title; 46 | this.prefix = prefix; 47 | this.body = body; 48 | } 49 | 50 | @Override 51 | public String getCodeTitle() { 52 | return title; 53 | } 54 | 55 | @Override 56 | public String getCodePrefix() { 57 | return prefix; 58 | } 59 | 60 | @Override 61 | public String getCodeBody() { 62 | return body; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /codeview/src/main/java/com/amrdeveloper/codeview/Token.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 AmrDeveloper (Amr Hesham) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.amrdeveloper.codeview; 26 | 27 | /** 28 | * Token is class to represent a position on the source code 29 | * 30 | * @since 1.2.1 31 | */ 32 | public class Token { 33 | 34 | private final int start; 35 | private final int end; 36 | 37 | public Token(int start, int end) { 38 | this.start = start; 39 | this.end = end; 40 | } 41 | 42 | /** 43 | * @return The start position of the current token in source code 44 | */ 45 | public int getStart() { 46 | return start; 47 | } 48 | 49 | /** 50 | * @return The end position of the current token in source code 51 | */ 52 | public int getEnd() { 53 | return end; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /codeview/src/test/java/com/amrdeveloper/codeview/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.amrdeveloper.codeview; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.junit.Test; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. For more details, visit 12 | # https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Enables namespacing of each library's R class so that its R class includes only the 19 | # resources declared in the library itself and none from the library's dependencies, 20 | # thereby reducing the size of the R class for that library 21 | android.nonTransitiveRClass=true -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | agp = "8.7.3" 3 | androidCodeView = "1.0.4" 4 | androidxJunit = "1.2.1" 5 | appcompatVersion = "1.7.0" 6 | dynamicanimation = "1.0.0" 7 | espressoCoreVersion = "3.6.1" 8 | janino = "3.1.9" 9 | javaparserCore = "3.26.2" 10 | junit = "4.13.2" 11 | junitVersion = "1.2.1" 12 | espressoCore = "3.6.1" 13 | appcompat = "1.7.0" 14 | material = "1.12.0" 15 | activity = "1.9.2" 16 | constraintlayout = "2.1.4" 17 | materialVersion = "1.12.0" 18 | 19 | [libraries] 20 | android-code-view = { module = "com.github.chen0040:android-code-view", version.ref = "androidCodeView" } 21 | appcompat-v161 = { module = "androidx.appcompat:appcompat", version.ref = "appcompatVersion" } 22 | dynamicanimation = { module = "androidx.dynamicanimation:dynamicanimation", version.ref = "dynamicanimation" } 23 | espresso-core-v351 = { module = "androidx.test.espresso:espresso-core", version.ref = "espressoCoreVersion" } 24 | janino = { module = "org.codehaus.janino:janino", version.ref = "janino" } 25 | javaparser-core = { module = "com.github.javaparser:javaparser-core", version.ref = "javaparserCore" } 26 | junit = { group = "junit", name = "junit", version.ref = "junit" } 27 | ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } 28 | espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } 29 | appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } 30 | junit-v115 = { module = "androidx.test.ext:junit", version.ref = "androidxJunit" } 31 | material = { group = "com.google.android.material", name = "material", version.ref = "material" } 32 | activity = { group = "androidx.activity", name = "activity", version.ref = "activity" } 33 | constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } 34 | material-v1110 = { module = "com.google.android.material:material", version.ref = "materialVersion" } 35 | 36 | [plugins] 37 | android-application = { id = "com.android.application", version.ref = "agp" } 38 | 39 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Siddhesh2377/AndroidBox/4cc1be686a75fa46cda216f4e3b74e04e6d4a2fc/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Sep 06 17:00:14 IST 2024 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /node/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /node/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | } 4 | 5 | android { 6 | compileSdk 34 7 | 8 | defaultConfig { 9 | minSdk 27 10 | targetSdk 34 11 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 12 | } 13 | 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | compileOptions { 21 | sourceCompatibility JavaVersion.VERSION_1_8 22 | targetCompatibility JavaVersion.VERSION_1_8 23 | } 24 | namespace 'com.gyso.treeview' 25 | } 26 | 27 | dependencies { 28 | implementation fileTree(dir: "libs", include: ["*.jar"]) 29 | implementation libs.appcompat.v161 30 | implementation libs.material.v1110 31 | testImplementation libs.junit 32 | androidTestImplementation libs.junit.v115 33 | androidTestImplementation libs.espresso.core.v351 34 | implementation libs.dynamicanimation 35 | 36 | } 37 | -------------------------------------------------------------------------------- /node/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Siddhesh2377/AndroidBox/4cc1be686a75fa46cda216f4e3b74e04e6d4a2fc/node/consumer-rules.pro -------------------------------------------------------------------------------- /node/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /node/src/androidTest/java/com/gyso/treeview/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | assertEquals("com.gyso.treeview.test", appContext.getPackageName()); 25 | } 26 | } -------------------------------------------------------------------------------- /node/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /node/src/main/java/com/gyso/treeview/adapter/DrawInfo.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview.adapter; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.Paint; 5 | import android.graphics.Path; 6 | import android.graphics.Point; 7 | import android.graphics.PointF; 8 | 9 | /** 10 | * @Author: 怪兽N 11 | * @Time: 2021/5/7 21:15 12 | * @Email: 674149099@qq.com 13 | * @WeChat: guaishouN 14 | * @Describe: 15 | * In order to draw a custom line, there are too many draw info should be passed. 16 | * So, this class is estimated to package the draw element that need to draw a colorful line between two nodes. 17 | */ 18 | public class DrawInfo { 19 | /** 20 | * canvas 21 | */ 22 | private Canvas canvas; 23 | /** 24 | * node from view 25 | */ 26 | private TreeViewHolder fromHolder; 27 | /** 28 | * node to view 29 | */ 30 | private TreeViewHolder toHolder; 31 | /** 32 | * paint, before use you should reset 33 | */ 34 | private Paint paint; 35 | /** 36 | * path, before use you should reset 37 | */ 38 | private Path path; 39 | /** 40 | * space between Peer and Peer, means child and child 41 | */ 42 | private int spacePeerToPeer; 43 | /** 44 | * space between parent and child 45 | */ 46 | private int spaceParentToChild; 47 | /** 48 | * viewport width 49 | */ 50 | private int windowWidth; 51 | /** 52 | * viewport height 53 | */ 54 | private int windowHeight; 55 | 56 | public Canvas getCanvas() { 57 | return canvas; 58 | } 59 | 60 | public void setCanvas(Canvas canvas) { 61 | this.canvas = canvas; 62 | } 63 | 64 | public TreeViewHolder getFromHolder() { 65 | return fromHolder; 66 | } 67 | 68 | public void setFromHolder(TreeViewHolder fromHolder) { 69 | this.fromHolder = fromHolder; 70 | } 71 | 72 | public TreeViewHolder getToHolder() { 73 | return toHolder; 74 | } 75 | 76 | public void setToHolder(TreeViewHolder toHolder) { 77 | this.toHolder = toHolder; 78 | } 79 | 80 | public Paint getPaint() { 81 | return paint; 82 | } 83 | 84 | public void setPaint(Paint paint) { 85 | this.paint = paint; 86 | } 87 | 88 | public Path getPath() { 89 | return path; 90 | } 91 | 92 | public void setPath(Path path) { 93 | this.path = path; 94 | } 95 | 96 | public void setSpace(int spacePeerToPeer,int spaceParentToChild) { 97 | this.spacePeerToPeer = spacePeerToPeer; 98 | this.spaceParentToChild = spaceParentToChild; 99 | } 100 | 101 | public int getSpacePeerToPeer() { 102 | return spacePeerToPeer; 103 | } 104 | 105 | public int getSpaceParentToChild() { 106 | return spaceParentToChild; 107 | } 108 | 109 | public int getWindowWidth() { 110 | return windowWidth; 111 | } 112 | 113 | public void setWindowWidth(int windowWidth) { 114 | this.windowWidth = windowWidth; 115 | } 116 | 117 | public int getWindowHeight() { 118 | return windowHeight; 119 | } 120 | 121 | public void setWindowHeight(int windowHeight) { 122 | this.windowHeight = windowHeight; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /node/src/main/java/com/gyso/treeview/adapter/TreeViewAdapter.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview.adapter; 2 | 3 | import android.view.ViewGroup; 4 | 5 | import androidx.annotation.NonNull; 6 | 7 | import com.gyso.treeview.line.BaseLine; 8 | import com.gyso.treeview.listener.TreeViewNotifier; 9 | import com.gyso.treeview.model.NodeModel; 10 | import com.gyso.treeview.model.TreeModel; 11 | 12 | /** 13 | * @Author: 怪兽N 14 | * @Time: 2021/4/23 15:19 15 | * @Email: 674149099@qq.com 16 | * @WeChat: guaishouN 17 | * @Describe: 18 | * The view adapter for the {@link com.gyso.treeview.TreeViewContainer} 19 | */ 20 | public abstract class TreeViewAdapter { 21 | private TreeViewNotifier notifier; 22 | private TreeModel treeModel; 23 | 24 | public void setTreeModel(TreeModel treeModel) { 25 | this.treeModel = treeModel; 26 | notifyDataSetChange(); 27 | } 28 | 29 | /** 30 | * Get tree model 31 | * @return tree model 32 | */ 33 | public TreeModel getTreeModel(){ 34 | return treeModel; 35 | } 36 | 37 | /** 38 | * For create view holder by your self 39 | * @param viewGroup parent 40 | * @param model node 41 | * @return holder 42 | */ 43 | public abstract TreeViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, NodeModel model); 44 | 45 | /** 46 | * when bind the holder, set up you view 47 | * @param holder holder 48 | */ 49 | public abstract void onBindViewHolder(@NonNull TreeViewHolder holder); 50 | 51 | /** 52 | * Draw line between node and node by you decision. 53 | * If you return an BaseLine, line will be draw by the return one instead of TreeViewLayoutManager's. 54 | * @param drawInfo provides all you need to draw you line 55 | * @return the line draw you want to use for different nodes 56 | */ 57 | public abstract BaseLine onDrawLine(DrawInfo drawInfo); 58 | 59 | /** 60 | * for recycling holder, exactly for recycling views 61 | * @param node 62 | * @return 63 | */ 64 | public int getHolderType(NodeModel node){ 65 | return 0; 66 | } 67 | 68 | public void setNotifier(TreeViewNotifier notifier){ 69 | this.notifier = notifier; 70 | } 71 | 72 | public void notifyDataSetChange(){ 73 | if(notifier!=null){ 74 | notifier.onDataSetChange(); 75 | } 76 | } 77 | 78 | public void notifyItemViewChange(NodeModel node){ 79 | if(notifier!=null){ 80 | notifier.onItemViewChange(node); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /node/src/main/java/com/gyso/treeview/adapter/TreeViewHolder.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview.adapter; 2 | 3 | import android.view.View; 4 | 5 | import androidx.annotation.NonNull; 6 | 7 | import com.gyso.treeview.layout.TreeLayoutManager; 8 | import com.gyso.treeview.model.NodeModel; 9 | 10 | /** 11 | * @Author: 怪兽N 12 | * @Time: 2021/4/23 15:20 13 | * @Email: 674149099@qq.com 14 | * @WeChat: guaishouN 15 | * @Describe: 16 | * View holder 17 | */ 18 | public class TreeViewHolder { 19 | private int holderLayoutType = TreeLayoutManager.LAYOUT_TYPE_NONE; 20 | private View view; 21 | private NodeModel node; 22 | public TreeViewHolder(View view, @NonNull NodeModel node) { 23 | this.view = view; 24 | this.node = node; 25 | } 26 | 27 | public NodeModel getNode() { 28 | return node; 29 | } 30 | 31 | public View getView() { 32 | return view; 33 | } 34 | 35 | public void setView(View view) { 36 | this.view = view; 37 | } 38 | 39 | public void setNode(NodeModel node) { 40 | this.node = (NodeModel)node; 41 | } 42 | 43 | public int getHolderLayoutType() { 44 | return holderLayoutType; 45 | } 46 | 47 | public void setHolderLayoutType(int holderLayoutType) { 48 | this.holderLayoutType = holderLayoutType; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /node/src/main/java/com/gyso/treeview/algorithm/force/FLink.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview.algorithm.force; 2 | 3 | /** 4 | * Created by Z.Pan on 2016/10/9. 5 | */ 6 | 7 | public class FLink { 8 | 9 | FNode source; 10 | FNode target; 11 | private String text; 12 | 13 | public FLink(FNode source, FNode target) { 14 | this.source = source; 15 | this.target = target; 16 | } 17 | 18 | public String getText() { 19 | return text; 20 | } 21 | 22 | public void setText(String text) { 23 | this.text = text; 24 | } 25 | 26 | double getNodeDistance() { 27 | float dx = source.x - target.x; 28 | float dy = source.y - target.y; 29 | return Math.sqrt(dx * dx + dy * dy); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /node/src/main/java/com/gyso/treeview/algorithm/force/FNode.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview.algorithm.force; 2 | 3 | import static java.lang.annotation.ElementType.ANNOTATION_TYPE; 4 | import static java.lang.annotation.RetentionPolicy.SOURCE; 5 | 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * 力导向图中显示的节点。 11 | * 12 | * Created by Z.Pan on 2016/10/8. 13 | */ 14 | 15 | public class FNode { 16 | 17 | /** 根节点级别 */ 18 | public static final int ROOT_NODE_LEVEL = 0; 19 | 20 | static final short DRAG_START = 2; 21 | static final short DRAG = 4; 22 | static final short DRAG_END = 6; 23 | 24 | private String text; // 节点显示的内容 25 | private Object obj; // 用来携带其他数据,如:该节点对应的数据实体 Bean,或数据库中的 _id 26 | private int level; // 级别 27 | 28 | public float x, y; // 当前坐标 29 | float px, py; // 前一个状态的坐标 30 | int weight; // 根据子节点自动计算,weight 越大,该节点越不容易被拖动 31 | 32 | private float radius = 50f; // 节点半径 33 | private short state; // 节点状态,该状态决定了是否处于稳定状态 34 | 35 | public FNode(String text) { 36 | this(text, 50f, ROOT_NODE_LEVEL); 37 | } 38 | 39 | public FNode(String text, float radius, int level) { 40 | this.text = text; 41 | this.radius = radius; 42 | this.level = level; 43 | x=y=-1f; 44 | weight=1; 45 | } 46 | 47 | public void setObj(Object obj) { 48 | this.obj = obj; 49 | } 50 | 51 | public String getText() { 52 | return text; 53 | } 54 | 55 | public Object getObj() { 56 | return obj; 57 | } 58 | 59 | public int getLevel() { 60 | return level; 61 | } 62 | 63 | public void setLevel(int level) { 64 | this.level = level; 65 | } 66 | 67 | float getRadius() { 68 | return radius; 69 | } 70 | 71 | boolean isRootNode() { 72 | return level == ROOT_NODE_LEVEL; 73 | } 74 | 75 | /** 76 | * 给定一个坐标 (x, y),判断该坐标是否在节点所在范围内。用来判断是否点击了该节点。 77 | * @param x x坐标 78 | * @param y y坐标 79 | * @param scale 缩放比例 80 | * @return true 表示 (x, y) 在该节点内部 81 | */ 82 | boolean isInside(float x, float y, float scale) { 83 | float left = (this.x - radius) * scale; 84 | float top = (this.y - radius) * scale; 85 | float right = (this.x + radius) * scale; 86 | float bottom = (this.y + radius) * scale; 87 | return x >= left && x <= right && y >= top && y <= bottom; 88 | } 89 | 90 | boolean isStable() { 91 | return state != 0; 92 | } 93 | 94 | /** 95 | * 设置节点的状态是否正在被手指拖动。 96 | * @param state {@linkplain #DRAG_START} 开始拖动;{@linkplain #DRAG_END} 结束拖动。 97 | */ 98 | void setDragState(@State short state) { 99 | switch (state) { 100 | case DRAG_START: 101 | this.state |= state; 102 | break; 103 | case DRAG_END: 104 | this.state &= ~state; 105 | break; 106 | } 107 | } 108 | 109 | @ShortDef({DRAG_START, DRAG, DRAG_END}) 110 | @Retention(SOURCE) 111 | public @interface State {} 112 | 113 | @Retention(SOURCE) 114 | @Target({ANNOTATION_TYPE}) 115 | public @interface ShortDef { 116 | short[] value() default {}; 117 | boolean flag() default false; 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /node/src/main/java/com/gyso/treeview/algorithm/force/ForceListener.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview.algorithm.force; 2 | 3 | /** 4 | * Created by Z.Pan on 2016/10/10. 5 | */ 6 | 7 | public interface ForceListener { 8 | void refresh(); 9 | } 10 | -------------------------------------------------------------------------------- /node/src/main/java/com/gyso/treeview/algorithm/force/QuadTree.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview.algorithm.force; 2 | 3 | /** 4 | * Created by Z.Pan on 2016/10/10. 5 | */ 6 | class QuadTree { 7 | private static final float NULL = -1; 8 | 9 | static class Node { 10 | boolean isLeaf; 11 | Node[] children; 12 | FNode point; 13 | float x = NULL; 14 | float y = NULL; 15 | 16 | float charge; 17 | float pointCharge; 18 | float cx, cy; 19 | 20 | Node(boolean isLeaf, Node[] children, FNode point, float x, float y) { 21 | this.isLeaf = isLeaf; 22 | this.children = children; 23 | this.point = point; 24 | this.x = x; 25 | this.y = y; 26 | } 27 | } 28 | 29 | Node root; 30 | 31 | QuadTree() { 32 | root = generateNode(); 33 | } 34 | 35 | void insert(Node n, FNode node, float minX, float minY, float maxX, float maxY) { 36 | if (Float.isNaN(minX) || Float.isNaN(minY) || Float.isNaN(maxX) || Float.isNaN(maxY)) { 37 | return; 38 | } 39 | if (n.isLeaf) { 40 | float nx = n.x; 41 | float ny = n.y; 42 | 43 | if (nx != NULL) { 44 | if (Math.abs(nx - node.x) + Math.abs(ny - node.y) < 0.01) { 45 | insertChild(n, node, minX, minY, maxX, maxY); 46 | } else { 47 | FNode nPoint = n.point; 48 | n.x = n.y = NULL; 49 | n.point = null; 50 | insertChild(n, nPoint, minX, minY, maxX, maxY); 51 | insertChild(n, node, minX, minY, maxX, maxY); 52 | } 53 | } else { 54 | n.x = node.x; 55 | n.y = node.y; 56 | n.point = node; 57 | } 58 | } else { 59 | insertChild(n, node, minX, minY, maxX, maxY); 60 | } 61 | } 62 | 63 | private void insertChild(Node n, FNode node, float minX, float minY, float maxX, float maxY) { 64 | float sx = (minX + maxX) * 0.5f; 65 | float sy = (minY + maxY) * 0.5f; 66 | boolean isRight = node.x >= sx; 67 | boolean isBottom = node.y >= sy; 68 | int right = isRight ? 1 : 0; 69 | int bottom = isBottom ? 1 : 0; 70 | int i = (bottom << 1) + right; 71 | 72 | n.isLeaf = false; 73 | if (n.children[i] == null) { 74 | Node nod = generateNode(); 75 | n.children[i] = nod; 76 | n = nod; 77 | } else { 78 | n = n.children[i]; 79 | } 80 | 81 | if (isRight) { 82 | minX = sx; 83 | } else { 84 | maxX = sx; 85 | } 86 | 87 | if (isBottom) { 88 | minY = sy; 89 | } else { 90 | maxY = sy; 91 | } 92 | 93 | insert(n, node, minX, minY, maxX, maxY); 94 | } 95 | 96 | /** generate a leaf node for quad tree. */ 97 | private Node generateNode() { 98 | return new Node(true, new Node[4], null, NULL, NULL); 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /node/src/main/java/com/gyso/treeview/algorithm/ring/RingForSimple.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview.algorithm.ring; 2 | 3 | import android.graphics.Point; 4 | import android.graphics.PointF; 5 | import android.util.SparseIntArray; 6 | import com.gyso.treeview.model.ITraversal; 7 | import com.gyso.treeview.model.NodeModel; 8 | import com.gyso.treeview.model.TreeModel; 9 | import com.gyso.treeview.util.TreeViewLog; 10 | 11 | import java.util.ArrayDeque; 12 | import java.util.Deque; 13 | import java.util.HashMap; 14 | import java.util.LinkedList; 15 | import java.util.Map; 16 | import java.util.concurrent.atomic.AtomicInteger; 17 | 18 | public class RingForSimple { 19 | public static final String TAG = RingForSimple.class.getSimpleName(); 20 | private final static Map, RingForSimple> RING_MAP = new HashMap<>(); 21 | private final PointF center = new PointF(); 22 | private final TreeModel model; 23 | private final Map, PointF> nodeModelPointFMap = new HashMap<>(); 24 | private final Map, Double> nodeModelAngleMap = new HashMap<>(); 25 | protected SparseIntArray floorStart = new SparseIntArray(200); 26 | private final AtomicInteger multi = new AtomicInteger(1); 27 | private int maxDeep; 28 | private final Map, Point> nodeWidthMap = new HashMap<>(); 29 | private RingForSimple(TreeModel model) { 30 | this.model = model; 31 | } 32 | public static RingForSimple getInstance(TreeModel model){ 33 | if(model==null){ 34 | return null; 35 | } 36 | RingForSimple ring = RING_MAP.get(model); 37 | if(ring==null){ 38 | ring = new RingForSimple(model); 39 | RING_MAP.put(model,ring); 40 | } 41 | return ring; 42 | } 43 | public RingForSimple setCenter(float x, float y) { 44 | center.x = x; 45 | center.y = y; 46 | TreeViewLog.e(TAG,"center["+x+","+y+"]"); 47 | return this; 48 | } 49 | 50 | public RingForSimple setFloorStart(SparseIntArray floorStart) { 51 | this.floorStart = floorStart; 52 | return this; 53 | } 54 | 55 | public void reconstruction(TreeModel mTreeModel) { 56 | TreeViewLog.e(TAG,"reconstruction start"); 57 | nodeWidthMap.clear(); 58 | Deque deque = new ArrayDeque<>(); 59 | NodeModel rootNode = mTreeModel.getRootNode(); 60 | deque.add(rootNode); 61 | SparseIntArray deepSum = new SparseIntArray(); 62 | //calculate base deep 63 | while (!deque.isEmpty()) { 64 | NodeModel cur = deque.poll(); 65 | if(cur==null){ 66 | return; 67 | } 68 | cur.deep = deepSum.get(cur.floor,0); 69 | record(cur); 70 | NodeModel tmp = cur.parentNode; 71 | while (!tmp.equals(rootNode)){ 72 | record(tmp); 73 | tmp = tmp.parentNode; 74 | } 75 | deepSum.put(cur.floor, cur.deep+1); 76 | LinkedList childNodes = cur.getChildNodes(); 77 | if (childNodes.size() > 0) { 78 | deque.addAll(childNodes); 79 | } 80 | } 81 | } 82 | 83 | private void record(NodeModel node) { 84 | if (node == null) { 85 | return; 86 | } 87 | Point point = nodeWidthMap.get(node); 88 | if(point==null){ 89 | point = new Point(); 90 | nodeWidthMap.put(node, point); 91 | } 92 | maxDeep = Math.max(node.deep, maxDeep); 93 | point.x= Math.max(node.deep, point.x); 94 | point.y = Math.min(node.deep, point.y); 95 | } 96 | 97 | public Map, PointF> genPositions() { 98 | if (model == null) { 99 | return null; 100 | } 101 | NodeModel rootNode = model.getRootNode(); 102 | nodeModelPointFMap.clear(); 103 | nodeModelPointFMap.put(rootNode, new PointF(center.y, center.x)); 104 | int leafCount = maxDeep; 105 | LinkedList> rootNodeChildNodes = rootNode.getChildNodes(); 106 | if(leafCount == 0 || rootNodeChildNodes.isEmpty()){ 107 | return nodeModelPointFMap; 108 | } 109 | 110 | //keep in pie center 111 | double pieAngle = 2f*Math.PI / leafCount; 112 | 113 | //doTraversalNodes 114 | model.doTraversalNodes((ITraversal>) next -> { 115 | if(next.equals(rootNode)){ 116 | return; 117 | } 118 | PointF pointF = new PointF(); 119 | int deep = next.deep; 120 | int floor = next.floor; 121 | double angle = pieAngle * deep; 122 | LinkedList> childNodes = next.getChildNodes(); 123 | if(!childNodes.isEmpty()){ 124 | Point point = nodeWidthMap.get(next); 125 | int count = point.x-point.y; 126 | if(count>=2){ 127 | double d = pieAngle*(count-1)/2f; 128 | angle += d; 129 | } 130 | } 131 | float radius = floorStart.get(floor); 132 | TreeViewLog.e(TAG,"radius["+radius+"]angle["+angle+"]"); 133 | pointF.x = (float) (radius * Math.sin(angle))+center.y; 134 | pointF.y = (float) (radius * Math.cos(angle))+center.x; 135 | nodeModelPointFMap.put(next, pointF); 136 | nodeModelAngleMap.put(next,angle); 137 | }); 138 | return nodeModelPointFMap; 139 | } 140 | } -------------------------------------------------------------------------------- /node/src/main/java/com/gyso/treeview/algorithm/table/TableKey.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview.algorithm.table; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.annotation.Nullable; 5 | 6 | public class TableKey { 7 | public int floor; 8 | public int deep; 9 | 10 | public TableKey(int floor, int deep) { 11 | this.floor = floor; 12 | this.deep = deep; 13 | } 14 | 15 | @Override 16 | public int hashCode() { 17 | return floor; 18 | } 19 | 20 | @Override 21 | public boolean equals(@Nullable Object obj) { 22 | if(obj instanceof TableKey){ 23 | TableKey o = (TableKey)obj; 24 | return floor == o.floor && deep==o.deep; 25 | } 26 | return false; 27 | } 28 | 29 | @NonNull 30 | @Override 31 | public String toString() { 32 | return "["+floor+","+deep+"]"; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /node/src/main/java/com/gyso/treeview/algorithm/tight_table/TightTable.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview.algorithm.tight_table; 2 | 3 | public class TightTable { 4 | public void tightTable(){ 5 | 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /node/src/main/java/com/gyso/treeview/cache_pool/HolderPool.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview.cache_pool; 2 | 3 | import android.graphics.PointF; 4 | 5 | import androidx.core.util.Pools; 6 | 7 | import com.gyso.treeview.adapter.TreeViewHolder; 8 | 9 | /** 10 | * @Author: 怪兽N 11 | * @Time: 2021/6/10 14:37 12 | * @Email: 674149099@qq.com 13 | * @WeChat: guaishouN 14 | * @Describe: 15 | * * Holder pool. {@link com.gyso.treeview.adapter.TreeViewHolder} 16 | * * NOTE: not safe pool, please just use in UI thread 17 | */ 18 | public class HolderPool extends Pools.SimplePool> { 19 | public static final int DEFAULT_SIZE = 30; 20 | private final static HolderPool POOL = new HolderPool(); 21 | /** 22 | * Creates a new instance. 23 | */ 24 | public HolderPool() { 25 | super(DEFAULT_SIZE); 26 | } 27 | 28 | public TreeViewHolder obtain(){ 29 | return POOL.acquire(); 30 | } 31 | 32 | public void free(TreeViewHolder holder){ 33 | try { 34 | POOL.release(holder); 35 | }catch (IllegalStateException e){ 36 | e.printStackTrace(); 37 | } 38 | 39 | } 40 | 41 | public static void freeAll(){ 42 | while (POOL.acquire()!=null); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /node/src/main/java/com/gyso/treeview/cache_pool/PointPool.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview.cache_pool; 2 | 3 | import android.graphics.PointF; 4 | 5 | import androidx.core.util.Pools; 6 | 7 | /** 8 | * @Author: 怪兽N 9 | * @Time: 2021/5/8 9:56 10 | * @Email: 674149099@qq.com 11 | * @WeChat: guaishouN 12 | * @Describe: 13 | * Point pool. 14 | * NOTE: not safe pool, please just use in UI thread 15 | */ 16 | public class PointPool extends Pools.SimplePool { 17 | public static final int DEFAULT_SIZE = 20; 18 | private final static PointPool POOL = new PointPool(); 19 | /** 20 | * Creates a new instance. 21 | */ 22 | private PointPool() { 23 | super(DEFAULT_SIZE); 24 | } 25 | 26 | public static PointF obtain(){ 27 | PointF point = POOL.acquire(); 28 | if(point==null){ 29 | return new PointF(); 30 | } 31 | return point; 32 | } 33 | 34 | public static PointF obtain(float x, float y){ 35 | PointF point = POOL.acquire(); 36 | if(point==null){ 37 | return new PointF(x,y); 38 | } 39 | point.set(x,y); 40 | return point; 41 | } 42 | 43 | public static void free(PointF p){ 44 | POOL.release(p); 45 | } 46 | 47 | public static void freeAll(){ 48 | while (POOL.acquire()!=null); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /node/src/main/java/com/gyso/treeview/layout/BoxLeftTreeLayoutManager.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview.layout; 2 | 3 | import android.content.Context; 4 | import android.view.View; 5 | 6 | import com.gyso.treeview.TreeViewContainer; 7 | import com.gyso.treeview.adapter.TreeViewHolder; 8 | import com.gyso.treeview.line.BaseLine; 9 | import com.gyso.treeview.model.ITraversal; 10 | import com.gyso.treeview.model.NodeModel; 11 | import com.gyso.treeview.model.TreeModel; 12 | import com.gyso.treeview.util.ViewBox; 13 | 14 | public class BoxLeftTreeLayoutManager extends BoxRightTreeLayoutManager{ 15 | private static final String TAG = BoxLeftTreeLayoutManager.class.getSimpleName(); 16 | private boolean isJustCalculate; 17 | public BoxLeftTreeLayoutManager(Context context, int spaceParentToChild, int spacePeerToPeer, BaseLine baseline) { 18 | super(context, spaceParentToChild, spacePeerToPeer, baseline); 19 | } 20 | 21 | @Override 22 | public int getTreeLayoutType() { 23 | return LAYOUT_TYPE_HORIZON_LEFT; 24 | } 25 | 26 | @Override 27 | public void performLayout(final TreeViewContainer treeViewContainer) { 28 | isJustCalculate = true; 29 | super.performLayout(treeViewContainer); 30 | isJustCalculate = false; 31 | final TreeModel mTreeModel = treeViewContainer.getTreeModel(); 32 | if (mTreeModel != null) { 33 | final int cx = fixedViewBox.getWidth()/2; 34 | mTreeModel.doTraversalNodes(new ITraversal>() { 35 | @Override 36 | public void next(NodeModel next) { 37 | mirrorByCx(next,treeViewContainer,cx); 38 | } 39 | @Override 40 | public void finish() { 41 | onManagerFinishLayoutAllNodes(treeViewContainer); 42 | } 43 | }); 44 | } 45 | } 46 | 47 | private void mirrorByCx(NodeModel currentNode, TreeViewContainer treeViewContainer,int centerX){ 48 | TreeViewHolder currentHolder = treeViewContainer.getTreeViewHolder(currentNode); 49 | View currentNodeView = currentHolder == null ? null : currentHolder.getView(); 50 | if (currentNodeView == null) { 51 | throw new NullPointerException(" currentNodeView can not be null"); 52 | } 53 | int left = centerX*2 - currentNodeView.getRight(); 54 | int right = centerX*2- currentNodeView.getLeft(); 55 | int top = currentNodeView.getTop(); 56 | int bottom =currentNodeView.getBottom(); 57 | ViewBox finalLocation = new ViewBox(top, left, bottom, right); 58 | onManagerLayoutNode(currentNode, currentNodeView, finalLocation, treeViewContainer); 59 | } 60 | 61 | @Override 62 | public void onManagerLayoutNode(NodeModel currentNode, View currentNodeView, ViewBox finalLocation, TreeViewContainer treeViewContainer) { 63 | if(isJustCalculate){ 64 | currentNodeView.layout(finalLocation.left, finalLocation.top, finalLocation.right, finalLocation.bottom); 65 | return; 66 | } 67 | if (!layoutAnimatePrepare(currentNode, currentNodeView, finalLocation, treeViewContainer)) { 68 | currentNodeView.layout(finalLocation.left, finalLocation.top, finalLocation.right, finalLocation.bottom); 69 | } 70 | } 71 | 72 | @Override 73 | public void onManagerFinishLayoutAllNodes(TreeViewContainer treeViewContainer) { 74 | if(!isJustCalculate){ 75 | super.onManagerFinishLayoutAllNodes(treeViewContainer); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /node/src/main/java/com/gyso/treeview/layout/BoxUpTreeLayoutManager.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview.layout; 2 | 3 | import android.content.Context; 4 | import android.view.View; 5 | 6 | import com.gyso.treeview.TreeViewContainer; 7 | import com.gyso.treeview.adapter.TreeViewHolder; 8 | import com.gyso.treeview.line.BaseLine; 9 | import com.gyso.treeview.model.ITraversal; 10 | import com.gyso.treeview.model.NodeModel; 11 | import com.gyso.treeview.model.TreeModel; 12 | import com.gyso.treeview.util.ViewBox; 13 | 14 | public class BoxUpTreeLayoutManager extends BoxDownTreeLayoutManager { 15 | private static final String TAG = BoxUpTreeLayoutManager.class.getSimpleName(); 16 | private boolean isJustCalculate; 17 | public BoxUpTreeLayoutManager(Context context, int spaceParentToChild, int spacePeerToPeer, BaseLine baseline) { 18 | super(context, spaceParentToChild, spacePeerToPeer, baseline); 19 | } 20 | @Override 21 | public int getTreeLayoutType() { 22 | return LAYOUT_TYPE_VERTICAL_UP; 23 | } 24 | 25 | @Override 26 | public void performLayout(final TreeViewContainer treeViewContainer) { 27 | isJustCalculate = true; 28 | super.performLayout(treeViewContainer); 29 | isJustCalculate = false; 30 | final TreeModel mTreeModel = treeViewContainer.getTreeModel(); 31 | if (mTreeModel != null) { 32 | final int cy= fixedViewBox.getHeight()/2; 33 | mTreeModel.doTraversalNodes(new ITraversal>() { 34 | @Override 35 | public void next(NodeModel next) { 36 | mirrorByCy(next,treeViewContainer,cy); 37 | } 38 | @Override 39 | public void finish() { 40 | onManagerFinishLayoutAllNodes(treeViewContainer); 41 | } 42 | }); 43 | } 44 | } 45 | 46 | private void mirrorByCy(NodeModel currentNode, TreeViewContainer treeViewContainer,int centerY){ 47 | TreeViewHolder currentHolder = treeViewContainer.getTreeViewHolder(currentNode); 48 | View currentNodeView = currentHolder == null ? null : currentHolder.getView(); 49 | if (currentNodeView == null) { 50 | throw new NullPointerException(" currentNodeView can not be null"); 51 | } 52 | int left =currentNodeView.getLeft(); 53 | int right = currentNodeView.getRight(); 54 | int bottom= centerY*2- currentNodeView.getTop(); 55 | int top = centerY*2- currentNodeView.getBottom(); 56 | ViewBox finalLocation = new ViewBox(top, left, bottom, right); 57 | onManagerLayoutNode(currentNode, currentNodeView, finalLocation, treeViewContainer); 58 | } 59 | 60 | @Override 61 | public void onManagerLayoutNode(NodeModel currentNode, View currentNodeView, ViewBox finalLocation, TreeViewContainer treeViewContainer) { 62 | if(isJustCalculate){ 63 | currentNodeView.layout(finalLocation.left, finalLocation.top, finalLocation.right, finalLocation.bottom); 64 | return; 65 | } 66 | if (!layoutAnimatePrepare(currentNode, currentNodeView, finalLocation, treeViewContainer)) { 67 | currentNodeView.layout(finalLocation.left, finalLocation.top, finalLocation.right, finalLocation.bottom); 68 | } 69 | } 70 | 71 | @Override 72 | public void onManagerFinishLayoutAllNodes(TreeViewContainer treeViewContainer) { 73 | if(!isJustCalculate){ 74 | super.onManagerFinishLayoutAllNodes(treeViewContainer); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /node/src/main/java/com/gyso/treeview/layout/CompactLeftTreeLayoutManager.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview.layout; 2 | 3 | import android.content.Context; 4 | import android.view.View; 5 | 6 | import com.gyso.treeview.TreeViewContainer; 7 | import com.gyso.treeview.adapter.TreeViewHolder; 8 | import com.gyso.treeview.line.BaseLine; 9 | import com.gyso.treeview.model.ITraversal; 10 | import com.gyso.treeview.model.NodeModel; 11 | import com.gyso.treeview.model.TreeModel; 12 | import com.gyso.treeview.util.ViewBox; 13 | 14 | public class CompactLeftTreeLayoutManager extends CompactRightTreeLayoutManager{ 15 | private static final String TAG = TableLeftTreeLayoutManager.class.getSimpleName(); 16 | private boolean isJustCalculate; 17 | public CompactLeftTreeLayoutManager(Context context, int spaceParentToChild, int spacePeerToPeer, BaseLine baseline) { 18 | super(context, spaceParentToChild, spacePeerToPeer, baseline); 19 | } 20 | 21 | @Override 22 | public int getTreeLayoutType() { 23 | return LAYOUT_TYPE_HORIZON_LEFT; 24 | } 25 | 26 | @Override 27 | public void performLayout(final TreeViewContainer treeViewContainer) { 28 | isJustCalculate = true; 29 | super.performLayout(treeViewContainer); 30 | isJustCalculate = false; 31 | final TreeModel mTreeModel = treeViewContainer.getTreeModel(); 32 | if (mTreeModel != null) { 33 | final int cx = fixedViewBox.getWidth()/2; 34 | mTreeModel.doTraversalNodes(new ITraversal>() { 35 | @Override 36 | public void next(NodeModel next) { 37 | mirrorByCx(next,treeViewContainer,cx); 38 | } 39 | @Override 40 | public void finish() { 41 | onManagerFinishLayoutAllNodes(treeViewContainer); 42 | } 43 | }); 44 | } 45 | } 46 | 47 | private void mirrorByCx(NodeModel currentNode, TreeViewContainer treeViewContainer,int centerX){ 48 | TreeViewHolder currentHolder = treeViewContainer.getTreeViewHolder(currentNode); 49 | View currentNodeView = currentHolder == null ? null : currentHolder.getView(); 50 | if (currentNodeView == null) { 51 | throw new NullPointerException(" currentNodeView can not be null"); 52 | } 53 | int left = centerX*2 - currentNodeView.getRight(); 54 | int right = centerX*2- currentNodeView.getLeft(); 55 | int top = currentNodeView.getTop(); 56 | int bottom =currentNodeView.getBottom(); 57 | ViewBox finalLocation = new ViewBox(top, left, bottom, right); 58 | onManagerLayoutNode(currentNode, currentNodeView, finalLocation, treeViewContainer); 59 | } 60 | 61 | @Override 62 | public void onManagerLayoutNode(NodeModel currentNode, View currentNodeView, ViewBox finalLocation, TreeViewContainer treeViewContainer) { 63 | if(isJustCalculate){ 64 | currentNodeView.layout(finalLocation.left, finalLocation.top, finalLocation.right, finalLocation.bottom); 65 | return; 66 | } 67 | if (!layoutAnimatePrepare(currentNode, currentNodeView, finalLocation, treeViewContainer)) { 68 | currentNodeView.layout(finalLocation.left, finalLocation.top, finalLocation.right, finalLocation.bottom); 69 | } 70 | } 71 | 72 | @Override 73 | public void onManagerFinishLayoutAllNodes(TreeViewContainer treeViewContainer) { 74 | if(!isJustCalculate){ 75 | super.onManagerFinishLayoutAllNodes(treeViewContainer); 76 | } 77 | } 78 | } 79 | 80 | -------------------------------------------------------------------------------- /node/src/main/java/com/gyso/treeview/layout/CompactUpTreeLayoutManager.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview.layout; 2 | 3 | import android.content.Context; 4 | import android.view.View; 5 | 6 | import com.gyso.treeview.TreeViewContainer; 7 | import com.gyso.treeview.adapter.TreeViewHolder; 8 | import com.gyso.treeview.line.BaseLine; 9 | import com.gyso.treeview.model.ITraversal; 10 | import com.gyso.treeview.model.NodeModel; 11 | import com.gyso.treeview.model.TreeModel; 12 | import com.gyso.treeview.util.ViewBox; 13 | 14 | public class CompactUpTreeLayoutManager extends CompactDownTreeLayoutManager { 15 | private static final String TAG = CompactUpTreeLayoutManager.class.getSimpleName(); 16 | private boolean isJustCalculate; 17 | public CompactUpTreeLayoutManager(Context context, int spaceParentToChild, int spacePeerToPeer, BaseLine baseline) { 18 | super(context, spaceParentToChild, spacePeerToPeer, baseline); 19 | } 20 | @Override 21 | public int getTreeLayoutType() { 22 | return LAYOUT_TYPE_VERTICAL_UP; 23 | } 24 | 25 | @Override 26 | public void performLayout(final TreeViewContainer treeViewContainer) { 27 | isJustCalculate = true; 28 | super.performLayout(treeViewContainer); 29 | isJustCalculate = false; 30 | final TreeModel mTreeModel = treeViewContainer.getTreeModel(); 31 | if (mTreeModel != null) { 32 | final int cy= fixedViewBox.getHeight()/2; 33 | mTreeModel.doTraversalNodes(new ITraversal>() { 34 | @Override 35 | public void next(NodeModel next) { 36 | mirrorByCy(next,treeViewContainer,cy); 37 | } 38 | @Override 39 | public void finish() { 40 | onManagerFinishLayoutAllNodes(treeViewContainer); 41 | } 42 | }); 43 | } 44 | } 45 | 46 | private void mirrorByCy(NodeModel currentNode, TreeViewContainer treeViewContainer,int centerY){ 47 | TreeViewHolder currentHolder = treeViewContainer.getTreeViewHolder(currentNode); 48 | View currentNodeView = currentHolder == null ? null : currentHolder.getView(); 49 | if (currentNodeView == null) { 50 | throw new NullPointerException(" currentNodeView can not be null"); 51 | } 52 | int left =currentNodeView.getLeft(); 53 | int right = currentNodeView.getRight(); 54 | int bottom= centerY*2- currentNodeView.getTop(); 55 | int top = centerY*2- currentNodeView.getBottom(); 56 | ViewBox finalLocation = new ViewBox(top, left, bottom, right); 57 | onManagerLayoutNode(currentNode, currentNodeView, finalLocation, treeViewContainer); 58 | } 59 | 60 | @Override 61 | public void onManagerLayoutNode(NodeModel currentNode, View currentNodeView, ViewBox finalLocation, TreeViewContainer treeViewContainer) { 62 | if(isJustCalculate){ 63 | currentNodeView.layout(finalLocation.left, finalLocation.top, finalLocation.right, finalLocation.bottom); 64 | return; 65 | } 66 | if (!layoutAnimatePrepare(currentNode, currentNodeView, finalLocation, treeViewContainer)) { 67 | currentNodeView.layout(finalLocation.left, finalLocation.top, finalLocation.right, finalLocation.bottom); 68 | } 69 | } 70 | 71 | @Override 72 | public void onManagerFinishLayoutAllNodes(TreeViewContainer treeViewContainer) { 73 | if(!isJustCalculate){ 74 | super.onManagerFinishLayoutAllNodes(treeViewContainer); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /node/src/main/java/com/gyso/treeview/layout/TableLeftTreeLayoutManager.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview.layout; 2 | 3 | import android.content.Context; 4 | import android.view.View; 5 | import com.gyso.treeview.TreeViewContainer; 6 | import com.gyso.treeview.adapter.TreeViewHolder; 7 | import com.gyso.treeview.line.BaseLine; 8 | import com.gyso.treeview.model.ITraversal; 9 | import com.gyso.treeview.model.NodeModel; 10 | import com.gyso.treeview.model.TreeModel; 11 | import com.gyso.treeview.util.ViewBox; 12 | 13 | public class TableLeftTreeLayoutManager extends TableRightTreeLayoutManager { 14 | private static final String TAG = TableLeftTreeLayoutManager.class.getSimpleName(); 15 | private boolean isJustCalculate; 16 | public TableLeftTreeLayoutManager(Context context, int spaceParentToChild, int spacePeerToPeer, BaseLine baseline) { 17 | super(context, spaceParentToChild, spacePeerToPeer, baseline); 18 | } 19 | 20 | @Override 21 | public int getTreeLayoutType() { 22 | return LAYOUT_TYPE_HORIZON_LEFT; 23 | } 24 | 25 | @Override 26 | public void performLayout(final TreeViewContainer treeViewContainer) { 27 | isJustCalculate = true; 28 | super.performLayout(treeViewContainer); 29 | isJustCalculate = false; 30 | final TreeModel mTreeModel = treeViewContainer.getTreeModel(); 31 | if (mTreeModel != null) { 32 | final int cx = fixedViewBox.getWidth()/2; 33 | mTreeModel.doTraversalNodes(new ITraversal>() { 34 | @Override 35 | public void next(NodeModel next) { 36 | mirrorByCx(next,treeViewContainer,cx); 37 | } 38 | @Override 39 | public void finish() { 40 | onManagerFinishLayoutAllNodes(treeViewContainer); 41 | } 42 | }); 43 | } 44 | } 45 | 46 | private void mirrorByCx(NodeModel currentNode, TreeViewContainer treeViewContainer,int centerX){ 47 | TreeViewHolder currentHolder = treeViewContainer.getTreeViewHolder(currentNode); 48 | View currentNodeView = currentHolder == null ? null : currentHolder.getView(); 49 | if (currentNodeView == null) { 50 | throw new NullPointerException(" currentNodeView can not be null"); 51 | } 52 | int left = centerX*2 - currentNodeView.getRight(); 53 | int right = centerX*2- currentNodeView.getLeft(); 54 | int top = currentNodeView.getTop(); 55 | int bottom =currentNodeView.getBottom(); 56 | ViewBox finalLocation = new ViewBox(top, left, bottom, right); 57 | onManagerLayoutNode(currentNode, currentNodeView, finalLocation, treeViewContainer); 58 | } 59 | 60 | @Override 61 | public void onManagerLayoutNode(NodeModel currentNode, View currentNodeView, ViewBox finalLocation, TreeViewContainer treeViewContainer) { 62 | if(isJustCalculate){ 63 | currentNodeView.layout(finalLocation.left, finalLocation.top, finalLocation.right, finalLocation.bottom); 64 | return; 65 | } 66 | if (!layoutAnimatePrepare(currentNode, currentNodeView, finalLocation, treeViewContainer)) { 67 | currentNodeView.layout(finalLocation.left, finalLocation.top, finalLocation.right, finalLocation.bottom); 68 | } 69 | } 70 | 71 | @Override 72 | public void onManagerFinishLayoutAllNodes(TreeViewContainer treeViewContainer) { 73 | if(!isJustCalculate){ 74 | super.onManagerFinishLayoutAllNodes(treeViewContainer); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /node/src/main/java/com/gyso/treeview/layout/TableUpTreeLayoutManagerTable.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview.layout; 2 | 3 | import android.content.Context; 4 | import android.view.View; 5 | 6 | import com.gyso.treeview.TreeViewContainer; 7 | import com.gyso.treeview.adapter.TreeViewHolder; 8 | import com.gyso.treeview.line.BaseLine; 9 | import com.gyso.treeview.model.ITraversal; 10 | import com.gyso.treeview.model.NodeModel; 11 | import com.gyso.treeview.model.TreeModel; 12 | import com.gyso.treeview.util.ViewBox; 13 | 14 | public class TableUpTreeLayoutManagerTable extends TableDownTreeLayoutManager { 15 | private static final String TAG = TableUpTreeLayoutManagerTable.class.getSimpleName(); 16 | private boolean isJustCalculate; 17 | public TableUpTreeLayoutManagerTable(Context context, int spaceParentToChild, int spacePeerToPeer, BaseLine baseline) { 18 | super(context, spaceParentToChild, spacePeerToPeer, baseline); 19 | } 20 | @Override 21 | public int getTreeLayoutType() { 22 | return LAYOUT_TYPE_VERTICAL_UP; 23 | } 24 | 25 | @Override 26 | public void performLayout(final TreeViewContainer treeViewContainer) { 27 | isJustCalculate = true; 28 | super.performLayout(treeViewContainer); 29 | isJustCalculate = false; 30 | final TreeModel mTreeModel = treeViewContainer.getTreeModel(); 31 | if (mTreeModel != null) { 32 | final int cy= fixedViewBox.getHeight()/2; 33 | mTreeModel.doTraversalNodes(new ITraversal>() { 34 | @Override 35 | public void next(NodeModel next) { 36 | mirrorByCy(next,treeViewContainer,cy); 37 | } 38 | @Override 39 | public void finish() { 40 | onManagerFinishLayoutAllNodes(treeViewContainer); 41 | } 42 | }); 43 | } 44 | } 45 | 46 | private void mirrorByCy(NodeModel currentNode, TreeViewContainer treeViewContainer,int centerY){ 47 | TreeViewHolder currentHolder = treeViewContainer.getTreeViewHolder(currentNode); 48 | View currentNodeView = currentHolder == null ? null : currentHolder.getView(); 49 | if (currentNodeView == null) { 50 | throw new NullPointerException(" currentNodeView can not be null"); 51 | } 52 | int left =currentNodeView.getLeft(); 53 | int right = currentNodeView.getRight(); 54 | int bottom= centerY*2- currentNodeView.getTop(); 55 | int top = centerY*2- currentNodeView.getBottom(); 56 | ViewBox finalLocation = new ViewBox(top, left, bottom, right); 57 | onManagerLayoutNode(currentNode, currentNodeView, finalLocation, treeViewContainer); 58 | } 59 | 60 | @Override 61 | public void onManagerLayoutNode(NodeModel currentNode, View currentNodeView, ViewBox finalLocation, TreeViewContainer treeViewContainer) { 62 | if(isJustCalculate){ 63 | currentNodeView.layout(finalLocation.left, finalLocation.top, finalLocation.right, finalLocation.bottom); 64 | return; 65 | } 66 | if (!layoutAnimatePrepare(currentNode, currentNodeView, finalLocation, treeViewContainer)) { 67 | currentNodeView.layout(finalLocation.left, finalLocation.top, finalLocation.right, finalLocation.bottom); 68 | } 69 | } 70 | 71 | @Override 72 | public void onManagerFinishLayoutAllNodes(TreeViewContainer treeViewContainer) { 73 | if(!isJustCalculate){ 74 | super.onManagerFinishLayoutAllNodes(treeViewContainer); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /node/src/main/java/com/gyso/treeview/line/AngledLine.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview.line; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Color; 6 | import android.graphics.Paint; 7 | import android.graphics.Path; 8 | import android.graphics.PointF; 9 | import android.view.View; 10 | 11 | import com.gyso.treeview.adapter.DrawInfo; 12 | import com.gyso.treeview.adapter.TreeViewHolder; 13 | import com.gyso.treeview.cache_pool.PointPool; 14 | import com.gyso.treeview.layout.TreeLayoutManager; 15 | import com.gyso.treeview.model.NodeModel; 16 | import com.gyso.treeview.util.DensityUtils; 17 | 18 | /** 19 | * @Author: 怪兽N 20 | * @Time: 2021/5/18 16:47 21 | * @Email: 674149099@qq.com 22 | * @WeChat: guaishouN 23 | * @Describe: 24 | * AngledLine between two nodes 25 | */ 26 | public class AngledLine extends BaseLine { 27 | public static final int DEFAULT_LINE_WIDTH_DP = 3; 28 | private int lineColor = Color.parseColor("#055287"); 29 | private int lineWidth = DEFAULT_LINE_WIDTH_DP; 30 | public AngledLine() { 31 | super(); 32 | } 33 | 34 | public AngledLine(int lineColor, int lineWidth_dp) { 35 | this(); 36 | this.lineColor = lineColor; 37 | this.lineWidth = lineWidth_dp; 38 | } 39 | 40 | public void setLineColor(int lineColor) { 41 | this.lineColor = lineColor; 42 | } 43 | 44 | public void setLineWidth(int lineWidth) { 45 | this.lineWidth = lineWidth; 46 | } 47 | @Override 48 | public void draw(DrawInfo drawInfo) { 49 | Canvas canvas = drawInfo.getCanvas(); 50 | TreeViewHolder fromHolder = drawInfo.getFromHolder(); 51 | TreeViewHolder toHolder = drawInfo.getToHolder(); 52 | Paint mPaint = drawInfo.getPaint(); 53 | Path mPath = drawInfo.getPath(); 54 | int holderLayoutType = toHolder.getHolderLayoutType(); 55 | int spacePeerToPeer = drawInfo.getSpacePeerToPeer(); 56 | int spaceParentToChild = drawInfo.getSpaceParentToChild(); 57 | 58 | //get view and node 59 | View fromView = fromHolder.getView(); 60 | NodeModel fromNode = fromHolder.getNode(); 61 | View toView = toHolder.getView(); 62 | NodeModel toNode = toHolder.getNode(); 63 | Context context = fromView.getContext(); 64 | 65 | PointF startPoint,point1,endPoint,point2; 66 | if(holderLayoutType== TreeLayoutManager.LAYOUT_TYPE_HORIZON_RIGHT){ 67 | startPoint = PointPool.obtain(fromView.getRight(),(fromView.getTop()+fromView.getBottom())/2f); 68 | point1 = PointPool.obtain(startPoint.x+spaceParentToChild/3f,startPoint.y); 69 | endPoint = PointPool.obtain(toView.getLeft(),(toView.getTop()+toView.getBottom())/2f); 70 | point2 = PointPool.obtain(startPoint.x+spaceParentToChild/3f,endPoint.y); 71 | 72 | }else if(holderLayoutType== TreeLayoutManager.LAYOUT_TYPE_HORIZON_LEFT){ 73 | startPoint = PointPool.obtain(fromView.getLeft(),(fromView.getTop()+fromView.getBottom())/2f); 74 | point1 = PointPool.obtain(startPoint.x-spaceParentToChild/3f,startPoint.y); 75 | endPoint = PointPool.obtain(toView.getRight(),(toView.getTop()+toView.getBottom())/2f); 76 | point2 = PointPool.obtain(startPoint.x-spaceParentToChild/3f,endPoint.y); 77 | 78 | }else if(holderLayoutType== TreeLayoutManager.LAYOUT_TYPE_VERTICAL_DOWN){ 79 | startPoint = PointPool.obtain((fromView.getLeft()+fromView.getRight())/2f,fromView.getBottom()); 80 | point1 = PointPool.obtain(startPoint.x,startPoint.y+spaceParentToChild/3f); 81 | endPoint = PointPool.obtain((toView.getLeft()+toView.getRight())/2f,toView.getTop()); 82 | point2 = PointPool.obtain(endPoint.x,startPoint.y+spaceParentToChild/3f); 83 | 84 | }else if(holderLayoutType== TreeLayoutManager.LAYOUT_TYPE_VERTICAL_UP){ 85 | startPoint = PointPool.obtain((fromView.getLeft()+fromView.getRight())/2f,fromView.getTop()); 86 | point1 = PointPool.obtain(startPoint.x,startPoint.y-spaceParentToChild/3f); 87 | endPoint = PointPool.obtain((toView.getLeft()+toView.getRight())/2f,toView.getBottom()); 88 | point2 = PointPool.obtain(endPoint.x,startPoint.y-spaceParentToChild/3f); 89 | 90 | }else{ 91 | super.draw(drawInfo); 92 | return; 93 | } 94 | 95 | //set paint 96 | mPath.reset(); 97 | mPaint.reset(); 98 | mPaint.setColor(lineColor); 99 | mPaint.setStyle(Paint.Style.STROKE); 100 | mPaint.setStrokeWidth(DensityUtils.dp2px(context,lineWidth)); 101 | mPaint.setAntiAlias(true); 102 | 103 | mPath.moveTo(startPoint.x,startPoint.y); 104 | mPath.lineTo(point1.x,point1.y); 105 | mPath.lineTo(point2.x,point2.y); 106 | mPath.lineTo(endPoint.x,endPoint.y); 107 | 108 | //do not forget release 109 | PointPool.free(startPoint); 110 | PointPool.free(point1); 111 | PointPool.free(point2); 112 | PointPool.free(endPoint); 113 | //draw 114 | canvas.drawPath(mPath,mPaint); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /node/src/main/java/com/gyso/treeview/line/BaseLine.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview.line; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Color; 6 | import android.graphics.Paint; 7 | import android.graphics.Path; 8 | import android.view.View; 9 | import com.gyso.treeview.adapter.DrawInfo; 10 | import com.gyso.treeview.adapter.TreeViewHolder; 11 | import com.gyso.treeview.util.DensityUtils; 12 | 13 | /** 14 | * @Author: 怪兽N 15 | * @Time: 2021/5/7 21:02 16 | * @Email: 674149099@qq.com 17 | * @WeChat: guaishouN 18 | * @Describe: 19 | * line to connect the fromNodeView and toNodeView 20 | */ 21 | public class BaseLine { 22 | /** 23 | * this method will be invoke when the tree view is onDispatchDraw 24 | */ 25 | public void draw(DrawInfo drawInfo){ 26 | Canvas canvas = drawInfo.getCanvas(); 27 | TreeViewHolder fromHolder = drawInfo.getFromHolder(); 28 | TreeViewHolder toHolder = drawInfo.getToHolder(); 29 | Paint mPaint = drawInfo.getPaint(); 30 | Path mPath = drawInfo.getPath(); 31 | 32 | //get view and node 33 | View fromView = fromHolder.getView(); 34 | View toView = toHolder.getView(); 35 | Context context = fromView.getContext(); 36 | 37 | //set paint 38 | mPaint.reset(); 39 | mPath.reset(); 40 | mPaint.setColor(Color.MAGENTA); 41 | mPaint.setStyle(Paint.Style.STROKE); 42 | mPaint.setStrokeWidth(DensityUtils.dp2px(context,3)); 43 | mPaint.setAntiAlias(true); 44 | 45 | int fromCenterX = (fromView.getLeft()+fromView.getRight())/2; 46 | int fromCenterY = (fromView.getTop()+fromView.getBottom())/2; 47 | int toCenterX = (toView.getLeft()+toView.getRight())/2; 48 | int toCenterY = (toView.getTop()+toView.getBottom())/2; 49 | 50 | mPath.moveTo(fromCenterX, fromCenterY); 51 | mPath.lineTo(toCenterX, toCenterY); 52 | 53 | //draw 54 | canvas.drawPath(mPath,mPaint); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /node/src/main/java/com/gyso/treeview/line/DashLine.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview.line; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Color; 6 | import android.graphics.DashPathEffect; 7 | import android.graphics.Paint; 8 | import android.graphics.Path; 9 | import android.graphics.PointF; 10 | import android.view.View; 11 | 12 | import com.gyso.treeview.adapter.DrawInfo; 13 | import com.gyso.treeview.adapter.TreeViewHolder; 14 | import com.gyso.treeview.cache_pool.PointPool; 15 | import com.gyso.treeview.layout.TreeLayoutManager; 16 | import com.gyso.treeview.model.NodeModel; 17 | import com.gyso.treeview.util.DensityUtils; 18 | 19 | /** 20 | * @Author: 怪兽N 21 | * @Time: 2021/5/8 17:41 22 | * @Email: 674149099@qq.com 23 | * @WeChat: guaishouN 24 | * @Describe: 25 | * DashPathEffect line 26 | */ 27 | public class DashLine extends BaseLine { 28 | public static final int DEFAULT_LINE_WIDTH_DP = 3; 29 | private int lineColor = Color.parseColor("#E57373"); 30 | private int lineWidth = DEFAULT_LINE_WIDTH_DP; 31 | private final static DashPathEffect effect = new DashPathEffect(new float[]{20,10},10); 32 | public DashLine() { 33 | super(); 34 | } 35 | 36 | public DashLine(int lineColor, int lineWidth_dp) { 37 | this(); 38 | this.lineColor = lineColor; 39 | this.lineWidth = lineWidth_dp; 40 | } 41 | 42 | public void setLineColor(int lineColor) { 43 | this.lineColor = lineColor; 44 | } 45 | 46 | public void setLineWidth(int lineWidth) { 47 | this.lineWidth = lineWidth; 48 | } 49 | 50 | @Override 51 | public void draw(DrawInfo drawInfo) { 52 | Canvas canvas = drawInfo.getCanvas(); 53 | TreeViewHolder fromHolder = drawInfo.getFromHolder(); 54 | TreeViewHolder toHolder = drawInfo.getToHolder(); 55 | Paint mPaint = drawInfo.getPaint(); 56 | Path mPath = drawInfo.getPath(); 57 | int holderLayoutType = toHolder.getHolderLayoutType(); 58 | int spacePeerToPeer = drawInfo.getSpacePeerToPeer(); 59 | int spaceParentToChild = drawInfo.getSpaceParentToChild(); 60 | 61 | //get view and node 62 | View fromView = fromHolder.getView(); 63 | NodeModel fromNode = fromHolder.getNode(); 64 | View toView = toHolder.getView(); 65 | NodeModel toNode = toHolder.getNode(); 66 | Context context = fromView.getContext(); 67 | 68 | PointF startPoint,point1,endPoint,point2; 69 | if(holderLayoutType== TreeLayoutManager.LAYOUT_TYPE_HORIZON_RIGHT){ 70 | startPoint = PointPool.obtain(fromView.getRight(),(fromView.getTop()+fromView.getBottom())/2f); 71 | point1 = PointPool.obtain(startPoint.x+DensityUtils.dp2px(context,15),startPoint.y); 72 | endPoint = PointPool.obtain(toView.getLeft(),(toView.getTop()+toView.getBottom())/2f); 73 | point2 = PointPool.obtain(startPoint.x,endPoint.y); 74 | }else if(holderLayoutType== TreeLayoutManager.LAYOUT_TYPE_HORIZON_LEFT){ 75 | startPoint = PointPool.obtain(fromView.getLeft(),(fromView.getTop()+fromView.getBottom())/2f); 76 | point1 = PointPool.obtain(startPoint.x-DensityUtils.dp2px(context,15),startPoint.y); 77 | endPoint = PointPool.obtain(toView.getRight(),(toView.getTop()+toView.getBottom())/2f); 78 | point2 = PointPool.obtain(startPoint.x,endPoint.y); 79 | }else if(holderLayoutType== TreeLayoutManager.LAYOUT_TYPE_VERTICAL_DOWN){ 80 | startPoint = PointPool.obtain((fromView.getLeft()+fromView.getRight())/2f,fromView.getBottom()); 81 | point1 = PointPool.obtain(startPoint.x,startPoint.y+DensityUtils.dp2px(context,15)); 82 | endPoint = PointPool.obtain((toView.getLeft()+toView.getRight())/2f,toView.getTop()); 83 | point2 = PointPool.obtain(endPoint.x,startPoint.y); 84 | }else if(holderLayoutType== TreeLayoutManager.LAYOUT_TYPE_VERTICAL_UP){ 85 | startPoint = PointPool.obtain((fromView.getLeft()+fromView.getRight())/2f,fromView.getTop()); 86 | point1 = PointPool.obtain(startPoint.x,startPoint.y-DensityUtils.dp2px(context,15)); 87 | endPoint = PointPool.obtain((toView.getLeft()+toView.getRight())/2f,toView.getBottom()); 88 | point2 = PointPool.obtain(endPoint.x,startPoint.y); 89 | }else{ 90 | super.draw(drawInfo); 91 | return; 92 | } 93 | 94 | mPaint.reset(); 95 | mPath.reset(); 96 | //set paint 97 | mPaint.setColor(lineColor); 98 | mPaint.setStyle(Paint.Style.STROKE); 99 | mPaint.setStrokeWidth(DensityUtils.dp2px(context,lineWidth)); 100 | mPaint.setAntiAlias(true); 101 | mPaint.setPathEffect(effect); 102 | 103 | mPath.moveTo(startPoint.x,startPoint.y); 104 | mPath.cubicTo( 105 | point1.x,point1.y, 106 | point2.x,point2.y, 107 | endPoint.x,endPoint.y); 108 | 109 | //do not forget release 110 | PointPool.free(startPoint); 111 | PointPool.free(point1); 112 | PointPool.free(point2); 113 | PointPool.free(endPoint); 114 | //draw 115 | canvas.drawPath(mPath,mPaint); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /node/src/main/java/com/gyso/treeview/line/SmoothLine.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview.line; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Color; 6 | import android.graphics.Paint; 7 | import android.graphics.Path; 8 | import android.graphics.PointF; 9 | import android.view.View; 10 | 11 | import com.gyso.treeview.adapter.DrawInfo; 12 | import com.gyso.treeview.adapter.TreeViewHolder; 13 | import com.gyso.treeview.cache_pool.PointPool; 14 | import com.gyso.treeview.layout.TreeLayoutManager; 15 | import com.gyso.treeview.model.NodeModel; 16 | import com.gyso.treeview.util.DensityUtils; 17 | 18 | /** 19 | * @Author: 怪兽N 20 | * @Time: 2021/5/8 9:47 21 | * @Email: 674149099@qq.com 22 | * @WeChat: guaishouN 23 | * @Describe: 24 | * Simple smooth line 25 | */ 26 | public class SmoothLine extends BaseLine { 27 | public static final int DEFAULT_LINE_WIDTH_DP = 3; 28 | private int lineColor = Color.parseColor("#055287"); 29 | private int lineWidth = DEFAULT_LINE_WIDTH_DP; 30 | public SmoothLine() { 31 | super(); 32 | } 33 | 34 | public SmoothLine(int lineColor, int lineWidth_dp) { 35 | this(); 36 | this.lineColor = lineColor; 37 | this.lineWidth = lineWidth_dp; 38 | } 39 | 40 | public void setLineColor(int lineColor) { 41 | this.lineColor = lineColor; 42 | } 43 | 44 | public void setLineWidth(int lineWidth) { 45 | this.lineWidth = lineWidth; 46 | } 47 | 48 | @Override 49 | public void draw(DrawInfo drawInfo) { 50 | Canvas canvas = drawInfo.getCanvas(); 51 | TreeViewHolder fromHolder = drawInfo.getFromHolder(); 52 | TreeViewHolder toHolder = drawInfo.getToHolder(); 53 | Paint mPaint = drawInfo.getPaint(); 54 | Path mPath = drawInfo.getPath(); 55 | int holderLayoutType = toHolder.getHolderLayoutType(); 56 | int spacePeerToPeer = drawInfo.getSpacePeerToPeer(); 57 | int spaceParentToChild = drawInfo.getSpaceParentToChild(); 58 | 59 | //get view and node 60 | View fromView = fromHolder.getView(); 61 | NodeModel fromNode = fromHolder.getNode(); 62 | View toView = toHolder.getView(); 63 | NodeModel toNode = toHolder.getNode(); 64 | Context context = fromView.getContext(); 65 | 66 | PointF startPoint,point1,endPoint,point2; 67 | if(holderLayoutType== TreeLayoutManager.LAYOUT_TYPE_HORIZON_RIGHT){ 68 | startPoint = PointPool.obtain(fromView.getRight(),(fromView.getTop()+fromView.getBottom())/2f); 69 | point1 = PointPool.obtain(startPoint.x+DensityUtils.dp2px(context,15),startPoint.y); 70 | endPoint = PointPool.obtain(toView.getLeft(),(toView.getTop()+toView.getBottom())/2f); 71 | point2 = PointPool.obtain(startPoint.x,endPoint.y); 72 | 73 | }else if(holderLayoutType== TreeLayoutManager.LAYOUT_TYPE_HORIZON_LEFT){ 74 | startPoint = PointPool.obtain(fromView.getLeft(),(fromView.getTop()+fromView.getBottom())/2f); 75 | point1 = PointPool.obtain(startPoint.x-DensityUtils.dp2px(context,15),startPoint.y); 76 | endPoint = PointPool.obtain(toView.getRight(),(toView.getTop()+toView.getBottom())/2f); 77 | point2 = PointPool.obtain(startPoint.x,endPoint.y); 78 | 79 | }else if(holderLayoutType== TreeLayoutManager.LAYOUT_TYPE_VERTICAL_DOWN){ 80 | startPoint = PointPool.obtain((fromView.getLeft()+fromView.getRight())/2f,fromView.getBottom()); 81 | point1 = PointPool.obtain(startPoint.x,startPoint.y+DensityUtils.dp2px(context,15)); 82 | endPoint = PointPool.obtain((toView.getLeft()+toView.getRight())/2f,toView.getTop()); 83 | point2 = PointPool.obtain(endPoint.x,startPoint.y); 84 | 85 | }else if(holderLayoutType== TreeLayoutManager.LAYOUT_TYPE_VERTICAL_UP){ 86 | startPoint = PointPool.obtain((fromView.getLeft()+fromView.getRight())/2f,fromView.getTop()); 87 | point1 = PointPool.obtain(startPoint.x,startPoint.y-DensityUtils.dp2px(context,15)); 88 | endPoint = PointPool.obtain((toView.getLeft()+toView.getRight())/2f,toView.getBottom()); 89 | point2 = PointPool.obtain(endPoint.x,startPoint.y); 90 | 91 | }else{ 92 | super.draw(drawInfo); 93 | return; 94 | } 95 | 96 | mPaint.reset(); 97 | mPath.reset(); 98 | //set paint 99 | mPaint.setColor(lineColor); 100 | mPaint.setStyle(Paint.Style.STROKE); 101 | mPaint.setStrokeWidth(DensityUtils.dp2px(context,lineWidth)); 102 | mPaint.setAntiAlias(true); 103 | mPath.moveTo(startPoint.x,startPoint.y); 104 | mPath.cubicTo( 105 | point1.x,point1.y, 106 | point2.x,point2.y, 107 | endPoint.x,endPoint.y); 108 | //do not forget release 109 | PointPool.free(startPoint); 110 | PointPool.free(point1); 111 | PointPool.free(point2); 112 | PointPool.free(endPoint); 113 | //draw 114 | canvas.drawPath(mPath,mPaint); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /node/src/main/java/com/gyso/treeview/line/StraightLine.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview.line; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Color; 6 | import android.graphics.Paint; 7 | import android.graphics.Path; 8 | import android.graphics.PointF; 9 | import android.view.View; 10 | 11 | import com.gyso.treeview.adapter.DrawInfo; 12 | import com.gyso.treeview.adapter.TreeViewHolder; 13 | import com.gyso.treeview.cache_pool.PointPool; 14 | import com.gyso.treeview.layout.TreeLayoutManager; 15 | import com.gyso.treeview.model.NodeModel; 16 | import com.gyso.treeview.util.DensityUtils; 17 | 18 | /** 19 | * @Author: 怪兽N 20 | * @Time: 2021/5/8 9:40 21 | * @Email: 674149099@qq.com 22 | * @WeChat: guaishouN 23 | * @Describe: 24 | * Straight Line 25 | */ 26 | public class StraightLine extends BaseLine { 27 | public static final int DEFAULT_LINE_WIDTH_DP = 3; 28 | private int lineColor = Color.parseColor("#055287"); 29 | private int lineWidth = DEFAULT_LINE_WIDTH_DP; 30 | public StraightLine() { 31 | super(); 32 | } 33 | 34 | public StraightLine(int lineColor, int lineWidth_dp) { 35 | this(); 36 | this.lineColor = lineColor; 37 | this.lineWidth = lineWidth_dp; 38 | } 39 | 40 | public void setLineColor(int lineColor) { 41 | this.lineColor = lineColor; 42 | } 43 | 44 | public void setLineWidth(int lineWidth) { 45 | this.lineWidth = lineWidth; 46 | } 47 | 48 | @Override 49 | public void draw(DrawInfo drawInfo) { 50 | Canvas canvas = drawInfo.getCanvas(); 51 | TreeViewHolder fromHolder = drawInfo.getFromHolder(); 52 | TreeViewHolder toHolder = drawInfo.getToHolder(); 53 | Paint mPaint = drawInfo.getPaint(); 54 | Path mPath = drawInfo.getPath(); 55 | int holderLayoutType = toHolder.getHolderLayoutType(); 56 | int spacePeerToPeer = drawInfo.getSpacePeerToPeer(); 57 | int spaceParentToChild = drawInfo.getSpaceParentToChild(); 58 | //get view and node 59 | View fromView = fromHolder.getView(); 60 | NodeModel fromNode = fromHolder.getNode(); 61 | View toView = toHolder.getView(); 62 | NodeModel toNode = toHolder.getNode(); 63 | Context context = fromView.getContext(); 64 | 65 | 66 | PointF startPoint,endPoint; 67 | if(holderLayoutType == TreeLayoutManager.LAYOUT_TYPE_HORIZON_RIGHT){ 68 | startPoint = PointPool.obtain(fromView.getRight(),(fromView.getTop()+fromView.getBottom())/2f); 69 | endPoint = PointPool.obtain(toView.getLeft(),(toView.getTop()+toView.getBottom())/2f); 70 | 71 | }else if(holderLayoutType == TreeLayoutManager.LAYOUT_TYPE_HORIZON_LEFT){ 72 | startPoint = PointPool.obtain(fromView.getLeft(),(fromView.getTop()+fromView.getBottom())/2f); 73 | endPoint = PointPool.obtain(toView.getRight(),(toView.getTop()+toView.getBottom())/2f); 74 | 75 | }else if(holderLayoutType == TreeLayoutManager.LAYOUT_TYPE_VERTICAL_UP){ 76 | startPoint = PointPool.obtain((fromView.getLeft()+fromView.getRight())/2f,fromView.getTop()); 77 | endPoint = PointPool.obtain((toView.getLeft()+toView.getRight())/2f,toView.getBottom()); 78 | 79 | }else if (holderLayoutType == TreeLayoutManager.LAYOUT_TYPE_VERTICAL_DOWN){ 80 | startPoint = PointPool.obtain((fromView.getLeft()+fromView.getRight())/2f,fromView.getBottom()); 81 | endPoint = PointPool.obtain((toView.getLeft()+toView.getRight())/2f,toView.getTop()); 82 | 83 | }else{ 84 | super.draw(drawInfo); 85 | return; 86 | } 87 | 88 | //set paint 89 | mPaint.reset(); 90 | mPath.reset(); 91 | mPaint.setColor(lineColor); 92 | mPaint.setStyle(Paint.Style.STROKE); 93 | mPaint.setStrokeWidth(DensityUtils.dp2px(context,lineWidth)); 94 | mPaint.setAntiAlias(true); 95 | 96 | mPath.moveTo(startPoint.x,startPoint.y); 97 | mPath.lineTo(endPoint.x,endPoint.y); 98 | //release 99 | PointPool.free(startPoint); 100 | PointPool.free(endPoint); 101 | //draw 102 | canvas.drawPath(mPath,mPaint); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /node/src/main/java/com/gyso/treeview/listener/TreeViewControlListener.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview.listener; 2 | 3 | import android.view.View; 4 | 5 | import androidx.annotation.Nullable; 6 | 7 | import com.gyso.treeview.model.NodeModel; 8 | 9 | /** 10 | * @Author: 怪兽N 11 | * @Time: 2021/6/15 12 | * @Email: 674149099@qq.com 13 | * @WeChat: guaishouN 14 | * @Describe: 15 | * Listener for drag-move node, scale, drag the hold treeView 16 | */ 17 | public interface TreeViewControlListener { 18 | int MIN_SCALE = -1; 19 | int FREE_SCALE = 0; 20 | int MAX_SCALE = 1; 21 | void onScaling(int state, int percent); 22 | void onDragMoveNodesHit(@Nullable NodeModel draggingNode, @Nullable NodeModel hittingNode, @Nullable View draggingView, @Nullable View hittingView); 23 | } 24 | -------------------------------------------------------------------------------- /node/src/main/java/com/gyso/treeview/listener/TreeViewItemClick.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview.listener; 2 | 3 | import android.view.View; 4 | 5 | /** 6 | * guaishouN 674149099@qq.com 7 | */ 8 | 9 | public interface TreeViewItemClick { 10 | void onItemClick(View item); 11 | } 12 | -------------------------------------------------------------------------------- /node/src/main/java/com/gyso/treeview/listener/TreeViewItemLongClick.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview.listener; 2 | 3 | import android.view.View; 4 | 5 | /** 6 | * guaishouN 674149099@qq.com 7 | */ 8 | 9 | public interface TreeViewItemLongClick { 10 | void onLongClick(View view); 11 | } 12 | -------------------------------------------------------------------------------- /node/src/main/java/com/gyso/treeview/listener/TreeViewNotifier.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview.listener; 2 | 3 | 4 | import com.gyso.treeview.model.NodeModel; 5 | 6 | /** 7 | * @Author: 怪兽N 8 | * @Time: 2021/4/23 9 | * @Email: 674149099@qq.com 10 | * @WeChat: guaishouN 11 | * @Describe: 12 | */ 13 | public interface TreeViewNotifier{ 14 | void onDataSetChange(); 15 | void onRemoveNode(NodeModelnodeModel); 16 | void onRemoveChildNodes(NodeModel parentNode); 17 | void onItemViewChange(NodeModel nodeModel); 18 | void onAddNodes(NodeModel parent, NodeModel... childNodes); 19 | } -------------------------------------------------------------------------------- /node/src/main/java/com/gyso/treeview/model/ITraversal.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview.model; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * traversal callback 7 | * guaishouN 674149099@qq.com 8 | */ 9 | 10 | public interface ITraversal extends Serializable { 11 | void next(T next); 12 | default void finish(){} 13 | } 14 | -------------------------------------------------------------------------------- /node/src/main/java/com/gyso/treeview/onNodeEvents.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview; 2 | 3 | import com.gyso.treeview.model.NodeModel; 4 | 5 | public interface onNodeEvents { 6 | boolean onNodeDrop(NodeModel parent, NodeModel child); 7 | } 8 | -------------------------------------------------------------------------------- /node/src/main/java/com/gyso/treeview/touch/DragBlock.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview.touch; 2 | 3 | import android.graphics.PointF; 4 | import android.os.Handler; 5 | import android.os.Looper; 6 | import android.os.SystemClock; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.view.ViewParent; 10 | import android.view.animation.Interpolator; 11 | import android.widget.OverScroller; 12 | 13 | import com.gyso.treeview.R; 14 | import com.gyso.treeview.TreeViewContainer; 15 | import com.gyso.treeview.adapter.TreeViewHolder; 16 | import com.gyso.treeview.cache_pool.PointPool; 17 | import com.gyso.treeview.model.NodeModel; 18 | import com.gyso.treeview.util.ViewBox; 19 | 20 | import java.util.ArrayList; 21 | import java.util.HashMap; 22 | import java.util.LinkedList; 23 | import java.util.List; 24 | import java.util.Map; 25 | 26 | /** 27 | * @Author: 怪兽N 28 | * @Time: 2021/6/7 17:56 29 | * @Email: 674149099@qq.com 30 | * @WeChat: guaishouN 31 | * @Describe: 32 | * drag block 33 | */ 34 | public class DragBlock { 35 | private final List tmp; 36 | private volatile boolean isDragging; 37 | private final TreeViewContainer container; 38 | private final Map originPositionMap; 39 | private final OverScroller mScroller; 40 | private PointF prePointF = null; 41 | /** 42 | * Interpolator defining the animation curve for mScroller 43 | */ 44 | private static final Interpolator sInterpolator = t -> { 45 | t -= 1.0f; 46 | return t * t * t * t * t + 1.0f; 47 | }; 48 | 49 | public DragBlock(TreeViewContainer parent){ 50 | tmp = new ArrayList<>(); 51 | container = parent; 52 | this.mScroller = new OverScroller(container.getContext(), sInterpolator); 53 | originPositionMap = new HashMap<>(); 54 | } 55 | 56 | public boolean load(View view){ 57 | if(originPositionMap.isEmpty() && tmp.isEmpty()){ 58 | tmp.add(view); 59 | originPositionMap.put(view,new ViewBox(view)); 60 | addItem(view); 61 | return true; 62 | } 63 | return false; 64 | } 65 | 66 | private void addItem(View view){ 67 | Object tag = view.getTag(R.id.item_holder); 68 | if(tag instanceof TreeViewHolder){ 69 | TreeViewHolder holder = (TreeViewHolder)tag; 70 | NodeModel node = holder.getNode(); 71 | for (NodeModel n:node.getChildNodes()){ 72 | TreeViewHolder h = container.getTreeViewHolder(n); 73 | tmp.add(h.getView()); 74 | originPositionMap.put(h.getView(),new ViewBox(h.getView())); 75 | addItem(h.getView()); 76 | } 77 | } 78 | } 79 | 80 | public void drag(int dx, int dy){ 81 | if(!mScroller.isFinished()){ 82 | return; 83 | } 84 | this.isDragging = true; 85 | for (int i = 0; i < tmp.size(); i++) { 86 | View view = tmp.get(i); 87 | view.offsetLeftAndRight(dx); 88 | view.offsetTopAndBottom(dy); 89 | } 90 | } 91 | 92 | public void setDragging(boolean dragging) { 93 | isDragging = dragging; 94 | } 95 | 96 | private void autoRelease(){ 97 | if(mScroller.isFinished() && !isDragging){ 98 | originPositionMap.clear(); 99 | tmp.clear(); 100 | System.gc(); 101 | } 102 | } 103 | 104 | public void smoothRecover(View referenceView) { 105 | if(!mScroller.isFinished()){ 106 | return; 107 | } 108 | ViewBox rBox = originPositionMap.get(referenceView); 109 | if(rBox ==null){ 110 | return; 111 | } 112 | prePointF=PointPool.obtain(0f,0f); 113 | mScroller.startScroll(0,0,referenceView.getLeft()-rBox.left,referenceView.getTop()-rBox.top); 114 | } 115 | 116 | public boolean computeScroll() { 117 | boolean isSuc = mScroller.computeScrollOffset(); 118 | if(isSuc){ 119 | PointF curPointF = PointPool.obtain(mScroller.getCurrX(), mScroller.getCurrY()); 120 | mScroller.getCurrY(); 121 | for (int i = 0; i < tmp.size(); i++) { 122 | View view = tmp.get(i); 123 | int dx = (int)(curPointF.x-prePointF.x); 124 | int dy = (int)(curPointF.y-prePointF.y); 125 | view.offsetLeftAndRight(-dx); 126 | view.offsetTopAndBottom(-dy); 127 | } 128 | if(prePointF!=null){ 129 | prePointF.set(curPointF); 130 | } 131 | PointPool.free(curPointF); 132 | return true; 133 | } 134 | autoRelease(); 135 | return false; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /node/src/main/java/com/gyso/treeview/touch/onDoubleTapListener.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview.touch; 2 | 3 | public interface onDoubleTapListener { 4 | void onDoubleTapListener(); 5 | } 6 | -------------------------------------------------------------------------------- /node/src/main/java/com/gyso/treeview/util/DensityUtils.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview.util; 2 | 3 | import android.content.Context; 4 | import android.util.TypedValue; 5 | 6 | /** 7 | * 单位转换 工具类
8 | */ 9 | public class DensityUtils { 10 | 11 | /** 12 | * dp转px 13 | */ 14 | public static int dp2px(Context context, float dpVal) { 15 | int result = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, context.getResources() 16 | .getDisplayMetrics()); 17 | return result; 18 | } 19 | 20 | /** 21 | * sp转px 22 | */ 23 | public static int sp2px(Context context, float spVal) { 24 | int result = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spVal, context.getResources() 25 | .getDisplayMetrics()); 26 | return result; 27 | } 28 | 29 | /** 30 | * px转dp 31 | */ 32 | public static int px2dp(Context context, float pxVal) { 33 | final float scale = context.getResources().getDisplayMetrics().density; 34 | int result = (int) (pxVal / scale); 35 | return result; 36 | } 37 | 38 | /** 39 | * px转sp 40 | */ 41 | public static float px2sp(Context context, float pxVal) { 42 | int result = (int) (pxVal / context.getResources().getDisplayMetrics().scaledDensity); 43 | return result; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /node/src/main/java/com/gyso/treeview/util/Interpolators.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview.util; 2 | 3 | 4 | import android.view.animation.AccelerateDecelerateInterpolator; 5 | import android.view.animation.AccelerateInterpolator; 6 | import android.view.animation.BounceInterpolator; 7 | import android.view.animation.DecelerateInterpolator; 8 | import android.view.animation.Interpolator; 9 | import android.view.animation.LinearInterpolator; 10 | import android.view.animation.PathInterpolator; 11 | 12 | 13 | /** 14 | * @Author: 怪兽N 15 | * @Time: 2021/6/10 16:24 16 | * @Email: 674149099@qq.com 17 | * @WeChat: guaishouN 18 | * @Describe: 19 | * Utility class to receive interpolators from 20 | */ 21 | public class Interpolators { 22 | public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f); 23 | 24 | /** 25 | * Like {@link #FAST_OUT_SLOW_IN}, but used in case the animation is played in reverse (i.e. t 26 | * goes from 1 to 0 instead of 0 to 1). 27 | */ 28 | public static final Interpolator FAST_OUT_SLOW_IN_REVERSE = 29 | new PathInterpolator(0.8f, 0f, 0.6f, 1f); 30 | public static final Interpolator FAST_OUT_LINEAR_IN = new PathInterpolator(0.4f, 0f, 1f, 1f); 31 | public static final Interpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0f, 0f, 0.2f, 1f); 32 | public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f); 33 | public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f); 34 | public static final Interpolator LINEAR = new LinearInterpolator(); 35 | public static final Interpolator ACCELERATE = new AccelerateInterpolator(); 36 | public static final Interpolator ACCELERATE_DECELERATE = new AccelerateDecelerateInterpolator(); 37 | public static final Interpolator DECELERATE_QUINT = new DecelerateInterpolator(2.5f); 38 | public static final Interpolator CUSTOM_40_40 = new PathInterpolator(0.4f, 0f, 0.6f, 1f); 39 | public static final Interpolator ICON_OVERSHOT = new PathInterpolator(0.4f, 0f, 0.2f, 1.4f); 40 | public static final Interpolator PANEL_CLOSE_ACCELERATED 41 | = new PathInterpolator(0.3f, 0, 0.5f, 1); 42 | public static final Interpolator BOUNCE = new BounceInterpolator(); 43 | 44 | /** 45 | * Interpolator to be used when animating a move based on a click. Pair with enough duration. 46 | */ 47 | public static final Interpolator TOUCH_RESPONSE = 48 | new PathInterpolator(0.3f, 0f, 0.1f, 1f); 49 | 50 | /** 51 | * Like {@link #TOUCH_RESPONSE}, but used in case the animation is played in reverse (i.e. t 52 | * goes from 1 to 0 instead of 0 to 1). 53 | */ 54 | public static final Interpolator TOUCH_RESPONSE_REVERSE = 55 | new PathInterpolator(0.9f, 0f, 0.7f, 1f); 56 | } 57 | -------------------------------------------------------------------------------- /node/src/main/java/com/gyso/treeview/util/TreeViewLog.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview.util; 2 | 3 | import android.util.Log; 4 | 5 | 6 | /** 7 | * @Author: 怪兽N 8 | * @Time: 2021/5/8 15:10 9 | * @Email: 674149099@qq.com 10 | * @WeChat: guaishouN 11 | * @Describe: 12 | * logger 13 | */ 14 | public class TreeViewLog{ 15 | private static boolean isDebug = false; 16 | public static void d(String tag, String msg){ 17 | if(isDebug){ 18 | Log.d(tag, msg); 19 | } 20 | } 21 | public static void e(String tag, String msg){ 22 | if(isDebug){ 23 | Log.e(tag, msg); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /node/src/main/java/com/gyso/treeview/util/ViewBox.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview.util; 2 | 3 | import android.graphics.Matrix; 4 | import android.view.View; 5 | 6 | public class ViewBox { 7 | 8 | public int top; 9 | public int left; 10 | public int right; 11 | public int bottom; 12 | 13 | public ViewBox(){ 14 | 15 | } 16 | public ViewBox(View view){ 17 | this(view.getTop(),view.getLeft(),view.getBottom(),view.getRight()); 18 | } 19 | 20 | public ViewBox(int top, int left, int bottom, int right) { 21 | this.top = top; 22 | this.left = left; 23 | this.right = right; 24 | this.bottom = bottom; 25 | } 26 | 27 | public void setValues(ViewBox viewBox) { 28 | this.top = viewBox.top; 29 | this.left = viewBox.left; 30 | this.right = viewBox.right; 31 | this.bottom = viewBox.bottom; 32 | } 33 | 34 | public void setValues(int top, int left,int bottom,int right) { 35 | this.top = top; 36 | this.left = left; 37 | this.bottom = bottom; 38 | this.right = right; 39 | } 40 | 41 | public static ViewBox getViewBox(View view) { 42 | return new ViewBox(view); 43 | } 44 | 45 | /** 46 | *.scaleX(centerM[0]) 47 | *.translationX(centerM[2]) 48 | *.scaleY(centerM[4]) 49 | *.translationY(centerM[5]) 50 | * 51 | *float left = v.getLeft()*v.getScaleX()+v.getTranslationX(); 52 | *float top = v.getTop()*v.getScaleY()+v.getTranslationY(); 53 | *float right = v.getRight()*v.getScaleX()+v.getTranslationX(); 54 | *float bottom = v.getBottom()*v.getScaleY()+v.getTranslationY(); 55 | */ 56 | public ViewBox convert(Matrix matrix) { 57 | float[] fs = new float[9]; 58 | matrix.getValues(fs); 59 | float scaleX = fs[Matrix.MSCALE_X]; 60 | float scaleY = fs[Matrix.MSCALE_Y]; 61 | float translX = fs[Matrix.MTRANS_X]; 62 | float translY = fs[Matrix.MTRANS_Y]; 63 | float leftI = left*scaleX+translX; 64 | float topI = top*scaleY+translY; 65 | float rightI = right*scaleX+translX; 66 | float bottomI = bottom*scaleY+translY; 67 | return new ViewBox((int)topI,(int)leftI,(int)bottomI,(int)rightI); 68 | } 69 | 70 | public ViewBox invert(Matrix matrix){ 71 | float[] fs = new float[9]; 72 | matrix.getValues(fs); 73 | float scaleX = fs[Matrix.MSCALE_X]; 74 | float scaleY = fs[Matrix.MSCALE_Y]; 75 | float translX = fs[Matrix.MTRANS_X]; 76 | float translY = fs[Matrix.MTRANS_Y]; 77 | float leftI = (left-translX)/scaleX; 78 | float topI = (top-translY)/scaleY; 79 | float rightI = (right-translX)/scaleX; 80 | float bottomI = (bottom-translY)/scaleY; 81 | setValues((int)topI,(int)leftI,(int)rightI,(int)bottomI); 82 | return new ViewBox((int)topI,(int)leftI,(int)bottomI,(int)rightI); 83 | } 84 | 85 | public boolean contain(ViewBox other){ 86 | return other!=null && 87 | top<=other.top && 88 | left<=other.left && 89 | right>=other.right && 90 | bottom>=other.bottom; 91 | } 92 | 93 | public ViewBox multiply(float radio) { 94 | return new ViewBox( 95 | (int)(top * radio), 96 | (int)(left * radio), 97 | (int)(bottom * radio), 98 | (int)(right * radio) 99 | ); 100 | } 101 | 102 | public ViewBox add(ViewBox other) { 103 | if (other == null) { 104 | return this; 105 | } 106 | return new ViewBox( 107 | top + other.top, 108 | left + other.left, 109 | bottom + other.bottom, 110 | right + other.right 111 | ); 112 | } 113 | 114 | public ViewBox subtract(ViewBox other){ 115 | if (other == null) { 116 | return this; 117 | } 118 | return new ViewBox( 119 | top - other.top, 120 | left - other.left, 121 | bottom - other.bottom, 122 | right - other.right 123 | ); 124 | } 125 | 126 | /** 127 | * get the box height which added 2*dy 128 | * @return height 129 | */ 130 | public int getHeight(){ 131 | return bottom-top; 132 | } 133 | 134 | /** 135 | * get the box width which added 2*dy 136 | * @return width 137 | */ 138 | public int getWidth(){ 139 | return right-left; 140 | } 141 | 142 | public void clear() { 143 | this.top = 0; 144 | this.left = 0; 145 | this.right = 0; 146 | this.bottom = 0; 147 | } 148 | 149 | @Override 150 | public String toString() { 151 | return "{" + 152 | "t:" + top + 153 | " l:" + left + 154 | " b:" + bottom + 155 | " r:" + right + 156 | '}'; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /node/src/main/res/values/values.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | #E1BEE7 16 | -------------------------------------------------------------------------------- /node/src/test/java/com/gyso/treeview/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.gyso.treeview; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | google { 4 | content { 5 | includeGroupByRegex("com\\.android.*") 6 | includeGroupByRegex("com\\.google.*") 7 | includeGroupByRegex("androidx.*") 8 | } 9 | } 10 | mavenCentral() 11 | gradlePluginPortal() 12 | maven { url "https://jitpack.io" } 13 | } 14 | } 15 | dependencyResolutionManagement { 16 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 17 | repositories { 18 | google() 19 | mavenCentral() 20 | maven { url "https://jitpack.io" } 21 | } 22 | } 23 | 24 | rootProject.name = "AndroidBox" 25 | include ':app' 26 | include ':codeview' 27 | include ':node' 28 | --------------------------------------------------------------------------------