Instrument Android App with startInstrumentation - android

I want to instrument my App with the Instrumentation Interface from inside the tested app.
I have a custom activity that every of my activities extend. In that I want to start the instrumentation to get information about the app:
public class BxActivity extends Activity {
#Override
protected void onResume() {
super.onResume();
...
boolean instru = startInstrumentation(new ComponentName(BxActivity.this, InstrumentationRobot.class), null, null);
Imho that should restart the app with the instrumentation code in the InstrumentationRobot.class. It is in the same Android project and package.
public class InstrumentationRobot extends Instrumentation {
#Override
public void onCreate(Bundle arguments) {
super.onCreate(arguments);
Log.v("InstrRobot", "Hello from Robot");
start();
}
I've added the instrumentation to the manifest like this:
<instrumentation
android:name="InstrumentationRobot" (class in the same pkg)
android:targetPackage="my.package.name" />
</application>
This is the correct manifest code for my instrumentation, so my little robot prints out "hello" to me.
Thanks, soeren

I've found the solution and updated my question with the correct code.
The mistake was in the manifest file.
I deleted the
<uses-library android:name="android.test.runner" />
tag and the instrumentation class name must not start with a dot, if in the instrumentation class is in the same package and .apk file as the app itself.
The instrumentation tag must also be a direct child of the manifest.

Related

Importing two Unity projects into Android Studio doesn't work as expected

I am currently training to make applications which use Unity to add some features (AR, VR, etc...). For now I've been working on Android, with Android Studio, and once I'll be done with I'll train on iOS.
My aim is simple: my MainActivity displays two buttons, each one calls a separate Unity project (exported from Unity as Google Android project) to launch its scene.
To do so, I imported those two Unity projects Scene1 and Scene2 as libraries and I call them by starting their activities (see the code below).
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void goToUnity1(View v){
Intent intent = new Intent(this, com.example.unityscene1.UnityPlayerActivity.class);
startActivity(intent);
}
public void goToUnity2(View v){
Intent intent = new Intent(this, com.example.unityscene2.UnityPlayerActivity.class);
startActivity(intent);
}
}
And its AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.multipleunity">
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.example.unityscene1.UnityPlayerActivity"></activity>
<activity android:name="com.example.unityscene2.UnityPlayerActivity"></activity>
</application>
The UnityPlayerActivity files in Scene1 and Scene2 are generated by Unity so they are similar (here are their onCreate method):
public class UnityPlayerActivity extends Activity {
protected UnityPlayer mUnityPlayer; // don't change the name of this variable; referenced from native code
// Setup activity layout
#Override protected void onCreate (Bundle savedInstanceState)
{
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
getWindow().setFormat(PixelFormat.RGBX_8888); // <--- This makes xperia play happy
mUnityPlayer = new UnityPlayer(this);
setContentView(mUnityPlayer);
mUnityPlayer.requestFocus();
}
...
}
The code compiles without any problem, the application launches and displays the MainActivity with its two buttons. When I click on the first button, it launches the first Unity scene as I expected. However, when I click on the second button it also launches the first Unity scene instead of the second one.
So I tried to understand why this happens and here is what I can tell so far:
I didn't put twice the same Unity project by mistake
Before transforming Scene2 into library it works well on its own
The activity com.example.unityscene2.UnityPlayerActivity is called when I click on the second button
UPDATE
After spending hours on this I'm sure the problem comes from the name of the ressources Unity delivers when I exported in Google Android project which are always the same (unity-classes.jar).
Here's what I read here:
The Android development tools merges the resources of a library project with the resources of the application project. In the case that a resource’s ID is defined several times, the tools select the resource from the application, or the library with highest priority, and discard the other resource.
Adding to that, if I have multiple libraries the priority matches the dependency order (the order they appear in the Gradle dependencies block).
So I tried to reverse the order of the dependencies, I put the second Unity project before the first one and as that was expected both buttons launch the second Scene of Unity.
Now I know that, is there a way to avoid this name conflict ?
I did not solve my problem, but I managed to find a way to avoid it: instead of importing two small Unity projects I decided to make a bigger one which includes two different scenes (one with Vuforia AR and the other with some UI text). Then the challenge was to find a way to call the right Unity scene when I click on the associated button I designed on Android.
Unity
On each scene I created an empty object (I called it SceneManagerObject) which will be attached the script GoToScene. In this script there will be the following methods:
private void ChangeScene(string sceneName)
{
SceneManager.LoadScene (sceneName);
}
public void ReceiveJavaMessage(string message)
{
if (message.Equals ("Scene1") || message.Equals ("Scene2"))
{
ChangeScene (message);
}
else
{
Debug.Log ("The scene name is incorrect");
}
}
The second method will be the one being called from the Java code.
Then I created a UI button on each scene which will call onClick method when clicked, its purpose is to be able to go back to Android without destroying the Unity activity so we can resume it after if I wanted to. For this, I created another Empty Object with the script GoBackToAndroid attached:
public class GoBackToAndroid : MonoBehaviour {
public string activityName; // contains the name of the activity I want to go when I quit this scene
public void onClick()
{
callJavaMethod();
}
private void callJavaMethod()
{
AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer);
AnroidJavaObject jo = jc.GetStatic<AndroidJavaObject>("currentActivity");
jo.Call("ReceiveUnityMessage", activityName); // Will call a method in Java with activityName as parameter
}
That's it for the Unity, after I exported the project as a Google Android project and I kept it warm for the next step.
Android Studio
After importing the Unity project as a library. I created three activities: MainActivity, SecondActivity and MultipleScenesUnityActivity:
public class MultipleScenesUnityActivity extends UnityPlayerActivity {
public static boolean unityIsRunning = false; // true if a Unity Activity is running on background
#Override protected void onDestroy()
{
unityIsRunning = false;
super.mUnityPlayer.quit();
super.onDestroy();
}
#Override protected void onResume()
{
Intent currentIntent = getIntent();
// If there's no Unity Activity inside the back stack
// (meaning it just had been created)
if (!unityIsRunning)
{
super.mUnityPlayer.UnitySendMessage("SceneManagerObject", "ReceiveJavaMessage", currentIntent.getStringExtra("sceneName");
}
unityIsRunning = true;
super.onResume();
super.mUnityPlayer.resume();
}
public void ReceiveUnityMessage (String messageFromUnity)
{
Intent intent;
if (messageFromUnity.equals("MainActivity"))
{
intent = new Intent(this, MainActivity.class);
startActivity(intent);
}
else if (messageFromUnity.equals("SecondActivity"))
{
intent = new Intent(this, SecondActivity.class);
startActivity(intent);
}
else
Log.d("Test", "The activity " + messageFromUnity + " doesn't exist");
}
}
The ReceiveUnityMessage is the method called when we leave the Unity activity and its purpose is to lead us to the activity we desire to go.
Then the MainActivity (the SecondActivity follows the same pattern):
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void goToActivity2(View view) // called when I click on a button
{
Intent intent = new Intent(this, SecondActivity.class);
startActivity(intent);
}
public void goToUnity(View view) // called when I click on another button
{
Intent intentUnity = new Intent(this, MultipleScenesUnityActivity.class);
if (unityIsRunning)
{
intentUnity.removeExtra("sceneName"); // We have to clean the extra before putting another one otherwise we'll get always the same Unity scene
/* If set in an Intent passed to Context.startActivity(), this flag will cause the launched activity to be brought
* to the front of its task's history stack if it is already running. */
intentUnity.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
/* We send a string to Unity which, when received, will change the scene.
* This ONLY works when the Unity activity is running on background. */
UnitySendMessage("SceneManagerObject", "ReceiveJavaMessage", "Scene1");
}
else
{
/* The intent gets a key with its String value. The value has to be the name of the Unity
* scene and is received in UnityActivity1 when the activity starts.*/
intentUnity.putExtra("sceneName", "Scene1");
}
startActivity(intentUnity);
}
}
So what I did is connecting everything by sending messages so I can manage the scene I'd like to launch and the same goes for the activities.
I'm sure there are a better ways to do so, I'm still a novice and I can be clumsy.. Anyway, I managed to avoid my original problem so if anyone faces the same issue this answer might be an interesting alternative.
One possible solution which i came across is dynamically loading multiple smaller unity apks inside a single apk . I have done some research and found out that it is possible to launch one apk from other without even actually installing the apk. This can be done by various methods and there are many library available for doing this to. Few of them are.
DexClassLoader
class loader that loads classes from .jar and .apk files containing a classes.dex entry. This can be used to execute code not installed as part of an application.
Grab-n-Run
Securely load code dynamically into your Android application from APK containers or JAR libraries translated to be executable by both the Dalvik Virtual Machine (DVM) and the Android Runtime (ART).

How to add analytics to an Android application with a library in its own process

I have an Android application which uses Google Analytics. As recommended by Google, it overrides android.app.Application and supplies the class name in its AndroidManifest.xml:
<application
android:name=".MyApplication">
My app relies on a library which contains a service which must run in its own process. Therefore, its AndroidManifest.xml has:
<application>
<service
android:name="org.myservice"
android:enabled="true"
android:exported="false"
android:process=":myservice">
When running, I found that a second, independent instance of MyApplication is launched in the library's process. This tries to set up analytics, etc. But the library never asked for this class and doesn't want it.
I tried to declare a different application class in the library's AndroidManifest.xml but this causes "Manifest merger failed" build errors.
How can I keep the library process from depending on MyApplication?
Because manifests are merged together, it is only possible to have a single class designated by android:name (in either one library or in the main app). One of these objects will be instantiated per process.
My solution is for the Main application class to detect whether the process name matches the APPLICATION_ID. This will only be true for the main process and not for any other.
public class MyApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
// The print library has its own process but will use this class.
// Only set up analytics if we are in the main process.
if (isMainProcess()) {
// Once-per-app code here
}
}
public boolean isMainProcess() {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (RunningAppProcessInfo processInfo : manager.getRunningAppProcesses()) {
if (processInfo.pid == android.os.Process.myPid()) {
return TextUtils.equals(BuildConfig.APPLICATION_ID,
processInfo.processName);
}
}
return false;
}
}

