Using a technique borrowed from http://www.gutterbling.com/blog/synchronous-javascript-evaluation-in-android-webview/ we have successfully implemented a number of features within our app that allow our Android app to synchronously get data from a Webview.
Here's the example from gutterbling:
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import android.content.Context;
import android.util.Log;
import android.webkit.WebView;
/**
* Provides an interface for getting synchronous javascript calls
* #author btate
*
*/
public class SynchronousJavascriptInterface {
/** The TAG for logging. */
private static final String TAG = "SynchronousJavascriptInterface";
/** The javascript interface name for adding to web view. */
private final String interfaceName = "SynchJS";
/** Countdown latch used to wait for result. */
private CountDownLatch latch = null;
/** Return value to wait for. */
private String returnValue;
/**
* Base Constructor.
*/
public SynchronousJavascriptInterface() {
}
/**
* Evaluates the expression and returns the value.
* #param webView
* #param expression
* #return
*/
public String getJSValue(WebView webView, String expression)
{
latch = new CountDownLatch(1);
String code = "javascript:window." + interfaceName + ".setValue((function(){try{return " + expression
+ "+\"\";}catch(js_eval_err){return '';}})());";
webView.loadUrl(code);
try {
// Set a 1 second timeout in case there's an error
latch.await(1, TimeUnit.SECONDS);
return returnValue;
} catch (InterruptedException e) {
Log.e(TAG, "Interrupted", e);
}
return null;
}
/**
* Receives the value from the javascript.
* #param value
*/
public void setValue(String value)
{
returnValue = value;
try { latch.countDown(); } catch (Exception e) {}
}
/**
* Gets the interface name
* #return
*/
public String getInterfaceName(){
return this.interfaceName;
}
}
This JS Interface is used like this:
WebView webView = new WebView(context);
SynchronousJavascriptInterface jsInterface = new jsInterface();
webView.addJavascriptInterface(jsInterface, jsInterface.getInterfaceName());
String jsResult = jsInterface.getJSValue(webView, "2 + 5");
Despite working nicely in Android 4.0 - 4.4.4 this is not working for us with Android 5.0 (Lollipop).
It appears as though in Lollipop the JS executes after the latch countdown has completed, whereas previously it would return a value prior to the countdown completing.
Has something changed with the threads that JS in an Webview executes on? And is there any way that I can fix this without re-writing the large chunks of our app that depend on being able to call the JS synchronously?
loadUrl("javascript:" + code)
don't work with API > 19, instead use:
evaluateJavascript(code, null);
Or, you can improve your code to use the callback provided by evaluateJavascript, though.
Related
I was wondering what is probably a simple question: What method should I use to have Speech.speak from expo-speak call a function or callback at every delimiter like a space. To be clear this is for react Native
For example if I ask it to say "Hey this is an interesting sentence", I would like it to run a function after each word.
I currently believe that it has something to do with the methods onMark or onBoundary which are written in source code with little to no documentation
This is the Speech options in source code:
export type SpeechOptions = {
/**
* The code of a language that should be used to read the `text`, refer to IETF BCP 47 to see
* valid codes.
*/
language?: string;
/**
* Pitch of the voice to speak `text`. `1.0` is the normal pitch.
*/
pitch?: number;
/**
* Rate of the voice to speak `text`. `1.0` is the normal rate.
*/
rate?: number;
/**
* A callback that is invoked when speaking starts.
*/
onStart?: () => void | SpeechEventCallback;
/**
* A callback that is invoked when speaking is stopped by calling `Speech.stop()`.
*/
onStopped?: () => void | SpeechEventCallback;
/**
* A callback that is invoked when speaking finishes.
*/
onDone?: () => void | SpeechEventCallback;
/**
* A callback that is invoked when an error occurred while speaking.
* #param error
* #platform android
*/
onError?: (error: Error) => void | SpeechEventCallback;
/**
* Volume of the voice to speak `text`. A number between `0.0` (muted) and `1.0` (max volume)
*
* #default 1.0
* #platform web
*/
volume?: number;
/**
* Voice identifier.
*/
voice?: string;
_voiceIndex?: number;
onBoundary?: SpeechEventCallback | null; // This is what I am wondering about
onMark?: SpeechEventCallback | null;
onPause?: SpeechEventCallback | null;
onResume?: SpeechEventCallback | null;
};
and here is what I am trying to run
Speech.speak(someText,{
language: 'en-US',
pitch: 1,
rate: 1,
onMark: (event) => { // I want this to run every time a space happens
console.log(typeof event);
}
});
I have tried obvious combinations of onMark and onBoundary but cant get it to work. Thank you so much and if another text to speech library should be used I would be happy to do it as long as it uses expo go.
I have two tables a ''users'' and ''announcement'', which announcement table have 3 download file so I wanna add a function when a user download a file add user id to the download file?
Annoucements Table
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateAnnoucementsTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('announcements', function (Blueprint $table) {
$table->increments('project_id')->unsigned();;
$table->integer('types_id')->unsigned();
$table->foreign('types_id')->references('id')->on('types')->onDelete('CASCADE')->onUpdate('CASCADE');
$table->string('project_name');
$table->string('extension');
$table->date('deadline');
$table->string('biddingdocuments');
$table->string('amendmentdocuments');
$table->string('noticestenders');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::drop('announcements');
}
}
Users Table
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateUsersTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('username')->unique();
$table->string('email')->unique();
$table->string('securityq');
$table->string('securitya');
$table->string('name');
$table->string('fname');
$table->string('lname');
$table->string('gender');
$table->string('cname');
$table->integer('lnumber')->unique();
$table->string('province');
$table->string('city');
$table->string('pnumber');
$table->text('address');
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::drop('users');
}
}
userAnnoucement controller
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use App\Http\Requests;
use App\Announcements;
class UserAnnoucment extends Controller
{
public function index(Request $request)
{
$announcements = Announcements::orderBy('project_id','DESC')->paginate(5);
return view('userAnnoucements.index',compact('announcements'))->with('i',
($request->input('page', 1) - 1) * 5);
}
/**
* Display the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function count(){
$announcements = new Announcements;
$announcements->extension = Auth::user()->id;
$announcements->save();
return $announcements;
}
}
``
No problem if the site is not overly busy. Otherwise you can have conflicts if there a simultaneous download.
If there is little to no traffic, programmatically copy the download file to a temporary folder, prepend the UserID to the filename and then download from there, ie: 1024_file.pdf. If the user breaks the download or returns then the file is already there, although you may want to clear the file after each download to conserve disk space, and use the same download code each time.
On a busier service, create a folder for each user logs on (if one is not already available) and do your business in those folders.
I have some ajax Behaviour that should pick some data using JS, and turn it back to Java. Sometimes it works but quite ofen it is just add url parameter and do page refresing/
public abstract class LoggedVKIdBehaviour extends AbstractDefaultAjaxBehavior {
private static final Logger logger = LoggerFactory.getLogger(LoggedVKIdBehaviour.class);
#Override
protected void respond(AjaxRequestTarget target) {
String loggedVkId = RequestCycle.get().getRequest().getRequestParameters().getParameterValue("logged_vkid").toString();
logger.info("ajax has comming with logged VK ID " + loggedVkId);
recived(target, loggedVkId);
}
protected abstract void recived(AjaxRequestTarget target, String loggedVkId);
#Override
public void renderHead(final Component component, IHeaderResponse response) {
super.renderHead(component, response);
Map<String, Object> map = new HashMap<>();
map.put("callbackFunction", getCallbackFunction(CallbackParameter.explicit("logged_vkid")));
//
PackageTextTemplate ptt = new PackageTextTemplate(LoggedVKIdBehaviour.class, "vkid_callback.js");
OnDomReadyHeaderItem onDomReadyHeaderItem = OnDomReadyHeaderItem.forScript(ptt.asString(map));
response.render(onDomReadyHeaderItem);
}
}
js template
var calback = ${callbackFunction};
var logged_vk_id = 11;
function authInfo(response) {
if (response.session) {
logged_vk_id = response.session.mid;
calback(response.session.mid);
console.log("recived callback from VK " + logged_vk_id);
}
}
$(document).ready(function () {
VK.Auth.getLoginStatus(authInfo);
});
it is do recursive redirection like http://localhost:8080/mytool/product/1?logged_vkid=332797331&logged_vkid=332797331&logged_vkid=332797331&logged_vkid=332797331&logged_vkid=332773...
As i understand Ajaj technology - iti asynchronus requests, that shouldn't touch main url at all. So what is the reason for page refreshing?
this is generated Callback function
function (logged_vkid) {
var attrs = {"u":"../wicket/bookmarkable/com.tac.kulik.pages.product.ProductPage?12-1.IBehaviorListener.0-&productID=1"};
var params = [{"name":"logged_vkid","value":logged_vkid}];
attrs.ep = params.concat(attrs.ep || []);
Wicket.Ajax.ajax(attrs);
}
I use wicket 7.2
I did a lot investigations for few days. And found that when i remove
setPageManagerProvider(new NoSerializationPageManagerProvider(this));
Application throw me exepton in polite logs
org.apache.wicket.WicketRuntimeException: A problem occurred while
trying to collect debug information about not serializable object look
like it is could come from aused by: java.io.NotSerializableException:
com.tac.kulik.panel.smaccounts.SMAccountsPanel$1
which means that page tryed to be serialized for SOME REASON but $1 it is mean Anonimous class. I had few class created anonimously to ges some ajax links coming from ListView to be managed on parent panel. So After removing this Anonimous class logic, everything start and run well.
So i am happy, but still don't understand which reason page did serialization after ajax, and what the reason was to refresh whole page.
I try to update information through java script code in Android side.
On Android version 4.3, it's work.
But Android version 4.4, it appeared these error, so can not update information.
[INFO:CONSOLE(1)] "Uncaught TypeError: Cannot set property 'value' of
null", source: (1) I/chromium﹕ [INFO:CONSOLE(1)] "Uncaught TypeError:
Cannot set property 'value' of null", source: (1) I/chromium﹕
[INFO:CONSOLE(1)] "Uncaught TypeError: Cannot call method 'submit' of
null", source: (1)
I know from Android version 4.4, already used new Web View to support, but i don't know where is main issue.
People who know how to update information successfully via Java script code even on Android 4.4,
Please tell me,
Thanks you,
p/s : Codes
// Load Web View to update new SSID, Security Key, Security Mode
WebView mWv = new WebView(this);
// Simplest usage: note that an exception will NOT be thrown
// if there is an error loading this page (see below).
mWv.loadUrl("http://" + Constants.IP + "/cgi-bin/input");
mWv.getSettings().setJavaScriptEnabled(true);
mWv.getSettings().setDomStorageEnabled(true);
mWv.setWebChromeClient(new WebChromeClient());
mWv.setWebViewClient(new WebViewClient(){
public void onPageFinished(WebView mWv, String url) {
super.onPageFinished(mWv, url);
/**
* Pass parametes :
* - New SSID
* - New Security Key
* - Display raw picture
*/
// #"document.getElementsByName('%#')[0].value='%#'"
mWv.loadUrl("javascript:document.getElementById('wifi_ssid').value='" +
mEtSSID.getText().toString() + "'");
if (mEtSecurityKey.length() == 0
| Utils.checkValidatePassword(mEtSecurityKey, 8)) {
mWv.loadUrl("javascript:document.getElementById('wifi_key').value='" +
mEtSecurityKey.getText().toString() + "'");
if (mCbDisplayRawPicture.isChecked())
mWv.loadUrl(
"javascript:document.getElementById('displayraw').value='checked'");
else
mWv.loadUrl("javascript:document.getElementById('displayraw').value=''");
// #"document.forms['sendForm'].submit()"
mWv.loadUrl("javascript:document.getElementById('sendForm').submit()");
/**
* Also get new SSID and new Security Key in Card Setting page
*/
IS_BACK_FROM_CHANGE_SSID_AND_SCCURITY_KEY_TO_CARD_SETTING_PAGE = true;
/**
* Finish current Change SSID And Security Key after submit success
*/
finish();
} else
Toast.makeText(SettingAndReviewSettingPage.this,
getString(R.string.toast_your_password_is_case_sensitive),
Toast.LENGTH_SHORT).show();
}
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
SOLUTION
I surprised when I find out the solution. I need define the variables for Java script codes. Like following code & it worked.
mWv.setWebViewClient(new WebViewClient(){
public void onPageFinished(WebView mWv, String url) {
super.onPageFinished(mWv, url);
/**
* Pass parametes :
* - New SSID
* - New Security Key
* - Display raw picture
*/
// #"document.getElementsByName('%#')[0].value='%#'"
mWv.loadUrl("javascript:var x = document.getElementById('wifi_ssid').value = '" +
mEtSSID.getText().toString() + "';");
// need check password is correct or not
// - empty or minimum has 8 characters.
// - have both character & number in typed password
if ((mEtSecurityKey.length() == 0
| Utils.checkValidatePassword(mEtSecurityKey, 8)
& Utils.checkValidatePassword(mEtSecurityKey.getText().toString()))) {
mWv.loadUrl("javascript:var y = document.getElementById('wifi_key').value = '" +
mEtSecurityKey.getText().toString() + "';");
if (mCbDisplayRawPicture.isChecked())
mWv.loadUrl("javascript:var z = document.getElementById('displayraw').value='checked'");
else
mWv.loadUrl("javascript:var z = document.getElementById('displayraw').value=''");
// #"document.forms['sendForm'].submit()"
// mWv.loadUrl("javascript:console.log('sendForm: '+document.getElementById('sendForm').value);");
mWv.loadUrl("javascript:document.getElementById('sendForm').submit()");
/**
* Also get new SSID and new Security Key in Card Setting page
*/
IS_BACK_FROM_CHANGE_SSID_AND_SCCURITY_KEY_TO_CARD_SETTING_PAGE = true;
/**
* Finish current Change SSID And Security Key after submit success
*/
finish();
} else
Toast.makeText(
ChangeSSIDAndPasswordPage.this,
getString(R.string.toast_your_password_is_case_sensitive),
Toast.LENGTH_SHORT).show();
}
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
The simple solution is to ensure the WebView has the following setting enabled:
WebView webView = new WebView(this); // this is the context
webView.getSettings().setDomStorageEnabled(true);
This allows the browser to store a DOM model of the page elements, so that Javascript can perform operations on it.
Hint: This works only from api 7.
I surprised when I find out the solution. I need define the variables for Java script codes. Like following code javascript:var x = & it worked.
mWv.setWebViewClient(new WebViewClient(){
public void onPageFinished(WebView mWv, String url) {
super.onPageFinished(mWv, url);
/**
* Pass parametes :
* - New SSID
* - New Security Key
* - Display raw picture
*/
// #"document.getElementsByName('%#')[0].value='%#'"
mWv.loadUrl("javascript:var x = document.getElementById('wifi_ssid').value = '" +
mEtSSID.getText().toString() + "';");
// need check password is correct or not
// - empty or minimum has 8 characters.
// - have both character & number in typed password
if ((mEtSecurityKey.length() == 0
| Utils.checkValidatePassword(mEtSecurityKey, 8)
& Utils.checkValidatePassword(mEtSecurityKey.getText().toString()))) {
mWv.loadUrl("javascript:var y = document.getElementById('wifi_key').value = '" +
mEtSecurityKey.getText().toString() + "';");
if (mCbDisplayRawPicture.isChecked())
mWv.loadUrl("javascript:var z = document.getElementById('displayraw').value='checked'");
else
mWv.loadUrl("javascript:var z = document.getElementById('displayraw').value=''");
// #"document.forms['sendForm'].submit()"
// mWv.loadUrl("javascript:console.log('sendForm: '+document.getElementById('sendForm').value);");
mWv.loadUrl("javascript:document.getElementById('sendForm').submit()");
/**
* Also get new SSID and new Security Key in Card Setting page
*/
IS_BACK_FROM_CHANGE_SSID_AND_SCCURITY_KEY_TO_CARD_SETTING_PAGE = true;
/**
* Finish current Change SSID And Security Key after submit success
*/
finish();
} else
Toast.makeText(
ChangeSSIDAndPasswordPage.this,
getString(R.string.toast_your_password_is_case_sensitive),
Toast.LENGTH_SHORT).show();
}
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
Simple solution:
wv.loadUrl("javascript:document.getElementById('xx').value='';void(0);");
To prevent browser from redirecting to a plain text version of the result of evaluating that JavaScript append the JavaScript string with void(0);
Reference: void operator
I'm writing a mobile app with c++ using marmalade middleware software, my plugin I'm making has a component that relies on a compiled python library.
my application works with this 2.6 version:
https://github.com/marmalade/python
switched to 2.7 with socket support:
https://github.com/guyburton/python-loves-marmalade
My first step is I compile my library for the right platform, it is then linked into my application> this creates a file libpython.a ( debug libpython_d.a )
My application has some c-python functions so I also link the required python.h and pyrun.h headers.
none of my headers make reference to python.h or python.c And wmApplication.cpp doesnt make reference to python at all, it does call my plugin though.
1> ARM Compiling(GCC) c:\marmalade\7.3\web\wmapplication\wmApplication.cpp ...
1> ARM Compiling(GCC) c:\TestMove\fluidSec\pyRun.cpp ...
1> ARM Linking(GCC) ...
1> c:/TestMove/python-master/lib/arm\libpython.a(python.obj): In function `main':
1> python.c:(.text.startup.main+0x0): multiple definition of `main'
1> :Release_fluidSec_vc12x_gcc_arm/wmApplication.obj(wmApplication.cpp) : first defined here (col (.text.startup.main+0x0))
I had my plugin working when I was on python version 2.6 then I switched to a different implementation of python 2.7
Isn't
" multiple definition of `main' "only an issue when you have two main functions with the same function signature??? meaning they both have the same program structure? As I understand this can happen regardless of the actual function name being main, the compiler switches it to main as compile time.
I don't have any other instances of python.c ( I searched )
Also: if i compile with the debug version of the library, the error appears to keep the old path in which the library was built ( on a network drive Z:.. even after I disconnect ), if I open the library in a hex editor the path is embedded in the debug info... any change this could be an issue?
1> ARM Compiling(GCC) c:\marmalade\7.3\web\wmapplication\wmApplication.cpp ...
1> ARM Compiling(GCC) c:\TestMove\fluidSec\pyRun.cpp ...
1> ARM Linking(GCC) ...
1> c:/TestMove/python-master/lib/arm\libpython_d.a(python.obj): In function `main':
1> z:/_ProjectFolder/python-master/modified/Modules/python.c:11: multiple definition of `main'
1> :Debug_fluidSec_vc12x_gcc_arm/wmApplication.obj(wmApplication.cpp) : first defined here (col (.text.main+0x0))
Any ideas? I'm clearly missing something!
( definitely cleaned all temp folders and restarted, hoping for a cache issue )
edit:
here is python.c
/* Minimal main program -- everything is loaded from the library */
#include "Python.h"
#ifdef __FreeBSD__
#include <floatingpoint.h>
#endif
int
main(int argc, char **argv)
{
/* 754 requires that FP exceptions run in "no stop" mode by default,
* and until C vendors implement C99's ways to control FP exceptions,
* Python requires non-stop mode. Alas, some platforms enable FP
* exceptions by default. Here we disable them.
*/
#ifdef __FreeBSD__
fp_except_t m;
m = fpgetmask();
fpsetmask(m & ~FP_X_OFL);
#endif
return Py_Main(argc, argv);
}
wmApplication.c
/*
* (C) 2001-2012 Marmalade. All Rights Reserved.
*
* This document is protected by copyright, and contains information
* proprietary to Marmalade.
*
* This file consists of source code released by Marmalade under
* the terms of the accompanying End User License Agreement (EULA).
* Please do not use this program/source code before you have read the
* EULA and have agreed to be bound by its terms.
*/
#include <string>
#include "s3eKeyboard.h"
#include "s3eConfig.h"
#include "s3eWebView.h"
#include "IwDebug.h"
#include "IwGx.h"
#include "../IwWMDispatcher.h"
#include "FallbackPage.h"
#include "../IwWMRegisterModules.h"
#define WEB_DEFAULT_ROOT_DIRECTORY "webassets"
#define WEB_DEFAULT_URL "index.html"
//TOOD Wide char support (relevant for the next line)??
#define ALLOCATION_SIZE_FOR_CONFIG_STRINGS sizeof(char) * S3E_CONFIG_STRING_MAX
#define WEB_DEFAULT_SCHEME LOCAL_ROM_URL
#define URL_HAS_SCHEME(x) strstr(x, "://")
typedef enum CurrentURLType
{
STARTUP_URL_USER_DEFINED,
STARTUP_URL_DEFAULT,
STARTUP_URL_EMBEDDED
} CurrentURLType;
//TODO Implement these globals in a better way (there's no point yet as we may wildly change
//where all out defaults are coming from)
// Tells us which url to try and navigate to (no ownership)
static const char* const * g_WebStartupURL = NULL;
// Scheme to locate local files (might one day not be const)
static const char* const g_LocalFileURLScheme = WEB_DEFAULT_SCHEME;
// Root folder of all web app assets
static char* g_WebAssetsRootDirectory = NULL;
// User defined start URL from icf
static char* g_WebUserDefinedURL = NULL;
// Default start URL if user didn't specify one
static char* g_WebDefaultURL = NULL;
// State representing which stage of fallbacks we're in
static CurrentURLType g_CurrentURLType = STARTUP_URL_USER_DEFINED;
// Name of temp file to create and load as our final fallback url
static const char* g_WebInternalFallbackURL = LOCAL_RAM_URL "error.html";
// Flag to indicate whether we need to cleanup a tempfile
static bool g_DeleteTempURL = false;
static bool g_UsingWinSim = false;
// The main webview
static s3eWebView* g_WebView = NULL;
// The Javascript Dispatcher
static IwWebMarmalade::CDispatcher* g_Dispatcher = NULL;
// Fwd delcarations
bool CreateFallbackPage(const char* url, const char* content);
void DeleteFallbackPage(const char* url);
//Assumes that str is long enough to take the prefix
static void strprepend(const char* prefix, char* str)
{
const int originalLen = strlen(str);
const int prefixLen = strlen(prefix);
//Shift the string along
memmove(str+prefixLen, str, originalLen);
//Can use memcpy for the prefix
memcpy(str, prefix, prefixLen);
//NULL terminate
str[originalLen+prefixLen] = '\0';
}
static bool InitGlobals()
{
if (!g_WebAssetsRootDirectory)
{
if (!(g_WebAssetsRootDirectory = (char*)s3eMalloc(ALLOCATION_SIZE_FOR_CONFIG_STRINGS + 1))) // + 1 for possible extra "/"
return false; //Like it's going to happen at this stage!
if (s3eConfigGetString("Web", "WebRootDirectory", g_WebAssetsRootDirectory) != S3E_RESULT_SUCCESS)
{
strcpy(g_WebAssetsRootDirectory, WEB_DEFAULT_ROOT_DIRECTORY);
}
int length = strlen(g_WebAssetsRootDirectory);
if (g_WebAssetsRootDirectory[length - 1] != '/' || g_WebAssetsRootDirectory[length - 1] != '\\') //TODO Better way?
{
g_WebAssetsRootDirectory[length] = '/';
g_WebAssetsRootDirectory[length + 1] = 0;
}
//Root directory mustn't contain a scheme
if (URL_HAS_SCHEME(g_WebAssetsRootDirectory))
{
IwTrace(WEBMARMALADE, ("WebRootDirectory must not contain a scheme"));
return false;
}
}
if (!g_WebUserDefinedURL)
{
const int totalCapacity = ALLOCATION_SIZE_FOR_CONFIG_STRINGS +
strlen(g_WebAssetsRootDirectory) + strlen(g_LocalFileURLScheme);
if (!(g_WebUserDefinedURL = (char*)s3eMalloc(totalCapacity)))
return false; //Like it's going to happen at this stage!
if (s3eConfigGetString("Web", "WebStartURL", g_WebUserDefinedURL) != S3E_RESULT_SUCCESS)
{
//No user defined URL
g_CurrentURLType = STARTUP_URL_DEFAULT;
g_WebUserDefinedURL[0] = 0;
}
//TODO This means if the user specifies e.g. www.madewithmarmalade.com then this fails.
//We should possibly check for things like "www." as well.
else if (!URL_HAS_SCHEME(g_WebUserDefinedURL))
{
//The user defined url is "relative" then add the scheme and the default file location
strprepend(g_WebAssetsRootDirectory, g_WebUserDefinedURL);
strprepend(g_LocalFileURLScheme, g_WebUserDefinedURL);
}
}
if (!g_WebDefaultURL)
{
const int totalCapacity = strlen(WEB_DEFAULT_URL) +
strlen(g_WebAssetsRootDirectory) + strlen(g_LocalFileURLScheme);
if (!(g_WebDefaultURL = (char*)s3eMalloc(totalCapacity)))
return false; //Like it's going to happen at this stage!
strcpy(g_WebDefaultURL, WEB_DEFAULT_URL);
if (!URL_HAS_SCHEME(g_WebDefaultURL))
{
//The default url is "relative" then add the scheme and the default file location
strprepend(g_WebAssetsRootDirectory, g_WebDefaultURL);
strprepend(g_LocalFileURLScheme, g_WebDefaultURL);
}
}
if (g_CurrentURLType == STARTUP_URL_USER_DEFINED)
g_WebStartupURL = &g_WebUserDefinedURL;
else if (g_CurrentURLType == STARTUP_URL_DEFAULT)
g_WebStartupURL = &g_WebDefaultURL;
g_UsingWinSim = s3eDeviceGetInt(S3E_DEVICE_OS) == S3E_OS_ID_WINDOWS;
IwTrace(WMAPP, ("xxxxxxxxxxxxxxxxxxxxxx: (userdefined) %s", g_WebUserDefinedURL));
return true;
}
static bool CheckQuit()
{
bool rtn = s3eDeviceCheckQuitRequest()
|| (s3eKeyboardGetState(s3eKeyEsc) & S3E_KEY_STATE_PRESSED)
|| (s3eKeyboardGetState(s3eKeyAbsBSK) & S3E_KEY_STATE_PRESSED);
if (rtn)
IwTrace(WEBMARMALADE, ("Quitting Web Marmalade App"));
return rtn;
}
static int32 handleReset(void* systemData, void* userData)
{
s3eWebViewNavigate(g_WebView, *g_WebStartupURL);
return 1;
}
static void Terminate()
{
s3eDeviceUnRegister(S3E_DEVICE_SIMULATOR_RESTART, handleReset);
if (g_Dispatcher)
{
delete g_Dispatcher;
g_Dispatcher = NULL;
}
if (g_WebView)
{
s3eWebViewDestroy(g_WebView);
g_WebView = NULL;
//Free the strings
free(g_WebAssetsRootDirectory);
free(g_WebDefaultURL);
free(g_WebUserDefinedURL);
g_WebAssetsRootDirectory = NULL;
g_WebDefaultURL = NULL;
g_WebUserDefinedURL = NULL;
if (g_DeleteTempURL)
DeleteFallbackPage(g_WebInternalFallbackURL);
}
}
void DoNavigate()
{
IwTrace(WEBMARMALADE, ("loading url = %s", *g_WebStartupURL));
s3eWebViewNavigate(g_WebView, *g_WebStartupURL);
}
static int32 screenResizePending(void* systemData, void* userData)
{
IwTrace(WEBMARMALADE, ("screenResize Pending, calling s3eSurfaceShow()"));
// First call to surface show resizes the surface and calls the S3E_SURFACE_SCREENSIZE
// callback. No surface is actually displayed
s3eSurfaceShow();
// On ios the first call only sets the state to resize pending. The second call actually performs it
s3eSurfaceShow();
return 0;
}
static int32 screenSizeChanged(void* systemData, void* userData)
{
IwTrace(WEBMARMALADE, ("screenSizeChanged callback fired. Resizing webview"));
s3eWebViewResize(g_WebView, 0, 0, s3eSurfaceGetInt(S3E_SURFACE_WIDTH), s3eSurfaceGetInt(S3E_SURFACE_HEIGHT));
// This is annoying. If we don't do this on android then the OS locks the surface and
// we the screen doesn't even rotate natively.
s3eSurfaceShow();
return 0;
}
static int32 pageNotFound(s3eWebView *instance, void *systemData, void *userData)
{
switch (g_CurrentURLType)
{
case STARTUP_URL_USER_DEFINED:
g_CurrentURLType = STARTUP_URL_DEFAULT;
IwTrace(WEBMARMALADE, ("pageNotFound: trying default url = %s", g_WebDefaultURL));
g_WebStartupURL = &g_WebDefaultURL;
break;
case STARTUP_URL_DEFAULT:
g_CurrentURLType = STARTUP_URL_EMBEDDED;
IwTrace(WEBMARMALADE, ("pageNotFound: creating and loading fallback url = %s", g_WebInternalFallbackURL));
if (!(g_DeleteTempURL = CreateFallbackPage(g_WebInternalFallbackURL, g_WebInteralFallbackContent)))
{
IwTrace(WEBMARMALADE, ("failed to create fallback url: quitting"));
s3eDeviceRequestQuit();
return 0;
}
else
{
g_WebStartupURL = &g_WebInternalFallbackURL;
}
break;
default:
IwTrace(WEBMARMALADE, ("pageNotFound: can't find loadable page, quitting"));
s3eDeviceRequestQuit();
return 0;
}
DoNavigate();
return 0;
}
bool Init()
{
// Clear the debug screen from behind the app (you can see it on iOS when you drag
// the browser).
s3eSurfaceClear(0, 0, 0);
s3eSurfaceShow();
if (!s3eWebViewAvailable())
{
IwError(("Webview not available"));
return false;
}
InitGlobals();
if (!(g_WebView = s3eWebViewCreate()))
{
IwTrace(WEBMARMALADE, ("Failed to create webview"));
return false;
}
g_Dispatcher = new IwWebMarmalade::CDispatcher(g_WebView);
if (!g_Dispatcher)
{
IwTrace(WEBMARMALADE, ("Failed to create dispatcher"));
return false;
}
s3eWebViewRegister(S3E_WEBVIEW_FAILED_LOADING, pageNotFound, 0, g_WebView);
s3eSurfaceRegister(S3E_SURFACE_SCREENSIZE, screenSizeChanged, NULL);
s3eDeviceRegister(S3E_DEVICE_SIMULATOR_RESTART, handleReset, NULL);
s3eSurfaceRegister((s3eSurfaceCallback)2, screenResizePending, NULL);
s3eWebViewShow(g_WebView, 0, 0, s3eSurfaceGetInt(S3E_SURFACE_WIDTH), s3eSurfaceGetInt(S3E_SURFACE_HEIGHT));
// For some iOS devices we need to call this on init otherwise the webview is not displayed
s3eSurfaceShow();
DoNavigate();
return true;
}
bool Update()
{
// Work-around since socket events do not unyield the app in windows
if (!g_UsingWinSim)
s3eDeviceYieldUntilEvent();
else
s3eDeviceYield(0);
s3eKeyboardUpdate();
return true;
}
int main()
{
IwTrace(WEBMARMALADE, ("Started Web Marmalade App"));
if (!Init())
return 0;
//Main loop
while (!CheckQuit())
{
if (!Update())
break;
}
Terminate();
return 0;
}
I figured out why it was working before and now it wasn't,
The marmalade MKB file ( which sets up visual studio for cross compiling) had python.c referenced in it to include when it wasn't necessary.
for those wanting to do the same, python in web marmalade, i'll be linking my framework on github as i've made numerous changes to the python implementations, just a few minor issues ( that took me way too long to solve)