I am using Unity along with the Google Play Services Plugin for Unity found over here: https://github.com/playgameservices/play-games-plugin-for-unity
I am trying to access the players avatar to be included in a profile pic ingame. The problem is when I try accessing the Texture2D Social.localuser.image, it always returns null. Upon more research it seems that the code uses some kind of AvatarURL to find the image and that is the thing that is null. I used string.IsNullOrEmpty(AvatarURL) to check this. Does anyone know why AvatarURL is null, and/or how I can fix it. If not that, is there any alternative way to accessing the players avatar to use for a profile picture in my game.
Here is the code I used to test this:
PlayGamesPlatform.Activate();
//Authenticate User
Social.localUser.Authenticate((bool success) => {
if(success) {
Debug.Log("Successfully Authenticated");
Textures.profilePic = Sprite.Create(Social.localUser.image, new Rect(0, 0, Social.localUser.image.width, Social.localUser.image.height), new Vector2(0.5f, 0.5f));
SceneManager.LoadScene("Main Menu");
} else {
Debug.Log("Failed to Authenticate User");
SceneManager.LoadScene("ErrorCanNotSignIn");
}
});
The error happens when setting Textures.profilePic (Textures is another class I created that stores textures, and profilePic is a static Sprite variable in it). It says there is a NullReferenceException: Object reference not set to an instance of the object.
Again based on what I've seen, I think the source of the error seems to be the AvatarURL being null, as it causes this code, which I am pretty sure is what loads the image, not to run:
if (!mImageLoading && mImage == null && !string.IsNullOrEmpty(AvatarURL))
{
Debug.Log("Starting to load image: " + AvatarURL);
mImageLoading = true;
PlayGamesHelperObject.RunCoroutine(LoadImage());
}
Also if it's important, I am testing this on an android device.
Question answered by JeanLuc on this SO question
The implementation for Social.localUser.image of the Play Games Unity
Plugin returns always null.
The reason why the image returns null is because it's loaded asynchronously, so you'll need to wait for it.
Furthermore there's a bug where you first need to access Social.localUser.userName.
This code should be working:
Debug.Log(Social.localUser.userName); // you need to access Social.localUser.userName, otherwise the image will always return null
StartCoroutine(KeepCheckingAvatar());
// ...
private IEnumerator KeepCheckingAvatar()
{
float secondsOfTrying = 20;
float secondsPerAttempt = 0.2f;
while (secondsOfTrying > 0)
{
if (Social.localUser.image != null)
{
// Do something with freshly loaded image
break;
}
secondsOfTrying -= secondsPerAttempt;
yield return new WaitForSeconds(secondsPerAttempt);
}
}
Credits:
https://github.com/playgameservices/play-games-plugin-for-unity/issues/1056#issuecomment-212257972
Related
I want to implement mesibo real time API in my code for audiocall, but still having problem
here provided code , random number gives null pointer. what mistake is did here.
Mesibo api = Mesibo.getInstance();
api.init(getApplicationContext());
// set path for storing DB and messaging files
Mesibo.setPath(Environment.getExternalStorageDirectory().getAbsolutePath());
// add listener
Mesibo.addListener(this);
// set access token
if(0 != Mesibo.setAccessToken(access_token)) {
// return false;
}
// set database after setting access token so that it's associated with the user
Mesibo.setDatabase("mesibo.db", 0);
// Now start mesibo
if(0 != Mesibo.start()) {
// return false;
}
Mesibo.UserProfile mProfile = new Mesibo.UserProfile();
mProfile.name = "user_name";
mProfile.address = "xyz";
Post logs and details of the platform. Why have you commented - "return false"? Are you using an emulator or phone?
It is likely that you are passing a wrong token in setAccessToken method and hence mesibo did not initialize. Any attempt to use the Mesibo function without initialization may produce a crash. Check the value of status in Mesibo_onConnectionStatus listener.
https://mesibo.com/documentation/tutorials/get-started/android/
or download the first app here
https://github.com/mesibo/samples/tree/master/android
I am trying to create an Android application created using Adobe Flash Actionscript 3. I wanted each user of the app to input their name in the beginning of the application, then they have the capability to save their progress in current frame (and it will be saved into a save slot or similar). However, the problem arises when another user is going to use the app, he/she must enter a distinct username, and he/she can save anytime (and load his/her distinct load progress, different from the previous user.). And it goes on.
I am a newbie in programming and I hope you could help me. Any suggestions will be appreciated. Thanks!
This my code for creating a username and saving it:
function handleClick(Event:MouseEvent):void
{
var myFirstVariable = boxOne.text;
boxTwo.text = myFirstVariable;
gotoAndStop("opening_scene")
}
myButton2.addEventListener(MouseEvent.MOUSE_UP, handleClick);
UPDATED EDIT 2: Here is my code for saving and loading. Still not working:
var so:SharedObject = SharedObject.getLocal("Test");
var userName:String = nameField.text;
if (so.data.users == null)
so.data.users = new Object();
btnSave.addEventListener(MouseEvent.CLICK, onClick);
function onClick(e:MouseEvent):void
{
if (so.data.users[userName] == null)
so.data.users[userName] = new Object();
so.data.users[userName].lastframe = currentFrame;
so.flush();
trace(userName);
}
btnLoad.addEventListener(MouseEvent.CLICK, reloadBtnClick);
function reloadBtnClick(e:MouseEvent):void
{
if (so.data.users[userName] == null)return;
if (so.data.users[userName].lastFrame == null) return;
gotoAndStop(so.data.users[userName].lastFrame);
trace(userName);
}
I recommend that you use a SQLite Data Base to save your data can find all the documentation here Manipulating SQL database data Well is a little bit more complicate but works like have tables in Excel.
As Vesper suggested, you need to have a storage with all the users you store. For example:
// Put user name here.
var userName:String;
var SO:SharedObject = SharedObject.getLocal("save");
// Create storage for users.
if (SO.data.users == null) SO.data.users = new Object();
// ...
function saveCurrentFrame(event:MouseEvent):void
{
// Create storage for current user by their name.
if (SO.data.users[userName] == null) SO.data.users[userName] = new Object();
SO.data.users[userName].lastframe = currentFrame;
SO.flush();
}
function getLastFrame(event:MouseEvent):void
{
trace(1);
// Skip this if there's no record for
if (SO.data.users[userName] == null) return;
trace(2);
if (SO.data.users[userName].lastFrame == null) return;
trace(3);
gotoAndStop(SO.data.users[userName].lastFrame);
trace(4);
}
I use the odata4j library to access a WCF Data Service.
This is how I call a Service Method from my Android code:
OQueryRequest<OEntity> l = consumer.getEntities("GetDataList")
.custom("dataId", String.format("'%s'", actualData.ID))
.orderBy("Name").skip(0).top(200);
I checked it with WireShark, and I see that every method call is preceded with 2 calls of metadata information request:
Why? Are they essential? The metadata information is quite heavy, it shouldn't request is every time (not to mention 2 times).
What should I do to prevent odata4j from requesting metadata information so many times?
I found in the source code where the 'extra' request happens (in odata4j/odata4j-core/src/main/java/org/odata4j/consumer/AbstractODataConsumer.java ):
#Override
public EdmEntitySet findEdmEntitySet(String entitySetName) {
EdmEntitySet rt = super.findEdmEntitySet(entitySetName);
if (rt == null && delegate != EdmDataServices.EMPTY) {
refreshDelegate();
rt = super.findEdmEntitySet(entitySetName);
}
return rt;
}
It seems that if the entity set can't be found, the consumer creates an extra roundtrip to the server to get the metadata again (by calling refreshDelegate()):
private void refreshDelegate() {
ODataClientRequest request = ODataClientRequest.get(AbstractODataConsumer.this.getServiceRootUri() + "$metadata");
try {
delegate = AbstractODataConsumer.this.getClient().getMetadata(request);
} catch (ODataProducerException e) {
// to support services that do not expose metadata information
delegate = EdmDataServices.EMPTY;
}
}
I don't quite understand why: maybe it assumes that the server has changed and a new version of the metadata is available so it tries again.
If it fails then it tries to find a function with the given name.
Personally I don't consider this very effective unless the server side is so volatile that it changes between calls.
So, if you have no changing metadata on the server, it is safe to remove the check for the entitySet and let it return as a null:
#Override
public EdmEntitySet findEdmEntitySet(String entitySetName) {
EdmEntitySet rt = super.findEdmEntitySet(entitySetName);
//if (rt == null && delegate != EdmDataServices.EMPTY) {
// refreshDelegate();
// rt = super.findEdmEntitySet(entitySetName);
//}
return rt; //if it is null, then the search for a function will commence
}
I am trying to test a sample project called Android.Routing.Offline from OsmSharp.Samples in Github.
After two taps on the screen (the first one gets just the GeoCoordinate) I get a ProtoBuf.ProtoException in the Router.cs
private static IBasicRouterDataSource<CHEdgeData> _graph;
public static void Initialize()
{
var routingSerializer = new CHEdgeDataDataSourceSerializer();
_graph = routingSerializer.Deserialize(
Assembly.GetExecutingAssembly().GetManifestResourceStream(#"Android.Routing.Offline.kempen-big.contracted.mobile.routing"));
}
public static Route Calculate(GeoCoordinate from, GeoCoordinate to)
{
try
{
lock(_graph)
{
var router = Router.CreateCHFrom(_graph, new CHRouter(), new OsmRoutingInterpreter());
// The exception happens here below
var fromResolved = router.Resolve(Vehicle.Car, from);
var toResolved = router.Resolve(Vehicle.Car, to);
if(fromResolved != null && toResolved !=null)
{
return router.Calculate(Vehicle.Car, fromResolved, toResolved);
}
}
}
catch(Exception ex)
{
OsmSharp.Logging.Log.TraceEvent("Router", OsmSharp.Logging.TraceEventType.Critical, "Unhandled exception occured: {0}", ex.ToString());
}
return null;
}
And the exception:
> {ProtoBuf.ProtoException: Invalid wire-type; this usually means you
> have over-written a file without truncating or setting the length; see
> http://stackoverflow.com/q/2152978/23354 at
> ProtoBuf.ProtoReader.ReadSingle () ...
I didnt overwrite the file (kempen-big.contracted.mobile.routing) just added it as a linked file in the project. Any ideas how I can solve this issue?
Well, the first thing to try is to check that the contents of the Stream you are reading (via GetManifestResourceStream) contains exactly the contents you are expecting, and not some wrapper or otherwise-corrupt mess. If you have some checksum algorithm you can run: great! Checking just the .Length would be a great start. Otherwise, you could cheat (just for the purposes of validating the contents) by getting the hex:
using (var ms = new MemoryStream())
{
stream.CopyTo(ms);
string hex = BitConverter.ToString(
ms.GetBuffer(), 0, (int)ms.Length);
// dump this string, and compare it to the same output run on the
// oringal file; they should be identical
}
Note that this duplicates the contents in-memory, purely so we can get a byte[] (oversized) to get the hex from - it isn't intended for "real" code, but until you are sure that the contents are correct, all other bets are off. I strongly suspect that you'll find that the contents are not identical to the contents in the original file. Note that I'm also implicitly assuming that the original file works fine in terms of deserialization. If the original file doesn't work: again, all bets are off.
Background
I've noticed a weird column for MediaStore.Images.ImageColumns called "MINI_THUMB_MAGIC" .
the documentation says just that :
The mini thumb id.
Type: INTEGER
Constant Value: "mini_thumb_magic"
The question
my guess is that this field is related to MediaStore.Images.Thumbnails .
Is it correct ? if not, what is this and how do you use it?
if it is correct , i have other questions related to it:
Is it a mini sized image of the original one? does it use the same aspect ratio or does it do center-cropping on it?
how come the size of "MICRO" is square (96 x 96) and the size of "MINI" is a non-square rectangle ( 512 x 384 ) ?
How do you use it? My guess is that it's done by using "THUMB_DATA", which is a blob, so you use it like this, but then what is the purpose of using "getThumbnail" if you already have this field?
does it get a rotated thumbnail in case the orientation value is not 0 ? meaning that if I wish to show it, I won't need to rotate the image?
Is it possible to do a query of the images together with their thumbnails? maybe using inner join?
Is it available for all Android devices and versions?
Why is it even called "magic" ? Is it because it's also available for videos (and for some reason doesn't exist for music, as it could be the album's cover photo, for example) ?
Check this file: https://github.com/android/platform_packages_providers_mediaprovider/blob/master/src/com/android/providers/media/MediaThumbRequest.java in the Android source code. This value is some magic number which allows to determine if the thumbnail is still valid. I didn't investigate that file further, but it should be no bit issue to dive deeper.
To your questions:
No, no mini-sized image
Well, I guess it's a definition by Google who want to have a square thumbnail for some lists, where only very small previews should be visible and where many items should fit on the screen and there's another thumbnail format where the images are bigger...
I don't know that, but according to Google's doc, one (THUMB_DATA) is only some raw byte array of the thumbnail (dunno in which format) and the other one (getThumbnail) retrieves a full-fledged bitmap object...
don't know
don't know
I guess so, as it's part of AOSP source code.
The word "magic" is often used for some kind of identifier. There are "magic packets" who can wake up a computer from sleep or shutdown over the network, there are magic numbers on hard disks, where some sectors (e.g. the MBR) has the hexadecimal values AA 55 on its last two byte positions, there are also magic numbers in image files which help software packages determine the image type (e.g. GIF files begin with GIF89a or GIF87a (ASCII), JPEG files begin with FF D8 hexadecimal) and there are many, many more examples. So, magic numbers are a very common term here :-)
According to the source code at the following URL, the Magic Number is the Id of the original image * a constant. That value is then used to check for a long int. If the int isn't as expected, it's considered out of sync with the image media.
http://grepcode.com/file/repo1.maven.org/maven2/org.robolectric/android-all/4.4_r1-robolectric-0/android/media/MiniThumbFile.java#MiniThumbFile.getMagic%28long%29
// Get the magic number for the specified id in the mini-thumb file.
// Returns 0 if the magic is not available.
public synchronized long getMagic(long id) {
// check the mini thumb file for the right data. Right is
// defined as having the right magic number at the offset
// reserved for this "id".
RandomAccessFile r = miniThumbDataFile();
if (r != null) {
long pos = id * BYTES_PER_MINTHUMB;
FileLock lock = null;
try {
mBuffer.clear();
mBuffer.limit(1 + 8);
lock = mChannel.lock(pos, 1 + 8, true);
// check that we can read the following 9 bytes
// (1 for the "status" and 8 for the long)
if (mChannel.read(mBuffer, pos) == 9) {
mBuffer.position(0);
if (mBuffer.get() == 1) {
return mBuffer.getLong();
}
}
} catch (IOException ex) {
Log.v(TAG, "Got exception checking file magic: ", ex);
} catch (RuntimeException ex) {
// Other NIO related exception like disk full, read only channel..etc
Log.e(TAG, "Got exception when reading magic, id = " + id +
", disk full or mount read-only? " + ex.getClass());
} finally {
try {
if (lock != null) lock.release();
}
catch (IOException ex) {
// ignore it.
}
}
}
return 0;
}
I got the runtime exception when trying to get the original Id of a thumbnail by looking up the thumbnail's path. (BTW, the disk isn't full and it's not read-only.)
It's a bit strange parameter...
While exploring the Gallery source code,
noticed that the value is being read from the cursor, but then is Never used:
#Override
protected BaseImage loadImageFromCursor(Cursor cursor) {
long id = cursor.getLong(INDEX_ID);
String dataPath = cursor.getString(INDEX_DATA_PATH);
long dateTaken = cursor.getLong(INDEX_DATE_TAKEN);
if (dateTaken == 0) {
dateTaken = cursor.getLong(INDEX_DATE_MODIFIED) * 1000;
}
// here they read it ====>>
long miniThumbMagic = cursor.getLong(INDEX_MINI_THUMB_MAGIC);
int orientation = cursor.getInt(INDEX_ORIENTATION);
String title = cursor.getString(INDEX_TITLE);
String mimeType = cursor.getString(INDEX_MIME_TYPE);
if (title == null || title.length() == 0) {
title = dataPath;
}
// and not use at all ==>>>
return new Image(this, mContentResolver, id, cursor.getPosition(),
contentUri(id), dataPath, mimeType, dateTaken, title,
orientation);
}
Maybe it was used on the previous APIs.
ref: https://android.googlesource.com/platform/packages/apps/Gallery/+/android-8.0.0_r12/src/com/android/camera/gallery/ImageList.java?autodive=0%2F%2F.
and videos list:
https://android.googlesource.com/platform/packages/apps/Gallery/+/android-8.0.0_r12/src/com/android/camera/gallery/VideoList.java?autodive=0%2F%2F