Unity WebGL POST requests does not work after build the game - unity3d

my unity webGL Game doesn't seems sending post request to the server when after build the game, but its working fine in the editor. Is there anything to do additionally than this?
IEnumerator PostRequestY(string url, string jsonToAPI)
{
Debug.Log("--------------Game Data Sending --------------");
var uwr = new UnityWebRequest(url, "POST");
jsonToAPI = jsonToAPI.Replace("'", "\"");
//Debug.Log(jsonToAPI);
byte[] jsonToSend = new System.Text.UTF8Encoding().GetBytes(jsonToAPI);
uwr.uploadHandler = (UploadHandler)new UploadHandlerRaw(jsonToSend);
uwr.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
uwr.SetRequestHeader("Content-Type", CONTENT_TYPE);
uwr.SetRequestHeader("x-api-key", X_API_KEY);
//Send the request then wait here until it returns
yield return uwr.SendWebRequest();
if (uwr.isNetworkError)
{
Debug.Log("Error While Sending Game Data: " + uwr.error);
Debug.Log("URL ::: " + url);
messageText.text = uwr.error;
}
else
{
//Debug.Log("Game Data Received: " + uwr.downloadHandler.text);
string recievedMessage = uwr.downloadHandler.text;
//Debug.Log("Respond for Game Data " + recievedMessage);
messageText.text = uwr.downloadHandler.text +" & "+ accessToken;
}
}
This message appears when the game is played in the editor...
This message appears when the game is played after the build...
Why this is happens?
Any suggestions would be highly appreciated...
Thanks

For the server to accept my request, You should add a header to the API response itself, with the header and its value being Access-Control-Allow-Origin: domainName or Access-Control-Allow-Origin: *
For example:
public class BookApi: MonoBehaviour {
private const string URL = "https://retrofit-backend-demo.herokuapp.com/book";
public void GenerateRequest () {
StartCoroutine (ProcessRequest (URL));
}
private IEnumerator ProcessRequest (string uri) {
using (UnityWebRequest request = UnityWebRequest.Get (uri)) {
request.SetRequestHeader("Access-Control-Allow-Origin", "*");
yield return request.SendWebRequest ();
if (request.isNetworkError) {
Debug.Log (request.error);
} else {
JSONNode books = JSON.Parse(request.downloadHandler.text);
Debug.Log(books["facts"][0]);
}
}
}
}

In Unity WebGL, the underlying web request is done with fetch or XMLHttpRequest, which means you must correctly configure CORS on your server. You are not allowed to set nearly any request header by default.
See https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader for more information.

Related

Why does Post request to OpenAI in Unity result in error 400?

I am trying to use GPT3 in a game I am making but I can't seem to be able to call the OpenAI API correctly. I got most of this from the Unity docs.
Here is the code I am using:
public class gpt3_complete : MonoBehaviour
{
public string model;
public string prompt;
public int len;
public string temp;
public string api_key = "<key>";
void Start()
{
StartCoroutine(Upload());
}
IEnumerator Upload()
{
WWWForm form = new WWWForm();
form.AddField("model", model);
form.AddField("prompt", prompt);
form.AddField("max_tokens", len);
form.AddField("temperature", temp);
//form.headers.Add("Authorization", "Bearer "+api_key);
using (UnityWebRequest www = UnityWebRequest.Post("https://api.openai.com/v1/completions", form))
{
www.SetRequestHeader("Authorization", "Bearer " + api_key);
www.SetRequestHeader("Content-Type", "application/json");
yield return www.SendWebRequest();
if (www.result != UnityWebRequest.Result.Success)
{
Debug.Log(www.error);
}
else
{
Debug.Log(www.result);
Debug.Log("Form upload complete!");
}
}
}
}
This always returns: 400 Bad Request.
The GPT3 docs can be found here: https://beta.openai.com/docs/api-reference/completions/create
Any idea why this is?
This is my first time making any web requests in unity so I'm probably missing something obvious.
Thanks!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using System.Text;
public class OpenAIRequest : MonoBehaviour
{
public string apiKey = "YOUR_API_KEY_HERE";
public string prompt = "Once upon a time, in a land far far away, there lived a brave knight";
public string model = "text-davinci-002";
public int maxTokens = 100;
void Start()
{
StartCoroutine(GetOpenAIResponse());
}
IEnumerator GetOpenAIResponse()
{
string url = "https://api.openai.com/v1/engines/" + model + "/completions";
string requestData = "{\"prompt\": \"" + prompt + "\", \"max_tokens\": " + maxTokens + "}";
UnityWebRequest request = new UnityWebRequest(url, "POST");
byte[] bodyRaw = Encoding.UTF8.GetBytes(requestData);
request.uploadHandler = (UploadHandler)new UploadHandlerRaw(bodyRaw);
request.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
request.SetRequestHeader("Content-Type", "application/json");
request.SetRequestHeader("Authorization", "Bearer " + apiKey);
yield return request.SendWebRequest();
if (request.result != UnityWebRequest.Result.Success)
{
Debug.Log(request.error);
}
else
{
string response = request.downloadHandler.text;
Debug.Log(response);
}
}
}
result