NameNotFoundException at ActivityUnitTestCase with ActionBarActivity

I am trying to write some tests for an existing application. I wanted to supply a test Application class to the tests and I followed the example here, since I am also using Dagger for DI.
However, if the activity under test is an ActionBarActivity, I get the following exception:
java.lang.IllegalArgumentException: android.content.pm.PackageManager$NameNotFoundException: ComponentInfo{mypackage.view.activity/mypackage.view.activity.MyActivity}
at android.support.v4.app.NavUtils.getParentActivityName(NavUtils.java:282)
at android.support.v7.app.ActionBarActivityDelegate.onCreate(ActionBarActivityDelegate.java:116)
at android.support.v7.app.ActionBarActivityDelegateICS.onCreate(ActionBarActivityDelegateICS.java:57)
at android.support.v7.app.ActionBarActivity.onCreate(ActionBarActivity.java:98)
at mypackage.view.activity.MyActivity.onCreate(MyActivity.java:68)
at android.app.Activity.performCreate(Activity.java:5231)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
at android.test.ActivityUnitTestCase.startActivity(ActivityUnitTestCase.java:158)
at mypackage.MyActivityTest.test(MyActivityTest.java:89)
at java.lang.reflect.Method.invokeNative(Native Method)
at android.test.InstrumentationTestCase.runMethod(InstrumentationTestCase.java:214)
at android.test.InstrumentationTestCase.runTest(InstrumentationTestCase.java:199)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:191)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:176)
at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:554)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1701)
Caused by: android.content.pm.PackageManager$NameNotFoundException: ComponentInfo{mypackage.view.activity/mypackage.view.activity.MyActivity}
at android.app.ApplicationPackageManager.getActivityInfo(ApplicationPackageManager.java:242)
at android.support.v4.app.NavUtils.getParentActivityName(NavUtils.java:298)
at android.support.v4.app.NavUtils.getParentActivityName(NavUtils.java:279)
... 21 more
My test class looks like follows:
public class MyActivityTest extends ActivityUnitTestCase<MyActivity> {
...
public MyActivityTest() {
super(MyActivity.class);
}
private Context context;
private TestBaseApplication application;
#Override
protected void setUp() throws Exception {
super.setUp();
context = new ContextThemeWrapper( getInstrumentation().getTargetContext(), R.style.Theme_AppCompat){
#Override
public Context getApplicationContext() {
return application;
}
};
application = new TestBaseApplication( context);
setApplication(application);
...
}
public void test() throws InterruptedException {
setActivityContext( context);
Intent intent = new Intent( context, MyActivity.class);
startActivity(intent, null, null);
...
}
}
The activity appears in the AndroidManifest as follows:
<activity
android:name=".view.activity.MyActivity"
android:icon="#drawable/actionbar_logo"
android:screenOrientation="portrait"
android:parentActivityName="mypackage.ParentActivity">
<meta-data android:name="android.support.PARENT_ACTIVITY"
android:value="mypackage.ParentActivity"/>
</activity>
After some troubleshooting, I tried running the example at the link above and it works just fine, even when I change the activity to extend ActionBarActivity.
Since I wasn't able to find the cause of the problem, I also played around with the manifest, build.gradle, etc. Now, I am just stuck with this and I cannot think of anything else.
This post may also be close to related to the problem, but there is not any comment on that one either. And this post also seemed to have similar problem but the solution there doesn't work me as I don't want the real application to be launched with the activity.
EDIT:
I've created a simple separate project, in order to isolate the issue.
First I've written an ActivityUnitTestCase for an Activity which extends ActionBarActivity. It worked fine.
After that, I've tried adding few more activities, making them parent of each other.( so that it looks like my actual project). And that worked fine too.
Lastly, I've added more ActivityUnitTestCase classes with different activities from my actual project, all extending ActionBarActivity, with same setup for all of them , and run the tests on two devices, one being an emulator (Genymotion) and the other is my physical device. (Nexus 4)
Tests all gave the NameNotFoundException on the emulator. All but one of the tests passed on the physical device, which made me even more confused.
It all comes down to this bug in ActivityUnitTestCase.
Activities built using ActivityUnitTestCase.startAcitvity() have a componentName pointing to the application package. So when ActionBarActivity checks for a parent activity to update the Up symbol, the test crashes if the activity is not in the app 'root' package.
Fortunately the workaround proposed in the issue description works just fine, so until this is fixed, just make a local copy of ActivityUnitTestCase, update the line in which the componentName is defined as below, and make sure your test cases extend that class instead of the original ActivityUnitTestCase.
// replace this line
new ComponentName(mActivityClass.getPackage().getName(), mActivityClass.getName());
// with this
new ComponentName(getInstrumentation().getTargetContext(), mActivityClass);

