I have got a solution, it's actually a demo on how Win App Driver should work but I can't for the life of me get it to work. Using Win App Driver with selenium and appium web drivers (as mentioned at 5 minutes into this video). I have the solution as shown below and when I run my AddAlarm test I get the error ... "the target machine actively refused it 127.0.0.1:4723".
The full error message is at the bottom of this post.
My question is, what do I need to do to make the application we're testing "Alarm & Clock" actually launch on the url 127.0.0.1:4723 is there anything I have to do to make it available on that url / port? Also, how do I verify is "app" and "Microsoft.WindowsAlarms_8wekyb3d8bbwe!App" are correct in the setup?
//Class with my test "AddAlarm"
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium.Appium.Windows;
using System.Threading;
using System;
namespace AlarmClockTest
{
[TestClass]
public class ScenarioAlarm : AutoTest_SynTQ.UnitTestSession
{
private const string NewAlarmName = "Sample Test Alarm";
[TestMethod]
public void AlarmAdd()
{
// Navigate to New Alarm page
session.FindElementByAccessibilityId("AddAlarmButton").Click();
// Set alarm name
session.FindElementByAccessibilityId("AlarmNameTextBox").Clear();
session.FindElementByAccessibilityId("AlarmNameTextBox").SendKeys(NewAlarmName);
// Set alarm hour
WindowsElement hourSelector = session.FindElementByAccessibilityId("HourLoopingSelector");
hourSelector.FindElementByName("3").Click();
Assert.AreEqual("3", hourSelector.Text);
// Set alarm minute
WindowsElement minuteSelector = session.FindElementByAccessibilityId("MinuteLoopingSelector");
minuteSelector.FindElementByName("55").Click();
Assert.AreEqual("55", minuteSelector.Text);
// Save the newly configured alarm
session.FindElementByAccessibilityId("AlarmSaveButton").Click();
Thread.Sleep(TimeSpan.FromSeconds(3));
// Verify that a new alarm entry is created with the given hour, minute, and name
WindowsElement alarmEntry = session.FindElementByXPath($"//ListItem[starts-with(#Name, \"{NewAlarmName}\")]");
Assert.IsNotNull(alarmEntry);
Assert.IsTrue(alarmEntry.Text.Contains("3"));
Assert.IsTrue(alarmEntry.Text.Contains("55"));
Assert.IsTrue(alarmEntry.Text.Contains(NewAlarmName));
// Verify that the alarm is active and deactivate it
WindowsElement alarmEntryToggleSwitch = alarmEntry.FindElementByAccessibilityId("AlarmToggleSwitch") as WindowsElement;
Assert.IsTrue(alarmEntryToggleSwitch.Selected);
alarmEntryToggleSwitch.Click();
Assert.IsFalse(alarmEntryToggleSwitch.Selected);
}
[ClassInitialize]
public static void ClassInitialize(TestContext context)
{
Setup(context);
}
[ClassCleanup]
public static void ClassCleanup()
{
// Try to delete any alarm entry that may have been created
while (true)
{
try
{
var alarmEntry = session.FindElementByXPath($"//ListItem[starts-with(#Name, \"{NewAlarmName}\")]");
session.Mouse.ContextClick(alarmEntry.Coordinates);
session.FindElementByName("Delete").Click();
}
catch
{
break;
}
}
TearDown();
}
[TestInitialize]
public override void TestInit()
{
// Invoke base class test initialization to ensure that the app is in the main page
base.TestInit();
// Navigate to Alarm tab
session.FindElementByAccessibilityId("AlarmPivotItem").Click();
}
}
}
//Inherited class below
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium.Appium.Windows;
using OpenQA.Selenium.Remote;
using System;
using System.Threading;
namespace AutoTest_SynTQ
{
[TestClass]
public class UnitTestSession
{
private const string WindowsApplicationDriverUrl = "http://127.0.0.1:4723";
private const string AlarmClockAppId = "Microsoft.WindowsAlarms_8wekyb3d8bbwe!App";
protected static WindowsDriver<WindowsElement> session;
protected static RemoteTouchScreen touchScreen;
public static void Setup(TestContext context)
{
// Launch Alarms & Clock application if it is not yet launched
if (session == null || touchScreen == null)
{
TearDown();
// Create a new session to bring up the Alarms & Clock application
DesiredCapabilities appCapabilities = new DesiredCapabilities();
appCapabilities.SetCapability("app", AlarmClockAppId);
session = new WindowsDriver<WindowsElement>(new Uri(WindowsApplicationDriverUrl), appCapabilities);
Assert.IsNotNull(session);
Assert.IsNotNull(session.SessionId);
// Set implicit timeout to 1.5 seconds to make element search to retry every 500 ms for at most three times
session.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(1.5));
// Initialize touch screen object
touchScreen = new RemoteTouchScreen(session);
Assert.IsNotNull(touchScreen);
}
}
public static void TearDown()
{
// Cleanup RemoteTouchScreen object if initialized
touchScreen = null;
// Close the application and delete the session
if (session != null)
{
session.Quit();
session = null;
}
}
[TestInitialize]
public virtual void TestInit()
{
WindowsElement alarmTabElement = null;
// Attempt to go back to the main page in case Alarms & Clock app is started in EditAlarm view
try
{
alarmTabElement = session.FindElementByAccessibilityId("AlarmPivotItem");
}
catch
{
// Click back button if application is in a nested page such as New Alarm or New Timer
session.FindElementByAccessibilityId("Back").Click();
Thread.Sleep(TimeSpan.FromSeconds(1));
alarmTabElement = session.FindElementByAccessibilityId("AlarmPivotItem");
}
// Verify that the app is in the main view showing alarmTabElement
Assert.IsNotNull(alarmTabElement);
Assert.IsTrue(alarmTabElement.Displayed);
}
}
}
Test Name: AlarmAdd
Test FullName: AlarmClockTest.ScenarioAlarm.AlarmAdd
Test Source: C:\Users\ECombe.OPTIDOORS\Documents\SynTQCodedUITesting\AutoTest_SynTQ\SCN_Alarm.cs : line 30
Test Outcome: Failed
Test Duration: 0:00:00
Result StackTrace:
at OpenQA.Selenium.Remote.RemoteWebDriver.UnpackAndThrowOnError(Response errorResponse)
at OpenQA.Selenium.Remote.RemoteWebDriver.Execute(String driverCommandToExecute, Dictionary2 parameters)
at OpenQA.Selenium.Remote.RemoteWebDriver.StartSession(ICapabilities desiredCapabilities)
at OpenQA.Selenium.Remote.RemoteWebDriver..ctor(ICommandExecutor commandExecutor, ICapabilities desiredCapabilities)
at OpenQA.Selenium.Appium.AppiumDriver1..ctor(Uri remoteAddress, ICapabilities desiredCapabilities)
at OpenQA.Selenium.Appium.Windows.WindowsDriver1..ctor(Uri remoteAddress, DesiredCapabilities desiredCapabilities)
at AutoTest_SynTQ.UnitTestSession.Setup(TestContext context) in C:\Users\ECombe.OPTIDOORS\Documents\SynTQCodedUITesting\AutoTest_SynTQ\UnitTestSession.cs:line 28
at AlarmClockTest.ScenarioAlarm.ClassInitialize(TestContext context) in C:\Users\ECombe.OPTIDOORS\Documents\SynTQCodedUITesting\AutoTest_SynTQ\SCN_Alarm.cs:line 71
Result Message:
Class Initialization method AlarmClockTest.ScenarioAlarm.ClassInitialize threw exception. OpenQA.Selenium.WebDriverException: OpenQA.Selenium.WebDriverException: Unexpected error. System.Net.WebException: Unable to connect to the remote server ---> System.Net.Sockets.SocketException: No connection could be made because the target machine actively refused it 127.0.0.1:4723
at System.Net.Sockets.Socket.DoConnect(EndPoint endPointSnapshot, SocketAddress socketAddress)
at System.Net.ServicePoint.ConnectSocketInternal(Boolean connectFailure, Socket s4, Socket s6, Socket& socket, IPAddress& address, ConnectSocketState state, IAsyncResult asyncResult, Exception& exception)
--- End of inner exception stack trace ---
at OpenQA.Selenium.Appium.Service.AppiumCommandExecutor.Execute(Command commandToExecute)
at OpenQA.Selenium.Remote.RemoteWebDriver.Execute(String driverCommandToExecute, Dictionary2 parameters).
The answer in my case was to use Developer mode. The actual problem was winappdriver.exe closing immediately. More details here.
Related
I want to implement Play Install Referrer API and I found document and I read that but I have have some confusion. First I have implemented all code provided by google. But I want to know which type of url I need to create so that user can click on link and go to play store and install my app and then I get the referral detail..
I use this code:
InstallReferrerClient mReferrerClient;
mReferrerClient = newBuilder(this).build();
mReferrerClient.startConnection(this);
#Override
public void onInstallReferrerSetupFinished ( int responseCode){
switch (responseCode) {
case InstallReferrerClient.InstallReferrerResponse.OK:
// Connection established
/* ReferrerDetails response = null;
try {
response = mReferrerClient.getInstallReferrer();
response.getInstallReferrer();
response.getReferrerClickTimestampSeconds();
response.getInstallBeginTimestampSeconds();
} catch (RemoteException e) {
e.printStackTrace();
}*/
break;
case InstallReferrerClient.InstallReferrerResponse.FEATURE_NOT_SUPPORTED:
// API not available on the current Play Store app
break;
case InstallReferrerClient.InstallReferrerResponse.SERVICE_UNAVAILABLE:
// Connection could not be established
break;
}
}
#Override
public void onInstallReferrerServiceDisconnected () {
}
But which type of link I create so user go to play store and after install play store referral api give me data..
Sample url - "https://play.google.com/store/apps/details?id=com.dummy.app&referrer=referralCode%3D311566%26source%3DFacebook+App"
When using the Google Play Referrer API -
InstallReferrerClient mReferrerClient;
mReferrerClient = newBuilder(this).build();
mReferrerClient.startConnection(this);
#Override
public void onInstallReferrerSetupFinished ( int responseCode){
switch (responseCode) {
case InstallReferrerClient.InstallReferrerResponse.OK:
// Connection established
/* ReferrerDetails response = null;
try {
response = mReferrerClient.getInstallReferrer();
response.getInstallReferrer();
response.getReferrerClickTimestampSeconds();
response.getInstallBeginTimestampSeconds();
} catch (RemoteException e) {
e.printStackTrace();
}*/
// End the connection once you get the data
referrerClient.endConnection();
break;
case InstallReferrerClient.InstallReferrerResponse.FEATURE_NOT_SUPPORTED:
// API not available on the current Play Store app
break;
case InstallReferrerClient.InstallReferrerResponse.SERVICE_UNAVAILABLE:
// Connection could not be established
break;
}
}
#Override
public void onInstallReferrerServiceDisconnected () {
}
getInstallReferrer()
will return String 'referralCode=311566&source=Facebook App'
play install referral library i wants to describe this in simple wording, being a developer you wants to know about these elements how much time you app bundle take to install on the user devices from play store, and referral url , referral click time and many others elements , google make it easy for you know you have to use play install referral library for this purpose.
add this dependency
implementation 'com.android.installreferrer:installreferrer:1.1'
you can follow the guidelines from here:
play installer referral guidelines
declare this variable in any java activity
InstallReferrerClient referrerClient;
in on create method use this code below :
referrerClient = InstallReferrerClient.newBuilder(this).build();
referrerClient.startConnection(new InstallReferrerStateListener() {
#Override
public void onInstallReferrerSetupFinished(int responseCode) {
switch (responseCode) {
case InstallReferrerClient.InstallReferrerResponse.OK:
// Connection established.
break;
case InstallReferrerClient.InstallReferrerResponse.FEATURE_NOT_SUPPORTED:
// API not available on the current Play Store app.
break;
case InstallReferrerClient.InstallReferrerResponse.SERVICE_UNAVAILABLE:
// Connection couldn't be established.
break;
}
}
#Override
public void onInstallReferrerServiceDisconnected() {
// Try to restart the connection on the next request to
// Google Play by calling the startConnection() method.
}
});
inside onInstallReferrerSetupFinished stabled you can get these data easily,you code will be after that like this
ReferrerDetails response = null;
try {
response = referrerClient.getInstallReferrer();
} catch (RemoteException e) {
e.printStackTrace();
}
String referrerUrl = response.getInstallReferrer();
long referrerClickTime = response.getReferrerClickTimestampSeconds();
long appInstallTime = response.getInstallBeginTimestampSeconds();
boolean instantExperienceLaunched = response.getGooglePlayInstantParam();
whole code will be like this
referrerClient = InstallReferrerClient.newBuilder(this).build();
referrerClient.startConnection(new InstallReferrerStateListener() {
#Override
public void onInstallReferrerSetupFinished(int responseCode) {
switch (responseCode) {
case InstallReferrerClient.InstallReferrerResponse.OK:
// Connection established.
ReferrerDetails response = null;
try {
response = referrerClient.getInstallReferrer();
} catch (RemoteException e) {
e.printStackTrace();
}
String referrerUrl = response.getInstallReferrer();
long referrerClickTime = response.getReferrerClickTimestampSeconds();
long appInstallTime = response.getInstallBeginTimestampSeconds();
boolean instantExperienceLaunched = response.getGooglePlayInstantParam();
break;
case InstallReferrerClient.InstallReferrerResponse.FEATURE_NOT_SUPPORTED:
// API not available on the current Play Store app.
break;
case InstallReferrerClient.InstallReferrerResponse.SERVICE_UNAVAILABLE:
// Connection couldn't be established.
break;
}
}
#Override
public void onInstallReferrerServiceDisconnected() {
// Try to restart the connection on the next request to
// Google Play by calling the startConnection() method.
}
});
I want create a tcp server in c# and use it in universal app javascript based project, and I create the folowing code (Server):
//C# Windows Runtime Component
public sealed class Server
{
public Server()
{
Debug.WriteLine("Server...");
}
public async void Connection()
{
IPAddress ip = IPAddress.Parse("192.168.0.10");
TcpListener server = new TcpListener(ip, portNumber);
TcpClient client = default(TcpClient);
try
{
server.Start();
Debug.WriteLine("Server started ... " + ip.ToString());
}
catch (Exception e)
{
Debug.WriteLine(e.ToString());
}
while (true)
{
client = await server.AcceptTcpClientAsync();
byte[] recievedBuffer = new byte[100];
NetworkStream stream = client.GetStream();
stream.Read(recievedBuffer, 0, recievedBuffer.Length);
string msg = Encoding.UTF8.GetString(recievedBuffer, 0, recievedBuffer.Length);
Debug.WriteLine(msg);
}
}
}
//in HTML
<script>
console.log("test");
var server = new Server.Server();
server.connection();
console.log("msg");
</script>
I don't know why Debug.WriteLine and console.log method don't work, nothing are printed in output or in javascript console.
The Server code works with Android client, if the server is "Console App" project but in "Universal App Javscript" nothing append, I don't have warning or error.
So I don't know if I'm doing bad, because console.log and Debug.WriteLine don't work.
I have a solution that work with windows universal app, I remove Connection and add followings methods:
public async void StartServer()
{
try
{
var streamSocketListener = new Windows.Networking.Sockets.StreamSocketListener();
streamSocketListener.ConnectionReceived += this.StreamSocketListener_ConnectionReceived;
await streamSocketListener.BindEndpointAsync(new HostName("192.168.0.10"), PortNumber);
}
catch (Exception ex){}
}
private async void StreamSocketListener_ConnectionReceived(Windows.Networking.Sockets.StreamSocketListener sender, Windows.Networking.Sockets.StreamSocketListenerConnectionReceivedEventArgs args)
{
string request;
using (var streamReader = new StreamReader(args.Socket.InputStream.AsStreamForRead()))
{
request = await streamReader.ReadLineAsync();
}
sender.Dispose();
}
//in main.js just call this method
new Server.Server().startServer();
But I still don't know why Debug.WriteLine() in c# and console.log() in javascript don't work.
I have created, with JavaFX, a game on desktop that works fine (20000 Java lines. As it is a game, the Real Time constraint is important (response time of player's actions).
The final aim is to run this application with Android. I have almost finished to "transfer the Java code" from PC to Android, even if I have encountered some real time trouble. I think almost all of them are solved now.
For instance, I have minimized the CPU time (consumption) of Shape or Rectangle.intersect(node1, node2) calls that are used for detecting impacts between two mobiles. Thus, the real time has been divided by 3. Great!
For testing this Android version, I use Eclipse + Neon2, JavaFX, JavaFXports + gluon and my phone (Archos Diamond S).
But, for Android phones, I had a real time problem related to the sounds that are generated with MediaPlayer and NativeAudioSrvice.
Yet, I have followed this advice that suggests the synchronous mode:
javafxports how to call android native Media Player
1st question:
Does it exist an asynchronous mode with this Mediaplayer class?I think that would solve this latency problem?
In practice, I have tried the asynchronous solution ... without success: the real time problem due to the audio generation with MediaPlayer stays: an audio generation costs from 50 ms to 80 ms whereas the main cyclic processing runs each 110 ms. Each audio generation can interfer with the main processing execution.
And, in each periodic task (rate: 110 ms), I can play several sounds like that. And, in a trace, there was up to six sound activations that take (together) about 300 ms (against the 110 ms of the main cyclic task )
QUESTION:
How to improve the performance of NativeAudio class (especially, the method play() with its calls that create the real time problem: setDataSource(...), prepare() and start() )?
THE SOLUTION
The main processing must be a "synchronized" method to be sure that this complete processing will be run, without any audio interruption.
More, each complete processing for generating a sound is under a dedicated thread, defined with a Thread.MIN_PRIORITY priority.
Now, the main processing is run each 110 ms and, when it begins, it cannot be disturbed by any audio generation. The display is very "soft" (no more jerky moving).
There is just a minor problem: when an audio seDataSource(), a start() or a prepare() method has begun, it seems to be that the next main processing shall wait the end of the method before beginning (TBC)
I hope this solution could help another people. It is applicable in any case of audio generations with MediaPlayer.
JAVA code of the solution
The main processing is defined like that:
public static ***synchronized*** void mainProcessing() {
// the method handles the impacts, explosions, sounds, movings, ... , in other words almost the entiere game .. in a CRITICAL SECTION
}
/****************************************************/
In the NativeAudio class that implements "NativeAudioService":
#Override
public void play() {
if (bSon) {
Task<Void> taskSound = new Task<Void>() {
#Override
protected Void call() throws Exception {
generateSound();
return null;
}};
Thread threadSound = new Thread(taskSound);
threadSound.setPriority(Thread.MIN_PRIORITY);
threadSound.start();
}
}
/****************************************************/
private void generateSound() {
currentPosition = 0;
nbTask++;
noTask = nbTask;
try {
if (mediaPlayer != null) {
stop();
}
mediaPlayer = new MediaPlayer();
AssetFileDescriptor afd = FXActivity.getInstance().getAssets().openFd(audioFileName);
mediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
mediaPlayer.setAudioStreamType(AudioManager.STREAM_RING);
float floatLevel = (float) audioLevel;
mediaPlayer.setVolume(floatLevel, floatLevel);
mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mediaPlayer) {
if (nbCyclesAudio >= 1) {
mediaPlayer.start();
nbCyclesAudio--;
} else {
mediaPlayer.stop();
mediaPlayer.release(); // for freeing the resource - useful for the phone codec
mediaPlayer = null;
}
}
});
mediaPlayer.prepare();
mediaPlayer.start();
nbCyclesAudio--;
} catch (IOException e) {
}
}
I've changed a little bit the implementation you mentioned, given that you have a bunch of short audio files to play, and that you want a very short time to play them on demand. Basically I'll create the AssetFileDescriptor for all the files once, and also I'll use the same single MediaPlayer instance all the time.
The design follows the pattern of the Charm Down library, so you need to keep the package names below.
EDIT
After the OP's feedback, I've changed the implementation to have one MediaPlayer for each audio file, so you can play any of them at any time.
Source Packages/Java:
package: com.gluonhq.charm.down.plugins
AudioService interface
public interface AudioService {
void addAudioName(String audioName);
void play(String audioName, double volume);
void stop(String audioName);
void pause(String audioName);
void resume(String audioName);
void release();
}
AudioServiceFactory class
public class AudioServiceFactory extends DefaultServiceFactory<AudioService> {
public AudioServiceFactory() {
super(AudioService.class);
}
}
Android/Java Packages
package: com.gluonhq.charm.down.plugins.android
AndroidAudioService class
public class AndroidAudioService implements AudioService {
private final Map<String, MediaPlayer> playList;
private final Map<String, Integer> positionList;
public AndroidAudioService() {
playList = new HashMap<>();
positionList = new HashMap<>();
}
#Override
public void addAudioName(String audioName) {
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setOnCompletionListener(m -> pause(audioName)); // don't call stop, allows reuse
try {
mediaPlayer.setDataSource(FXActivity.getInstance().getAssets().openFd(audioName));
mediaPlayer.setOnPreparedListener(mp -> {
System.out.println("Adding audio resource " + audioName);
playList.put(audioName, mp);
positionList.put(audioName, 0);
});
mediaPlayer.prepareAsync();
} catch (IOException ex) {
System.out.println("Error retrieving audio resource " + audioName + " " + ex);
}
}
#Override
public void play(String audioName, double volume) {
MediaPlayer mp = playList.get(audioName);
if (mp != null) {
if (positionList.get(audioName) > 0) {
positionList.put(audioName, 0);
mp.pause();
mp.seekTo(0);
}
mp.start();
}
}
#Override
public void stop(String audioName) {
MediaPlayer mp = playList.get(audioName);
if (mp != null) {
mp.stop();
}
}
#Override
public void pause(String audioName) {
MediaPlayer mp = playList.get(audioName);
if (mp != null) {
mp.pause();
positionList.put(audioName, mp.getCurrentPosition());
}
}
#Override
public void resume(String audioName) {
MediaPlayer mp = playList.get(audioName);
if (mp != null) {
mp.start();
mp.seekTo(positionList.get(audioName));
}
}
#Override
public void release() {
for (MediaPlayer mp : playList.values()) {
if (mp != null) {
mp.stop();
mp.release();
}
}
}
}
Sample
I've added five short audio files (from here), and added five buttons to my main view:
#Override
public void start(Stage primaryStage) throws Exception {
Button play1 = new Button("p1");
Button play2 = new Button("p2");
Button play3 = new Button("p3");
Button play4 = new Button("p4");
Button play5 = new Button("p5");
HBox hBox = new HBox(10, play1, play2, play3, play4, play5);
hBox.setAlignment(Pos.CENTER);
Services.get(AudioService.class).ifPresent(audio -> {
audio.addAudioName("beep28.mp3");
audio.addAudioName("beep36.mp3");
audio.addAudioName("beep37.mp3");
audio.addAudioName("beep39.mp3");
audio.addAudioName("beep50.mp3");
play1.setOnAction(e -> audio.play("beep28.mp3", 5));
play2.setOnAction(e -> audio.play("beep36.mp3", 5));
play3.setOnAction(e -> audio.play("beep37.mp3", 5));
play4.setOnAction(e -> audio.play("beep39.mp3", 5));
play5.setOnAction(e -> audio.play("beep50.mp3", 5));
});
Scene scene = new Scene(new StackPane(hBox), Screen.getPrimary().getVisualBounds().getWidth(),
Screen.getPrimary().getVisualBounds().getHeight());
primaryStage.setScene(scene);
primaryStage.show();
}
#Override
public void stop() throws Exception {
Services.get(AudioService.class).ifPresent(AudioService::release);
}
The prepare step takes place when the app is launched and the service is instanced, so when playing later on any of the audio files, there won't be any delay.
I haven't checked if there could be any memory issues when adding several media players with big audio files, as that wasn't the initial scenario. Maybe a cache strategy will help in this case (see CacheService in Gluon Charm Down).
Our application should have the functionality to save Application files to Google Drive. Of course, using the local configured account.
From Android API i tried to figure out some clue. But android API with Xamarin implementation seems very tough for me.
I have installed Google Play Services- Drive from Xamarin Components but there are no examples listed from which we can refer the flow and functionality.
The basic steps (see the link below for full details):
Create GoogleApiClient with the Drive API and Scope
Try to connect (login) the GoogleApiClient
The first time you try to connect it will fail as the user has not selected a Google Account that should be used
Use StartResolutionForResult to handle this condition
When GoogleApiClient is connected
Request a Drive content (DriveContentsResult) to write the file contents to.
When the result is obtained, write data into the Drive content.
Set the metadata for the file
Create the Drive-based file with the Drive content
Note: This example assumes that you have Google Drive installed on your device/emulator and you have registered your app in Google's Developer API Console with the Google Drive API Enabled.
C# Example:
[Activity(Label = "DriveOpen", MainLauncher = true, Icon = "#mipmap/icon")]
public class MainActivity : Activity, GoogleApiClient.IConnectionCallbacks, IResultCallback, IDriveApiDriveContentsResult
{
const string TAG = "GDriveExample";
const int REQUEST_CODE_RESOLUTION = 3;
GoogleApiClient _googleApiClient;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.Main);
Button button = FindViewById<Button>(Resource.Id.myButton);
button.Click += delegate
{
if (_googleApiClient == null)
{
_googleApiClient = new GoogleApiClient.Builder(this)
.AddApi(DriveClass.API)
.AddScope(DriveClass.ScopeFile)
.AddConnectionCallbacks(this)
.AddOnConnectionFailedListener(onConnectionFailed)
.Build();
}
if (!_googleApiClient.IsConnected)
_googleApiClient.Connect();
};
}
protected void onConnectionFailed(ConnectionResult result)
{
Log.Info(TAG, "GoogleApiClient connection failed: " + result);
if (!result.HasResolution)
{
GoogleApiAvailability.Instance.GetErrorDialog(this, result.ErrorCode, 0).Show();
return;
}
try
{
result.StartResolutionForResult(this, REQUEST_CODE_RESOLUTION);
}
catch (IntentSender.SendIntentException e)
{
Log.Error(TAG, "Exception while starting resolution activity", e);
}
}
public void OnConnected(Bundle connectionHint)
{
Log.Info(TAG, "Client connected.");
DriveClass.DriveApi.NewDriveContents(_googleApiClient).SetResultCallback(this);
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_RESOLUTION)
{
switch (resultCode)
{
case Result.Ok:
_googleApiClient.Connect();
break;
case Result.Canceled:
Log.Error(TAG, "Unable to sign in, is app registered for Drive access in Google Dev Console?");
break;
case Result.FirstUser:
Log.Error(TAG, "Unable to sign in: RESULT_FIRST_USER");
break;
default:
Log.Error(TAG, "Should never be here: " + resultCode);
return;
}
}
}
void IResultCallback.OnResult(Java.Lang.Object result)
{
var contentResults = (result).JavaCast<IDriveApiDriveContentsResult>();
if (!contentResults.Status.IsSuccess) // handle the error
return;
Task.Run(() =>
{
var writer = new OutputStreamWriter(contentResults.DriveContents.OutputStream);
writer.Write("Stack Overflow");
writer.Close();
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.SetTitle("New Text File")
.SetMimeType("text/plain")
.Build();
DriveClass.DriveApi
.GetRootFolder(_googleApiClient)
.CreateFile(_googleApiClient, changeSet, contentResults.DriveContents);
});
}
public void OnConnectionSuspended(int cause)
{
throw new NotImplementedException();
}
public IDriveContents DriveContents
{
get
{
throw new NotImplementedException();
}
}
public Statuses Status
{
get
{
throw new NotImplementedException();
}
}
}
Ref: https://developers.google.com/drive/android/create-file
I am using PushStreamContent to keep a persistent connection to each client. Pushing short heartbeat messages to each client stream every 20 seconds works great with 100 clients, but at about 200 clients, the client first starts receiving it a few seconds delayed, then it doesn't show up at all.
My controller code is
// Based loosely on https://aspnetwebstack.codeplex.com/discussions/359056
// and http://blogs.msdn.com/b/henrikn/archive/2012/04/23/using-cookies-with-asp-net-web-api.aspx
public class LiveController : ApiController
{
public HttpResponseMessage Get(HttpRequestMessage request)
{
if (_timer == null)
{
// 20 second timer
_timer = new Timer(TimerCallback, this, 20000, 20000);
}
// Get '?clientid=xxx'
HttpResponseMessage response = request.CreateResponse();
var kvp = request.GetQueryNameValuePairs().Where(q => q.Key.ToLower() == "clientid").FirstOrDefault();
string clientId = kvp.Value;
HttpContext.Current.Response.ClientDisconnectedToken.Register(
delegate(object obj)
{
// Client has cleanly disconnected
var disconnectedClientId = (string)obj;
CloseStreamFor(disconnectedClientId);
}
, clientId);
response.Content = new PushStreamContent(
delegate(Stream stream, HttpContent content, TransportContext context)
{
SaveStreamFor(clientId, stream);
}
, "text/event-stream");
return response;
}
private static void CloseStreamFor(string clientId)
{
Stream oldStream;
_streams.TryRemove(clientId, out oldStream);
if (oldStream != null)
oldStream.Close();
}
private static void SaveStreamFor(string clientId, Stream stream)
{
_streams.TryAdd(clientId, stream);
}
private static void TimerCallback(object obj)
{
DateTime start = DateTime.Now;
// Disable timer
_timer.Change(Timeout.Infinite, Timeout.Infinite);
// Every 20 seconds, send a heartbeat to each client
var recipients = _streams.ToArray();
foreach (var kvp in recipients)
{
string clientId = kvp.Key;
var stream = kvp.Value;
try
{
// ***
// Adding this Trace statement and running in debugger caused
// heartbeats to be reliably flushed!
// ***
Trace.WriteLine(string.Format("** {0}: Timercallback: {1}", DateTime.Now.ToString("G"), clientId));
WriteHeartBeat(stream);
}
catch (Exception ex)
{
CloseStreamFor(clientId);
}
}
// Trace... (this trace statement had no effect)
_timer.Change(20000, 20000); // re-enable timer
}
private static void WriteHeartBeat(Stream stream)
{
WriteStream(stream, "event:heartbeat\ndata:-\n\n");
}
private static void WriteStream(Stream stream, string data)
{
byte[] arr = Encoding.ASCII.GetBytes(data);
stream.Write(arr, 0, arr.Length);
stream.Flush();
}
private static readonly ConcurrentDictionary<string, Stream> _streams = new ConcurrentDictionary<string, Stream>();
private static Timer _timer;
}
Could there be some ASP.NET or IIS setting that affects this? I am running on Windows Server 2008 R2.
UPDATE:
Heartbeats are reliably sent if 1) the Trace.WriteLine statement is added, 2) Visual Studio 2013 debugger is attached and debugging and capturing the Trace.WriteLines).
Both of these are necessary; if the Trace.WriteLine is removed, running under the debugger has no effect. And if the Trace.WriteLine is there but the program is not running under the debugger (instead SysInternals' DbgView is showing the trace messages), the heartbeats are unreliable.
UPDATE 2:
Two support incidents with Microsoft later, here are the conclusions:
1) The delays with 200 clients were resolved by using a business class Internet connection instead of a Home connection
2) whether the debugger is attached or not really doesn't make any difference;
3) The following two additions to web.config are required to ensure heartbeats are sent timely, and failed heartbeats due to client disconnecting "uncleanly" (e.g. by unplugging computer rather than normal closing of program which cleanly issues TCP RST) trigger a timely ClientDisconnected callback as well:
<httpRuntime executionTimeout="5" />
<serverRuntime appConcurrentRequestLimit="50000" uploadReadAheadSize="1" frequentHitThreshold="2147483647" />