Client to Client notification [duplicate]

I have been trying to read the official docs and guides about how to send message from one device to another. I have saved registration token of both devices in the Real Time Database, thus I have the registration token of another device.
I have tried the following way to send the message
RemoteMessage message = new RemoteMessage.Builder(getRegistrationToken())
.setMessageId(incrementIdAndGet())
.addData("message", "Hello")
.build();
FirebaseMessaging.getInstance().send(message);
However this is not working. The other device doesn't receive any message. I am not even sure, if I can use upstream message sending to conduct device to device communication.
PS: I just want to know if device-to-device messaging is possible using FCM? If yes, then is the code I used have some issue? If yes, then what is the correct way.
Update:
My question was to ask whether device to device messaging without using any separate server other than firebase could messaging is possible or not, if yes than how, since there's no documentation about it. I do not understand what is left to explain here? Anyways I got the answer and will update it as an answer once the question gets reopened.
Firebase has two features to send messages to devices:
the Notifications panel in your Firebase Console allows you to send notifications to specific devices, groups of users, or topics that users subscribed to.
by calling Firebase Cloud Messaging API, you can send messages with whatever targeting strategy you prefer. Calling the FCM API requires access to your Server key, which you should never expose on client devices. That's why you should always run such code on an app server.
The Firebase documentation shows this visually:
Sending messages from one device directly to another device is not supported through the Firebase Cloud Messaging client-side SDKs.
Update: I wrote a blog post detailing how to send notifications between Android devices using Firebase Database, Cloud Messaging and Node.js.
Update 2: You can now also use Cloud Functions for Firebase to send messages securely, without spinning up a server. See this sample use-case to get started. If you don't want to use Cloud Functions, you can run the same logic on any trusted environment you already have, such as your development machine, or a server you control.
Warning There is a very important reason why we don't mention this approach anywhere. This exposes your server key in the APK that
you put on every client device. It can (and thus will) be taken from
there and may lead to abuse of your project. I highly recommend
against taking this approach, except for apps that you only put on
your own devices. – Frank van Puffelen
Ok, so the answer by Frank was correct that Firebase does not natively support device to device messaging. However there's one loophole in that. The Firebase server doesn't identify whether you have send the request from an actual server or are you doing it from your device.
So all you have to do is send a Post Request to Firebase's messaging server along with the Server Key. Just keep this in mind that the server key is not supposed to be on the device, but there's no other option if you want device-to-device messaging using Firebase Messaging.
I am using OkHTTP instead of default way of calling the Rest API. The code is something like this -
public static final String FCM_MESSAGE_URL = "https://fcm.googleapis.com/fcm/send";
OkHttpClient mClient = new OkHttpClient();
public void sendMessage(final JSONArray recipients, final String title, final String body, final String icon, final String message) {
new AsyncTask<String, String, String>() {
#Override
protected String doInBackground(String... params) {
try {
JSONObject root = new JSONObject();
JSONObject notification = new JSONObject();
notification.put("body", body);
notification.put("title", title);
notification.put("icon", icon);
JSONObject data = new JSONObject();
data.put("message", message);
root.put("notification", notification);
root.put("data", data);
root.put("registration_ids", recipients);
String result = postToFCM(root.toString());
Log.d(TAG, "Result: " + result);
return result;
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(String result) {
try {
JSONObject resultJson = new JSONObject(result);
int success, failure;
success = resultJson.getInt("success");
failure = resultJson.getInt("failure");
Toast.makeText(getCurrentActivity(), "Message Success: " + success + "Message Failed: " + failure, Toast.LENGTH_LONG).show();
} catch (JSONException e) {
e.printStackTrace();
Toast.makeText(getCurrentActivity(), "Message Failed, Unknown error occurred.", Toast.LENGTH_LONG).show();
}
}
}.execute();
}
String postToFCM(String bodyString) throws IOException {
RequestBody body = RequestBody.create(JSON, bodyString);
Request request = new Request.Builder()
.url(FCM_MESSAGE_URL)
.post(body)
.addHeader("Authorization", "key=" + SERVER_KEY)
.build();
Response response = mClient.newCall(request).execute();
return response.body().string();
}
I hope Firebase will come with a better solution in future. But till then, I think this is the only way. The other way would be to send topic message or group messaging. But that was not in the scope of the question.
Update:
The JSONArray is defined like this -
JSONArray regArray = new JSONArray(regIds);
regIds is a String array of registration ids, you want to send this message to. Keep in mind that the registration ids must always be in an array, even if you want it to send to a single recipient.
I have also been using direct device to device gcm messaging in my prototype. It has been working very well. We dont have any server. We exchange GCM reg id using sms/text and then communicate using GCM after that. I am putting here code related to GCM handling
**************Sending GCM Message*************
//Sends gcm message Asynchronously
public class GCM_Sender extends IntentService{
final String API_KEY = "****************************************";
//Empty constructor
public GCM_Sender() {
super("GCM_Sender");
}
//Processes gcm send messages
#Override
protected void onHandleIntent(Intent intent) {
Log.d("Action Service", "GCM_Sender Service Started");
//Get message from intent
String msg = intent.getStringExtra("msg");
msg = "\"" + msg + "\"";
try{
String ControllerRegistrationId = null;
//Check registration id in db
if(RegistrationIdAdapter.getInstance(getApplicationContext()).getRegIds().size() > 0 ) {
String controllerRegIdArray[] = RegistrationIdAdapter.getInstance(getApplicationContext()).getRegIds().get(1);
if(controllerRegIdArray.length>0)
ControllerRegistrationId = controllerRegIdArray[controllerRegIdArray.length-1];
if(!ControllerRegistrationId.equalsIgnoreCase("NULL")){
// 1. URL
URL url = new URL("https://android.googleapis.com/gcm/send");
// 2. Open connection
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
// 3. Specify POST method
urlConnection.setRequestMethod("POST");
// 4. Set the headers
urlConnection.setRequestProperty("Content-Type", "application/json");
urlConnection.setRequestProperty("Authorization", "key=" + API_KEY);
urlConnection.setDoOutput(true);
// 5. Add JSON data into POST request body
JSONObject obj = new JSONObject("{\"time_to_live\": 0,\"delay_while_idle\": true,\"data\":{\"message\":" + msg + "},\"registration_ids\":[" + ControllerRegistrationId + "]}");
// 6. Get connection output stream
OutputStreamWriter out = new OutputStreamWriter(urlConnection.getOutputStream());
out.write(obj.toString());
out.close();
// 6. Get the response
int responseCode = urlConnection.getResponseCode();
BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null){
response.append(inputLine);
}
in.close();
Log.d("GCM getResponseCode:", new Integer(responseCode).toString());
}else{
Log.d("GCM_Sender:","Field REGISTRATION_TABLE is null");
}
}else {
Log.d("GCM_Sender:","There is no Registration ID in DB ,please sync devices");
}
} catch (Exception e) {
e.printStackTrace();
//MessageSender.getInstance().sendMessage(msg, Commands.SMS_MESSAGE);
}
}
//Called when service is no longer alive
#Override
public void onDestroy() {
super.onDestroy();
//Do a log that GCM_Sender service has been destroyed
Log.d("Action Service", "GCM_Sender Service Destroyed");
}
}
**************Receiving GCM Message*************
public class GCM_Receiver extends WakefulBroadcastReceiver {
public static final String RETRY_ACTION ="com.google.android.c2dm.intent.RETRY";
public static final String REGISTRATION ="com.google.android.c2dm.intent.REGISTRATION";
public SharedPreferences preferences;
//Processes Gcm message .
#Override
public void onReceive(Context context, Intent intent) {
ComponentName comp = new ComponentName(context.getPackageName(),
GCMNotificationIntentService.class.getName());
//Start GCMNotificationIntentService to handle gcm message asynchronously
startWakefulService(context, (intent.setComponent(comp)));
setResultCode(Activity.RESULT_OK);
/*//Check if DatabaseService is running .
if(!DatabaseService.isServiceRunning) {
Intent dbService = new Intent(context,DatabaseService.class);
context.startService(dbService);
}*/
//Check if action is RETRY_ACTION ,if it is then do gcm registration again .
if(intent.getAction().equals(RETRY_ACTION)) {
String registrationId = intent.getStringExtra("registration_id");
if(TextUtils.isEmpty(registrationId)){
DeviceRegistrar.getInstance().register(context);
}else {
//Save registration id to prefs .
preferences = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = preferences.edit();
editor.putString("BLACKBOX_REG_ID",registrationId);
editor.commit();
}
} else if (intent.getAction().equals(REGISTRATION)) {
}
}
}
//Processes gcm messages asynchronously .
public class GCMNotificationIntentService extends IntentService{
public static final int NOTIFICATION_ID = 1;
private NotificationManager mNotificationManager;
String gcmData;
private final String TAG = "GCMNotificationIntentService";
//Constructor with super().
public GCMNotificationIntentService() {
super("GcmIntentService");
}
//Called when startService() is called by its Client .
//Processes gcm messages .
#Override
protected void onHandleIntent(Intent intent) {
Log.d("GCMNotificationIntentService", "GCMNotificationIntentService Started");
Bundle extras = intent.getExtras();
//Get instance of GoogleCloudMessaging .
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
//Get gcm message type .
String messageType = gcm.getMessageType(intent);
if (!extras.isEmpty()) {
if (GoogleCloudMessaging.MESSAGE_TYPE_SEND_ERROR
.equals(messageType)) {
sendNotification("Send error: " + extras.toString());
} else if (GoogleCloudMessaging.MESSAGE_TYPE_DELETED
.equals(messageType)) {
sendNotification("Deleted messages on server: "
+ extras.toString());
} else if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE
.equals(messageType)) {
Log.i(TAG, "Completed work # " + SystemClock.elapsedRealtime());
gcmData = extras.getString("message");
Intent actionService = new Intent(getApplicationContext(),Action.class);
actionService.putExtra("data", gcmData);
//start Action service .
startService(actionService);
//Show push notification .
sendNotification("Action: " + gcmData);
//Process received gcmData.
Log.d(TAG,"Received Gcm Message from Controller : " + extras.getString("message"));
}
}
GCM_Receiver.completeWakefulIntent(intent);
}
//Shows notification on device notification bar .
private void sendNotification(String msg) {
mNotificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
Intent notificationIntent = new Intent(this, BlackboxStarter.class);
//Clicking on GCM notification add new layer of app.
notificationIntent.setFlags( Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(
this).setSmallIcon(R.drawable.gcm_cloud)
.setContentTitle("Notification from Controller")
.setStyle(new NotificationCompat.BigTextStyle().bigText(msg))
.setContentText(msg);
mBuilder.setContentIntent(contentIntent);
mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
//Play default notification
try {
Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
Ringtone r = RingtoneManager.getRingtone(getApplicationContext(), notification);
r.play();
} catch (Exception e) {
e.printStackTrace();
}
}
//Called when service is no longer be available .
#Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
Log.d("GCMNotificationIntentService", "GCMNotificationIntentService Destroyed");
}
}
According to the new documentation which was updated on October 2, 2018 you must send post request as below
https://fcm.googleapis.com/fcm/send
Content-Type:application/json
Authorization:key=AIzaSyZ-1u...0GBYzPu7Udno5aA //Server key
{
"to": "sent device's registration token",
"data": {
"hello": "message from someone",
}
}
To get device's registration token extend FirebaseMessagingService and override onNewToken(String token)
For more info refer to doc https://firebase.google.com/docs/cloud-messaging/android/device-group
I am late but above solutions has helped me to write down this simple answer, you can send your message directly to android devices from android application, here is the simple implementation I have done and it works great for me.
compile android volley library
compile 'com.android.volley:volley:1.0.0'
Just copy paste this simple function ;) and your life will become smooth just like knife in butter. :D
public static void sendPushToSingleInstance(final Context activity, final HashMap dataValue /*your data from the activity*/, final String instanceIdToken /*firebase instance token you will find in documentation that how to get this*/ ) {
final String url = "https://fcm.googleapis.com/fcm/send";
StringRequest myReq = new StringRequest(Request.Method.POST,url,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
Toast.makeText(activity, "Bingo Success", Toast.LENGTH_SHORT).show();
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(activity, "Oops error", Toast.LENGTH_SHORT).show();
}
}) {
#Override
public byte[] getBody() throws com.android.volley.AuthFailureError {
Map<String, Object> rawParameters = new Hashtable();
rawParameters.put("data", new JSONObject(dataValue));
rawParameters.put("to", instanceIdToken);
return new JSONObject(rawParameters).toString().getBytes();
};
public String getBodyContentType()
{
return "application/json; charset=utf-8";
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> headers = new HashMap<String, String>();
headers.put("Authorization", "key="+YOUR_LEGACY_SERVER_KEY_FROM_FIREBASE_CONSOLE);
headers.put("Content-Type","application/json");
return headers;
}
};
Volley.newRequestQueue(activity).add(myReq);
}
Note
If you want to send message to topics so you can change parameter instanceIdToken to something like /topics/topicName.
For groups implementation is the same but you just need to take care of parameters. checkout Firebase documentation and you can pass those parameters.
let me know if you face any issue.

