├── .github
└── workflows
│ └── main.yml
├── .gitignore
├── .idea
├── .gitignore
├── .name
├── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
├── compiler.xml
├── deploymentTargetDropDown.xml
├── gradle.xml
├── misc.xml
└── vcs.xml
├── LICENSE
├── README.md
├── SECURITY.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── doyouhost
│ │ └── servercommander
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── doyouhost
│ │ │ └── servercommander
│ │ │ ├── Container.kt
│ │ │ ├── ContainersAdapter.kt
│ │ │ ├── ContainersDiffCallback.kt
│ │ │ ├── DockerViewPagerAdapter.kt
│ │ │ ├── LoginActivity.kt
│ │ │ ├── MainActivity.kt
│ │ │ ├── SshConnection.kt
│ │ │ ├── YunohostConnection.kt
│ │ │ ├── YunohostViewPagerAdapter.kt
│ │ │ ├── fragments
│ │ │ ├── DockerFragment.kt
│ │ │ ├── HomeFragment.kt
│ │ │ ├── SettingsFragment.kt
│ │ │ ├── SystemFragment.kt
│ │ │ ├── TerminalFragment.kt
│ │ │ └── YunohostFragment.kt
│ │ │ └── viewModels
│ │ │ └── RefreshViewModel.kt
│ ├── res
│ │ ├── drawable-v24
│ │ │ ├── account_group.xml
│ │ │ ├── alpha_y_circle_outline.xml
│ │ │ ├── application.xml
│ │ │ ├── application_export.xml
│ │ │ ├── bottle_tonic_plus.xml
│ │ │ ├── briefcase_arrow_up_down.xml
│ │ │ ├── console_network.xml
│ │ │ ├── docker.xml
│ │ │ ├── domain.xml
│ │ │ ├── download.xml
│ │ │ ├── ic_launcher_foreground.xml
│ │ │ ├── oci.xml
│ │ │ ├── package_variant_closed.xml
│ │ │ ├── power.xml
│ │ │ ├── run_fast.xml
│ │ │ ├── server_network.xml
│ │ │ ├── server_network_off.xml
│ │ │ ├── stop_circle.xml
│ │ │ └── swap_vertical_bold.xml
│ │ ├── drawable
│ │ │ ├── archive_arrow_up.xml
│ │ │ ├── archive_search.xml
│ │ │ ├── bmc_button.xml
│ │ │ ├── bmc_logo.xml
│ │ │ ├── circular_loading_bar.xml
│ │ │ ├── cpu_64_bit.xml
│ │ │ ├── fire_alert.xml
│ │ │ ├── harddisk.xml
│ │ │ ├── ic_baseline_device_thermostat_24.xml
│ │ │ ├── ic_launcher_background.xml
│ │ │ ├── memory.xml
│ │ │ ├── package_variant_closed.xml
│ │ │ ├── penguin.xml
│ │ │ ├── play_circle.xml
│ │ │ ├── refresh.xml
│ │ │ ├── restart.xml
│ │ │ ├── router_network.xml
│ │ │ ├── router_wireless.xml
│ │ │ ├── thermometer.xml
│ │ │ └── weather_sunny.xml
│ │ ├── layout
│ │ │ ├── activity_login.xml
│ │ │ ├── activity_main.xml
│ │ │ ├── alert_dialog_container_stats.xml
│ │ │ ├── alert_dialog_password.xml
│ │ │ ├── alert_dialog_updates.xml
│ │ │ ├── dashboard_item_disk_info.xml
│ │ │ ├── dashboard_item_heaviest_app_info.xml
│ │ │ ├── dashboard_item_kernel_info.xml
│ │ │ ├── dashboard_item_localip_info.xml
│ │ │ ├── dashboard_item_package_info.xml
│ │ │ ├── dashboard_item_publicip_info.xml
│ │ │ ├── dashboard_item_uptime_info.xml
│ │ │ ├── docker_app_item.xml
│ │ │ ├── edit_text_dialog.xml
│ │ │ ├── fragment_docker.xml
│ │ │ ├── fragment_home.xml
│ │ │ ├── fragment_settings.xml
│ │ │ ├── fragment_system.xml
│ │ │ ├── fragment_terminal.xml
│ │ │ ├── fragment_yunohost.xml
│ │ │ ├── yunohost_dashboard_item_1.xml
│ │ │ └── yunohost_dashboard_item_2.xml
│ │ ├── mipmap-anydpi-v26
│ │ │ ├── ic_launcher.xml
│ │ │ ├── ic_launcher_round.xml
│ │ │ ├── server_commander.xml
│ │ │ └── server_commander_round.xml
│ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.webp
│ │ │ ├── ic_launcher_round.webp
│ │ │ ├── server_commander.png
│ │ │ ├── server_commander_background.png
│ │ │ ├── server_commander_foreground.png
│ │ │ └── server_commander_round.png
│ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.webp
│ │ │ ├── ic_launcher_round.webp
│ │ │ ├── server_commander.png
│ │ │ ├── server_commander_background.png
│ │ │ ├── server_commander_foreground.png
│ │ │ └── server_commander_round.png
│ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.webp
│ │ │ ├── ic_launcher_round.webp
│ │ │ ├── server_commander.png
│ │ │ ├── server_commander_background.png
│ │ │ ├── server_commander_foreground.png
│ │ │ └── server_commander_round.png
│ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.webp
│ │ │ ├── ic_launcher_round.webp
│ │ │ ├── server_commander.png
│ │ │ ├── server_commander_background.png
│ │ │ ├── server_commander_foreground.png
│ │ │ └── server_commander_round.png
│ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.webp
│ │ │ ├── ic_launcher_round.webp
│ │ │ ├── server_commander.png
│ │ │ ├── server_commander_background.png
│ │ │ ├── server_commander_foreground.png
│ │ │ └── server_commander_round.png
│ │ ├── values-land
│ │ │ └── dimens.xml
│ │ ├── values-night
│ │ │ ├── colors.xml
│ │ │ └── themes.xml
│ │ ├── values-v29
│ │ │ └── themes.xml
│ │ ├── values-w1240dp
│ │ │ └── dimens.xml
│ │ ├── values-w600dp
│ │ │ └── dimens.xml
│ │ ├── values
│ │ │ ├── colors.xml
│ │ │ ├── dimens.xml
│ │ │ ├── strings.xml
│ │ │ └── themes.xml
│ │ └── xml
│ │ │ ├── backup_rules.xml
│ │ │ └── data_extraction_rules.xml
│ └── server_commander-playstore.png
│ └── test
│ └── java
│ └── com
│ └── doyouhost
│ └── servercommander
│ └── ExampleUnitTest.kt
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: Build and create release on merged PR to master
2 | on:
3 | workflow_dispatch:
4 |
5 | pull_request:
6 | types: [closed]
7 | branches:
8 | - master
9 |
10 | jobs:
11 | gradle:
12 | if: github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch'
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v3
16 |
17 | - uses: actions/setup-java@v3
18 | with:
19 | distribution: temurin
20 | java-version: 11
21 |
22 | - name: Setup Gradle
23 | uses: gradle/gradle-build-action@v2
24 |
25 | - name: Get version name
26 | run: echo "VERSION_INFORMATION=$(${{github.workspace}}/gradlew -q --no-configuration-cache printVersionName)" >> $GITHUB_ENV
27 |
28 | - name: Execute Gradle build
29 | run: ./gradlew assembleDebug
30 |
31 | - name: Upload APK
32 | uses: actions/upload-artifact@v3
33 | with:
34 | name: apk
35 | path: app/build/outputs/apk/debug/app-debug.apk
36 |
37 | - name: Check for new app version
38 | run: |
39 | TAGS=$(git tag -l)
40 | if echo "$TAGS" | grep -q "${{ env.VERSION_INFORMATION }}"; then
41 | echo "This is not a new version! Exiting..."
42 | exit 1
43 | else
44 | echo "New version!"
45 | fi
46 |
47 | - name: Create tag
48 | id: create_tag
49 | run: |
50 | tag=${{ env.VERSION_INFORMATION }}
51 | echo "::set-output name=tag::$tag"
52 |
53 | - name: Create Draft Release
54 | id: create_release
55 | uses: actions/create-release@v1
56 | env:
57 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
58 | with:
59 | tag_name: ${{ steps.create_tag.outputs.tag }}
60 | release_name: ${{ steps.create_tag.outputs.tag }}
61 | draft: true
62 | prerelease: false
63 |
64 | - uses: actions/upload-release-asset@v1.0.1
65 | env:
66 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
67 | with:
68 | upload_url: ${{ steps.create_release.outputs.upload_url }}
69 | asset_path: app/build/outputs/apk/debug/app-debug.apk
70 | asset_name: app-debug.apk
71 | asset_content_type: application/apk
72 |
73 | - uses: eregon/publish-release@v1
74 | env:
75 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
76 | with:
77 | release_id: ${{ steps.create_release.outputs.id }}
78 |
--------------------------------------------------------------------------------
/.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 | *.aab
17 | *.apk
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | ServerCommander
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | xmlns:android
19 |
20 | ^$
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | xmlns:.*
30 |
31 | ^$
32 |
33 |
34 | BY_NAME
35 |
36 |
37 |
38 |
39 |
40 |
41 | .*:id
42 |
43 | http://schemas.android.com/apk/res/android
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | .*:name
53 |
54 | http://schemas.android.com/apk/res/android
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | name
64 |
65 | ^$
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | style
75 |
76 | ^$
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 | .*
86 |
87 | ^$
88 |
89 |
90 | BY_NAME
91 |
92 |
93 |
94 |
95 |
96 |
97 | .*
98 |
99 | http://schemas.android.com/apk/res/android
100 |
101 |
102 | ANDROID_ATTRIBUTE_ORDER
103 |
104 |
105 |
106 |
107 |
108 |
109 | .*
110 |
111 | .*
112 |
113 |
114 | BY_NAME
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/deploymentTargetDropDown.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 | # ServerCommander
3 | [](https://www.codefactor.io/repository/github/morganmlgman/servercommander)
4 |
5 | - [ServerCommander compatibility](https://github.com/MorganMLGman/ServerCommander#servercommander-compatibility)
6 | - [How to install?](https://github.com/MorganMLGman/ServerCommander#installation)
7 | - [Why YunoHost?](https://github.com/MorganMLGman/ServerCommander#why-yunohost)
8 | - [Why Docker?](https://github.com/MorganMLGman/ServerCommander#why-docker)
9 | - [Known bugs](https://github.com/MorganMLGman/ServerCommander#known-bugs-)
10 | - [Changelog](https://github.com/MorganMLGman/ServerCommander#changelog)
11 | - [Privacy Policy](https://github.com/MorganMLGman/ServerCommander#privacy-policy)
12 | - [Thanks to](https://github.com/MorganMLGman/ServerCommander#thanks-to)
13 |
14 |
15 | 
16 |
17 | Mobile application that helps manage self-hosted homelab. The mobile client application is a great tool that allows you to manage your server quickly, conveniently, and almost anytime. For now, the application is mainly focused on retrieving information from the server. In the app you can see nicely displayed stats about your server kernel version, server uptime, boot disk usage, currently most demanding process, number of installed packages, and much more. If you are running Docker on your system there are some great options available for you. Listing Docker containers, showing statistics like processor and memory usage, disk or network i/o operations. Starting, stopping, and restarting containers is also ready to use. If you are running Yunohost instead, have no fear, there are also plenty of options available for you! You can check if you have any apps updates, and how many domains or users you have. With one click you can write the SSH key for a specific system user or check how many backups are created. Without system restrictions, auto update of the dashboard is available with a minimum period of 10 seconds and a maximum of 1 minute.
18 |
19 | **ATTENTION:** *Check Connection* button will download [Python script](https://github.com/MorganMLGman/copilot) onto your desktop in `/home/{user}/` directory. YunoHost uses only API and You can use this function without clicking on *Check connection* button.
20 |
21 | Within **ServerCommander** you can check such things as:
22 | - CPU temperature
23 | - CPU usage
24 | - RAM usage
25 | - Linux kernel version
26 | - Your local and public IP
27 | - System uptime
28 | - Disk usage
29 | - Most demanding app at the moment
30 | - Number of installed packages
31 | - And much much more (new features will be released in app updates)
32 |
33 | If your server is running Docker in addition you can _**start/stop/restart**_ containers, briefly view containers summary and check statistics about single container.
34 |
35 | If you are running YunoHost you can see ***how many users, backups and domains*** you have. Also there is a possibility to see ***how many applications are waiting for an update*** and ***you can list them***. You can ***push new ssh keys*** for a chosen user.
36 |
37 | However no matter what type of server you have, you can always perform actions like:
38 | - Server shutdown
39 | - Server reboot
40 | - Check system updates
41 | - Perform system update
42 | - (New features will be released in upcoming app updates)
43 |
44 | ## ServerCommander compatibility
45 |
46 | App is compatible with servers running:
47 | - [YunoHost](https://github.com/YunoHost)
48 | - [Docker](https://github.com/docker)
49 |
50 | under **Debian** and **Debian-based** operating system.
51 |
52 | Our app has been tested under these systems:
53 | - Debian 11
54 | - Ubuntu 22.04, Ubuntu 22.10
55 | - Raspberry Pi OS
56 |
57 | ## Installation
58 |
59 | You can install ServerCommander in three ways:
60 | - by downloading from [Google Play Store](https://play.google.com/store/apps/details?id=com.doyouhost.servercommander)
61 | - by downloading `.apk` from [GitHub Releases](https://github.com/MorganMLGman/ServerCommander/releases)
62 | - by building from source with Android Studio
63 |
64 | ## Why YunoHost?
65 |
66 | YunoHost is a great system that helps run own server without professional IT knowledge. That's why it is a great option for a lot of users that want to have own, private server with services, tools and applications. WebAdmin is a great option to manage server, but it is not that comfortable on mobile devices like it is on PCs.
67 |
68 | ## Why Docker?
69 |
70 | Docker is very powerful tool to host many applications on one machine. One of **ServerCommander** developers is using such machine :) Self-hosting with Docker is very flexible, you can choose one of thousands containers with the application you like and deploy it in a breeze.
71 |
72 | ## Known bugs :)
73 | - SwipeRefreshLayout on _SYSTEM_ and _DOCKER_ tabs in not returning to default state when _PasswordAlertDialog_ is dismissed by clicking outside the dialog or pressing back button.
74 | - App sometimes crashes when _TestConnection_ is ongoing and connection settings in _SETTINGS_ tab are changed.
75 |
76 | ## Changelog
77 | ### [v1.1.0](https://github.com/MorganMLGman/ServerCommander/releases/tag/v1.1.0)
78 | App is now compatible with new YunoHost 11.1 API. Some other small fixes.
79 |
80 | ### [v1.0.3](https://github.com/MorganMLGman/ServerCommander/releases/tag/v1.0.3)
81 | Fixed app crashing when running on VPS and temperature sensors are not available.
82 |
83 | Fixed app not able to call Python script on systems where `~` in file path is not allowed.
84 |
85 | ### [v20221220.143412](https://github.com/MorganMLGman/ServerCommander/releases/tag/v20221220.143412)
86 | Implemented changing SSH port and fixed some issues with too low contrast.
87 |
88 | ### [v20221213.202445](https://github.com/MorganMLGman/ServerCommander/releases/tag/v20221213.202445)
89 | This is out first _**"production"**_ ready release. Feel free to check it out and submit issues.
90 |
91 | ## Privacy Policy
92 |
93 | **Our app is not collecting any user data.**
94 |
95 | We have never collected, do not collect, nor will we collect any user data, usage statistics or any other data about app users, their system or other installed applications.
96 |
97 | If you're still not convinced that our app is in a **completely anonymous** state, you're in the perfect place, the app code is **fully open source** and is available at https://github.com/MorganMLGman/ServerCommander
98 |
99 | ## Thanks to
100 |
101 | - [Docker authors](https://github.com/docker)
102 | - [YunoHost authors](https://github.com/YunoHost)
103 | - [Cedrica from DCU](https://dribbble.com/shots/3896634-Profile-Screens) for dashboard idea and [Chirag Kachhadiya](https://www.youtube.com/watch?v=ZjAxAw0kmrY) for showing how to make it real
104 | - Intuit developers for providing [sdp](https://github.com/intuit/sdp) library
105 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | | Version | Supported |
6 | |------------------| ------------------ |
7 | | v1.1.0 | ✅ |
8 | | v1.0.3 | ❌ |
9 | | v20221220.143412 | ❌ |
10 | | v20221213.202445 | ❌ |
11 |
12 | ## Reporting a Vulnerability
13 |
14 | If you have any problem you can report that on [ServerCommander issues](https://github.com/MorganMLGman/ServerCommander/issues)
15 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | id 'org.jetbrains.kotlin.android'
4 | }
5 |
6 | android {
7 | namespace 'com.doyouhost.servercommander'
8 | compileSdk 33
9 |
10 | defaultConfig {
11 | applicationId "com.doyouhost.servercommander"
12 | minSdk 30
13 | targetSdk 33
14 | versionCode 6
15 | versionName "1.1.0"
16 |
17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
18 | }
19 |
20 | buildTypes {
21 | release {
22 | minifyEnabled false
23 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
24 | }
25 | }
26 | compileOptions {
27 | sourceCompatibility JavaVersion.VERSION_1_8
28 | targetCompatibility JavaVersion.VERSION_1_8
29 | }
30 | kotlinOptions {
31 | jvmTarget = '1.8'
32 | }
33 | buildFeatures {
34 | viewBinding true
35 | }
36 | }
37 |
38 | task printVersionName {
39 | println "v" + android.defaultConfig.versionName
40 | }
41 |
42 | dependencies {
43 |
44 | implementation 'androidx.core:core-ktx:1.9.0'
45 | implementation 'androidx.appcompat:appcompat:1.5.1'
46 | implementation 'com.google.android.material:material:1.7.0'
47 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
48 | implementation 'androidx.navigation:navigation-fragment-ktx:2.5.3'
49 | implementation 'androidx.navigation:navigation-ui-ktx:2.5.3'
50 |
51 | implementation("com.github.mwiede:jsch:0.2.4")
52 | implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1")
53 |
54 | testImplementation 'junit:junit:4.13.2'
55 | androidTestImplementation 'androidx.test.ext:junit:1.1.4'
56 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0'
57 |
58 | implementation 'com.intuit.sdp:sdp-android:1.0.6'
59 | implementation 'com.intuit.ssp:ssp-android:1.0.6'
60 | implementation 'androidx.cardview:cardview:1.0.0'
61 | implementation "androidx.recyclerview:recyclerview:1.2.1"
62 | implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
63 | implementation("com.squareup.okhttp3:okhttp:4.10.0")
64 | }
65 |
66 |
--------------------------------------------------------------------------------
/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/doyouhost/servercommander/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.doyouhost.servercommander
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.example.servercommander", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
21 |
25 |
28 |
29 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/app/src/main/java/com/doyouhost/servercommander/Container.kt:
--------------------------------------------------------------------------------
1 | package com.doyouhost.servercommander
2 |
3 | class Container(val name: String, val isRunning: Boolean, val runtime: String) {
4 |
5 | companion object {
6 |
7 | fun createContainersList(numOfContainers: Int): ArrayList {
8 | val containers = ArrayList()
9 | for (i in 1..numOfContainers) {
10 | containers.add(Container("Container ", false, "Pull to refresh"))
11 | }
12 | return containers
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/doyouhost/servercommander/ContainersAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.doyouhost.servercommander
2 |
3 | import android.view.LayoutInflater
4 | import android.view.View
5 | import android.view.ViewGroup
6 | import android.widget.TextView
7 | import androidx.appcompat.widget.AppCompatImageButton
8 | import androidx.recyclerview.widget.DiffUtil
9 | import androidx.recyclerview.widget.RecyclerView
10 |
11 | class ContainersAdapter(var mContainers: List, var mListener: OnViewClickListener) : RecyclerView.Adapter() {
12 |
13 | inner class ViewHolder(itemView: View, mListener: OnViewClickListener) : RecyclerView.ViewHolder(itemView) {
14 |
15 | val dockerAppName: TextView = itemView.findViewById(R.id.dockerAppName)
16 | val dockerAppStatus: TextView = itemView.findViewById(R.id.dockerAppStatus)
17 | val dockerAppRuntime: TextView = itemView.findViewById(R.id.dockerAppRuntime)
18 | val buttonContainerUp: AppCompatImageButton = itemView.findViewById(R.id.buttonContainerStart)
19 | val buttonContainerDown: AppCompatImageButton = itemView.findViewById(R.id.buttonContainerDown)
20 | val buttonContainerRestart: AppCompatImageButton = itemView.findViewById(R.id.buttonContainerRestart)
21 |
22 | init {
23 |
24 | }
25 | }
26 |
27 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
28 | val context = parent.context
29 | val inflater = LayoutInflater.from(context)
30 | val contactView = inflater.inflate(R.layout.docker_app_item, parent, false)
31 | return ViewHolder(contactView, mListener)
32 | }
33 |
34 | override fun onBindViewHolder(holder: ViewHolder, position: Int) {
35 | val container = mContainers[position]
36 |
37 | val dockerAppName = holder.dockerAppName
38 | val dockerAppStatus = holder.dockerAppStatus
39 | val dockerAppRuntime = holder.dockerAppRuntime
40 |
41 | dockerAppName.text = container.name
42 |
43 | if (container.isRunning) dockerAppStatus.text = "RUNNING"
44 | else dockerAppStatus.text = "STOPPED"
45 |
46 | dockerAppRuntime.text = container.runtime
47 |
48 | holder.itemView.setOnClickListener{
49 | mListener.onRowClickListener(holder.itemView, container)
50 | }
51 |
52 | holder.buttonContainerUp.setOnClickListener{
53 | mListener.onButtonStartClickListener(holder.buttonContainerUp, container)
54 | }
55 |
56 | holder.buttonContainerDown.setOnClickListener{
57 | mListener.onButtonStopClickListener(holder.buttonContainerDown, container)
58 | }
59 |
60 | holder.buttonContainerRestart.setOnClickListener {
61 | mListener.onButtonRestartClickListener(holder.buttonContainerRestart, container)
62 | }
63 |
64 | }
65 |
66 | override fun getItemCount(): Int {
67 | return mContainers.size
68 | }
69 |
70 | fun updateList(newContainers: List){
71 | val diffCallback = ContainersDiffCallback(this.mContainers, newContainers)
72 | val diffResult = DiffUtil.calculateDiff(diffCallback)
73 | diffResult.dispatchUpdatesTo(this)
74 | this.mContainers = newContainers
75 | }
76 |
77 | interface OnViewClickListener{
78 | fun onRowClickListener(view: View, container: Container)
79 | fun onButtonStartClickListener(button: AppCompatImageButton, container: Container)
80 | fun onButtonStopClickListener(button: AppCompatImageButton, container: Container)
81 | fun onButtonRestartClickListener(button: AppCompatImageButton, container: Container)
82 | }
83 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/doyouhost/servercommander/ContainersDiffCallback.kt:
--------------------------------------------------------------------------------
1 | package com.doyouhost.servercommander
2 |
3 | import androidx.recyclerview.widget.DiffUtil
4 |
5 | open class ContainersDiffCallback(
6 | private val oldList: List,
7 | private val newList: List
8 | ): DiffUtil.Callback() {
9 | override fun getOldListSize(): Int {
10 | return oldList.size
11 | }
12 |
13 | override fun getNewListSize(): Int {
14 | return newList.size
15 | }
16 |
17 | override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
18 | return oldList[oldItemPosition].name == newList[newItemPosition].name
19 | }
20 |
21 | override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
22 | val oldContainer = oldList[oldItemPosition]
23 | val newContainer = newList[newItemPosition]
24 |
25 | return (oldContainer.name == newContainer.name) and
26 | (oldContainer.runtime == newContainer.runtime) and
27 | (oldContainer.isRunning == newContainer.isRunning)
28 | }
29 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/doyouhost/servercommander/DockerViewPagerAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.doyouhost.servercommander
2 |
3 | import androidx.fragment.app.Fragment
4 | import androidx.fragment.app.FragmentActivity
5 | import androidx.viewpager2.adapter.FragmentStateAdapter
6 | import com.doyouhost.servercommander.fragments.*
7 |
8 | class DockerViewPagerAdapter(fragmentActivity: FragmentActivity) :
9 | FragmentStateAdapter(fragmentActivity) {
10 | override fun getItemCount(): Int {
11 | return 5
12 | }
13 |
14 | override fun createFragment(position: Int): Fragment {
15 | return when(position){
16 | 0 -> HomeFragment()
17 | 1 -> SystemFragment()
18 | 2 -> DockerFragment()
19 | 3 -> TerminalFragment()
20 | 4 -> SettingsFragment()
21 | else -> HomeFragment()
22 | }
23 | }
24 |
25 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/doyouhost/servercommander/LoginActivity.kt:
--------------------------------------------------------------------------------
1 | package com.doyouhost.servercommander
2 |
3 | import android.app.Activity
4 | import android.content.ClipData
5 | import android.content.ClipboardManager
6 | import android.content.Context
7 | import android.content.Intent
8 | import android.os.Bundle
9 | import android.widget.Button
10 | import android.widget.EditText
11 | import android.widget.RadioButton
12 | import android.widget.Toast
13 | import androidx.appcompat.app.AppCompatActivity
14 | import java.io.File
15 |
16 | class LoginActivity : AppCompatActivity() {
17 |
18 | private var backPressed: Boolean = false
19 |
20 | override fun onCreate(savedInstanceState: Bundle?) {
21 | super.onCreate(savedInstanceState)
22 | setContentView(R.layout.activity_login)
23 |
24 | val sharedPref = getSharedPreferences(
25 | "ServerCommander", Context.MODE_PRIVATE
26 | )
27 |
28 | val serverUrl = findViewById(R.id.serverUrl)
29 | val username = findViewById(R.id.username)
30 | val sshPort = findViewById(R.id.sshPort)
31 | val pubkey = findViewById(R.id.pubkey)
32 | val radioYunohost = findViewById(R.id.radioYH)
33 | // val radioDocker = findViewById(R.id.radioDocker)
34 | val generateButton = findViewById(R.id.generatePubKey)
35 | val readButton = findViewById(R.id.readPubkeyButton)
36 | val loginButton = findViewById(R.id.loginButton)
37 |
38 | generateButton.setOnClickListener {
39 | SshConnection.generateKeyPair(this)
40 |
41 | val pubKeyPath = getExternalFilesDir(null)?.absolutePath
42 |
43 | pubkey.setText(pubKeyPath)
44 |
45 | val idRsaPub = File(pubKeyPath, "id_rsa.pub").readText()
46 |
47 | val clipboard = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
48 | val clip = ClipData.newPlainText("id_rsa.pub", idRsaPub)
49 | clipboard.setPrimaryClip(clip)
50 | }
51 |
52 | readButton.setOnClickListener {
53 | val pubKeyPath = getExternalFilesDir(null)?.absolutePath
54 |
55 | if (pubKeyPath != null) {
56 | if( File(pubKeyPath, "id_rsa").exists() and File(pubKeyPath, "id_rsa.pub").exists() ) {
57 | pubkey.setText(pubKeyPath)
58 | }
59 | else
60 | {
61 | Toast.makeText(this, getString(R.string.no_pubkey_found), Toast.LENGTH_SHORT).show()
62 | }
63 | }
64 | else
65 | {
66 | Toast.makeText(this, getString(R.string.pubkey_read_error), Toast.LENGTH_SHORT).show()
67 | }
68 | }
69 |
70 | loginButton.setOnClickListener{
71 | if (sharedPref != null) {
72 | if (!validateInput())
73 | {
74 | Toast.makeText(
75 | this,
76 | getString(R.string.correctErrorToast),
77 | Toast.LENGTH_SHORT
78 | ).show()
79 | }
80 | else
81 | {
82 | with(sharedPref.edit()) {
83 | putString("serverUrl", serverUrl.text.toString())
84 | putString("username", username.text.toString())
85 | putInt("sshPort", sshPort.text.toString().toInt())
86 | putString("pubkey", pubkey.text.toString())
87 |
88 | if(radioYunohost.isChecked) putString("server_type", "yunohost")
89 | else putString("server_type", "docker")
90 |
91 | apply()
92 | }
93 |
94 | loginButton.animate().apply {
95 | duration = 1000
96 | rotationXBy(360f)
97 | }.withEndAction{
98 | Toast.makeText(this, getString(R.string.connectionSaved), Toast.LENGTH_LONG).show()
99 | val intent = Intent()
100 | intent.putExtra("server_type", sharedPref.getString("server_type", ""))
101 | setResult(Activity.RESULT_OK, intent)
102 | finish()
103 | }.start()
104 | }
105 | }
106 | else
107 | {
108 | setResult(Activity.RESULT_CANCELED)
109 | finish()
110 | }
111 | }
112 |
113 | }
114 |
115 | private fun validateInput(pubKeyRequired: Boolean = true, radioRequired: Boolean = true): Boolean{
116 | var wrongData = false
117 |
118 | val serverUrl = findViewById(R.id.serverUrl)
119 | val username = findViewById(R.id.username)
120 | val sshPort = findViewById(R.id.sshPort)
121 | val pubkey = findViewById(R.id.pubkey)
122 | val radioYunohost = findViewById(R.id.radioYH)
123 | val radioDocker = findViewById(R.id.radioDocker)
124 |
125 | if (!serverUrl.text.toString().matches(Regex("[A-Za-z0-9.]*"))
126 | or serverUrl.text.toString().isEmpty())
127 | {
128 | wrongData = true
129 | serverUrl.error = getString(R.string.serverUrlError)
130 | }
131 |
132 | if (!username.text.toString().matches(Regex("[A-Za-z0-9]*"))
133 | or username.text.toString().isEmpty())
134 | {
135 | wrongData = true
136 | username.error = getString(R.string.usernameError)
137 | }
138 |
139 | if (!sshPort.text.toString().matches(Regex("[0-9]*"))
140 | or sshPort.text.toString().isEmpty()
141 | or (sshPort.text.toString().toInt() < 1)
142 | or (sshPort.text.toString().toInt() > 65535))
143 | {
144 | wrongData = true
145 | sshPort.error = getString(R.string.sshPortError)
146 | }
147 |
148 | if (pubKeyRequired and pubkey.text.toString().isEmpty())
149 | {
150 | wrongData = true
151 | pubkey.error = getString(R.string.pubkeyError)
152 | }
153 |
154 | return !wrongData
155 | }
156 |
157 | override fun onBackPressed() {
158 | if (!backPressed){
159 | backPressed = true
160 | Toast.makeText(this, "Press one more time to exit", Toast.LENGTH_SHORT).show()
161 | }
162 | else
163 | {
164 | finishAffinity()
165 | }
166 | }
167 |
168 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/doyouhost/servercommander/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.doyouhost.servercommander
2 |
3 | import android.app.Activity
4 | import android.content.Context
5 | import android.content.Intent
6 | import android.content.SharedPreferences
7 | import android.os.Bundle
8 | import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
9 | import androidx.appcompat.app.AppCompatActivity
10 | import androidx.core.view.WindowCompat
11 | import androidx.viewpager2.widget.ViewPager2
12 | import com.google.android.material.tabs.TabLayout
13 |
14 | class MainActivity : AppCompatActivity() {
15 |
16 | private lateinit var tabLayout: TabLayout
17 | private lateinit var viewPager2: ViewPager2
18 |
19 | private lateinit var sharedPref: SharedPreferences
20 |
21 | override fun onCreate(savedInstanceState: Bundle?) {
22 | WindowCompat.setDecorFitsSystemWindows(window, false)
23 | super.onCreate(savedInstanceState)
24 |
25 | setContentView(R.layout.activity_main)
26 | tabLayout = findViewById(R.id.tabLayout)
27 | viewPager2 = findViewById(R.id.viewPager)
28 |
29 | sharedPref = getSharedPreferences(
30 | "ServerCommander", Context.MODE_PRIVATE
31 | )
32 |
33 | with( sharedPref.edit()){
34 | putBoolean("connectionTested", false)
35 | apply()
36 | }
37 |
38 | if ( sharedPref.contains("serverUrl") and
39 | sharedPref.contains("username") and
40 | sharedPref.contains("sshPort") and
41 | sharedPref.contains("pubkey") and
42 | sharedPref.contains("server_type")
43 | ) {
44 | when (sharedPref.getString("server_type", "")) {
45 | "docker" -> {
46 | viewPager2.adapter = DockerViewPagerAdapter(this)
47 | tabLayout.getTabAt(2)?.text = "DOCKER"
48 | }
49 | "yunohost" -> {
50 | viewPager2.adapter = YunohostViewPagerAdapter(this)
51 | tabLayout.getTabAt(2)?.text = "YUNOHOST"
52 | }
53 | else -> {
54 | finishAffinity()
55 | }
56 | }
57 | }
58 | else
59 | {
60 | openLoginActivityForResult()
61 | }
62 |
63 | tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
64 | override fun onTabSelected(tab: TabLayout.Tab) {
65 | viewPager2.currentItem = tab.position
66 | }
67 | override fun onTabUnselected(tab: TabLayout.Tab) {}
68 | override fun onTabReselected(tab: TabLayout.Tab) {}
69 | })
70 |
71 | viewPager2.registerOnPageChangeCallback( object: ViewPager2.OnPageChangeCallback() {
72 | override fun onPageSelected(position: Int) {
73 | super.onPageSelected(position)
74 | tabLayout.getTabAt(position)!!.select()
75 | }
76 | })
77 | }
78 |
79 | private fun openLoginActivityForResult() {
80 | val intent = Intent(this, LoginActivity::class.java)
81 | resultLauncher.launch(intent)
82 | }
83 |
84 | private var resultLauncher = registerForActivityResult(StartActivityForResult()) { result ->
85 | if (result.resultCode == Activity.RESULT_OK) {
86 | // val data: Intent? = result.data
87 |
88 | when (sharedPref.getString("server_type", "")) {
89 | "docker" -> {
90 | viewPager2.adapter = DockerViewPagerAdapter(this)
91 | tabLayout.getTabAt(2)?.text = "DOCKER"
92 | }
93 | "yunohost" -> {
94 | viewPager2.adapter = YunohostViewPagerAdapter(this)
95 | tabLayout.getTabAt(2)?.text = "YUNOHOST"
96 | }
97 | else -> {
98 | finishAffinity()
99 | }
100 | }
101 | }
102 | }
103 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/doyouhost/servercommander/YunohostConnection.kt:
--------------------------------------------------------------------------------
1 | package com.doyouhost.servercommander
2 |
3 | import android.util.Log
4 | import android.widget.Toast
5 | import com.doyouhost.servercommander.fragments.YunohostFragment
6 | import okhttp3.*
7 | import org.json.JSONObject
8 | import org.json.JSONTokener
9 | import java.io.IOException
10 |
11 | class YunohostConnection {
12 |
13 | companion object
14 | {
15 | var boolIsApiInstalled: Boolean = false
16 | lateinit var cookie: List
17 | var usersNumberValue = 0
18 | var domainNumberValue = 0
19 | var appToUpdateNumberValue = 0
20 | var appToUpdateNames = ""
21 | var IsSshKeysPushed : Boolean = true
22 | var createdBackupsValue = 0
23 |
24 | fun authenticate(url: String, password: String, username: String) {
25 | val client = OkHttpClient()
26 |
27 | val formBody = FormBody.Builder()
28 | .add("credentials", "$username:$password")
29 | .build()
30 |
31 | val request = Request.Builder()
32 | .url(url)
33 | .header("X-Requested-With", "serverCommander")
34 | .post(formBody)
35 | .build()
36 |
37 | client.newCall(request).execute().use { response ->
38 | cookie = response.headers.values("Set-Cookie")
39 | }
40 | }
41 |
42 | fun isCookieInitalized() = ::cookie.isInitialized
43 |
44 | fun getUserNumber(url: String) {
45 |
46 | val client = OkHttpClient()
47 | if(::cookie.isInitialized){
48 | val request = Request.Builder()
49 | .url(url)
50 | .header(
51 | name = "Cookie",
52 | value = cookie[0]
53 | )
54 | .header("accept", "*/*")
55 | .build()
56 |
57 | client.newCall(request).execute().use { response ->
58 | val output = response.body!!.string()
59 |
60 | val rsp = JSONTokener(output).nextValue() as JSONObject
61 | val users = rsp.getJSONObject("users")
62 | usersNumberValue = users.length()
63 |
64 | }
65 | }
66 |
67 | }
68 |
69 | fun getCreatedBackupsNumber(url: String) {
70 |
71 | val client = OkHttpClient()
72 | if(::cookie.isInitialized){
73 | val request = Request.Builder()
74 | .url(url)
75 | .header(
76 | name = "Cookie",
77 | value = cookie[0]
78 | )
79 | .header("accept", "*/*")
80 | .build()
81 |
82 | client.newCall(request).execute().use { response ->
83 | val output = response.body!!.string()
84 |
85 | val rsp = JSONTokener(output).nextValue() as JSONObject
86 | val archives = rsp.getJSONObject("archives")
87 | createdBackupsValue = archives.length()
88 |
89 | }
90 | }
91 |
92 | }
93 |
94 |
95 |
96 |
97 | fun getDomainNumber(url: String) {
98 |
99 | val client = OkHttpClient()
100 | if(::cookie.isInitialized){
101 | val request = Request.Builder()
102 | .url(url)
103 | .header(
104 | name = "Cookie",
105 | value = cookie[0]
106 | )
107 | .header("accept", "*/*")
108 | .build()
109 |
110 |
111 | client.newCall(request).execute().use { response ->
112 | val output = response.body!!.string()
113 |
114 | val rsp = JSONTokener(output).nextValue() as JSONObject
115 | val array = rsp.getJSONArray("domains")
116 | domainNumberValue = array.length()
117 | }
118 | }
119 |
120 | }
121 |
122 | fun getAppToUpdateNumberMethod(url: String) {
123 |
124 | val client = OkHttpClient()
125 | if(::cookie.isInitialized){
126 | val request = Request.Builder()
127 | .url(url)
128 | .header(
129 | name = "Cookie",
130 | value = cookie[0]
131 | )
132 | .header("accept", "*/*")
133 | .build()
134 |
135 | client.newCall(request).execute().use { response ->
136 | val output = response.body!!.string()
137 |
138 | val rsp = JSONTokener(output).nextValue() as JSONObject
139 | val array = rsp.getJSONArray("apps")
140 |
141 | appToUpdateNumberValue = array.length()
142 |
143 | var names = ""
144 |
145 | for (i in 0 until array.length()) {
146 | names += " " + array.getJSONObject(i).getString("name") + ","
147 | }
148 | Log.d("names", names)
149 |
150 | appToUpdateNames = names
151 |
152 | }
153 | }
154 |
155 | }
156 |
157 | fun isAPIInstalled (url: String) {
158 |
159 | val client = OkHttpClient()
160 | boolIsApiInstalled = false
161 |
162 | val request = Request.Builder()
163 | .url(url)
164 | .header("accept", "*/*")
165 | .build()
166 |
167 | client.newCall(request).execute().use{response ->
168 | try {
169 | val resp = JSONTokener(response.body!!.string()).nextValue() as JSONObject
170 | if (resp.getBoolean("installed")) {
171 | boolIsApiInstalled = true
172 | } else boolIsApiInstalled = true
173 | } catch (_: Exception) {}
174 | }
175 | }
176 |
177 | fun postNewSshKey(url: String, pubkey: String, username: String) {
178 | val client = OkHttpClient()
179 | IsSshKeysPushed = true
180 |
181 | val formBody = FormBody.Builder()
182 | .add("username", username)
183 | .add("key", pubkey)
184 | .add("comment", "Added with Server Commander")
185 | .build()
186 |
187 | val request = Request.Builder()
188 | .url(url)
189 | .header("accept", "*/*")
190 | .header("Content-type", "multipart/form-data")
191 | .header("X-Requested-With", "serverCommander")
192 | .header("Cookie", cookie[0])
193 | .post(formBody)
194 | .build()
195 |
196 | client.newCall(request).execute().use { response ->
197 | val request = (response.body!!.string())
198 |
199 | Log.d ("Req", request)
200 |
201 | if (request.contains("error")) {
202 | IsSshKeysPushed = false
203 | }
204 | }
205 | }
206 | }
207 | }
208 |
--------------------------------------------------------------------------------
/app/src/main/java/com/doyouhost/servercommander/YunohostViewPagerAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.doyouhost.servercommander
2 |
3 | import androidx.fragment.app.Fragment
4 | import androidx.fragment.app.FragmentActivity
5 | import androidx.viewpager2.adapter.FragmentStateAdapter
6 | import com.doyouhost.servercommander.fragments.*
7 |
8 | class YunohostViewPagerAdapter(fragmentActivity: FragmentActivity) :
9 | FragmentStateAdapter(fragmentActivity) {
10 | override fun getItemCount(): Int {
11 | return 5
12 | }
13 |
14 | override fun createFragment(position: Int): Fragment {
15 | return when(position){
16 | 0 -> HomeFragment()
17 | 1 -> SystemFragment()
18 | 2 -> YunohostFragment()
19 | 3 -> TerminalFragment()
20 | 4 -> SettingsFragment()
21 | else -> HomeFragment()
22 | }
23 | }
24 |
25 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/doyouhost/servercommander/fragments/SettingsFragment.kt:
--------------------------------------------------------------------------------
1 | package com.doyouhost.servercommander.fragments
2 |
3 | import android.content.Context
4 | import android.content.Intent
5 | import android.net.Uri
6 | import android.os.Bundle
7 | import android.view.LayoutInflater
8 | import android.view.View
9 | import android.view.ViewGroup
10 | import android.widget.Toast
11 | import androidx.fragment.app.Fragment
12 | import androidx.fragment.app.activityViewModels
13 | import com.doyouhost.servercommander.R
14 | import com.doyouhost.servercommander.databinding.FragmentSettingsBinding
15 | import com.doyouhost.servercommander.viewModels.RefreshViewModel
16 | import com.google.android.material.slider.Slider
17 |
18 | class SettingsFragment : Fragment() {
19 |
20 | private var _binding: FragmentSettingsBinding? = null
21 | private val binding get() = _binding!!
22 |
23 | private val refreshViewModel: RefreshViewModel by activityViewModels()
24 |
25 | override fun onCreate(savedInstanceState: Bundle?) {
26 | super.onCreate(savedInstanceState)
27 | }
28 |
29 | override fun onCreateView(
30 | inflater: LayoutInflater, container: ViewGroup?,
31 | savedInstanceState: Bundle?
32 | ): View {
33 | // Inflate the layout for this fragment
34 | _binding = FragmentSettingsBinding.inflate(inflater, container, false)
35 | return binding.root
36 | }
37 |
38 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
39 | super.onViewCreated(view, savedInstanceState)
40 |
41 | val bmcButton = binding.bmcButton
42 | val clearSettingsButton = binding.clearSettingsButton
43 |
44 | val serverUrlUpdateText = binding.serverUrlUpdateText
45 | val serverUrlUpdateButton = binding.serverUrlUpdateButton
46 | val usernameUpdateText = binding.usernameUpdateText
47 | val usernameUpdateButton = binding.usernameUpdateButton
48 | val sshPortUpdateText = binding.sshPortUpdateText
49 | val sshPortUpdateButton = binding.sshPortUpdateButton
50 | val refreshSlider = binding.refreshIntervalValue
51 | val refreshSwitch = binding.refreshSwitch
52 |
53 | val sudoPassword = binding.sudoPassword
54 | val sudoSave = binding.sudoSaveButton
55 |
56 | val yunohostPassword = binding.yunohostPassword
57 | val yunohostSave = binding.saveYunohostPassword
58 |
59 | refreshSwitch.isChecked = refreshViewModel.enabled.value == true
60 |
61 | val sharedPref = requireActivity().getSharedPreferences(
62 | "ServerCommander", Context.MODE_PRIVATE
63 | )
64 |
65 | serverUrlUpdateText.setText(sharedPref.getString("serverUrl", getString(R.string.not_available)))
66 | usernameUpdateText.setText(sharedPref.getString("username", getString(R.string.not_available)))
67 | sshPortUpdateText.setText(sharedPref.getInt("sshPort", 22).toString())
68 |
69 | bmcButton.setOnClickListener {
70 | val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse("https://www.buymeacoffee.com/doyouhost"))
71 | startActivity(browserIntent)
72 | }
73 |
74 | clearSettingsButton.setOnClickListener{
75 | with(sharedPref.edit()){
76 | remove("serverUrl")
77 | remove("username")
78 | remove("pubkey")
79 | remove("sshPort")
80 | remove("sudo_password")
81 | putBoolean("connectionTested", false)
82 | apply()
83 |
84 | requireActivity().finish()
85 | startActivity(requireActivity().intent)
86 | }
87 | }
88 |
89 | serverUrlUpdateButton.setOnClickListener{
90 | if(serverUrlUpdateText.text.toString().matches(Regex("[A-Za-z0-9.]*")))
91 | {
92 | with(sharedPref.edit()){
93 | putString("serverUrl", serverUrlUpdateText.text.toString())
94 | putBoolean("connectionTested", false)
95 | apply()
96 | }
97 | Toast.makeText(context, getString(R.string.server_addres_updated), Toast.LENGTH_SHORT).show()
98 | }
99 | else
100 | {
101 | serverUrlUpdateText.error = getString(R.string.serverUrlError)
102 | }
103 | }
104 |
105 | usernameUpdateButton.setOnClickListener{
106 | if(usernameUpdateText.text.toString().matches(Regex("[A-Za-z0-9.]*")))
107 | {
108 | with(sharedPref.edit()){
109 | putString("username", usernameUpdateText.text.toString())
110 | putBoolean("connectionTested", false)
111 | apply()
112 | }
113 | Toast.makeText(context, getString(R.string.username_updated), Toast.LENGTH_SHORT).show()
114 | }
115 | else
116 | {
117 | usernameUpdateText.error = getString(R.string.usernameError)
118 | }
119 | }
120 |
121 | sshPortUpdateButton.setOnClickListener {
122 | if(sshPortUpdateText.text.toString().matches(Regex("[0-9.]*")) and
123 | (sshPortUpdateText.text.toString().toInt() > 0) and
124 | (sshPortUpdateText.text.toString().toInt() <= 65535))
125 | {
126 | with(sharedPref.edit()){
127 | putInt("sshPort", sshPortUpdateText.text.toString().toInt())
128 | putBoolean("connectionTested", false)
129 | apply()
130 | }
131 | Toast.makeText(context, getString(R.string.sshPortUpdated), Toast.LENGTH_SHORT).show()
132 | }
133 | else
134 | {
135 | usernameUpdateText.error = getString(R.string.sshPortError)
136 | }
137 | }
138 |
139 | refreshSlider.addOnChangeListener(Slider.OnChangeListener {
140 | _, value, _ ->
141 |
142 | refreshViewModel.interval(value.toInt())
143 | })
144 |
145 | refreshSwitch.setOnClickListener {
146 | refreshViewModel.enabled(refreshSwitch.isChecked)
147 | }
148 |
149 | sudoSave.setOnClickListener {
150 | val password = sudoPassword.text.toString()
151 |
152 | if (password.isNotEmpty() and (password != ""))
153 | {
154 | with(sharedPref.edit()){
155 | putString("sudo_password", password)
156 | apply()
157 | }
158 | Toast.makeText(context, "Password saved", Toast.LENGTH_SHORT).show()
159 | }
160 | else Toast.makeText(context, "Password cannot be saved", Toast.LENGTH_SHORT).show()
161 | }
162 |
163 | yunohostSave.setOnClickListener{
164 | val yunohostPasswordText = yunohostPassword.text.toString()
165 |
166 | if (yunohostPasswordText.isNotEmpty() and (yunohostPasswordText != ""))
167 | {
168 | with(sharedPref.edit()){
169 | putString("yunohost_password", yunohostPasswordText)
170 | apply()
171 | }
172 | Toast.makeText(context, "Password saved", Toast.LENGTH_SHORT).show()
173 | }
174 | else Toast.makeText(context, "Password cannot be saved", Toast.LENGTH_SHORT).show()
175 |
176 |
177 | }
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/app/src/main/java/com/doyouhost/servercommander/fragments/TerminalFragment.kt:
--------------------------------------------------------------------------------
1 | package com.doyouhost.servercommander.fragments
2 |
3 | import android.content.Context
4 | import android.content.SharedPreferences
5 | import android.os.Bundle
6 | import android.text.method.ScrollingMovementMethod
7 | import android.view.LayoutInflater
8 | import android.view.View
9 | import android.view.ViewGroup
10 | import android.widget.Toast
11 | import androidx.fragment.app.Fragment
12 | import com.doyouhost.servercommander.SshConnection
13 | import com.doyouhost.servercommander.databinding.FragmentTerminalBinding
14 | import kotlinx.coroutines.Dispatchers
15 | import kotlinx.coroutines.MainScope
16 | import kotlinx.coroutines.async
17 | import kotlinx.coroutines.launch
18 |
19 | class TerminalFragment : Fragment() {
20 |
21 | private var _binding: FragmentTerminalBinding? = null
22 | private val binding get() = _binding!!
23 |
24 | private lateinit var sharedPref: SharedPreferences
25 | private lateinit var sshConnection: SshConnection
26 |
27 | override fun onCreate(savedInstanceState: Bundle?) {
28 | super.onCreate(savedInstanceState)
29 |
30 | sharedPref = requireActivity().getSharedPreferences(
31 | "ServerCommander", Context.MODE_PRIVATE
32 | )
33 |
34 | if(sharedPref.contains("serverUrl") and
35 | sharedPref.contains("username") and
36 | sharedPref.contains("sshPort") and
37 | sharedPref.contains("pubkey") and
38 | sharedPref.contains("connectionTested")) {
39 |
40 | sshConnection = SshConnection(
41 | sharedPref.getString("serverUrl", "").toString(),
42 | sharedPref.getInt("sshPort", 22),
43 | sharedPref.getString("username", "").toString(),
44 | sharedPref.getString("pubkey", "").toString()
45 | )
46 | }
47 |
48 | }
49 |
50 | override fun onCreateView(
51 | inflater: LayoutInflater, container: ViewGroup?,
52 | savedInstanceState: Bundle?
53 | ): View {
54 | _binding = FragmentTerminalBinding.inflate(inflater, container, false)
55 | return binding.root
56 | }
57 |
58 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
59 | super.onViewCreated(view, savedInstanceState)
60 |
61 | val terminalText = binding.terminalText
62 | val commandText = binding.commandText
63 | val commandSendButton = binding.commandSendButton
64 |
65 | terminalText.movementMethod = ScrollingMovementMethod()
66 |
67 | if (sharedPref.getBoolean("connectionTested", false)){
68 | if (::sshConnection.isInitialized){
69 | val coroutineScope = MainScope()
70 | coroutineScope.launch {
71 | val defer = async(Dispatchers.IO) {
72 | if (!sshConnection.isOpen()) {
73 | sshConnection.openConnection()
74 | }
75 | }
76 | defer.await()
77 | }
78 | }
79 | }
80 |
81 | commandSendButton.setOnClickListener{
82 |
83 | if (sharedPref.getBoolean("connectionTested", false)){
84 | if (::sshConnection.isInitialized){
85 | val coroutineScope = MainScope()
86 | coroutineScope.launch {
87 | var text = terminalText.text.toString()
88 | val command = commandText.text.toString()
89 | val defer = async(Dispatchers.IO) {
90 | sshConnection.executeRemoteCommand(command)
91 | }
92 | val output = defer.await()
93 | text += output
94 | terminalText.text = text
95 |
96 | val scrollAmount: Int = terminalText.layout.getLineTop(terminalText.lineCount) - terminalText.height
97 |
98 | if (scrollAmount > 0)
99 | terminalText.scrollTo(0, scrollAmount)
100 | else
101 | terminalText.scrollTo(0, 0)
102 |
103 | commandText.setText("")
104 | }
105 | }
106 | else Toast.makeText(context, "Connection with provided settings is not possible", Toast.LENGTH_LONG).show()
107 | }
108 | else Toast.makeText(context, "You need to test your connection first. Please click red server icon at the HOME tab", Toast.LENGTH_LONG).show()
109 | }
110 | }
111 |
112 | override fun onResume() {
113 | super.onResume()
114 |
115 | if( ::sshConnection.isInitialized )
116 | {
117 | if ((sharedPref.getString("serverUrl", "") != sshConnection.serverAddress )
118 | or (sharedPref.getString("username", "") != sshConnection.username )
119 | or (sharedPref.getInt("sshPort", 22) != sshConnection.serverPort))
120 | {
121 | val serverUrl = sharedPref.getString("serverUrl", "")!!
122 | val username = sharedPref.getString("username", "")!!
123 | val sshPort = sharedPref.getInt("sshPort", 22)
124 | val pubkey = sharedPref.getString("pubkey", "")!!
125 | sshConnection = SshConnection(serverUrl, sshPort, username, pubkey)
126 | }
127 | }
128 |
129 |
130 | if(!::sshConnection.isInitialized)
131 | {
132 | val serverUrl = sharedPref.getString("serverUrl", "")!!
133 | val username = sharedPref.getString("username", "")!!
134 | val sshPort = sharedPref.getInt("sshPort", 22)
135 | val pubkey = sharedPref.getString("pubkey", "")!!
136 | sshConnection = SshConnection(serverUrl, sshPort, username, pubkey)
137 | }
138 | }
139 |
140 | override fun onPause() {
141 | super.onPause()
142 |
143 | }
144 |
145 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/doyouhost/servercommander/viewModels/RefreshViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.doyouhost.servercommander.viewModels
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.lifecycle.MutableLiveData
5 | import androidx.lifecycle.ViewModel
6 |
7 | class RefreshViewModel : ViewModel() {
8 | private val refreshEnabled = MutableLiveData(false)
9 | private val refreshInterval = MutableLiveData(30)
10 |
11 | val enabled: LiveData get() = refreshEnabled
12 |
13 | val interval: LiveData get() = refreshInterval
14 |
15 | fun enabled(state: Boolean) {
16 | refreshEnabled.value = state
17 | }
18 |
19 | fun interval(interval: Int) {
20 | refreshInterval.value = interval
21 | }
22 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/account_group.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/alpha_y_circle_outline.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/application.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/application_export.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/bottle_tonic_plus.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/briefcase_arrow_up_down.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/console_network.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/docker.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/domain.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/download.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/oci.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/package_variant_closed.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/power.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/run_fast.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/server_network.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/server_network_off.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/stop_circle.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/swap_vertical_bold.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/archive_arrow_up.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/archive_search.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bmc_logo.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/circular_loading_bar.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/cpu_64_bit.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/fire_alert.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/harddisk.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_device_thermostat_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/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/memory.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/package_variant_closed.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/penguin.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/play_circle.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/refresh.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/restart.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/router_network.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/router_wireless.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/thermometer.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/weather_sunny.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_login.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
12 |
13 |
28 |
29 |
48 |
49 |
62 |
63 |
79 |
80 |
94 |
95 |
106 |
107 |
116 |
117 |
123 |
124 |
128 |
129 |
135 |
136 |
137 |
138 |
150 |
151 |
163 |
164 |
176 |
177 |
178 |
179 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
21 |
22 |
27 |
28 |
33 |
34 |
39 |
40 |
45 |
46 |
51 |
52 |
53 |
61 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/alert_dialog_password.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
16 |
17 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/alert_dialog_updates.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
16 |
17 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dashboard_item_disk_info.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
20 |
21 |
22 |
23 |
30 |
31 |
42 |
43 |
44 |
54 |
55 |
56 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
82 |
83 |
89 |
90 |
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dashboard_item_heaviest_app_info.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
20 |
21 |
22 |
23 |
30 |
31 |
42 |
43 |
44 |
54 |
55 |
56 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
82 |
83 |
89 |
90 |
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dashboard_item_kernel_info.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
20 |
21 |
22 |
23 |
32 |
33 |
44 |
45 |
46 |
57 |
58 |
59 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
85 |
86 |
92 |
93 |
94 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dashboard_item_localip_info.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
21 |
22 |
23 |
24 |
33 |
34 |
45 |
46 |
47 |
58 |
59 |
60 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
86 |
87 |
93 |
94 |
95 |
96 |
97 |
98 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dashboard_item_package_info.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
20 |
21 |
22 |
23 |
30 |
31 |
42 |
43 |
44 |
54 |
55 |
56 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
82 |
83 |
89 |
90 |
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dashboard_item_publicip_info.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
21 |
22 |
23 |
24 |
33 |
34 |
45 |
46 |
47 |
58 |
59 |
60 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
86 |
87 |
93 |
94 |
95 |
96 |
97 |
98 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dashboard_item_uptime_info.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
20 |
21 |
22 |
23 |
30 |
31 |
42 |
43 |
44 |
54 |
55 |
56 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
82 |
83 |
89 |
90 |
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/docker_app_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
21 |
22 |
23 |
24 |
31 |
32 |
44 |
45 |
55 |
56 |
67 |
68 |
82 |
83 |
97 |
98 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/edit_text_dialog.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_docker.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
29 |
30 |
43 |
44 |
54 |
55 |
59 |
60 |
70 |
71 |
81 |
82 |
87 |
88 |
95 |
96 |
103 |
104 |
105 |
106 |
107 |
117 |
118 |
123 |
124 |
131 |
132 |
139 |
140 |
141 |
142 |
143 |
153 |
154 |
159 |
160 |
167 |
168 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
192 |
193 |
205 |
206 |
207 |
208 |
209 |
210 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_terminal.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
26 |
27 |
38 |
39 |
60 |
61 |
74 |
75 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_yunohost.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
29 |
30 |
44 |
45 |
55 |
56 |
60 |
61 |
71 |
72 |
82 |
83 |
93 |
94 |
100 |
101 |
108 |
109 |
116 |
117 |
118 |
119 |
120 |
130 |
131 |
136 |
137 |
144 |
145 |
152 |
153 |
154 |
155 |
156 |
166 |
167 |
172 |
173 |
180 |
181 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
208 |
209 |
213 |
214 |
217 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/yunohost_dashboard_item_1.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
20 |
21 |
22 |
23 |
30 |
31 |
43 |
44 |
55 |
56 |
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/yunohost_dashboard_item_2.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
21 |
22 |
23 |
24 |
31 |
32 |
45 |
46 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/server_commander.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/server_commander_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MorganMLGman/ServerCommander/847c40f128f8a98a4530ab1c2301084d8fe2563c/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MorganMLGman/ServerCommander/847c40f128f8a98a4530ab1c2301084d8fe2563c/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/server_commander.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MorganMLGman/ServerCommander/847c40f128f8a98a4530ab1c2301084d8fe2563c/app/src/main/res/mipmap-hdpi/server_commander.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/server_commander_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MorganMLGman/ServerCommander/847c40f128f8a98a4530ab1c2301084d8fe2563c/app/src/main/res/mipmap-hdpi/server_commander_background.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/server_commander_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MorganMLGman/ServerCommander/847c40f128f8a98a4530ab1c2301084d8fe2563c/app/src/main/res/mipmap-hdpi/server_commander_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/server_commander_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MorganMLGman/ServerCommander/847c40f128f8a98a4530ab1c2301084d8fe2563c/app/src/main/res/mipmap-hdpi/server_commander_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MorganMLGman/ServerCommander/847c40f128f8a98a4530ab1c2301084d8fe2563c/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MorganMLGman/ServerCommander/847c40f128f8a98a4530ab1c2301084d8fe2563c/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/server_commander.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MorganMLGman/ServerCommander/847c40f128f8a98a4530ab1c2301084d8fe2563c/app/src/main/res/mipmap-mdpi/server_commander.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/server_commander_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MorganMLGman/ServerCommander/847c40f128f8a98a4530ab1c2301084d8fe2563c/app/src/main/res/mipmap-mdpi/server_commander_background.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/server_commander_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MorganMLGman/ServerCommander/847c40f128f8a98a4530ab1c2301084d8fe2563c/app/src/main/res/mipmap-mdpi/server_commander_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/server_commander_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MorganMLGman/ServerCommander/847c40f128f8a98a4530ab1c2301084d8fe2563c/app/src/main/res/mipmap-mdpi/server_commander_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MorganMLGman/ServerCommander/847c40f128f8a98a4530ab1c2301084d8fe2563c/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MorganMLGman/ServerCommander/847c40f128f8a98a4530ab1c2301084d8fe2563c/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/server_commander.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MorganMLGman/ServerCommander/847c40f128f8a98a4530ab1c2301084d8fe2563c/app/src/main/res/mipmap-xhdpi/server_commander.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/server_commander_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MorganMLGman/ServerCommander/847c40f128f8a98a4530ab1c2301084d8fe2563c/app/src/main/res/mipmap-xhdpi/server_commander_background.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/server_commander_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MorganMLGman/ServerCommander/847c40f128f8a98a4530ab1c2301084d8fe2563c/app/src/main/res/mipmap-xhdpi/server_commander_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/server_commander_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MorganMLGman/ServerCommander/847c40f128f8a98a4530ab1c2301084d8fe2563c/app/src/main/res/mipmap-xhdpi/server_commander_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MorganMLGman/ServerCommander/847c40f128f8a98a4530ab1c2301084d8fe2563c/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MorganMLGman/ServerCommander/847c40f128f8a98a4530ab1c2301084d8fe2563c/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/server_commander.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MorganMLGman/ServerCommander/847c40f128f8a98a4530ab1c2301084d8fe2563c/app/src/main/res/mipmap-xxhdpi/server_commander.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/server_commander_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MorganMLGman/ServerCommander/847c40f128f8a98a4530ab1c2301084d8fe2563c/app/src/main/res/mipmap-xxhdpi/server_commander_background.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/server_commander_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MorganMLGman/ServerCommander/847c40f128f8a98a4530ab1c2301084d8fe2563c/app/src/main/res/mipmap-xxhdpi/server_commander_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/server_commander_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MorganMLGman/ServerCommander/847c40f128f8a98a4530ab1c2301084d8fe2563c/app/src/main/res/mipmap-xxhdpi/server_commander_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MorganMLGman/ServerCommander/847c40f128f8a98a4530ab1c2301084d8fe2563c/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MorganMLGman/ServerCommander/847c40f128f8a98a4530ab1c2301084d8fe2563c/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/server_commander.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MorganMLGman/ServerCommander/847c40f128f8a98a4530ab1c2301084d8fe2563c/app/src/main/res/mipmap-xxxhdpi/server_commander.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/server_commander_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MorganMLGman/ServerCommander/847c40f128f8a98a4530ab1c2301084d8fe2563c/app/src/main/res/mipmap-xxxhdpi/server_commander_background.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/server_commander_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MorganMLGman/ServerCommander/847c40f128f8a98a4530ab1c2301084d8fe2563c/app/src/main/res/mipmap-xxxhdpi/server_commander_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/server_commander_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MorganMLGman/ServerCommander/847c40f128f8a98a4530ab1c2301084d8fe2563c/app/src/main/res/mipmap-xxxhdpi/server_commander_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values-land/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 48dp
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values-night/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FF000000
4 | #FFFFFFFF
5 | #667AFF
6 | #252C4B
7 | #27325F
8 | #FFFFFF
9 | #797F95
10 | #2CC2EA
11 | #4DE9C9
12 | #72A4FF
13 | #8B72FF
14 | #ff7440
15 | #ff0066
16 | #cf956b
17 | #ff0003
18 | #4ff055
19 | #8762c9
20 | #fcfcfc
21 | #e81c0e
22 | #32b019
23 | #218ed1
24 |
--------------------------------------------------------------------------------
/app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v29/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w1240dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 200dp
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w600dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 48dp
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FF000000
4 | #FFFFFFFF
5 | #6A7CFF
6 | #F7F7Fa
7 | #FFFFFF
8 | #565B65
9 | #9B9A9D
10 | #2CC2EA
11 | #4DE9C9
12 | #72A4FF
13 | #8B72FF
14 | #ff7440
15 | #ff0066
16 | #cf956b
17 | #ff0003
18 | #4ff055
19 | #8762c9
20 | #262626
21 | #e81c0e
22 | #32b019
23 | #218ed1
24 | #f2f2f2
25 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 16dp
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | ServerCommander
3 | Settings
4 |
5 | First Fragment
6 | Second Fragment
7 | Next
8 | Previous
9 | SAVE CONNECTION
10 | ServerUrl
11 | Username
12 | Password
13 | YunoHost
14 | Select system type
15 | Public Key Location
16 | Docker
17 | GENERATE RSA KEY PAIR
18 | appLogo
19 | CLEAR APP SETTINGS
20 | Must be a valid server address
21 | Username can contains only letters and numbers
22 | Pubkey cannot be empty
23 | Please correct all the errors.
24 | Connection details saved.
25 | YunoHost needs \'admin\' user
26 | Application is not able to write data to external storage
27 | New RSA keys have been generated
28 | New keys have not been generated
29 | Overwrite
30 | Cancel
31 | CAUTION
32 | You are about to overwrite previously generated keys! Are you sure you want to do this?
33 | user@ServerCommander
34 | Hello blank fragment
35 | LoginActivity
36 | Email
37 | Password
38 | Sign in or register
39 | Sign in
40 | "Welcome !"
41 | Not a valid username
42 | Password must be >5 characters
43 | "Login failed"
44 | ServerCommander
45 | Quick Overview
46 | Temp
47 | 43 C
48 | Dashboard
49 | UPDATE
50 | Server Address
51 | Username updated
52 | username
53 | Server address updated
54 | serverUrl
55 | pubkey
56 | Not available
57 | SEND
58 | Command
59 | terminalOutputLabel
60 | READ PUBKEY
61 | No pubkey available. You can generate one now.
62 | App is not able to read file location
63 | ---
64 | connectionTested
65 | Refresh Interval
66 | refresh interval slider
67 | Auto refresh
68 | Packages
69 | Number of packages
70 | Read error
71 | Uptime
72 | REBOOT
73 | SAVE
74 | SUDO Password
75 | RUN
76 | UPGRADE
77 | Please enter SUDO password
78 | Available updates
79 | connection test
80 | refresh
81 | Running Containers
82 | Stopped CTR
83 | Docker Dashboard
84 | Running CTR
85 | Containers Status
86 | All CTR
87 | Container restart
88 | Container down
89 | Container start
90 | YunoHost Dashboard
91 | Go to SSO
92 | Refreshing…
93 | Container
94 | CPU Usage
95 | RAM Usage
96 | Disk read / write
97 | Network up / down
98 | Commands
99 | Yunohost password
100 | SHUTDOWN
101 | HOME
102 | SYSTEM
103 | APPS
104 | TERMINAL
105 | SETTINGS
106 | Created Backups
107 | SSH port
108 | Push new SSH Key!
109 | Enter username
110 | SSH
111 | SSH port must be a number greater than 0 and lesser or equal to 65535
112 | SSH port updated
113 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/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/main/server_commander-playstore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MorganMLGman/ServerCommander/847c40f128f8a98a4530ab1c2301084d8fe2563c/app/src/main/server_commander-playstore.png
--------------------------------------------------------------------------------
/app/src/test/java/com/doyouhost/servercommander/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.doyouhost.servercommander
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun 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 | id 'com.android.application' version '7.3.1' apply false
4 | id 'com.android.library' version '7.3.1' apply false
5 | id 'org.jetbrains.kotlin.android' version '1.7.10' apply false
6 | }
7 |
--------------------------------------------------------------------------------
/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. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec: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 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
20 | # Enables namespacing of each library's R class so that its R class includes only the
21 | # resources declared in the library itself and none from the library's dependencies,
22 | # thereby reducing the size of the R class for that library
23 | android.nonTransitiveRClass=true
24 | org.gradle.unsafe.configuration-cache=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MorganMLGman/ServerCommander/847c40f128f8a98a4530ab1c2301084d8fe2563c/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Oct 18 14:16:34 CEST 2022
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
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 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | gradlePluginPortal()
4 | google()
5 | mavenCentral()
6 | }
7 | }
8 | dependencyResolutionManagement {
9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
10 | repositories {
11 | google()
12 | mavenCentral()
13 | }
14 | }
15 | rootProject.name = "ServerCommander"
16 | include ':app'
17 |
--------------------------------------------------------------------------------