I tried to share data between Safari browser and standalone PWA on iPhone12 with iOS 14.3.
The information, that this should work are here: https://firt.dev/ios-14/
I#ve tried this: https://www.netguru.com/codestories/how-to-share-session-cookie-or-state-between-pwa-in-standalone-mode-and-safari-on-ios
Without success.
Are there any suggestions to running this? Or is it not possible ...
This is the code
const CACHE_NAME = "auth";
const TOKEN_KEY = "token";
const FAKE_TOKEN = "sRKWQu6hCJgR25lslcf5s12FFVau0ugi";
// Cache Storage was designed for caching
// network requests with service workers,
// mainly to make PWAs work offline.
// You can give it any value you want in this case.
const FAKE_ENDPOINT = "/fake-endpoint";
const saveToken = async (token: string) => {
try {
const cache = await caches.open(CACHE_NAME);
const responseBody = JSON.stringify({
[TOKEN_KEY]: token
});
const response = new Response(responseBody);
await cache.put(FAKE_ENDPOINT, response);
console.log("Token saved! 🎉");
} catch (error) {
// It's up to you how you resolve the error
console.log("saveToken error:", { error });
}
};
const getToken = async () => {
try {
const cache = await caches.open(CACHE_NAME);
const response = await cache.match(FAKE_ENDPOINT);
if (!response) {
return null;
}
const responseBody = await response.json();
return responseBody[TOKEN_KEY];
} catch (error) {
// Gotta catch 'em all
console.log("getToken error:", { error });
}
};
const displayCachedToken = async () => {
const cachedToken = await getToken();
console.log({ cachedToken });
};
// Uncomment the line below to save the fake token
// saveToken(FAKE_TOKEN);
displayCachedToken();
Without success means no result, i've tried to set data in safari and get them in standalone pwa
I'm trying to setup a StreamingRecognize, with multiple request's. Is it possible ?
The point is that i want to send audio stream from the mic with a unknown time, so i think that i must implement multiple requests. (Considering that a request session has a max_time = 65 seconds).
Anyone can help me with this ?
Thank's alot ;)
Google sample code:
static async Task<object> StreamingMicRecognizeAsync(int seconds)
{
if (NAudio.Wave.WaveIn.DeviceCount < 1)
{
Console.WriteLine("No microphone!");
return -1;
}
var speech = SpeechClient.Create();
var streamingCall = speech.StreamingRecognize();
// Write the initial request with the config.
await streamingCall.WriteAsync(
new StreamingRecognizeRequest()
{
StreamingConfig = new StreamingRecognitionConfig()
{
Config = new RecognitionConfig()
{
Encoding =
RecognitionConfig.Types.AudioEncoding.Linear16,
SampleRateHertz = 16000,
LanguageCode = "en",
},
InterimResults = true,
}
});
// Print responses as they arrive.
Task printResponses = Task.Run(async () =>
{
while (await streamingCall.ResponseStream.MoveNext(
default(CancellationToken)))
{
foreach (var result in streamingCall.ResponseStream
.Current.Results)
{
foreach (var alternative in result.Alternatives)
{
Console.WriteLine(alternative.Transcript);
}
}
}
});
// Read from the microphone and stream to API.
object writeLock = new object();
bool writeMore = true;
var waveIn = new NAudio.Wave.WaveInEvent();
waveIn.DeviceNumber = 0;
waveIn.WaveFormat = new NAudio.Wave.WaveFormat(16000, 1);
waveIn.DataAvailable +=
(object sender, NAudio.Wave.WaveInEventArgs args) =>
{
lock (writeLock)
{
if (!writeMore) return;
streamingCall.WriteAsync(
new StreamingRecognizeRequest()
{
AudioContent = Google.Protobuf.ByteString
.CopyFrom(args.Buffer, 0, args.BytesRecorded)
}).Wait();
}
};
waveIn.StartRecording();
Console.WriteLine("Speak now.");
await Task.Delay(TimeSpan.FromSeconds(seconds));
// Stop recording and shut down.
waveIn.StopRecording();
lock (writeLock) writeMore = false;
await streamingCall.WriteCompleteAsync();
await printResponses;
return 0;
}
In Cloud Speech-to-Text audio length limit for each streaming request is around 1 minute [1]. You can either use asynchronous speech recognition [2] for audio files up to 180 minutes or renew the streaming request before it reaches to the time limit for streaming speech recognition [3].
Here is a Python example how to renew streaming request and stream audio more than 1 minute [4].
I want to play multiple Audio files simultaneously on iOS .
On the click of a button I create multiple instance of an Audio file and put them into an array.
let audio = new Audio('path.wav')
audio.play().then(() => {
audio.pause();
possibleAudiosToPlay.push(audio);
});
After a while I play them all:
possibleAudiosToPlay.forEach(el => {
el.currentTime = 0;
el.play();
});
While this plays all audio files: When a new one begins it stops the old one. (on iOS)
Apples developer guide says this isn't possible at all with HTML5 Audio:
Playing multiple simultaneous audio streams is also not supported.
But can this be achieved with the Web Audio API?
There isn't anything written about it in Apples developer guide.
Yes you can with Web Audio API. You have to create an AudioBufferSourceNode for each one of your audio sources, since each source can be played only once (you can't stop it and play it again).
const AudioContext = window.AudioContext || window.webkitAudioContext;
const ctx = new AudioContext();
const audioPaths = [
"path/to/audio_file1.wav",
"path/to/audio_file2.wav",
"path/to/audio_file3.wav"
];
let promises = [];
// utility function to load an audio file and resolve it as a decoded audio buffer
function getBuffer(url, audioCtx) {
return new Promise((resolve, reject) => {
if (!url) {
reject("Missing url!");
return;
}
if (!audioCtx) {
reject("Missing audio context!");
return;
}
let xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.responseType = "arraybuffer";
xhr.onload = function() {
let arrayBuffer = xhr.response;
audioCtx.decodeAudioData(arrayBuffer, decodedBuffer => {
resolve(decodedBuffer);
});
};
xhr.onerror = function() {
reject("An error occurred.");
};
xhr.send();
});
}
audioPaths.forEach(p => {
promises.push(getBuffer(p, ctx));
});
// Once all your sounds are loaded, create an AudioBufferSource for each one and start sound
Promise.all(promises).then(buffers => {
buffers.forEach(b => {
let source = ctx.createBufferSource();
source.buffer = b;
source.connect(ctx.destination);
source.start();
})
});
This is the scenario I want:
I'm checking for the presence of an application (using protocol detection), if it's not present the exe file automatically starts downloading. The webpage continuously checks in regular interval whether the app has been successfully downloaded and when the installation is complete, the browser prompts to open the app.
What is working:
The download is starting automatically. The webpage is checking for the presence of the app in regular interval.
What is NOT working:
After the exe is downloaded, when I start the download, the focus shifts and the webpage thinks that the app has been launched, whereas the app actually hasn't yet been installed even.
I need a focus checking mechanism which detects when the specific app has been launched and focus shifts and not respond to any other focus shifts.
This is my code:
_registerEvent = function(target, eventType, cb) {
if (target.addEventListener) {
target.addEventListener(eventType, cb);
return {
remove: function() {
target.removeEventListener(eventType, cb);
}
};
} else {
target.attachEvent(eventType, cb);
return {
remove: function() {
target.detachEvent(eventType, cb);
}
};
}
},
checkIfHasProtocol = function($el) {
openUriWithTimeoutHack(url);
},
openUriWithTimeoutHack = function(protocol) {
var timeout = setTimeout(function() {
handler.remove();
checkIfHasProtocol(protocol);
}, 5000);
var target = window;
while (target != target.parent) {
target = target.parent;
}
var handler = _registerEvent(window, "blur", onBlur);
function onBlur() {
if (tabVisible() && isBlurred) {
clearTimeout(timeout);
handler.remove();
//App has been launched
}
}
}
window.location = protocol;
};
tabVisible(function() {
if (tabVisible() && inCheck) {
inCheck = false;
}
});
window.addEventListener("blur", function() {
isBlurred = true;
});
window.addEventListener("focus", function() {
isBlurred = false;
inCheck = false;
});
I'm sending a data to the server like this:
save(){
var el = this.parent.nodes;
print(el);print(el.length);
request = new HttpRequest();
if(el.length == 1) print('No lines to save!');
else
{
var opt = el[i].shadowRoot.nodes[0].options[el[i].shadowRoot.nodes[0].selectedIndex].text;
print(this.parent.nodes.length);
for(var i=1; i < el.length; i++)
{
orderLines.add({
'poid': orderHeader[0]['OrderID'],
'ponum': orderHeader[0]['onum'],
'lnum' : i.toString(),
'itmid' :el[i].shadowRoot.nodes[0].value,
'icode' : opt,
'qty': el[i].shadowRoot.nodes[1].value,
'rqty': 0,
'bqty': el[i].shadowRoot.nodes[1].value,
'iqty': 0,
'biqty': el[i].shadowRoot.nodes[1].value,
'price': el[i].shadowRoot.nodes[2].value,
'rdd': orderHeader[0]['rdd'],
'eta': '',
'flag': 0
});
print(orderLines);
request.onReadyStateChange.listen(onData_save);
request.open('POST', host+'/sPO');
request.send(JSON.encode(orderLines));
}
}
}
and my server side function is:
void main() {
connections = new List<WebSocket>();
HttpServer.bind(HOST, PORT).then((HttpServer server) {
print('Server listening on port ${PORT}.');
server.listen((HttpRequest request) {
if (WebSocketTransformer.isUpgradeRequest(request)) {
WebSocketTransformer.upgrade(request).then(handleWS);
} else gotMSG(request);
});
});
}
handleWS(WebSocket ws){
connections.add(ws);
print('Client connected, there are now ${connections.length} client(s) connected.');
ws.listen((String message) {
for (WebSocket connection in connections) {
connection.add(message);
}
},
onDone: () {
connections.remove(ws);
print('Client disconnected, there are now ${connections.length} client(s) connected.');
});
}
void gotMSG(HttpRequest request) {
switch (request.method) {
case 'POST':
handlePost(request);
break;
case 'OPTIONS':
handleOptions(request);
break;
default:
defaultHandler(request);
}
}
void serveRequest(HttpRequest request){
print('Listening for GET and POST on http://$HOST:$PORT');
request.response.statusCode = HttpStatus.FORBIDDEN;
request.response.reasonPhrase = "WebSocket connections only";
request.response.close();
}
void handlePost(HttpRequest req) {
HttpResponse res = req.response;
switch (req.uri.path) {
case '/login': login(req); break;
...
case '/sPO': savePO(req); break;
default: break;
}
}
The /sPO => savePO is executed once if the order sent is of one line only, but if n lines in the order, the function is executed more than once, could not find a pattern for that,
In the SavePO I used oracledart pub, so thought something wrong in it, and tried postgresql pub, but got same results, the savePO function is:
void savePO(HttpRequest req){
HttpResponse res = req.response;
addCorsHeaders(res);
print('${req.method}: ${req.uri.path}');
Future future() => new Future.value(true);
req.listen((List<int> buffer) {
var theDataLines = JSON.decode(new String.fromCharCodes(buffer));
print(theDataLines);
connect(db).then((conn) {
for (var theData in theDataLines)
conn.execute("""
insert into pol
(poid,ponum,lnum,itmid,icode,qty,rqty,bqty,iqty,biqty,price,rdd, eta, flag)
values (#poid,#ponum,#lnum,#itmid,#icode,#qty,#rqty,#bqty,#iqty,#biqty,#price,
to_timestamp(#rdd,'YYYY-MM-DD'), to_timestamp(#eta,'YYYY-MM-DD'), #flag)
""",
{
'poid': theData['poid'],
'ponum': theData['ponum'],
'lnum' : theData['lnum'],
'itmid' : theData['itmid'],
'icode' : theData['icode'],
'qty': theData['qty'],
'rqty': theData['rqty'],
'bqty': theData['bqty'],
'iqty': theData['iqty'],
'biqty': theData['biqty'],
'price': theData['price'],
'rdd': theData['rdd'].toString(),
'eta': theData['eta'].toString(),
'flag': theData['flag']
})
.then((_)=>conn.query('commit').toList().then((rows) {print('committed');}))
.then((_){
res.write('done');
res.close();
});
}); // END of SQL
}, onError: printError); // End of server listen
} // END of function
I even tried to change the:
case '/sPO': savePO(req); break;
to be
case '/sPO': print(1); break;
the it printed the 1, 4 times after sending an order of 6 lines!!
It's hard to see for me what you actually try to accomplish.
The problem is very probably your save() method. You wrote how it behaves but not much about what you try to accomplish?
Why don't you put more lines into one JSON string and post them together in one request?
You create one request instance and call send repeatedly on this one request instance.
You also register the onReadyStateChange handler more than once on the same request object which results in onData_save being called several times when the event occurs just once.
I think you should either move request = new HttpRequest(); down just before
request.open('POST', host+'/sPO');
request.send(JSON.encode(orderLines));
or better move request.onReadyStateChange.listen(onData_save); up to request = new HttpRequest();,
add all orderlines into one JSON and call
request.open('POST', host+'/sPO');
request.send(JSON.encode(orderLines));
after the for loop.
Another problem I see is that you do a fire and forget. What if the send request fails for some reason?
I would create a sendJSON method that returns a future (with a Completer which completes on onDone and completeError when something goes wrong.
When you want to create more than one request in your save() you can use something like
// create your JSON
var futures = [];
for(i = 0; i < 5; i++) {
futures.add(sendData(myJson)); // collects the futures returned from sendData
}
// executes all futures and waits for all to respond and then returns another future
return Future.wait()
.then((results) {
results.forEach((r) {
// check result
});
});