Robotium testcase - android

I have a Robotium test case and It should be like
UI Application starts uploading data to server
User swaps to some other application on the device
uploading operation is running at the background
user comes to the main UI application
How to keep track of uploading the data at background? can we use multithreading for this?
try {
mSolo.clickOnMenuItem("UPLOAD");
mSolo.sleep(1000);
Instrumentation inst = new Instrumentation();
inst.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
mSolo.waitForActivity(Settings.ACTION_APPLICATION_SETTINGS);
mSolo.goBack();
mSolo.assertCurrentActivity("main",
UIActivity.class);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Is this code correct? If not suggest me a modification or correct code.
Help is always appreciated,
Thanks

You cannot interact with other applications unless you signed the third party application with your own key (see black box testing).
But what you can is pressing Home, Back and starting Intents. The following code is untested but hopefully gives you an idea:
try {
mSolo.clickOnMenuItem("UPLOAD"); // start upload
mSolo.sleep(1000);
mSolo.goBack(); // leave app
...
Intent intent = new Intent("com.company.another.app.SomeActivity");
startActivity(inent); // start another app
...
// option one: get app context and use it for access on preferences, etc.
Context context = this.getInstrumentation().getTargetContext().getApplicationContext();
// option two: wait for logs that you write while uploading
solo.waitForLogMessage("Upload completed");
...
Intent intent = new Intent("com.myapp.MyMainUIActivity");
startActivity(inent); // start own Main Activity again
...
} catch (Exception e) {
e.printStackTrace();
}
So you could use log messages, preferences or any other methods of your app in order to follow up the upload progress.

You cannot leave your application and run it again with Instrumentation. This part is not correct:
Instrumentation inst = new Instrumentation();
inst.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
Why do you create new instrumentation? You can simply run:
getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
by the way, solo.goBack() just does it, so it doesn't make sense to call it with instrumentation. I would simply rewrite it to:
try {
mSolo.clickOnMenuItem("UPLOAD");
mSolo.sleep(1000);
mSolo.goBack();
assertTrue(mSolo.waitForActivity(Settings.ACTION_APPLICATION_SETTINGS));
mSolo.goBack();
mSolo.assertCurrentActivity("main", UIActivity.class);
} catch (Exception e) {
e.printStackTrace();
}

Related

How do I programmatically dismiss a crash dialog?

I'm building a DPC (Device Policy Controller), and one of the issues I'm seeing is that while the Play Store and Play Services are being updated, the Google Contact Sync service crashes -- leaving the typical crash dialog on the screen. Since part of the idea of the initial set up process is to have as little user interaction as possible, how can I dismiss this dialog programmatically (since I seem to be pretty much guaranteed that this will happen)?
I've tried dismissing system dialogs...
ctx.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
... but that doesn't seem to do the trick.
Since this is a DPC, anything that requires device ownership/administration is fine.
edit: Usually I have no UI on screen at the time, so if one is necessary please do mention it. Also, preferably the solution should work on at least 6.0+, if not 4.0+.
Try to do it onWindowsFocusChanged method like this for example :
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (!hasFocus) {
Intent ctx= new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
sendBroadcast(ctx);
}
}
I'm not sure about app crash Dialog but maybe it'll help you
AppErrorDialog can be dismissed by broadcasting ACTION_CLOSE_SYSTEM_DIALOGS if Android version is N.
ctx.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
However, AppErrorDialog won't be displayed if phone is locked.
public boolean canShowErrorDialogs() {
return mShowDialogs && !mSleeping && !mShuttingDown
&& mLockScreenShown != LOCK_SCREEN_SHOWN;
} // ActivityManagerService
Please try this code.
try {
Class ActivityManagerNative = Class.forName("android.app.ActivityManagerNative");
Class IActivityManager = Class.forName("android.app.IActivityManager");
Method getDefault = ActivityManagerNative.getMethod("getDefault", null);
Object am = IActivityManager.cast(getDefault.invoke(ActivityManagerNative, null));
Method closeSystemDialogs = am.getClass().getMethod("closeSystemDialogs", String.class);
closeSystemDialogs.invoke(am, "DPC close");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}

Injecting key events from android service

