I'm currently trying to develop an application under Android using Mono.
I'd like to add support for plugins to my application so additional features could be brought to it.
I was able to load simple .dll at runtime in my program, however whenever I try creating a dll implementing both my interface and a custom activity, an exception of type Java.Lang.NoClassDefFoundError is thrown.
There is the class inside the dll code:
[Activity (Label = "Vestiaire")]
public class Vestiaire : Activity, IModule
{
public string Name { get; set; }
public string Version { get; set; }
void OnClickVestiaireButton(object sender, System.EventArgs e)
{
;
}
public void InitVestiaireModule()
{
Run();
}
public Type LaunchActivity ()
{
return typeof(Vestiaire);
}
public void Init()
{
Name = "Vestiaire Module";
Version = "0.1";
}
public void Run()
{
}
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
}
}
The line responsible for the exception: (from the program core)
LoadedPlugin.Add((IModule)(Activator.CreateInstance(Plugin)));
Things I'm actually wonderring are:
- Is it possible to actually achieve what i'm trying to ?
If yes, help would be apreciated on that problem :P
Otherwise what would be the best alternative ?
Global point is to be able to load a custom menu at runtime loaded from a dll.
Thanks.
i think the key to your problem is that the Activity needs to be registered in you Manifest.xml file.
For Activities in you main app, MonoDroid does this for you - but I don't think this will work for your plugin.
Things you could try are:
putting the Activity in the Manifest yourself (MonoDroid does seem very capable at merging these files)
if that doesn't work, then you could try using a Fragment instead - and loading the Fragment into a custom FragmentActivity in your main app.
Related
Godot version: 3.2.3
Issue description:
I am new at using Android Plugins in Godot, so I created this simple plugin with only one method.
public class GodotProva extends GodotPlugin
{
public GodotProva(Godot godot) {
super(godot);
}
#NonNull
#Override
public String getPluginName() {
return "mylibrary";
}
}
I tried to use it in Godot, with the following code:
func _pressed():
if Engine.has_singleton("mylibrary"):
var singleton = Engine.get_singleton("mylibrary")
print(singleton.getPluginName())
I created an apk and installed it on my Android device. The problem is that when I press the button (and the function _pressed() is called), I can see from the logcat the error Nonexistent function 'getPluginName' in base 'JNISingleton'
I am sure that the plugin is found, because the "singleton" variable is not null.
What am I doing wrong?
I found out that in Godot I can only call custom functions. The methods of the class GodotPlugin (such as getPluginName, getPluginMethods...) cannot be directly called.
In my case, I needed to define methods in getPluginMethods in my GodotPlugin like this:
public void firstMethod() {
// TODO
}
public void secondMethod() {
// TODO
}
#SuppressWarnings("deprecation")
#NonNull
#Override
public List<String> getPluginMethods() {
return Arrays.asList("firstMethod", "secondMethod");
}
I don't know why this case is not documented, but I wasted a lot of time on this and I think it would be useful for other people...
I am trying to implement EventBus on Xamarin but Could not able to Subscribe the Events because when I am adding the annotation #Subscribe(), I am getting an error "#Subscribe does not exist in the current context". And when I am running the Application without #Subscribe annotation, it is showing me an Error saying "MainActivity and its super classes have no public-methods with the #Subscribe annotation". Please help me on how to subscribe the Event on Xamarin Android platform
#MainActivity class:
public class MainActivity : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
// Set our view from the "main" layout resource
//Registering Event Bus here
EventBus.Default.Register(this);
SetContentView(Resource.Layout.activity_main);
}
#Subscribe //Showing me an error while adding this annotation
public void OnEvent(NetworkInfo networkInfo)
{
Console.WriteLine(networkInfo);
}
protected void OnDestroy()
{
base.OnDestroy();
EventBus.Default.Unregister(this);
}
}
PublisherEvent class:
public class PublishEvent
{
public PublishEvent()
{
EventBus.getDefault().post(message);
}
}
I'm not sure whether Xamarin Forms supports EventBus, and uses #Subscribe() as the same with native Android.
However, there is a same way in Xamarin Forms. You could use Xamarin.Forms MessagingCenter to achieve that, it also can subscribe the message and receive message.
Send Messaage:
MessagingCenter.Send<object>(this, "Hi");
Subscribe Message:
MessagingCenter.Subscribe<object> (this, "Hi", (sender) =>
{
// Do something whenever the "Hi" message is received
});
I was able to get Subscribe to work via Xamarin
I noticed your code here
#Subscribe //Showing me an error while adding this annotation
public void OnEvent(NetworkInfo networkInfo)
{
Console.WriteLine(networkInfo);
}
was this implemented as follows? (note the use of attributes rather than the java annotation)
[Subscribe]
public void OnEvent(NetworkInfo networkInfo)
{
Console.WriteLine(networkInfo);
}
In order for this to work, Android needs to convert the Subscribe attribute to the #Subscribe Annotation in Java. The Xamarin Binding Library should be creating SubscribeAttribute for you if you've got that project setup correctly (though it may notably be missing the ThreadMode property, I may be investigating that for myself soon!)
Anyways, this attribute won't be correctly generated if the OnEvent method isn't also generated in the corresponding Java class. I found this did not happen automatically for me so I simply needed to add [Export] to the method.
[Export]
[Subscribe]
public void OnEvent(NetworkInfo networkInfo)
{
Console.WriteLine(networkInfo);
}
This is actually (somewhat unobviously) documented here
I am rewriting my vanilla Xamarin app to use Prism Library.
The current app uses Azure ADB2C for authorisation using this framework.
Android needs to have its parent window set, which is achieved by adding this code into the MainActivity.cs of the Android project:
var authenticationService = DependencyService.Get<IAuthenticationService>();
authenticationService.SetParent(this);
This doesn't work for the Prism app, authenticationService is null. For the record, the DependencyService used here is Xamarin.Forms.DependencyService.
I also tried the example from the Prism docs and put this code into the AndroidInitializer:
public void RegisterTypes(IContainerRegistry container)
{
// Register any platform specific implementations
container.RegisterSingleton<IAuthenticationService, B2CAuthenticationService>("B2CAuthenticationService");
var authService = Container.Resolve<IAuthenticationService>();
authService.SetParent(this);
}
In this code, Container (which is a DryIoC Container) had no definition for Resolve.
For completeness, this is my App.cs RegisterTypes:
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterSingleton<IAuthenticationService, B2CAuthenticationService>();
...
...
}
There are a few wrong assumptions you're making here. To start you'll notice that IContainerRegistry specifically has the name Registry in it to imply we don't want you resolving types here. This is why you don't see the Resolve method on it but rather the IContainerProvider instance.
By design, Prism no longer works directly with the Xamarin.Forms DependencyService as this is a complete anti-pattern. That said if you follow the guidance for registering Platform Specific types you can see how to use the IPlatformInitializer to register platform specific types. It is important to realize here that the IPlatformInitializer is called before RegisterTypes is called in PrismApplication.
What I would suggest is to introduce a IParentWindowProvider like:
public interface IParentWindowProvider
{
object Parent { get; }
}
You can then implement this on Android like:
public class MainActivity : IPlatformInitializer, IParentWindowProvider
{
object IParentWindowProvider.Parent => this;
void IPlatformInitializer.RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterInstance<IParentWindowProvider>(this);
}
}
Then in your Application you might do something like:
protected override void OnInitialized()
{
if(Container.IsRegistered<IParentWindowProvider>())
{
var provider = Container.Resolve<IParentWindowProvider>();
var authService = Container.Resolve<IAuthenticationService>();
authService.SetParent(provider.Parent);
}
}
For more info be sure to check out the relevant docs and sample
https://prismlibrary.com/docs/xamarin-forms/dependency-injection/platform-specific-services.html
https://github.com/PrismLibrary/Prism-Samples-Forms/tree/master/03-PlatformSpecificServices
I need to determine in runtime from code if the application is run under TestInstrumentation.
I could initialize the test environment with some env/system variable, but Eclipse ADK launch configuration would not allow me to do that.
Default Android system properties and environment do not to have any data about it. Moreover, they are identically same, whether the application is started regularly or under test.
This one could be a solution: Is it possible to find out if an Android application runs as part of an instrumentation test but since I do not test activities, all proposed methods there won't work. The ActivityManager.isRunningInTestHarness() method uses this under the hood:
SystemProperties.getBoolean("ro.test_harness")
which always returns false in my case. (To work with the hidden android.os.SystemProperties class I use reflection).
What else can I do to try to determine from inside the application if it's under test?
I have found one hacky solution: out of the application one can try to load a class from the testing package. The appication classloader surprisingly can load classes by name from the testing project if it was run under test. In other case the class is not found.
private static boolean isTestMode() {
boolean result;
try {
application.getClassLoader().loadClass("foo.bar.test.SomeTest");
// alternatively (see the comment below):
// Class.forName("foo.bar.test.SomeTest");
result = true;
} catch (final Exception e) {
result = false;
}
return result;
}
I admit this is not elegant but it works. Will be grateful for the proper solution.
The isTestMode() solution did not work for me on Android Studio 1.2.1.1. Almighty Krzysztof from our company tweaked your method by using:
Class.forName("foo.bar.test.SomeTest");
instead of getClassLoader(). Thanks for Krzysztof!
We created a solution to pass parameters to the MainActivity and use it inside the onCreate method, enabling you to define how the Activity will be created.
In MainActivity class, we created some constants, which could also be an enum. We created a static attribute too.
public class MainActivity {
public static final int APPLICATION_MODE = 5;
public static final int UNIT_TEST_MODE = 10;
public static final int OTHER_MODE = 15;
public static int activityMode = APPLICATION_MODE;
(...)
#Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
switch (activityMode) {
case OTHER_MODE:
(...)
break;
case UNIT_TEST_MODE:
Log.d(TAG, "Is in Test Mode!");
break;
case APPLICATION_MODE:
(...)
break;
}
(...)
}
(...)
}
We made MainActivityTest class abstract, created a setApplicationMode and called this method inside the setUp() method, before calling the super.setUp() method.
public abstract class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity> {
protected void setUp() throws Exception {
setApplicationMode(); // <=====
super.setUp();
getActivity();
(...)
}
(...)
public void setApplicationMode() {
MainActivity.activityMode = MainActivity.UNIT_TEST_MODE;
}
}
All other test classes inherit from MainActivityTest, if we want it to have another behaviour, we can simply override the setApplicationMode method.
public class OtherMainActivityTest extends MainActivityTest {
(...)
#Override
public void setApplicationMode() {
MainActivity.activityMode = MainActivity.OTHER_MODE;
}
}
The user nathan-almeida is the friend that is co-author of this solution.
In an effort to reduce duplication, I have my app's workspace split into 3 projects:
Main (A library project, where all of the common code lives)
Free (To make the free version)
Paid (To make the paid version)
Behavior in the free version and the paid version must differ sometimes. How can I "call into" the final projects from the library project?
Here is some sample psuedo-code to illustrate my question:
In the Main project:
private void makeADecision() {
if (AppCode.isPaid()) {
// do one thing
} else {
// do something else
}
}
In the Free project:
public class AppCode {
public static bool isPaid() {
return false;
}
}
In the Paid project:
public class AppCode {
public static bool isPaid() {
return true;
}
}
That is basically what I have tried, but it won't compile because the Main project doesn't know about the AppCode class.
Bear in mind that this is only an example, so try not to focus on how an app can tell if it is the paid version or not. :) So far the best solution I have found is to put a string in the resources of all three projects and then make a decision based on its value but I don't like that method. Besides being ugly, I would prefer to keep functionality where it belongs. That way I can prevent "paid-only" functionality from being compiled into the free version at all and I can avoid having to include any "free-only" code in the paid version.
Step #1: Define an interface in the library, so it is available to all three parties, whose methods are whatever operations you want the library to perform on the app
Step #2: Have the app supply an implementation of the interface to the library via some library-supplied API
Step #3: Have the library call methods on the supplied interface as needed
I don't think it's a good idea to call the main App from the library, even if it's possible.
Instead I'd be adding a public static boolean to the library and set it from within your application once it starts for the first time.
public class MyLibrary {
public static boolean IS_PAID = false;
public void makeADecision() {
if(IS_PAID) {
// do one thing
} else {
// do something else
}
}
}
and in your main application you could do something like
com.yourname.yourlib.MyLibrary.IS_PAID = true;
to set it. Since it's not final, you can change it's state at any time. If it's more complicated behavior, you could use a public static listener or callback which you could assign from your full/free app and then call it from your library
You could use reflection to achieve that - take care that it is usually not a very good idea.
For example:
static private boolean isAppPaid;
static {
try {
Class c = Class.forName("your.package.AppCode");
Method m = c.getMethod("isPaid");
isAppPaid = (boolean) m.invoke(null);
}
catch (Exception e) {
isAppPaid = false;
}
}
There probably are mistakes in my code - I have never used Java reflection much.
Edit: I agree with Tseng that making a library invoke application code is debatable at best. (Except if said library is a framework that takes over the client application.)
You could also make the free and paid versions make subclasses of whatever class makeADecision is in and implement the separate behavior that way.
so in main
public class BaseClass {
...
public void makeADecision() {}
...
}
in free
public class FreeClass extends BaseClass {
...
public void makeADecision() {
//free implementation here
}
...
}
in paid
public class PaidClass extends BaseClass {
...
public void makeADecision() {
//paid implementation here
}
...
}
Tseng Solution is an easy and straightforward solution. Thanks for that, it is the one i have used. Also, I fell upon that article which could help you implement the solution :
http://www.firstlightassociates.co.uk/blog/2011/software/android-software/managing-free-and-paid-android-applications-2/
Hope it will help some of you !
Cheers