POST requests to Flask from Unity result in `null` values

After getting this demo server working I am able return GET requests from it to Unity, but when I would try to send data from Unity to the local server using POST requests it would only show null values added into the server. This is the code I was using in Unity:
IEnumerator Upload()
{
WWWForm form = new WWWForm();
form.AddField("charge","+4/3");
form.AddField("name", "doubletop");
using (UnityWebRequest www = UnityWebRequest.Post("http://localhost:5000/quarks/", form))
{
yield return www.SendWebRequest();
if (www.isNetworkError || www.isHttpError)
{
Debug.Log(www.error);
}
else
{
Debug.Log("Form upload complete!");
}
}
}
I would get "Form upload complete!" in the console, and GET requests would work, but those null values kept coming.
I modified my Upload() method to the PostRequest() in this example, and now it works!
Here's the full code:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
public class HTTP : MonoBehaviour
{
void Start()
{
// A correct website page.
StartCoroutine(GetRequest("localhost:5000/quarks"));
PostData();
StartCoroutine(GetRequest("localhost:5000/quarks"));
// A non-existing page.
//StartCoroutine(GetRequest("https://error.html"));
}
IEnumerator GetRequest(string uri)
{
using (UnityWebRequest webRequest = UnityWebRequest.Get(uri))
{
// Request and wait for the desired page.
yield return webRequest.SendWebRequest();
string[] pages = uri.Split('/');
int page = pages.Length - 1;
if (webRequest.isNetworkError)
{
Debug.Log(pages[page] + ": Error: " + webRequest.error);
}
else
{
Debug.Log(pages[page] + ":\nReceived: " + webRequest.downloadHandler.text);
}
}
}
[Serializable]
public class Quark
{
public string name;
public string charge;
}
public void PostData()
{
Quark gamer = new Quark();
gamer.name = "doublebottom";
gamer.charge = "4/3";
string json = JsonUtility.ToJson(gamer);
StartCoroutine(PostRequest("http://localhost:5000/quarks", json));
}
IEnumerator PostRequest(string url, string json)
{
var uwr = new UnityWebRequest(url, "POST");
byte[] jsonToSend = new System.Text.UTF8Encoding().GetBytes(json);
uwr.uploadHandler = (UploadHandler)new UploadHandlerRaw(jsonToSend);
uwr.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
uwr.SetRequestHeader("Content-Type", "application/json");
//Send the request then wait here until it returns
yield return uwr.SendWebRequest();
if (uwr.isNetworkError)
{
Debug.Log("Error While Sending: " + uwr.error);
}
else
{
Debug.Log("Received: " + uwr.downloadHandler.text);
}
}
}

