Using Unity 5.4 and FB SDK 7.9 I want to use the onclick button functionality to run the method Share. This method has many parameters, so I need to create the onclick functionality in code and not in the inspector. I am getting the error unexpected symbol link parameter expecting '.' What is going on? How can I fix this?
public class FBScript : MonoBehaviour {
public Button PostBtn;
void Start() {
Button btn = PostBtn.GetComponent<Button>();
btn.onClick.AddListener(delegate {
Share(string linkParameter, string namerParameter, string captionParameter, string descriptionParameter, string pictureParameter, string redirectParameter);
});
}
void Awake () {
FB.Init(SetInit, OnHideUnity);
}
void SetInit () {
if (FB.IsLoggedIn) {
Debug.Log("FB is Logged In");
} else {
Debug.Log("FB is not Logged In");
}
}
void OnHideUnity(bool isGameShown) {
if (!isGameShown) {
Time.timeScale = 0;
} else {
Time.timeScale = 1;
}
}
public void FBLogin() {
List<string> permissions = new List<string>();
permissions.Add("public_profile");
FB.LogInWithReadPermissions(permissions, AuthCallBack);
}
void AuthCallBack(ILoginResult result) {
if (result.Error != null) {
Debug.Log(result.Error);
} else {
if (FB.IsLoggedIn) {
Debug.Log("FB is Logged In");
} else {
Debug.Log("FB is not Logged In");
}
}
}
private const string FACEBOOK_APP_ID = "999999999999999";
private const string FACEBOOK_URL = "http://www.facebook.com/dialog/feed";
public void Share(string linkParameter, string namerParameter, string captionParameter, string descriptionParameter, string pictureParameter, string redirectParameter) {
Application.OpenURL(FACEBOOK_URL + "?app_id=" + FACEBOOK_APP_ID +
"&link=" + WWW.EscapeURL(linkParameter) +
"&name=" + WWW.EscapeURL(namerParameter) +
"&caption=" + WWW.EscapeURL(captionParameter) +
"&description=" + WWW.EscapeURL(descriptionParameter) +
"&picture=" + WWW.EscapeURL(pictureParameter) +
"&redirect_url=" + WWW.EscapeURL(redirectParameter));
}
}
Related
I have weird behaviour for my SignInWithGoogle.
I have everything set up for QAuth, SSH, WebClieng etc.
And when I sent a build to my phone directly. All work fine. I can SignIn, LogIn etc.
But when I made an aab build and uploaded it to google console and downloaded it from GooglePlay, as a tester, I received DeveloperError Exception of type 'Google.GoogleSignIn+SignInException.
Is there maybe something I need to change on QAuth to fix that?
public class SignInWithGoogle : MonoBehaviour
{
public static string slaveUserEmail;
public static string slaveUserPassword;
string webClientId = "536446807232-vh3olku8c637olltqlge92p17qmsqmtl.apps.googleusercontent.com";
private GoogleSignInConfiguration configuration;
FirebaseAuth _auth;
bool _initialized = false;
void Awake()
{
FireBaseInit.OnInitialized += OnFirebaseInit;
configuration = new GoogleSignInConfiguration
{
WebClientId = webClientId,
RequestIdToken = true
};
}
public void OnFirebaseInit()
{
if (_initialized) return;
_auth = FirebaseAuth.DefaultInstance;
_initialized = true;
Debug.Log("GoogleAuth Initialized");
}
public void OnSignIn()
{
Debug.Log("Calling SignIn");
GoogleSignIn.Configuration = configuration;
GoogleSignIn.Configuration.UseGameSignIn = false;
GoogleSignIn.Configuration.RequestIdToken = true;
var signInCompleted = new TaskCompletionSource<FirebaseUser>();
Debug.Log("SignInInit");
try
{
GoogleSignIn.DefaultInstance.SignIn().ContinueWith(task =>
{
if (task.IsFaulted)
{
using (IEnumerator<Exception> enumerator =
task.Exception.InnerExceptions.GetEnumerator())
{
if (enumerator.MoveNext())
{
GoogleSignIn.SignInException error =
(GoogleSignIn.SignInException)enumerator.Current;
Debug.Log("Got Error: " + error.Status + " " + error.Message);
}
else
{
Debug.Log("Got Unexpected Exception?!?" + task.Exception);
}
}
}
else if (task.IsCanceled)
{
Debug.Log("Canceled");
}
else
{
Debug.Log("Welcome in Google: " + task.Result.DisplayName + "!");
Debug.Log("GEt Ceds");
Credential credential = GoogleAuthProvider.GetCredential(task.Result.IdToken, null);
Debug.Log("Creds added");
Debug.Log("Firebase Log In try!");
FirebaseAuth.DefaultInstance.SignInWithCredentialAsync(credential).ContinueWith(authTask =>
{
if (authTask.IsCanceled)
{
Debug.Log("Auth Canceld");
signInCompleted.SetCanceled();
}
else if (authTask.IsFaulted)
{
Debug.Log("Auth Faulted");
signInCompleted.SetException(authTask.Exception);
}
else
{
Debug.Log("Auth Coplited");
signInCompleted.SetResult(((Task<FirebaseUser>)authTask).Result);
}
});
}
});
}
catch (Exception ex)
{
Debug.Log(ex.Message);
}
}
The full error:
Got Error: DeveloperError Exception of type 'Google.GoogleSignIn+SignInException' was thrown. <>c__DisplayClass8_0:b__0(Task`1) System.Threading.ThreadPoolWorkQueue:Dispatch()
i'm implementing a third party android sdk in flutter and i want a message to be passed from android to flutter when sdk starts
i have implemented the sdk using platform channel just need to work on the callback code. In the code there is a function called onChannelJoin i want to send message to flutter when this function is called
Main Activity
public class MainActivity extends FlutterActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
final String CHANNEL = "samples.flutter.io/screen_record";
new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(
new MethodChannel.MethodCallHandler() {
#Override
public void onMethodCall(MethodCall call, MethodChannel.Result result) {
// TODO
if (call.method.equals("startScreenShare")) {
Intent intent = new Intent(MainActivity.this , HelloAgoraScreenSharingActivity.class);
startActivity(intent);
} else {
result.notImplemented();
}
}
});
}
}
ScreenSharingActivity
public class HelloAgoraScreenSharingActivity extends Activity {
private static final String LOG_TAG = "AgoraScreenSharing";
private static final int PERMISSION_REQ_ID_RECORD_AUDIO = 22;
private ScreenCapture mScreenCapture;
private GLRender mScreenGLRender;
private RtcEngine mRtcEngine;
private boolean mIsLandSpace = false;
private void initModules() {
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
if (mScreenGLRender == null) {
mScreenGLRender = new GLRender();
}
if (mScreenCapture == null) {
mScreenCapture = new ScreenCapture(getApplicationContext(), mScreenGLRender, metrics.densityDpi);
}
mScreenCapture.mImgTexSrcConnector.connect(new SinkConnector<ImgTexFrame>() {
#Override
public void onFormatChanged(Object obj) {
Log.d(LOG_TAG, "onFormatChanged " + obj.toString());
}
#Override
public void onFrameAvailable(ImgTexFrame frame) {
Log.d(LOG_TAG, "onFrameAvailable " + frame.toString());
if (mRtcEngine == null) {
return;
}
AgoraVideoFrame vf = new AgoraVideoFrame();
vf.format = AgoraVideoFrame.FORMAT_TEXTURE_OES;
vf.timeStamp = frame.pts;
vf.stride = frame.mFormat.mWidth;
vf.height = frame.mFormat.mHeight;
vf.textureID = frame.mTextureId;
vf.syncMode = true;
vf.eglContext14 = mScreenGLRender.getEGLContext();
vf.transform = frame.mTexMatrix;
mRtcEngine.pushExternalVideoFrame(vf);
}
});
mScreenCapture.setOnScreenCaptureListener(new ScreenCapture.OnScreenCaptureListener() {
#Override
public void onStarted() {
Log.d(LOG_TAG, "Screen Record Started");
}
#Override
public void onError(int err) {
Log.d(LOG_TAG, "onError " + err);
switch (err) {
case ScreenCapture.SCREEN_ERROR_SYSTEM_UNSUPPORTED:
break;
case ScreenCapture.SCREEN_ERROR_PERMISSION_DENIED:
break;
}
}
});
WindowManager wm = (WindowManager) getApplicationContext()
.getSystemService(Context.WINDOW_SERVICE);
int screenWidth = wm.getDefaultDisplay().getWidth();
int screenHeight = wm.getDefaultDisplay().getHeight();
if ((mIsLandSpace && screenWidth < screenHeight) ||
(!mIsLandSpace) && screenWidth > screenHeight) {
screenWidth = wm.getDefaultDisplay().getHeight();
screenHeight = wm.getDefaultDisplay().getWidth();
}
setOffscreenPreview(screenWidth, screenHeight);
if (mRtcEngine == null) {
try {
mRtcEngine = RtcEngine.create(getApplicationContext(), "Agora_id", new IRtcEngineEventHandler() {
#Override
public void onJoinChannelSuccess(String channel, int uid, int elapsed) {
Log.d(LOG_TAG, "onJoinChannelSuccess " + channel + " " + elapsed);
}
#Override
public void onWarning(int warn) {
Log.d(LOG_TAG, "onWarning " + warn);
}
#Override
public void onError(int err) {
Log.d(LOG_TAG, "onError " + err);
}
#Override
public void onAudioRouteChanged(int routing) {
Log.d(LOG_TAG, "onAudioRouteChanged " + routing);
}
});
} catch (Exception e) {
Log.e(LOG_TAG, Log.getStackTraceString(e));
throw new RuntimeException("NEED TO check rtc sdk init fatal error\n" + Log.getStackTraceString(e));
}
mRtcEngine.setChannelProfile(Constants.CHANNEL_PROFILE_LIVE_BROADCASTING);
mRtcEngine.enableVideo();
if (mRtcEngine.isTextureEncodeSupported()) {
mRtcEngine.setExternalVideoSource(true, true, true);
} else {
throw new RuntimeException("Can not work on device do not supporting texture" + mRtcEngine.isTextureEncodeSupported());
}
mRtcEngine.setVideoProfile(Constants.VIDEO_PROFILE_360P, true);
mRtcEngine.setClientRole(Constants.CLIENT_ROLE_BROADCASTER);
}
}
private void deInitModules() {
RtcEngine.destroy();
mRtcEngine = null;
if (mScreenCapture != null) {
mScreenCapture.release();
mScreenCapture = null;
}
if (mScreenGLRender != null) {
mScreenGLRender.quit();
mScreenGLRender = null;
}
}
/**
* Set offscreen preview.
*
* #param width offscreen width
* #param height offscreen height
* #throws IllegalArgumentException
*/
public void setOffscreenPreview(int width, int height) throws IllegalArgumentException {
if (width <= 0 || height <= 0) {
throw new IllegalArgumentException("Invalid offscreen resolution");
}
mScreenGLRender.init(width, height);
}
private void startCapture() {
mScreenCapture.start();
}
private void stopCapture() {
mScreenCapture.stop();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_hello_agora_screen_sharing);
}
public void onLiveSharingScreenClicked(View view) {
Button button = (Button) view;
boolean selected = button.isSelected();
button.setSelected(!selected);
if (button.isSelected()) {
initModules();
startCapture();
String channel = "ss_test" + System.currentTimeMillis();
channel = "ss_test";
button.setText("stop");
mRtcEngine.muteAllRemoteAudioStreams(true);
mRtcEngine.muteAllRemoteVideoStreams(true);
mRtcEngine.joinChannel(null, channel, "", 0);
} else {
button.setText("start");
mRtcEngine.leaveChannel();
stopCapture();
}
}
#Override
protected void onDestroy() {
super.onDestroy();
deInitModules();
}
}
Dart Code
const platform = const MethodChannel('samples.flutter.io/screen_record');
try {
final int result = await platform.invokeMethod('startScreenShare');
} on PlatformException catch (e) {}
setState(() {
});
I'm trying to save a variable using google play games cloud save. However it crashes when it signs in. I've definitely enabled it on the developer console. I never had this problem before I added the cloud save feature and it was just doing achievements and scoreboards. Also, when I'm not connected to the internet, it doesn't crash and locally saving the data works fine. Can any one help?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using GooglePlayGames;
using GooglePlayGames.BasicApi;
using GooglePlayGames.BasicApi.SavedGame;
using System.Text;
public class playgamesscript : MonoBehaviour {
public static playgamesscript Instance { get; private set; }
const string SAVE_NAME = "Test";
bool isSaving;
textEdit textEditScript;
control controlScript;
bool isCloudDataLoaded;
// Use this for initialization
void Start () {
Instance = this;
textEditScript = GameObject.FindGameObjectWithTag("UIControl").GetComponent<textEdit>();
controlScript = GameObject.FindGameObjectWithTag("Control").GetComponent<control>();
if (!PlayerPrefs.HasKey(SAVE_NAME))
PlayerPrefs.SetString(SAVE_NAME, "0");
if (!PlayerPrefs.HasKey("IsFirstTime"))
PlayerPrefs.SetInt("IsFirstTime", 1);
LoadLocal();
PlayGamesClientConfiguration config = new PlayGamesClientConfiguration.Builder().EnableSavedGames().Build();
PlayGamesPlatform.InitializeInstance(config);
PlayGamesPlatform.Activate();
if (control.signInAttempt == false)
{
SignIn();
}
}
void SignIn()
{
control.signInAttempt = true;
Social.localUser.Authenticate(success => { LoadData(); });
}
#region Saved Games
string GameDataToString()
{
return control.Highscore.ToString();
}
void StringToGameData(string cloudData, string localData)
{
if (PlayerPrefs.GetInt("IsFirstTime") == 1){
PlayerPrefs.SetInt("IsFirstTime", 0);
if (int.Parse(cloudData) > int.Parse(localData)){
PlayerPrefs.SetString(SAVE_NAME, cloudData);
}
}
else if (int.Parse(localData) > int.Parse(cloudData))
{
control.Highscore = int.Parse(localData);
AddScoreToLoeaderBoard(textEdit.leaderboardStat, control.Highscore);
isCloudDataLoaded = true;
SaveData();
return;
}
control.Highscore = int.Parse(cloudData);
isCloudDataLoaded = true;
}
void StringToGameData (string localData)
{
control.Highscore = int.Parse(localData);
}
void LoadData()
{
if (Social.localUser.authenticated)
{
isSaving = false;
((PlayGamesPlatform)Social.Active).SavedGame.OpenWithManualConflictResolution(SAVE_NAME, DataSource.ReadCacheOrNetwork, true, ResolveConflict, OnSavedGameOpened);
}
else {
LoadLocal();
}
}
private void LoadLocal()
{
StringToGameData(PlayerPrefs.GetString(SAVE_NAME));
}
public void SaveData()
{
if (!isCloudDataLoaded)
{
SaveLocal();
return;
}
if (Social.localUser.authenticated)
{
isSaving = true;
((PlayGamesPlatform)Social.Active).SavedGame.OpenWithManualConflictResolution(SAVE_NAME, DataSource.ReadCacheOrNetwork, true, ResolveConflict, OnSavedGameOpened);
}
else
{
SaveLocal();
}
}
private void SaveLocal()
{
PlayerPrefs.SetString(SAVE_NAME, GameDataToString());
}
private void ResolveConflict(IConflictResolver resolver, ISavedGameMetadata original, byte[] originalData, ISavedGameMetadata unmerged, byte[] unmergedData)
{
if (originalData == null)
{
resolver.ChooseMetadata(unmerged);
} else if (unmergedData == null)
{
resolver.ChooseMetadata(original);
} else
{
string originalStr = Encoding.ASCII.GetString(originalData);
string unmergedStr = Encoding.ASCII.GetString(unmergedData);
int originalNum = int.Parse(originalStr);
int unmergedNum = int.Parse(unmergedStr);
if (originalNum > unmergedNum)
{
resolver.ChooseMetadata(original);
return;
} else if (unmergedNum> originalNum)
{
resolver.ChooseMetadata(unmerged);
}
resolver.ChooseMetadata(original);
}
}
private void OnSavedGameOpened(SavedGameRequestStatus status, ISavedGameMetadata game)
{
if (status == SavedGameRequestStatus.Success)
{
if (!isSaving)
{
LoadGame(game);
} else
{
SaveGame(game);
}
}
else
{
if (!isSaving)
{
LoadLocal();
}else
{
SaveLocal();
}
}
}
private void LoadGame(ISavedGameMetadata game)
{
((PlayGamesPlatform)Social.Active).SavedGame.ReadBinaryData(game, OnSavedGameDataRead);
}
private void SaveGame(ISavedGameMetadata game)
{
string stringToSave = GameDataToString();
PlayerPrefs.SetString(SAVE_NAME, stringToSave);
byte[] dataToSave = Encoding.ASCII.GetBytes(stringToSave);
SavedGameMetadataUpdate update = new SavedGameMetadataUpdate.Builder().Build();
((PlayGamesPlatform)Social.Active).SavedGame.CommitUpdate(game, update, dataToSave, OnSavedGameDataWritten);
}
private void OnSavedGameDataRead(SavedGameRequestStatus status, byte[] savedData)
{
if (status == SavedGameRequestStatus.Success)
{
string cloudDataString;
if (savedData.Length == 0)
{
cloudDataString = "0";
} else
cloudDataString = Encoding.ASCII.GetString(savedData);
string localDataString = PlayerPrefs.GetString(SAVE_NAME);
StringToGameData(cloudDataString, localDataString);
}
}
private void OnSavedGameDataWritten(SavedGameRequestStatus status, ISavedGameMetadata game)
{
}
#endregion /Saved Games
///
JNI DETECTED ERROR IN APPLICATION: can't call void com.google.android.gms.common.api.PendingResult.setResultCallback(com.google.android.gms.common.api.ResultCallback) on null object'
I get the following log during on-device debugging
Error:
*** [Play Games Plugin DLL] ERROR: Cloud load failed with status code 7
Basically the OnStateLoaded() callback function always returns the boolean success = false and I can't figure out the reason why.
All that the plugin debugging logs mention is "Cloud load failed with status code 7".
According to the android doc, 7 is a generic "developer error", see https://developer.android.com/reference/com/google/android/gms/appstate/AppStateStatusCodes.html#STATUS_DEVELOPER_ERROR
I tried a quick sample and everything worked ok. Here are my steps:
Created a new game in the play console
(https://play.google.com/apps/publish)
Made sure Saved Games is set
to ON
Linked an Android Application Remembering the application ID
(the number after the title) and the package ID
Created a new project in Unity
Added the play games plugin (Assets/Import Package.../Custom
Package)
Set the application ID (Google Play Games/Android Setup...)
Switched the platform to Android (File/Build Settings...)
Set the player settings (bundle identifier and the keystore info)
Added a new script component to the camera
Saved everything and hit build and run.
Here are the contents:
using UnityEngine;
using System.Collections;
using GooglePlayGames;
using GooglePlayGames.BasicApi;
using System;
public class SaveSample : MonoBehaviour {
System.Action<bool> mAuthCallback;
GameData slot0;
void Start () {
slot0 = new GameData(0,"waiting for login....");
mAuthCallback = (bool success) => {
if (success) {
Debug.Log("Authentication was successful!");
slot0.Data =" loading....";
slot0.LoadState();
}
else {
Debug.LogWarning("Authentication failed!");
}
};
// make Play Games the default social implementation
PlayGamesPlatform.Activate();
// enable debug logs
PlayGamesPlatform.DebugLogEnabled = true;
//Login explicitly for this sample, usually this would be silent
PlayGamesPlatform.Instance.Authenticate(mAuthCallback, false);
}
protected void OnGUI() {
Screen.fullScreen = true;
int buttonHeight = Screen.height / 20;
int buttonWidth = Screen.width / 5;
GUI.skin.label.fontSize = 60;
GUI.skin.button.fontSize = 60;
Rect statusRect = new Rect(10,20,Screen.width,100);
Rect dataRect = new Rect( 10, 150, Screen.width,100);
Rect b1Rect = new Rect(10, 400, buttonWidth, buttonHeight);
Rect b2Rect = new Rect(b1Rect.x + 20 + buttonWidth,
b1Rect.y, buttonWidth, buttonHeight);
if(!Social.localUser.authenticated) {
if(GUI.Button(b1Rect, "Signin")) {
Social.localUser.Authenticate(mAuthCallback);
}
}
else {
// logged in, so show the load button and the contents of the saved data.
if(GUI.Button(b1Rect, "Load")) {
slot0.LoadState();
}
GUI.Label(dataRect, slot0.Data);
}
if(GUI.Button(b2Rect, "Save")) {
// just save a string, incrementing the number on the end.
int idx = slot0.Data.IndexOf("_");
if (idx > 0) {
int val = Convert.ToInt32(slot0.Data.Substring(idx+1));
val++;
slot0.Data = "Save_" + val;
}
else {
slot0.Data = "Save_0";
}
slot0.SaveState();
}
GUI.Label(statusRect, slot0.State);
}
// Class to handle save/load callbacks.
public class GameData : OnStateLoadedListener {
int slot;
string data;
string state;
public GameData(int slot, string data) {
this.slot = slot;
this.data = data;
this.state = "Initialized, modified";
}
public void LoadState() {
this.state += ", loading";
((PlayGamesPlatform)Social.Active).LoadState(0, this);
}
public void SaveState() {
byte[] bytes = new byte[data.Length * sizeof(char)];
System.Buffer.BlockCopy(data.ToCharArray(), 0, bytes, 0, bytes.Length);
this.state += ", saving";
((PlayGamesPlatform) Social.Active).UpdateState(slot, bytes, this);
}
public void OnStateLoaded(bool success, int slot, byte[] data) {
if (success) {
Debug.Log ("Save game slot : " + slot + " loaded: " + data);
if (data != null) {
char[] chars = new char[data.Length / sizeof(char)];
System.Buffer.BlockCopy(data, 0, chars, 0, data.Length);
this.data = new string(chars);
this.state = "loaded";
} else {
Debug.Log ("Saved data is null");
this.data = "";
this.state = "loaded, but empty";
}
} else {
// handle failure
Debug.LogWarning ("Save game slot : " + slot + " failed!: ");
this.data = "";
this.state = "loading failed!";
}
}
public byte[] OnStateConflict(int slot, byte[] local, byte[] server) {
// resolve conflict and return a byte[] representing the
// resolved state.
Debug.LogWarning("Conflict in saved data!");
state = "conflicted";
// merge or resolve using app specific logic, here
byte[] resolved = local.Length > server.Length ? local : server;
char[] chars = new char[resolved.Length / sizeof(char)];
System.Buffer.BlockCopy(resolved, 0, chars, 0, resolved.Length);
this.data = new string(chars);
return resolved;
}
public void OnStateSaved(bool success, int slot) {
Debug.Log ("Save game slot : " + slot + " success: " + success);
state = "saved";
}
public string Data {
get {
return data;
}
set {
data = value;
state += ", modified";
}
}
public int Slot {
get {
return slot;
}
}
public string State {
get {
return state;
}
}
}
}
The error code 7 is because the Cloud Save API has been deprecated and is only currently accessible to existing games that have used the API. The Unity plugin version 0.9.11 has been updated to use the SavedGames API.
I tried a quick sample and everything worked ok. Here are my steps:
Created a new game in the play console
(https://play.google.com/apps/publish)
Made sure Saved Games is set to ON
Linked an Android Application Remembering the application ID
(the number after the title) and the package ID Created a new
project in Unity
Added the play games plugin (Assets/Import
Package.../Custom Package)
Set the application ID (Google Play
Games/Android Setup...)
Switched the platform to Android (File/Build
Settings...)
Set the player settings (bundle identifier and the
keystore info)
Added a new script component to the camera Saved
everything and hit build and run.
Here is my script:
using UnityEngine;
using System.Collections;
using GooglePlayGames;
using GooglePlayGames.BasicApi;
using System;
using GooglePlayGames.BasicApi.SavedGame;
public class SaveSample : MonoBehaviour {
System.Action<bool> mAuthCallback;
GameData slot0;
bool mSaving;
private Texture2D mScreenImage;
// Use this for initialization
void Start () {
slot0 = new GameData("New game");
mAuthCallback = (bool success) => {
if (success) {
Debug.Log("Authentication was successful!");
slot0.State = "Click load or save";
}
else {
Debug.LogWarning("Authentication failed!");
}
};
PlayGamesClientConfiguration config = new PlayGamesClientConfiguration.Builder()
.EnableSavedGames()
.Build();
PlayGamesPlatform.InitializeInstance(config);
// Activate the Play Games platform. This will make it the default
// implementation of Social.Active
PlayGamesPlatform.Activate();
// enable debug logs (note: we do this because this is a sample; on your production
// app, you probably don't want this turned on by default, as it will fill the user's
// logs with debug info).
PlayGamesPlatform.DebugLogEnabled = true;
//Login explicitly for this sample, usually this would be silent
PlayGamesPlatform.Instance.Authenticate(mAuthCallback, false);
}
public void CaptureScreenshot() {
mScreenImage = new Texture2D(Screen.width, Screen.height);
mScreenImage.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0);
mScreenImage.Apply();
}
protected virtual void OnGUI() {
Screen.fullScreen = true;
int buttonHeight = Screen.height / 20;
int buttonWidth = Screen.width / 5;
GUI.skin.label.fontSize = 60;
GUI.skin.button.fontSize = 60;
Rect statusRect = new Rect(10,20,Screen.width,200);
Rect dataRect = new Rect( 10, 250, Screen.width,100);
Rect b1Rect = new Rect(10, 800, buttonWidth, buttonHeight);
Rect b2Rect = new Rect(b1Rect.x + 20 + buttonWidth, b1Rect.y, buttonWidth, buttonHeight);
if(!Social.localUser.authenticated) {
if(GUI.Button(b1Rect, "Signin")) {
Social.localUser.Authenticate(mAuthCallback);
}
}
else {
if(GUI.Button(b1Rect, "Load")) {
mSaving = false;
((PlayGamesPlatform)Social.Active).SavedGame.ShowSelectSavedGameUI("Select game to load",
4,false,false,SavedGameSelected);
}
GUI.Label(dataRect, slot0.Data);
}
if(GUI.Button(b2Rect, "Save")) {
int idx = slot0.Data.IndexOf("_");
if (idx > 0) {
int val = Convert.ToInt32(slot0.Data.Substring(idx+1));
val++;
slot0.Data = "Save_" + val;
}
else {
slot0.Data = "Save_0";
}
mSaving = true;
CaptureScreenshot();
((PlayGamesPlatform)Social.Active).SavedGame.ShowSelectSavedGameUI("Save game progress",
4,true,true,SavedGameSelected);
}
GUI.Label(statusRect, slot0.State);
}
public void SavedGameSelected(SelectUIStatus status, ISavedGameMetadata game) {
if (status == SelectUIStatus.SavedGameSelected) {
string filename = game.Filename;
Debug.Log("opening saved game: " + game);
if(mSaving && (filename == null || filename.Length == 0)) {
filename = "save" + DateTime.Now.ToBinary();
}
if (mSaving) {
slot0.State = "Saving to " + filename;
}
else {
slot0.State = "Loading from " + filename;
}
//open the data.
((PlayGamesPlatform)Social.Active).SavedGame.OpenWithAutomaticConflictResolution(filename,
DataSource.ReadCacheOrNetwork,
ConflictResolutionStrategy.UseLongestPlaytime,
SavedGameOpened);
} else {
Debug.LogWarning("Error selecting save game: " + status);
}
}
public void SavedGameOpened(SavedGameRequestStatus status, ISavedGameMetadata game) {
if(status == SavedGameRequestStatus.Success) {
if( mSaving) {
slot0.State = "Opened, now writing";
byte[] pngData = (mScreenImage!=null) ?mScreenImage.EncodeToPNG():null;
Debug.Log("Saving to " + game);
byte[] data = slot0.ToBytes();
TimeSpan playedTime = slot0.TotalPlayingTime;
SavedGameMetadataUpdate.Builder builder = new
SavedGameMetadataUpdate.Builder()
.WithUpdatedPlayedTime(playedTime)
.WithUpdatedDescription("Saved Game at " + DateTime.Now);
if (pngData != null) {
Debug.Log("Save image of len " + pngData.Length);
builder = builder.WithUpdatedPngCoverImage(pngData);
}
else {
Debug.Log ("No image avail");
}
SavedGameMetadataUpdate updatedMetadata = builder.Build();
((PlayGamesPlatform)Social.Active).SavedGame.CommitUpdate(game,updatedMetadata,data,SavedGameWritten);
} else {
slot0.State = "Opened, reading...";
((PlayGamesPlatform)Social.Active).SavedGame.ReadBinaryData(game,SavedGameLoaded);
}
} else {
Debug.LogWarning("Error opening game: " + status);
}
}
public void SavedGameLoaded(SavedGameRequestStatus status, byte[] data) {
if (status == SavedGameRequestStatus.Success) {
Debug.Log("SaveGameLoaded, success=" + status);
slot0 = GameData.FromBytes(data);
} else {
Debug.LogWarning("Error reading game: " + status);
}
}
public void SavedGameWritten(SavedGameRequestStatus status, ISavedGameMetadata game) {
if(status == SavedGameRequestStatus.Success) {
Debug.Log ("Game " + game.Description + " written");
slot0.State = "Saved!";
} else {
Debug.LogWarning("Error saving game: " + status);
}
}
public class GameData {
private TimeSpan mPlayingTime;
private DateTime mLoadedTime;
string mData;
string mState;
static readonly string HEADER = "GDv1";
public GameData(string data) {
mData = data;
mState = "Initialized, modified";
mPlayingTime = new TimeSpan();
mLoadedTime = DateTime.Now;
}
public TimeSpan TotalPlayingTime {
get {
TimeSpan delta = DateTime.Now.Subtract(mLoadedTime);
return mPlayingTime.Add(delta);
}
}
public override string ToString () {
string s = HEADER + ":" + mData;
s += ":" + TotalPlayingTime.TotalMilliseconds;
return s;
}
public byte[] ToBytes() {
return System.Text.ASCIIEncoding.Default.GetBytes(ToString());
}
public static GameData FromBytes (byte[] bytes) {
return FromString(System.Text.ASCIIEncoding.Default.GetString(bytes));
}
public static GameData FromString (string s) {
GameData gd = new GameData("initializing from string");
string[] p = s.Split(new char[] { ':' });
if (!p[0].StartsWith(HEADER)) {
Debug.LogError("Failed to parse game data from: " + s);
return gd;
}
gd.mData = p[1];
double val = Double.Parse(p[2]);
gd.mPlayingTime = TimeSpan.FromMilliseconds(val>0f?val:0f);
gd.mLoadedTime = DateTime.Now;
gd.mState = "Loaded successfully";
return gd;
}
public string Data {
get {
return mData;
}
set {
mData = value;
mState += ", modified";
}
}
public string State {
get {
return mState;
}
set {
mState = value;
}
}
}
}
I am using below class to enable stream management("urn:xmpp:sm:3") in our ejabberd server(we have latest version of ejabberd). But when I send the Enable packet to server it says Service Unavailable(503). But when I use "yaxim" it works perfectly. Please help me to solve this problem. Thanks.
public class XmppStreamHandler {
public static final String URN_SM_3 = "urn:xmpp:sm:3";
private static final int MAX_OUTGOING_QUEUE_SIZE = 20;
private static final int OUTGOING_FILL_RATIO = 4;
private XMPPConnection mConnection;
private boolean isSmAvailable = false;
private boolean isSmEnabled = false;
private boolean isOutgoingSmEnabled = false;
private long previousIncomingStanzaCount = -1;
private String sessionId;
private long incomingStanzaCount = 0;
private long outgoingStanzaCount = 0;
private Queue<Packet> outgoingQueue;
private int maxOutgoingQueueSize = MAX_OUTGOING_QUEUE_SIZE;
private ConnectionListener mConnectionListener;
public XmppStreamHandler(XMPPConnection connection, ConnectionListener connectionListener) {
mConnection = connection;
mConnectionListener = connectionListener;
startListening();
}
/** Perform a quick shutdown of the XMPPConnection if a resume is possible */
public void quickShutdown() {
if (isResumePossible()) {
mConnection.quickShutdown();
// We will not necessarily get any notification from a quickShutdown, so adjust our state here.
closeOnError();
} else {
mConnection.shutdown();
}
}
public void setMaxOutgoingQueueSize(int maxOutgoingQueueSize) {
this.maxOutgoingQueueSize = maxOutgoingQueueSize;
}
public boolean isResumePossible() {
return sessionId != null;
}
public boolean isResumePending() {
return isResumePossible() && !isSmEnabled;
}
public static void addExtensionProviders() {
addSimplePacketExtension("sm", URN_SM_3);
addSimplePacketExtension("r", URN_SM_3);
addSimplePacketExtension("a", URN_SM_3);
addSimplePacketExtension("enabled", URN_SM_3);
addSimplePacketExtension("resumed", URN_SM_3);
addSimplePacketExtension("failed", URN_SM_3);
}
public void notifyInitialLogin() {
if (sessionId == null && isSmAvailable)
sendEnablePacket();
}
private void sendEnablePacket() {
debug("sm send enable " + sessionId);
if (sessionId != null) {
isOutgoingSmEnabled = true;
// TODO binding
StreamHandlingPacket resumePacket = new StreamHandlingPacket("resume", URN_SM_3);
resumePacket.addAttribute("h", String.valueOf(previousIncomingStanzaCount));
resumePacket.addAttribute("previd", sessionId);
mConnection.sendPacket(resumePacket);
} else {
outgoingStanzaCount = 0;
outgoingQueue = new ConcurrentLinkedQueue<Packet>();
isOutgoingSmEnabled = true;
StreamHandlingPacket enablePacket = new StreamHandlingPacket("enable", URN_SM_3);
enablePacket.addAttribute("resume", "true");
mConnection.sendPacket(enablePacket);
}
}
private void closeOnError() {
if (isSmEnabled && sessionId != null) {
previousIncomingStanzaCount = incomingStanzaCount;
}
isSmEnabled = false;
isOutgoingSmEnabled = false;
isSmAvailable = false;
}
private void startListening() {
mConnection.forceAddConnectionListener(new ConnectionListener() {
public void reconnectionSuccessful() {
}
public void reconnectionFailed(Exception e) {
}
public void reconnectingIn(int seconds) {
}
public void connectionClosedOnError(Exception e) {
if (e instanceof XMPPException &&
((XMPPException)e).getStreamError() != null) {
// Non-resumable stream error
close();
} else {
// Resumable
closeOnError();
}
}
public void connectionClosed() {
previousIncomingStanzaCount = -1;
}
});
mConnection.addPacketSendingListener(new PacketListener() {
public void processPacket(Packet packet) {
// Ignore our own request for acks - they are not counted
if (!isStanza(packet)) {
trace("send " + packet.toXML());
return;
}
if (isOutgoingSmEnabled && !outgoingQueue.contains(packet)) {
outgoingStanzaCount++;
outgoingQueue.add(packet);
trace("send " + outgoingStanzaCount + " : " + packet.toXML());
// Don't let the queue grow beyond max size. Request acks and drop old packets
// if acks are not coming.
if (outgoingQueue.size() >= maxOutgoingQueueSize / OUTGOING_FILL_RATIO) {
mConnection.sendPacket(new StreamHandlingPacket("r", URN_SM_3));
}
if (outgoingQueue.size() > maxOutgoingQueueSize) {
// Log.e(XmppConnection.TAG, "not receiving acks? outgoing queue full");
outgoingQueue.remove();
}
} else if (isOutgoingSmEnabled && outgoingQueue.contains(packet)) {
outgoingStanzaCount++;
trace("send DUPLICATE " + outgoingStanzaCount + " : " + packet.toXML());
} else {
trace("send " + packet.toXML());
}
}
}, new PacketFilter() {
public boolean accept(Packet packet) {
return true;
}
});
mConnection.addPacketListener(new PacketListener() {
public void processPacket(Packet packet) {
if (isSmEnabled && isStanza(packet)) {
incomingStanzaCount++;
trace("recv " + incomingStanzaCount + " : " + packet.toXML());
} else {
trace("recv " + packet.toXML());
}
if (packet instanceof StreamHandlingPacket) {
StreamHandlingPacket shPacket = (StreamHandlingPacket) packet;
String name = shPacket.getElementName();
if ("sm".equals(name)) {
debug("sm avail");
isSmAvailable = true;
if (sessionId != null)
sendEnablePacket();
} else if ("r".equals(name)) {
StreamHandlingPacket ackPacket = new StreamHandlingPacket("a", URN_SM_3);
ackPacket.addAttribute("h", String.valueOf(incomingStanzaCount));
mConnection.sendPacket(ackPacket);
} else if ("a".equals(name)) {
long ackCount = Long.valueOf(shPacket.getAttribute("h"));
removeOutgoingAcked(ackCount);
trace(outgoingQueue.size() + " in outgoing queue after ack");
} else if ("enabled".equals(name)) {
incomingStanzaCount = 0;
isSmEnabled = true;
mConnection.getRoster().setOfflineOnError(false);
String resume = shPacket.getAttribute("resume");
if ("true".equals(resume) || "1".equals(resume)) {
sessionId = shPacket.getAttribute("id");
}
debug("sm enabled " + sessionId);
} else if ("resumed".equals(name)) {
debug("sm resumed");
incomingStanzaCount = previousIncomingStanzaCount;
long resumeStanzaCount = Long.valueOf(shPacket.getAttribute("h"));
// Removed acked packets
removeOutgoingAcked(resumeStanzaCount);
trace(outgoingQueue.size() + " in outgoing queue after resume");
// Resend any unacked packets
for (Packet resendPacket : outgoingQueue) {
mConnection.sendPacket(resendPacket);
}
// Enable only after resend, so that the interceptor does not
// queue these again or increment outgoingStanzaCount.
isSmEnabled = true;
// Re-notify the listener - we are really ready for packets now
// Before this point, isSuspendPending() was true, and the listener should have
// ignored reconnectionSuccessful() from XMPPConnection.
mConnectionListener.reconnectionSuccessful();
} else if ("failed".equals(name)) {
// Failed, shutdown and the parent will retry
debug("sm failed");
mConnection.getRoster().setOfflineOnError(true);
mConnection.getRoster().setOfflinePresences();
sessionId = null;
mConnection.shutdown();
// isSmEnabled / isOutgoingSmEnabled are already false
}
}
}
}, new PacketFilter() {
public boolean accept(Packet packet) {
return true;
}
});
}
private void removeOutgoingAcked(long ackCount) {
if (ackCount > outgoingStanzaCount) {
// Log.e(XmppConnection.TAG,
// "got ack of " + ackCount + " but only sent " + outgoingStanzaCount);
// Reset the outgoing count here in a feeble attempt to re-sync. All bets
// are off.
outgoingStanzaCount = ackCount;
}
int size = outgoingQueue.size();
while (size > outgoingStanzaCount - ackCount) {
outgoingQueue.remove();
size--;
}
}
private static void addSimplePacketExtension(final String name, final String namespace) {
ProviderManager.getInstance().addExtensionProvider(name, namespace,
new PacketExtensionProvider() {
public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
StreamHandlingPacket packet = new StreamHandlingPacket(name, namespace);
int attributeCount = parser.getAttributeCount();
for (int i = 0; i < attributeCount; i++) {
packet.addAttribute(parser.getAttributeName(i),
parser.getAttributeValue(i));
}
return packet;
}
});
}
private void debug(String message) {
System.out.println(message);
}
private void trace(String message) {
System.out.println(message);
}
public static class StreamHandlingPacket extends UnknownPacket {
private String name;
private String namespace;
Map<String, String> attributes;
StreamHandlingPacket(String name, String namespace) {
this.name = name;
this.namespace = namespace;
attributes = Collections.emptyMap();
}
public void addAttribute(String name, String value) {
if (attributes == Collections.EMPTY_MAP)
attributes = new HashMap<String, String>();
attributes.put(name, value);
}
public String getAttribute(String name) {
return attributes.get(name);
}
public String getNamespace() {
return namespace;
}
public String getElementName() {
return name;
}
public String toXML() {
StringBuilder buf = new StringBuilder();
buf.append("<").append(getElementName());
// TODO Xmlns??
if (getNamespace() != null) {
buf.append(" xmlns=\"").append(getNamespace()).append("\"");
}
for (String key : attributes.keySet()) {
buf.append(" ").append(key).append("=\"")
.append(StringUtils.escapeForXML(attributes.get(key))).append("\"");
}
buf.append("/>");
return buf.toString();
}
}
/** Returns true if the packet is a Stanza as defined in RFC-6121 - a Message, IQ or Presence packet. */
public static boolean isStanza(Packet packet) {
if (packet instanceof Message)
return true;
if (packet instanceof IQ)
return true;
if (packet instanceof Presence)
return true;
return false;
}
public void queue(Packet packet) {
if (outgoingQueue.size() >= maxOutgoingQueueSize) {
System.out.println("outgoing queue full");
return;
}
outgoingStanzaCount++;
outgoingQueue.add(packet);
}
private void close() {
isSmEnabled = false;
isOutgoingSmEnabled = false;
sessionId = null;
}
}
when I send the Enable packet to server it says Service
Unavailable(503)
Service Unavailable means that the service is unavailable on the server. Does ejabberd support XEP-198? Did you enable it?
You should also consider switching to Smack 4.1.0-alpha, which also runs on Android and comes with Stream Management support. yaxim will soon switch to from it's custom XmppStreamHandler implementation to Smack 4.1.