diff options
author | RaindropsSys <contact@minteck.org> | 2023-08-14 21:26:07 +0200 |
---|---|---|
committer | RaindropsSys <contact@minteck.org> | 2023-08-14 21:26:07 +0200 |
commit | 04c6aef158f46f7aac3e71c7100cea952fed87e6 (patch) | |
tree | 478d3738ef56cba622041dd97f4f445068e57dff | |
download | ponyplay-04c6aef158f46f7aac3e71c7100cea952fed87e6.tar.gz ponyplay-04c6aef158f46f7aac3e71c7100cea952fed87e6.tar.bz2 ponyplay-04c6aef158f46f7aac3e71c7100cea952fed87e6.zip |
Initial commit
136 files changed, 2380 insertions, 0 deletions
diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 0000000..aa724b7 --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,15 @@ +*.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/.idea/.gitignore b/android/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/android/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/android/.idea/.name b/android/.idea/.name new file mode 100644 index 0000000..b778484 --- /dev/null +++ b/android/.idea/.name @@ -0,0 +1 @@ +App Additions
\ No newline at end of file diff --git a/android/.idea/compiler.xml b/android/.idea/compiler.xml new file mode 100644 index 0000000..b589d56 --- /dev/null +++ b/android/.idea/compiler.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="CompilerConfiguration"> + <bytecodeTargetLevel target="17" /> + </component> +</project>
\ No newline at end of file diff --git a/android/.idea/deploymentTargetDropDown.xml b/android/.idea/deploymentTargetDropDown.xml new file mode 100644 index 0000000..0c0c338 --- /dev/null +++ b/android/.idea/deploymentTargetDropDown.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="deploymentTargetDropDown"> + <value> + <entry key="app"> + <State /> + </entry> + </value> + </component> +</project>
\ No newline at end of file diff --git a/android/.idea/discord.xml b/android/.idea/discord.xml new file mode 100644 index 0000000..cf77f1e --- /dev/null +++ b/android/.idea/discord.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="DiscordProjectSettings"> + <option name="show" value="DISABLE" /> + <option name="description" value="" /> + </component> +</project>
\ No newline at end of file diff --git a/android/.idea/gradle.xml b/android/.idea/gradle.xml new file mode 100644 index 0000000..0897082 --- /dev/null +++ b/android/.idea/gradle.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="GradleMigrationSettings" migrationVersion="1" /> + <component name="GradleSettings"> + <option name="linkedExternalProjectsSettings"> + <GradleProjectSettings> + <option name="externalProjectPath" value="$PROJECT_DIR$" /> + <option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" /> + <option name="modules"> + <set> + <option value="$PROJECT_DIR$" /> + <option value="$PROJECT_DIR$/app" /> + </set> + </option> + <option name="resolveExternalAnnotations" value="false" /> + </GradleProjectSettings> + </option> + </component> +</project>
\ No newline at end of file diff --git a/android/.idea/kotlinc.xml b/android/.idea/kotlinc.xml new file mode 100644 index 0000000..217e5c5 --- /dev/null +++ b/android/.idea/kotlinc.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="KotlinJpsPluginSettings"> + <option name="version" value="1.8.21" /> + </component> +</project>
\ No newline at end of file diff --git a/android/.idea/migrations.xml b/android/.idea/migrations.xml new file mode 100644 index 0000000..f8051a6 --- /dev/null +++ b/android/.idea/migrations.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="ProjectMigrations"> + <option name="MigrateToGradleLocalJavaHome"> + <set> + <option value="$PROJECT_DIR$" /> + </set> + </option> + </component> +</project>
\ No newline at end of file diff --git a/android/.idea/misc.xml b/android/.idea/misc.xml new file mode 100644 index 0000000..8978d23 --- /dev/null +++ b/android/.idea/misc.xml @@ -0,0 +1,9 @@ +<project version="4"> + <component name="ExternalStorageConfigurationManager" enabled="true" /> + <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK"> + <output url="file://$PROJECT_DIR$/build/classes" /> + </component> + <component name="ProjectType"> + <option name="id" value="Android" /> + </component> +</project>
\ No newline at end of file diff --git a/android/app/.gitignore b/android/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/android/app/.gitignore @@ -0,0 +1 @@ +/build
\ No newline at end of file diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts new file mode 100644 index 0000000..ab64399 --- /dev/null +++ b/android/app/build.gradle.kts @@ -0,0 +1,52 @@ +@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed +plugins { + alias(libs.plugins.androidApplication) + alias(libs.plugins.kotlinAndroid) +} + +android { + namespace = "dev.equestria.ponyplay" + compileSdk = 33 + + defaultConfig { + applicationId = "dev.equestria.ponyplay" + minSdk = 33 + targetSdk = 33 + versionCode = 1 + versionName = "1.0.0" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + buildFeatures { + buildConfig = true + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + implementation(libs.okhttp) + implementation(libs.volley) + implementation(libs.core.ktx) + implementation(libs.appcompat) + implementation(libs.material) + implementation(libs.constraintlayout) + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.test.ext.junit) + androidTestImplementation(libs.espresso.core) +}
\ No newline at end of file diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/android/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# 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/release/app-release.apk b/android/app/release/app-release.apk Binary files differnew file mode 100644 index 0000000..9a04caf --- /dev/null +++ b/android/app/release/app-release.apk diff --git a/android/app/release/output-metadata.json b/android/app/release/output-metadata.json new file mode 100644 index 0000000..8f082d7 --- /dev/null +++ b/android/app/release/output-metadata.json @@ -0,0 +1,20 @@ +{ + "version": 3, + "artifactType": { + "type": "APK", + "kind": "Directory" + }, + "applicationId": "dev.equestria.phoneadditions", + "variantName": "release", + "elements": [ + { + "type": "SINGLE", + "filters": [], + "attributes": [], + "versionCode": 5, + "versionName": "2.1.0-phone", + "outputFile": "app-release.apk" + } + ], + "elementType": "File" +}
\ No newline at end of file diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..26dc29c --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools"> + + <uses-feature + android:name="android.hardware.camera" + android:required="false" /> + + <uses-permission android:name="android.permission.WAKE_LOCK" /> + <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.RECORD_AUDIO" /> + <uses-permission android:name="android.permission.CAMERA" /> + <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> + <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> + <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> + <uses-permission android:name="android.permission.WRITE_SETTINGS" + tools:ignore="ProtectedPermissions" /> + + <application + android:allowBackup="true" + android:dataExtractionRules="@xml/data_extraction_rules" + android:fullBackupContent="@xml/backup_rules" + android:icon="@mipmap/ic_launcher" + android:hardwareAccelerated="true" + android:label="@string/app_name" + android:supportsRtl="false" + android:theme="@style/Theme.Ponyplay" + tools:targetApi="31"> + <activity + android:name=".MainActivity" + android:label="Ponyplay" + android:icon="@mipmap/ic_launcher" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + +</manifest>
\ No newline at end of file diff --git a/android/app/src/main/ic_app-playstore.png b/android/app/src/main/ic_app-playstore.png Binary files differnew file mode 100644 index 0000000..395a1d5 --- /dev/null +++ b/android/app/src/main/ic_app-playstore.png diff --git a/android/app/src/main/ic_launcher-playstore.png b/android/app/src/main/ic_launcher-playstore.png Binary files differnew file mode 100644 index 0000000..476b975 --- /dev/null +++ b/android/app/src/main/ic_launcher-playstore.png diff --git a/android/app/src/main/java/dev/equestria/ponyplay/JavaScriptExtensions.kt b/android/app/src/main/java/dev/equestria/ponyplay/JavaScriptExtensions.kt new file mode 100644 index 0000000..4ea2d95 --- /dev/null +++ b/android/app/src/main/java/dev/equestria/ponyplay/JavaScriptExtensions.kt @@ -0,0 +1,63 @@ +package dev.equestria.ponyplay + +import android.content.Intent +import android.provider.Settings +import android.webkit.JavascriptInterface +import android.webkit.WebView + + +class JavaScriptExtensions(originalActivity: MainActivity) { + private val activity: MainActivity = originalActivity + private var lightSensorValue: Int = 0 + + @JavascriptInterface + fun getSystemBrightness(): Int { + return Settings.System.getInt( + activity.baseContext.contentResolver, + Settings.System.SCREEN_BRIGHTNESS) + } + + @JavascriptInterface + fun getBrightness(): Float { + return activity.lightSensorValue + } + + @JavascriptInterface + fun setBrightness(brightness: Int): Int { + Settings.System.putInt( + activity.baseContext.contentResolver, + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL + ) + + Settings.System.putInt( + activity.baseContext.contentResolver, Settings.System.SCREEN_BRIGHTNESS, brightness + ) + + return Settings.System.getInt( + activity.baseContext.contentResolver, Settings.System.SCREEN_BRIGHTNESS + ) + } + + @JavascriptInterface + fun reloadWebView() { + val wv = activity.findViewById<WebView>(R.id.webview) + wv.reload() + } + + @JavascriptInterface + fun startApp(url: String) { + val i = Intent() + i.setAction("startApp") + i.putExtra("url", url) + activity.sendBroadcast(i) + } + + @JavascriptInterface + fun closeApp() { + val i = Intent() + i.setAction("closeApp") + activity.sendBroadcast(i) + } +} + diff --git a/android/app/src/main/java/dev/equestria/ponyplay/MainActivity.kt b/android/app/src/main/java/dev/equestria/ponyplay/MainActivity.kt new file mode 100644 index 0000000..9d7b663 --- /dev/null +++ b/android/app/src/main/java/dev/equestria/ponyplay/MainActivity.kt @@ -0,0 +1,237 @@ +package dev.equestria.ponyplay + +import android.annotation.SuppressLint +import android.app.Activity +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.content.pm.ActivityInfo +import android.graphics.Color +import android.hardware.Sensor +import android.hardware.SensorEvent +import android.hardware.SensorEventListener +import android.hardware.SensorManager +import android.os.Bundle +import android.util.Log +import android.view.Display +import android.view.View +import android.view.WindowManager +import android.webkit.GeolocationPermissions +import android.webkit.PermissionRequest +import android.webkit.WebChromeClient +import android.webkit.WebSettings +import android.webkit.WebView +import android.webkit.WebViewClient +import com.android.volley.Request +import com.android.volley.toolbox.JsonObjectRequest +import com.android.volley.toolbox.Volley +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import okhttp3.* +import kotlin.system.exitProcess + + +class MainActivity : SensorEventListener, Activity() { + private lateinit var mSensorManager: SensorManager + private lateinit var mLightSensor: Sensor + private lateinit var broadcastReceiver: BroadcastReceiver + var lightSensorValue: Float = 0f + val mainAppURL: String = "https://ponyplay-raindrops.equestria.dev:20443/app.html" + + override fun onSensorChanged(sensorEvent: SensorEvent) { + if (sensorEvent.sensor.type == Sensor.TYPE_LIGHT) { + Log.d("SensorTest", "" + sensorEvent.values[0]) + lightSensorValue = sensorEvent.values[0] + } + } + + @Deprecated("Deprecated in Java") + override fun onBackPressed() { + window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE + or View.SYSTEM_UI_FLAG_LAYOUT_STABLE + or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_FULLSCREEN) + } + + override fun onAccuracyChanged(sensor: Sensor?, i: Int) {} + + fun showError() { + MaterialAlertDialogBuilder(this).setCancelable(false) + .setTitle("Error") + .setMessage("Unable to communicate with your local Ponyplay Station.") + .setNegativeButton("Quit") { _, _ -> + this.moveTaskToBack(true) + exitProcess(0) + }.show() + } + + override fun onDestroy() { + unregisterReceiver(broadcastReceiver) + super.onDestroy() + } + + override fun onResume() { + window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE + or View.SYSTEM_UI_FLAG_LAYOUT_STABLE + or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_FULLSCREEN) + + super.onResume() + + mSensorManager.registerListener(this, mLightSensor, SensorManager.SENSOR_DELAY_NORMAL) + } + + override fun onWindowFocusChanged(hasFocus: Boolean) { + window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE + or View.SYSTEM_UI_FLAG_LAYOUT_STABLE + or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_FULLSCREEN) + + super.onWindowFocusChanged(hasFocus) + } + + @SuppressLint("SetJavaScriptEnabled") + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + // + // getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE) + setContentView(R.layout.activity_main) + + val intentFilter = IntentFilter() + intentFilter.addAction("startApp") + intentFilter.addAction("closeApp") + + broadcastReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + if (intent.action == "startApp") { + val av = findViewById<WebView>(R.id.appview) + av.visibility = View.VISIBLE + + val webSettings = av.settings + webSettings.javaScriptEnabled = true + webSettings.cacheMode = WebSettings.LOAD_NO_CACHE + webSettings.domStorageEnabled = true + webSettings.allowFileAccess = true + webSettings.allowContentAccess = true + webSettings.safeBrowsingEnabled = false + + webSettings.allowUniversalAccessFromFileURLs = true + webSettings.allowFileAccessFromFileURLs = true + + webSettings.mediaPlaybackRequiresUserGesture = false + webSettings.userAgentString = "Mozilla/5.0 (Linux; Ponyplay; main) AppleWebKit/" + webSettings.userAgentString.split("AppleWebKit/")[1] + + av.webViewClient = object : WebViewClient() { + @Deprecated("Deprecated in Java") + override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean { + av.loadUrl(url) + return true + } + } + + av.setLayerType(View.LAYER_TYPE_HARDWARE, null) + + intent.getStringExtra("url")?.let { av.loadUrl(it) } + } else if (intent.action == "closeApp") { + val av = findViewById<WebView>(R.id.appview) + av.visibility = View.GONE + av.loadUrl("about:blank") + } + } + } + + registerReceiver(broadcastReceiver, intentFilter, RECEIVER_NOT_EXPORTED) + + mSensorManager = getSystemService(SENSOR_SERVICE) as SensorManager + mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT) + + window.statusBarColor = Color.TRANSPARENT + window.navigationBarColor = Color.TRANSPARENT + + window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE + or View.SYSTEM_UI_FLAG_LAYOUT_STABLE + or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_FULLSCREEN) + this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) + + val wv = findViewById<WebView>(R.id.webview) + + val volleyQueue = Volley.newRequestQueue(baseContext) + + val jsonObjectRequest = JsonObjectRequest( + Request.Method.GET, "https://ponyplay-raindrops.equestria.dev:20443/availabilitycheck.txt", null, + + { response -> + Log.i("HTTPRequest", response.toString()) + + if (response.getString("status") == "OK") { + wv.clearCache(true) + wv.clearHistory() + + val webSettings = wv.settings + webSettings.javaScriptEnabled = true + webSettings.cacheMode = WebSettings.LOAD_NO_CACHE + webSettings.domStorageEnabled = true + webSettings.allowFileAccess = true + webSettings.allowContentAccess = true + webSettings.safeBrowsingEnabled = false + + webSettings.allowUniversalAccessFromFileURLs = true + webSettings.allowFileAccessFromFileURLs = true + + webSettings.mediaPlaybackRequiresUserGesture = false + webSettings.userAgentString = "Mozilla/5.0 (Linux; Ponyplay; main) AppleWebKit/" + webSettings.userAgentString.split("AppleWebKit/")[1] + + wv.loadUrl(mainAppURL) + wv.addJavascriptInterface(JavaScriptExtensions(this), "engine") + wv.setLayerType(View.LAYER_TYPE_HARDWARE, null) + + wv.webViewClient = object : WebViewClient() { + @Deprecated("Deprecated in Java") + override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean { + wv.loadUrl(url) + return true + } + } + + wv.webChromeClient = object : WebChromeClient() { + override fun onPermissionRequest(request: PermissionRequest) { + Log.d("WebView", "onPermissionRequest") + runOnUiThread { + Log.d("WebView", request.origin.toString()) + Log.d("WebView", "GRANTED") + request.grant(request.resources) + } + } + + override fun onGeolocationPermissionsShowPrompt( + origin: String?, + callback: GeolocationPermissions.Callback + ) { + callback.invoke(origin, true, false) + } + } + + WebView.setWebContentsDebuggingEnabled(false) + } else { + showError() + } + }, + + { error -> + showError() + Log.e("HTTPRequest", "Request error: ${error.localizedMessage}") + }) + + volleyQueue.add(jsonObjectRequest) + } +}
\ No newline at end of file diff --git a/android/app/src/main/res/drawable/ic_launcher_foreground.xml b/android/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..9ea0ceb --- /dev/null +++ b/android/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,15 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="108dp" + android:height="108dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="#000000"> + <group android:scaleX="0.58" + android:scaleY="0.58" + android:translateX="5.04" + android:translateY="5.04"> + <path + android:pathData="M17.6,11.48 L19.44,8.3a0.63,0.63 0,0 0,-1.09 -0.63l-1.88,3.24a11.43,11.43 0,0 0,-8.94 0L5.65,7.67a0.63,0.63 0,0 0,-1.09 0.63L6.4,11.48A10.81,10.81 0,0 0,1 20L23,20A10.81,10.81 0,0 0,17.6 11.48ZM7,17.25A1.25,1.25 0,1 1,8.25 16,1.25 1.25,0 0,1 7,17.25ZM17,17.25A1.25,1.25 0,1 1,18.25 16,1.25 1.25,0 0,1 17,17.25Z" + android:fillColor="#FF000000"/> + </group> +</vector>
\ No newline at end of file diff --git a/android/app/src/main/res/layout/activity_main.xml b/android/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..32c5989 --- /dev/null +++ b/android/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:theme="@style/Base.Theme.Ponyplay" + android:configChanges="orientation" + android:screenOrientation="landscape" + tools:context=".MainActivity"> + + <LinearLayout + android:id="@+id/linearLayout" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <WebView + android:id="@+id/webview" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="10" + android:hardwareAccelerated="true"> + + </WebView> + + <WebView + android:id="@+id/appview" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="90" + android:visibility="gone" /> + + </LinearLayout> + +</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..7353dbd --- /dev/null +++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@color/ic_launcher_background"/> + <foreground android:drawable="@drawable/ic_launcher_foreground"/> +</adaptive-icon>
\ No newline at end of file diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..7353dbd --- /dev/null +++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@color/ic_launcher_background"/> + <foreground android:drawable="@drawable/ic_launcher_foreground"/> +</adaptive-icon>
\ No newline at end of file diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp Binary files differnew file mode 100644 index 0000000..b30f614 --- /dev/null +++ b/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp b/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp Binary files differnew file mode 100644 index 0000000..fbfe3aa --- /dev/null +++ b/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp Binary files differnew file mode 100644 index 0000000..b30f614 --- /dev/null +++ b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp Binary files differnew file mode 100644 index 0000000..ec87dd6 --- /dev/null +++ b/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp b/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp Binary files differnew file mode 100644 index 0000000..9668e99 --- /dev/null +++ b/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp Binary files differnew file mode 100644 index 0000000..ec87dd6 --- /dev/null +++ b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp Binary files differnew file mode 100644 index 0000000..9b84016 --- /dev/null +++ b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp Binary files differnew file mode 100644 index 0000000..edc6bd5 --- /dev/null +++ b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp Binary files differnew file mode 100644 index 0000000..9b84016 --- /dev/null +++ b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp Binary files differnew file mode 100644 index 0000000..737647c --- /dev/null +++ b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp Binary files differnew file mode 100644 index 0000000..d4a60ea --- /dev/null +++ b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp Binary files differnew file mode 100644 index 0000000..737647c --- /dev/null +++ b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp Binary files differnew file mode 100644 index 0000000..5c74df5 --- /dev/null +++ b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp Binary files differnew file mode 100644 index 0000000..71545d9 --- /dev/null +++ b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp Binary files differnew file mode 100644 index 0000000..5c74df5 --- /dev/null +++ b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..c8524cd --- /dev/null +++ b/android/app/src/main/res/values/colors.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <color name="black">#FF000000</color> + <color name="white">#FFFFFFFF</color> +</resources>
\ No newline at end of file diff --git a/android/app/src/main/res/values/ic_launcher_background.xml b/android/app/src/main/res/values/ic_launcher_background.xml new file mode 100644 index 0000000..c5d5899 --- /dev/null +++ b/android/app/src/main/res/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <color name="ic_launcher_background">#FFFFFF</color> +</resources>
\ No newline at end of file diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..69a66f4 --- /dev/null +++ b/android/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ +<resources> + <string name="app_name">Equestria.dev Ponyplay</string> +</resources>
\ No newline at end of file diff --git a/android/app/src/main/res/values/themes.xml b/android/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..12e97e4 --- /dev/null +++ b/android/app/src/main/res/values/themes.xml @@ -0,0 +1,10 @@ +<resources xmlns:tools="http://schemas.android.com/tools"> + <!-- Base application theme. --> + <style name="Base.Theme.Ponyplay" parent="Theme.Material3.Dark.NoActionBar"> + <!-- Customize your light theme here. --> + <!-- <item name="colorPrimary">@color/my_light_primary</item> --> + <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item> + </style> + + <style name="Theme.Ponyplay" parent="Base.Theme.Ponyplay" /> +</resources>
\ No newline at end of file diff --git a/android/app/src/main/res/xml/backup_rules.xml b/android/app/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..fa0f996 --- /dev/null +++ b/android/app/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + Sample backup rules file; uncomment and customize as necessary. + See https://developer.android.com/guide/topics/data/autobackup + for details. + Note: This file is ignored for devices older that API 31 + See https://developer.android.com/about/versions/12/backup-restore +--> +<full-backup-content> + <!-- + <include domain="sharedpref" path="."/> + <exclude domain="sharedpref" path="device.xml"/> +--> +</full-backup-content>
\ No newline at end of file diff --git a/android/app/src/main/res/xml/data_extraction_rules.xml b/android/app/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..9ee9997 --- /dev/null +++ b/android/app/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + Sample data extraction rules file; uncomment and customize as necessary. + See https://developer.android.com/about/versions/12/backup-restore#xml-changes + for details. +--> +<data-extraction-rules> + <cloud-backup> + <!-- TODO: Use <include> and <exclude> to control what is backed up. + <include .../> + <exclude .../> + --> + </cloud-backup> + <!-- + <device-transfer> + <include .../> + <exclude .../> + </device-transfer> + --> +</data-extraction-rules>
\ No newline at end of file diff --git a/android/build.gradle.kts b/android/build.gradle.kts new file mode 100644 index 0000000..20d87a7 --- /dev/null +++ b/android/build.gradle.kts @@ -0,0 +1,7 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed +plugins { + alias(libs.plugins.androidApplication) apply false + alias(libs.plugins.kotlinAndroid) apply false +} +true // Needed to make the Suppress annotation work for the plugins block
\ No newline at end of file diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000..3c5031e --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,23 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true
\ No newline at end of file diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml new file mode 100644 index 0000000..0235b33 --- /dev/null +++ b/android/gradle/libs.versions.toml @@ -0,0 +1,28 @@ +[versions] +agp = "8.2.0-alpha14" +kotlin = "1.8.21" +core-ktx = "1.10.1" +junit = "4.13.2" +androidx-test-ext-junit = "1.1.5" +espresso-core = "3.5.1" +appcompat = "1.6.1" +material = "1.9.0" +constraintlayout = "2.1.4" +okhttp = "4.10.0" +volley = "1.2.1" + +[libraries] +core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" } +junit = { group = "junit", name = "junit", version.ref = "junit" } +androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-ext-junit" } +espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-core" } +appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } +material = { group = "com.google.android.material", name = "material", version.ref = "material" } +constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } +okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" } +volley = { module = "com.android.volley:volley", version.ref = "volley" } + +[plugins] +androidApplication = { id = "com.android.application", version.ref = "agp" } +kotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } + diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar Binary files differnew file mode 100644 index 0000000..e708b1c --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.jar diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..da3c56c --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Aug 09 22:22:46 CEST 2023 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/android/gradlew b/android/gradlew new file mode 100755 index 0000000..4f906e0 --- /dev/null +++ b/android/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/android/gradlew.bat b/android/gradlew.bat new file mode 100644 index 0000000..ac1b06f --- /dev/null +++ b/android/gradlew.bat @@ -0,0 +1,89 @@ +@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/android/logo.png b/android/logo.png Binary files differnew file mode 100644 index 0000000..81a5e45 --- /dev/null +++ b/android/logo.png diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts new file mode 100644 index 0000000..bb6fd36 --- /dev/null +++ b/android/settings.gradle.kts @@ -0,0 +1,19 @@ +import java.net.URI + +pluginManagement { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +rootProject.name = "App Additions" +include(":app") diff --git a/android/themed.svg b/android/themed.svg new file mode 100644 index 0000000..f7a7634 --- /dev/null +++ b/android/themed.svg @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 27.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 139.7 139.7" style="enable-background:new 0 0 139.7 139.7;" xml:space="preserve"> +<style type="text/css"> + .st0{opacity:0.25;enable-background:new ;} + .st1{opacity:0.5;enable-background:new ;} +</style> +<path class="st0" d="M85.9,29.8C78.5,30.6,71.8,34,71.1,40c-0.2,2-1.5,10.8-3.6,11.6c-10.6,3.8-19.8,20-19.9,26.8 + c-2,15.3-3.6,20.9,1.2,31L68.9,79c2.2-5.6,5.3-10.6,7-16.5c0.5-1.9,1-4.5,1.9-5.8c0.9-1.5,2.4-2.5,4.2-4.5c1.2-1.3,3-4.2,4.5-6.6 + c2.9-4.9,3.9-7.1,5.6-10.3C92.3,33.4,90.2,29.3,85.9,29.8z M60,72.6c-1,1.7-2.9,5.1-3.9,6.7c-0.5,0.9-0.9,2.2-1.2,3.2 + c-0.3-1.2-0.3-3,0.6-4.6l0,0c0.7-0.9,2-2.7,3-3.9c2.1-2.9,8.2-12,9.5-12.8C66.8,62.4,61.9,69.5,60,72.6z"/> +<path d="M56.3,75c-3.1,3.4-3.5,10-2.4,14.3C57.7,76,66,65.7,72.3,57.1C66.5,59.4,61.1,69.5,56.3,75z M60,72.6 + c-1,1.7-2.9,5.1-3.9,6.7c-0.5,0.9-0.9,2.2-1.2,3.2c-0.3-1.2-0.3-3,0.6-4.6l0,0c0.7-0.9,2-2.7,3-3.9c2.1-2.9,8.2-12,9.5-12.8 + C66.8,62.4,61.9,69.5,60,72.6z"/> +<path d="M72.3,57.1L72.3,57.1L72.3,57.1z"/> +<path id="path4677" d="M81.9,35c0.1,0.9,0,1.1,0.3,1.4c1.2,0.1,3-0.8,4.2-1.2c-0.6,0.9-2.1,2.1-3.5,3.3c-0.2,0.5-0.1,0.8-0.1,1.2 + c-1-0.1-2.1,0.3-2.6,0.6c0.4,0.3,1.7,3.5,1.2,5.2c-0.4-0.4-1.7-3.9-2.7-4.2c-0.6,1.5-0.4,5-1.2,6.8c-1-1-1-3.9-0.4-5.5 + c-1.2,0.4-2.2,1.2-3.4,1.2c1.2-1.5,2.6-2.3,3.9-3.5c0-0.5-0.3-0.6-0.9-0.9c1.2-0.4,2.4-1,2.3-2.7c0.4,0.3,0.8,0.6,1.2,0.7 + C81.1,36.6,81.6,35.6,81.9,35L81.9,35z"/> +<path d="M86.7,29.2C80.6,29.4,76.1,30.6,73,34c-2.2,2.6-3.1,6-3.6,9.9c-0.9,6.4-0.9,6.2-5.5,8.9c-4.3,2.6-8.2,7.1-11.2,10.7 + c-6.5,7.8-11.9,33-4.9,46.9l0,0l2.1-2.3c-6.8-7.7-2.1-28.1,0.3-35.2c2.2-6.6,7.9-13.3,13.7-17.6c6.8-5,6-3.8,7.2-9.7 + c0.7-3.7,1.5-8,3.6-10.2c2.7-2.9,6.8-5,12.1-4.9L86.7,29.2z"/> +<path id="path3177" d="M77.6,56.5c4.2-2,11.4-11.3,14.4-21.2c-1.2,1.5-2.9,3.9-4.4,7C84.5,48.7,78.7,54.9,77.6,56.5z"/> +<path d="M54.5,97.2c-7.9,9.6-6.3,12.7-5.7,12.3c1.8-1.8,4.5-6.6,8.1-11.2c8.9-11.5,18.7-28.7,20.3-40.4 + C71.5,75.6,60.1,90.2,54.5,97.2z"/> +<path id="path4334_00000171706154884340887790000012676028719422921878_" class="st1" d="M55.6,77.9c-0.9,1.6-0.9,3.5-0.6,4.6 + c0.3-1,0.6-2.2,1.2-3.2c0.9-1.6,2.7-5,3.9-6.7c2-3.1,6.8-10.2,8.2-11.5c-1.4,0.8-7.5,9.9-9.5,12.8C57.6,75.2,56.3,77,55.6,77.9 + L55.6,77.9z"/> +</svg> diff --git a/web/.idea/.gitignore b/web/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/web/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/web/.idea/deployment.xml b/web/.idea/deployment.xml new file mode 100644 index 0000000..924f11d --- /dev/null +++ b/web/.idea/deployment.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="PublishConfigData" autoUpload="Always" serverName="maretimebay" confirmBeforeDeletion="false" autoUploadExternalChanges="true"> + <option name="confirmBeforeDeletion" value="false" /> + <serverData> + <paths name="maretimebay"> + <serverdata> + <mappings> + <mapping deploy="/opt/ponyplay" local="$PROJECT_DIR$" web="/" /> + </mappings> + </serverdata> + </paths> + </serverData> + <option name="myAutoUpload" value="ALWAYS" /> + </component> +</project>
\ No newline at end of file diff --git a/web/.idea/discord.xml b/web/.idea/discord.xml new file mode 100644 index 0000000..cf77f1e --- /dev/null +++ b/web/.idea/discord.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="DiscordProjectSettings"> + <option name="show" value="DISABLE" /> + <option name="description" value="" /> + </component> +</project>
\ No newline at end of file diff --git a/web/.idea/modules.xml b/web/.idea/modules.xml new file mode 100644 index 0000000..f589ca3 --- /dev/null +++ b/web/.idea/modules.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="ProjectModuleManager"> + <modules> + <module fileurl="file://$PROJECT_DIR$/.idea/web.iml" filepath="$PROJECT_DIR$/.idea/web.iml" /> + </modules> + </component> +</project>
\ No newline at end of file diff --git a/web/.idea/php.xml b/web/.idea/php.xml new file mode 100644 index 0000000..88cd1bc --- /dev/null +++ b/web/.idea/php.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="MessDetectorOptionsConfiguration"> + <option name="transferred" value="true" /> + </component> + <component name="PHPCSFixerOptionsConfiguration"> + <option name="transferred" value="true" /> + </component> + <component name="PHPCodeSnifferOptionsConfiguration"> + <option name="highlightLevel" value="WARNING" /> + <option name="transferred" value="true" /> + </component> +</project>
\ No newline at end of file diff --git a/web/.idea/web.iml b/web/.idea/web.iml new file mode 100644 index 0000000..c956989 --- /dev/null +++ b/web/.idea/web.iml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="WEB_MODULE" version="4"> + <component name="NewModuleRootManager"> + <content url="file://$MODULE_DIR$" /> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + </component> +</module>
\ No newline at end of file diff --git a/web/TODO.md b/web/TODO.md new file mode 100644 index 0000000..d3ad99d --- /dev/null +++ b/web/TODO.md @@ -0,0 +1,22 @@ +# Ponyplay roadmap + +* [x] <s>Time and date</s> +* [x] <s>Sleep mode</s> +* [x] <s>Ambient EQ</s> +* [x] <s>Material You</s> +* [ ] Offline mode +* [x] <s>Notifications</s> +* [ ] Alarms +* [ ] Ponypush + * [ ] High priority notifications take up the entire screen +* [ ] Face unlock +* [ ] Settings + * [ ] Volume + * [ ] Brightness + * [ ] System +* [ ] Weather app +* [ ] Applications + * [ ] Installer + * [ ] Marehood + * [ ] Apple Music + * [ ] YouTube
\ No newline at end of file diff --git a/web/alarms/Awaken.ogg b/web/alarms/Awaken.ogg Binary files differnew file mode 100644 index 0000000..0667166 --- /dev/null +++ b/web/alarms/Awaken.ogg diff --git a/web/alarms/Bright_morning.ogg b/web/alarms/Bright_morning.ogg Binary files differnew file mode 100644 index 0000000..4e3ed8c --- /dev/null +++ b/web/alarms/Bright_morning.ogg diff --git a/web/alarms/Fresh_start.ogg b/web/alarms/Fresh_start.ogg Binary files differnew file mode 100644 index 0000000..76ecf57 --- /dev/null +++ b/web/alarms/Fresh_start.ogg diff --git a/web/alarms/Your_new_adventure.ogg b/web/alarms/Your_new_adventure.ogg Binary files differnew file mode 100644 index 0000000..e770ac4 --- /dev/null +++ b/web/alarms/Your_new_adventure.ogg diff --git a/web/app.html b/web/app.html new file mode 100644 index 0000000..06a55b8 --- /dev/null +++ b/web/app.html @@ -0,0 +1,310 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" /> + <title>app</title> + <style id="dynamic-theme"> + :root { + --text-color: white; + --surface-bg: #555; + --elevated-bg: #777; + --bg: #333; + --on-surface: #bbb; + --on-elevated: #aaa; + } + </style> + <style> + @font-face { + src: url("/fonts/google.ttf"); + font-family: "Google Sans"; + } + + @font-face { + src: url("/fonts/mlp.ttf"); + font-family: "MLPFindYourSparkle"; + } + + * { + color: var(--text-color); + font-family: "Google Sans", sans-serif; + user-select: none; + -webkit-user-drag: none; + } + + body { + font-size: 2.4vw; + } + + #sleep.show { + opacity: 1 !important; + pointer-events: initial !important; + } + + #sleep.inhibited { + transition: opacity 200ms !important; + } + + .notification { + box-shadow: 0 0 .5vw rgba(0, 0, 0, .5); + background-color: var(--surface-bg); + border-radius: 1.5vw; + width: 25vw; + padding: 2vw; + } + + .notification-header { + height: 2.5vw; + display: flex; + align-items: center; + margin-bottom: 1.5vw; + } + + .notification-application { + font-size: 1.3vw; + vertical-align: middle; + display: inline-block; + margin-left: 1vw; + width: 21.5vw; + white-space: nowrap; + overflow: hidden !important; + text-overflow: ellipsis; + } + + .notification-icon { + height: 2.5vw; + vertical-align: middle; + width: 2.5vw; + border-radius: 999px; + background: var(--on-surface); + display: inline-block; + } + + .notification-title { + font-weight: bold; + margin-bottom: .5vw; + } + + .notification-content { + font-size: 1.8vw; + } + + .notification-image { + max-width: 100%; + border-radius: 1.5vw; + margin-top: 1.5vw; + } + + #container.deployed { + transition: top 200ms, opacity 200ms, border-radius 200ms; + transition-timing-function: linear; + top: 0 !important; + opacity: 1 !important; + pointer-events: initial !important; + border-radius: 0 !important; + } + + #container.deployed #bottom-row { + transition: border-radius 200ms; + border-radius: 0 !important; + } + + #container.backing { + pointer-events: initial !important; + } + + #container.undeploying { + transition: top 200ms, opacity 200ms; + transition-timing-function: linear; + top: -100vh !important; + opacity: 0 !important; + } + + #content, #notifications { + transition: opacity 200ms; + } + + #bg, #bg-blur { + transition: opacity 200ms; + } + + body.deploying #bg, body.deploying #bg-blur { + transition: none !important; + } + + body.deploying #content, body.deploying #notifications { + opacity: 0; + } + + #app.show { + opacity: 1 !important; + pointer-events: initial !important; + } + + #app.show-native iframe { + display: none; + } + + #app.show-native #app-header-icon-container { + width: 100vh !important; + } + + #app.show-native #app-header { + height: 100vh !important; + } + + #alarm.show { + opacity: 1 !important; + pointer-events: initial !important; + } + </style> +</head> +<body style="background-color: var(--bg);"> + <div style="position: fixed; inset: 0; z-index: 5000; opacity: 0; pointer-events: none;"> + <canvas id="canvas"></canvas> + <img id="photo"> + <video id="video"></video> + <span id="rgb"></span> + <span style="width: 2ch; height: 2ch; background-color: black; display: inline-block;" id="sq1"></span> + <span style="width: 2ch; height: 2ch; background-color: black; display: inline-block;" id="sq2"></span> + </div> + + <div id="app" style="position: fixed; inset: 0; z-index: 200; background-color: var(--bg); transition: opacity 200ms; pointer-events: none; opacity: 0;"> + <div id="app-header" style="background-color: var(--surface-bg); height: 10vh; display: grid; grid-template-columns: max-content 1fr;"> + <div id="app-header-icon-container" onclick="closeApp();" style="width: 10vh; text-align: center; justify-content: center; align-items: center; display: flex;"> + <img id="app-header-icon"> + </div> + <div style="text-align: center; justify-content: center; align-items: center; display: flex;" id="app-header-title"></div> + </div> + + <iframe id="app-frame" src="about:blank" style="border: none; width: 100%; height: 90vh;"></iframe> + </div> + + <div id="error" style="background-color: blue; color: white; font-family: monospace !important; font-size: 14px; z-index: 99999; position: fixed; inset: 0; display: none;"> + <pre id="error-message" style="font-family: monospace !important; font-size: 14px; margin: 0 !important;">-</pre> + </div> + <pre id="warnings" style="font-family: monospace !important; font-size: 14px; z-index: 99998; position: fixed; top: 0; left: 0; pointer-events: none; color: red; margin: 0 !important;"></pre> + <script> + function addWarning(warning) { + document.getElementById("warnings").innerText += "\n" + warning; + document.getElementById("warnings").innerText = document.getElementById("warnings").innerText.trim(); + } + + if (navigator.userAgent.includes("(Linux; Ponyplay; ")) { + document.getElementById("warnings").style.display = "none"; + } else { + addWarning("UNSUPPORTED SETUP -- The application is running outside of the Ponyplay engine. You will get NO SUPPORT."); + } + + window.onerror = (_1, _2, _3, _4, error) => { + document.getElementById("error").style.display = ""; + document.getElementById("error-message").innerText = error.stack + "\n\n----\n\n" + JSON.stringify(window.weather, null, 2); + return false; + } + + window.onunhandledrejection = (e) => { + document.getElementById("error").style.display = ""; + document.getElementById("error-message").innerText = (e.reason?.stack ?? e.reason) + "\n\n----\n\n" + JSON.stringify(window.weather, null, 2); + return false; + } + </script> + + <div id="container" style="position: fixed; left: 0; background-color: var(--surface-bg); z-index: 50; top: -100vh; right: 0; height: 100vh; pointer-events: none; border-bottom-left-radius: 2vw; border-bottom-right-radius: 2vw; display: grid; grid-template-rows: 80vh 20vh;"> + <div id="top-row"> + <div id="no-apps" style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; opacity: .5;">You don't have any applications .c.</div> + </div> + <div id="bottom-row" style="background-color: var(--elevated-bg); border-bottom-left-radius: 2vw; border-bottom-right-radius: 2vw; display: grid; grid-template-columns: repeat(6, 1fr);"> + <div style="display: flex; align-items: center; justify-content: center; text-align: center;"> + <div> + <div id="app-1-icon" style="display: flex; align-items: center; justify-content: center; margin-left: auto; margin-right: auto; background-color: var(--on-elevated); width: 7.5vh; height: 7.5vh; border-radius: 999px;"> + <img id="app-1-icon-img" style="width: 5vh; height: 5vh;"> + </div> + <div id="app-1-name" style="font-size: 1.6vw; margin-top: .5vw;">Installer</div> + </div> + </div> + <div style="display: flex; align-items: center; justify-content: center; text-align: center;" onclick="openApp('Alarms', '/apps/alarms.html', true);"> + <div> + <div id="app-2-icon" style="display: flex; align-items: center; justify-content: center; margin-left: auto; margin-right: auto; background-color: var(--on-elevated); width: 7.5vh; height: 7.5vh; border-radius: 999px;"> + <img id="app-2-icon-img" style="width: 5vh; height: 5vh;"> + </div> + <div id="app-2-name" style="font-size: 1.6vw; margin-top: .5vw;">Alarms</div> + </div> + </div> + <div style="display: flex; align-items: center; justify-content: center; text-align: center;"> + <div> + <div id="app-3-icon" style="display: flex; align-items: center; justify-content: center; margin-left: auto; margin-right: auto; background-color: var(--on-elevated); width: 7.5vh; height: 7.5vh; border-radius: 999px;"> + <img id="app-3-icon-img" style="width: 5vh; height: 5vh;"> + </div> + <div id="app-3-name" style="font-size: 1.6vw; margin-top: .5vw;">Face unlock</div> + </div> + </div> + <div style="display: flex; align-items: center; justify-content: center; text-align: center;"> + <div> + <div id="app-4-icon" style="display: flex; align-items: center; justify-content: center; margin-left: auto; margin-right: auto; background-color: var(--on-elevated); width: 7.5vh; height: 7.5vh; border-radius: 999px;"> + <img id="app-4-icon-img" style="width: 5vh; height: 5vh;"> + </div> + <div id="app-4-name" style="font-size: 1.6vw; margin-top: .5vw;">Ponypush</div> + </div> + </div> + <div style="display: flex; align-items: center; justify-content: center; text-align: center;"> + <div> + <div id="app-5-icon" style="display: flex; align-items: center; justify-content: center; margin-left: auto; margin-right: auto; background-color: var(--on-elevated); width: 7.5vh; height: 7.5vh; border-radius: 999px;"> + <img id="app-5-icon-img" style="width: 5vh; height: 5vh;"> + </div> + <div id="app-5-name" style="font-size: 1.6vw; margin-top: .5vw;">Weather</div> + </div> + </div> + <div style="display: flex; align-items: center; justify-content: center; text-align: center;" onclick="openApp('Configuration', '/apps/settings.html', true);"> + <div> + <div id="app-6-icon" style="display: flex; align-items: center; justify-content: center; margin-left: auto; margin-right: auto; background-color: var(--on-elevated); width: 7.5vh; height: 7.5vh; border-radius: 999px;"> + <img id="app-6-icon-img" style="width: 5vh; height: 5vh;"> + </div> + <div id="app-6-name" style="font-size: 1.6vw; margin-top: .5vw;">Configuration</div> + </div> + </div> + </div> + </div> + + <div id="sleep" style="z-index: 1000; color: white !important; background-color: black; position: fixed; inset: 0; opacity: 0; pointer-events: none; transition: opacity 2000ms; display: flex; align-items: center; justify-content: center;" onclick="startInhibitSleep();"> + <div id="time-sleep" style="color: white !important; font-size: 32vw; margin-top: 16vw; font-family: MLPFindYourSparkle, 'Google Sans', sans-serif; display: inline-block; pointer-events: none;">--:--</div> + </div> + + <div id="alarm" style="z-index: 2000; color: black !important; background-color: white; position: fixed; inset: 0; display: flex; align-items: center; justify-content: center; background-image: radial-gradient(circle, rgba(255,210,121,1) 0%, rgba(255,255,255,1) 50%); opacity: 0; transition: opacity 200ms; pointer-events: none;" onclick="stopAlarm();"> + <div> + <div id="time-alarm" style="color: black !important; font-size: 32vw; margin-top: 16vw; font-family: MLPFindYourSparkle, 'Google Sans', sans-serif; display: block; pointer-events: none;">--:--</div> + <div style="color: black !important; font-size: 2.4vw; position: absolute; text-align: center; width: 100%; left: 0; margin-top: -8vw;">Tap to dismiss alarm</div> + </div> + </div> + + <div id="bg" style="position: fixed; inset: 0; background-position: center; background-size: cover; z-index: 5;"></div> + <div id="bg-blur" style="position: fixed; inset: 0; background-position: center; background-size: cover; z-index: 5;"></div> + <div id="swipe-zone" style="position: fixed; inset: 0; background-image: linear-gradient(180deg, rgba(0,0,0,0) 0%, rgba(0,0,0,0) 50%, rgba(0,0,0,0.25) 100%); z-index: 10;"></div> + <div id="filter" style="background-color: white; position: fixed; inset: 0; z-index: 500; opacity: .1; transition: background-color 2000ms;pointer-events: none;"></div> + <div style="position: fixed; bottom: 3vw; left: 4.5vw; z-index: 20; text-shadow: 0 0 .5vw rgba(0, 0, 0, .5);" id="content"> + <div id="date">---, -- ---</div> + <div id="time" style="font-size: 8vw; margin-top: 4vw; font-family: MLPFindYourSparkle, 'Google Sans', sans-serif; display: inline-block;" onclick="openApp('Configuration', '/apps/settings.html', false);">--:--</div> + <div style="display: inline-block; font-size: 3vw; margin-left: 1vw;"> + <img style="vertical-align: middle; width: 3vw; height: 3vw;" id="weather"> + <span style="vertical-align: middle;" id="temperature">--°</span> + </div> + <div id="alarm-coming" style="position: fixed; right: 4.5vw; bottom: 6vw; display: none;"> + <img id="alarm-coming-icon" style="vertical-align: middle;"> + <span id="alarm-coming-time" style="vertical-align: middle;">--:--</span> + </div> + </div> + + <div id="notifications" style="position: fixed; z-index: 3000; right: 2vw; top: 2vw;"></div> + + <script src="/src/appmanager.js"></script> + <script src="/src/color.js"></script> + <script src="/src/loader.js"></script> + <script src="/src/alarms.js"></script> + <script src="/src/theme.js"></script> + <script src="/src/notification.js"></script> + <script src="/src/sleep.js"></script> + <script src="/src/weather.js"></script> + <script src="/src/timedate.js"></script> + <script src="/src/ambienteq.js"></script> + <script src="/src/scroll.js"></script> +</body> +</html>
\ No newline at end of file diff --git a/web/apps/alarms.html b/web/apps/alarms.html new file mode 100644 index 0000000..326dd95 --- /dev/null +++ b/web/apps/alarms.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" /> + <title>app</title> + <style id="dynamic-theme"> + :root { + --text-color: white; + --surface-bg: #555; + --elevated-bg: #777; + --bg: #333; + --on-surface: #bbb; + --on-elevated: #aaa; + } + </style> + <style> + @font-face { + src: url("/fonts/google.ttf"); + font-family: "Google Sans"; + } + + @font-face { + src: url("/fonts/mlp.ttf"); + font-family: "MLPFindYourSparkle"; + } + + * { + color: var(--text-color); + font-family: "Google Sans", sans-serif; + user-select: none; + -webkit-user-drag: none; + } + + body { + font-size: 2.4vw; + } + + input::placeholder { + color: var(--text-color); + opacity: .5; + } + </style> +</head> +<body style="background-color: var(--bg);"> + <div id="settings" style="border-top: 1px solid var(--on-elevated); border-left: 1px solid var(--on-elevated); border-right: 1px solid var(--on-elevated);"></div> + + <script src="/apps/settings.js"></script> + <script src="/src/apptheme.js"></script> +</body> +</html>
\ No newline at end of file diff --git a/web/apps/settings.html b/web/apps/settings.html new file mode 100644 index 0000000..326dd95 --- /dev/null +++ b/web/apps/settings.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" /> + <title>app</title> + <style id="dynamic-theme"> + :root { + --text-color: white; + --surface-bg: #555; + --elevated-bg: #777; + --bg: #333; + --on-surface: #bbb; + --on-elevated: #aaa; + } + </style> + <style> + @font-face { + src: url("/fonts/google.ttf"); + font-family: "Google Sans"; + } + + @font-face { + src: url("/fonts/mlp.ttf"); + font-family: "MLPFindYourSparkle"; + } + + * { + color: var(--text-color); + font-family: "Google Sans", sans-serif; + user-select: none; + -webkit-user-drag: none; + } + + body { + font-size: 2.4vw; + } + + input::placeholder { + color: var(--text-color); + opacity: .5; + } + </style> +</head> +<body style="background-color: var(--bg);"> + <div id="settings" style="border-top: 1px solid var(--on-elevated); border-left: 1px solid var(--on-elevated); border-right: 1px solid var(--on-elevated);"></div> + + <script src="/apps/settings.js"></script> + <script src="/src/apptheme.js"></script> +</body> +</html>
\ No newline at end of file diff --git a/web/apps/settings.js b/web/apps/settings.js new file mode 100644 index 0000000..58191f0 --- /dev/null +++ b/web/apps/settings.js @@ -0,0 +1,35 @@ +(async () => { + window.settings = await (await fetch("/settings.json?_" + Math.random())).json(); + window.oldSettings = {}; + + for (let setting of window.settings) { + oldSettings[setting.name] = localStorage.getItem(setting.name); + } + + document.getElementById("settings").innerHTML = settings.sort((a, b) => ('' + a.name).localeCompare(b.name)).map(setting => `<div style="display: grid; grid-gap: 1vw; grid-template-columns: 1fr 1fr; border-bottom: 1px solid var(--on-elevated);"> + <div style="border-right: 1px solid var(--on-elevated); margin-left: 1vw;">${setting.name}</div> + <input id="setting--${setting.name}" type="text" value="${setting.secret ? '' : (localStorage.getItem(setting.name) ?? setting.default)}" ${setting.secret ? 'placeholder="(no change)"' : ''} style="border: none; font-size: 2.4vw; outline: none; background: transparent;" spellcheck="false" autocapitalize="off" autocomplete="off" onchange="updateSetting('${setting.name}');"> + </div>`).join(""); +})(); + +function updateSetting(name) { + if (window.settings.filter(i => i.name === name)[0].secret) { + if (document.getElementById("setting--" + name).value.trim() !== "") { + localStorage.setItem(name, document.getElementById("setting--" + name).value.trim()); + } else { + if (window.oldSettings[name]) { + localStorage.setItem(name, window.oldSettings[name]); + } else { + localStorage.setItem(name, window.settings.filter(i => i.name === name)[0].default); + document.getElementById("setting--" + name).value = window.settings.filter(i => i.name === name)[0].default; + } + } + } else { + if (document.getElementById("setting--" + name).value.trim() !== "") { + localStorage.setItem(name, document.getElementById("setting--" + name).value.trim()); + } else { + localStorage.setItem(name, window.settings.filter(i => i.name === name)[0].default); + document.getElementById("setting--" + name).value = window.settings.filter(i => i.name === name)[0].default; + } + } +}
\ No newline at end of file diff --git a/web/availabilitycheck.txt b/web/availabilitycheck.txt new file mode 100644 index 0000000..6340169 --- /dev/null +++ b/web/availabilitycheck.txt @@ -0,0 +1 @@ +{"status": "OK"} diff --git a/web/fonts/google.ttf b/web/fonts/google.ttf Binary files differnew file mode 100755 index 0000000..e2c69c3 --- /dev/null +++ b/web/fonts/google.ttf diff --git a/web/fonts/mlp.ttf b/web/fonts/mlp.ttf Binary files differnew file mode 100644 index 0000000..ced52e8 --- /dev/null +++ b/web/fonts/mlp.ttf diff --git a/web/icons/alarms.svg b/web/icons/alarms.svg new file mode 100644 index 0000000..44a7485 --- /dev/null +++ b/web/icons/alarms.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M479-82q-74 0-139.5-28t-114-76.5q-48.5-48.5-77-114T120-440.733q0-74.733 28.5-140T225.5-695q48.5-49 114-77T479-800q74 0 139.5 28T733-695q49 49 77 114.267t28 140Q838-366 810-300.5t-77 114Q684-138 618.5-110T479-82Zm0-357Zm121 161 42-42-130-130v-190h-60v214l148 148ZM214-867l42 42L92-667l-42-42 164-158Zm530 0 164 158-42 42-164-158 42-42ZM479.043-142Q604-142 691-229.043t87-212Q778-566 690.957-653t-212-87Q354-740 267-652.957t-87 212Q180-316 267.043-229t212 87Z"/></svg>
\ No newline at end of file diff --git a/web/icons/close.svg b/web/icons/close.svg new file mode 100644 index 0000000..ded3e2a --- /dev/null +++ b/web/icons/close.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="m249-207-42-42 231-231-231-231 42-42 231 231 231-231 42 42-231 231 231 231-42 42-231-231-231 231Z"/></svg>
\ No newline at end of file diff --git a/web/icons/face.svg b/web/icons/face.svg new file mode 100644 index 0000000..a0f8315 --- /dev/null +++ b/web/icons/face.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M222-255q63-44 125-67.5T480-346q71 0 133.5 23.5T739-255q44-54 62.5-109T820-480q0-145-97.5-242.5T480-820q-145 0-242.5 97.5T140-480q0 61 19 116t63 109Zm257.814-195Q422-450 382.5-489.686q-39.5-39.686-39.5-97.5t39.686-97.314q39.686-39.5 97.5-39.5t97.314 39.686q39.5 39.686 39.5 97.5T577.314-489.5q-39.686 39.5-97.5 39.5Zm.654 370Q398-80 325-111.5q-73-31.5-127.5-86t-86-127.266Q80-397.532 80-480.266T111.5-635.5q31.5-72.5 86-127t127.266-86q72.766-31.5 155.5-31.5T635.5-848.5q72.5 31.5 127 86t86 127.032q31.5 72.532 31.5 155T848.5-325q-31.5 73-86 127.5t-127.032 86q-72.532 31.5-155 31.5ZM480-140q55 0 107.5-16T691-212q-51-36-104-55t-107-19q-54 0-107 19t-104 55q51 40 103.5 56T480-140Zm0-370q34 0 55.5-21.5T557-587q0-34-21.5-55.5T480-664q-34 0-55.5 21.5T403-587q0 34 21.5 55.5T480-510Zm0-77Zm0 374Z"/></svg>
\ No newline at end of file diff --git a/web/icons/installer.svg b/web/icons/installer.svg new file mode 100644 index 0000000..eb90940 --- /dev/null +++ b/web/icons/installer.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M480-313 287-506l43-43 120 120v-371h60v371l120-120 43 43-193 193ZM220-160q-24 0-42-18t-18-42v-143h60v143h520v-143h60v143q0 24-18 42t-42 18H220Z"/></svg>
\ No newline at end of file diff --git a/web/icons/ponypush.svg b/web/icons/ponypush.svg new file mode 100644 index 0000000..03221aa --- /dev/null +++ b/web/icons/ponypush.svg @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 26.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" + id="svg8" inkscape:export-filename="/home/pheckel/Code/ntfy-android/assets/launcher_full_bg.png" inkscape:export-xdpi="260.10001" inkscape:export-ydpi="260.10001" inkscape:version="1.1.1 (3bf5ae0, 2021-09-20)" sodipodi:docname="main-list-icon.svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 141.7 141.7" + style="enable-background:new 0 0 141.7 141.7;" xml:space="preserve"> +<style type="text/css"> + .st0{fill:none;stroke:#777777;stroke-width:12;stroke-miterlimit:10;} +</style> +<sodipodi:namedview bordercolor="#666666" borderopacity="1.0" fit-margin-bottom="0" fit-margin-left="0" fit-margin-right="0" fit-margin-top="0" id="base" inkscape:current-layer="layer1" inkscape:cx="42.40716" inkscape:cy="53.189293" inkscape:document-units="mm" inkscape:guide-bbox="true" inkscape:measure-end="0,0" inkscape:measure-start="0,0" inkscape:pagecheckerboard="0" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:rotation="-1" inkscape:snap-text-baseline="true" inkscape:window-height="1025" inkscape:window-maximized="1" inkscape:window-width="1863" inkscape:window-x="57" inkscape:window-y="27" inkscape:zoom="2.6887315" pagecolor="#ffffff" showgrid="false" showguides="false"> + <sodipodi:guide id="guide1770" orientation="1,0" position="9.8690703,86.715698"></sodipodi:guide> + <sodipodi:guide id="guide1772" orientation="1,0" position="39.661132,81.074874"></sodipodi:guide> + <sodipodi:guide id="guide1774" orientation="0,-1" position="9.8690703,58.786381"></sodipodi:guide> + <sodipodi:guide id="guide1776" orientation="0,-1" position="-2.6121775,28.943566"></sodipodi:guide> + <sodipodi:guide id="guide4020" orientation="1,0" position="14.686182,55.195651"></sodipodi:guide> + <sodipodi:guide id="guide4022" orientation="1,0" position="34.626283,58.786381"></sodipodi:guide> + <sodipodi:guide id="guide4024" orientation="0,-1" position="12.398156,51.002016"></sodipodi:guide> + <sodipodi:guide id="guide4026" orientation="0,-1" position="11.073267,36.978591"></sodipodi:guide> +</sodipodi:namedview> +<path class="st0" d="M13.2,46.1c0,0,58.1-28.7,111.3-20.8c0,0,32.2,49-18.6,91.9c0,0-37.6-14.6-92.7,3.8"/> +</svg> diff --git a/web/icons/settings.svg b/web/icons/settings.svg new file mode 100644 index 0000000..dc0d1ec --- /dev/null +++ b/web/icons/settings.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M427-120v-225h60v83h353v60H487v82h-60Zm-307-82v-60h247v60H120Zm187-166v-82H120v-60h187v-84h60v226h-60Zm120-82v-60h413v60H427Zm166-165v-225h60v82h187v60H653v83h-60Zm-473-83v-60h413v60H120Z"/></svg>
\ No newline at end of file diff --git a/web/icons/system.svg b/web/icons/system.svg new file mode 100644 index 0000000..23ace3d --- /dev/null +++ b/web/icons/system.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M480-200q-99 0-169.5-13.5T240-246v-34h-95q-26.145 0-44.072-19.5Q83-319 85-345l31-360q2-23 18.808-39 16.807-16 40.192-16h610q23.385 0 40.192 16Q842-728 844-705l31 360q2 26-15.928 45.5Q841.145-280 815-280h-95v34q0 19-70.5 32.5T480-200ZM145-340h670l-30-360H175l-30 360Zm335-180Z"/></svg>
\ No newline at end of file diff --git a/web/icons/weather.svg b/web/icons/weather.svg new file mode 100644 index 0000000..2175d03 --- /dev/null +++ b/web/icons/weather.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M450-770v-150h60v150h-60Zm256 106-42-42 106-107 42 43-106 106Zm64 214v-60h150v60H770ZM450-40v-150h60v150h-60ZM253-665 148-770l42-42 106 106-43 41Zm518 517L664-254l41-41 108 104-42 43ZM40-450v-60h150v60H40Zm151 302-43-42 105-105 22 20 22 21-106 106Zm289-92q-100 0-170-70t-70-170q0-100 70-170t170-70q100 0 170 70t70 170q0 100-70 170t-170 70Zm0-60q75 0 127.5-52.5T660-480q0-75-52.5-127.5T480-660q-75 0-127.5 52.5T300-480q0 75 52.5 127.5T480-300Zm0-180Z"/></svg>
\ No newline at end of file diff --git a/web/settings.json b/web/settings.json new file mode 100644 index 0000000..036ef4e --- /dev/null +++ b/web/settings.json @@ -0,0 +1,72 @@ +[ + { + "name": "weather_owm_key", + "secret": true, + "default": "" + }, + { + "name": "sleep_trigger_brightness", + "secret": false, + "default": "8" + }, + { + "name": "sleep_inhibit_duration", + "secret": false, + "default": "30000" + }, + { + "name": "sleep_minimum_opacity", + "secret": false, + "default": "0.15" + }, + { + "name": "ambienteq_brightness_add", + "secret": false, + "default": "15" + }, + { + "name": "ambienteq_add_whilesleep", + "secret": false, + "default": "false" + }, + { + "name": "ambienteq_brightness_enable", + "secret": false, + "default": "true" + }, + { + "name": "ambienteq_filter_enable", + "secret": false, + "default": "true" + }, + { + "name": "notification_duration", + "secret": false, + "default": "10000" + }, + { + "name": "sleep_disable_wakelock", + "secret": false, + "default": "false" + }, + { + "name": "ui_deploy_blur", + "secret": false, + "default": "true" + }, + { + "name": "ui_deploy_trigger", + "secret": false, + "default": "50" + }, + { + "name": "weather_refresh_interval", + "secret": false, + "default": "60000" + }, + { + "name": "weather_iplocation_fallback", + "secret": false, + "default": "true" + } +]
\ No newline at end of file diff --git a/web/src/alarms.js b/web/src/alarms.js new file mode 100644 index 0000000..7edaf1a --- /dev/null +++ b/web/src/alarms.js @@ -0,0 +1,78 @@ +window.alarmShown = false; + +(async () => { + window.alarmRingtones = [ + new Audio(URL.createObjectURL(new Blob([await (await fetch("/alarms/Awaken.ogg")).arrayBuffer()], { type: "audio/ogg" }))), + new Audio(URL.createObjectURL(new Blob([await (await fetch("/alarms/Bright_morning.ogg")).arrayBuffer()], { type: "audio/ogg" }))), + new Audio(URL.createObjectURL(new Blob([await (await fetch("/alarms/Fresh_start.ogg")).arrayBuffer()], { type: "audio/ogg" }))), + new Audio(URL.createObjectURL(new Blob([await (await fetch("/alarms/Your_new_adventure.ogg")).arrayBuffer()], { type: "audio/ogg" }))) + ]; + + for (let alarm of window.alarmRingtones) { + alarm.loop = true; + } +})(); + +window.loadingItems.push(() => { + // TODO: Actually load alarms + window.alarms = [ + { + enabled: true, + time: new Date(new Date().getTime() + 30000).getTime() - new Date(new Date().toISOString().split("T")[0]).getTime(), + repeat: 0b1111111, + alarm: 3 + }, + { + enabled: true, + time: new Date(new Date().getTime() + 60000).getTime() - new Date(new Date().toISOString().split("T")[0]).getTime(), + repeat: 0b1111100, + alarm: 2 + } + ]; + + refreshAlarms(); + + setInterval(() => { + let list = window.alarms.filter(i => i.enabled).sort((a, b) => a.time - b.time); + let alarm = 0; + + if (!list[alarm] || !list[alarm].time) return; + + while (new Date().getTime() - (list[alarm].time + new Date(new Date().toISOString().split("T")[0]).getTime()) > 1000) { + alarm++; + } + + if (new Date().getTime() - (list[alarm].time + new Date(new Date().toISOString().split("T")[0]).getTime()) > 0 && new Date().getTime() - (list[alarm].time + new Date(new Date().toISOString().split("T")[0]).getTime()) <= 1000) { + startAlarm(list[alarm].alarm ?? 3); + list[alarm].enabled = false; + refreshAlarms(); + } + }, 1000); +}); + +function startAlarm(ringtone) { + window.alarmShown = true; + document.getElementById("alarm").classList.add("show"); + window.alarmRingtones[ringtone].play(); +} + +function stopAlarm() { + window.alarmShown = false; + document.getElementById("alarm").classList.remove("show"); + + for (let alarm of window.alarmRingtones) { + alarm.pause(); + alarm.currentTime = 0; + } +} + +function refreshAlarms() { + if (window.alarms.filter(i => i.enabled).length > 0) { + let nextAlarmTime = alarms.filter(i => i.enabled).sort((a, b) => a.time - b.time)[0].time + new Date(new Date().toISOString().split("T")[0]).getTime(); + document.getElementById("alarm-coming-time").innerText = fixed(new Date(nextAlarmTime).getHours(), 2) + ":" + fixed(new Date(nextAlarmTime).getMinutes(), 2); + document.getElementById("alarm-coming").style.display = ""; + } else { + document.getElementById("alarm-coming-time").innerText = "--:--"; + document.getElementById("alarm-coming").style.display = "none"; + } +}
\ No newline at end of file diff --git a/web/src/ambienteq.js b/web/src/ambienteq.js new file mode 100644 index 0000000..81acb65 --- /dev/null +++ b/web/src/ambienteq.js @@ -0,0 +1,103 @@ +window.loadingItems.push(() => { + const width = 320; + let height = 0; + let streaming = false; + + let video = null; + let canvas = null; + let photo = null; + + function startup() { + video = document.getElementById("video"); + canvas = document.getElementById("canvas"); + photo = document.getElementById("photo"); + + if (localStorage.getItem("ambienteq_filter_enable") === "false") { + document.getElementById("filter").style.display = "none"; + return; + } + + navigator.mediaDevices + .getUserMedia({ video: true, audio: false }) + .then((stream) => { + video.srcObject = stream; + video.play(); + }) + .catch(async (err) => { + console.error(`An error occurred: ${err}`); + document.getElementById("filter").style.display = "none"; + addWarning("[CAMERA_ERROR] Unable to use camera, ambient lighting will be unavailable. See console for details."); + if (navigator.userAgent.includes("Ponyplay/")) sendNotification("Ponyplay System", await renderIcon("system", true), "Unable to access camera", "Face unlock and Ambient Brightness will not work", null, true); + }); + + video.addEventListener( + "canplay", + (ev) => { + if (!streaming) { + height = video.videoHeight / (video.videoWidth / width); + + video.setAttribute("width", width); + video.setAttribute("height", height); + canvas.setAttribute("width", width); + canvas.setAttribute("height", height); + streaming = true; + } + }, + false, + ); + + clearphoto(); + } + + function clearphoto() { + const context = canvas.getContext("2d"); + context.fillStyle = "#AAA"; + context.fillRect(0, 0, canvas.width, canvas.height); + + const data = canvas.toDataURL("image/png"); + photo.setAttribute("src", data); + } + + function takepicture() { + const context = canvas.getContext("2d"); + + if (width && height) { + canvas.width = width; + canvas.height = height; + context.drawImage(video, 0, 0, width, height); + + const data = canvas.toDataURL("image/png"); + photo.setAttribute("src", data); + } else { + clearphoto(); + } + } + + window.addEventListener("load", startup, false); + + setInterval(() => { + takepicture(); + + if (window.engine && localStorage.getItem("ambienteq_brightness_enable") === "true") { + if (!window.sleeping || localStorage.getItem("ambienteq_add_whilesleep") === "true") { + if (Math.abs(window.engine.getBrightness() - window.engine.getSystemBrightness()) > 10) window.engine.setBrightness(window.engine.getBrightness() + parseInt(localStorage.getItem("ambianteq_brightness_add"))); + } else { + if (Math.abs(window.engine.getBrightness() - window.engine.getSystemBrightness()) > 10) window.engine.setBrightness(window.engine.getBrightness()); + } + } + }, 2000); + + document.getElementById("photo").onload = () => { + let rgb = Object.values(getAverageRGB(document.getElementById("photo"))); + document.getElementById("rgb").innerText = rgb2hue(rgb[0], rgb[1], rgb[2]).toFixed(2); + document.getElementById("sq1").style.backgroundColor = "rgb(" + rgb[0] + ", " + rgb[1] + ", " + rgb[2] + ")"; + document.getElementById("sq2").style.backgroundColor = document.getElementById("filter").style.backgroundColor = "hsl(" + rgb2hue(rgb[0], rgb[1], rgb[2]) + "deg, 100%, 50%)"; + + let opacity = (rgb.reduce((a, b) => a + b) / rgb.length) / 255; + if (opacity < parseFloat(localStorage.getItem("sleep_minimum_opacity"))) opacity = parseFloat(localStorage.getItem("sleep_minimum_opacity")); + + document.getElementById("time-sleep").style.opacity = opacity.toString(); + } + + startup(); +});
\ No newline at end of file diff --git a/web/src/appmanager.js b/web/src/appmanager.js new file mode 100644 index 0000000..3f0b027 --- /dev/null +++ b/web/src/appmanager.js @@ -0,0 +1,28 @@ +window.appManagerCloseTimeout = null; +window.appOpen = false; + +function closeApp() { + appOpen = false; + document.getElementById("app").classList.remove("show-native"); + document.getElementById("app").classList.remove("show"); + + if (window.engine) window.engine.closeApp(); + + window.appManagerCloseTimeout = setTimeout(() => { + document.getElementById("app-header-title").innerText = ""; + document.getElementById("app-frame").src = "about:blank"; + }, 250); +} + +function openApp(title, url, external) { + appOpen = true; + document.getElementById("app-header-title").innerText = title; + document.getElementById("app").classList.add("show"); + + if (window.engine && external) { + document.getElementById("app").classList.add("show-native"); + window.engine.startApp(url); + } else { + document.getElementById("app-frame").src = url; + } +}
\ No newline at end of file diff --git a/web/src/apptheme.js b/web/src/apptheme.js new file mode 100644 index 0000000..205f74b --- /dev/null +++ b/web/src/apptheme.js @@ -0,0 +1,12 @@ +window.dynamicColor = window.parent.dynamicColor; + +document.getElementById("dynamic-theme").innerText = ` +:root { + --text-color: #${window.dynamicColor.text}; + --surface-bg: #${window.dynamicColor.surface}; + --elevated-bg: #${window.dynamicColor.elevated}; + --bg: #${window.dynamicColor.bg}; + --on-surface: #${window.dynamicColor.onSurface}; + --on-elevated: #${window.dynamicColor.onElevated}; +} +`;
\ No newline at end of file diff --git a/web/src/color.js b/web/src/color.js new file mode 100644 index 0000000..322b0ed --- /dev/null +++ b/web/src/color.js @@ -0,0 +1,119 @@ +function getAverageRGB(imgEl) { + let blockSize = 5, + defaultRGB = {r:0,g:0,b:0}, + canvas = document.createElement('canvas'), + context = canvas.getContext && canvas.getContext('2d'), + data, width, height, + i = -4, + length, + rgb = {r:0,g:0,b:0}, + count = 0; + + if (!context) { + return null; + } + + height = canvas.height = imgEl.naturalHeight || imgEl.offsetHeight || imgEl.height; + width = canvas.width = imgEl.naturalWidth || imgEl.offsetWidth || imgEl.width; + + context.drawImage(imgEl, 0, 0); + + try { + data = context.getImageData(0, 0, width, height); + } catch(e) { + console.error(e); + return null; + } + + length = data.data.length; + + while ( (i += blockSize * 4) < length ) { + ++count; + rgb.r += data.data[i]; + rgb.g += data.data[i+1]; + rgb.b += data.data[i+2]; + } + + rgb.r = ~~(rgb.r/count); + rgb.g = ~~(rgb.g/count); + rgb.b = ~~(rgb.b/count); + + return rgb; +} + +function rgb2hue(r, g, b) { + r /= 255; + g /= 255; + b /= 255; + + let max = Math.max(r, g, b); + let min = Math.min(r, g, b); + let c = max - min; + let hue; + let segment; + let shift; + + if (c === 0) { + hue = 0; + } else { + switch(max) { + case r: + segment = (g - b) / c; + shift = 0 / 60; + if (segment < 0) { + shift = 360 / 60; + } + hue = segment + shift; + break; + case g: + segment = (b - r) / c; + shift = 120 / 60; + hue = segment + shift; + break; + case b: + segment = (r - g) / c; + shift = 240 / 60; + hue = segment + shift; + break; + } + } + + return hue * 60; +} + +function hslToRgb(h, s, l) { + let r, g, b; + + if (s === 0){ + r = g = b = l; + } else { + let hue2rgb = function hue2rgb(p, q, t) { + if (t < 0) t += 1; + if (t > 1) t -= 1; + if (t < 1/6) return p + (q - p) * 6 * t; + if (t < 1/2) return q; + if (t < 2/3) return p + (q - p) * (2/3 - t) * 6; + return p; + } + + let q = l < 0.5 ? l * (1 + s) : l + s - l * s; + let p = 2 * l - q; + r = hue2rgb(p, q, h + 1/3); + g = hue2rgb(p, q, h); + b = hue2rgb(p, q, h - 1/3); + } + + return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)]; +} + +function rgbToHex(r, g, b) { + let rh = r.toString(16); + let gh = g.toString(16); + let bh = b.toString(16); + + if (rh.length < 2) rh = "0" + rh; + if (gh.length < 2) gh = "0" + gh; + if (bh.length < 2) bh = "0" + bh; + + return rh + gh + bh; +}
\ No newline at end of file diff --git a/web/src/loader.js b/web/src/loader.js new file mode 100644 index 0000000..a227a3b --- /dev/null +++ b/web/src/loader.js @@ -0,0 +1,7 @@ +window.loadingItems = []; + +function load() { + for (let item of loadingItems) { + item(); + } +}
\ No newline at end of file diff --git a/web/src/notification.js b/web/src/notification.js new file mode 100644 index 0000000..1a7841a --- /dev/null +++ b/web/src/notification.js @@ -0,0 +1,33 @@ +function sendNotification(app, icon, title, description, image, persistent) { + let id = crypto.randomUUID(); + + document.getElementById("notifications").innerHTML = ` + <div class="notification" id="notification-${id}" style="margin-bottom: 1.5vw; transition: opacity 500ms; opacity: 0;"> + <div class="notification-header"> + <img class="notification-icon" ${icon ? `src="${icon}"` : ""}> + <span class="notification-application">${app ?? "System"}</span> + </div> + <div class="notification-content"> + ${title ? `<div class="notification-title">${title}</div>` : ""} + ${description ? `<div class="notification-text">${description}</div>` : ""} + ${image ? `<img alt="Image." src="${image}" class="notification-image">` : ""} + </div> + </div> + ` + document.getElementById("notifications").innerHTML; + + setTimeout(() => { + document.getElementById("notification-" + id).style.opacity = "1"; + + if (!persistent) { + setTimeout(() => { + document.getElementById("notification-" + id).style.opacity = "0"; + + setTimeout(() => { + document.getElementById("notification-" + id).outerHTML = ""; + }, 500); + }, parseInt(localStorage.getItem("notification_duration"))); + } + }, 100); + + return id; +}
\ No newline at end of file diff --git a/web/src/scroll.js b/web/src/scroll.js new file mode 100644 index 0000000..8ea3bb8 --- /dev/null +++ b/web/src/scroll.js @@ -0,0 +1,111 @@ +let tracking = false; +let startY = 0; + +document.getElementById("swipe-zone").onmousedown = document.getElementById("swipe-zone").ontouchstart = (event) => { + tracking = true; + startY = (event.touches ? event.touches[0].clientY : event.clientY); + + document.getElementById("container").classList.remove("backing"); + document.getElementById("container").classList.remove("undeploying"); + document.getElementById("bg").style.opacity = ""; + document.getElementById("bg-blur").style.backdropFilter = ""; +} + +document.getElementById("swipe-zone").onmouseup = document.getElementById("swipe-zone").ontouchcancel = document.getElementById("swipe-zone").ontouchend = document.getElementById("swipe-zone").onmouseleave = (event) => { + tracking = false; + + if (parseInt(document.getElementById("container").style.top.split("vh")[0]) > -(parseInt(localStorage.getItem("ui_deploy_trigger")))) { + document.getElementById("container").style.top = "0vh"; + document.getElementById("container").classList.add("deployed"); + } else { + document.getElementById("container").style.top = "-100vh"; + document.getElementById("container").classList.add("undeploying"); + document.body.classList.remove("deploying"); + document.getElementById("bg").style.opacity = ""; + document.getElementById("bg-blur").style.backdropFilter = ""; + } +} + +document.getElementById("swipe-zone").onmousemove = document.getElementById("swipe-zone").ontouchmove = (event) => { + if (tracking) { + let triggerHeight = window.innerHeight / 2; + + let difference = (event.touches ? event.touches[0].clientY : event.clientY) - startY; + if (difference < 0) difference = 0; + + let percentage = difference / triggerHeight; + if (percentage > 1) percentage = 1; + + console.log((percentage * 100) + "vh", difference, triggerHeight); + document.getElementById("container").style.top = -(100 - (percentage * 100)) + "vh"; + + if (percentage > .05) { + let scaleSubtracter = percentage / 2; + let blurSubtracter = (percentage / 4) * 100; + + document.body.classList.add("deploying"); + document.getElementById("bg").style.opacity = (1 - scaleSubtracter).toString(); + if (localStorage.getItem("ui_deploy_blur") === "true") document.getElementById("bg-blur").style.backdropFilter = "blur(" + blurSubtracter + "px)"; + } else { + document.body.classList.remove("deploying"); + document.getElementById("bg").style.opacity = ""; + document.getElementById("bg-blur").style.transform = ""; + } + } +} + +document.getElementById("container").onmousedown = document.getElementById("container").ontouchstart = (event) => { + tracking = true; + startY = (event.touches ? event.touches[0].clientY : event.clientY); + + document.getElementById("bg").style.opacity = ""; + document.getElementById("bg-blur").style.backdropFilter = ""; +} + +document.getElementById("container").onmouseup = document.getElementById("container").onmouseleave = document.getElementById("container").ontouchend = document.getElementById("container").ontouchcancel = (event) => { + tracking = false; + document.getElementById("container").classList.remove("backing"); + + if (parseInt(document.getElementById("container").style.top.split("vh")[0]) > -(parseInt(localStorage.getItem("ui_deploy_trigger")))) { + document.getElementById("container").style.top = "0vh"; + document.getElementById("container").classList.add("deployed"); + } else { + document.getElementById("container").style.top = "-100vh"; + document.getElementById("container").classList.add("undeploying"); + document.body.classList.remove("deploying"); + document.getElementById("bg").style.opacity = ""; + document.getElementById("bg-blur").style.backdropFilter = ""; + } +} + +document.getElementById("container").onmousemove = document.getElementById("container").ontouchmove = (event) => { + if (tracking) { + document.getElementById("container").classList.remove("deployed"); + document.getElementById("container").classList.add("backing"); + + let triggerHeight = window.innerHeight / 2; + + let difference = startY - (event.touches ? event.touches[0].clientY : event.clientY); + if (difference < 0) difference = 0; + + let percentage = difference / triggerHeight; + if (percentage > 1) percentage = 1; + percentage = 1 - percentage; + + console.log((percentage * 100) + "vh", -(100 - (percentage * 100)) + "vh", difference, triggerHeight); + document.getElementById("container").style.top = -(100 - (percentage * 100)) + "vh"; + + if (percentage > .05) { + let scaleSubtracter = percentage / 2; + let blurSubtracter = (percentage / 4) * 100; + + document.body.classList.add("deploying"); + document.getElementById("bg").style.opacity = (1 - scaleSubtracter).toString(); + if (localStorage.getItem("ui_deploy_blur") === "true") document.getElementById("bg-blur").style.backdropFilter = "blur(" + blurSubtracter + "px)"; + } else { + document.body.classList.remove("deploying"); + document.getElementById("bg").style.opacity = ""; + document.getElementById("bg-blur").style.transform = ""; + } + } +}
\ No newline at end of file diff --git a/web/src/sleep.js b/web/src/sleep.js new file mode 100644 index 0000000..43f6f47 --- /dev/null +++ b/web/src/sleep.js @@ -0,0 +1,57 @@ +window.lastInteraction = new Date().getTime(); +window.inhibitSleep = false; +window.sleeping = false; + +document.body.onmousemove = document.body.onclick = document.body.ontouchmove = document.body.ontouchstart = () => { + window.lastInteraction = new Date().getTime(); +} + +setInterval(() => { + if (new Date().getTime() - window.lastInteraction > parseInt(localStorage.getItem("sleep_trigger_brightness"))) { + window.inhibitSleep = false; + document.getElementById("sleep").classList.remove("inhibited"); + } +}); + +function startInhibitSleep() { + window.sleeping = false; + document.getElementById("sleep").classList.add("inhibited"); + document.getElementById("sleep").classList.remove("show"); + window.inhibitSleep = true; +} + +window.loadingItems.push(() => { + let hasBrightness = false; + let brightnessError = "INTERNAL_ERROR"; + + if (window.engine && window.engine.getBrightness && window.engine.getSystemBrightness && window.engine.setBrightness) { + try { + window.engine.getBrightness(); + hasBrightness = true; + } catch (e) { + brightnessError = "INVALID_VALUE"; + } + } else if (window.engine) { + brightnessError = "INVALID_NATIVE_INTEGRATION"; + } else { + brightnessError = "NATIVE_INTEGRATION_NOTFOUND"; + } + + if (!hasBrightness) { + addWarning("[" + brightnessError + "] Unable to use light sensor, sleep mode and auto-brightness will be unavailable."); + + (async () => { + if (navigator.userAgent.includes("Ponyplay/")) sendNotification("Ponyplay System", await renderIcon("system", true), "Unable to access light sensor", "Your display will not adjust its content based on ambient light", null, true); + })(); + } + + setInterval(() => { + if (hasBrightness && window.engine.getBrightness() < parseInt(localStorage.getItem("sleep_trigger_brightness")) && !window.inhibitSleep && !(window.appOpen || localStorage.getItem("sleep_disable_wakelock") === "true" || window.alarmShown)) { + window.sleeping = true; + document.getElementById("sleep").classList.add("show"); + } else { + window.sleeping = false; + document.getElementById("sleep").classList.remove("show"); + } + }); +});
\ No newline at end of file diff --git a/web/src/theme.js b/web/src/theme.js new file mode 100644 index 0000000..d2d15b9 --- /dev/null +++ b/web/src/theme.js @@ -0,0 +1,63 @@ +window.loaded = false; + +function setBackground(url) { + const img = document.createElement("img"); + + img.onload = () => { + document.getElementById("bg").style.backgroundImage = 'url("' + url.replaceAll('"', '\\"') + '")'; + + let rgb = getAverageRGB(img); + let hue = rgb2hue(rgb.r, rgb.g, rgb.b); + + window.dynamicColor = { + _raw: {...rgb, hue: hue}, + text: rgbToHex(...hslToRgb(hue / 360, 0.35, 0.85)), + surface: rgbToHex(...hslToRgb(hue / 360, 0.35, 0.25)), + bg: rgbToHex(...hslToRgb(hue / 360, 0.35, 0.15)), + elevated: rgbToHex(...hslToRgb(hue / 360, 0.35, 0.35)), + onSurface: rgbToHex(...hslToRgb(hue / 360, 0.45, 0.75)), + onElevated: rgbToHex(...hslToRgb(hue / 360, 0.45, 0.65)) + } + + document.getElementById("dynamic-theme").innerText = ` + :root { + --text-color: #${window.dynamicColor.text}; + --surface-bg: #${window.dynamicColor.surface}; + --elevated-bg: #${window.dynamicColor.elevated}; + --bg: #${window.dynamicColor.bg}; + --on-surface: #${window.dynamicColor.onSurface}; + --on-elevated: #${window.dynamicColor.onElevated}; + } + `; + + (async () => { + document.getElementById("app-header-icon").src = await renderIcon("close", false); + document.getElementById("alarm-coming-icon").src = await renderIcon("alarms", false); + document.getElementById("app-6-icon-img").src = await renderIcon("settings", true); + document.getElementById("app-1-icon-img").src = await renderIcon("installer", true); + document.getElementById("app-2-icon-img").src = await renderIcon("alarms", true); + document.getElementById("app-3-icon-img").src = await renderIcon("face", true); + document.getElementById("app-5-icon-img").src = await renderIcon("weather", true); + document.getElementById("app-4-icon-img").src = await renderIcon("ponypush", true); + + if (!window.loaded) { + let defaults = await (await fetch("/settings.json?_" + Math.random())).json(); + + for (let setting of defaults) { + if (!localStorage.getItem(setting.name)) localStorage.setItem(setting.name, setting.default); + } + + load(); + window.loaded = true; + } + })(); + } + + img.src = url; +} + +async function renderIcon(icon, surfaced) { + return "data:image/svg+xml;base64," + btoa((await (await fetch("/icons/" + icon + ".svg")).text()).replace('width="48">', 'width="48" fill="#' + (surfaced ? window.dynamicColor.surface : window.dynamicColor.text) + '">').replace('#777777', '#' + (surfaced ? window.dynamicColor.surface : window.dynamicColor.text))); +} + +setBackground("/img/2023/8/12/3181099.jpg");
\ No newline at end of file diff --git a/web/src/timedate.js b/web/src/timedate.js new file mode 100644 index 0000000..5930727 --- /dev/null +++ b/web/src/timedate.js @@ -0,0 +1,16 @@ +function fixed(number, digits) { + number = Math.round(number); + return "0".repeat(digits).substring(0, digits - number.toString().length) + number.toString(); +} + +document.getElementById("time").innerText = fixed(new Date().getHours(), 2) + ":" + fixed(new Date().getMinutes(), 2); +document.getElementById("time-sleep").innerText = fixed(new Date().getHours(), 2) + ":" + fixed(new Date().getMinutes(), 2); +document.getElementById("time-alarm").innerText = fixed(new Date().getHours(), 2) + ":" + fixed(new Date().getMinutes(), 2); +document.getElementById("date").innerText = (new Date().toDateString().split(" "))[0] + ", " + (new Date().toDateString().split(" "))[2] + " " + (new Date().toDateString().split(" "))[1]; + +setInterval(() => { + document.getElementById("time").innerText = fixed(new Date().getHours(), 2) + ":" + fixed(new Date().getMinutes(), 2); + document.getElementById("time-sleep").innerText = fixed(new Date().getHours(), 2) + ":" + fixed(new Date().getMinutes(), 2); + document.getElementById("time-alarm").innerText = fixed(new Date().getHours(), 2) + ":" + fixed(new Date().getMinutes(), 2); + document.getElementById("date").innerText = (new Date().toDateString().split(" "))[0] + ", " + (new Date().toDateString().split(" "))[2] + " " + (new Date().toDateString().split(" "))[1]; +}, 1000);
\ No newline at end of file diff --git a/web/src/weather.js b/web/src/weather.js new file mode 100644 index 0000000..666ac4c --- /dev/null +++ b/web/src/weather.js @@ -0,0 +1,57 @@ +window.loadingItems.push(() => { + window.latitude = 0; + window.longitude = 0; + + navigator.geolocation.getCurrentPosition(async (pos) => { + window.latitude = pos.coords.latitude; + window.longitude = pos.coords.longitude; + + setInterval(() => { + refreshWeather(); + }, parseInt(localStorage.getItem("weather_refresh_interval"))); + + refreshWeather(); + }, async (error) => { + switch(error.code) { + case error.PERMISSION_DENIED: + addWarning("[GEO_PERMISSION_DENIED] Permission to use location was denied, weather will be unavailable."); + if (navigator.userAgent.includes("Ponyplay/")) sendNotification("Ponyplay System", await renderIcon("system", true), "Unable to access location", "Permission to access your location was denied", null, true); + break; + case error.POSITION_UNAVAILABLE: + addWarning("[GEO_POSITION_UNAVAILABLE] Gathering a location failed, weather will be unavailable."); + if (navigator.userAgent.includes("Ponyplay/")) sendNotification("Ponyplay System", await renderIcon("system", true), "Unable to access location", "Ponyplay was unable to figure out your physical position", null, true); + break; + case error.TIMEOUT: + addWarning("[GEO_TIMEOUT] Gathering a location timed out, weather will be unavailable."); + if (navigator.userAgent.includes("Ponyplay/")) sendNotification("Ponyplay System", await renderIcon("system", true), "Unable to access location", "Ponyplay was unable to access your location in time", null, true); + break; + default: + addWarning("[GEO_UNKNOWN_ERROR] An unknown error occurred while gathering a location, weather will be unavailable."); + if (navigator.userAgent.includes("Ponyplay/")) sendNotification("Ponyplay System", await renderIcon("system", true), "Unable to access location", "An unknown error occurred while gathering your location", null, true); + break; + } + + if (localStorage.getItem("weather_iplocation_fallback") === "true") { + let location = (await (await fetch("https://ipinfo.io/json")).json())['loc'].split(",").map(i => parseFloat(i)); + + window.latitude = location[0]; + window.longitude = location[1]; + + setInterval(() => { + refreshWeather(); + }, parseInt(localStorage.getItem("weather_refresh_interval"))); + + refreshWeather(); + } + }); + + async function refreshWeather() { + try { + window.weather = await (await fetch("https://api.openweathermap.org/data/2.5/weather?lat=" + window.latitude + "&lon=" + window.longitude + "&appid=" + localStorage.getItem("openweathermap_api_key") + "&units=metric")).json(); + window.weatherIcons = await (await fetch("/weather/list.json")).json(); + + document.getElementById("temperature").innerText = Math.round(window.weather['main']['temp']) + "°"; + document.getElementById("weather").src = "/weather/" + (window.weatherIcons.includes(window.weather.weather[0].id + window.weather.weather[0].icon.substring(2, 3) + ".png") ? window.weather.weather[0].id + window.weather.weather[0].icon.substring(2, 3) + ".png" : window.weather.weather[0].icon + ".png"); + } catch (e) {} + } +});
\ No newline at end of file diff --git a/web/test.html b/web/test.html new file mode 100644 index 0000000..c7b57e3 --- /dev/null +++ b/web/test.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>app</title> +</head> +<body> + <h1 id="message">JavaScript</h1> + <p> + <textarea id="input" autocomplete="off" autocapitalize="off"></textarea> + </p> + <button onclick="run();">Run</button> + <hr> + <pre id="output"></pre> + <hr> + <video autoplay id="preview" src="/test.webm" controls style="width: 500px; height: 200px;"></video> + + <script> + function run() { + try { + document.getElementById("output").innerText = eval(document.getElementById("input").value); + } catch (e) { + document.getElementById("output").innerText = e.stack; + } + } + + /*navigator.mediaDevices.getUserMedia({ video: {} }) + .then(function(mediaStream) { + let video = document.querySelector('video'); + video.srcObject = mediaStream; + }) + .catch(function(err) { document.getElementById("message").innerText = err.name + ": " + err.message; }); + + setInterval(() => { + if (document.querySelector('video').paused) { + document.querySelector('video').play(); + } + })*/ + </script> +</body> +</html>
\ No newline at end of file diff --git a/web/weather/01d.png b/web/weather/01d.png Binary files differnew file mode 100644 index 0000000..22fd67f --- /dev/null +++ b/web/weather/01d.png diff --git a/web/weather/01n.png b/web/weather/01n.png Binary files differnew file mode 100644 index 0000000..289d923 --- /dev/null +++ b/web/weather/01n.png diff --git a/web/weather/02d.png b/web/weather/02d.png Binary files differnew file mode 100644 index 0000000..3aa2038 --- /dev/null +++ b/web/weather/02d.png diff --git a/web/weather/02n.png b/web/weather/02n.png Binary files differnew file mode 100644 index 0000000..3a785aa --- /dev/null +++ b/web/weather/02n.png diff --git a/web/weather/03d.png b/web/weather/03d.png Binary files differnew file mode 100644 index 0000000..6371747 --- /dev/null +++ b/web/weather/03d.png diff --git a/web/weather/03n.png b/web/weather/03n.png Binary files differnew file mode 100644 index 0000000..19b80d7 --- /dev/null +++ b/web/weather/03n.png diff --git a/web/weather/04d.png b/web/weather/04d.png Binary files differnew file mode 100644 index 0000000..20d9245 --- /dev/null +++ b/web/weather/04d.png diff --git a/web/weather/04n.png b/web/weather/04n.png Binary files differnew file mode 100644 index 0000000..20d9245 --- /dev/null +++ b/web/weather/04n.png diff --git a/web/weather/09d.png b/web/weather/09d.png Binary files differnew file mode 100644 index 0000000..c3daa01 --- /dev/null +++ b/web/weather/09d.png diff --git a/web/weather/09n.png b/web/weather/09n.png Binary files differnew file mode 100644 index 0000000..c3daa01 --- /dev/null +++ b/web/weather/09n.png diff --git a/web/weather/10d.png b/web/weather/10d.png Binary files differnew file mode 100644 index 0000000..2bb071a --- /dev/null +++ b/web/weather/10d.png diff --git a/web/weather/10n.png b/web/weather/10n.png Binary files differnew file mode 100644 index 0000000..8073e99 --- /dev/null +++ b/web/weather/10n.png diff --git a/web/weather/11d.png b/web/weather/11d.png Binary files differnew file mode 100644 index 0000000..a5784a9 --- /dev/null +++ b/web/weather/11d.png diff --git a/web/weather/11n.png b/web/weather/11n.png Binary files differnew file mode 100644 index 0000000..acc1dbb --- /dev/null +++ b/web/weather/11n.png diff --git a/web/weather/13d.png b/web/weather/13d.png Binary files differnew file mode 100644 index 0000000..d3d1ba6 --- /dev/null +++ b/web/weather/13d.png diff --git a/web/weather/13n.png b/web/weather/13n.png Binary files differnew file mode 100644 index 0000000..d3d1ba6 --- /dev/null +++ b/web/weather/13n.png diff --git a/web/weather/202d.png b/web/weather/202d.png Binary files differnew file mode 100644 index 0000000..bcdc1e0 --- /dev/null +++ b/web/weather/202d.png diff --git a/web/weather/202n.png b/web/weather/202n.png Binary files differnew file mode 100644 index 0000000..bcdc1e0 --- /dev/null +++ b/web/weather/202n.png diff --git a/web/weather/211d.png b/web/weather/211d.png Binary files differnew file mode 100644 index 0000000..15a15e4 --- /dev/null +++ b/web/weather/211d.png diff --git a/web/weather/211n.png b/web/weather/211n.png Binary files differnew file mode 100644 index 0000000..15a15e4 --- /dev/null +++ b/web/weather/211n.png diff --git a/web/weather/212d.png b/web/weather/212d.png Binary files differnew file mode 100644 index 0000000..15a15e4 --- /dev/null +++ b/web/weather/212d.png diff --git a/web/weather/212n.png b/web/weather/212n.png Binary files differnew file mode 100644 index 0000000..15a15e4 --- /dev/null +++ b/web/weather/212n.png diff --git a/web/weather/221d.png b/web/weather/221d.png Binary files differnew file mode 100644 index 0000000..15a15e4 --- /dev/null +++ b/web/weather/221d.png diff --git a/web/weather/221n.png b/web/weather/221n.png Binary files differnew file mode 100644 index 0000000..15a15e4 --- /dev/null +++ b/web/weather/221n.png diff --git a/web/weather/232d.png b/web/weather/232d.png Binary files differnew file mode 100644 index 0000000..bcdc1e0 --- /dev/null +++ b/web/weather/232d.png diff --git a/web/weather/232n.png b/web/weather/232n.png Binary files differnew file mode 100644 index 0000000..bcdc1e0 --- /dev/null +++ b/web/weather/232n.png diff --git a/web/weather/300d.png b/web/weather/300d.png Binary files differnew file mode 100644 index 0000000..75853f6 --- /dev/null +++ b/web/weather/300d.png diff --git a/web/weather/300n.png b/web/weather/300n.png Binary files differnew file mode 100644 index 0000000..75853f6 --- /dev/null +++ b/web/weather/300n.png diff --git a/web/weather/500d.png b/web/weather/500d.png Binary files differnew file mode 100644 index 0000000..75853f6 --- /dev/null +++ b/web/weather/500d.png diff --git a/web/weather/500n.png b/web/weather/500n.png Binary files differnew file mode 100644 index 0000000..75853f6 --- /dev/null +++ b/web/weather/500n.png diff --git a/web/weather/50d.png b/web/weather/50d.png Binary files differnew file mode 100644 index 0000000..fde3800 --- /dev/null +++ b/web/weather/50d.png diff --git a/web/weather/50n.png b/web/weather/50n.png Binary files differnew file mode 100644 index 0000000..fde3800 --- /dev/null +++ b/web/weather/50n.png diff --git a/web/weather/511d.png b/web/weather/511d.png Binary files differnew file mode 100644 index 0000000..c21fbd6 --- /dev/null +++ b/web/weather/511d.png diff --git a/web/weather/511n.png b/web/weather/511n.png Binary files differnew file mode 100644 index 0000000..c21fbd6 --- /dev/null +++ b/web/weather/511n.png diff --git a/web/weather/615d.png b/web/weather/615d.png Binary files differnew file mode 100644 index 0000000..c21fbd6 --- /dev/null +++ b/web/weather/615d.png diff --git a/web/weather/615n.png b/web/weather/615n.png Binary files differnew file mode 100644 index 0000000..c21fbd6 --- /dev/null +++ b/web/weather/615n.png diff --git a/web/weather/616d.png b/web/weather/616d.png Binary files differnew file mode 100644 index 0000000..cbc921f --- /dev/null +++ b/web/weather/616d.png diff --git a/web/weather/616n.png b/web/weather/616n.png Binary files differnew file mode 100644 index 0000000..cbc921f --- /dev/null +++ b/web/weather/616n.png diff --git a/web/weather/804d.png b/web/weather/804d.png Binary files differnew file mode 100644 index 0000000..8c8c451 --- /dev/null +++ b/web/weather/804d.png diff --git a/web/weather/804n.png b/web/weather/804n.png Binary files differnew file mode 100644 index 0000000..8c8c451 --- /dev/null +++ b/web/weather/804n.png diff --git a/web/weather/list.json b/web/weather/list.json new file mode 100644 index 0000000..4ac36da --- /dev/null +++ b/web/weather/list.json @@ -0,0 +1 @@ +["01d.png","01n.png","02d.png","02n.png","03d.png","03n.png","04d.png","04n.png","09d.png","09n.png","10d.png","10n.png","11d.png","11n.png","13d.png","13n.png","202d.png","202n.png","211d.png","211n.png","212d.png","212n.png","221d.png","221n.png","232d.png","232n.png","300d.png","300n.png","500d.png","500n.png","50d.png","50n.png","511d.png","511n.png","615d.png","615n.png","616d.png","616n.png","804d.png","804n.png"]
\ No newline at end of file |