Encountered Invalid redirect. Missing location header. Unity issue

I am using Unity WWWForm to post licence validation request to a URL.
The code is working in Unity 5.6, but it is not working in Unity 2017.3.1f1. I also tried this in Unity 2018. It didn't work.
This is the error message:
Encountered invalid redirect (missing Location header?)
This is the code I am using.
void Awake() {
Instance = this;
WWWForm form = new WWWForm ();
mainHeader = form.headers;
mainHeader ["Authorization"] = "Basic " + System.Convert.ToBase64String (System.Text.Encoding.ASCII.GetBytes ("dummyId:#dummyPassword"));
}
public void HitForLicence(){
string jsonData = JsonUtility.ToJson (new LicenseData {
LICENCE_KEY="kwsnfdksfksnf",
MACHINE_IP="192.168.1.1"
});
Debug.Log (jsonData);
byte[ ] postData = System.Text.Encoding.ASCII.GetBytes (jsonData);
//headers ["Authorization"] = "Basic " + System.Convert.ToBase64String (System.Text.Encoding.ASCII.GetBytes ("unity:#piun!ty"));
if (mainHeader.ContainsKey ("Content-Type")) {
mainHeader ["Content-Type"] = "application/json";
} else {
mainHeader.Add ("Content-Type", "application/json");
}
WWW www = new WWW (LicenseURL, postData, mainHeader);
StartCoroutine (CheckForLicense (www));
}
public IEnumerator CheckForLicense (WWW www)
{
Debug.Log("Check For License..");
yield return www;
//if (www.isDone ) {
if (www.error != null) {
ClearKeyBox ();
print (www.error);
}
else {
print (www.text);
jsonNode = SimpleJSON.JSON.Parse(www.text);
print ("MSG "+ jsonNode["MSG"].ToString());
}
//}
if (jsonNode != null && jsonNode["MSG"].Equals(ValidStr)) {
HandleTextFile.WriteString(_SystemMACAddress+"-"+keyEntered);
// Next screen
} else {
ClearKeyBox ();
}
}
Anyone faced this before? Please help.
I had some problems with WWW under Unity 2018, too.
Currently I am using UnityWebRequest - I think WWW is obsolete, but I can not find any reference to this.
Basically change your code like this:
...
UnityWebRequest www = UnityWebRequest.Post(LicenseURL, form)
StartCoroutine (CheckForLicense (www));
}
public IEnumerator CheckForLicense (WWW www)
{
Debug.Log("Check For License..");
...
I think you have to set the head via SetRequestHeader and put your post data into the WWWForm...
I am not able to check it right now... Hopefully it helps!
Like dome12b said, change to UnityWebRequest.
Additionaly however, set chunked transfer to false. I had the same problem and it was driving me nuts. Chunked transfer fixed it all.
UnityWebRequest www = UnityWebRequest.Post(LicenseURL, form)
www.chunkedTransfer = false;
This finally solved my problem.
public void Post(string url)
{
ServicePointManager.ServerCertificateValidationCallback =
delegate(object s, X509Certificate certificate,
X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
return true;
};
string jsonData = JsonUtility.ToJson (new LicenseData {
LICENCE_KEY="kwsnfdksfksnf",
MACHINE_IP="192.168.1.1"
});
Debug.Log (jsonData);
byte[ ] postData = System.Text.Encoding.ASCII.GetBytes (jsonData);
if (mainHeader.ContainsKey ("Content-Type")) {
mainHeader ["Content-Type"] = "application/json";
} else {
mainHeader.Add ("Content-Type", "application/json");
}
StartCoroutine (HttpRequest(postData, url));
}
IEnumerator HttpRequest(byte[] postData, string url)
{
var httpWebRequest = (HttpWebRequest)WebRequest.Create(url);
httpWebRequest.ContentType = "application/json";
httpWebRequest.Method = "POST";
httpWebRequest.AllowAutoRedirect = false;
using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
string jsonData = JsonUtility.ToJson(new LicenseData
{
LICENCE_KEY = "kwsnfdksfksnf",
MACHINE_IP = "192.168.1.1"
});
streamWriter.Write(jsonData);
streamWriter.Flush();
streamWriter.Close();
}
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
var result = streamReader.ReadToEnd();
print(result);
}
yield return null;
}

