mirror of
https://gitlab.com/lecarore/breakout71.git
synced 2025-04-20 12:15:06 -04:00
Build 29038466
This commit is contained in:
parent
ffdbd71a88
commit
83b9b8b9e8
12 changed files with 7661 additions and 80 deletions
|
@ -11,8 +11,8 @@ android {
|
||||||
applicationId = "me.lecaro.breakout"
|
applicationId = "me.lecaro.breakout"
|
||||||
minSdk = 21
|
minSdk = 21
|
||||||
targetSdk = 34
|
targetSdk = 34
|
||||||
versionCode = 29038230
|
versionCode = 29038466
|
||||||
versionName = "29038230"
|
versionName = "29038466"
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables {
|
vectorDrawables {
|
||||||
useSupportLibrary = true
|
useSupportLibrary = true
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
<?xml version="1.0" encoding ="utf-8"?>
|
<?xml version="1.0" encoding ="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
|
||||||
android:maxSdkVersion="32" />
|
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
|
|
||||||
android:maxSdkVersion="32" />
|
|
||||||
<application
|
<application
|
||||||
android:requestLegacyExternalStorage="true"
|
android:requestLegacyExternalStorage="true"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
|
@ -27,5 +23,6 @@
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
File diff suppressed because one or more lines are too long
|
@ -2,12 +2,15 @@ package me.lecaro.breakout
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.app.DownloadManager
|
import android.app.DownloadManager
|
||||||
|
import android.content.ContentValues
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
|
import android.provider.MediaStore
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.Window
|
import android.view.Window
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
|
@ -35,8 +38,7 @@ class MainActivity : android.app.Activity() {
|
||||||
if (resultCode == RESULT_OK) {
|
if (resultCode == RESULT_OK) {
|
||||||
filePathCallback?.onReceiveValue(
|
filePathCallback?.onReceiveValue(
|
||||||
WebChromeClient.FileChooserParams.parseResult(
|
WebChromeClient.FileChooserParams.parseResult(
|
||||||
resultCode,
|
resultCode, data
|
||||||
data
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
filePathCallback = null
|
filePathCallback = null
|
||||||
|
@ -44,23 +46,10 @@ class MainActivity : android.app.Activity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
|
||||||
when (requestCode) {
|
|
||||||
PERM_REQUEST_CODE -> {
|
|
||||||
if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
|
|
||||||
downloadFile()
|
|
||||||
} else {
|
|
||||||
Toast.makeText(this, "We cant make a save file without that permission", Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var filePathCallback: ValueCallback<Array<Uri>>? = null
|
|
||||||
var fileToDownload:String? = null
|
|
||||||
|
|
||||||
fun downloadFile(){
|
var filePathCallback: ValueCallback<Array<Uri>>? = null
|
||||||
val url = fileToDownload ?: return
|
|
||||||
|
private fun downloadFile(url: String) {
|
||||||
try {
|
try {
|
||||||
if (!url.startsWith("data:")) {
|
if (!url.startsWith("data:")) {
|
||||||
Log.w("DL", "url ignored because it does not start with data:")
|
Log.w("DL", "url ignored because it does not start with data:")
|
||||||
|
@ -68,44 +57,70 @@ class MainActivity : android.app.Activity() {
|
||||||
}
|
}
|
||||||
val sdf = SimpleDateFormat("yyyy-M-dd-hh-mm")
|
val sdf = SimpleDateFormat("yyyy-M-dd-hh-mm")
|
||||||
val currentDate = sdf.format(Date())
|
val currentDate = sdf.format(Date())
|
||||||
// Extract filename from contentDisposition if available
|
val base64Data = url.substringAfterLast(',')
|
||||||
|
val decodedBytes = android.util.Base64.decode(base64Data, android.util.Base64.DEFAULT)
|
||||||
|
|
||||||
if (url.startsWith("data:application/json;base64,")) {
|
if (url.startsWith("data:application/json;base64,")) {
|
||||||
Log.d("DL", "saving application/json ")
|
writeFile(decodedBytes, "breakout-71-save-$currentDate.b71", "application/b71")
|
||||||
val base64Data = url.substringAfterLast(',')
|
|
||||||
val decodedBytes =
|
|
||||||
android.util.Base64.decode(base64Data, android.util.Base64.DEFAULT)
|
|
||||||
val jsonData = String(decodedBytes);
|
|
||||||
val dir =
|
|
||||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
|
|
||||||
val fileName = "breakout-71-save-$currentDate.b71"
|
|
||||||
val file = File(dir, fileName)
|
|
||||||
file.writeText(jsonData)
|
|
||||||
Toast.makeText(this, "Saved in $dir", Toast.LENGTH_LONG).show()
|
|
||||||
Log.d("DL", "finished saving application/json ")
|
|
||||||
|
|
||||||
} else if (url.startsWith("data:video/webm;base64,")) {
|
} else if (url.startsWith("data:video/webm;base64,")) {
|
||||||
Log.d("DL", "saving video/webm ")
|
writeFile(decodedBytes, "breakout-71-gameplay-capture-$currentDate.webm", "application/b71")
|
||||||
// TODO
|
|
||||||
Log.d("DL", "finished savign video/webm ")
|
|
||||||
} else {
|
} else {
|
||||||
Log.w("DL", "unexpected type " + url)
|
Log.w("DL", "unexpected type " + url)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e("DL", "Error ${e.message}")
|
Log.e("DL", "Error ${e.message}")
|
||||||
Toast.makeText(this, "Error ${e.message}", Toast.LENGTH_LONG).show()
|
Toast.makeText(this, "Error ${e.message}", Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun writeFile(decodedBytes:ByteArray,fileName:String, mime:String){
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
val jsonData = String(decodedBytes);
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
|
|
||||||
|
|
||||||
|
val contentValues = ContentValues().apply {
|
||||||
|
put(MediaStore.Downloads.DISPLAY_NAME, fileName)
|
||||||
|
put(MediaStore.Downloads.MIME_TYPE,mime )
|
||||||
|
put(MediaStore.Downloads.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS)
|
||||||
|
}
|
||||||
|
|
||||||
|
val uri: Uri? = contentResolver.insert(
|
||||||
|
MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues
|
||||||
|
)
|
||||||
|
uri?.let {
|
||||||
|
contentResolver.openOutputStream(it)?.use { outputStream ->
|
||||||
|
outputStream.write(decodedBytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val shareIntent: Intent = Intent().apply {
|
||||||
|
action = Intent.ACTION_SEND
|
||||||
|
// Example: content://com.google.android.apps.photos.contentprovider/...
|
||||||
|
putExtra(Intent.EXTRA_STREAM, uri)
|
||||||
|
type = mime
|
||||||
|
}
|
||||||
|
startActivity(Intent.createChooser(shareIntent, null))
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
|
||||||
|
val dir = getExternalFilesDir(null)
|
||||||
|
val file = File(dir, fileName)
|
||||||
|
file.writeText(jsonData)
|
||||||
|
Toast.makeText(this, "Saved in $dir", Toast.LENGTH_LONG).show()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||||
window.setFlags(
|
window.setFlags(
|
||||||
WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN
|
||||||
WindowManager.LayoutParams.FLAG_FULLSCREEN
|
|
||||||
);
|
);
|
||||||
val webView = WebView(this)
|
val webView = WebView(this)
|
||||||
webView.settings.javaScriptEnabled = true
|
webView.settings.javaScriptEnabled = true
|
||||||
|
@ -118,8 +133,8 @@ class MainActivity : android.app.Activity() {
|
||||||
webView.webChromeClient = object : WebChromeClient() {
|
webView.webChromeClient = object : WebChromeClient() {
|
||||||
override fun onConsoleMessage(consoleMessage: ConsoleMessage): Boolean {
|
override fun onConsoleMessage(consoleMessage: ConsoleMessage): Boolean {
|
||||||
Log.d(
|
Log.d(
|
||||||
"WebView", "${consoleMessage.message()} -- From line " +
|
"WebView",
|
||||||
"${consoleMessage.lineNumber()} of ${consoleMessage.sourceId()}"
|
"${consoleMessage.message()} -- From line " + "${consoleMessage.lineNumber()} of ${consoleMessage.sourceId()}"
|
||||||
)
|
)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -131,7 +146,9 @@ class MainActivity : android.app.Activity() {
|
||||||
): Boolean {
|
): Boolean {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
startActivityForResult(fileChooserParams?.createIntent(), CHOOSE_FILE_REQUEST_CODE)
|
startActivityForResult(
|
||||||
|
fileChooserParams?.createIntent(), CHOOSE_FILE_REQUEST_CODE
|
||||||
|
)
|
||||||
this@MainActivity.filePathCallback = filePathCallback
|
this@MainActivity.filePathCallback = filePathCallback
|
||||||
return true
|
return true
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
@ -144,14 +161,7 @@ class MainActivity : android.app.Activity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
webView.setDownloadListener(DownloadListener { url, userAgent, contentDisposition, mimetype, contentLength ->
|
webView.setDownloadListener(DownloadListener { url, userAgent, contentDisposition, mimetype, contentLength ->
|
||||||
|
downloadFile(url)
|
||||||
fileToDownload = url
|
|
||||||
if (activity.checkSelfPermission( android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
|
||||||
!= PackageManager.PERMISSION_GRANTED) {
|
|
||||||
activity.requestPermissions(arrayOf( android.Manifest.permission.WRITE_EXTERNAL_STORAGE), PERM_REQUEST_CODE)
|
|
||||||
}else{
|
|
||||||
downloadFile()
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
33
dist/PWA/sw-b71.js
vendored
33
dist/PWA/sw-b71.js
vendored
|
@ -1,2 +1,33 @@
|
||||||
function e(e,t,n,r,a,i,c){try{var o=e[i](c),u=o.value}catch(e){n(e);return}o.done?t(u):Promise.resolve(u).then(r,a)}function t(t){return function(){var n=this,r=arguments;return new Promise(function(a,i){var c=t.apply(n,r);function o(t){e(c,a,i,o,u,"next",t)}function u(t){e(c,a,i,o,u,"throw",t)}o(void 0)})}}function n(e,t){var n,r,a,i,c={label:0,sent:function(){if(1&a[0])throw a[1];return a[1]},trys:[],ops:[]};return i={next:o(0),throw:o(1),return:o(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function o(i){return function(o){return function(i){if(n)throw TypeError("Generator is already executing.");for(;c;)try{if(n=1,r&&(a=2&i[0]?r.return:i[0]?r.throw||((a=r.return)&&a.call(r),0):r.next)&&!(a=a.call(r,i[1])).done)return a;switch(r=0,a&&(i=[2&i[0],a.value]),i[0]){case 0:case 1:a=i;break;case 4:return c.label++,{value:i[1],done:!1};case 5:c.label++,r=i[1],i=[0];continue;case 7:i=c.ops.pop(),c.trys.pop();continue;default:if(!(a=(a=c.trys).length>0&&a[a.length-1])&&(6===i[0]||2===i[0])){c=0;continue}if(3===i[0]&&(!a||i[1]>a[0]&&i[1]<a[3])){c.label=i[1];break}if(6===i[0]&&c.label<a[1]){c.label=a[1],a=i;break}if(a&&c.label<a[2]){c.label=a[2],c.ops.push(i);break}a[2]&&c.ops.pop(),c.trys.pop();continue}i=t.call(e,c)}catch(e){i=[6,e],r=0}finally{n=a=0}if(5&i[0])throw i[1];return{value:i[0]?i[1]:void 0,done:!0}}([i,o])}}}var r="breakout-71-".concat("29038230"),a=["/"];self.addEventListener("install",function(e){e.waitUntil(t(function(){return n(this,function(e){switch(e.label){case 0:return[4,caches.open(r)];case 1:return e.sent().addAll(a),[2]}})})())}),self.addEventListener("activate",function(e){e.waitUntil(t(function(){return n(this,function(e){switch(e.label){case 0:return[4,caches.keys()];case 1:return[4,Promise.all(e.sent().map(function(e){if(e!==r)return caches.delete(e)}))];case 2:return e.sent(),[4,clients.claim()];case 3:return e.sent(),[2]}})})())}),self.addEventListener("fetch",function(e){if("navigate"===e.request.mode&&e.request.url.endsWith("/index.html?isPWA=true")){e.respondWith(caches.match("/"));return}});
|
// The version of the cache.
|
||||||
|
const VERSION = "29038466";
|
||||||
|
// The name of the cache
|
||||||
|
const CACHE_NAME = `breakout-71-${VERSION}`;
|
||||||
|
// The static resources that the app needs to function.
|
||||||
|
const APP_STATIC_RESOURCES = [
|
||||||
|
"/"
|
||||||
|
];
|
||||||
|
// On install, cache the static resources
|
||||||
|
self.addEventListener("install", (event)=>{
|
||||||
|
event.waitUntil((async ()=>{
|
||||||
|
const cache = await caches.open(CACHE_NAME);
|
||||||
|
cache.addAll(APP_STATIC_RESOURCES);
|
||||||
|
})());
|
||||||
|
});
|
||||||
|
// delete old caches on activate
|
||||||
|
self.addEventListener("activate", (event)=>{
|
||||||
|
event.waitUntil((async ()=>{
|
||||||
|
const names = await caches.keys();
|
||||||
|
await Promise.all(names.map((name)=>{
|
||||||
|
if (name !== CACHE_NAME) return caches.delete(name);
|
||||||
|
}));
|
||||||
|
await clients.claim();
|
||||||
|
})());
|
||||||
|
});
|
||||||
|
self.addEventListener("fetch", (event)=>{
|
||||||
|
if (event.request.mode === "navigate" && event.request.url.endsWith("/index.html?isPWA=true")) {
|
||||||
|
event.respondWith(caches.match("/"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
//# sourceMappingURL=sw-b71.js.map
|
//# sourceMappingURL=sw-b71.js.map
|
||||||
|
|
2
dist/PWA/sw-b71.js.map
vendored
2
dist/PWA/sw-b71.js.map
vendored
File diff suppressed because one or more lines are too long
3765
dist/index.html
vendored
3765
dist/index.html
vendored
File diff suppressed because one or more lines are too long
|
@ -1,5 +1,5 @@
|
||||||
// The version of the cache.
|
// The version of the cache.
|
||||||
const VERSION = "29038230";
|
const VERSION = "29038466";
|
||||||
|
|
||||||
// The name of the cache
|
// The name of the cache
|
||||||
const CACHE_NAME = `breakout-71-${VERSION}`;
|
const CACHE_NAME = `breakout-71-${VERSION}`;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
"29038230"
|
"29038466"
|
||||||
|
|
|
@ -1284,9 +1284,7 @@ export function append<T>(
|
||||||
where.list[where.indexMin].destroyed = false;
|
where.list[where.indexMin].destroyed = false;
|
||||||
makeItem(where.list[where.indexMin]);
|
makeItem(where.list[where.indexMin]);
|
||||||
where.indexMin++;
|
where.indexMin++;
|
||||||
console.log("Reused item " + where.indexMin);
|
|
||||||
} else {
|
} else {
|
||||||
console.log("Created item " + where.indexMin);
|
|
||||||
const p = { destroyed: false };
|
const p = { destroyed: false };
|
||||||
makeItem(p);
|
makeItem(p);
|
||||||
where.list.push(p);
|
where.list.push(p);
|
||||||
|
@ -1321,5 +1319,3 @@ export function forEachLiveOne<T>(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO check destroyed usage in code
|
|
||||||
|
|
|
@ -117,20 +117,42 @@ export function startRecordingGame(gameState: GameState) {
|
||||||
video.loop = true;
|
video.loop = true;
|
||||||
video.muted = true;
|
video.muted = true;
|
||||||
video.playsInline = true;
|
video.playsInline = true;
|
||||||
|
|
||||||
video.src = URL.createObjectURL(blob);
|
video.src = URL.createObjectURL(blob);
|
||||||
|
targetDiv.appendChild(video);
|
||||||
|
|
||||||
const a = document.createElement("a");
|
const a = document.createElement("a");
|
||||||
a.download = captureFileName("webm");
|
a.download = captureFileName("webm");
|
||||||
a.target = "_blank";
|
a.target = "_blank";
|
||||||
|
if (window.location.href.endsWith("index.html?isInWebView=true")) {
|
||||||
|
a.href = await blobToBase64(blob);
|
||||||
|
} else {
|
||||||
a.href = video.src;
|
a.href = video.src;
|
||||||
|
}
|
||||||
|
|
||||||
a.textContent = t("main_menu.record_download", {
|
a.textContent = t("main_menu.record_download", {
|
||||||
size: (blob.size / 1000000).toFixed(2),
|
size: (blob.size / 1000000).toFixed(2),
|
||||||
});
|
});
|
||||||
targetDiv.appendChild(video);
|
|
||||||
targetDiv.appendChild(a);
|
targetDiv.appendChild(a);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function blobToBase64(blob: Blob): Promise<string> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let reader = new FileReader();
|
||||||
|
|
||||||
|
reader.onload = function () {
|
||||||
|
resolve(reader.result);
|
||||||
|
};
|
||||||
|
reader.onerror = function (e) {
|
||||||
|
console.error(e);
|
||||||
|
reject(new Error("Failed to readAsDataURL of the video "));
|
||||||
|
};
|
||||||
|
|
||||||
|
reader.readAsDataURL(blob);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function pauseRecording() {
|
export function pauseRecording() {
|
||||||
if (!isOptionOn("record")) {
|
if (!isOptionOn("record")) {
|
||||||
return;
|
return;
|
||||||
|
|
1
src/types.d.ts
vendored
1
src/types.d.ts
vendored
|
@ -150,7 +150,6 @@ export type PerksMap = {
|
||||||
[k in PerkId]: number;
|
[k in PerkId]: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO ensure T has a destroyed;boolean field
|
|
||||||
export type ReusableArray<T> = {
|
export type ReusableArray<T> = {
|
||||||
// All items below that index should not be destroyed
|
// All items below that index should not be destroyed
|
||||||
indexMin: number;
|
indexMin: number;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue