diff --git a/3x_base.go b/3x_base.go deleted file mode 100644 index 7bc61b6..0000000 --- a/3x_base.go +++ /dev/null @@ -1,42 +0,0 @@ -// Package controller provides HTTP request handlers and controllers for the 3x-ui web management panel. -// It handles routing, authentication, and API endpoints for managing Xray inbounds, settings, and more. -package controller - -import ( - "net/http" - - "github.com/mhsanaei/3x-ui/v2/logger" - "github.com/mhsanaei/3x-ui/v2/web/locale" - "github.com/mhsanaei/3x-ui/v2/web/session" - - "github.com/gin-gonic/gin" -) - -// BaseController provides common functionality for all controllers, including authentication checks. -type BaseController struct{} - -// checkLogin is a middleware that verifies user authentication and handles unauthorized access. -func (a *BaseController) checkLogin(c *gin.Context) { - if !session.IsLogin(c) { - if isAjax(c) { - pureJsonMsg(c, http.StatusUnauthorized, false, I18nWeb(c, "pages.login.loginAgain")) - } else { - c.Redirect(http.StatusTemporaryRedirect, c.GetString("base_path")) - } - c.Abort() - } else { - c.Next() - } -} - -// I18nWeb retrieves an internationalized message for the web interface based on the current locale. -func I18nWeb(c *gin.Context, name string, params ...string) string { - anyfunc, funcExists := c.Get("I18n") - if !funcExists { - logger.Warning("I18n function not exists in gin context!") - return "" - } - i18nFunc, _ := anyfunc.(func(i18nType locale.I18nType, key string, keyParams ...string) string) - msg := i18nFunc(locale.Web, name, params...) - return msg -} diff --git a/3x_inbound.go b/3x_inbound.go deleted file mode 100644 index 8317de3..0000000 --- a/3x_inbound.go +++ /dev/null @@ -1,424 +0,0 @@ -package controller - -import ( - "encoding/json" - "fmt" - "strconv" - - "github.com/mhsanaei/3x-ui/v2/database/model" - "github.com/mhsanaei/3x-ui/v2/web/service" - "github.com/mhsanaei/3x-ui/v2/web/session" - "github.com/mhsanaei/3x-ui/v2/web/websocket" - - "github.com/gin-gonic/gin" -) - -// InboundController handles HTTP requests related to Xray inbounds management. -type InboundController struct { - inboundService service.InboundService - xrayService service.XrayService -} - -// NewInboundController creates a new InboundController and sets up its routes. -func NewInboundController(g *gin.RouterGroup) *InboundController { - a := &InboundController{} - a.initRouter(g) - return a -} - -// initRouter initializes the routes for inbound-related operations. -func (a *InboundController) initRouter(g *gin.RouterGroup) { - - g.GET("/list", a.getInbounds) - g.GET("/get/:id", a.getInbound) - g.GET("/getClientTraffics/:email", a.getClientTraffics) - g.GET("/getClientTrafficsById/:id", a.getClientTrafficsById) - - g.POST("/add", a.addInbound) - g.POST("/del/:id", a.delInbound) - g.POST("/update/:id", a.updateInbound) - g.POST("/clientIps/:email", a.getClientIps) - g.POST("/clearClientIps/:email", a.clearClientIps) - g.POST("/addClient", a.addInboundClient) - g.POST("/:id/delClient/:clientId", a.delInboundClient) - g.POST("/updateClient/:clientId", a.updateInboundClient) - g.POST("/:id/resetClientTraffic/:email", a.resetClientTraffic) - g.POST("/resetAllTraffics", a.resetAllTraffics) - g.POST("/resetAllClientTraffics/:id", a.resetAllClientTraffics) - g.POST("/delDepletedClients/:id", a.delDepletedClients) - g.POST("/import", a.importInbound) - g.POST("/onlines", a.onlines) - g.POST("/lastOnline", a.lastOnline) - g.POST("/updateClientTraffic/:email", a.updateClientTraffic) - g.POST("/:id/delClientByEmail/:email", a.delInboundClientByEmail) -} - -// getInbounds retrieves the list of inbounds for the logged-in user. -func (a *InboundController) getInbounds(c *gin.Context) { - user := session.GetLoginUser(c) - inbounds, err := a.inboundService.GetInbounds(user.Id) - if err != nil { - jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.obtain"), err) - return - } - jsonObj(c, inbounds, nil) -} - -// getInbound retrieves a specific inbound by its ID. -func (a *InboundController) getInbound(c *gin.Context) { - id, err := strconv.Atoi(c.Param("id")) - if err != nil { - jsonMsg(c, I18nWeb(c, "get"), err) - return - } - inbound, err := a.inboundService.GetInbound(id) - if err != nil { - jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.obtain"), err) - return - } - jsonObj(c, inbound, nil) -} - -// getClientTraffics retrieves client traffic information by email. -func (a *InboundController) getClientTraffics(c *gin.Context) { - email := c.Param("email") - clientTraffics, err := a.inboundService.GetClientTrafficByEmail(email) - if err != nil { - jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.trafficGetError"), err) - return - } - jsonObj(c, clientTraffics, nil) -} - -// getClientTrafficsById retrieves client traffic information by inbound ID. -func (a *InboundController) getClientTrafficsById(c *gin.Context) { - id := c.Param("id") - clientTraffics, err := a.inboundService.GetClientTrafficByID(id) - if err != nil { - jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.trafficGetError"), err) - return - } - jsonObj(c, clientTraffics, nil) -} - -// addInbound creates a new inbound configuration. -func (a *InboundController) addInbound(c *gin.Context) { - inbound := &model.Inbound{} - err := c.ShouldBind(inbound) - if err != nil { - jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundCreateSuccess"), err) - return - } - user := session.GetLoginUser(c) - inbound.UserId = user.Id - if inbound.Listen == "" || inbound.Listen == "0.0.0.0" || inbound.Listen == "::" || inbound.Listen == "::0" { - inbound.Tag = fmt.Sprintf("inbound-%v", inbound.Port) - } else { - inbound.Tag = fmt.Sprintf("inbound-%v:%v", inbound.Listen, inbound.Port) - } - - inbound, needRestart, err := a.inboundService.AddInbound(inbound) - if err != nil { - jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err) - return - } - jsonMsgObj(c, I18nWeb(c, "pages.inbounds.toasts.inboundCreateSuccess"), inbound, nil) - if needRestart { - a.xrayService.SetToNeedRestart() - } - // Broadcast inbounds update via WebSocket - inbounds, _ := a.inboundService.GetInbounds(user.Id) - websocket.BroadcastInbounds(inbounds) -} - -// delInbound deletes an inbound configuration by its ID. -func (a *InboundController) delInbound(c *gin.Context) { - id, err := strconv.Atoi(c.Param("id")) - if err != nil { - jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundDeleteSuccess"), err) - return - } - needRestart, err := a.inboundService.DelInbound(id) - if err != nil { - jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err) - return - } - jsonMsgObj(c, I18nWeb(c, "pages.inbounds.toasts.inboundDeleteSuccess"), id, nil) - if needRestart { - a.xrayService.SetToNeedRestart() - } - // Broadcast inbounds update via WebSocket - user := session.GetLoginUser(c) - inbounds, _ := a.inboundService.GetInbounds(user.Id) - websocket.BroadcastInbounds(inbounds) -} - -// updateInbound updates an existing inbound configuration. -func (a *InboundController) updateInbound(c *gin.Context) { - id, err := strconv.Atoi(c.Param("id")) - if err != nil { - jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundUpdateSuccess"), err) - return - } - inbound := &model.Inbound{ - Id: id, - } - err = c.ShouldBind(inbound) - if err != nil { - jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundUpdateSuccess"), err) - return - } - inbound, needRestart, err := a.inboundService.UpdateInbound(inbound) - if err != nil { - jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err) - return - } - jsonMsgObj(c, I18nWeb(c, "pages.inbounds.toasts.inboundUpdateSuccess"), inbound, nil) - if needRestart { - a.xrayService.SetToNeedRestart() - } - // Broadcast inbounds update via WebSocket - user := session.GetLoginUser(c) - inbounds, _ := a.inboundService.GetInbounds(user.Id) - websocket.BroadcastInbounds(inbounds) -} - -// getClientIps retrieves the IP addresses associated with a client by email. -func (a *InboundController) getClientIps(c *gin.Context) { - email := c.Param("email") - - ips, err := a.inboundService.GetInboundClientIps(email) - if err != nil || ips == "" { - jsonObj(c, "No IP Record", nil) - return - } - - jsonObj(c, ips, nil) -} - -// clearClientIps clears the IP addresses for a client by email. -func (a *InboundController) clearClientIps(c *gin.Context) { - email := c.Param("email") - - err := a.inboundService.ClearClientIps(email) - if err != nil { - jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.updateSuccess"), err) - return - } - jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.logCleanSuccess"), nil) -} - -// addInboundClient adds a new client to an existing inbound. -func (a *InboundController) addInboundClient(c *gin.Context) { - data := &model.Inbound{} - err := c.ShouldBind(data) - if err != nil { - jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundUpdateSuccess"), err) - return - } - - needRestart, err := a.inboundService.AddInboundClient(data) - if err != nil { - jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err) - return - } - jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundClientAddSuccess"), nil) - if needRestart { - a.xrayService.SetToNeedRestart() - } -} - -// delInboundClient deletes a client from an inbound by inbound ID and client ID. -func (a *InboundController) delInboundClient(c *gin.Context) { - id, err := strconv.Atoi(c.Param("id")) - if err != nil { - jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundUpdateSuccess"), err) - return - } - clientId := c.Param("clientId") - - needRestart, err := a.inboundService.DelInboundClient(id, clientId) - if err != nil { - jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err) - return - } - jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundClientDeleteSuccess"), nil) - if needRestart { - a.xrayService.SetToNeedRestart() - } -} - -// updateInboundClient updates a client's configuration in an inbound. -func (a *InboundController) updateInboundClient(c *gin.Context) { - clientId := c.Param("clientId") - - inbound := &model.Inbound{} - err := c.ShouldBind(inbound) - if err != nil { - jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundUpdateSuccess"), err) - return - } - - needRestart, err := a.inboundService.UpdateInboundClient(inbound, clientId) - if err != nil { - jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err) - return - } - jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundClientUpdateSuccess"), nil) - if needRestart { - a.xrayService.SetToNeedRestart() - } -} - -// resetClientTraffic resets the traffic counter for a specific client in an inbound. -func (a *InboundController) resetClientTraffic(c *gin.Context) { - id, err := strconv.Atoi(c.Param("id")) - if err != nil { - jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundUpdateSuccess"), err) - return - } - email := c.Param("email") - - needRestart, err := a.inboundService.ResetClientTraffic(id, email) - if err != nil { - jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err) - return - } - jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.resetInboundClientTrafficSuccess"), nil) - if needRestart { - a.xrayService.SetToNeedRestart() - } -} - -// resetAllTraffics resets all traffic counters across all inbounds. -func (a *InboundController) resetAllTraffics(c *gin.Context) { - err := a.inboundService.ResetAllTraffics() - if err != nil { - jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err) - return - } else { - a.xrayService.SetToNeedRestart() - } - jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.resetAllTrafficSuccess"), nil) -} - -// resetAllClientTraffics resets traffic counters for all clients in a specific inbound. -func (a *InboundController) resetAllClientTraffics(c *gin.Context) { - id, err := strconv.Atoi(c.Param("id")) - if err != nil { - jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundUpdateSuccess"), err) - return - } - - err = a.inboundService.ResetAllClientTraffics(id) - if err != nil { - jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err) - return - } else { - a.xrayService.SetToNeedRestart() - } - jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.resetAllClientTrafficSuccess"), nil) -} - -// importInbound imports an inbound configuration from provided data. -func (a *InboundController) importInbound(c *gin.Context) { - inbound := &model.Inbound{} - err := json.Unmarshal([]byte(c.PostForm("data")), inbound) - if err != nil { - jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err) - return - } - user := session.GetLoginUser(c) - inbound.Id = 0 - inbound.UserId = user.Id - if inbound.Listen == "" || inbound.Listen == "0.0.0.0" || inbound.Listen == "::" || inbound.Listen == "::0" { - inbound.Tag = fmt.Sprintf("inbound-%v", inbound.Port) - } else { - inbound.Tag = fmt.Sprintf("inbound-%v:%v", inbound.Listen, inbound.Port) - } - - for index := range inbound.ClientStats { - inbound.ClientStats[index].Id = 0 - inbound.ClientStats[index].Enable = true - } - - needRestart := false - inbound, needRestart, err = a.inboundService.AddInbound(inbound) - jsonMsgObj(c, I18nWeb(c, "pages.inbounds.toasts.inboundCreateSuccess"), inbound, err) - if err == nil && needRestart { - a.xrayService.SetToNeedRestart() - } -} - -// delDepletedClients deletes clients in an inbound who have exhausted their traffic limits. -func (a *InboundController) delDepletedClients(c *gin.Context) { - id, err := strconv.Atoi(c.Param("id")) - if err != nil { - jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundUpdateSuccess"), err) - return - } - err = a.inboundService.DelDepletedClients(id) - if err != nil { - jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err) - return - } - jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.delDepletedClientsSuccess"), nil) -} - -// onlines retrieves the list of currently online clients. -func (a *InboundController) onlines(c *gin.Context) { - jsonObj(c, a.inboundService.GetOnlineClients(), nil) -} - -// lastOnline retrieves the last online timestamps for clients. -func (a *InboundController) lastOnline(c *gin.Context) { - data, err := a.inboundService.GetClientsLastOnline() - jsonObj(c, data, err) -} - -// updateClientTraffic updates the traffic statistics for a client by email. -func (a *InboundController) updateClientTraffic(c *gin.Context) { - email := c.Param("email") - - // Define the request structure for traffic update - type TrafficUpdateRequest struct { - Upload int64 `json:"upload"` - Download int64 `json:"download"` - } - - var request TrafficUpdateRequest - err := c.ShouldBindJSON(&request) - if err != nil { - jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundUpdateSuccess"), err) - return - } - - err = a.inboundService.UpdateClientTrafficByEmail(email, request.Upload, request.Download) - if err != nil { - jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err) - return - } - - jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundClientUpdateSuccess"), nil) -} - -// delInboundClientByEmail deletes a client from an inbound by email address. -func (a *InboundController) delInboundClientByEmail(c *gin.Context) { - inboundId, err := strconv.Atoi(c.Param("id")) - if err != nil { - jsonMsg(c, "Invalid inbound ID", err) - return - } - - email := c.Param("email") - needRestart, err := a.inboundService.DelInboundClientByEmail(inboundId, email) - if err != nil { - jsonMsg(c, "Failed to delete client by email", err) - return - } - - jsonMsg(c, "Client deleted successfully", nil) - if needRestart { - a.xrayService.SetToNeedRestart() - } -} diff --git a/3x_xui.go b/3x_xui.go deleted file mode 100644 index 5150290..0000000 --- a/3x_xui.go +++ /dev/null @@ -1,54 +0,0 @@ -package controller - -import ( - "github.com/gin-gonic/gin" -) - -// XUIController is the main controller for the X-UI panel, managing sub-controllers. -type XUIController struct { - BaseController - - settingController *SettingController - xraySettingController *XraySettingController -} - -// NewXUIController creates a new XUIController and initializes its routes. -func NewXUIController(g *gin.RouterGroup) *XUIController { - a := &XUIController{} - a.initRouter(g) - return a -} - -// initRouter sets up the main panel routes and initializes sub-controllers. -func (a *XUIController) initRouter(g *gin.RouterGroup) { - g = g.Group("/panel") - g.Use(a.checkLogin) - - g.GET("/", a.index) - g.GET("/inbounds", a.inbounds) - g.GET("/settings", a.settings) - g.GET("/xray", a.xraySettings) - - a.settingController = NewSettingController(g) - a.xraySettingController = NewXraySettingController(g) -} - -// index renders the main panel index page. -func (a *XUIController) index(c *gin.Context) { - html(c, "index.html", "pages.index.title", nil) -} - -// inbounds renders the inbounds management page. -func (a *XUIController) inbounds(c *gin.Context) { - html(c, "inbounds.html", "pages.inbounds.title", nil) -} - -// settings renders the settings management page. -func (a *XUIController) settings(c *gin.Context) { - html(c, "settings.html", "pages.settings.title", nil) -} - -// xraySettings renders the Xray settings page. -func (a *XUIController) xraySettings(c *gin.Context) { - html(c, "xray.html", "pages.xray.title", nil) -} diff --git a/android-app/.gitignore b/android-app/.gitignore deleted file mode 100644 index aa724b7..0000000 --- a/android-app/.gitignore +++ /dev/null @@ -1,15 +0,0 @@ -*.iml -.gradle -/local.properties -/.idea/caches -/.idea/libraries -/.idea/modules.xml -/.idea/workspace.xml -/.idea/navEditor.xml -/.idea/assetWizardSettings.xml -.DS_Store -/build -/captures -.externalNativeBuild -.cxx -local.properties diff --git a/android-app/app/.gitignore b/android-app/app/.gitignore deleted file mode 100644 index 42afabf..0000000 --- a/android-app/app/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/android-app/app/build.gradle b/android-app/app/build.gradle deleted file mode 100644 index 9479e51..0000000 --- a/android-app/app/build.gradle +++ /dev/null @@ -1,45 +0,0 @@ -plugins { - id 'com.android.application' - id 'org.jetbrains.kotlin.android' -} - -android { - namespace 'com.flux' - compileSdk 34 - - defaultConfig { - applicationId "com.flux" - minSdk 24 - targetSdk 34 - versionCode 1 - versionName "1.0.1" - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - kotlinOptions { - jvmTarget = '1.8' - } -} - -dependencies { - - implementation 'androidx.core:core-ktx:1.9.0' - implementation 'androidx.appcompat:appcompat:1.7.1' - implementation 'com.google.android.material:material:1.12.0' - implementation 'androidx.constraintlayout:constraintlayout:2.2.1' - implementation 'androidx.webkit:webkit:1.8.0' - testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test.ext:junit:1.3.0' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.7.0' -} \ No newline at end of file diff --git a/android-app/app/proguard-rules.pro b/android-app/app/proguard-rules.pro deleted file mode 100644 index 481bb43..0000000 --- a/android-app/app/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/android-app/app/release/flux.apk b/android-app/app/release/flux.apk deleted file mode 100644 index a04c67f..0000000 Binary files a/android-app/app/release/flux.apk and /dev/null differ diff --git a/android-app/app/release/output-metadata.json b/android-app/app/release/output-metadata.json deleted file mode 100644 index 0b32f7f..0000000 --- a/android-app/app/release/output-metadata.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "version": 3, - "artifactType": { - "type": "APK", - "kind": "Directory" - }, - "applicationId": "com.flux", - "variantName": "release", - "elements": [ - { - "type": "SINGLE", - "filters": [], - "attributes": [], - "versionCode": 1, - "versionName": "1.0.1", - "outputFile": "app-release.apk" - } - ], - "elementType": "File" -} \ No newline at end of file diff --git a/android-app/app/src/androidTest/java/com/flux/ExampleInstrumentedTest.kt b/android-app/app/src/androidTest/java/com/flux/ExampleInstrumentedTest.kt deleted file mode 100644 index 1f124ec..0000000 --- a/android-app/app/src/androidTest/java/com/flux/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.flux - -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.ext.junit.runners.AndroidJUnit4 - -import org.junit.Test -import org.junit.runner.RunWith - -import org.junit.Assert.* - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("com.flux", appContext.packageName) - } -} \ No newline at end of file diff --git a/android-app/app/src/main/AndroidManifest.xml b/android-app/app/src/main/AndroidManifest.xml deleted file mode 100644 index 952f2e9..0000000 --- a/android-app/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/android-app/app/src/main/ic_launcher-playstore.png b/android-app/app/src/main/ic_launcher-playstore.png deleted file mode 100644 index 80c80a0..0000000 Binary files a/android-app/app/src/main/ic_launcher-playstore.png and /dev/null differ diff --git a/android-app/app/src/main/java/com/flux/MainActivity.kt b/android-app/app/src/main/java/com/flux/MainActivity.kt deleted file mode 100644 index c766e9a..0000000 --- a/android-app/app/src/main/java/com/flux/MainActivity.kt +++ /dev/null @@ -1,214 +0,0 @@ -package com.flux - -import androidx.core.content.edit -import android.graphics.Color -import android.os.Build -import android.os.Bundle -import android.view.View -import android.view.WindowInsetsController -import androidx.appcompat.app.AppCompatActivity -import android.webkit.* -import android.content.res.Configuration -import org.json.JSONArray -import org.json.JSONObject - - -class MainActivity : AppCompatActivity() { - - private lateinit var webView: WebView - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) - - webView = findViewById(R.id.webView) - WebView.setWebContentsDebuggingEnabled(true) - setupWebView() - loadUrl() - - setupSystemBars() - setupRootBackground() - } - - private fun setupWebView() { - val settings = webView.settings - settings.javaScriptEnabled = true - settings.domStorageEnabled = true - settings.databaseEnabled = true - settings.loadWithOverviewMode = true - settings.useWideViewPort = true - settings.setSupportZoom(true) - settings.builtInZoomControls = true - settings.displayZoomControls = false - - // 隐藏滚动条但不影响滚动 - webView.isVerticalScrollBarEnabled = false - webView.isHorizontalScrollBarEnabled = false - webView.scrollBarStyle = View.SCROLLBARS_INSIDE_OVERLAY - webView.overScrollMode = View.OVER_SCROLL_NEVER - - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - settings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW - } - - val isDarkTheme = resources.configuration.uiMode and - Configuration.UI_MODE_NIGHT_MASK == - Configuration.UI_MODE_NIGHT_YES - - webView.setBackgroundColor(if (isDarkTheme) Color.BLACK else Color.WHITE) - - - // 添加JavaScript接口 - webView.addJavascriptInterface(object { - - @android.webkit.JavascriptInterface - fun getPanelAddresses(callback: String) { - val sharedPrefs = getSharedPreferences("panel_config", MODE_PRIVATE) - val addresses = sharedPrefs.getString("panel_addresses", "[]") - webView.post { - webView.evaluateJavascript("window.$callback($addresses);", null) - } - } - - @android.webkit.JavascriptInterface - fun savePanelAddress(name: String, address: String) { - val json = JSONObject() - json.put("name", name) - json.put("address", address) - json.put("inx", false) - val sharedPrefs = getSharedPreferences("panel_config", MODE_PRIVATE) - val addresses = sharedPrefs.getString("panel_addresses", "[]") - val jsonArray = JSONArray(addresses) - jsonArray.put(json) - sharedPrefs.edit { - putString("panel_addresses", jsonArray.toString()) - } - webView.post { - webView.evaluateJavascript("window.setPanelAddresses(${jsonArray.toString()});", null) - } - } - - @android.webkit.JavascriptInterface - fun setCurrentPanelAddress(name: String) { - - val sharedPrefs = getSharedPreferences("panel_config", MODE_PRIVATE) - val addresses = sharedPrefs.getString("panel_addresses", "[]") - val jsonArray = JSONArray(addresses) - for (i in 0 until jsonArray.length()) { - val obj = jsonArray.getJSONObject(i) - if (name == obj.getString("name")) { - obj.put("inx", true) - }else{ - obj.put("inx", false) - } - } - sharedPrefs.edit { - putString("panel_addresses", jsonArray.toString()) - } - webView.post { - webView.evaluateJavascript("window.setPanelAddresses(${jsonArray.toString()});", null) - } - } - - @android.webkit.JavascriptInterface - fun deletePanelAddress(name: String) { - val jsonArray = JSONArray() - val sharedPrefs = getSharedPreferences("panel_config", MODE_PRIVATE) - val addresses = sharedPrefs.getString("panel_addresses", "[]") - val jsonArraya = JSONArray(addresses) - for (i in 0 until jsonArraya.length()) { - val obj = jsonArraya.getJSONObject(i) - if (name != obj.getString("name")) { - jsonArray.put(obj) - } - } - sharedPrefs.edit { - putString("panel_addresses", jsonArray.toString()) - } - webView.post { - webView.evaluateJavascript("window.setPanelAddresses(${jsonArray.toString()});", null) - } - } - }, "JsInterface") - - } - - private fun loadUrl() { - //webView.loadUrl("http://192.168.100.9:3000") - webView.loadUrl("file:///android_asset/index.html") - } - - private fun setupSystemBars() { - val isDarkTheme = resources.configuration.uiMode and - Configuration.UI_MODE_NIGHT_MASK == - Configuration.UI_MODE_NIGHT_YES - - if (isDarkTheme) { - window.statusBarColor = Color.BLACK - window.navigationBarColor = Color.BLACK - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - window.insetsController?.setSystemBarsAppearance( - 0, - WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS or - WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS - ) - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - @Suppress("DEPRECATION") - window.decorView.systemUiVisibility = - window.decorView.systemUiVisibility and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - @Suppress("DEPRECATION") - window.decorView.systemUiVisibility = - window.decorView.systemUiVisibility and View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.inv() - } - } - } else { - window.statusBarColor = Color.WHITE - window.navigationBarColor = Color.WHITE - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - window.insetsController?.setSystemBarsAppearance( - WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS or - WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS, - WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS or - WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS - ) - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - @Suppress("DEPRECATION") - window.decorView.systemUiVisibility = - window.decorView.systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - @Suppress("DEPRECATION") - window.decorView.systemUiVisibility = - window.decorView.systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR - } - } - } - } - - override fun onBackPressed() { - if (webView.canGoBack()) webView.goBack() else super.onBackPressed() - } - - override fun onConfigurationChanged(newConfig: Configuration) { - super.onConfigurationChanged(newConfig) - setupSystemBars() - setupWebViewBackground() - setupRootBackground() - } - - private fun setupWebViewBackground() { - val isDarkTheme = resources.configuration.uiMode and - Configuration.UI_MODE_NIGHT_MASK == - Configuration.UI_MODE_NIGHT_YES - webView.setBackgroundColor(if (isDarkTheme) Color.BLACK else Color.WHITE) - } - - private fun setupRootBackground() { - val isDarkTheme = resources.configuration.uiMode and - Configuration.UI_MODE_NIGHT_MASK == - Configuration.UI_MODE_NIGHT_YES - val rootView = findViewById(android.R.id.content) - rootView.setBackgroundColor(if (isDarkTheme) Color.BLACK else Color.WHITE) - } -} diff --git a/android-app/app/src/main/res/drawable/ic_launcher_background.xml b/android-app/app/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index ca3826a..0000000 --- a/android-app/app/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/android-app/app/src/main/res/drawable/ic_launcher_foreground.xml b/android-app/app/src/main/res/drawable/ic_launcher_foreground.xml deleted file mode 100644 index 2b068d1..0000000 --- a/android-app/app/src/main/res/drawable/ic_launcher_foreground.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/android-app/app/src/main/res/layout/activity_main.xml b/android-app/app/src/main/res/layout/activity_main.xml deleted file mode 100644 index 7f11c64..0000000 --- a/android-app/app/src/main/res/layout/activity_main.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/android-app/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android-app/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index c4a603d..0000000 --- a/android-app/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/android-app/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android-app/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index c4a603d..0000000 --- a/android-app/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/android-app/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/android-app/app/src/main/res/mipmap-hdpi/ic_launcher.webp deleted file mode 100644 index 3c965f7..0000000 Binary files a/android-app/app/src/main/res/mipmap-hdpi/ic_launcher.webp and /dev/null differ diff --git a/android-app/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp b/android-app/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp deleted file mode 100644 index b0cc786..0000000 Binary files a/android-app/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp and /dev/null differ diff --git a/android-app/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/android-app/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp deleted file mode 100644 index 9c7cc2c..0000000 Binary files a/android-app/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp and /dev/null differ diff --git a/android-app/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/android-app/app/src/main/res/mipmap-mdpi/ic_launcher.webp deleted file mode 100644 index 15b2494..0000000 Binary files a/android-app/app/src/main/res/mipmap-mdpi/ic_launcher.webp and /dev/null differ diff --git a/android-app/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp b/android-app/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp deleted file mode 100644 index bdc56f3..0000000 Binary files a/android-app/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp and /dev/null differ diff --git a/android-app/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/android-app/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp deleted file mode 100644 index 39f430d..0000000 Binary files a/android-app/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp and /dev/null differ diff --git a/android-app/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/android-app/app/src/main/res/mipmap-xhdpi/ic_launcher.webp deleted file mode 100644 index 0a50157..0000000 Binary files a/android-app/app/src/main/res/mipmap-xhdpi/ic_launcher.webp and /dev/null differ diff --git a/android-app/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/android-app/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp deleted file mode 100644 index 1412240..0000000 Binary files a/android-app/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp and /dev/null differ diff --git a/android-app/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/android-app/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp deleted file mode 100644 index 80f2aef..0000000 Binary files a/android-app/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/android-app/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/android-app/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp deleted file mode 100644 index 6794017..0000000 Binary files a/android-app/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp and /dev/null differ diff --git a/android-app/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/android-app/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp deleted file mode 100644 index ebce376..0000000 Binary files a/android-app/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp and /dev/null differ diff --git a/android-app/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/android-app/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp deleted file mode 100644 index d3178ed..0000000 Binary files a/android-app/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/android-app/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/android-app/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp deleted file mode 100644 index 99e51c9..0000000 Binary files a/android-app/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and /dev/null differ diff --git a/android-app/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/android-app/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp deleted file mode 100644 index 1fee8d3..0000000 Binary files a/android-app/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp and /dev/null differ diff --git a/android-app/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/android-app/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp deleted file mode 100644 index 2f58dc7..0000000 Binary files a/android-app/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/android-app/app/src/main/res/values-night/themes.xml b/android-app/app/src/main/res/values-night/themes.xml deleted file mode 100644 index 6252187..0000000 --- a/android-app/app/src/main/res/values-night/themes.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - \ No newline at end of file diff --git a/android-app/app/src/main/res/values/colors.xml b/android-app/app/src/main/res/values/colors.xml deleted file mode 100644 index aacedae..0000000 --- a/android-app/app/src/main/res/values/colors.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - #FF000000 - #FFFFFFFF - - \ No newline at end of file diff --git a/android-app/app/src/main/res/values/strings.xml b/android-app/app/src/main/res/values/strings.xml deleted file mode 100644 index fc68ff2..0000000 --- a/android-app/app/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - Flux - \ No newline at end of file diff --git a/android-app/app/src/main/res/values/themes.xml b/android-app/app/src/main/res/values/themes.xml deleted file mode 100644 index 560737c..0000000 --- a/android-app/app/src/main/res/values/themes.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - -