Publish Forma Engine 0.3.0 source with documentation and CI
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
plugins { id 'com.android.application' }
|
||||
def game = new groovy.json.JsonSlurper().parse(rootProject.file('game-config.json'))
|
||||
def keyPath = System.getenv('FORMA_KEYSTORE')
|
||||
android {
|
||||
namespace 'com.forma.shell'
|
||||
compileSdk 36
|
||||
buildToolsVersion '35.0.0'
|
||||
defaultConfig {
|
||||
applicationId game.appId
|
||||
minSdk 26
|
||||
targetSdk 36
|
||||
versionCode game.versionCode
|
||||
versionName game.version
|
||||
resValue 'string', 'app_name', '"' + game.name + '"'
|
||||
manifestPlaceholders = [gameOrientation: game.orientation == 'landscape' ? 'sensorLandscape' : game.orientation == 'portrait' ? 'sensorPortrait' : 'fullSensor']
|
||||
}
|
||||
signingConfigs {
|
||||
release {
|
||||
if (keyPath) {
|
||||
storeFile file(keyPath)
|
||||
storePassword System.getenv('FORMA_KEYSTORE_PASSWORD')
|
||||
keyAlias System.getenv('FORMA_KEY_ALIAS')
|
||||
keyPassword System.getenv('FORMA_KEY_PASSWORD')
|
||||
}
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
debug { debuggable true }
|
||||
release { debuggable false; minifyEnabled false; signingConfig signingConfigs.release }
|
||||
}
|
||||
compileOptions { sourceCompatibility JavaVersion.VERSION_17; targetCompatibility JavaVersion.VERSION_17 }
|
||||
}
|
||||
dependencies { implementation 'androidx.webkit:webkit:1.14.0' }
|
||||
@@ -0,0 +1,8 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-feature android:glEsVersion="0x00030000" android:required="true" />
|
||||
<application android:icon="@drawable/forma_icon" android:label="@string/app_name" android:theme="@android:style/Theme.Material.NoActionBar" android:hardwareAccelerated="true" android:allowBackup="false" android:usesCleartextTraffic="false" android:appCategory="game">
|
||||
<activity android:name=".MainActivity" android:exported="true" android:screenOrientation="${gameOrientation}" android:configChanges="orientation|screenSize|keyboardHidden|smallestScreenSize|screenLayout|uiMode" >
|
||||
<intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.forma.shell;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.WindowInsetsController;
|
||||
import android.view.WindowManager;
|
||||
import android.webkit.*;
|
||||
import android.window.OnBackInvokedDispatcher;
|
||||
import androidx.webkit.WebViewAssetLoader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.util.HashMap;
|
||||
|
||||
public final class MainActivity extends Activity {
|
||||
private WebView game;
|
||||
private static final String ENTRY = "https://appassets.androidplatform.net/assets/game/index.html";
|
||||
private static final String CSP = "default-src 'none'; script-src 'self' 'unsafe-eval'; worker-src 'self' blob:; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; connect-src 'self' data: blob:; font-src 'self' data:; media-src 'self' blob:; object-src 'none'; base-uri 'none'; frame-src 'none'";
|
||||
@Override public void onCreate(Bundle saved) {
|
||||
super.onCreate(saved);
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
createGame();
|
||||
if (Build.VERSION.SDK_INT >= 33) getOnBackInvokedDispatcher().registerOnBackInvokedCallback(OnBackInvokedDispatcher.PRIORITY_DEFAULT, this::confirmExit);
|
||||
}
|
||||
@SuppressWarnings("SetJavaScriptEnabled") private void createGame() {
|
||||
game = new WebView(this);
|
||||
game.setBackgroundColor(0xff151719);
|
||||
WebSettings s = game.getSettings();
|
||||
s.setJavaScriptEnabled(true);
|
||||
s.setDomStorageEnabled(true);
|
||||
s.setAllowFileAccess(false);
|
||||
s.setAllowContentAccess(false);
|
||||
s.setMixedContentMode(WebSettings.MIXED_CONTENT_NEVER_ALLOW);
|
||||
s.setMediaPlaybackRequiresUserGesture(true);
|
||||
s.setSupportMultipleWindows(false);
|
||||
WebView.setWebContentsDebuggingEnabled((getApplicationInfo().flags & android.content.pm.ApplicationInfo.FLAG_DEBUGGABLE) != 0);
|
||||
final WebViewAssetLoader loader = new WebViewAssetLoader.Builder().addPathHandler("/assets/", new WebViewAssetLoader.AssetsPathHandler(this)).build();
|
||||
game.setWebViewClient(new WebViewClient() {
|
||||
@Override public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
|
||||
WebResourceResponse response = null;
|
||||
if ("GET".equals(request.getMethod()) && "https".equals(request.getUrl().getScheme()) && "appassets.androidplatform.net".equals(request.getUrl().getHost()) && request.getUrl().getPath().startsWith("/assets/game/")) response = loader.shouldInterceptRequest(request.getUrl());
|
||||
if (response == null) return new WebResourceResponse("text/plain", "UTF-8", 404, "Not found", new HashMap<>(), new ByteArrayInputStream(new byte[0]));
|
||||
HashMap<String,String> headers = new HashMap<>();
|
||||
headers.put("Content-Security-Policy", CSP);
|
||||
headers.put("X-Content-Type-Options", "nosniff");
|
||||
response.setResponseHeaders(headers);
|
||||
return response;
|
||||
}
|
||||
@Override public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { return !ENTRY.equals(request.getUrl().toString()); }
|
||||
@Override public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) {
|
||||
view.destroy(); game = null;
|
||||
new AlertDialog.Builder(MainActivity.this).setTitle("Игра остановлена").setMessage("Обновите Android System WebView или уменьшите качество графики в проекте.").setPositiveButton("Перезапустить", (d,w) -> createGame()).setNegativeButton("Выйти", (d,w) -> finish()).setCancelable(false).show();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
game.setWebChromeClient(new WebChromeClient());
|
||||
setContentView(game);
|
||||
game.loadUrl(ENTRY);
|
||||
immersive();
|
||||
}
|
||||
private void immersive() {
|
||||
if (Build.VERSION.SDK_INT >= 30) {
|
||||
WindowInsetsController c = getWindow().getInsetsController();
|
||||
if (c != null) { c.hide(WindowInsets.Type.systemBars()); c.setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE); }
|
||||
} else getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
|
||||
}
|
||||
@Override public void onWindowFocusChanged(boolean focused) { super.onWindowFocusChanged(focused); if (focused) immersive(); }
|
||||
@Override protected void onPause() { if (game != null) {game.onPause();game.pauseTimers();} super.onPause(); }
|
||||
@Override protected void onResume() { super.onResume(); if (game != null) {game.onResume();game.resumeTimers();} }
|
||||
@Override public void onBackPressed() { confirmExit(); }
|
||||
private void confirmExit() { new AlertDialog.Builder(this).setTitle("Выйти из игры?").setPositiveButton("Выйти", (d,w) -> finish()).setNegativeButton("Продолжить", null).show(); }
|
||||
@Override protected void onDestroy() { if (game != null) game.destroy(); super.onDestroy(); }
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="108dp" android:height="108dp" android:viewportWidth="40" android:viewportHeight="40">
|
||||
<path android:fillColor="#b77c60" android:pathData="M0,0h40v40h-40z" />
|
||||
<path android:fillColor="@android:color/transparent" android:strokeColor="#fff5df" android:strokeWidth="1.8" android:strokeLineJoin="round" android:pathData="M20,8 L31,14.5 L31,26.5 L20,33 L9,26.5 L9,14.5 Z M9,14.5 L20,20.5 L31,14.5 M20,20.5 L20,33 M20,8 L20,20" />
|
||||
</vector>
|
||||
@@ -0,0 +1 @@
|
||||
plugins { id 'com.android.application' version '8.13.2' apply false }
|
||||
@@ -0,0 +1,3 @@
|
||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||
android.useAndroidX=true
|
||||
org.gradle.daemon=false
|
||||
@@ -0,0 +1,4 @@
|
||||
pluginManagement { repositories { google(); mavenCentral(); gradlePluginPortal() } }
|
||||
dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS); repositories { google(); mavenCentral() } }
|
||||
rootProject.name = 'FormaGame'
|
||||
include ':app'
|
||||
Reference in New Issue
Block a user