Firebase initialisation exception with Unity Android - c#

We are developing an application in unity with the aim to deploy to android and are using firebase to facilitate user login and real time database.
We are using the real time database to store the location and information about specific objects and synchronize them across all players. The database also stores the individual data about each user. Our implementation has a single parent object called GenericInterface which has all of the shared code between the PlayerInterface and BuildingInterface which extend GenericInterface.
GenericInterface has the initialization code for our firebase implemention:
Auth = FirebaseAuth.DefaultInstance;
// Set this before calling into the realtime database.
FirebaseApp.DefaultInstance.SetEditorDatabaseUrl(/*DB Url*/);
//set up restricted access.
// Set these values before calling into the realtime database.
FirebaseApp.DefaultInstance.SetEditorP12FileName(/* P12 filename */);
FirebaseApp.DefaultInstance.SetEditorServiceAccountEmail(/*AccountEmail*/);
FirebaseApp.DefaultInstance.SetEditorP12Password(/*Password*/);
// Get the root reference location of the database
Reference = FirebaseDatabase.DefaultInstance.RootReference;
This Reference is then used by PlayerInterface and BuildingInterface to load data through listeners, for example in the PlayerInterface:
var path = string.Format("players/{0}", pid);
Reference = FirebaseDatabase.DefaultInstance.GetReference(path);
Reference.ValueChanged += HandleValueChanged;
Where pid is the player's Id.
The project (apart from player login) works as intended when launched and run in the unity editor with buildings being fetched and displayed correctly by the BuildingInterface. However when building and running on either an AVD or Android Phone, none are displayed and a adb.exe logcat -s Unity shows the following error:
DBInterfaces.Abstract.GenericInterface`1:_start() (at [...]\Assets\Scripts\Firebase\Interfaces\Abstract\GenericInterface.cs:24)
DBInterfaces.Abstract.GenericInterface`1:.ctor() (at [...]\Assets\Scripts\Firebase\Interfaces\Abstract\GenericInterface.cs:18)
DBInterfaces.BuildingInterface:.ctor() (at E:\Users\Documen
InitializationException: Exception of type 'Firebase.InitializationException' was thrown.
at Firebase.Auth.FirebaseAuth+<GetAuth>c__AnonStorey1.<>m__0 () [0x00000] in <filename unknown>:0
at Firebase.FirebaseApp.TranslateDllNotFoundException (System.Action closureToExecute) [0x00000] in <filename unknown>:0
[Trimmed long file path for clarity]
Line 24 of GenericInterface Corresponds to Auth = FirebaseAuth.DefaultInstance;
We have been working on this for the better part of the week and haven't found any potential solutions.

Related

Can I use MongoDB 3.6 or later with Unity 2018.4?

I'm trying to apply the MongoDB with this unity tutorial MongoDB bought mLab, so the signing into mongoDB is done via mongoDB atlas. And they only support MongoDB 3.6 and later.
I've created a cluster with AWS (similarly to the GUI mLab has),
On "Overview" tab hit the "CONNECT" button. Out of the three options: "Connect with the Mongo Shell" / "Connect Your Application" / "Connect with MongoDB Compass" - Connect Your Application looked the most relevant to the code you've provided.
In: "Connect Your Application" you need to choose a driver: I chose C# / .NET (because this is a C# project), version 2.5 or later.
Got "Connection string": mongodb+srv://user_name:<password>#testmp-pkump.mongodb.net/test?retryWrites=true
Few things:
The project name is not "test" (as it says here, right before the retryWrites = true): mongodb+srv://:#testmp-pkump.mongodb.net/test?retryWrites=true
I've passed this link to the code under: private const string MONGO_URI , and for user name I write the user I've added to the cluster, and for the password, I write the password I've added to the user in the cluster. - and not the user I've used to create the MongoDB account
Running the "Server" scene resolves with an error: =
ArgumentException: Invalid keyword 'mongodb+srv://:#serverclientmp-
1oqao.gcp.mongodb.net/test?retrywrites'.
MongoDB.Driver.MongoConnectionStringBuilder.set_Item (System.String
keyword, System.Object value)
System.Data.Common.DbConnectionStringBuilder.ParseConnectionStringNonOdbc
(System.String connectionString)
System.Data.Common.DbConnectionStringBuilder.ParseConnectionString
(System.String connectionString)
System.Data.Common.DbConnectionStringBuilder.set_ConnectionString
(System.String value) MongoDB.Driver.MongoConnectionStringBuilder..ctor
(System.String connectionString)
MongoDB.Driver.MongoClient.ParseConnectionString (System.String
connectionString) MongoDB.Driver.MongoClient..ctor (System.String
connectionString) Mongo.Init () (at Assets/Scripts/Database/Mongo.cs:16)
Server.Init () (at Assets/Scripts/Server.cs:40) Server.Start () (at
Assets/Scripts/Server.cs:28)
I've tried to change the API Compatibility Level to .NET 4.x and the following error came:
ArgumentException: Invalid option 'retryWrites'.
Parameter name: url
MongoDB.Driver.MongoUrlBuilder.Parse (System.String url) (at
<6da29fc855c44d33ad78b3e27475ff27>:0)
MongoDB.Driver.MongoUrlBuilder..ctor (System.String url) (at
<6da29fc855c44d33ad78b3e27475ff27>:0)
MongoDB.Driver.MongoUrl..ctor (System.String url) (at
<6da29fc855c44d33ad78b3e27475ff27>:0)
MongoDB.Driver.MongoClient.ParseConnectionString (System.String
connectionString) (at <6da29fc855c44d33ad78b3e27475ff27>:0)
MongoDB.Driver.MongoClient..ctor (System.String connectionString) (at
<6da29fc855c44d33ad78b3e27475ff27>:0)
Mongo.Init () (at Assets/Script/Database/Mongo.cs:15)
Server.Init () (at Assets/Script/Server.cs:38)
Server.Start () (at Assets/Script/Server.cs:27)
I would like to know what I need to add to my scripts to make it work or if this tutorial is outdated and a new approach needs to be done.
Thanks
I know I am 8 months too late but, I have recently began working with MongoDB in Unity and I don't know if this will help but the short answer is I'm not sure. But I managed to connect to my remote and local db, so I'll explain how I did it below.
The Long answer:
I did not watch the tutorial video you followed but i setup my connections with these dll files (I am assuming you also created a Plugins folder for them). As for the "tutorial", I simply followed this quick start guide and this one. Everything else API related I just read the documentation.
I'm using MongoDB v4.2.1
I'm using Unity 2019.3
I'm using API compatibility .Net Standard 2.0
I'm using the admin user on the cluster. I don't know what your case is but I remember the documentation says that your user should have read and write privileges, so check that maybe.
Finally, I had an error when I first tried to connect to the remote DB, but maybe because I have my code wrapped around a try catch block, it didn't produce the error you provided but rather this:
Unable to authenticate using sasl protocol mechanism SCRAM-SHA-1 mongo c# driver
Which occurred because I forgot to take the angle brackets out of the code and had this instead - <password> - yes I left my password wrapped around the brackets, rookie mistake lol.
Here's how I connected to my Atlas cluster:
private const string remoteDB = "mongodb+srv://user_name:<password>#mycluster.mongodb.net/test?retryWrites=true";
static MongoClient newClient = new MongoClient(remoteDB);
internal Database()//This is a constructor, I'm not using MonoBehaviour
{
db = newClient.GetDatabase(DATABASE_NAME);
lbCollection = db.GetCollection<BsonDocument>(lbColl);
gameStateCollection = db.GetCollection<BsonDocument>(gameStateColl);
}
internal static async Task TestConnection()
{
try
{
await GetDBNames();
}
catch (Exception e)
{
Debug.Log("<color=red> Error</color> found while testing db connection:");
Debug.Log(e.Message);
}
}
private static async Task<List<string>> GetDBNames()
{
List<string> dbNames = new List<string>();
var dbs = await newClient.ListDatabaseNamesAsync();
dbNames = dbs.ToList();
if (dbNames.Count > 0)
{
Debug.Log($"Databases in {nameof(dbNames)}: ");
for (int i = 0; i < dbNames.Count; i++)
{
Debug.Log($"{dbNames[i]}");
}
return dbNames;
}
Debug.Log($"<color=red>{nameof(GetDBNames)}</color> Returning null. No Dbs found.");
return null;
}

Setup Database (SQLite) for Unity

I have looked at too many tutorials to list and they all recommend the same thing. However, they have not helped to solve my problem.
I am trying to include in my project an SQLite DB, and when building for PC, MAC & Linux Standalone (testing on a Windows machine), the database works as expected. When testing on an Android device, I get the following errors.
E/Unity: ArgumentException: Invalid ConnectionString format for parameter "/storage/emulated/0/Android/data/com.tbltools.tbl_project/files/TBLDatabase.db"
at Mono.Data.Sqlite.SqliteConnection.ParseConnectionString (System.String connectionString) [0x00000] in <filename unknown>:0
at Mono.Data.Sqlite.SqliteConnection.Open () [0x00000] in <filename unknown>:0
at UIHandler+<RequestAllStudentNames>c__Iterator2.MoveNext () [0x00000] in <filename unknown>:0
at UnityEngine.SetupCoroutine.InvokeMoveNext (IEnumerator enumerator, IntPtr returnValueAddress) [0x00000] in <filename unknown>:0
I thought that making an amendment to the connectionString should be simple enough, but that hasn't solved my problem. This is what I have so far:
if (Application.platform != RuntimePlatform.Android)
{
// The name of the db.
tblDatabase = "URI=file:" + Application.dataPath + "/TBLDatabase.db"; //returns the complete path to database file exist.
}
else
{
tblDatabase = Application.persistentDataPath + "/TBLDatabase.db";
if (!File.Exists(tblDatabase))
{
// if it doesn't ->
Debug.LogWarning("File \"" + tblDatabase + "\" does not exist. Attempting to create from \"" + Application.dataPath + "!/assets/" + "TBLDatabase.db");
// open StreamingAssets directory and load the db ->
// #if UNITY_ANDROID
var loadDb = new WWW("jar:file://" + Application.dataPath + "!/assets/" + "TBLDatabase.db"); // this is the path to your StreamingAssets in android
while (!loadDb.isDone) { } // CAREFUL here, for safety reasons you shouldn't let this while loop unattended, place a timer and error check
// then save to Application.persistentDataPath
File.WriteAllBytes(tblDatabase, loadDb.bytes);
}
}
//open db connection
var connection = new SqliteConnection(tblDatabase);
connection.Open();
var command = connection.CreateCommand();
I have used adb shell and pulled the DB from my Android device and everything is as expected (the DB does exist and it isn't empty).
I believe I have all the relevant dll files, but if anyone could give me some guidance I would appreciate it.
***************************************************EDIT**********************************************
I have since made the following alterations based on advice given.
I am now calling the following method to start my connection and handle DB requestsStartCoroutine(RunDbCode(dbFileName, jsonStudentID, jsonIndiNames, jsonIndiStudentNumbers));
Then I have the following method:
IEnumerator RunDbCode(string fileName, List jsonStudentID, List jsonIndiNames, List jsonIndiStudentNumbers)
{
//Where to copy the db to
string dbDestination = Path.Combine(Application.persistentDataPath, "data");
dbDestination = Path.Combine(dbDestination, fileName);
//Check if the File do not exist then copy it
if (!File.Exists(dbDestination))
{
//Where the db file is at
string dbStreamingAsset = Path.Combine(Application.streamingAssetsPath, fileName);
byte[] result;
//Read the File from streamingAssets. Use WWW for Android
if (dbStreamingAsset.Contains("://") || dbStreamingAsset.Contains(":///"))
{
WWW www = new WWW(dbStreamingAsset);
yield return www;
result = www.bytes;
}
else
{
result = File.ReadAllBytes(dbStreamingAsset);
}
Debug.Log("Loaded db file");
//Create Directory if it does not exist
if (!Directory.Exists(Path.GetDirectoryName(dbDestination)))
{
Directory.CreateDirectory(Path.GetDirectoryName(dbDestination));
}
//Copy the data to the persistentDataPath where the database API can freely access the file
File.WriteAllBytes(dbDestination, result);
Debug.Log("Copied db file");
}
//Now you can do the database operation
//open db connection
var connection = new SqliteConnection(dbDestination);
connection.Open();
var command = connection.CreateCommand();
// Drop the table if it already exists.
command.CommandText = "DROP TABLE IF EXISTS existing_individual;";
command.ExecuteNonQuery();
var sql = "CREATE TABLE existing_individual (studentID VARCHAR(23), fullName VARCHAR(50), studentNumber VARCHAR(20))";
command.CommandText = sql;
command.ExecuteNonQuery();
//Inserting the exisiting student names returned, into the SQLite DB
int count = 0;
foreach (var individuals in jsonStudentID)
{
//looping through the existing students registered for the individual quiz - below has been written to avoid SQL injection
sql = "INSERT INTO existing_individual (studentID, fullName, studentNumber) VALUES (#jsonStudentID, #jsonIndiNames, #jsonIndiStudentNumbers)";
command.Parameters.AddWithValue("#jsonStudentID", jsonStudentID[count]);
command.Parameters.AddWithValue("#jsonIndiNames", jsonIndiNames[count]);
command.Parameters.AddWithValue("#jsonIndiStudentNumbers", jsonIndiStudentNumbers[count]);
command.CommandText = sql;
command.ExecuteNonQuery();
count++;
}
//close the connection
command.Dispose();
command = null;
connection.Close();
connection = null;
}
However, I am still getting the following errors:
06-08 15:26:56.498 16300-16315/? E/Unity: ArgumentException: Invalid ConnectionString format for parameter "/storage/emulated/0/Android/data/com.tbltools.tbl_project/files/data/TBLDatabase.db"
at Mono.Data.Sqlite.SqliteConnection.ParseConnectionString (System.String connectionString) [0x00000] in <filename unknown>:0
at Mono.Data.Sqlite.SqliteConnection.Open () [0x00000] in <filename unknown>:0
at UIHandler+<RunDbCode>c__Iterator3.MoveNext () [0x00000] in <filename unknown>:0
at UnityEngine.SetupCoroutine.InvokeMoveNext (IEnumerator enumerator, IntPtr returnValueAddress) [0x00000] in <filename unknown>:0
UnityEngine.MonoBehaviour:StartCoroutineManaged2(IEnumerator)
UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
<RequestAllStudentNames>c__Iterator2:MoveNext()
UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr)
(Filename: Line: -1)
06-08 15:26:56.502 16300-16315/? E/Unity: ArgumentException: Invalid ConnectionString format for parameter "URI"
at Mono.Data.Sqlite.SqliteConnection.ParseConnectionString (System.String connectionString) [0x00000] in <filename unknown>:0
at Mono.Data.Sqlite.SqliteConnection.Open () [0x00000] in <filename unknown>:0
at UIHandler.CreateIndiButton () [0x00000] in <filename unknown>:0
at UIHandler+<RequestAllStudentNames>c__Iterator2.MoveNext () [0x00000] in <filename unknown>:0
at UnityEngine.SetupCoroutine.InvokeMoveNext (IEnumerator enumerator, IntPtr returnValueAddress) [0x00000] in <filename unknown>:0
I have also added my database to the 'StreamingAssets' folder as shown in the image below:
Below also shows an image of my plugins folder that holds my dll files.
Most tutorials on this topic are outdated.
Looked at the code and found few problems but I can't tell if those are the reason you are getting this error. WWW should be used in a coroutine function so that you can yield or wait for loadDb.isDone is finish by adding yield return null inside the while loop. You can also yield the WWW request itself and that's the method I will use in my answer.
Also, jar:file://" + Application.dataPath is an old code. Use Application.streamingAssetsPath for that. Furthermore, you don't need "URI=file:" + Application.dataPath. Just use Application.persistentDataPath for that.
I will just put an instruction on how to do the setup.
Setting up the MANAGED code part:
1.Go to your Unity installation path
<UnityInstallationDirecory>\Editor\Data\Mono\lib\mono\2.0
Copy the following files:
I18N.MidEast.dll
I18N.Other.dll
I18N.Rare.dll
I18N.West.dll
Mono.Data.Sqlite.dll
Mono.Data.SqliteClient.dll
System.Data.dll
to the project's <ProjectName>\Assets\Plugins path.
This will allow you to compile API from the Mono.Data.Sqlite namespace without any error.
Setting up the UNMANAGED code part:
In this step, you will need to get the native Sqlite library. You can get the source code, build it and use it or use already pre-compiled binray.
1.Get the native library for Windows
Download the pre-compiled sqlite3.dll for Windows 64 bit from here, and put it in the <ProjectName>\Assets\Plugins\x86_64 path.
If using Windows 32 bit then get the sqlite3.dll version from here and put it in the <ProjectName>\Assets\Plugins\x86 path.
2.Get the native library for Android
Download the pre-compiled libsqlite3.so for Android ARM processor from
here and put it in the <ProjectName>\Assets\Plugins\Android\libs\armeabi-v7a path.
Download the pre-compiled libsqlite3.so for Android Intel x86 processor from
here and put it in the <ProjectName>\Assets\Plugins\Android\libs\x86 path.
This covers most processors used on Android devices.
3.Get the native library for UWP
A.Download the WSA folder then put the WSA folder in the <ProjectName>\Assets\Plugins path. That folder contains the native part.
B.Create 2 files named "mcs.rsp" and "csc.rsp" in the <ProjectName>\Assets path.
C.Add the following inside the "mcs.rsp" and "csc.rsp" files:
-r:I18N.MidEast.dll
-r:I18N.Other.dll
-r:I18N.Rare.dll
-r:I18N.West.dll
-r:Mono.Data.Sqlite.dll
-r:Mono.Data.SqliteClient.dll
-r:System.Data.dll
D.You will have to move the managed dlls to the root folder of the project when building for UWP. So, move I18N.MidEast.dll, I18N.Other.dll, I18N.Rare.dll, I18N.West.dll, Mono.Data.Sqlite.dll, Mono.Data.SqliteClient.dll, System.Data.dll to the <ProjectName> path not <ProjectName>\Assets\Plugins path.
4.For iOS, Linux and Mac, it looks like you don't have to download anything else for them or do this step. They usually have the native pre-compiled Sqlite libraries built-in.
Including the Database file in the Build:
1.Create a folder in your <ProjectName>\Assets folder and name it StreamingAssets. Spelling counts and it's case sensitive.
2.Put the database file (TBLDatabase.db) in this StreamingAssets folder.
Accessing the Database File after building the project
Sqlite cannot work on files in the StreamingAssets folder in a build since that's a read-only path. Also, Android requires that you use the WWW API instead of the standard System.IO API to read from the StreamingAssets folder. You have to copy the db file from Application.streamingAssetsPath/filename.db to Application.persistentDataPath/filename.db.
On some platforms, it is required that you create a folder inside Application.persistentDataPath and save data to that folder instead. Always do that. The folder in the example code below is "data" so that will become Application.persistentDataPath/data/filename.db.
3.Because of the statement above, check if the database file exist in the
Application.persistentDataPath/data/filename.db. If it does, use Application.persistentDataPath/data/filename.db as a path for your database operation. If it doesn't, continue from #4.
4.Read and copy the database file from the StreamingAssets folder to Application.persistentDataPath
On some platforms, it is required that you create a folder inside Application.persistentDataPath and save data to that folder instead. Always do that. The folder in the example below is "data".
Detect if this is Android and use WWW read to the file from Application.streamingAssetsPath/filename.db. Use File.ReadAllBytes to read it on anything else other than Android. In your example, you used Application.platform for that. In my example, I will simply check if the path contains "://" or :/// to do that.
5.Once you read the file, write the data you just read to Application.persistentDataPath/data/filename.db with File.WriteAllBytes. Now, you can use this path for your database operation.
6.Prefix "URI=file:" to the Application.persistentDataPath/data/filename.db path and that's the path for that should be used in your database operation with the Sqlite API.
It's very important that you understand all these in order to fix it when something changes but I've already done step #3 to #6 below.
IEnumerator RunDbCode(string fileName)
{
//Where to copy the db to
string dbDestination = Path.Combine(Application.persistentDataPath, "data");
dbDestination = Path.Combine(dbDestination, fileName);
//Check if the File do not exist then copy it
if (!File.Exists(dbDestination))
{
//Where the db file is at
string dbStreamingAsset = Path.Combine(Application.streamingAssetsPath, fileName);
byte[] result;
//Read the File from streamingAssets. Use WWW for Android
if (dbStreamingAsset.Contains("://") || dbStreamingAsset.Contains(":///"))
{
WWW www = new WWW(dbStreamingAsset);
yield return www;
result = www.bytes;
}
else
{
result = File.ReadAllBytes(dbStreamingAsset);
}
Debug.Log("Loaded db file");
//Create Directory if it does not exist
if (!Directory.Exists(Path.GetDirectoryName(dbDestination)))
{
Directory.CreateDirectory(Path.GetDirectoryName(dbDestination));
}
//Copy the data to the persistentDataPath where the database API can freely access the file
File.WriteAllBytes(dbDestination, result);
Debug.Log("Copied db file");
}
try
{
//Tell the db final location for debugging
Debug.Log("DB Path: " + dbDestination.Replace("/", "\\"));
//Add "URI=file:" to the front of the url beore using it with the Sqlite API
dbDestination = "URI=file:" + dbDestination;
//Now you can do the database operation below
//open db connection
var connection = new SqliteConnection(dbDestination);
connection.Open();
var command = connection.CreateCommand();
Debug.Log("Success!");
}
catch (Exception e)
{
Debug.Log("Failed: " + e.Message);
}
}
Usage:
string dbFileName = "TBLDatabase.db";
void Start()
{
StartCoroutine(RunDbCode(dbFileName));
}
This error occurs when we are trying to use SQLite in Unity 2019 or later versions. This error occurs due to missing of Mono library for Unity 2019 or later versions. You won't get this error if you are using Unity 2018 or less versions.
If you installed Unity 2018 or below versions then go to the installation directory <UnityInstallationDirecory>\Editor\Data\Mono\lib\mono\2.0 and copy the following files:
I18N.MidEast.dll
I18N.Other.dll
I18N.Rare.dll
I18N.West.dll
Mono.Data.Sqlite.dll
Mono.Data.SqliteClient.dll
System.Data.dll
to the project's <ProjectName>\Assets\Plugins path.
Or you can simply resolve this error by downgrading the Unity version to 2018 or below.

Xamarin.Forms Android - Can't add event to calendar "My calendar" programmatically

I have an app written in Xamarin.Forms which is able to create calendar events (although the calendar code is Android specific). I have confirmed that the code works for most calendars, but on my two physical phones, there exists a calendar called "My calendar" that I cannot add to.
I have confirmed that the value for key calendar_access_level corresponds to one of the following fields:
CalendarAccess.AccessContributor,
CalendarAccess.AccessEditor,
CalendarAccess.AccessOwner,
CalendarAccess.AccessRoot (See CalendarAccessLevel, CalendarAccessLevel values)
Am I including an access level that I should not? Or is "My calendar" some kind of special calendar that I need to filter out?
I can add to it manually in the stock Calendar app, just not programmatically. I can add to any other calendars programmatically, so I don't believe it's an issue with that code.
Update: After getting the app to debug on an physical device that was having the issue, I was able to figure out the issue. Here is a partial stack trace (the rest is unrelated to the issue, it's just the gesture recognizer that called the command):
Exception: java.lang.NullPointerException
id == null
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in /Users/builder/data/lanes/4009/f3074d2c/source/mono/mcs/class/referencesource/mscorlib/system/runtime/exceptionservices/exceptionservicescommon.cs:143
at Java.Interop.JniEnvironment+StaticMethods.CallStaticObjectMethod (Java.Interop.JniObjectReference type, Java.Interop.JniMethodInfo method, Java.Interop.JniArgumentValue* args) [0x00082] in /Users/builder/data/lanes/4009/9578cdcd/source/Java.Interop/src/Java.Interop/Java.Interop/JniEnvironment.g.cs:12649
at Java.Interop.JniPeerMembers+JniStaticMethods.InvokeObjectMethod (System.String encodedMember, Java.Interop.JniArgumentValue* parameters) [0x0001b] in /Users/builder/data/lanes/4009/9578cdcd/source/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniStaticMethods.cs:97
at Java.Util.TimeZone.GetTimeZone (System.String id) [0x0001e] in /Users/builder/data/lanes/4009/9578cdcd/source/monodroid/src/Mono.Android/platforms/android-23/src/generated/Java.Util.TimeZone.cs:402
at MyProject.Droid.CalendarEventsAndroid+<CreateCalendarEventsAsync>d__4.MoveNext () [0x000bf] in C:\Projects\MyProject\MyProject.App\MyProject\Droid\DependencyServices\CalendarEventsAndroid.cs:64
After finding a physical device that was having the issue, I was able to reproduce it while debugging and solve it. The issue is that "My calendar" did not have a timezone assigned to it, I'm assuming because it's a default calendar and inherits the user's timezone setting. Because it didn't have a timezone, Java.Util.TimeZone.GetTimeZone was throwing an exception when I passed in an empty timezone identifier.
string calTzId = GetTimeZoneIdForCalendar(calId); // Evaluates to null
Java.Util.TimeZone jCalTz = Java.Util.TimeZone.GetTimeZone(calTzId); // Exception thrown here
The solution is to check if calTzId is null or empty and use the user's preferred timezone in the event that it is.
Java.Util.TimeZone jCalTz = !string.IsNullOrEmpty(calTzId)
? Java.Util.TimeZone.GetTimeZone(calTzId)
: Java.Util.TimeZone.Default;
See Xamarin's Java.Util.TimeZone.Default documentation.

Cross Domain Policy troubles non port 80 in Unity

I'm having trouble accessing my web service from Unity if it is not running on port 80. This is the C# code that I'm using in Unity
private void RunSimulation()
{
string url = #"http://192.168.1.116:9000/simulate";
WWW goServer = new WWW(url);
}
On 192.168.1.116, I have a program written in go that is listening on port 9000. I can easily reach this URL from a web browser and I will get my expected result.
When I try this in Unity, if I examine the contents of my goServer variable, I will see the message
System.Security.Exception: No valid crossdomain policy available to allow access
There is a crossdomain policy file. It is located at /var/www/html/crossdomain.xml Is has the following contents:
<allow-access-from domain="*" to-ports="*" secure="false"/>
<site-control permitted-cross-domain-policies="all"/>
This file existed before and there were .php programs that were able to successfully be called. I added the to-ports"*" to try to get it to work, but no luck.
I have tried having my go server listen on port 80. When I do this, I am able to connect from Unity. Any idea?
(The input sanitizer is defeating me and I can't figure out how to post without the input form telling me that my links are not allowed, so I'm going to omit 192.168.1.116:9000)
I was not getting anything at serverurl.com:9000/crossdomain.xml
So I put some code into my go server to respond to this request and return a response with headers Access-Control-Allow-Origin, Access-Control-Allow-Headers, and Access-Control-Allow-Credentials.
I set the ENABLE_CROSSDOMAIN_LOGGING environment variable. I see this in the log file plus a bunch more
Determining crossdomain.xml location for request: /simulate?asdfa=sadfsa
About to parse url: /simulate?asdfa=sadfsa
Determining crossdomain.xml location for request:/simulate?asdfa=sadfsa
About to parse url: /crossdomain.xml
About to parse url: /simulate?asdfa=sadfsa
Determining crossdomain.xml location for request: /simulate?asdfa=sadfsa
Download had OK statuscode
Received the following crossdomain.xml
----------
----------
received policy
BuildFlashPolicy caught an exception while parsing
/crossdomain.xml: Policy can't be constructed from empty stream.
ArgumentException: Policy can't be constructed from empty stream.
at MonoForks.System.Windows.Browser.Net.FlashCrossDomainPolicy.FromStream (System.IO.Stream originalStream) [0x00000] in :0
at MonoForks.System.Windows.Browser.Net.CrossDomainPolicyManager.BuildFlashPolicy (Boolean statuscodeOK, MonoForks.System.Uri uri, System.IO.Stream responsestream, System.Collections.Generic.Dictionary`2 responseheaders) [0x00000] in :0
UnityEngine.WWW:get_assetBundle()
PlayEditorMain:RunSimulation() (at Assets\Scripts\Play Editor Scripts\Menu Scripts\PlayEditorMain.cs:526)
PlayEditorMain:ButtonPressed(GameObject) (at Assets\Scripts\Play Editor Scripts\Menu Scripts\PlayEditorMain.cs:261)
MainMenuButton:OnClick() (at Assets\Scripts\Play Editor Scripts\Menu Buttons\MainMenuButton.cs:23)
UnityEngine.GameObject:SendMessage(String, Object, SendMessageOptions)
UICamera:Notify(GameObject, String, Object) (at Assets\3rd Party\NGUI\Scripts\UI\UICamera.cs:765)
UICamera:ProcessTouch(Boolean, Boolean) (at Assets\3rd Party\NGUI\Scripts\UI\UICamera.cs:1435)
UICamera:ProcessMouse() (at Assets\3rd Party\NGUI\Scripts\UI\UICamera.cs:1063)
UICamera:Update() (at Assets\3rd Party\NGUI\Scripts\UI\UICamera.cs:909)
(Filename: Assets/Scripts/Play Editor Scripts/Menu Scripts/PlayEditorMain.cs Line: 526)
Unable to determine the audio type from the URL (/simulate?asdfa=sadfsa) . Please specify the type.
UnityEngine.WWW:GetAudioClip(Boolean, Boolean, AudioType)
UnityEngine.WWW:GetAudioClip(Boolean, Boolean) (at C:\BuildAgent\work\d63dfc6385190b60\artifacts\EditorGenerated\Utils.cs:308)
UnityEngine.WWW:GetAudioClip(Boolean) (at C:\BuildAgent\work\d63dfc6385190b60\artifacts\EditorGenerated\Utils.cs:301)
UnityEngine.WWW:get_audioClip() (at C:\BuildAgent\work\d63dfc6385190b60\artifacts\EditorGenerated\Utils.cs:293)
PlayEditorMain:RunSimulation() (at Assets\Scripts\Play Editor Scripts\Menu Scripts\PlayEditorMain.cs:526)
PlayEditorMain:ButtonPressed(GameObject) (at Assets\Scripts\Play Editor Scripts\Menu Scripts\PlayEditorMain.cs:261)
MainMenuButton:OnClick() (at Assets\Scripts\Play Editor Scripts\Menu Buttons\MainMenuButton.cs:23)
UnityEngine.GameObject:SendMessage(String, Object, SendMessageOptions)
UICamera:Notify(GameObject, String, Object) (at Assets\3rd Party\NGUI\Scripts\UI\UICamera.cs:765)
UICamera:ProcessTouch(Boolean, Boolean) (at Assets\3rd Party\NGUI\Scripts\UI\UICamera.cs:1435)
UICamera:ProcessMouse() (at Assets\3rd Party\NGUI\Scripts\UI\UICamera.cs:1063)
UICamera:Update() (at Assets\3rd Party\NGUI\Scripts\UI\UICamera.cs:909)
[C:/BuildAgent/work/d63dfc6385190b60/Runtime/Audio/AudioClip.cpp line 118]
(Filename: Assets/Scripts/Play Editor Scripts/Menu Scripts/PlayEditorMain.cs Line: 526)
So I think I am now returning the correct headers, but I'm thinking that I also need to return the contents in the body. I'm thinking that I'll just return the same contents that the crossdomain.xml file I get when I query it on port 80.
I know that the crossdomain.xml endpoint on my server is being hit. If I understand what the logfile that unity is outputting, the problem might just be that the body is empty and I have an empty stream.
No proxy needed, unity has logging crossdomain stuff built in: take a look at the the debuging section of the unity3d manual on the security sandbox and turn on crossdomain debugging with ENABLE_CROSSDOMAIN_LOGGING. Then look for the results in the logs.
Just to be sure, a crossdomain file is reachable at http://192.168.1.116:9000/crossdomain.xml?
What I had to do was implement my own version of the crossdomain.xml endpoint. It currently looks like this.
func crossdomain(res http.ResponseWriter, req *http.Request) {
log.Println("At the top of crossdomain")
if origin := req.Header.Get("Origin"); origin != "" {
res.Header().Set("Access-Control-Allow-Origin", origin)
}
res.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
res.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token")
res.Header().Set("Access-Control-Allow-Credentials", "true")
body := "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<!DOCTYPE cross-domain-policy SYSTEM \"http://www.adobe.com/xml/dtds/cross-domain-policy.dtd\">" +
"<cross-domain-policy>" +
"<site-control permitted-cross-domain-policies=\"all\"/>" +
"<allow-access-from domain=\"*\" to-ports=\"*\" secure=\"false\"/>" +
"<allow-access-from domain=\"*\" to-ports=\"*\"/>" +
"<allow-access-from domain=\"*\"/>" +
"<allow-http-request-headers-from domain=\"*\" to-ports=\"*\"/>" +
"<allow-http-request-headers-from domain=\"*\"/>" +
"<site-control permitted-cross-domain-policies=\"all\"/>" +
"</cross-domain-policy>"
io.WriteString(res,body)
}
func main() {
http.HandleFunc("/simulate", simulate)
http.HandleFunc("/crossdomain.xml", crossdomain)
listenErr := http.ListenAndServe(":9000", nil)
}

MonoTouch iOS application can't find files in directory on device - works on simulator

If I run this code on the device, it returns "got it":
if (Directory.Exists (NSBundle.MainBundle.BundlePath + "/ARCSDev")) {
Console.WriteLine ("got it");
} else {
Console.WriteLine ("can't find it");
}
Which means the directory is in the main bundle.
I need to use that file in this method call:
private void generateChart (int chartNumber, string palette)
{
string filePath = NSBundle.MainBundle.BundlePath + "/ARCSDev";
Console.WriteLine("Filepath - " + filePath);
Loader loader = new Loader (filePath);
loader.LoadChart (Convert.ToString (chartNumber));
The above code works fine on the simulator, but not on the device.
I get the following stack trace when the error occurs on the device:
System.InvalidOperationException: Operation is not valid due to the current state of the object
at System.Linq.Enumerable.Max[TileIndexRecord] (IEnumerable`1 source, System.Func`2 selector) [0x00000] in <filename unknown>:0
at MyCompany.Data.Arcs.Loader.ExtractWriteableBitmap (MyCompany.Data.Arcs.Records.RGBPaletteRecord rgbPalette, Double dpi, MyCompany.Data.Arcs.Raschts.ChartIndexFile indexFile, MyCompany.Data.Arcs.Raschts.RasterChartFile chartFile) [0x00000] in /Users/me/Desktop/ARCSViewer/ARCSViewer/Loader.cs:571
at MyCompany.Data.Arcs.Loader.GetHiResImage (MyCompany.Data.Arcs.Records.RGBPaletteRecord rgbPalette) [0x00000] in /Users/me/Desktop/ARCSViewer/ARCSViewer/Loader.cs:362
at ARCSViewer.SecondViewController.generateChart (Int32 chartNumber, System.String palette) [0x0004e] in /Users/me/Desktop/ARCSViewer/ARCSViewer/SecondViewController.cs:118
at ARCSViewer.SecondViewController.ViewDidAppear (Boolean animated) [0x00007] in /Users/me/Desktop/ARCSViewer/ARCSViewer/SecondViewController.cs:84
at MonoTouch.UIKit.UIApplication.Main (System.String[] args, System.String principalClassName, System.String delegateClassName) [0x0004c] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIApplication.cs:38
at ARCSViewer.Application.Main (System.String[] args) [0x00000] in /Users/me/Desktop/ARCSViewer/ARCSViewer/Main.cs:17
The directory contains unix executable files and radiance files.
Can anyone explain whats going on? The file is definitely in the right place (the bundle) as I've tested the code that checks for existence with other files that I know to be there as well..
It might be an issue of case sensitivity, the simulator is case-insensitive while the device is case sensitive. Check your files to see if you're accessing all of them with the right case (i.e. not only the directory).
The permissions on the devices are very different (far more restricted) from the simulator. There's a lot of place you do not have access for various reasons (e.g. changing some files would break the application digital signature which would disallow your application from running anymore).
Also if your application does not follow Apple guidelines on where (and when) to store data your application will be rejected (if you target the appstore).
There's a nice article from Xamarin on how to work with the iOS file system.
I solved the problem, several parts of my application use code like below:
fs = new FileStream(fileName, FileMode.Open);
I needed to change every occurrence of FileStream() to the following:
fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
As my files are in the bundle and don't need to make use of write operations, this limits the file stream to only read operations and allowed the files to be read in properly.

Categories

Resources