diff --git a/Android/app/src/main/cpp/native.cpp b/Android/app/src/main/cpp/native.cpp
index 986634ee..835381bf 100644
--- a/Android/app/src/main/cpp/native.cpp
+++ b/Android/app/src/main/cpp/native.cpp
@@ -74,58 +74,18 @@ string cppExec(const char *cmd) {
extern "C"
JNIEXPORT jstring JNICALL
-Java_com_dergoogler_mmrl_Lib_test(JNIEnv *env, jclass clazz) {
- string hello = "Hello from C++";
- return env->NewStringUTF(hello.c_str());
-}
-
-extern "C"
-JNIEXPORT jstring JNICALL
-Java_com_dergoogler_mmrl_Lib_baseUrl(JNIEnv *env, jclass clazz) {
- string result = "file:///android_asset/";
- return env->NewStringUTF(result.c_str());
-}
-
-extern "C"
-JNIEXPORT jstring JNICALL
-Java_com_dergoogler_mmrl_Lib_interfaceName(JNIEnv *env, jclass clazz) {
- string result = "android";
- return env->NewStringUTF(result.c_str());
-}
-
-extern "C"
-JNIEXPORT jstring JNICALL
-Java_com_dergoogler_mmrl_Lib_getStorageKey(JNIEnv *env, jclass clazz) {
- string result = "localstorage";
- return env->NewStringUTF(result.c_str());
-}
-
-extern "C"
-JNIEXPORT jstring JNICALL
-Java_com_dergoogler_mmrl_Lib_getUserAgent(JNIEnv *env, jclass clazz) {
- string result = "MMRL";
- return env->NewStringUTF(result.c_str());
-}
-
-extern "C"
-JNIEXPORT jstring JNICALL
-Java_com_dergoogler_mmrl_Lib_pageContent(JNIEnv *env, jclass clazz, jstring cssInject) {
+Java_com_dergoogler_components_ModuleView_pageContent(JNIEnv *env, jclass clazz,
+ jstring cssInject) {
string doctype = R"()";
string htmlStart = R"()";
string headStart = R"(
)";
string styleVendor = R"()";
string styleApp = R"()";
string meta = R"()";
- string cssInject1 = R"()";
- string cssInjectResult =
- cssInject1 + cssInject2 + cssInject3 + cssInject4 + cssInject5 + cssInject6 +
- cssInject7;
+ string cssInject1 = R"()";
+ string cssInjectResult = cssInject1 + cssInject2 + cssInject3;
string headEnd = R"()";
string bodyStart = R"()";
string app = R"()";
diff --git a/Android/app/src/main/java/com/dergoogler/components/ModuleView.java b/Android/app/src/main/java/com/dergoogler/components/ModuleView.java
index 0c406a7a..28cfb3f5 100644
--- a/Android/app/src/main/java/com/dergoogler/components/ModuleView.java
+++ b/Android/app/src/main/java/com/dergoogler/components/ModuleView.java
@@ -20,6 +20,15 @@ public class ModuleView extends WebView {
private final WebSettings webSettings;
+ static {
+ System.loadLibrary("native-lib");
+ }
+ /**
+ * Returns the html page to load. This is to prevent js injection though the html page with tags
+ * @return HTML page string
+ */
+ public static native String pageContent(@NonNull String cssInject);
+
public ModuleView(Context context) {
super(context);
this.webSettings = this.getSettings();
diff --git a/Android/app/src/main/java/com/dergoogler/mmrl/Interface.java b/Android/app/src/main/java/com/dergoogler/mmrl/Interface.java
index 8ee1ecac..6d7c0721 100644
--- a/Android/app/src/main/java/com/dergoogler/mmrl/Interface.java
+++ b/Android/app/src/main/java/com/dergoogler/mmrl/Interface.java
@@ -81,7 +81,7 @@ class retn {
private final SharedPreferences localstorage;
public retn(@NonNull Context context) {
- this.localstorage = context.getSharedPreferences(Lib.getStorageKey(), Activity.MODE_PRIVATE);
+ this.localstorage = context.getSharedPreferences("localstorage", Activity.MODE_PRIVATE);
}
/**
diff --git a/Android/app/src/main/java/com/dergoogler/mmrl/Lib.java b/Android/app/src/main/java/com/dergoogler/mmrl/Lib.java
deleted file mode 100644
index e3ac4707..00000000
--- a/Android/app/src/main/java/com/dergoogler/mmrl/Lib.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.dergoogler.mmrl;
-
-import androidx.annotation.NonNull;
-
-public class Lib {
-
- static {
- System.loadLibrary("native-lib");
- }
-
- public static native String baseUrl();
-
- public static native String interfaceName();
-
- public static native String getStorageKey();
-
- public static native String getUserAgent();
-
- /**
- * Returns the html page to load. This is to prevent js injection though the html page with tags
- * @return HTML page string
- */
- public static native String pageContent(@NonNull String cssInject);
-
- static class log {
-
- }
-}
diff --git a/Android/app/src/main/java/com/dergoogler/mmrl/MainActivity.java b/Android/app/src/main/java/com/dergoogler/mmrl/MainActivity.java
index b33bb812..4e9b5488 100755
--- a/Android/app/src/main/java/com/dergoogler/mmrl/MainActivity.java
+++ b/Android/app/src/main/java/com/dergoogler/mmrl/MainActivity.java
@@ -34,9 +34,9 @@ protected void onCreate(Bundle savedInstanceState) {
view = findViewById(R.id.mmrl_view);
view.setJavaScriptEnabled(true);
- view.setUserAgentString(Lib.getUserAgent());
- view.loadHTML(Lib.baseUrl(), Lib.pageContent(this.cssInject()));
- view.setJavascriptInterface(new Interface(this), Lib.interfaceName());
+ view.setUserAgentString("MMRL");
+ view.loadHTML("file:///android_asset/", ModuleView.pageContent(this.cssInject()));
+ view.setJavascriptInterface(new Interface(this), "android");
}
@Override
diff --git a/README.md b/README.md
index 64d5f2f8..80a8643a 100644
--- a/README.md
+++ b/README.md
@@ -32,7 +32,7 @@ module = {
}
```
-## What MMRL Supports
+## What MMRL Supports (Roadmap)
- [x] Custom repo loading
- [ ] Translation
@@ -41,9 +41,9 @@ module = {
- [x] Custom theming
- [x] View installed modules
-- [x] Delete installed modules
-- [ ] Disable installed modules
-- [ ] Enable installed modules
+- [x] Remove installed modules
+- [x] Disable installed modules
+- [x] Enable installed modules
- [ ] Module install
## Meet the Description API (DAPI)
diff --git a/Website/licenseBuild/licenses.json b/Website/licenseBuild/licenses.json
index a3d16ca7..f66b2ab8 100644
--- a/Website/licenseBuild/licenses.json
+++ b/Website/licenseBuild/licenses.json
@@ -2263,6 +2263,14 @@
"version": "1.0.0",
"description": "Use node's fs.realpath, but fall back to the JS implementation if the native one fails"
},
+ "fuchs-template@1.0.0": {
+ "licenses": "MIT",
+ "repository": "https://github.com/maxhoffmann/fuchs-template",
+ "publisher": "Maximilian Hoffmann",
+ "name": "fuchs-template",
+ "version": "1.0.0",
+ "description": "minimum templating engine by thomas fuchs"
+ },
"function-bind@1.1.1": {
"licenses": "MIT",
"repository": "https://github.com/Raynos/function-bind",
diff --git a/Website/package-lock.json b/Website/package-lock.json
index 847eac8d..b8ae0d75 100644
--- a/Website/package-lock.json
+++ b/Website/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "com.dergoogler.mmrl.web",
- "version": "1.3.0",
+ "version": "1.4.0@alpha-2",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "com.dergoogler.mmrl.web",
- "version": "1.3.0",
+ "version": "1.4.0@alpha-2",
"license": "ISC",
"dependencies": {
"@emotion/react": "^11.9.0",
@@ -17,6 +17,7 @@
"axios": "^0.26.1",
"bootstrap": "^5.1.3",
"dot-properties": "^1.0.1",
+ "fuchs-template": "^1.0.0",
"jss": "^10.9.0",
"jss-preset-default": "^10.9.0",
"markdown-to-jsx": "^7.1.7",
@@ -3822,6 +3823,11 @@
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
+ "node_modules/fuchs-template": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fuchs-template/-/fuchs-template-1.0.0.tgz",
+ "integrity": "sha1-gUwMiS2Qcd7Jkwk5mRR8IBnxRqE="
+ },
"node_modules/function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
@@ -9738,6 +9744,11 @@
"dev": true,
"optional": true
},
+ "fuchs-template": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fuchs-template/-/fuchs-template-1.0.0.tgz",
+ "integrity": "sha1-gUwMiS2Qcd7Jkwk5mRR8IBnxRqE="
+ },
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
diff --git a/Website/package.json b/Website/package.json
index 6abf2674..2255bf43 100644
--- a/Website/package.json
+++ b/Website/package.json
@@ -24,6 +24,7 @@
"axios": "^0.26.1",
"bootstrap": "^5.1.3",
"dot-properties": "^1.0.1",
+ "fuchs-template": "^1.0.0",
"jss": "^10.9.0",
"jss-preset-default": "^10.9.0",
"markdown-to-jsx": "^7.1.7",
diff --git a/Website/src/activitys/MainApplication.tsx b/Website/src/activitys/MainApplication.tsx
index 003e3301..7781c1b6 100644
--- a/Website/src/activitys/MainApplication.tsx
+++ b/Website/src/activitys/MainApplication.tsx
@@ -25,6 +25,7 @@ interface Props {
}
class MainApplication extends AppCompatActivity {
+ t: any;
public constructor(props: Props | Readonly) {
super(props);
this.state = {};
diff --git a/Website/src/components/DeviceModule.tsx b/Website/src/components/DeviceModule.tsx
index 5f6b4789..884a502a 100644
--- a/Website/src/components/DeviceModule.tsx
+++ b/Website/src/components/DeviceModule.tsx
@@ -1,5 +1,5 @@
import * as React from "react";
-import { Card, Dialog, List, ListItem } from "react-onsenui";
+import { Card, Dialog, List, ListItem, Switch, SwitchChangeEvent } from "react-onsenui";
import Properties from "@js.properties/properties";
import SuFile from "@Builders/SuFile";
import AlertBuilder from "@Builders/AlertBuilder";
@@ -7,6 +7,7 @@ import DeleteIcon from "./icons/DeleteIcon";
import Log from "@Builders/Log";
import Toast from "@Builders/Toast";
import Gesture from "./Gesture";
+import TimeIcon from "./icons/TimeIcon";
interface Props {
module: string;
@@ -14,6 +15,8 @@ interface Props {
interface States {
dialogShown: boolean;
+ isEnabled: boolean;
+ isSwitchDisabled: boolean;
props: {
id?: string;
name?: string;
@@ -33,21 +36,35 @@ interface States {
}
class DeviceModule extends React.Component {
+ private log: Log;
public constructor(props: Props | Readonly) {
super(props);
this.state = {
props: {},
dialogShown: false,
+ isEnabled: true,
+ isSwitchDisabled: false,
};
+ this.log = new Log(this.constructor.name);
}
- public componentDidMount() {
+ public componentDidMount = () => {
const module = this.props.module;
const readProps = new SuFile(`/data/adb/modules/${module}/module.prop`).system.read();
this.setState({
props: Properties.parseToProperties(readProps),
});
- }
+
+ const disable = new SuFile(`/data/adb/modules/${module}/disable`);
+ if (disable.system.exists()) {
+ this.setState({ isEnabled: false });
+ }
+
+ const remove = new SuFile(`/data/adb/modules/${module}/remove`);
+ if (remove.system.exists()) {
+ this.setState({ isSwitchDisabled: true });
+ }
+ };
private showDialog = () => {
this.setState({ dialogShown: true });
@@ -60,6 +77,7 @@ class DeviceModule extends React.Component {
public render = () => {
const module = this.props.module;
const { id, name, version, versionCode, author, description } = this.state.props;
+ const { isEnabled, isSwitchDisabled } = this.state;
return (
<>
@@ -74,7 +92,33 @@ class DeviceModule extends React.Component
{
>
- {name}
+
+ {name}
+
+ {
+ const checked = e.target.checked;
+ const disable = new SuFile(`/data/adb/modules/${module}/disable`);
+
+ if (checked) {
+ if (disable.system.exists()) {
+ if (disable.system.delete()) {
+ this.log.i(`${module} has been enabled`);
+ }
+ }
+ } else {
+ if (!disable.system.exists()) {
+ if (disable.system.createNewFile()) {
+ this.log.i(`${module} has been disabled`);
+ }
+ }
+ }
+ }}
+ />
+ {" "}
+
@@ -91,28 +135,62 @@ class DeviceModule extends React.Component {
{/*
// @ts-ignore */}
- {
- const file = new SuFile(`/data/adb/modules/${module}`);
- new AlertBuilder()
- .setTitle(`Delete ${name}`)
- .setMessage("Are you sure to delete the module forever?")
- .setPositiveButton("Yes", () => {
- if (file.system.exists()) {
- file.system.deleteRecursive();
- Toast.makeText("Module deleted", Toast.LENGTH_LONG).show();
- } else {
- Toast.makeText(`Failed to delete ${module} module`, Toast.LENGTH_LONG).show();
- }
- })
- .showAlert();
- }}
- >
-
-
-
- Delete module
-
+ {(() => {
+ if (isSwitchDisabled) {
+ return (
+ <>
+ {
+ const remove = new SuFile(`/data/adb/modules/${module}/remove`);
+ if (remove.system.exists()) {
+ if (remove.system.delete()) {
+ this.setState({ isSwitchDisabled: false });
+ this.log.i(`${module} has been recovered`);
+ } else {
+ this.log.e(`Failed to recover ${module}`);
+ }
+ } else {
+ this.log.e(`This remove file don't exists for ${module}`);
+ }
+ }}
+ >
+
+
+
+ Recover module
+
+ >
+ );
+ } else {
+ return (
+ <>
+ {
+ const file = new SuFile(`/data/adb/modules/${module}/remove`);
+ new AlertBuilder()
+ .setTitle(`Delete ${name}`)
+ .setMessage("Are you sure to delete the module forever?")
+ .setPositiveButton("Yes", () => {
+ if (file.system.createNewFile()) {
+ this.setState({ isSwitchDisabled: true });
+ Toast.makeText("Module get removed on next reboot", Toast.LENGTH_LONG).show();
+ } else {
+ this.setState({ isSwitchDisabled: false });
+ Toast.makeText(`Failed to add reomve to ${module} module`, Toast.LENGTH_LONG).show();
+ }
+ })
+ .showAlert();
+ }}
+ >
+
+
+
+ Remove module
+
+ >
+ );
+ }
+ })()}
diff --git a/Website/src/components/ExploreModule.tsx b/Website/src/components/ExploreModule.tsx
index 4d2b5869..70224952 100644
--- a/Website/src/components/ExploreModule.tsx
+++ b/Website/src/components/ExploreModule.tsx
@@ -2,7 +2,7 @@ import * as React from "react";
import ons from "onsenui";
import axios from "axios";
import Properties from "@js.properties/properties";
-import { Card } from "react-onsenui";
+import { Card, Switch } from "react-onsenui";
import { Chip } from "@mui/material";
import tools from "@Utils/tools";
import ViewModuleActivity from "@Activitys/ViewModuleActivity";
@@ -192,7 +192,20 @@ class ExploreModule extends React.Component {
return null;
}
})()}
- {props.name}
+
+
+ {props.name}
+
+
{(() => {
if (isVerified) {
return (
diff --git a/Website/src/components/icons/TimeIcon.jsx b/Website/src/components/icons/TimeIcon.jsx
new file mode 100644
index 00000000..e305fa25
--- /dev/null
+++ b/Website/src/components/icons/TimeIcon.jsx
@@ -0,0 +1,30 @@
+import * as React from "react";
+
+class TimeIcon extends React.Component {
+ render() {
+ const { color, size, className } = this.props;
+ return (
+
+ );
+ }
+}
+
+TimeIcon.defaultProps = {
+ size: "16",
+};
+
+export default TimeIcon;
diff --git a/Website/src/language/de.json b/Website/src/language/de.json
new file mode 100644
index 00000000..544b7b4d
--- /dev/null
+++ b/Website/src/language/de.json
@@ -0,0 +1,3 @@
+{
+
+}
\ No newline at end of file
diff --git a/Website/src/styles/addtional.scss b/Website/src/styles/addtional.scss
index 6bbab099..50ffa63b 100644
--- a/Website/src/styles/addtional.scss
+++ b/Website/src/styles/addtional.scss
@@ -27,6 +27,20 @@ item-card-wrapper {
margin-top: 8px;
}
item-module-name {
+ display: flex;
+ item-name {
+ font-size: large;
+ overflow: hidden;
+ text-align: start;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ width: 100%;
+ }
+ item-switch {
+ font-size: small;
+ text-align: end;
+ white-space: nowrap;
+ }
}
}
diff --git a/Website/src/typings/declaration.d.ts b/Website/src/typings/declaration.d.ts
new file mode 100644
index 00000000..39257771
--- /dev/null
+++ b/Website/src/typings/declaration.d.ts
@@ -0,0 +1 @@
+declare module 'fuchs-template';
diff --git a/Website/src/typings/global.d.ts b/Website/src/typings/global.d.ts
index bc827a41..2bd7771e 100644
--- a/Website/src/typings/global.d.ts
+++ b/Website/src/typings/global.d.ts
@@ -47,6 +47,8 @@ declare global {
"item-description": HTMLAttributes;
"item-last-update": HTMLAttributes;
"item-module-name": HTMLAttributes;
+ "item-name": HTMLAttributes;
+ "item-switch": HTMLAttributes;
// License cards
"license-card-wrapper": HTMLAttributes;
diff --git a/Website/src/utils/translations.ts b/Website/src/utils/translations.ts
new file mode 100644
index 00000000..5d4564fe
--- /dev/null
+++ b/Website/src/utils/translations.ts
@@ -0,0 +1,34 @@
+import * as t from "fuchs-template";
+import de from "./../language/de.json";
+
+const variableRegex = /\{[\w-]+?\}/g;
+
+function translations(locale: any, isDevelopment: boolean) {
+ if (typeof locale !== "object") throw new Error("The locale has to be an object.");
+
+ const invalidLocale = Object.keys(locale).some(function (key) {
+ return typeof locale[key] !== "string";
+ });
+
+ if (isDevelopment && invalidLocale) throw new Error("The locale json file has to be a map of strings to strings.");
+
+ return function (key: string, values: string) {
+ if (typeof locale[key] !== "string") {
+ if (!isDevelopment) {
+ return key;
+ }
+ throw new Error('There is no translation for the key "' + key + '" in this locale.');
+ }
+
+ const result = t(locale[key], values);
+
+ if (isDevelopment && variableRegex.test(result)) {
+ const undefinedVariables = result.match(variableRegex).join(", ");
+ throw new Error("Missing value for " + undefinedVariables);
+ }
+
+ return result;
+ };
+}
+
+export default translations;