Page 49 - MSDN Magazine, February 2018
P. 49
General Structure of the Project
Where in the workflow will the CSCS code be executed? The answer is different for the iOS and Android projects. You’ll see it in what follows, but the full details are in the accompanying source code download at github.com/vassilych/mobile.
The common code, used by both platforms, is in the shared proj- ect part, scripting.Shared, which contains all the C# files needed for parsing the CSCS code. The code specific to each platform is located in the scripting.iOS and scripting.Droid projects. See the structure of a sample project in Figure 3.
The actual CSCS script is located in the msdnScript.cscs file under the Resources folder in the scripting.Shared project. Note that you can include other CSCS files by calling the following CSCS function:
ImportFile("anotherScriptFile.cscs");
For the Android project I set up a link to the msdnScript.cscs file from the scripting.Droid Assets folder, and for the iOS proj- ect I set up a link from the scripting.iOS Resources folder. You can also reference the script in a number of ways, for example keeping different versions of the script on different platforms.
The CommonFunctions.cs file contains functionality common to iOS and Android. In particular, it holds the method that exe- cutes the msdnScripting.cscs script that’s shown in Figure 4. Note that I distinguish between the iOS- and Android-specific code by using the preprocessor __IOS__ and __ANDROID__ directives. The platform-specific code is mostly located in the corresponding projects, scripting.iOS or scripting.Droid.
Where do you call the RunScript function from? You can call it only after the global layout has been initialized, so you can add widgets to it.
It turns out that it’s trickier to do this on Android than on iOS: Just calling the RunScript function at the end of the MainActivity.On- Create function fails because some variables haven’t been initialized yet. So you must put RunScript right before the main activity actu- ally starts running. The Android Activity Lifestyle documentation at
Figure 4 Executing the CSCS Script
goo.gl/yF8dTZ provides a clue: It must go right after the Main- Activity.OnResume method completes. Some global variables (for instance, the screen size, the orientation and so on) are not yet ini- tialized even at the end of the OnResume method, so the trick is to register a global layout watcher at the end of the OnResume method that will be triggered as soon as the global layout is constructed:
protected override void OnResume() {
base.OnResume(); if (!m_scriptRun) {
ViewTreeObserver vto = TheLayout.ViewTreeObserver; vto.AddOnGlobalLayoutListener(new LayoutListener()); m_scriptRun = true;
Where do you call the RunScript function from? You can call it only after the global layout has been initialized, so you can add widgets to it.
Note that I use a special Boolean variable m_scriptRun to make sure that the script runs just once. The OnGlobalLayout method in the layout listener then executes the script:
public class LayoutListener : Java.Lang.Object, ViewTreeObserver. IOnGlobalLayoutListener
{
public void OnGlobalLayout() {
var vto = MainActivity.TheLayout.ViewTreeObserver; vto.RemoveOnGlobalLayoutListener(this); CommonFunctions.RunScript();
} }
For iOS the situation is somewhat easier, you can just run the script at the end of the AppDelegate.FinishedLaunching method.
Text-to-Speech
Let’s see how to add some functionality to CSCS, using text-to- speech as an example.
Figure 5 Speak Function Implementation
} }
public static void RunScript() {
RegisterFunctions();
string fileName = "msdnScript.cscs"; string script = "";
#if __ANDROID__
Android.Content.Res.AssetManager assets = MainActivity.TheView.Assets; using (StreamReader sr = new StreamReader(assets.Open(fileName))) {
script = sr.ReadToEnd(); }
#endif
#if __IOS__
string[] lines = System.IO.File.ReadAllLines(fileName);
script = string.Join("\n", lines); #endif
Variable result = null; try {
result = Interpreter.Instance.Process(script); } catch (Exception exc) {
Console.WriteLine("Exception: " + exc.Message); Console.WriteLine(exc.StackTrace); ParserFunction.InvalidateStacksAfterLevel(0); throw;
} }
public class SpeakFunction : ParserFunction {
protected override Variable Evaluate(ParsingScript script) {
bool isList = false;
List<Variable> args = Utils.GetArgs(script,
Constants.START_ARG, Constants.END_ARG, out isList); Utils.CheckArgs(args.Count, 1, m_name);
TTS.Init();
string phrase = args[0].AsString();
TTS.Voice = Utils.GetSafeString(args, 1, TTS.Voice);
TTS.Speak(phrase);
return Variable.EmptyInstance; }
}
msdnmagazine.com
February 2018 45