Acra: install, extend Application - Activity?

I am trying to install the ACRA crash report system to my android project. Now, my project is already extending a class, the Activity class. How can I implement the Acra project then?
As they state in normal way, you have to make a class f.e. MyApplication and extend it with Application. Since I am already extending the Activity class I am not sure what to do... They say: If your app already contains an Application subclass, add ACRA to this class; however, I don't know how I should do this..
Thanks!
http://code.google.com/p/acra/wiki/BasicSetup
Just create a MyApplication class that extends from Application, do what they say about overriding onCreate() and then go to your AndroidManifest.
You should have an <application> with values such as android:label or android:theme. Just add android:name=".MyApplication" there and you're ready to go.
Have in mind that if your package is com.example.test, MyApplication has to be there. If you want to put MyApplication wherever else, you must point to where it is.
For example, if your package is com.example.test and MyApplication is in com.example.test.application, you must add android:name=".application.MyApplication to your manifest. I strongly reccomend you to use a package just for your Application, as it atomizes your project and makes it far more manageable and mantainable.
Application is used because of the manifest. In the manifest, it is just to add this to the application tag(with all activities inside):
android:name=".MyApplication"
Ex:
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:name=".MyApplication"
android:theme="#style/AppTheme" >
Because of the easy initialization(as it is automatically initialized by the Android System on launch) it will never not report. It can crash instantly on startup and still report. So it is a really smart setup.
My application class looks like this:
#ReportsCrashes(
formUri = "https://backend.com",
customReportContent = { /* */ReportField.APP_VERSION_NAME, ReportField.PACKAGE_NAME,ReportField.ANDROID_VERSION, ReportField.PHONE_MODEL,ReportField.LOGCAT },
mode = ReportingInteractionMode.TOAST,
resToastText = R.string.crash_toast_text
)
public class ACRAHandler extends Application {
#Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
final ACRAConfiguration config = new ConfigurationBuilder(this)
.build();
// Initialise ACRA
ACRA.init(this, config);
}
}
If you for an instance are using Firebase, you can use both together in the same application-extending class without any issues. I tried it myself and it worked, no problems with error reporting or Firebase.
Additionally, the new links for ACRA is now on Github: https://github.com/ACRA/acra/wiki/BasicSetup
I answered this because it was so long ago the answers came and it needs an update
An application sub class is required to maintain a global application state, it is not necessary for every app to sub class it. If you app does not have one yet, you can create it.
Example:
/* do ACRA imports */
#ReportsCrashes(formKey = "x-x-x-x-x-x")
public class YourApplication extends Application{
public void onCreate(){
ACRA.init(this);
super.onCreate();
}
}
you should also declare in the manifest file as stated in the tutorial.