google cloud print from android without dialog

Can someone tell me if it is possible to silently print using google cloud print from an android device?
The goal is that my app grabs a file from a URL or from the SD card and then sends it to a specific printer - all without interaction from anyone looking at the screen or touching anything. It will actually be triggered by a barcode scan on a blue tooth connected device.
Thanks
Well, it is possible but I don't know why there's not too much information about it in the documentation...
The tricky part is connecting to the google cloud print API using only the android device (with no third party servers as the documentation explains here: https://developers.google.com/cloud-print/docs/appDevGuide ), so that's what I'm going to explain.
First, you have to include in your app the Google sign-in API, I recommend firebase API https://firebase.google.com/docs/auth/android/google-signin
Then you have to go to your Google API console: https://console.developers.google.com in the menu, go to Credentials scroll to OAuth 2.0 client IDs select Web client (auto created by Google Service) and save into your project the Client ID and Client secret keys... In my project, I saved them as "gg_client_web_id" and "gg_client_web_secret" as you will see below in the code.
Next, I'm going to paste all the code and then I'll explain it:
public class MainActivity extends AppCompatActivity
implements GoogleApiClient.OnConnectionFailedListener {
private GoogleApiClient mGoogleApiClient;
private FirebaseAuth mAuth;
private FirebaseAuth.AuthStateListener mAuthListener;
private static final int REQUEST_SINGIN = 1;
private TextView txt;
public static final String TAG = "mysupertag";
public static final String URLBASE = "https://www.google.com/cloudprint/";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txt = (TextView) findViewById(R.id.txt);
mAuth = FirebaseAuth.getInstance();
// Configure Google Sign In
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(getString(R.string.gg_client_web_id))
.requestEmail()
.requestServerAuthCode(getString(R.string.gg_client_web_id))
.requestScopes(new Scope("https://www.googleapis.com/auth/cloudprint"))
.build();
mGoogleApiClient = new GoogleApiClient.Builder(this)
.enableAutoManage(this /* FragmentActivity */, this /* OnConnectionFailedListener */)
.addApi(Auth.GOOGLE_SIGN_IN_API, gso)
.build();
findViewById(R.id.sign_in_button).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
signIn();
}
});
mAuthListener = new FirebaseAuth.AuthStateListener() {
#Override
public void onAuthStateChanged(#NonNull FirebaseAuth firebaseAuth) {
FirebaseUser user = firebaseAuth.getCurrentUser();
if (user != null) {
// User is signed in
Log.d(TAG, "onAuthStateChanged:signed_in:" + user.getUid());
} else {
// User is signed out
Log.d(TAG, "onAuthStateChanged:signed_out");
}
// ...
}
};
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
Log.d(TAG, "error connecting: " + connectionResult.getErrorMessage());
Toast.makeText(this, "error CONN", Toast.LENGTH_LONG).show();
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// Result returned from launching the Intent from GoogleSignInApi.getSignInIntent(...);
if (requestCode == REQUEST_SINGIN) {
GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
if (result.isSuccess()) {
// Google Sign In was successful, authenticate with Firebase
GoogleSignInAccount account = result.getSignInAccount();
firebaseAuthWithGoogle(account);
} else {
// Google Sign In failed, update UI appropriately
// ...
Toast.makeText(this, "error ", Toast.LENGTH_LONG).show();
}
}
}
private void signIn() {
Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);
startActivityForResult(signInIntent, REQUEST_SINGIN);
}
#Override
public void onStart() {
super.onStart();
mAuth.addAuthStateListener(mAuthListener);
}
#Override
public void onStop() {
super.onStop();
if (mAuthListener != null) {
mAuth.removeAuthStateListener(mAuthListener);
}
}
private void firebaseAuthWithGoogle(final GoogleSignInAccount acct) {
Log.d(TAG, "firebaseAuthWithGoogle:" + acct.getId());
AuthCredential credential = GoogleAuthProvider.getCredential(acct.getIdToken(), null);
mAuth.signInWithCredential(credential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
Log.d(TAG, "signInWithCredential:onComplete:" + task.isSuccessful());
// If sign in fails, display a message to the user. If sign in succeeds
// the auth state listener will be notified and logic to handle the
// signed in user can be handled in the listener.
FirebaseUser user = task.getResult().getUser();
txt.setText(user.getDisplayName() + "\n" + user.getEmail());//todo
if (!task.isSuccessful()) {
Log.w(TAG, "signInWithCredential", task.getException());
Toast.makeText(MainActivity.this, "Authentication failed.",
Toast.LENGTH_SHORT).show();
}
getAccess(acct.getServerAuthCode());
}
});
}
private void getPrinters(String token) {
Log.d(TAG, "TOKEN: " + token);
String url = URLBASE + "search";
Ion.with(this)
.load("GET", url)
.addHeader("Authorization", "Bearer " + token)
.asString()
.withResponse()
.setCallback(new FutureCallback<Response<String>>() {
#Override
public void onCompleted(Exception e, Response<String> result) {
Log.d(TAG, "finished " + result.getHeaders().code() + ": " +
result.getResult());
if (e == null) {
Log.d(TAG, "nice");
} else {
Log.d(TAG, "error");
}
}
});
}
private void getAccess(String code) {
String url = "https://www.googleapis.com/oauth2/v4/token";
Ion.with(this)
.load("POST", url)
.setBodyParameter("client_id", getString(R.string.gg_client_web_id))
.setBodyParameter("client_secret", getString(R.string.gg_client_web_secret))
.setBodyParameter("code", code)
.setBodyParameter("grant_type", "authorization_code")
.asString()
.withResponse()
.setCallback(new FutureCallback<Response<String>>() {
#Override
public void onCompleted(Exception e, Response<String> result) {
Log.d(TAG, "result: " + result.getResult());
if (e == null) {
try {
JSONObject json = new JSONObject(result.getResult());
getPrinters(json.getString("access_token"));
} catch (JSONException e1) {
e1.printStackTrace();
}
} else {
Log.d(TAG, "error");
}
}
});
}}
As you can see, in the onCreate the important part is creating the GoogleSignInOptions WITH the google cloud print scope AND calling the requestIdToken/requestServerAuthCode methods.
Then in the firebaseAuthWithGoogle method call the getAccess method in order to get the OAuth access token, for making all requests I'm using Ion library: https://github.com/koush/ion
Next with the access_token you can now do requests to the google cloud print API, in this case I call the getPrinters method, in this method I call the "search" method (from google cloud print API) to get all the printers associated to the google account that has signed in.. (to associate a printer to a google account visit this: https://support.google.com/cloudprint/answer/1686197?hl=en&p=mgmt_classic ) Note the .addHeader("Authorization", "Bearer " + token), this is the important part of the request, the "token" var is the access_token, you NEED add this Authorization header in order to use the API and don't forget to refresh when it expires, as explained here : https://developers.google.com/identity/protocols/OAuth2ForDevices in the "Using a refresh token" part.
And that's it, you can now print something sending a POST request to the "submit" method of the google cloud print API, I recommend to go here: https://developers.google.com/cloud-print/docs/appInterfaces and see all the methods available and how to use them (wich parameters send to them, etc). Of course in that link explains the "submit" method too.
EDIT:
EXAMPLE OF HOW TO SEND A REQUEST TO "/submit" FOR PRINTING USING ION LIBRARY AND MJSON LIBRARY (https://bolerio.github.io/mjson/) THE MJSON IS FOR CREATING A JSON OBJECT, YOU CAN CREATE IT THE WAY YOU PREFER
private void printPdf(String pdfPath, String printerId) {
String url = URLBASE + "submit";
Ion.with(this)
.load("POST", url)
.addHeader("Authorization", "Bearer " + YOUR_ACCESS_TOKEN)
.setMultipartParameter("printerid", printerId)
.setMultipartParameter("title", "print test")
.setMultipartParameter("ticket", getTicket())
.setMultipartFile("content", "application/pdf", new File(pdfPath))
.asString()
.withResponse()
.setCallback(new FutureCallback<Response<String>>() {
#Override
public void onCompleted(Exception e, Response<String> result) {
if (e == null) {
Log.d(TAG, "PRINTTT CODE: " + result.getHeaders().code() +
", RESPONSE: " + result.getResult());
Json j = Json.read(result.getResult());
if (j.at("success").asBoolean()) {
Toast.makeText(MainActivity.this, "Success", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(MainActivity.this, "ERROR", Toast.LENGTH_LONG).show();
}
} else {
Toast.makeText(MainActivity.this, "ERROR", Toast.LENGTH_LONG).show();
Log.d(TAG, e.toString());
}
}
});
}
private String getTicket() {
Json ticket = Json.object();
Json print = Json.object();
ticket.set("version", "1.0");
print.set("vendor_ticket_item", Json.array());
print.set("color", Json.object("type", "STANDARD_MONOCHROME"));
print.set("copies", Json.object("copies", 1));
ticket.set("print", print);
return ticket.toString();
}
Yes, You can achieve silent print using this REST API(https://www.google.com/cloudprint/submit) ,I have done it using WCF Service.
you need to download contents from url as base64 content, then add
contentType=dataUrl
in the request.
Here is the code..
postData = "printerid=" + PrinterId;
postData += "&title=" + JobTitle;
postData += "&ticket=" + ticket;
postData += "&content=data:" + documentContent.ContentType + ";base64," + documentContent.Base64Content;
postData += "&contentType=dataUrl";
postData += "&tag=test";
Then , please make a request to submit REST API in this way.
var request = (HttpWebRequest)WebRequest.Create("https://www.google.com/cloudprint/submit");
var data = Encoding.ASCII.GetBytes(postData);
request.Headers.Add("Authorization: Bearer " + Token);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = data.Length;
request.UseDefaultCredentials = true;
using (var stream = request.GetRequestStream())
{
stream.Write(data, 0, data.Length);
}
var response = (HttpWebResponse)request.GetResponse();
string responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
JavaScriptSerializer json_serializer = new JavaScriptSerializer();
PrintJobResponse printInfo = json_serializer.Deserialize<PrintJobResponse>(responseString);
return printInfo;
Thanks.
For anybody reading this now, after a lot of searching around I have found it is a lot easier and faster to set up to just use Zapier to catch a hook and print to google cloud print (from cordova at least, i can't speak for native apps)