├── .github
└── workflows
│ └── gradle.yml
├── .gitignore
├── LICENSE.md
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── releasestandard
│ │ └── scriptmanager
│ │ ├── ExampleInstrumentedTest.java
│ │ ├── StorageManagerTest.java
│ │ ├── model
│ │ └── TimeManagerTest.java
│ │ └── tools
│ │ └── CallStackTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── ic_launcher-playstore.png
│ ├── java
│ │ └── com
│ │ │ └── releasestandard
│ │ │ └── scriptmanager
│ │ │ ├── AlarmReceiver.java
│ │ │ ├── BootReceiver.java
│ │ │ ├── JavaCallbacksTrigger.java
│ │ │ ├── JavaEventsReceiver.java
│ │ │ ├── JobView.java
│ │ │ ├── JobsView.java
│ │ │ ├── MainActivity.java
│ │ │ ├── SettingsView.java
│ │ │ ├── controller
│ │ │ ├── JobData.java
│ │ │ ├── OverflowMenu.java
│ │ │ └── TimeManagerView.java
│ │ │ ├── model
│ │ │ ├── KSHEvent.java
│ │ │ ├── KornShellInterface.java
│ │ │ ├── Shell.java
│ │ │ ├── StorageManager.java
│ │ │ └── TimeManager.java
│ │ │ └── tools
│ │ │ ├── CallStack.java
│ │ │ ├── CompatAPI.java
│ │ │ └── Logger.java
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ ├── ic_launcher_background.xml
│ │ ├── logo.png
│ │ └── logo2.png
│ │ ├── layout
│ │ ├── job_view.xml
│ │ ├── jobs_view.xml
│ │ ├── main_activity.xml
│ │ └── rename_dialog.xml
│ │ ├── menu
│ │ └── menu_main.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ ├── ic_launcher_round.png
│ │ └── logo2.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ ├── ic_launcher_round.png
│ │ └── logo2.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ ├── ic_launcher_round.png
│ │ └── logo2.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ ├── ic_launcher_round.png
│ │ └── logo2.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ ├── ic_launcher_round.png
│ │ └── logo2.png
│ │ ├── values-night
│ │ └── themes.xml
│ │ ├── values
│ │ ├── api.xml
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── overflow_menu_values.xml
│ │ ├── quit_dialog.xml
│ │ ├── settings_view.xml
│ │ ├── strings.xml
│ │ └── themes.xml
│ │ └── xml
│ │ ├── provider_paths.xml
│ │ └── settings_fragment.xml
│ └── test
│ └── java
│ └── com
│ └── releasestandard
│ └── scriptmanager
│ └── ExampleUnitTest.java
├── build.gradle
├── fastlane
└── metadata
│ └── android
│ └── en-US
│ ├── full_description.txt
│ ├── images
│ └── phoneScreenshots
│ │ ├── 1.jpg
│ │ ├── 2.jpg
│ │ └── 3.jpg
│ ├── short_description.txt
│ └── title.txt
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── scripts
├── checkcompat.sh
└── newversion.sh
└── settings.gradle
/.github/workflows/gradle.yml:
--------------------------------------------------------------------------------
1 | # This workflow will build a Java project with Gradle
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
3 |
4 | name: Java CI with Gradle
5 |
6 | on:
7 | push:
8 | tags: '*'
9 |
10 | jobs:
11 | build:
12 | runs-on: ubuntu-latest
13 | env:
14 | GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
15 |
16 | steps:
17 | - uses: actions/checkout@v2
18 |
19 | - name: Set up JDK 1.8
20 | uses: actions/setup-java@v1
21 | with:
22 | java-version: 1.8
23 |
24 | - name: Grant execute permission for gradlew
25 | run: chmod +x gradlew
26 |
27 | - name: Build with Gradle
28 | run: ./gradlew packageDebug
29 |
30 | - name: Upload package to github packages
31 | run: G=./app/build.gradle && export VERSION=$(grep -oP 'versionName "\K(.*?)(?=")' $G) && export APPNAME=$(grep -oP 'applicationId ".*' $G |sed 's/.*\.\([^\.]\+\)"/\1/g') && export APK=./app/build/outputs/apk/debug/app-debug.apk && export FNAME=${APPNAME}-${VERSION}.apk && env && mv $APK $FNAME && hub release create -a $FNAME -m $FNAME $VERSION
32 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | local.properties
16 | .idea
17 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 ReleaseStandard Inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 | Terms of license apply to all files in this repository.
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | This project is no longer maintained but has been integrated and improved part of [scriptmanagerfree](https://github.com/ReleaseStandard/ScriptManagerFree).
3 |
4 | # ScriptManager
5 |
6 |
7 |
8 | Open source application to manage, launch and schedule your sh scripts.
9 | You can view it has cron schedulder of sh scripts.
10 |
11 |
16 |
17 |
18 |
19 | ## Basic usage
20 | Press "+" to create a new job.
21 | Longpress the new job and overflow menu > Edit (script your script).
22 | Then click play to launch the script.
23 | Check the result by long press then View log.
24 |
25 |
26 | ## Project motivations
27 | This project is inspired by [SL4A](https://en.wikipedia.org/wiki/Scripting_Layer_for_Android).
28 | The goal is to offert same capabilities on the device (ex: write and send SMS with sh line),
29 | throught an sh API.
30 |
31 |
32 | ## Current state of the project
33 | - [X] No root required.
34 | - [X] You can schedule and repeat jobs.
35 | - [X] Schedulded jobs persist accross reboot.
36 | - [X] Clear & Show jobs log.
37 | - [X] Import sh scripts.
38 | - [ ] SH API
39 |
40 | property avaliable
41 | smsReceived yes
42 |
43 | All GUI is well advanced but the sh api, documentation requires tons of work lol.
44 | Feel free to fork/reuse/complete it !
45 | Also if you have any question you can use releasestandard chez netcourrier point com
46 |
47 | ## Documentation
48 | Everything is on the [wiki](https://github.com/ReleaseStandard/ScriptManager/wiki).
49 |
50 | ## Donation
51 | ```bash
52 | https://paypal.me/ReleaseStandard
53 | ```
54 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | }
4 |
5 | android {
6 | compileSdkVersion 30
7 | buildToolsVersion "30.0.3"
8 |
9 | defaultConfig {
10 | applicationId "com.releasestandard.scriptmanager"
11 | minSdkVersion 23
12 | maxSdkVersion 30
13 | targetSdkVersion 28
14 | versionCode 5
15 | versionName "0.5"
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 | }
31 |
32 | dependencies {
33 |
34 | testImplementation 'androidx.test:core:1.0.0'
35 | testImplementation 'org.mockito:mockito-core:1.10.19'
36 | androidTestImplementation 'androidx.test.ext:junit:1.1.1'
37 |
38 |
39 | implementation 'androidx.appcompat:appcompat:1.2.0'
40 | implementation 'com.google.android.material:material:1.2.1'
41 | implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
42 | implementation 'androidx.navigation:navigation-fragment:2.3.2'
43 | implementation 'androidx.navigation:navigation-ui:2.3.2'
44 | implementation 'androidx.legacy:legacy-support-v4:1.0.0'
45 | implementation 'androidx.work:work-runtime:2.3.4'
46 | implementation 'androidx.preference:preference:1.1.1'
47 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
48 | }
49 |
--------------------------------------------------------------------------------
/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/releasestandard/scriptmanager/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.releasestandard.scriptmanager;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.test.platform.app.InstrumentationRegistry;
6 |
7 | import org.junit.Test;
8 |
9 | import static org.junit.Assert.*;
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * @see Testing documentation
15 | */
16 | public class ExampleInstrumentedTest {
17 | @Test
18 | public void useAppContext() {
19 | // Context of the app under test.
20 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
21 | assertEquals("com.releasestandard.scriptmanager", appContext.getPackageName());
22 | }
23 | }
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/releasestandard/scriptmanager/StorageManagerTest.java:
--------------------------------------------------------------------------------
1 | package com.releasestandard.scriptmanager;
2 |
3 | import com.releasestandard.scriptmanager.model.StorageManager;
4 |
5 | import junit.framework.TestCase;
6 |
7 | import java.lang.reflect.Field;
8 |
9 | public class StorageManagerTest extends TestCase {
10 |
11 | Field script_name;
12 |
13 | public void setUp() throws Exception {
14 | super.setUp();
15 |
16 | script_name = StorageManager.class.
17 | getDeclaredField("script_name");
18 |
19 | script_name.setAccessible(true);
20 | }
21 |
22 | public void testSetScriptName() {
23 | String scriptname = "test";
24 | StorageManager sm = new StorageManager();
25 | sm.setScriptName(scriptname);
26 | assertNull(sm.getLogAbsolutePath());
27 | assertTrue(script_name.equals(scriptname));
28 | }
29 |
30 | public void testGetLogAbsolutePath() {
31 | }
32 |
33 | public void testTestGetLogAbsolutePath() {
34 | }
35 |
36 | public void testGetStateFileAbsolutePath() {
37 | }
38 |
39 | public void testTestGetStateFileAbsolutePath() {
40 | }
41 |
42 | public void testGetScriptAbsolutePath() {
43 | }
44 |
45 | public void testTestGetScriptAbsolutePath() {
46 | }
47 |
48 | public void testGetOutputAbsolutePath() {
49 | }
50 |
51 | public void testTestGetOutputAbsolutePath() {
52 | }
53 |
54 | public void testGetExternalAbsolutePath() {
55 | }
56 |
57 | public void testGetInternalAbsolutePath() {
58 | }
59 |
60 | public void testGetScriptsFromFilesystem() {
61 | }
62 | }
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/releasestandard/scriptmanager/model/TimeManagerTest.java:
--------------------------------------------------------------------------------
1 | package com.releasestandard.scriptmanager.model;
2 |
3 | import com.releasestandard.scriptmanager.tools.Logger;
4 |
5 | import junit.framework.TestCase;
6 |
7 | import java.util.Random;
8 |
9 | import static com.releasestandard.scriptmanager.model.TimeManager.isRepeated;
10 | import static com.releasestandard.scriptmanager.model.TimeManager.sched2str;
11 | import static com.releasestandard.scriptmanager.model.TimeManager.str2sched;
12 | import static com.releasestandard.scriptmanager.model.TimeManager.validDate;
13 |
14 | public class TimeManagerTest extends TestCase {
15 |
16 | public void setUp() throws Exception {
17 | super.setUp();
18 | }
19 |
20 | public void testStr2sched() {
21 | {
22 | String s1 = "1 2 3 4 5";
23 | int[] expected = new int[]{1, 2, 3, 4, 5};
24 | int[] sched = str2sched(s1);
25 | assertEquals(sched, expected);
26 | }
27 | {
28 | String s = "* 2 3 4 5";
29 | int[] expected = new int[]{TimeManager.EACH_TIME, 2, 3, 4, 5};
30 | int[] sched = str2sched(s);
31 | assertEquals(sched, expected);
32 | }
33 | }
34 |
35 | public void testSched2str() {
36 | {
37 | String s1 = "1 2 3 4 5";
38 | int[] sched = new int[]{1, 2, 3, 4, 5};
39 | String expected = sched2str(sched);
40 | assertEquals(expected,s1);
41 | }
42 | {
43 | String s1 = "* 2 3 4 5";
44 | int[] sched = new int[]{TimeManager.EACH_TIME, 2, 3, 4, 5};
45 | String expected = sched2str(sched);
46 | assertEquals(expected,s1);
47 | }
48 | }
49 |
50 | public void testSymetry() {
51 | Random r = new Random();
52 | for ( int i = 0; i < 10 ; i = i + 1) {
53 | int [] sched = new int[5];
54 | for ( int j = 0; j < 5 ; j = j + 1) {
55 | int x = r.nextInt();
56 | if ( x < 0 ) { x = x * -1; }
57 | sched[j] = x;
58 | }
59 | String sched_ = sched2str(sched);
60 | Logger.debug("sched_=" + sched_);
61 | assertEquals(str2sched(sched_),sched);
62 | }
63 | }
64 |
65 | public void testIsRepeated() {
66 | String s1 = "* 2 3 4 5";
67 | assertEquals(isRepeated(str2sched(s1)),true);
68 | String s2 = "* * * * *";
69 | assertEquals(isRepeated(str2sched(s2)),true);
70 | String s3 = "1 2 3 4 5";
71 | assertEquals(isRepeated(str2sched(s3)),false);
72 | }
73 |
74 | public void testNextSched() {
75 | }
76 | public void testValidDate() {
77 | String [] valid = new String[] {
78 | "* 1 2 3 4",
79 | "1 2 3 4 5",
80 | "* * * * *",
81 | "0 1 2 3 4",
82 | "0 * 1 2 3"
83 | };
84 | for(String s : valid) {
85 | String ss = sched2str(str2sched(s));
86 | assertEquals(validDate(ss),true);
87 | }
88 | }
89 | }
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/releasestandard/scriptmanager/tools/CallStackTest.java:
--------------------------------------------------------------------------------
1 | package com.releasestandard.scriptmanager.tools;
2 | import junit.framework.TestCase;
3 | import org.junit.Test;
4 |
5 | public class CallStackTest extends TestCase {
6 |
7 | @Test
8 | public void testGetLastCaller() {
9 | String n = CallStack.getLastCaller();
10 | assertEquals(n, "testGetLastCaller");
11 | }
12 |
13 | public void testTestGetLastCaller() {
14 |
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
17 |
20 |
21 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-playstore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReleaseStandard/ScriptManager/270aae7e68f9cdaf8ff3f293a49ca8408df3773d/app/src/main/ic_launcher-playstore.png
--------------------------------------------------------------------------------
/app/src/main/java/com/releasestandard/scriptmanager/AlarmReceiver.java:
--------------------------------------------------------------------------------
1 | package com.releasestandard.scriptmanager;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 |
7 | import com.releasestandard.scriptmanager.controller.JobData;
8 | import com.releasestandard.scriptmanager.model.Shell;
9 | import com.releasestandard.scriptmanager.model.StorageManager;
10 | import com.releasestandard.scriptmanager.model.TimeManager;
11 | import com.releasestandard.scriptmanager.tools.Logger;
12 |
13 | import java.io.IOException;
14 | import java.io.InputStreamReader;
15 | import java.io.OutputStreamWriter;
16 | import java.util.Calendar;
17 | import java.util.GregorianCalendar;
18 | import java.util.Random;
19 |
20 | /*
21 | * Class that handle the return of alarm and start the task.
22 | */
23 | // This receiver is not exported so we dont have to secure it.
24 | public class AlarmReceiver extends BroadcastReceiver {
25 |
26 | public static int REQUEST_CODE = (new Random()).nextInt();
27 |
28 | /**
29 | * compat 8
30 | * @param context
31 | * @param intent
32 | */
33 | @Override
34 | public void onReceive(Context context, Intent intent) {
35 | String scriptname = intent.getStringExtra("script");
36 | int [] sched = intent.getIntArrayExtra("sched");
37 |
38 | Logger.debug("at:" + (new GregorianCalendar()).getTime().toString() + "scriptname="+scriptname+",sched="+ TimeManager.sched2str(sched));
39 | StorageManager sm = new StorageManager(context, scriptname);
40 | JobData jd = new JobData();
41 | sm.dump();
42 | InputStreamReader isr = StorageManager.getISR(context,sm.getStateFileNameInPath());
43 | if ( isr == null ) {
44 | Logger.debug("isr is null");
45 | }
46 | Logger.debug(sm.getStateFileNameInPath());
47 |
48 | jd.readState(isr);
49 | jd.dump("\t");
50 | Shell s = new Shell(sm);
51 | Integer j = s.execScript(scriptname);
52 | if ( j != -1 ) {
53 | jd.processes.add(j);
54 | }
55 | // add listener (will be nulled at job stop) //
56 | jd.listeners.add(JavaEventsReceiver.listeners.size()-1);
57 |
58 | if ( jd.isSchedulded && TimeManager.isRepeated(sched)) {
59 | Integer i =s.scheduleScript(context,scriptname,sched);
60 | if ( i != -1) {
61 | jd.intents.add(i);
62 | }
63 | }
64 | try {
65 | isr.close();
66 | } catch (IOException e) {
67 | e.printStackTrace(Logger.getTraceStream());
68 | }
69 |
70 | OutputStreamWriter osw = StorageManager.getOSW(context,sm.getStateFileNameInPath());
71 | jd.writeState(osw);
72 | try {
73 | osw.flush();
74 | osw.close();
75 | } catch (IOException e) {
76 | e.printStackTrace(Logger.getTraceStream());
77 | }
78 | }
79 |
80 | }
81 |
--------------------------------------------------------------------------------
/app/src/main/java/com/releasestandard/scriptmanager/BootReceiver.java:
--------------------------------------------------------------------------------
1 | package com.releasestandard.scriptmanager;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 |
7 | import com.releasestandard.scriptmanager.controller.JobData;
8 | import com.releasestandard.scriptmanager.model.Shell;
9 | import com.releasestandard.scriptmanager.model.StorageManager;
10 | import com.releasestandard.scriptmanager.tools.Logger;
11 |
12 | import java.io.IOException;
13 | import java.io.InputStreamReader;
14 | import java.io.OutputStreamWriter;
15 |
16 | public class BootReceiver extends BroadcastReceiver {
17 | /**
18 | * compat 8
19 | * @param context
20 | * @param intent
21 | */
22 | @Override
23 | public void onReceive(Context context, Intent intent) {
24 |
25 | // This is a privileged intent, we don't have to secure it.
26 | if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
27 |
28 | Logger.debug("boot signal received, starting the scripts");
29 |
30 | for ( String f : context.fileList()) {
31 | if ( ! StorageManager.isStateFile(f) ) {
32 | continue;
33 | }
34 | Logger.debug("statefile found : " + f);
35 | JobData jd = new JobData();
36 | InputStreamReader isr = StorageManager.getISR(context,f);
37 | jd.readState(isr, true);
38 | jd.name_in_path = StorageManager.removeSuffix(StorageManager.getTerminalPart(f));
39 | jd.dump();
40 | if (jd.isStarted) {
41 | Shell shell = new Shell(new StorageManager(context, jd.name_in_path));
42 | Logger.debug(jd.name_in_path + " is null");
43 | Integer i = shell.scheduleScript(context, jd.name_in_path, jd.sched,!jd.isSchedulded);
44 | if ( i != -1 ) {
45 | jd.intents.add(i);
46 | }
47 |
48 | }
49 | try {
50 | isr.close();
51 | } catch (IOException e) {
52 | e.printStackTrace(Logger.getTraceStream());
53 | }
54 | OutputStreamWriter osw = StorageManager.getOSW(context,f);
55 | jd.writeState(osw);
56 | try {
57 | osw.flush();
58 | osw.close();
59 | } catch (IOException e) {
60 | e.printStackTrace(Logger.getTraceStream());
61 | }
62 | }
63 |
64 | }
65 | }
66 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/releasestandard/scriptmanager/JavaCallbacksTrigger.java:
--------------------------------------------------------------------------------
1 | package com.releasestandard.scriptmanager;
2 |
3 | public class JavaCallbacksTrigger {
4 | }
5 |
--------------------------------------------------------------------------------
/app/src/main/java/com/releasestandard/scriptmanager/JavaEventsReceiver.java:
--------------------------------------------------------------------------------
1 | package com.releasestandard.scriptmanager;
2 |
3 |
4 | import android.content.BroadcastReceiver;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.content.SharedPreferences;
8 | import android.content.res.Resources;
9 | import android.os.Build;
10 | import android.os.Bundle;
11 | import android.telephony.SmsMessage;
12 |
13 | import com.releasestandard.scriptmanager.model.Shell;
14 | import com.releasestandard.scriptmanager.tools.Logger;
15 |
16 | import java.util.ArrayList;
17 |
18 | public class JavaEventsReceiver extends BroadcastReceiver {
19 |
20 | private SharedPreferences preferences;
21 | public static ArrayList listeners = new ArrayList<>();
22 |
23 | @Override
24 | public void onReceive(Context context, Intent intent) {
25 | /**
26 | * compat 19
27 | */
28 | if(intent.getAction().equals("android.provider.Telephony.SMS_RECEIVED")){
29 | Bundle bundle = intent.getExtras();
30 | SmsMessage[] msgs = null;
31 | String msg_from;
32 | Resources r = context.getResources();
33 | if (bundle != null){
34 | try{
35 | Object[] pdus = (Object[]) bundle.get("pdus");
36 | msgs = new SmsMessage[pdus.length];
37 | for(int i=0; i= 23) {
39 | msgs[i] = SmsMessage.createFromPdu((byte[]) pdus[i], bundle.getString("format"));
40 | } else {
41 | msgs[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
42 | }
43 | msg_from = msgs[i].getOriginatingAddress();
44 | String msgBody = msgs[i].getMessageBody();
45 | for (Shell s : listeners) {
46 | if ( s == null ) { continue ; } // listener has been disabled
47 | s.bi.triggerCallback(r.getString(R.string.callbackSmsReceived),msg_from,msgBody);
48 | }
49 | }
50 | }catch(Exception e){
51 | e.printStackTrace(Logger.getTraceStream());
52 | }
53 | }
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/releasestandard/scriptmanager/JobView.java:
--------------------------------------------------------------------------------
1 | package com.releasestandard.scriptmanager;
2 |
3 | import android.content.Context;
4 | import android.graphics.drawable.ColorDrawable;
5 | import android.os.Bundle;
6 |
7 | import androidx.annotation.NonNull;
8 | import androidx.annotation.Nullable;
9 | import androidx.appcompat.app.ActionBar;
10 | import androidx.fragment.app.Fragment;
11 | import androidx.fragment.app.FragmentTransaction;
12 |
13 | import android.os.Handler;
14 | import android.text.Editable;
15 | import android.text.TextWatcher;
16 | import android.view.LayoutInflater;
17 | import android.view.View;
18 | import android.view.ViewGroup;
19 | import android.widget.EditText;
20 | import android.widget.TextView;
21 | import android.widget.Toast;
22 |
23 | import com.google.android.material.floatingactionbutton.FloatingActionButton;
24 | import com.releasestandard.scriptmanager.controller.JobData;
25 | import com.releasestandard.scriptmanager.model.KSHEvent;
26 | import com.releasestandard.scriptmanager.model.Shell;
27 | import com.releasestandard.scriptmanager.model.StorageManager;
28 | import com.releasestandard.scriptmanager.model.TimeManager;
29 | import com.releasestandard.scriptmanager.tools.Logger;
30 | import com.releasestandard.scriptmanager.controller.TimeManagerView;
31 |
32 | import java.io.File;
33 | import java.io.IOException;
34 | import java.io.InputStreamReader;
35 | import java.io.OutputStreamWriter;
36 | import java.util.ArrayList;
37 | import java.util.Calendar;
38 |
39 | /**
40 | * compat 11
41 | * A simple {@link Fragment} subclass.
42 | * create an instance of this fragment.
43 | */
44 | public class JobView extends Fragment {
45 |
46 | public static String WRONG_DATE_FORMAT = "Wrong date format";
47 | public static Integer fragmentCount = 0;
48 |
49 | // is this fragment selected user
50 | public boolean isSelected = false;
51 | // données du modèle
52 |
53 | private View view = null;
54 |
55 | Shell shell = null;
56 | public JobData jd = new JobData();
57 |
58 | public void dump() {
59 | Logger.debug(dump(""));
60 | }
61 | public String dump(String init) {
62 | String ninit = init + "\t";
63 | return
64 | init + "JobFragment {\n"+
65 | ninit + "fragmentCount="+fragmentCount+"\n"+
66 | ninit + "WRONG_DATE_FORMAT="+WRONG_DATE_FORMAT+"\n"+
67 | jd.dump(ninit) +
68 | shell.dump(ninit) +
69 | "}\n";
70 | }
71 |
72 | public JobView(StorageManager ptr_sm, String scriptname) {
73 | this.shell = new Shell(ptr_sm);
74 | initializeInstance();
75 | this.shell.sm.setScriptName(scriptname);
76 | }
77 | public JobView(StorageManager ptr_sm) {
78 | this.shell = new Shell(ptr_sm);
79 | initializeInstance();
80 | }
81 |
82 | /**
83 | * compat 1
84 | * Used to initialize the JobFragment
85 | */
86 | public void initializeInstance() {
87 | Logger.debug("initializeInstance");
88 | Calendar rn = Calendar.getInstance();
89 | jd.sched = TimeManager.packIn(rn.get(Calendar.MINUTE),
90 | rn.get(Calendar.HOUR),
91 | rn.get(Calendar.DAY_OF_MONTH),
92 | rn.get(Calendar.MONTH),
93 | rn.get(Calendar.YEAR));
94 | rn.set(Calendar.SECOND,0);
95 | rn.set(Calendar.MILLISECOND,0);
96 |
97 | jd.id = fragmentCount++;
98 | String scriptname = "script_" + jd.id.toString();
99 | jd.name = "Script n°" + jd.id.toString();
100 | shell.sm.setScriptName(scriptname);
101 | jd.name_in_path = scriptname;
102 | jd.dump();
103 | }
104 |
105 | /**
106 | * compat 11
107 | * @param view
108 | * @param savedInstanceState
109 | */
110 | @Override
111 | public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
112 | super.onViewCreated(view, savedInstanceState);
113 | writeState();
114 | // update the date view
115 | // and schedule icon
116 | TextView dateView = view.findViewById(R.id.job_date_input);
117 | if ( jd.isDateSet ) {
118 | dateView.setText(com.releasestandard.scriptmanager.model.TimeManager.sched2str(jd.sched));
119 | }
120 | long delay = 500; // 1 seconds after user stops typing
121 | long last_text_edit = 0;
122 | Handler handler = new Handler();
123 | Runnable input_finish_checker = new Runnable() {
124 | public void run() {
125 | if (System.currentTimeMillis() > (last_text_edit + delay - delay/2)) {
126 | int [] s = getDateFromView();
127 | if ( s != null ) {
128 | jd.sched = s;
129 | setDate();
130 | writeState();
131 | }
132 | }
133 | }
134 | };
135 |
136 | dateView.addTextChangedListener(new TextWatcher() {
137 | private Editable s;
138 |
139 | @Override
140 | public void beforeTextChanged (CharSequence s,int start, int count,
141 | int after){
142 | }
143 | @Override
144 | public void onTextChanged ( final CharSequence s, int start, int before,
145 | int count){
146 | //You need to remove this to run only once
147 | handler.removeCallbacks(input_finish_checker);
148 |
149 | }
150 | @Override
151 | public void afterTextChanged ( final Editable s){
152 | //avoid triggering event when text is empty
153 | if (s.length() > 0) {
154 | handler.postDelayed(input_finish_checker, delay);
155 | } else {
156 |
157 | }
158 | }
159 | });
160 |
161 | Logger.debug("");
162 | }
163 |
164 | @Override
165 | public void onSaveInstanceState(@NonNull Bundle outState) {
166 | super.onSaveInstanceState(outState);
167 | Logger.debug("");
168 | }
169 |
170 | @Override
171 | public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
172 | super.onViewStateRestored(savedInstanceState);
173 | restoreView();
174 | Logger.debug("");
175 | }
176 |
177 | @Override
178 | public void onCreate(Bundle savedInstanceState) {
179 | super.onCreate(savedInstanceState);
180 |
181 | Logger.debug("");
182 | }
183 |
184 | /**
185 | * compat 11
186 | * @param inflater
187 | * @param container
188 | * @param savedInstanceState
189 | * @return
190 | */
191 | @Override
192 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
193 | Bundle savedInstanceState) {
194 | Logger.debug("");
195 | // Inflate the layout for this fragment
196 | View v = inflater.inflate(R.layout.job_view, container, false);
197 | TextView tv = v.findViewById(R.id.job_title);
198 | tv.setText(jd.name);
199 | shell.dump();
200 | jd.dump();
201 | TextView tv2 = v.findViewById(R.id.job_filename);
202 | tv2.setText(jd.name_in_path);
203 |
204 | v.setOnLongClickListener(new View.OnLongClickListener() {
205 | @Override
206 | public boolean onLongClick(View arg0) {
207 | selectView(arg0);
208 | return true;
209 | }});
210 | v.setOnClickListener(new View.OnClickListener(){
211 | @Override
212 | public void onClick(View v) {
213 | MainActivity main = (MainActivity)getActivity();
214 | if ( main.jobs_view.getNumberSelected() > 0 ) {
215 | if (isSelected) {
216 | unselectView(v);
217 | } else {
218 | selectView(v);
219 | }
220 | }
221 | }
222 | });
223 |
224 | View playpause_button = v.findViewById(R.id.job_trigger_button);
225 | playpause_button.setOnClickListener(new View.OnClickListener() {
226 | @Override
227 | public void onClick(View view) {
228 | EditText et = v.findViewById(R.id.job_date_input);
229 | int [] s = getDateFromView();
230 | if ( !isDateSet() || (s != null && isDateSet())) {
231 | if ( isDateSet() ) {
232 | jd.sched = s;
233 | }
234 | // update image
235 | if (!isStarted()) {
236 | startJob();
237 | } else {
238 | stopJob();
239 | }
240 | }
241 | else {
242 | showErrorView(WRONG_DATE_FORMAT);
243 | }
244 | }
245 | });
246 | if ( jd.isSchedulded ) {
247 | setViewWaitStartJob(playpause_button);
248 | }
249 | // set up event s for the date & time picker
250 | View vv = v.findViewById(R.id.job_date_picker_button);
251 | vv.setOnClickListener(new View.OnClickListener() {
252 | @Override
253 | public void onClick(View v) {
254 | showScheduleJob(v);
255 | }
256 | });
257 |
258 | return v;
259 | }
260 |
261 | // Model //
262 | public boolean isStarted() {
263 | return jd.isStarted;
264 | }
265 | // //
266 |
267 | // Controller //
268 | /*
269 | * Check if the guy has modified the time when to launch the script.
270 | */
271 | public void showScheduleJob(View v) {
272 |
273 | TimeManagerView tm = new TimeManagerView(){
274 | @Override
275 | public void onPicked(int minute,int hourOfDay,int dayOfMonth,int monthOfYear,int year) {
276 | jd.sched = TimeManager.packIn(minute,hourOfDay,dayOfMonth,monthOfYear,year);
277 | setDate();
278 | writeState();
279 | }
280 | };
281 | tm.show(v,jd.sched);
282 | }
283 | public boolean setDate() {
284 | jd.isDateSet = true;
285 | return jd.isDateSet;
286 | }
287 | public boolean isDateSet() {
288 | EditText et = getView().findViewById(R.id.job_date_input);
289 | String s = et.getText().toString();
290 | this.jd.isDateSet = ! s.equals(new String("")) && s != null ;
291 | return this.jd.isDateSet;
292 | }
293 | public void setName(String name) {
294 | this.jd.name = name;
295 | TextView tv = getView().findViewById(R.id.job_title);
296 | tv.setText(name);
297 | }
298 | public void stopJob() {
299 | readState(getContext(),jd.name_in_path);
300 | Logger.debug("kill " + jd.intents.size() + " intents");
301 | for (Integer i : jd.intents) {
302 | shell.terminateIntent(i);
303 | }
304 | Logger.debug("kill " + jd.processes.size() + " processes");
305 | for (Integer i : jd.processes) {
306 | shell.terminateProcess(i);
307 | }
308 | for (Integer i : jd.listeners) {
309 | JavaEventsReceiver.listeners.set(i, null);
310 | }
311 | jd.listeners.clear();jd.processes.clear();jd.intents.clear();
312 | Shell._execCmd("rm -rf " + shell.sm.getEventsAbsolutePath(jd.name_in_path));
313 |
314 | jd.isSchedulded = false;
315 | jd.isStarted = false;
316 | setViewStopJob();
317 | MainActivity main = (MainActivity)getActivity();
318 | int i = main.jobs_view.getNumberStarted();
319 | if ( i == 0 ) {
320 | main.ow_menu.leaveRunningMode();
321 | }
322 | writeState();
323 | }
324 | public void startJob() {
325 | Logger.debug("");
326 | MainActivity main = (MainActivity)getActivity();
327 | int i = main.jobs_view.getNumberStarted();
328 | if ( i == 0) {
329 | main.ow_menu.enterRunningMode();
330 | }
331 |
332 | jd.isStarted = true;
333 |
334 | int [] s = new int[]{};
335 | boolean immediate = false;
336 | if ( isDateSet() ) {
337 | s = getDateFromView();
338 | if ( s!=null) {
339 | jd.isSchedulded = true;
340 | setViewWaitStartJob();
341 | }
342 | }
343 | else {
344 | immediate = true;
345 | setViewStartJob();
346 | }
347 |
348 | if ( s != null ) {
349 | Integer intent_index = shell.scheduleScript(main, jd.name_in_path, s, immediate);
350 | if (intent_index != -1) {
351 | jd.intents.add(intent_index);
352 | }
353 | }
354 |
355 | writeState();
356 |
357 | if( isSelected ) {
358 | main.ow_menu.callbackSelectAndRunning(main);
359 | }
360 | }
361 | public int[] getDateFromView() {
362 | EditText et = getView().findViewById(R.id.job_date_input);
363 | String date_input = et.getText().toString();
364 | if (com.releasestandard.scriptmanager.model.TimeManager.validDate(date_input)) {
365 | return com.releasestandard.scriptmanager.model.TimeManager.str2sched(date_input);
366 | }
367 | return null;
368 | }
369 | // //
370 |
371 | // View //
372 | public void removeViewJob() {
373 | Logger.debug("");
374 | FragmentTransaction ft = getFragmentManager().beginTransaction();
375 | ft.remove(this);
376 | ft.commit();
377 |
378 | // remove the script file
379 | ArrayList files = new ArrayList();
380 | files.add(shell.sm.getScriptAbsolutePath()); //script file remove
381 | files.add(shell.sm.getLogAbsolutePath()); // remove log file
382 | getContext().deleteFile(shell.sm.getStateFileNameInPath()); // remove statefile in the private storage
383 | // remove state file
384 | for ( String s : files ) {
385 | Logger.log(s);
386 | File f = new File(s);
387 | if (f.exists()) {
388 | f.delete();
389 | }
390 | }
391 | }
392 | public void setViewStartJob() {
393 | FloatingActionButton fab = this.getView().findViewById(R.id.job_trigger_button);
394 | fab.setImageDrawable(
395 | getResources().getDrawable(android.R.drawable.ic_media_pause)
396 | );
397 | }
398 | public void setViewWaitStartJob(View v) {
399 | FloatingActionButton fab = (FloatingActionButton)v;
400 | fab.setImageDrawable(
401 | getResources().getDrawable(android.R.drawable.ic_menu_recent_history)
402 | );
403 | }
404 | public void setViewWaitStartJob() {
405 | setViewWaitStartJob(this.getView().findViewById(R.id.job_trigger_button));
406 | }
407 | public void setViewStopJob() {
408 | FloatingActionButton fab = this.getView().findViewById(R.id.job_trigger_button);
409 | fab.setImageDrawable(
410 | getResources().getDrawable(android.R.drawable.ic_media_play)
411 | );
412 | }
413 | public void unselectView() {
414 | this.isSelected = false;
415 | if ( this.view != null) {
416 | MainActivity main = (MainActivity) getActivity();
417 | int color =main.getColorFromId(main, R.attr.colorPrimary);
418 | view.setBackgroundColor(color);
419 | if( main.jobs_view.getNumberSelected() <= 0) {
420 | main.ow_menu.leaveSelectMode();
421 | }
422 | if( main.jobs_view.getNumberSelected() == 1) {
423 | main.ow_menu.enterOneOnlySelectMode();
424 | }
425 | }
426 | }
427 | public void unselectView(View v) {
428 | if ( v != null ) {
429 | this.view = v;
430 | }
431 | this.unselectView();
432 | }
433 | public void selectView() {
434 | if ( this.view != null ) {
435 | this.selectView(this.view);
436 | }
437 | }
438 | public void selectView(View v) {
439 | this.isSelected = true;
440 | // The selection action
441 | // Hightlight the ActionBar
442 | MainActivity main = (MainActivity) getActivity();
443 | ActionBar ab = main.getSupportActionBar();
444 | int color =main.getColorFromId(main, android.R.attr.colorLongPressedHighlight);
445 | ab.setBackgroundDrawable(new ColorDrawable(color));
446 | v.setBackgroundColor(color);
447 | if( main.jobs_view.getNumberSelected() == 1) {
448 | main.ow_menu.enterSelectMode();
449 | }
450 | if( main.jobs_view.getNumberSelected() > 1) {
451 | main.ow_menu.leaveOneOnlySelectMode();
452 | }
453 | if ( v != null ) {
454 | this.view = v;
455 | }
456 | main.ow_menu.callbackSelectAndRunning(main);
457 | }
458 | public void restoreView() {
459 | if (jd.isSchedulded) {
460 | setViewWaitStartJob();
461 | }
462 | else {
463 | if (isStarted()) {
464 | setViewStartJob();
465 | } else {
466 | setViewStopJob();
467 | }
468 | }
469 | }
470 | public void showErrorView(String text) {
471 | Context context = getContext();
472 | int duration = Toast.LENGTH_SHORT;
473 |
474 | Toast toast = Toast.makeText(context, text, duration);
475 | toast.show();
476 |
477 | }
478 | // //
479 |
480 |
481 | /*******************************************************************************
482 | * Model = filesystem *
483 | *******************************************************************************/
484 | /**
485 | * Write the state of user interface
486 | */
487 | public void writeState() {
488 | jd.dump();
489 | OutputStreamWriter osw = StorageManager.getOSW(getContext(),shell.sm.getStateFileNameInPath());
490 | jd.writeState(osw);
491 | try {
492 | osw.flush();
493 | osw.close();
494 | } catch (IOException e) {
495 | e.printStackTrace(Logger.getTraceStream());
496 | }
497 | }
498 |
499 | /**
500 | * Read the state at start
501 | * => pas d'update dans l'ui, c'est le probleme
502 | */
503 | public void readState(Context context, String path_name) {
504 | Logger.debug("");
505 | InputStreamReader isr = StorageManager.getISR(context,shell.sm.getStateFileNameInPath());
506 | jd.readState(isr);
507 | try {
508 | isr.close();
509 | } catch (IOException e) {
510 | e.printStackTrace(Logger.getTraceStream());
511 | }
512 | String script_name_path = path_name;
513 | this.shell.sm.setScriptName(script_name_path);
514 | }
515 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/releasestandard/scriptmanager/JobsView.java:
--------------------------------------------------------------------------------
1 | package com.releasestandard.scriptmanager;
2 |
3 | import android.os.Bundle;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 |
8 | import androidx.annotation.NonNull;
9 | import androidx.annotation.Nullable;
10 | import androidx.fragment.app.Fragment;
11 | import androidx.fragment.app.FragmentManager;
12 | import androidx.fragment.app.FragmentTransaction;
13 |
14 | import com.releasestandard.scriptmanager.model.StorageManager;
15 | import com.releasestandard.scriptmanager.tools.Logger;
16 |
17 | import java.util.ArrayList;
18 | import java.util.List;
19 |
20 | /**
21 | * Used to display many jobs on screen.
22 | * compat 11
23 | */
24 | public class JobsView extends Fragment {
25 |
26 | // list of Jobs
27 | public List fragments = new ArrayList();
28 | public List fragments_ss = new ArrayList();
29 |
30 | public StorageManager ptr_sm = null;
31 |
32 | public JobsView() {
33 | // Required empty public constructor
34 | }
35 | public JobsView(StorageManager ptr_sm) {
36 | this.ptr_sm = ptr_sm;
37 | }
38 |
39 | public static SettingsView newInstance(String param1, String param2) {
40 | SettingsView fragment = new SettingsView();
41 | Bundle args = new Bundle();
42 | return fragment;
43 | }
44 |
45 | @Override
46 | public void onCreate(Bundle savedInstanceState) {
47 | if ( savedInstanceState == null ) {
48 | Logger.debug( "savedInstanceState is null");
49 | }
50 | else{
51 | Logger.debug("savedInstanceState is not null");
52 | }
53 | super.onCreate(savedInstanceState);
54 | }
55 |
56 | /**
57 | * compat 1
58 | * @return
59 | */
60 | public ArrayListgetSelecteds() {
61 | ArrayList list = new ArrayList<>();
62 | for(JobView js : fragments) {
63 | if (js.isSelected) {
64 | list.add(js);
65 | }
66 | }
67 | return list;
68 | }
69 |
70 | /**
71 | * compat 1
72 | * @return
73 | */
74 | public JobView getSelected() {
75 | for(JobView js : fragments) {
76 | if(js.isSelected) {
77 | return js;
78 | }
79 | }
80 | return null;
81 | }
82 |
83 | /**
84 | * @param inflater
85 | * @param container
86 | * @param savedInstanceState
87 | * @return
88 | */
89 | @Override
90 | public View onCreateView(
91 | LayoutInflater inflater, ViewGroup container,
92 | Bundle savedInstanceState
93 | ) {
94 | Integer i = new Integer(fragments.size());
95 | Logger.debug("size in fragments : "+i);
96 | if ( savedInstanceState == null ) {
97 | Logger.debug("saveInstanceNull");
98 | }
99 | View v = inflater.inflate(R.layout.jobs_view, container, false);
100 | return v;
101 | }
102 |
103 | /**
104 | * @param view
105 | * @param savedInstanceState
106 | */
107 | @Override
108 | public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
109 | super.onViewCreated(view, savedInstanceState);
110 | Logger.debug("");
111 | }
112 |
113 | /**
114 | * compat 11
115 | * @param outState
116 | */
117 | @Override
118 | public void onSaveInstanceState(@NonNull Bundle outState) {
119 | super.onSaveInstanceState(outState);
120 | Logger.debug("");
121 | }
122 |
123 | @Override
124 | /**
125 | * compat 11
126 | */
127 | public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
128 | super.onViewStateRestored(savedInstanceState);
129 | Integer in = new Integer(getParentFragmentManager().getFragments().size());
130 | Logger.debug("nb of frags "+in);
131 | restoreFragments();
132 | }
133 |
134 | /**
135 | * compat 11
136 | */
137 | public void restoreFragments() {
138 | // we need to restore the view for all childs
139 | FragmentManager fm = getChildFragmentManager();
140 | FragmentTransaction ft = fm.beginTransaction();
141 | for(JobView jf : fragments) {
142 | ft.add(R.id.view_jobs_linearlayout, jf);
143 | }
144 | ft.commit();
145 | readState();
146 | }
147 |
148 | // Tools
149 | public void stopAllFragments() {
150 | stopAllFragments(false);
151 | }
152 |
153 | /**
154 | * compat 1
155 | * @param onlySelected
156 | */
157 | public void stopAllFragments(boolean onlySelected) {
158 | for (JobView jf : fragments) {
159 | if( onlySelected) {
160 | if(jf.isSelected) {
161 | jf.stopJob();
162 | }
163 | }
164 | else
165 | {
166 | jf.stopJob();
167 | }
168 | }
169 | }
170 |
171 | /**
172 | * compat 1
173 | */
174 | public void unselectAllFragments() {
175 | for (JobView jf : fragments) {
176 | jf.unselectView();
177 | }
178 | if ( getNumberSelected() == 0) {
179 | MainActivity main = (MainActivity) getActivity();
180 | main.ow_menu.leaveSelectMode();
181 | }
182 | }
183 |
184 | /**
185 | * compat 1
186 | * @return
187 | */
188 | public int getNumberSelected() {
189 | int count = 0;
190 | for (JobView jf : fragments) {
191 | if ( jf.isSelected ) {
192 | count += 1;
193 | }
194 | }
195 | return count;
196 | }
197 |
198 | /**
199 | * compat 1
200 | * @return
201 | */
202 | public int getNumberStartedAndSelected() {
203 | int count = 0;
204 | for (JobView jf : fragments) {
205 | if ( jf.isStarted() && jf.isSelected) {
206 | count += 1;
207 | }
208 | }
209 | return count;
210 | }
211 |
212 | /**
213 | * compat 1
214 | * @return
215 | */
216 | public int getNumberStarted() {
217 | int count = 0;
218 | for (JobView jf : fragments) {
219 | if ( jf.isStarted()) {
220 | count += 1;
221 | }
222 | }
223 | return count;
224 | }
225 |
226 |
227 | /**
228 | * compat 11
229 | * @return
230 | */
231 | public JobView addNewJob(){ return addNewJob(null); }
232 | public JobView addNewJob(String scriptname) {
233 | FragmentManager fm = getChildFragmentManager();
234 | FragmentTransaction ft = fm.beginTransaction();
235 |
236 | JobView f = null;
237 | if ( scriptname == null ) {
238 | f = new JobView(ptr_sm);
239 | }
240 | else {
241 | // don't create the files please
242 | f = new JobView(ptr_sm, scriptname);
243 | }
244 | ft.add(R.id.view_jobs_linearlayout, f);
245 | ft.commit();
246 |
247 | fragments.add(f);
248 | return f;
249 | }
250 |
251 | /**
252 | * compat 1
253 | */
254 | public void writeState() {
255 | for ( JobView jf : fragments) {
256 | jf.writeState();
257 | }
258 | }
259 |
260 | /**
261 | * compat 1
262 | */
263 | public void readState() {
264 | for(String scriptname : this.ptr_sm.getScriptsFromFilesystem(getContext())) {
265 | JobView alrdyIn = null;
266 | for(JobView jv : fragments) {
267 | if ( jv.jd.name_in_path.equals(scriptname ) ) {
268 | alrdyIn = jv;
269 | break;
270 | }
271 | }
272 | if ( alrdyIn != null ) {
273 | alrdyIn.readState(getActivity(),scriptname);
274 | alrdyIn.dump();
275 | continue;
276 | }
277 | JobView jf = addNewJob(scriptname);
278 | jf.readState(getActivity(),scriptname);
279 | jf.dump();
280 | }
281 | if ( fragments.size() > 0) {
282 | JobView.fragmentCount = fragments.get(fragments.size() - 1).jd.id + 1;
283 | }
284 | }
285 |
286 | }
287 |
--------------------------------------------------------------------------------
/app/src/main/java/com/releasestandard/scriptmanager/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.releasestandard.scriptmanager;
2 |
3 | import androidx.appcompat.app.ActionBar;
4 | import androidx.core.app.ActivityCompat;
5 | import androidx.core.content.FileProvider;
6 | import androidx.fragment.app.Fragment;
7 | import androidx.fragment.app.FragmentManager;
8 | //import android.app.FragmentManager;
9 | import androidx.fragment.app.FragmentTransaction;
10 | //import android.app.FragmentTransaction;
11 | import android.Manifest;
12 | import android.app.AlertDialog;
13 | import android.content.Context;
14 | import android.content.DialogInterface;
15 | import android.content.Intent;
16 | import android.net.Uri;
17 | import android.os.Bundle;
18 |
19 | import com.google.android.material.floatingactionbutton.FloatingActionButton;
20 | import com.releasestandard.scriptmanager.controller.OverflowMenu;
21 | import com.releasestandard.scriptmanager.model.KSHEvent;
22 | import com.releasestandard.scriptmanager.model.StorageManager;
23 | import com.releasestandard.scriptmanager.tools.CompatAPI;
24 | import com.releasestandard.scriptmanager.tools.Logger;
25 |
26 | import androidx.appcompat.app.AppCompatActivity;
27 | import androidx.appcompat.widget.Toolbar;
28 |
29 | import android.util.TypedValue;
30 | import android.view.View;
31 |
32 | import android.view.Menu;
33 | import android.view.MenuItem;
34 | import android.widget.EditText;
35 |
36 | import java.io.File;
37 | import java.io.FileNotFoundException;
38 | import java.io.FileOutputStream;
39 | import java.io.IOException;
40 | import java.io.InputStream;
41 | import java.util.Hashtable;
42 |
43 | public class MainActivity extends AppCompatActivity {
44 |
45 | private Toolbar toolbar = null;
46 |
47 | private static boolean FIRST_CREATION = true;
48 | public static int ACTIVITY_REQUEST_CODE_IMPORT = 1;
49 | public boolean isInSelectMode = false;
50 |
51 | // Objects that encapsulate the data //
52 | public JobsView jobs_view = null;
53 | public SettingsView sf = null;
54 | public OverflowMenu ow_menu = null;
55 | private Hashtable views = null;
56 |
57 | StorageManager sm = null;
58 |
59 | @Override
60 | protected void onCreate(Bundle savedInstanceState) {
61 |
62 | super.onCreate(savedInstanceState);
63 |
64 | setContentView(R.layout.main_activity);
65 |
66 | toolbar = findViewById(R.id.toolbar);
67 | setSupportActionBar(toolbar);
68 |
69 | // Get Shell locations path
70 | this.sm = new StorageManager(getApplicationContext());
71 |
72 | FloatingActionButton fab = findViewById(R.id.main_activity_fab);
73 | fab.setOnClickListener(new View.OnClickListener() {
74 | @Override
75 | public void onClick(View view) {
76 | handlerFabClick();
77 | }
78 | });
79 |
80 | this.views = new Hashtable<>();
81 | jobs_view = new JobsView(this.sm);
82 |
83 | sf = new SettingsView();
84 | views.put(R.id.nav_host_fragment, jobs_view);
85 | views.put(R.id.action_settings, sf);
86 |
87 | requestPermissions();
88 |
89 | FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
90 | ft.replace(R.id.nav_host_fragment, jobs_view);
91 | ft.commit();
92 |
93 | Logger.debug("");
94 | }
95 | private void requestPermissions() {
96 | ActivityCompat.requestPermissions(MainActivity.this,
97 | new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,
98 | Manifest.permission.RECEIVE_BOOT_COMPLETED,
99 | Manifest.permission.RECEIVE_SMS}, 1);
100 | }
101 | @Override
102 | protected void onStart() {
103 | super.onStart();
104 | if ( FIRST_CREATION ) {
105 | jobs_view.readState(); // get the state from the storage
106 | FIRST_CREATION = false;
107 | }
108 | Logger.debug("");
109 | }
110 |
111 |
112 | @Override
113 | public boolean onCreateOptionsMenu(Menu menu) {
114 | SettingsView.applySettings(this);
115 | getMenuInflater().inflate(R.menu.menu_main, menu);
116 | ow_menu = new OverflowMenu(this, menu);
117 | return true;
118 | }
119 |
120 | @Override
121 | public boolean onOptionsItemSelected(MenuItem item) {
122 | super.onOptionsItemSelected(item);
123 |
124 | MainActivity main = this;
125 | int id = item.getItemId();
126 |
127 | if (id == R.id.action_stopselected) {
128 | jobs_view.stopAllFragments(true);
129 | jobs_view.unselectAllFragments();
130 | }
131 | if (id == R.id.action_stopall) {
132 | jobs_view.stopAllFragments();
133 | jobs_view.unselectAllFragments();
134 | }
135 | if (id == R.id.action_unselectall) {
136 | jobs_view.unselectAllFragments();
137 | }
138 | if (id == R.id.action_settings) {
139 | jobs_view.unselectAllFragments();
140 | ow_menu.leaveSelectMode();
141 |
142 | ActionBar ab = super.getSupportActionBar();
143 | ab.setTitle(R.string.settings_page_title);
144 | ab.setDisplayHomeAsUpEnabled(true);
145 | ab.setDisplayUseLogoEnabled(false);
146 |
147 | findViewById(R.id.main_activity_fab).setVisibility(View.INVISIBLE);
148 |
149 | FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
150 | ft.replace(R.id.nav_host_fragment, sf);
151 | ft.commit();
152 |
153 | ow_menu.setMenuVisibility(false);
154 | return true;
155 | }
156 |
157 | if (id == android.R.id.home) {
158 | if (isInSelectMode) {
159 | mainLeaveSelectMode();
160 | } else {
161 | settings2main();
162 | }
163 | }
164 |
165 | if (id == R.id.action_oneonly_edit) {
166 | JobView jf = jobs_view.getSelected();
167 | showFileWithEditor(jf.shell.sm.getScriptAbsolutePath());
168 | jobs_view.unselectAllFragments();
169 | }
170 |
171 | if ( R.id.action_anyselection_delete == id ) {
172 | for( JobView jf : jobs_view.getSelecteds() ) {
173 | jf.removeViewJob();
174 | jobs_view.fragments.remove(jf);
175 | }
176 | jobs_view.unselectAllFragments();
177 | }
178 | if (R.id.action_oneonly_clear_log == id) {
179 | JobView jf = jobs_view.getSelected();
180 | try {
181 | jf.shell.clearLog(jf.shell.sm.getLogAbsolutePath());
182 | } catch (IOException e) {
183 | e.printStackTrace(Logger.getTraceStream());
184 | }
185 | jobs_view.unselectAllFragments();
186 | }
187 | if ( R.id.action_oneonly_show_log == id) {
188 | JobView jf = jobs_view.getSelected();
189 | showFileWithEditor(jf.shell.sm.getLogAbsolutePath());
190 | jobs_view.unselectAllFragments();
191 | }
192 | if (R.id.action_oneonly_rename == id) {
193 | JobView jf = jobs_view.getSelected();
194 | AlertDialog.Builder builder = new AlertDialog.Builder(this,R.style.AppTheme_RenameDialog);
195 | View customLayout = getLayoutInflater().inflate(R.layout.rename_dialog, null);
196 | builder.setView(customLayout);
197 | builder.setPositiveButton(R.string.action_oneonly_rename_ok, new DialogInterface.OnClickListener() {
198 | public void onClick(DialogInterface dialog, int id) {
199 | EditText et = (EditText) customLayout.findViewById(R.id.rename_dialog_input);
200 | jf.setName(et.getText().toString());
201 | jf.writeState();
202 | }
203 | });
204 | builder.setNegativeButton(R.string.action_oneonly_rename_cancel, new DialogInterface.OnClickListener() {
205 | public void onClick(DialogInterface dialog, int id) {
206 | dialog.cancel();
207 | }
208 | });
209 | AlertDialog alert = builder.create();
210 | alert.setTitle(R.string.action_oneonly_rename);
211 | alert.show();
212 | jobs_view.unselectAllFragments();
213 | }
214 |
215 | // not ready yet due to limitations to access the storage //
216 | if (R.id.action_browse_scripts == id) {
217 | JobView jf = jobs_view.getSelected();
218 | Uri selectedUri = Uri.parse(sm.externalStorage );
219 | CompatAPI.openDocument(this, selectedUri.toString());
220 | jobs_view.unselectAllFragments();
221 | }
222 | //
223 | // features : import multiple scripts (putExtra EXTRA_ALLOW_MULTIPLE true API 18), also ACTION_OPEN_DOCUMENT_TREE for a complete directory (API 21+)
224 | //
225 | if (R.id.action_import_script == id) {
226 | CompatAPI.openDocument(this);
227 | jobs_view.unselectAllFragments();
228 | }
229 | if ( R.id.action_flush_events == id ) {
230 | KSHEvent.recvKsh2java();
231 | }
232 | if ( R.id.action_test_button == id) {
233 | //showFileWithEditor("/data/data/com.releasestandard.scriptmanager/files/script_0.xml");
234 | jobs_view.writeState();
235 | }
236 | if ( R.id.action_test_button2 == id) {
237 | jobs_view.readState();
238 | }
239 |
240 | // Start service at boot / enregistrement sur disk
241 | return true;
242 | }
243 |
244 | /**
245 | * compat 1
246 | * @param path
247 | */
248 | public void showFileWithEditor(String path) {
249 | Context context = this;
250 | String pvd = context.getPackageName() + ".provider";
251 | File f = new File(path);
252 | if (!f.exists()) {
253 | try {
254 | f.createNewFile();
255 | } catch (IOException e) {
256 | return;
257 | }
258 | }
259 | Uri uri = FileProvider.getUriForFile(context, pvd, f);
260 | Intent myIntent = new Intent(Intent.ACTION_VIEW);
261 | myIntent.setData(uri);
262 | myIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
263 | context.startActivity(myIntent);
264 | }
265 |
266 | /*
267 | * Handle result of the import action.
268 | */
269 | @Override
270 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
271 | super.onActivityResult(requestCode,resultCode,data);
272 | if ( data == null) {
273 | // user has cancelled the request
274 | return;
275 | }
276 | if ( requestCode == ACTIVITY_REQUEST_CODE_IMPORT) {
277 | Uri uri = data.getData();
278 | InputStream is = null;
279 | try {
280 | is = getContentResolver().openInputStream(uri);
281 | } catch (FileNotFoundException e) {
282 | e.printStackTrace(Logger.getTraceStream());
283 | }
284 |
285 | handlerFabClick();
286 | // create a new fragment
287 | JobView jf = jobs_view.fragments.get(jobs_view.fragments.size() - 1);
288 | File f2 = new File(jf.shell.sm.getScriptAbsolutePath());
289 | if (!f2.exists()) {
290 | try {
291 | f2.createNewFile();
292 | } catch (IOException e) {
293 | e.printStackTrace(Logger.getTraceStream());
294 | }
295 | }
296 | FileOutputStream fos = null;
297 | try {
298 | fos = new FileOutputStream(f2);
299 | } catch (FileNotFoundException e) {
300 | e.printStackTrace(Logger.getTraceStream());
301 | }
302 | try {
303 | int c;
304 | while ((c = is.read()) != -1) {
305 | fos.write(c);
306 | }
307 | is.close();
308 | fos.close();
309 | } catch (IOException e) {
310 | e.printStackTrace(Logger.getTraceStream());
311 | }
312 | }
313 | }
314 | /*
315 | * get color associated with the current theme.
316 | */
317 | public static int getColorFromId(MainActivity main, int colorID) {
318 | TypedValue typedValue = new TypedValue();
319 | main.getTheme().resolveAttribute(colorID, typedValue, true);
320 | return typedValue.data;
321 | }
322 |
323 | // Handlers for click on interface
324 | public void handlerFabClick() {
325 | jobs_view.addNewJob();
326 | }
327 |
328 | public void settings2main() {
329 | ActionBar ab = super.getSupportActionBar();
330 | ab.setTitle(R.string.app_name);
331 | ab.setDisplayHomeAsUpEnabled(false);
332 | ab.setDisplayUseLogoEnabled(true);
333 |
334 | findViewById(R.id.main_activity_fab).setVisibility(View.VISIBLE);
335 | FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
336 | ft.replace(R.id.nav_host_fragment, jobs_view);
337 | ft.commit();
338 |
339 | ow_menu.setMenuVisibility(true);
340 | }
341 | public void mainLeaveSelectMode() {
342 | ActionBar ab = super.getSupportActionBar();
343 | ab.setTitle(R.string.app_name);
344 | ab.setDisplayHomeAsUpEnabled(false);
345 | ab.setDisplayUseLogoEnabled(true);
346 | jobs_view.unselectAllFragments();
347 | ow_menu.leaveSelectMode();
348 | }
349 | @Override
350 | public void onBackPressed() {
351 | FragmentManager fm = getSupportFragmentManager();
352 | for ( Fragment f : fm.getFragments()) {
353 | if ( f == sf) {
354 | settings2main();
355 | return;
356 | }
357 | }
358 | if ( isInSelectMode ) {
359 | mainLeaveSelectMode();
360 | }
361 | else {
362 | AlertDialog ad = new AlertDialog.Builder(this, R.style.AppTheme_AlertDialog)
363 | .setTitle(R.string.dialog_back_title)
364 | .setMessage(R.string.dialog_back_content)
365 |
366 | .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
367 | public void onClick(DialogInterface dialog, int which) {
368 | finish();
369 | }
370 | })
371 | .setNegativeButton(android.R.string.no, null)
372 | .setIcon(android.R.drawable.ic_dialog_alert)
373 | .show();
374 | }
375 | }
376 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/releasestandard/scriptmanager/SettingsView.java:
--------------------------------------------------------------------------------
1 | package com.releasestandard.scriptmanager;
2 |
3 | import android.app.AlarmManager;
4 | import android.content.Context;
5 | import android.content.SharedPreferences;
6 | import android.content.res.Resources;
7 | import android.os.Bundle;
8 |
9 | import androidx.preference.Preference;
10 | import androidx.preference.PreferenceFragmentCompat;
11 | import androidx.preference.PreferenceGroup;
12 | import androidx.preference.PreferenceManager;
13 |
14 | import com.releasestandard.scriptmanager.tools.CompatAPI;
15 | import com.releasestandard.scriptmanager.tools.Logger;
16 |
17 | public class SettingsView extends PreferenceFragmentCompat {
18 |
19 | private SharedPreferences.OnSharedPreferenceChangeListener listener = null;
20 |
21 | @Override
22 | public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
23 | setPreferencesFromResource(R.xml.settings_fragment, rootKey);
24 | SharedPreferences sp =
25 | PreferenceManager.getDefaultSharedPreferences(getActivity());
26 |
27 | listener = new SharedPreferences.OnSharedPreferenceChangeListener() {
28 | public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
29 | applySettings(prefs);
30 | }
31 | };
32 |
33 | // load current values into view
34 | sp.registerOnSharedPreferenceChangeListener(listener);
35 | CompatAPI.modifySettings(this);
36 | hideUnusedGroups();
37 | }
38 |
39 | private int getPreferenceCountShown(PreferenceGroup pg) {
40 | int c = 0;
41 | for( int i = 0; i < pg.getPreferenceCount(); i = i +1) {
42 | c += pg.getPreference(i).isShown()?1:0;
43 | }
44 | return c;
45 | }
46 |
47 | /**
48 | * Works only if at least one setting is defined in xml file
49 | * (eg: used to hide when all settings of given group are not usable)
50 | */
51 | private void hideUnusedGroups() {
52 | SharedPreferences sp =
53 | PreferenceManager.getDefaultSharedPreferences(getActivity());
54 | for(String k : sp.getAll().keySet()) {
55 | Preference pref = findPreference(k);
56 | if ( pref == null ) {
57 | continue;
58 | }
59 | PreferenceGroup pg = pref.getParent();
60 | if ( pg != null ) {
61 | if (getPreferenceCountShown(pg) == 0) {
62 | pg.setVisible(false);
63 | }
64 | }
65 | }
66 | }
67 |
68 | public void applySettings() { applySettings(getActivity());}
69 | public void applySettings(SharedPreferences sp) {
70 | applySettings(getActivity(),sp);
71 | }
72 | public static void applySettings(Context ctx) {
73 | SharedPreferences sp =
74 | PreferenceManager.getDefaultSharedPreferences(ctx);
75 | applySettings(ctx,sp);
76 | }
77 | public static void applySettings(Context ctx, SharedPreferences sp) {
78 | Resources r = ctx.getResources();
79 | Boolean preferences_developper_debug_mode = sp.getBoolean(r .getString(R.string.preferences_developper_debug_mode), r .getBoolean(R.bool.preferences_developper_debug_mode));
80 | Logger.DEBUG = preferences_developper_debug_mode;
81 | Logger.debug(r .getString(R.string.preferences_developper_debug_mode) + "="+preferences_developper_debug_mode);
82 |
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/app/src/main/java/com/releasestandard/scriptmanager/controller/JobData.java:
--------------------------------------------------------------------------------
1 | package com.releasestandard.scriptmanager.controller;
2 |
3 | import com.releasestandard.scriptmanager.model.StorageManager;
4 | import com.releasestandard.scriptmanager.model.TimeManager;
5 | import com.releasestandard.scriptmanager.tools.Logger;
6 |
7 | import java.io.FileNotFoundException;
8 | import java.io.IOException;
9 | import java.io.InputStreamReader;
10 | import java.io.OutputStreamWriter;
11 | import java.util.ArrayList;
12 | import java.util.List;
13 |
14 | /**
15 | * Classe used to parse offline data, extract information and do the thing.
16 | */
17 | public class JobData {
18 |
19 | public final static Integer EACH_TIME = -1;
20 |
21 | // id of the script
22 | public Integer id = 0;
23 | // name of the script
24 | public String name = "";
25 | // boolean for the (if it is schedulded)
26 | public Boolean isSchedulded = false;
27 | // boolean for the (if it is started)
28 | public Boolean isStarted = false;
29 | // is the date graphically set
30 | public boolean isDateSet = false;
31 | // set The date
32 | public int sched[] = {
33 | EACH_TIME, // minutes
34 | EACH_TIME, // hours
35 | EACH_TIME, // day of month
36 | EACH_TIME, // month
37 | EACH_TIME }; // year
38 | // index in array (intents for schedulded and processes for started)
39 | public List processes = new ArrayList();
40 | public List intents = new ArrayList<>();
41 | public List listeners = new ArrayList<>();
42 |
43 | //
44 | // Not stored
45 | public String name_in_path = "";
46 |
47 |
48 |
49 | /**
50 | * Beware this method is used at boot time to set alarms, Object like Matcher, File
51 | * could cause crashes.
52 | * compat 1
53 | */
54 | public void readState(InputStreamReader isr) { readState(isr,false); }
55 | public void readState(InputStreamReader isr, boolean ignore_intents_processes) {
56 | if ( isr == null ) { return; }
57 | try {
58 | // id of the script
59 | int id = isr.read();
60 | this.id = id;
61 | // name of the script
62 | int script_name_size = isr.read();
63 | char [] script_name = new char[script_name_size];
64 | isr.read(script_name);
65 | name = new String(script_name);
66 | // boolean for the (if it is schedulded)
67 | isSchedulded = (isr.read() == 0)?false:true;
68 | // boolean for the (if it is started)
69 | isStarted = (isr.read() == 0)?false:true;
70 | // boolean for the (if it is date set or not)
71 | isDateSet = (isr.read() == 0)?false:true;
72 | // get The date
73 | sched = StorageManager.readIntArray(isr);
74 | if ( ! ignore_intents_processes ) {
75 | Logger.debug("read processes");
76 | processes = StorageManager.readIntegerArray( isr);
77 | dump();
78 | Logger.debug("read intents");
79 | intents = StorageManager.readIntegerArray( isr);
80 | listeners = StorageManager.readIntegerArray( isr );
81 | }
82 | Logger.debug("after read from internal storage");
83 | dump();
84 | } catch (FileNotFoundException e) {
85 | e.printStackTrace(Logger.getTraceStream());
86 | } catch (IOException e) {
87 | e.printStackTrace(Logger.getTraceStream());
88 | }
89 | }
90 |
91 | /**
92 | * Write the state of user interface
93 | * WARNING state_file is just the terminal part of the path
94 | * compat 1
95 | */
96 | public void writeState(OutputStreamWriter osw) {
97 | if ( osw == null ) { return; }
98 | try {
99 | // id of the script
100 | osw.write(id.intValue());
101 | // size of the string
102 | osw.write(name.length());
103 | // name of the script
104 | osw.write(name);
105 | // boolean for the (if it is schedulded)
106 | osw.write((isSchedulded?1:0));
107 | // boolean for the (if it is started)
108 | osw.write((isStarted?1:0));
109 | // boolean for the (if it is date set or not)
110 | osw.write((isDateSet?1:0));
111 | StorageManager.writeIntArray(osw,sched,5);
112 | // index of (intent, process) in array (intents, processes)
113 | Logger.debug("write processes");
114 | StorageManager.writeIntegerArray(osw,processes);
115 | Logger.debug("write intents");
116 | StorageManager.writeIntegerArray(osw,intents);
117 | StorageManager.writeIntegerArray(osw,listeners);
118 | osw.flush();
119 | } catch (FileNotFoundException e) {
120 | e.printStackTrace(Logger.getTraceStream());
121 | } catch (IOException e) {
122 | e.printStackTrace(Logger.getTraceStream());
123 | }
124 | }
125 |
126 | /**
127 | * compat 1
128 | */
129 | public void dump() {
130 | Logger.debug(dump(""));
131 | }
132 | public String dump(String init) {
133 | String sched_as_ints = "";
134 | for ( int i : sched) {
135 | sched_as_ints = sched_as_ints + (new Integer(i)) + " ";
136 | }
137 | return
138 | Logger.SZERO + init + "JobData {\n" +
139 | Logger.SZERO + init + " id=" + id + "\n" +
140 | Logger.SZERO + init + " name=" + name + "\n" +
141 | Logger.SZERO + init + " isSchedulded=" + isSchedulded + "\n" +
142 | Logger.SZERO + init + " isStarted=" + isStarted + "\n" +
143 | Logger.SZERO + init + " isDateSet=" + isDateSet + "\n" +
144 | Logger.SZERO + init + " sched=" + TimeManager.sched2str(sched) + "\n" +
145 | Logger.SZERO + init + " (" + sched_as_ints + ")\n" +
146 | Logger.SZERO + init + " processes_sz=" + processes.size()+ "\n" +
147 | Logger.SZERO + init + " intent_sz=" + intents.size() + "\n" +
148 | Logger.SZERO + init + " name_in_path=" + name_in_path + "\n" +
149 | Logger.SZERO + init + "}\n"
150 | ;
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/app/src/main/java/com/releasestandard/scriptmanager/controller/OverflowMenu.java:
--------------------------------------------------------------------------------
1 | package com.releasestandard.scriptmanager.controller;
2 |
3 | import android.graphics.drawable.ColorDrawable;
4 | import android.view.Menu;
5 | import android.view.MenuItem;
6 |
7 | import androidx.appcompat.app.ActionBar;
8 | import androidx.appcompat.widget.Toolbar;
9 |
10 | import com.releasestandard.scriptmanager.MainActivity;
11 | import com.releasestandard.scriptmanager.R;
12 |
13 | import java.util.ArrayList;
14 |
15 | /**
16 | * Implement logic on the settings overflow menu.
17 | */
18 | public class OverflowMenu {
19 |
20 | private Menu optionsMenu = null;
21 |
22 | public static int MODE_STANDARD = 0;
23 | public static int MODE_NO_EXT = 1;
24 | private static int MODE = MODE_STANDARD;
25 |
26 | private ArrayList optionsMenuItemBackup = new ArrayList();
27 |
28 | // Collection off ids for the menu
29 |
30 | public static int any_selection_buttons_standard[] = {R.id.action_stopselected,
31 | R.id.action_unselectall,R.id.action_anyselection_delete};
32 | public static int any_selection_buttons[] = any_selection_buttons_standard;
33 | public static int any_selection_buttons_compat_no_external[] = any_selection_buttons;
34 |
35 | public static int one_only_selection_buttons_standard[] = {R.id.action_oneonly_edit,
36 | R.id.action_oneonly_rename, R.id.action_oneonly_show_log ,
37 | R.id.action_oneonly_clear_log
38 | };
39 | public static int one_only_selection_buttons[] = one_only_selection_buttons_standard;
40 | public static int one_only_selection_buttons_no_external[] = {
41 | R.id.action_oneonly_rename
42 | };
43 |
44 | int debug_mode[] = {/*R.id.settings_fragment_debug_mode*/};
45 |
46 | int running_mode[] = {R.id.action_stopall};
47 |
48 | private MainActivity main = null;
49 |
50 | public static void gotoMode() { gotoMode(MODE_STANDARD);}
51 | public static void gotoMode(int mod) {
52 | if ( MODE == mod ) { return; }
53 | if ( mod == MODE_NO_EXT) {
54 | any_selection_buttons = any_selection_buttons_compat_no_external;
55 | one_only_selection_buttons = one_only_selection_buttons_no_external;
56 | }
57 | if ( mod == MODE_STANDARD) {
58 | any_selection_buttons = any_selection_buttons_standard;
59 | one_only_selection_buttons = one_only_selection_buttons_standard;
60 | }
61 | MODE = mod;
62 | }
63 | public OverflowMenu(MainActivity main, Menu menu) {
64 | this.main = main;
65 | this.optionsMenu = menu;
66 | }
67 |
68 | // helpers
69 | /**
70 | * Hide or show the overflow menu.
71 | */
72 | public void setMenuVisibility(boolean b) {
73 | Toolbar toolbar = (Toolbar) main.findViewById(R.id.toolbar);
74 | Menu m = toolbar.getMenu();
75 |
76 | if( b ) {
77 | for (MenuItem mi : optionsMenuItemBackup) {
78 | mi.setVisible(true);
79 | }
80 | optionsMenuItemBackup.clear();
81 | }
82 | else {
83 | for (int a = 0; a < m.size(); a = a + 1) {
84 | MenuItem mi = m.getItem(a);
85 | if( mi.isVisible() ) {
86 | optionsMenuItemBackup.add(mi);
87 | mi.setVisible(false);
88 | }
89 | }
90 | }
91 | }
92 | /*
93 | * At least one job is running
94 | */
95 | public void enterRunningMode() {
96 | for (int id : running_mode) {
97 | MenuItem mi = optionsMenu.findItem(id);
98 | mi.setVisible(true);
99 | }
100 | }
101 | public void leaveRunningMode() {
102 | for (int id : running_mode) {
103 | MenuItem mi = optionsMenu.findItem(id);
104 | mi.setVisible(false);
105 | }
106 | }
107 | public void enterDebugMode() {
108 | for (int id : debug_mode) {
109 | MenuItem mi = optionsMenu.findItem(id);
110 | mi.setVisible(true);
111 | }
112 | }
113 | public void leaveDebugMode() {
114 | for (int id : debug_mode) {
115 | MenuItem mi = optionsMenu.findItem(id);
116 | mi.setVisible(false);
117 | }
118 | }
119 | public void enterOneOnlySelectMode() {
120 | for (int id : one_only_selection_buttons) {
121 | MenuItem mi = optionsMenu.findItem(id);
122 | mi.setVisible(true);
123 | }
124 | }
125 | public void leaveOneOnlySelectMode() {
126 | for (int id : one_only_selection_buttons) {
127 | MenuItem mi = optionsMenu.findItem(id);
128 | mi.setVisible(false);
129 | }
130 | }
131 |
132 | /**
133 | * compat 11
134 | */
135 | public void enterSelectMode() {
136 | ActionBar ab = main.getSupportActionBar();
137 | ab.setDisplayHomeAsUpEnabled(true);
138 |
139 | for (int id : any_selection_buttons) {
140 | MenuItem mi = optionsMenu.findItem(id);
141 | mi.setVisible(true);
142 | }
143 | main.isInSelectMode = true;
144 | enterOneOnlySelectMode();
145 | }
146 |
147 | /**
148 | * compat 11
149 | */
150 | public void leaveSelectMode() {
151 | ActionBar ab = main.getSupportActionBar();
152 | int color = main.getColorFromId(main, R.attr.colorPrimaryVariant);
153 | ab.setBackgroundDrawable(new ColorDrawable(color));
154 | ab.setDisplayHomeAsUpEnabled(false);
155 |
156 | for (int id : any_selection_buttons) {
157 | MenuItem mi = optionsMenu.findItem(id);
158 | mi.setVisible(false);
159 | }
160 | main.isInSelectMode = false;
161 | leaveOneOnlySelectMode();
162 | }
163 |
164 | public void callbackSelectAndRunning(MainActivity main) {
165 | if ( main.jobs_view.getNumberStartedAndSelected() > 0) {
166 | optionsMenu.findItem(R.id.action_stopselected)
167 | .setVisible(true);
168 | } else {
169 | optionsMenu.findItem(R.id.action_stopselected)
170 | .setVisible(false);
171 | }
172 | }
173 | }
174 |
--------------------------------------------------------------------------------
/app/src/main/java/com/releasestandard/scriptmanager/controller/TimeManagerView.java:
--------------------------------------------------------------------------------
1 | package com.releasestandard.scriptmanager.controller;
2 |
3 | import android.app.DatePickerDialog;
4 | import android.app.TimePickerDialog;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.DatePicker;
8 | import android.widget.TextView;
9 | import android.widget.TimePicker;
10 |
11 | import com.releasestandard.scriptmanager.R;
12 |
13 | public class TimeManagerView {
14 |
15 | public TimeManagerView() {
16 |
17 | }
18 |
19 | /*
20 | * handler called at the end of time picking.
21 | */
22 | public void onPicked(int minute,int hourOfDay,int dayOfMonth,int monthOfYear,int year) {
23 |
24 | }
25 |
26 | /**
27 | * compat 1
28 | * @param v
29 | * @param sched
30 | */
31 | public void show(View v, int []sched) {
32 | show(v,sched[0],sched[1],sched[2],sched[3],sched[4]);
33 | }
34 | public void show(View v, int minute, int hourOfDay, int dayOfMonth, int monthOfYear, int year) {
35 | ViewGroup vpg = (ViewGroup)v.getParent();
36 | TextView tv = vpg.findViewById(R.id.job_date_input);
37 |
38 | // Launch Time Picker Dialog
39 | TimePickerDialog timePickerDialog = new TimePickerDialog(v.getContext(),R.style.AppTheme_TimePicker,
40 | new TimePickerDialog.OnTimeSetListener() {
41 | @Override
42 | public void onTimeSet(TimePicker view, int hourOfDay,
43 | int minute) {
44 | DatePickerDialog datePickerDialog = new DatePickerDialog(v.getContext(),R.style.AppTheme_DatePicker,
45 | new DatePickerDialog.OnDateSetListener() {
46 | @Override
47 | public void onDateSet(DatePicker view, int year,
48 | int monthOfYear, int dayOfMonth) {
49 | int temp_sched[] = {minute,hourOfDay,dayOfMonth,monthOfYear,year};
50 | tv.setText(com.releasestandard.scriptmanager.model.TimeManager.sched2str(temp_sched));
51 | onPicked(minute,hourOfDay,dayOfMonth,monthOfYear,year);
52 | }
53 | }, year, monthOfYear, dayOfMonth);
54 | datePickerDialog.show();
55 | }
56 | }, hourOfDay, minute, false);
57 | timePickerDialog.show();
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/app/src/main/java/com/releasestandard/scriptmanager/model/KSHEvent.java:
--------------------------------------------------------------------------------
1 | package com.releasestandard.scriptmanager.model;
2 |
3 | import android.util.Log;
4 |
5 | import com.releasestandard.scriptmanager.tools.Logger;
6 |
7 | import java.util.Random;
8 |
9 | /**
10 | * How do we handle events from and to java.
11 | * hint: fs
12 | */
13 | public class KSHEvent {
14 |
15 | private static Integer id = (new Random().nextInt());
16 | private Integer _id; // id of the event
17 | public String type; // type of event eg SmsReceived
18 | public String[] args; // values of arg0 arg1 eg +33600000000
19 | public String base; // base rep for output
20 |
21 | public KSHEvent(String base) {
22 | this.base = base + "/";
23 | this._id=this.id;
24 | this.id+=1;
25 | }
26 | public KSHEvent(String base, String type) {
27 | this.base = base + "/";
28 | this.type = type;
29 | this._id=this.id;
30 | this.id+=1;
31 | }
32 | public KSHEvent(String base, String type, String []args) {
33 | this.type = type;
34 | this.base = base + "/";
35 | this.args = new String[args.length];
36 | this._id=this.id;
37 | this.id += 1;
38 | for (int i = 0; i < args.length ; i = i + 1 ) { this.args[i] = args[i]; }
39 | }
40 |
41 | /**
42 | * Send the event from java to ksh
43 | * @return
44 | */
45 | public String sendJava2ksh() { return sendJava2ksh(args); }
46 | public String sendJava2ksh(String[] args) {
47 | Logger.debug("sendJava2ksh::args");
48 | for ( String arg : args ) { Logger.debug("sendJava2ksh::args -> " + arg); }
49 | String res = "echo \""+ type +"\" > " + getFunctNameFile() + " ";
50 | Integer i = 0;
51 | for ( String arg : args ) {
52 | String argfile = getArgFile(i);
53 | res += " && echo \"" + arg + "\" > " + argfile + " ";
54 | i += 1;
55 | }
56 | res += "";
57 | return res;
58 | }
59 |
60 | /**
61 | * Recv events (ALL events that are in the events directory) from java to ksh
62 | * @return
63 | */
64 | public String recvJava2ksh() { return recvJava2ksh("");}
65 | public String recvJava2ksh(String off) {
66 | String res = "" +
67 | off + "for event in $(ls " + this.base + ") ; do\n" +
68 | off + " eventp=" + this.base + "/$event/;\n" +
69 | off + " functname=$(cat \"" + getFunctNameFile(this.base + "/$event/") + "\" );\n" +
70 | off + " ARGS=($(ls $eventp/arg*));\n" +
71 | off + " i=0;\n" +
72 | off + " while [ \"$i\" -lt \"${#ARGS[@]}\" ] ; do \n" +
73 | off + " ARGS[$i]=\"$(cat ${ARGS[$i]})\";\n" +
74 | off + " let i=i+1;\n" +
75 | off + " done\n" +
76 | off + " type $functname &> /dev/null && " +
77 | off + " $functname \"${ARGS[@]}\" \n" +
78 | off + " clearEvent $event\n" +
79 | off + "done\n" +
80 | off + "" ;
81 |
82 | Logger.debug("recvJava2ksh::res="+res);
83 | return res;
84 | }
85 |
86 | /**
87 | * Send event from ksh to java
88 | * @return
89 | */
90 | public String sendKsh2java() {
91 | Logger.debug("sendKsh2java not impemented");
92 | return "";
93 | }
94 |
95 | /**
96 | * Receive event from ksh to java
97 | * @return
98 | */
99 | public static boolean recvKsh2java() {
100 | Logger.debug("recvKsh2java not impemented");
101 | return false;
102 | }
103 | private String getFunctNameFile() { return getFunctNameFile(null); }
104 | private String getFunctNameFile(String path) {
105 | if ( path == null ) {
106 | path = base + "/" + _id;
107 | }
108 | return _getFunctNameFile(path);
109 | }
110 | private static String _getFunctNameFile(String path) {
111 | String name = "/functnamefile";
112 | return path + name;
113 | }
114 |
115 | private String getArgFile(Integer i) {
116 | return base + "/" + _id + "/arg" + i;
117 | }
118 | public String[] getArgFiles() {
119 | String [] args = new String[this.args.length];
120 | for(int i = 0; i < this.args.length; i = i +1) {
121 | args[i] = getArgFile(i);
122 | }
123 | return args;
124 | }
125 |
126 | /**
127 | * clear the event from ksh.
128 | */
129 | public String clearEvent() { return clearEvent(""); }
130 | public String clearEvent(String off) {
131 | return off + "function clearEvent() {\n" +
132 | off + " id=$1;\n" +
133 | off + " path=" + this.base + "/$id/;\n" +
134 | off + " if [ -e \"$path\" ] ; then \n" +
135 | off + " rm -rf \"$path\";\n" +
136 | off + " fi\n" +
137 | off + "}\n";
138 | }
139 |
140 | /**
141 | * clear the event from java.
142 | */
143 | public void clear() {
144 | Shell._execCmd("rm -rf " + this.base + "/" + this._id + "/");
145 | }
146 |
147 | /**
148 | * Check prerequisites (folder creation, ...) from ksh
149 | * @return
150 | */
151 | public String checkPrereq() { return checkPrereq(""); }
152 | public String checkPrereq(String off) {
153 | return off + "function checkPrereq() {\n" +
154 | off + " id=$1\n" +
155 | off + " mkdir -p \""+this.base+"/$id\"\n" +
156 | off + "}\n";
157 | }
158 | /**
159 | * check prerequisite from java.
160 | */
161 | public String prereq() {
162 | Logger.debug("prereq : " + this._id);
163 | String cmd= "mkdir -p \""+this.base+"/" + this._id + "\"";
164 | return cmd;
165 | }
166 |
167 | /**
168 | * Package function in a lib.
169 | * @return
170 | */
171 | public String packLib() { return packLib(""); }
172 | public String packLib(String off) {
173 | String res = "" +
174 | clearEvent(off) + "\n" +
175 | checkPrereq(off) + "\n" +
176 | sendKsh2java() + "\n";
177 |
178 | return res;
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/app/src/main/java/com/releasestandard/scriptmanager/model/KornShellInterface.java:
--------------------------------------------------------------------------------
1 | package com.releasestandard.scriptmanager.model;
2 |
3 | import android.util.Log;
4 |
5 | import com.releasestandard.scriptmanager.R;
6 | import com.releasestandard.scriptmanager.tools.Logger;
7 |
8 | import java.io.File;
9 | import java.lang.reflect.InvocationTargetException;
10 | import java.lang.reflect.Method;
11 | import java.util.HashMap;
12 | import java.util.Random;
13 | import java.util.concurrent.TimeUnit;
14 |
15 | /**
16 | * By convention, we use prefix _scriptmanager_ for our internal bash variables.
17 | */
18 | public class KornShellInterface {
19 |
20 | // these files are used only for this classe so we don't use the StorageManager //
21 | private static String SUFFIX_PID = ".pid";
22 | private String pidFile = null;
23 | private String evts_path;
24 | private String signal = "USR1";
25 | private String events_path;
26 |
27 | // public API definition
28 | public HashMap API = new HashMap() {{
29 |
30 | put(R.string.ioctlSmsReceived,"smsReceived"); // $1 : from $2 : body
31 |
32 | }};
33 |
34 |
35 | /**
36 | * compat 1
37 | */
38 | public void dump() { Logger.debug(dump("")); }
39 | public String dump(String off) {
40 | String noff = off + "\t";
41 | return Logger.SZERO + off + "KornShellInterface {\n"+
42 | Logger.SZERO + noff + "signal="+signal+"\n" +
43 | Logger.SZERO + off + "}\n"
44 | ;
45 | }
46 |
47 | public KornShellInterface(StorageManager sm) {
48 | }
49 |
50 | /**
51 | * This is a wrapper allow a script to handle events from android.
52 | * compat 23
53 | * (printf)
54 | * @return
55 | */
56 | public String wrappScript(String in,String out) {
57 | Logger.debug("Transform "+in+" > "+out);
58 | pidFile = out + SUFFIX_PID;
59 | evts_path = StorageManager.getEventsAbsolutePath(StorageManager.getTerminalPart(in));
60 | KSHEvent kse = new KSHEvent(evts_path);
61 | kse.prereq();
62 |
63 | String header = "" +
64 | " _scriptmanager_pidf=\"" + pidFile + "\" ; \n" +
65 | " _scriptmanager_SIG=\""+signal+"\" ; \n" +
66 | "\n" +
67 | kse.packLib() + "\n" +
68 | "events_interface () { \n" +
69 | kse.recvJava2ksh(" ") + "\n" +
70 | "}\n" +
71 | "trap \"events_interface\" $_scriptmanager_SIG; \n" +
72 | "\n" +
73 | "echo \"$$\" > " + pidFile + " ; \n" +
74 | "# \n" +
75 | "# user part\n" +
76 | "# \n" +
77 | "\n" +
78 | "\n";
79 |
80 | String footer = "" +
81 | "\n" +
82 | "\n" +
83 | "#\n" +
84 | "#\n" +
85 | "#\n" +
86 | "while true ; do\n" +
87 | "\tsleep 2 &\n" +
88 | "\twait $!\n" +
89 | "done\n";
90 |
91 |
92 | String cmd = "" +
93 | "{ printf '" + header + "' > " + out + ";" +
94 | "cat '" + in + "' >> " + out + ";" +
95 | "printf '" + footer + "' >> " + out + "; }";
96 |
97 |
98 | try {
99 | Shell._execCmd(cmd).waitFor();
100 | } catch (InterruptedException e) {
101 | Logger.debug("Wrapping has failed");
102 | e.printStackTrace(Logger.getTraceStream());
103 | }
104 |
105 | return out;
106 | }
107 |
108 | /**
109 | * Trigger a script callback method
110 | * compat 14
111 | * @param methodToCall
112 | * @param args
113 | */
114 | public void triggerCallback(String methodToCall,String... args) {
115 | String cmd ="";
116 | Method method = null;
117 | try {
118 | method = KornShellInterface.class.getDeclaredMethod(methodToCall, String.class, String.class);
119 | cmd += method.invoke(this, args[0], args[1]);
120 | cmd += " && ";
121 | } catch (NoSuchMethodException e) {
122 | e.printStackTrace(Logger.getTraceStream());
123 | } catch (IllegalAccessException e) {
124 | e.printStackTrace(Logger.getTraceStream());
125 | } catch (InvocationTargetException e) {
126 | e.printStackTrace(Logger.getTraceStream());
127 | }
128 | cmd += "kill -s " + signal + " $(cat " + pidFile + ");";
129 |
130 | Logger.debug(cmd);
131 | Shell._execCmd(cmd);
132 |
133 | }
134 | /**
135 | * React to events
136 | * compat 14
137 | * @return
138 | */
139 | public String triggerRecvMsg(String from, String body) {
140 |
141 | Logger.debug("from="+from+",body="+body);
142 | KSHEvent kshe = new KSHEvent(evts_path,
143 | API.get(R.string.ioctlSmsReceived),
144 | new String[]{from,body});
145 |
146 | return kshe.prereq() + " && " + kshe.sendJava2ksh();
147 | }
148 |
149 | /**
150 | * Offer a way to attach process to root (by cmd wrapping) (don't be killed by application when user close it)
151 | * @return
152 | */
153 | public static String attachToRoot(String cmd) {
154 | // Working example to attach to PPID 1 but get killed anyway :/
155 | // return "" + cmd + " &";
156 | return cmd;
157 | }
158 |
159 | /**
160 | * compat 14
161 | * @param cmd
162 | * @param log
163 | * @return
164 | */
165 | public static String outputToLog(String cmd, String log) {
166 | return "&>> " + log + " " + cmd;
167 | }
168 |
169 | /**
170 | * compat 1
171 | * @param cmd
172 | * @return
173 | */
174 | public static String[] packIn(String cmd) {
175 | return new String[]{"sh", "-c", cmd};
176 | }
177 |
178 | }
179 |
--------------------------------------------------------------------------------
/app/src/main/java/com/releasestandard/scriptmanager/model/Shell.java:
--------------------------------------------------------------------------------
1 | package com.releasestandard.scriptmanager.model;
2 |
3 | import android.app.AlarmManager;
4 | import android.app.PendingIntent;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.os.SystemClock;
8 |
9 | import com.releasestandard.scriptmanager.AlarmReceiver;
10 | import com.releasestandard.scriptmanager.JavaEventsReceiver;
11 | import com.releasestandard.scriptmanager.tools.CompatAPI;
12 | import com.releasestandard.scriptmanager.tools.Logger;
13 |
14 | import java.io.IOException;
15 | import java.util.ArrayList;
16 | import java.util.Calendar;
17 | import java.util.List;
18 |
19 | /*
20 | * This class use {KornShellInterface,StorageManager} to provide interface with Java.
21 | * all JobView got a Shell associated with it.
22 | */
23 | public class Shell {
24 |
25 | // The object is used by recursive alarms, so many processes can be setup
26 | private static List processes = new ArrayList();
27 | private static List intents = new ArrayList();
28 |
29 | public StorageManager sm = null;
30 | public KornShellInterface bi = null;
31 |
32 | public boolean eventReceiverRegistered = false;
33 |
34 | /**
35 | * This is the shell for a given JobView (one line on screen).
36 | */
37 | public Shell(StorageManager sm) {
38 | this.sm = new StorageManager(sm);
39 | this.bi = new KornShellInterface(sm);
40 | }
41 |
42 | /**
43 | * parameter could be the name of the script or an absolute path.
44 | * @param scriptname scriptname (last part of pathname)
45 | * @return index in the array of processes
46 | */
47 | public Integer execScript(String scriptname) {
48 | sm.setScriptName(scriptname);
49 | Logger.debug("<======== State before execScript =======>");
50 | sm.dump();
51 | Logger.debug("<========= =========>");
52 | String output = sm.getOutputAbsolutePath();
53 | bi.wrappScript( sm.getScriptAbsolutePath(),output);
54 | if ( !eventReceiverRegistered ) {
55 | Logger.debug("eventReceiverRegistered");
56 | JavaEventsReceiver.listeners.add(this);
57 | eventReceiverRegistered = true;
58 | }
59 |
60 | Logger.log("Job execution : " + output + "\n log=" + sm.getLogAbsolutePath());
61 | Process p = _execScript(output,sm.getLogAbsolutePath());
62 | if ( p != null ) {
63 | Logger.debug("Shell: Process has started");
64 | if ( ! processes.add(p) ) {
65 | return -1;
66 | }
67 | return processes.size()-1;
68 | }
69 | Logger.debug("Shell: Process failed to start");
70 | return -1;
71 | }
72 |
73 | /**
74 | * Execute script & cmd and return a Process
75 | * compat 1
76 | * @param script
77 | * @return
78 | */
79 | public static Process _execScript(String script) {
80 | return _execScript(script,null);
81 | }
82 | public static Process _execScript(String script, String log) { return _execCmd(". "+script,log,true); }
83 | public static Process _execCmd(String cmd) {
84 | return _execCmd(cmd,null);
85 | }
86 | public static Process _execCmd(String cmd,String log) {
87 | return _execCmd(cmd,log,false);
88 | }
89 | public static Process _execCmd(String cmd, String log,boolean attachToRoot) {
90 | try {
91 | String real_cmd = cmd;
92 | if ( log != null) {
93 | if ( attachToRoot ) {
94 | real_cmd=KornShellInterface.attachToRoot(
95 | KornShellInterface.outputToLog(cmd, log));
96 | } else {
97 | real_cmd=KornShellInterface.outputToLog(cmd, log);
98 | }
99 | } else {
100 | if ( attachToRoot ) {
101 | real_cmd=KornShellInterface.attachToRoot(cmd);
102 | }
103 | }
104 | Logger.debug("real_cmd="+real_cmd);
105 | return Runtime.getRuntime().exec(KornShellInterface.packIn(real_cmd));
106 | } catch (IOException e) {
107 | return null;
108 | }
109 | }
110 |
111 | /**
112 | * Clear logs
113 | * compat 14
114 | * @throws IOException
115 | */
116 | public void clearLog() throws IOException { clearLog(this.sm.script_name); }
117 | public void clearLog(String logpath) throws IOException {
118 | Shell._execCmd("> "+logpath);
119 | }
120 |
121 | /**
122 | * compat 1
123 | * @param context
124 | * @param scriptname
125 | * @param sched
126 | * @return
127 | */
128 | public Integer scheduleScript(Context context, String scriptname, int sched[]) { return scheduleScript(context, scriptname, sched, false);}
129 | public Integer scheduleScript(Context context, String scriptname, int sched[], boolean immediate) {
130 | if ( ! intents.add(_scheduleScript(context,scriptname, sched,immediate)) ) {
131 | return -1;
132 | }
133 | return intents.size()-1;
134 | }
135 | /**
136 | * Schedule a script for execution.
137 | * compat 1
138 | */
139 | public static PendingIntent _scheduleScript(Context context, String scriptname, int sched[]) { return _scheduleScript(context,scriptname,sched,false);}
140 | public static PendingIntent _scheduleScript(Context context, String scriptname, int sched[], boolean immediate) {
141 | // need to get the time here
142 | Calendar next = null;
143 | if (immediate) {
144 | Logger.debug("We got an immediate time here");
145 | next = TimeManager.getImmediate();
146 | }
147 | else {
148 | next = TimeManager.nextSched(sched);
149 | }
150 |
151 | Logger.log("[" + (new Integer(AlarmReceiver.REQUEST_CODE + 1)) + "] Script " + scriptname + " scheduled for " + next.getTime().toString());
152 | long t = next.getTimeInMillis();
153 |
154 | Intent intent = new Intent(context, AlarmReceiver.class);
155 | intent.putExtra("script",scriptname);
156 | intent.putExtra("sched",sched);
157 | PendingIntent alarmIntent = PendingIntent.getBroadcast(context, AlarmReceiver.REQUEST_CODE++, intent, PendingIntent.FLAG_UPDATE_CURRENT);
158 | CompatAPI.setAlarmIntentTime(context,t,alarmIntent);
159 | Logger.debug("official end");
160 | return alarmIntent;
161 | }
162 |
163 | /**
164 | * compat 1
165 | */
166 | public boolean terminateProcess(Integer i) {
167 | if ( i < 0 || i >= processes.size()) {
168 | Logger.debug("invalid index");
169 | return false;
170 | }
171 | processes.get(i).destroy();
172 | return true;
173 | }
174 |
175 | /**
176 | * compat 1
177 | * @param i
178 | * @return
179 | */
180 | public boolean terminateIntent(Integer i) {
181 | if ( i < 0 || i >= intents.size()) {
182 | Logger.debug("invalid index");
183 | return false;
184 | }
185 | intents.get(i).cancel();
186 | return true;
187 | }
188 |
189 | /**
190 | * compat 1
191 | */
192 | public void dump() { Logger.debug(dump("")); }
193 | public String dump(String offset) {
194 | return "" +
195 | Logger.SZERO + offset + "Shell { \n" +
196 | Logger.SZERO + offset + "\tprocesses=" + processes.size() + "\n" +
197 | Logger.SZERO + offset + "\tintents=" + intents.size() + "\n" +
198 | Logger.SZERO + bi.dump(offset + "\t" ) +
199 | Logger.SZERO + sm.dump(offset + "\t") +
200 | Logger.SZERO + offset + "}\n";
201 | }
202 | }
203 |
--------------------------------------------------------------------------------
/app/src/main/java/com/releasestandard/scriptmanager/model/StorageManager.java:
--------------------------------------------------------------------------------
1 | package com.releasestandard.scriptmanager.model;
2 |
3 | import android.content.Context;
4 | import android.util.Log;
5 |
6 | import com.releasestandard.scriptmanager.controller.OverflowMenu;
7 | import com.releasestandard.scriptmanager.tools.CompatAPI;
8 | import com.releasestandard.scriptmanager.tools.Logger;
9 | import com.releasestandard.scriptmanager.R;
10 |
11 | import java.io.BufferedReader;
12 | import java.io.File;
13 | import java.io.FileInputStream;
14 | import java.io.FileNotFoundException;
15 | import java.io.IOException;
16 | import java.io.InputStreamReader;
17 | import java.io.OutputStreamWriter;
18 | import java.util.ArrayList;
19 | import java.util.Arrays;
20 | import java.util.List;
21 | import java.util.regex.Matcher;
22 | import java.util.regex.Pattern;
23 |
24 | /**
25 | * Manage where the data is stored on phone (different places, suffixes, etc).
26 | */
27 | public class StorageManager {
28 |
29 | public static String externalStorage = "/sdcard/Android/data/" + R.string.app_packageid + "/files/";
30 | public static String internalStorage = "/data/data/" + R.string.app_packageid + "/files/";
31 |
32 | public static String SUFFIX_LOG = ".log.txt";
33 | public static String SUFFIX_SCRIPT = ".txt";
34 | public static String SUFFIX_STATE = ".xml";
35 | public static String SUFFIX_OUTPUT= ".out";
36 |
37 |
38 | public String script_name = "";
39 | /**
40 | * compat 1
41 | */
42 | public static void writeIntArray(OutputStreamWriter osw, int tab[], int sz) throws IOException {
43 | Logger.debug("sz="+(new Integer(sz)).toString());
44 | osw.write(sz);
45 | for(int i = 0; i < sz ; i += 1){
46 | osw.write(tab[i]);
47 | }
48 | }
49 |
50 | /**
51 | * compat 1
52 | * @param osw
53 | * @param tab
54 | * @throws IOException
55 | */
56 | public static void writeIntegerArray(OutputStreamWriter osw, List tab) throws IOException {
57 | int[] tabi = new int[tab.size()];
58 | for (int i = 0; i < tab.size(); i = i +1 ) {
59 | tabi[i]=tab.get(i);
60 | }
61 | writeIntArray(osw,tabi,tab.size());
62 | }
63 | /**
64 | * Read an int array from input stream.
65 | * compat 1
66 | * @param isr input stream
67 | * @return array readed
68 | * @throws IOException
69 | */
70 | public static List readIntegerArray(InputStreamReader isr) throws IOException {
71 | List tab = new ArrayList();
72 | for( int j : readIntArray(isr) ) { tab.add(j); }
73 | return tab;
74 | }
75 | public static int[] readIntArray(InputStreamReader isr) throws IOException {
76 | int j = isr.read();
77 | short jj = (short)j;
78 | return readIntArray(isr,new Integer(jj));
79 | }
80 | private static int[] readIntArray(InputStreamReader isr, Integer i) throws IOException {
81 | if ( i < 0 ) { return null; }
82 | int[] tab = new int[i];
83 | for(int ii = 0; ii < i ; ii += 1) {
84 | int j = isr.read();
85 | // since any of secondes, minutes, hours, day, month year will go to much high we stop here
86 | short jj = (short)j;
87 | tab[ii]=jj;
88 | }
89 | return tab;
90 | }
91 |
92 | /**
93 | * compat 1
94 | * @param sm
95 | */
96 | public StorageManager(StorageManager sm) {
97 | setInstance(sm);
98 | }
99 | public StorageManager(Context ctx, String script_name) { StorageManager sm = newInstance(ctx,script_name); setInstance(sm);}
100 | public StorageManager(Context ctx) { StorageManager sm = newInstance(ctx); setInstance(sm);}
101 | public static StorageManager newInstance(Context ctx) { return newInstance(ctx,null); }
102 | public static StorageManager newInstance(Context ctx,String script_name) {
103 | StorageManager sm = new StorageManager();
104 | if ( script_name != null ) {
105 | sm.script_name = script_name;
106 | }
107 | sm.internalStorage = ctx.getFilesDir().getAbsolutePath();
108 | sm.externalStorage = CompatAPI.getExternalStorage(ctx);
109 | if ( sm.externalStorage == null ) {
110 | sm.externalStorage = sm.internalStorage;
111 | OverflowMenu.gotoMode(OverflowMenu.MODE_NO_EXT);
112 | }
113 | return sm;
114 | }
115 | public void setInstance(StorageManager sm) {
116 | this.externalStorage = sm.externalStorage;
117 | this.internalStorage = sm.internalStorage;
118 | this.script_name = sm.script_name;
119 | }
120 | public StorageManager() {
121 |
122 | }
123 |
124 |
125 | public String getScriptName() {
126 | return this.script_name;
127 | }
128 | public void setScriptName(String script) {
129 | this.script_name = script;
130 | }
131 |
132 |
133 | /**
134 | * Input : script name or abs path
135 | * Output : log path
136 | * compat
137 | */
138 | private String getLogPath() { return getLogPath(this.script_name); }
139 | private String getLogPath(String script_name) { return addSuffixeIfNeeded(script_name , SUFFIX_LOG); }
140 | public String getLogAbsolutePath() { return getLogAbsolutePath(this.script_name); }
141 | public String getLogAbsolutePath(String script_name) { return getExternalAbsolutePath(getLogPath(script_name)); }
142 | private String getScriptPath() { return getScriptPath(this.script_name);}
143 | private String getScriptPath(String script_name) { return addSuffixeIfNeeded(script_name,SUFFIX_SCRIPT);}
144 | public String getScriptAbsolutePath() { return this.getScriptAbsolutePath(this.script_name); }
145 | public String getScriptAbsolutePath(String scriptname) { return getExternalAbsolutePath(getScriptPath(scriptname)); }
146 | private String getOutputPath() { return getOutputPath(this.script_name); }
147 | private String getOutputPath(String scriptname) { return addSuffixeIfNeeded(scriptname,SUFFIX_OUTPUT); }
148 | public String getOutputAbsolutePath() { return this.getOutputAbsolutePath(this.script_name); }
149 | public String getOutputAbsolutePath(String scriptname) { return getInternalAbsolutePath(getOutputPath(scriptname)); }
150 | public String getStateFileNameInPath() { return this.getStateFileNameInPath(this.script_name); }
151 | public String getStateFileNameInPath(String script_name) { return script_name + SUFFIX_STATE ; }
152 | public static String getEventsAbsolutePath(String script_name) {
153 | String name_wo_suf = removeSuffix(script_name);
154 | return internalStorage + "/" + name_wo_suf + "/events/";
155 | }
156 | public static String getEventsRelativePath(String script_name) {
157 | String name_wo_suf = removeSuffix(script_name);
158 | return name_wo_suf + "/events/";
159 | }
160 | public static String getEventRelativePath(String script_name, String eid) {
161 | return getEventsRelativePath(script_name) + "/"+eid+"/";
162 | }
163 | public static Boolean isArgFile(String path) {
164 | return getTerminalPart(path).lastIndexOf("arg") != -1;
165 | }
166 |
167 | /**
168 | * Remove any suffix from script_name.
169 | * @return
170 | */
171 | public String removeSuffix() { return removeSuffix(this.script_name); }
172 | public static String removeSuffix(String script_name) {
173 | String [] suffixes = new String[]{
174 | StorageManager.SUFFIX_LOG,
175 | StorageManager.SUFFIX_SCRIPT,
176 | StorageManager.SUFFIX_OUTPUT,
177 | StorageManager.SUFFIX_STATE
178 | };
179 | for(String suffixe : suffixes) {
180 | int i = script_name.lastIndexOf(suffixe);
181 | if (i < 0) {
182 | continue;
183 | }
184 | String name_wo_suf = script_name.substring(0, i);
185 | return name_wo_suf;
186 | }
187 | return script_name;
188 | }
189 |
190 | /**
191 | * Input : script name or abs path
192 | * Output : script abs path to external storage
193 | * compat 1
194 | */
195 | private String getExternalAbsolutePath() { return getExternalAbsolutePath(this.script_name); }
196 | public String getExternalAbsolutePath(String name) { return getResolvedPath(name,this.externalStorage); }
197 | private String getInternalAbsolutePath() { return getInternalAbsolutePath(this.script_name); }
198 | public String getInternalAbsolutePath(String name) { return getResolvedPath(name,this.internalStorage); }
199 | private String getResolvedPath(String name, String path) {
200 | Pattern p = Pattern.compile("^/");
201 | Matcher m = p.matcher(name);
202 | if (!m.find()) {
203 | name = path + "/" + name;
204 | }
205 | return name;
206 | }
207 | /**
208 | * Check if a filename has a suffix and add it if needed.
209 | * compat 1
210 | */
211 | private String addSuffixeIfNeeded(String string, String suf) {
212 | Pattern p = Pattern.compile(suf+"$");
213 | Matcher m = p.matcher(script_name);
214 | if (!m.find()) {
215 | return string + suf;
216 | }
217 | return string;
218 | }
219 | /**
220 | * Output : script names (usables by get*AbsolutePath)
221 | * compat 1
222 | */
223 | public ArrayList getScriptsFromFilesystem(Context c) {
224 | ArrayList l = new ArrayList<>();
225 | File directory = c.getFilesDir();
226 | File[] files = directory.listFiles();
227 | Arrays.sort(files);
228 | for (int i = 0; i < files.length; i++) {
229 | File file = files[i];
230 | String n = file.getName();
231 | Pattern p = Pattern.compile("([^/]+)" + SUFFIX_STATE + "$");
232 | Matcher m = p.matcher(n);
233 | if (m.matches()) {
234 | l.add(m.group(1));
235 | }
236 | }
237 | return l;
238 | }
239 |
240 | /**
241 | * Extract filename from file path.
242 | * compat 1
243 | * @param pathname
244 | * @return
245 | */
246 | public static String getTerminalPart(String pathname) {
247 | String [] tab = pathname.split("/");
248 | if ( tab.length <= 0) {
249 | return null;
250 | }
251 | return tab[tab.length-1];
252 | }
253 |
254 | // This function will be executed when no access to Regex object
255 |
256 | /**
257 | * Check if a file is a "state file" of a given job.
258 | * compat 1
259 | * @param fname
260 | * @return
261 | */
262 | public static boolean isStateFile(String fname) {
263 | int i = fname.lastIndexOf(SUFFIX_STATE);
264 | if ( i < 0 ) {
265 | return false;
266 | }
267 | return (SUFFIX_STATE.length() + i ) == fname.length();
268 | }
269 |
270 | /**
271 | * compat 1
272 | */
273 | public static OutputStreamWriter getOSW(Context ctx, String name) {
274 | try {
275 | return new OutputStreamWriter(ctx.openFileOutput(name,Context.MODE_PRIVATE));
276 | } catch (FileNotFoundException e) {
277 | return null;
278 | }
279 | }
280 |
281 | /**
282 | * compat 1
283 | */
284 | public static InputStreamReader getISR(Context ctx, String name) {
285 | try {
286 | FileInputStream fis = ctx.openFileInput(name);
287 | InputStreamReader isr = new InputStreamReader(fis);
288 | return isr;
289 | } catch (FileNotFoundException e) {
290 | return null;
291 | }
292 | }
293 | /**
294 | * better implementation
295 | * @param ctx
296 | * @param path
297 | * @return
298 | */
299 | public static InputStreamReader newGetISR(Context ctx, String path) {
300 | FileInputStream fis = null;
301 | try {
302 | fis = new FileInputStream(new File(path));
303 | } catch (FileNotFoundException e) {
304 | e.printStackTrace(Logger.getTraceStream());
305 | return null;
306 | }
307 | InputStreamReader isr = new InputStreamReader(fis);
308 | return isr;
309 | }
310 | public static String fileAsText(Context ctx, String path) {
311 | String res = "";
312 | InputStreamReader isr = newGetISR(ctx,path);
313 | BufferedReader buffr = new BufferedReader(isr);
314 | while (true) {
315 | String l = null;
316 | try {
317 | l = buffr.readLine();
318 | } catch (IOException e) {
319 | e.printStackTrace(Logger.getTraceStream());
320 | break;
321 | }
322 | if ( l == null ) {
323 | break;
324 | }
325 | res += l;
326 | }
327 | return res;
328 | }
329 |
330 | /**
331 | * compat 1
332 | */
333 | public void dump() { Logger.debug(dump("")); }
334 | public String dump(String off) {
335 | String noff = off + "\t";
336 | return "" +
337 | Logger.SZERO + off + "StorageManager {\n" +
338 | Logger.SZERO + noff + "externalStorage="+externalStorage+"\n" +
339 | Logger.SZERO + noff + "internalStorage="+internalStorage+"\n"+
340 | Logger.SZERO + noff + "SUFFIX_LOG=" + SUFFIX_LOG + "\n" +
341 | Logger.SZERO + noff + "SUFFIX_SCRIPT=" + SUFFIX_SCRIPT + "\n" +
342 | Logger.SZERO + noff + "SUFFIX_STATE=" + SUFFIX_STATE + "\n" +
343 | Logger.SZERO + noff + "SUFFIX_OUTPUT=" + SUFFIX_OUTPUT + "\n" +
344 | Logger.SZERO + noff + "script_name=" + script_name + "\n" +
345 | Logger.SZERO + off + "}\n"
346 | ;
347 | }
348 | }
349 |
--------------------------------------------------------------------------------
/app/src/main/java/com/releasestandard/scriptmanager/model/TimeManager.java:
--------------------------------------------------------------------------------
1 | package com.releasestandard.scriptmanager.model;
2 |
3 | import java.util.Calendar;
4 | import java.util.GregorianCalendar;
5 | import java.util.regex.Matcher;
6 | import java.util.regex.Pattern;
7 |
8 | /**
9 | * Handle time conversions between different formats.
10 | * compat 1
11 | */
12 | public class TimeManager {
13 |
14 | public final static Integer EACH_TIME = -1;
15 |
16 | public static int[] packIn(int minute, int hourOfDay, int dayOfMonth, int monthOfYear, int year) { int sched[]={minute,hourOfDay,dayOfMonth,monthOfYear,year} ; return sched ; }
17 | public static int[] str2sched(String s) {
18 | int sched[] = {EACH_TIME, EACH_TIME, EACH_TIME, EACH_TIME, EACH_TIME};
19 | int i = 0;
20 | String [] parts = s.split(" ");
21 | if ( parts.length == sched.length ) {
22 | for (String ss : parts) {
23 | if (ss.equals(new String("*"))) {
24 | sched[i] = EACH_TIME;
25 | } else {
26 | sched[i] = (int) Integer.parseInt(ss);
27 | }
28 | i = i + 1;
29 | }
30 | }
31 | return sched;
32 | }
33 |
34 | public static String sched2str(int [] s) {
35 | String res = "";
36 | for (int i : s) {
37 | if ( i < 0 ) {
38 | res += "* ";
39 | }
40 | else {
41 | Integer ii = new Integer(i);
42 | res += ii + " ";
43 | }
44 | }
45 | return res;
46 | }
47 |
48 | public static boolean isRepeated(int [] sched) {
49 | for ( int i = 0 ; i < 5 ; i = i +1 ) {
50 | if ( sched[i] == EACH_TIME ) {
51 | return true;
52 | }
53 | }
54 | return false;
55 | }
56 |
57 | public static Calendar getImmediate() {
58 | Calendar c = new GregorianCalendar();
59 | c.setTimeInMillis(System.currentTimeMillis());
60 | return c;
61 | }
62 | public static Calendar nextSched(int [] sched) {
63 | Calendar c = new GregorianCalendar();
64 | int parts[]={ Calendar.MINUTE,Calendar.HOUR,
65 | Calendar.DAY_OF_MONTH,Calendar.MONTH,
66 | Calendar.YEAR};
67 | c.set(Calendar.MILLISECOND,0);
68 | c.set(Calendar.SECOND,0);
69 |
70 | // if we count in
71 | boolean areWeInEachThing = false;
72 | for ( int i = 0; i < 5 ; i = i + 1 ) {
73 | if ( sched[i] == EACH_TIME ) {
74 | // we need to apply this only on the first each
75 | if ( areWeInEachThing == false ) {
76 | c.set(parts[i], c.get(parts[i]) + 1);
77 | areWeInEachThing = true;
78 | }
79 | else {
80 | c.set(parts[i], c.get(parts[i]));
81 | }
82 | }
83 | else {
84 | c.set(parts[i], sched[i]);
85 | }
86 | }
87 | return c;
88 | }
89 |
90 | public static boolean validDate(String s) {
91 | String family = "0-9\\*";
92 | Pattern p = Pattern.compile(
93 | "^[ ]*["+family+
94 | "]+[ ]+["+family+"]+[ ]+["+family+
95 | "]+[ ]+["+family+"]+[ ]+["+family+"]+[ ]*$");
96 | Matcher m = p.matcher(s);
97 | return m.matches();
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/app/src/main/java/com/releasestandard/scriptmanager/tools/CallStack.java:
--------------------------------------------------------------------------------
1 | package com.releasestandard.scriptmanager.tools;
2 |
3 | import android.util.Log;
4 |
5 | import java.util.Map;
6 | import java.util.Set;
7 |
8 | public class CallStack {
9 |
10 |
11 | public static String getLastCaller() {
12 | return getLastCaller(Thread.getAllStackTraces().entrySet());
13 | }
14 |
15 | public static String getLastCaller(Integer off) {
16 | return getLastCaller(Thread.getAllStackTraces().entrySet(), off);
17 | }
18 | public static String getLastCaller(Set> set) {
19 | return getLastCaller(set, 3);
20 | }
21 |
22 | private static String getLastCaller(Set> set, Integer offset) {
23 | for (Map.Entry entry : set) {
24 | Integer n = offset;
25 | if (n < 0 || !entry.getKey().getName().equals("main")) {
26 | continue;
27 | }
28 | StackTraceElement[] es = entry.getValue();
29 | for( int a = 0; a < es.length; a = a + 1) {
30 | StackTraceElement ste = es[a];
31 | String s;
32 | if(ste.getClassName().lastIndexOf(Logger.packageid) != -1 &&
33 | ste.getClassName().lastIndexOf(Logger.packageid + ".tools.Logger") == -1 &&
34 | ste.getClassName().lastIndexOf(Logger.packageid + ".tools.CallStack") == -1 ) {
35 | return clearName(ste.getClassName()) + "#" + clearName(ste.getMethodName());
36 | }
37 | }
38 | }
39 | return null;
40 | }
41 |
42 | private static String clearName(String name) {
43 | Integer i = name.lastIndexOf(".") + 1;
44 | if (i == -1) {
45 | return name;
46 | }
47 | return name.substring(i);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/app/src/main/java/com/releasestandard/scriptmanager/tools/CompatAPI.java:
--------------------------------------------------------------------------------
1 | package com.releasestandard.scriptmanager.tools;
2 |
3 | import android.app.AlarmManager;
4 | import android.app.PendingIntent;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.content.res.Resources;
8 | import android.os.Build;
9 | import android.os.Environment;
10 | import android.provider.DocumentsContract;
11 |
12 | import androidx.core.app.AlarmManagerCompat;
13 | import androidx.preference.PreferenceFragmentCompat;
14 |
15 | import com.releasestandard.scriptmanager.MainActivity;
16 |
17 | import java.io.File;
18 |
19 | import static android.app.AlarmManager.RTC_WAKEUP;
20 |
21 | /**
22 | * Handle API differences in Android.
23 | */
24 | public class CompatAPI {
25 |
26 | private static AlarmManager am = null;
27 | /**
28 | * Open a file or directory
29 | * compat 1
30 | */
31 | public static boolean openDocument(MainActivity main) { return openDocument(main,null); }
32 | public static boolean openDocument(MainActivity main, String selectedUri) {
33 | Intent intent;
34 | intent = new Intent(Intent.ACTION_GET_CONTENT);
35 | intent.addCategory(Intent.CATEGORY_OPENABLE);
36 | intent.setType("text/*");
37 | if ( Build.VERSION.SDK_INT >= 26 && selectedUri != null ) {
38 | intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, selectedUri);
39 | }
40 | main.startActivityForResult(intent, main.ACTIVITY_REQUEST_CODE_IMPORT);
41 | return true;
42 | }
43 |
44 | /**
45 | * Handle alarm set time.
46 | * compat 1
47 | */
48 | public static boolean setAlarmIntentTime(Context context, long t, PendingIntent alarmIntent) {
49 | if ( am == null ) {
50 | am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
51 | }
52 | AlarmManagerCompat.setExactAndAllowWhileIdle(am, RTC_WAKEUP, t, alarmIntent);
53 | return true;
54 | }
55 |
56 | /**
57 | * Modify settings when features are not avaliable.
58 | */
59 | public static boolean modifySettings(PreferenceFragmentCompat settings) {
60 | Resources r = settings.getContext().getResources();
61 | return true;
62 | }
63 | /**
64 | * compat 1
65 | * Return a path to external storage or null, if unmounted or doesn't exists.
66 | */
67 | public static String getExternalStorage(Context ctx) {
68 | Logger.debug("decision:");
69 | String state = Environment.getExternalStorageState();
70 | String bad_states[] = new String[]{Environment.MEDIA_REMOVED, Environment.MEDIA_UNMOUNTED, Environment.MEDIA_NOFS, Environment.MEDIA_MOUNTED_READ_ONLY,
71 | Environment.MEDIA_BAD_REMOVAL, Environment.MEDIA_UNMOUNTABLE};
72 | for(String bs : bad_states) {
73 | if( state.equals(bs) ) {
74 | Logger.debug("external storage not valid (" + bs + ")");
75 | return null;
76 | }
77 | }
78 |
79 | if (Build.VERSION.SDK_INT < 8) {
80 | String sf = Environment.getExternalStorageDirectory() + "/" + Logger.appname;
81 | File f = new File(sf);
82 | if ( f != null ) {
83 | f.mkdir();
84 | if (f.exists()) {
85 | Logger.debug("directory created in external storage");
86 | return f.getAbsolutePath();
87 | }
88 | }
89 | Logger.debug("something has failed");
90 | return null;
91 | }
92 | else {
93 | File f = ctx.getExternalFilesDir(null);
94 | Logger.debug("success");
95 | return f.getAbsolutePath();
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/app/src/main/java/com/releasestandard/scriptmanager/tools/Logger.java:
--------------------------------------------------------------------------------
1 | package com.releasestandard.scriptmanager.tools;
2 |
3 | import android.util.Log;
4 |
5 | import java.io.ByteArrayOutputStream;
6 | import java.io.PrintStream;
7 |
8 | /**
9 | * Show messages on stdout (logcat)
10 | * compat 1
11 | */
12 | public class Logger {
13 |
14 | public static boolean DEBUG = true;
15 | public static final String appname = "scriptmanager";
16 | public static final String packageid = "com.releasestandard.scriptmanager";
17 | private static String RED = "31";
18 | private static String GREEN = "32";
19 | private static String YELLOW = "33";
20 | public static String SRED = "\033[" + RED + "m";
21 | public static String SGREEN = "\033[" + GREEN + "m";
22 | public static String SYELLOW = "\033[" + YELLOW + "m";
23 | public static String SZERO = "\033[0m";
24 | public static void debug(String msg) { debug(msg,SGREEN); }
25 | public static void debug(String msg, String color) {
26 | if ( DEBUG ) {
27 | String tag = color + appname+"/" + CallStack.getLastCaller(6) ;
28 | Log.v(tag, SZERO + msg);
29 | }
30 | }
31 | public static void log(String msg) {
32 | Log.v(appname,msg);
33 | }
34 | public static void unsupported(Integer min) {
35 | unsupported(min,-1);
36 | }
37 | public static void unsupported(Integer min, Integer max) {
38 | if ( max < 0) {
39 | debug("API < " + min.toString() + " are not supported",SRED);
40 | } else {
41 | debug("API < " + min.toString() + " or API > " + max.toString() + " are not supported",SRED);
42 | }
43 | }
44 | public static PrintStream getTraceStream() {
45 | if ( DEBUG ) {
46 | return System.out;
47 | } else {
48 | ByteArrayOutputStream buffer = new ByteArrayOutputStream();
49 | PrintStream os = new PrintStream(buffer);
50 | return os;
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/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/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
10 |
12 |
14 |
16 |
18 |
20 |
22 |
24 |
26 |
28 |
30 |
32 |
34 |
36 |
38 |
40 |
42 |
44 |
46 |
48 |
50 |
52 |
54 |
56 |
58 |
60 |
62 |
64 |
66 |
68 |
70 |
72 |
74 |
75 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReleaseStandard/ScriptManager/270aae7e68f9cdaf8ff3f293a49ca8408df3773d/app/src/main/res/drawable/logo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/logo2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReleaseStandard/ScriptManager/270aae7e68f9cdaf8ff3f293a49ca8408df3773d/app/src/main/res/drawable/logo2.png
--------------------------------------------------------------------------------
/app/src/main/res/layout/job_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
14 |
22 |
23 |
36 |
37 |
41 |
42 |
49 |
50 |
56 |
57 |
58 |
63 |
64 |
73 |
74 |
80 |
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/jobs_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
15 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/main_activity.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
19 |
20 |
30 |
31 |
32 |
33 |
34 |
35 |
49 |
50 |
51 |
52 |
66 |
67 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/rename_dialog.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_main.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
13 |
19 |
25 |
31 |
37 |
43 |
49 |
55 |
61 |
62 |
68 |
74 |
75 |
76 |
82 |
83 |
89 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/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-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReleaseStandard/ScriptManager/270aae7e68f9cdaf8ff3f293a49ca8408df3773d/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReleaseStandard/ScriptManager/270aae7e68f9cdaf8ff3f293a49ca8408df3773d/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReleaseStandard/ScriptManager/270aae7e68f9cdaf8ff3f293a49ca8408df3773d/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/logo2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReleaseStandard/ScriptManager/270aae7e68f9cdaf8ff3f293a49ca8408df3773d/app/src/main/res/mipmap-hdpi/logo2.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReleaseStandard/ScriptManager/270aae7e68f9cdaf8ff3f293a49ca8408df3773d/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReleaseStandard/ScriptManager/270aae7e68f9cdaf8ff3f293a49ca8408df3773d/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReleaseStandard/ScriptManager/270aae7e68f9cdaf8ff3f293a49ca8408df3773d/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/logo2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReleaseStandard/ScriptManager/270aae7e68f9cdaf8ff3f293a49ca8408df3773d/app/src/main/res/mipmap-mdpi/logo2.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReleaseStandard/ScriptManager/270aae7e68f9cdaf8ff3f293a49ca8408df3773d/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReleaseStandard/ScriptManager/270aae7e68f9cdaf8ff3f293a49ca8408df3773d/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReleaseStandard/ScriptManager/270aae7e68f9cdaf8ff3f293a49ca8408df3773d/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/logo2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReleaseStandard/ScriptManager/270aae7e68f9cdaf8ff3f293a49ca8408df3773d/app/src/main/res/mipmap-xhdpi/logo2.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReleaseStandard/ScriptManager/270aae7e68f9cdaf8ff3f293a49ca8408df3773d/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReleaseStandard/ScriptManager/270aae7e68f9cdaf8ff3f293a49ca8408df3773d/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReleaseStandard/ScriptManager/270aae7e68f9cdaf8ff3f293a49ca8408df3773d/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/logo2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReleaseStandard/ScriptManager/270aae7e68f9cdaf8ff3f293a49ca8408df3773d/app/src/main/res/mipmap-xxhdpi/logo2.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReleaseStandard/ScriptManager/270aae7e68f9cdaf8ff3f293a49ca8408df3773d/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReleaseStandard/ScriptManager/270aae7e68f9cdaf8ff3f293a49ca8408df3773d/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReleaseStandard/ScriptManager/270aae7e68f9cdaf8ff3f293a49ca8408df3773d/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/logo2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReleaseStandard/ScriptManager/270aae7e68f9cdaf8ff3f293a49ca8408df3773d/app/src/main/res/mipmap-xxxhdpi/logo2.png
--------------------------------------------------------------------------------
/app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | @color/black
4 | @color/black
5 | #C0C0C0
6 | #66C0C0C0
7 | @color/white
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values/api.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | smsReceived
7 | triggerRecvMsg
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 16dp
3 | 64dp
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/overflow_menu_values.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | ScriptManager
4 | com.releasestandard.scriptmanager
5 | Settings
6 | Stop selected
7 | Stop all
8 | Unselect all
9 | Edit
10 | Rename
11 | Rename
12 | Show log
13 | Clear log
14 | Ok
15 | Cancel
16 | Browse scripts
17 | Import script
18 | Delete
19 |
--------------------------------------------------------------------------------
/app/src/main/res/values/quit_dialog.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Leave
4 | Really leave the application ?
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values/settings_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Developper
6 | Debug mode
7 | Send messages to logcat
8 | preferences_developper_debug_mode
9 | false
10 |
11 | Display
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FF00C0C0
4 | @color/white
5 | #C0C0C0
6 | #66C0C0C0
7 | @color/black
8 |
9 |
13 |
44 |
45 |
48 |
49 |
54 |
55 |
66 |
74 |
75 |
91 |
99 |
100 |
109 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
159 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/provider_paths.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/settings_fragment.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
9 |
10 |
11 |
14 |
15 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/test/java/com/releasestandard/scriptmanager/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.releasestandard.scriptmanager;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | repositories {
4 | google()
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath "com.android.tools.build:gradle:4.1.1"
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | google()
18 | jcenter()
19 | }
20 | }
21 |
22 | task clean(type: Delete) {
23 | delete rootProject.buildDir
24 | }
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/full_description.txt:
--------------------------------------------------------------------------------
1 | Manage, launch and schedule your sh scripts.
2 | FEATURES
3 | * Scripts schedulding and repetition (persistent accross reboot).
4 | * Launch your scripts via button.
5 | * No root required.
6 | * Scripts edition with your favourite text editor.
7 | HOW TO USE
8 | Press "+" to create a new job.
9 | Longpress the new job and overflow menu > Edit (script your script).
10 | Then click play to launch the script.
11 | Check the result by long press then View log.
12 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReleaseStandard/ScriptManager/270aae7e68f9cdaf8ff3f293a49ca8408df3773d/fastlane/metadata/android/en-US/images/phoneScreenshots/1.jpg
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReleaseStandard/ScriptManager/270aae7e68f9cdaf8ff3f293a49ca8408df3773d/fastlane/metadata/android/en-US/images/phoneScreenshots/2.jpg
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReleaseStandard/ScriptManager/270aae7e68f9cdaf8ff3f293a49ca8408df3773d/fastlane/metadata/android/en-US/images/phoneScreenshots/3.jpg
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/short_description.txt:
--------------------------------------------------------------------------------
1 | Manage sh scripts
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/title.txt:
--------------------------------------------------------------------------------
1 | ScriptManager
2 |
3 |
--------------------------------------------------------------------------------
/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 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReleaseStandard/ScriptManager/270aae7e68f9cdaf8ff3f293a49ca8408df3773d/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Dec 30 20:18:14 GMT 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/scripts/checkcompat.sh:
--------------------------------------------------------------------------------
1 | #
2 | # If nothing is sayed then compatibility has not been checked.
3 | # If class is compat x then all methods are compat level x.
4 | # W: For compat not recursive checks: e.g. class A extends B (B compat 10, A compat 1)
5 | # ~All basic blocks of the class are compat level x
6 |
7 |
8 | t="/tmp/t";
9 | g="./app/build.gradle";
10 | l=$(find app/src/main/java/ -name "*.java" -exec bash -c 'grep "* compat" {} | sed "s/^ \+\* \+compat \+\\([0-9]\\+\\).*/\1/g"' \; |sort -u -n|tail -n 1)
11 | lv=$(find app/src/main/java/ -name "*.java" -exec bash -c 'grep -H "* compat" {} | sed "s/^\\([^ ]\+\\): \+\* \+compat \+\\([0-9]\\+\\).*/\2 \1/g"' \; |sort -u -n |tail -n 1)
12 | echo "$lv";
13 |
14 | if [ "$1" = "write" ] ; then
15 | echo "Modify build.gradle";
16 | sed "s/\\(minSdkVersion \\+\\)[0-9]\\+/\\1${l}/g" $g > $t;
17 | mv $t $g;
18 | fi
19 |
20 | # Currently studio say 16 min maybe
21 |
--------------------------------------------------------------------------------
/scripts/newversion.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 |
4 | if [ "$VERSION" = "" ] ; then
5 | read -p "version?> " VERSION;
6 | read -p "confirmation?> " VERSION2;
7 | else
8 | VERSION2="$VERSION";
9 | fi
10 | if [ "$VERSION" = "" ] ; then
11 | echo "Empty version";
12 | exit 1;
13 | fi
14 | if ! [ "$VERSION" = "$VERSION2" ] ; then
15 | echo "$VERSION != $VERSION2";
16 | exit 1;
17 | fi
18 | F="./app/build.gradle";
19 | T="$F.temp";
20 | sed "s/\(versionName \"\)[^\"]\+/\1${VERSION}/" $F > $T;
21 | mv $T $F;
22 | vcode=$(python3 -c "a=${VERSION};print(str(int(a*10)));");
23 | sed "s/\(versionCode \)[0-9]\+/\1${vcode}/" $F > $T;
24 | mv $T $F;
25 | git add $F;
26 | git commit -m "Version $VERSION";
27 | git tag $VERSION
28 | git push origin HEAD:master
29 | git push --tags origin HEAD:master
30 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 | rootProject.name = "scriptmanager"
3 |
--------------------------------------------------------------------------------