How to Add Air Native Extension to Stage

I need to display an Activity written in Java using the Android SDK in my Flex Mobile app. I've seen it done with Map ANEs, but cannot find any example code or anything of the sort. I've already created the Java and ActionScript code necessary for my ANE to work (the activity is created and all the classes, events, and methods needed to truly bridge the Java and AS3 is there), I just cannot figure out how to add it to the stage. I know it cannot be added to the DisplayList and I am fine with it being a stage object.
If it helps at all, I am trying to display video using MediaPlayer (due to MP4 streaming issues when it is done using AS3/Flex).
For Michael (Aug 27, 2012 # 9:44AM MST):
08-27 09:27:07.836: I/CS VideoInit(2567): context is set
08-27 09:27:07.836: I/CS VideoInit(2567): intent is instantiated
08-27 09:27:07.836: I/ActivityManager(349): START {cmp=air.AndroidANETesting2/xi.video.android.extension.VideoActivity u=0} from pid 2567
The very first line of my Activity is
Log.i("CS VideoActivity","Made it inside the activity somehow");
Here is a look at my Java. This is the init function:
VideoInit.context = context;
Log.i("CS VideoInit","context is set");
Intent intent = new Intent( context.getActivity(), VideoActivity.class );
Log.i("CS VideoInit","intent is instantiated");
context.getActivity().startActivity( intent );
Log.i("CS VideoInit","Activity is started");
context.dispatchStatusEventAsync("PLAY", "PLAY");
And here is my VideoActivity onCreate():
super.onCreate(savedInstanceState);
Log.i("CS VideoActivity","Made it inside the activity somehow");
And my Manifest for good measure (just the application section):
<application
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name=".VideoActivity"
android:label="#string/title_activity_video" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
Update (August 27, 2012 # 10:52AM MST)
After further investigation (or trial and error, whatever you want to call it), I decided to throw the startActivity() line into a try{}catch(Throwable e) to see what, if any, errors it was throwing. Interestingly enough, it threw this.
08-27 10:49:41.406: I/CS VideoInit(7786): Unable to find explicit activity class {air.AndroidANETesting2.debug/xi.video.android.extension.VideoActivity}; have you declared this activity in your AndroidManifest.xml?
It would appear I need to recheck my Android Manifest file.
It's actually quite easy.
You need to create a class in an ANE that implements the android.app.Activity, then from a FREFunction, simply use the startActivity function of the base Activity instance from the FREContext.
So in a function, lets start an activity with an Intent:
public class ExampleFunction implements FREFunction
{
#Override
public FREObject call( FREContext context, FREObject[] passedArgs )
{
Intent intent = new Intent( context.getActivity(), ExampleActivity.class );
context.getActivity().startActivity( intent );
}
}
Then in the actual Activity implementation:
public class ExampleActivity extends Activity
{
#Override
public void onCreate(Bundle savedInstanceState)
{
// Do your stuff here, like create views, buttons etc
}
}
This activity will display ontop of your application. You can use a static reference to your FREContext to pass events / data back to your application if you wish.
You also need to add activity in your -app.xml inside manifest application-tag:
<application> <activity android:name="package.ExampleActivity"></activity></application>

Categories

Resources