I'm making a custom navigation bar for android. I want to inject KEYCODE_BACK event from any part of application whether I'm on menu, home screen or any application. Basically, any part of android. How can I do that using services. I found one piece of code but it doesn't work coz I have to install application as system.
new Thread() { // requires to use INJECT_EVENTS permission in android
#Override
public void run() {
try {
Instrumentation inst = new Instrumentation();
inst.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
} catch (Exception e) {
Log.e("Exception when sendKeyDownUpSync", e.toString());
}
}
}.start();
For this to work, I use uses-permission android:name="android.permission.INJECT_EVENTS" but it throws error saying INJECTING INTO ANOTHER APPLICATION REQUIRES INJECT_EVENT PERMISSION.
Is there anyway to implement back button through service.
Thanks

Properly running code in android main thread

So started programming android apps, mostly open source and tutorials to learn the language.
I have a problem now where if a user has signed in once, that he never has to sign in again. I used SharedPref for this after reading alot of answers on here, but the app is jumping frames due to this..
heres my code.
SharedPreferences prefs = getPreferences(Login.this);
prefs = PreferenceManager.getDefaultSharedPreferences(Login.this);
String remUsername = prefs.getString("username", null);
String remPassword = prefs.getString("password", null);
if (remUsername != null && remPassword != null)
{
String result = null;
try {
result = imService.authenticateUser(
remUsername.toString(),
remPassword.toString());
} catch (UnsupportedEncodingException e) {
//TODO Auto-generated catch block
e.printStackTrace();
}
Intent i = new Intent(Login.this, FriendList.class);
startActivity(i);
Login.this.finish();
}
The jumping frames are probably due to a resource-intensive operation (such as an HTTP request) being executed by the main thread. You must perform such operations in a separate thread, while leaving only UI and non-intensive operations for the main thread (that's why it is called the "UI" thread).
Read the basics here. Then you can implement it using an AsyncTask, for example. However for simplicity it is suggested to use a specialized library such as Volley or android-async-http.

How to join an active session running on a Chromcast device

I have to Users (User A and B) and one Chromecast device (C1).
User B starts a stream on C1.
User A connects to C1
Now User A should be able to control the stream running on C1. But every time I want to start a session the running stream on C1 is shut down and the receiver app is restarting.
Is there a way to join an active session? Or is that a job which has to be done by the web app running on the Chromecast device?
EDIT:
my sender app is a native Android app
Thanks!
You should have a look to the TicTacToe application. I think it does exactly that where 2 players can join the same game :
https://github.com/googlecast/cast-android-tictactoe
Hope this helps.
JN
What sort of sender are you using? Is it a native app (i.e. using Android or iOs SDK on a mobile device) or the sender is a chrome app?
On the receiver, you create a Receiver object and a ChannelHandler. You use the receiver to generate a ChannelFactory which you then pass to the ChannelHandler. The ChannelHandler now handles the creation of channels on the receiver. You will want to add an EventListener to the handler to listen to messages. Based on those messages you can do various things.
receiver = new cast.receiver.Receiver(YOUR_APP_ID, [YOUR_PROTOCOL], "", 5);
var dashHandler = new cast.receiver.ChannelHandler(YOUR_PROTOCOL);
dashHandler.addChannelFactory(receiver.createChannelFactory(YOUR_PROTOCOL));
dashHandler.addEventListener(cast.receiver.Channel.EventType.MESSAGE, onMessage.bind(this));
receiver.start();
...
onMessage = function (e) {
var message = e.message;
switch (message.type) {
...
}
}
On the sender, after a session is created you will want to send a check status message to the receiver to see if there are already channels attached. You can do this via your MessageStream and your receiver needs to respond in such a way that the MessageStream gets its status updated. You check that status to see if there are channels. If there are you can start listening to updates for your receiver. If not you can send a load event to the receiver to start your activity.
MediaProtocolCommand cmd = mMessageStream.requestStatus();
cmd.setListener(new MediaProtocolCommand.Listener() {
#Override
public void onCompleted(MediaProtocolCommand mPCommand) {
if (mMessageStream.getState() == 'channelsExist') {
//Start New Activity
} else {
//Join Existing Activity
}
#Override
public void onCancelled(MediaProtocolCommand mPCommand) {
}
});
This is kind of a vague response, but it could be more specific if I knew what you were trying to do. My app is using Google's RAMP protocol to play videos so my MessageStream and all it's messages are already defined. If you're doing something different, you need to create your own MessageStream.
Sorry for the late answer, but I figured it out by myself: It wasn't such complicated at all
I started the an Application like this
try {
mSession.startSession(applicationName,applicationArgs);
} catch (IllegalStateException e) {
Log.e(getClass().getSimpleName(), e.getMessage(), e);
} catch (IOException e) {
Log.e(getClass().getSimpleName(), e.getMessage(), e);
}
But it seems, that the MimeData applicationArgs is not needed at all. By removing the arguments and starting the session like below it works really fine!
try {
mSession.startSession(applicationName);
} catch (IllegalStateException e) {
Log.e(getClass().getSimpleName(), e.getMessage(), e);
} catch (IOException e) {
Log.e(getClass().getSimpleName(), e.getMessage(), e);
}
I hope this works for you too!

How to target a running app with a KeyEvent?

my goal is to target with a keyevent a specific application running either in foreground or background from a background service.
I tried many solutions, but have not yet managed to do it.
The few solutions tried (all from a background running service):
With a broadcast, I tried to target the first application (for example the phone app) that would manage the key event
KeyEvent lKey1Up = new KeyEvent(KeyEvent.ACTION_UP,KeyEvent.KEYCODE_ENDCALL);
KeyEvent lKey1Dwn = new KeyEvent(KeyEvent.ACTION_DOWN,KeyEvent.KEYCODE_ENDCALL);
Intent lKey1UpIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
Intent lKey1DwnIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
lKey1UpIntent.putExtra(Intent.EXTRA_KEY_EVENT, lKey1Up);
lKey1DwnIntent.putExtra(Intent.EXTRA_KEY_EVENT, lKey1Dwn );
sendOrderedBroadcast(lKey1UpIntent, null);
sendOrderedBroadcast(lKey1DwnIntent, null);
=> Nothing happens with my foreground phone app when the broadcast is performed while I am in a phone call state (OFFHOOK). Indeed, I was nearly sure this would not work since I have no way to specificely target the phone app.
With Instrumentation, I tried to target the application that has the focus :
Instrumentation lInst = new Instrumentation();
KeyEvent lKey1Up = new KeyEvent(KeyEvent.ACTION_UP,KeyEvent.KEYCODE_1);
KeyEvent lKey1Dwn = new KeyEvent(KeyEvent.ACTION_DOWN,KeyEvent.KEYCODE_1);
lInst.sendKeySync(lKey1Up);
lInst.sendKeySync(lKey1Dwn);
also tried with a single call to :
lInst.sendKeyDownUpSync(KeyEvent.KEYCODE_1)
=> Application crashes (also during phone call)
looks like I cannot use Instrumentation out of a testing purpose
Eventually, I thought about using
superDispatchKeyEvent(KeyEvent)
but since I don't know how to target a specific window from the targeted running application (and I have none in my service, indeed), I don't know how to use it at all.
And before anyone asks, I added the
android.permission.INJECT_EVENTS
android.permission.MODIY_PHONE_STATE
in my manifest in order to be sure all I do is "allowed".
Then... thanks first for reading until here, and now :
some of you know how I can manage to
do target a specific application with a
keystroke event from a service?
some of you know how to do the same with the phone application specificely?
Thanks in advance for your help.
my goal is to target with a keyevent a specific application running either in foreground or background from a background service.
This is not possible, because it is a security hole. Allowing application A to inject key events into application B raises all sorts of ugly malware possibilities.
You can use something like this, but need rooted device:
public static void inputKeyEvent(String keyCodeString) {
try {
int keyCode = Integer.parseInt(keyCodeString);
try {
Instrumentation m_Instrumentation = new Instrumentation();
m_Instrumentation.sendKeyDownUpSync(keyCode);
} catch (SecurityException e) {
try {
Process processKeyEvent = Runtime.getRuntime().exec("/system/xbin/su");
DataOutputStream os = new DataOutputStream(processKeyEvent.getOutputStream());
os.writeBytes("input keyevent " + keyCode + "\n");
} catch (IOException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
}

Categories

Resources