AndriodX activities extend androidx.appcompat.app.AppCompatActivity but that object chain doesn't include onActivityResult( requestCode, resultCode, data). In other words, you can't override it because it's not there.
How do you get the result of a startActivityForResult() if there's no way to add a listener?
implementation 'androidx.appcompat:appcompat:1.0.2'
I'm confused because
androidx.appcompat.app.AppCompatActivity
extends
androidx.fragment.app.FragmentActivity
which contains
protected void onActivityResult(int requestCode, int resultCode, #Nullable
Intent data) { ... }
which I should be able to override. Is this somehow because I'm using Kotlin?
I answered my own question. Yes, it's Kotlin, and I'm still not used to some of the effects of calling java from kotlin. It's mostly the same, but Intent isnullable, which should have signaled me that I need to do this:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
...
}
noting that the type is Intent? to match the nullable parameter. Whoops!
Related
I've a java binding for android which somewhat works bar the new feature I'm trying to integrate with. Only now I have realised that the intended callback is not happening. Here are the classes (decompiled to java) in question:
At the top level we have
public interface MyPackage {
MyPackage.Companion Companion = MyPackage.Companion.$$INSTANCE;
public static final class Companion {
#Nullable
private static MyEventHandler myEventHandler;
// $FF: synthetic field
static final MyPackage.Companion $$INSTANCE;
#Nullable
public final MyEventHandler getMyEventHandler() {
return myEventHandler;
}
public final void setMyEventHandler(#Nullable MyEventHandler var1) {
myEventHandler = var1;
}
private Companion() {
}
static {
MyPackage.Companion var0 = new MyPackage.Companion();
$$INSTANCE = var0;
}
}
}
MyEventHandler class:
public abstract class MyEventHandler {
public abstract void handleEvent(#NotNull String var1, #NotNull Properties var2);
}
Properties class:
import java.util.Map;
public class Properties extends r {
public Properties() {
}
Properties(Map<String, Object> var1) {
super(var1);
}
public Properties a(String var1, Object var2) {
super.b(var1, var2);
return this;
}
}
and the problematic r class:
public class r implements Map<String, Object> {
private final Map<String, Object> a;
various implementations...
}
So I noticed the issue when I couldnt override the HandleEvent method at the integration level and started looking at the Binding logs and found:
Warning=>
BINDINGSGENERATOR: Warning BG8801: Invalid parameter type MyPackage...Properties in method HandleEvent in managed type MyPackage.MyEventHandler. (BG8801)
And in build logs:
message BG0000: warning BG8102: Class MyPackage....Properties has unknown base type MyPackage....r.
warning BG8801: Invalid parameter type MyPackage...Properties in method HandleEvent in managed type MyPackage.MyEventHandler.
As it was obvious r is an obfuscated class I need to make chagnes to my Metadata so I went ahead and popped in:
<attr path="/api/package[#name='MyPackage']/class[#name='r']" name="obfuscated">false</attr>
Which resulted in the R being generated but now I get the 5 following compile error:
Error CS0535: 'R' does not implement interface member 'IMap.EntrySet()' (CS0535)
Error CS0738: 'R' does not implement interface member 'IMap.KeySet()'. 'R.KeySet()' cannot implement 'IMap.KeySet()' because it does not have the matching return type of 'ICollection'. (CS0738)
Error CS0535: 'R' does not implement interface member 'IMap.Put(Object?, Object?)' (CS0535)
Error CS0535: 'R' does not implement interface member 'IMap.PutAll(IDictionary?)' (CS0535)
Error CS0738: 'R' does not implement interface member 'IMap.Values()'. 'R.Values()' cannot implement 'IMap.Values()' because it does not have the matching return type of 'ICollection'. (CS0738)
I tried to make a managed return using
<attr path="/api/package[#name='MyPackage']/class[#name='r']/method[#name='entrySet' and count(parameter)=0]" name="managedReturn">Java.Util.IMap</attr>
With same number of compile error as above. Then I tried removing the node using:
<remove-node path="/api/package[#name='MyPackage']/class[#name='r']/method[#name='entrySet']"/>
Still no luck. :(
What am I missing here? Any pointers/suggestions will be appreciated!
It seems like you are trying to expose a Map to C# and as you stated, Java Generics are not handled very well.
In a very popular social network you received an answer from #mattleibow. I do not take credit for his answer but I went to check nonetheless and it seems fine.
If you look at the description of the Java.Lang.HashMap type
https://learn.microsoft.com/en-us/dotnet/api/java.util.hashmap?view=xamarin-android-sdk-9 it's a good candidate for you to expose.
You can also try with the corresponding interface for better safety https://learn.microsoft.com/en-us/dotnet/api/java.util.imap?view=xamarin-android-sdk-9
If it works you will still have to cast the types yourself.
Please answer to the comment to say that problem is solved for the sake of future generations arriving on this post :D
Credit is not mine so don't give it to me :-)
John,
I got arround fixing it by providing implementation of the the said methods in a partial class. Basically added a new file called R.cs under Additions folder as follows:
namespace YourNameSpace
{
public partial class R
{
public void PutAll(System.Collections.IDictionary p0)
{
PutAll(p0);
}
public Java.Lang.Object Put(Java.Lang.Object key, Java.Lang.Object value)
{
return Put(key, value);
}
public System.Collections.ICollection EntrySet()
{
return EntrySet();
}
public System.Collections.ICollection KeySet()
{
return KeySet();
}
public System.Collections.ICollection Values()
{
return Values();
}
}
}
I couldn't get it to work by adding XML transformation, but I think there was some tooling issue.
I have an aggregate root like this:
Aggregate root:
#NoArgsConstructor
#Aggregate(repository = "positionAggregateRepository")
#AggregateRoot
#XSlf4j
#Data
public class HopAggregate {
#AggregateIdentifier
private String hopId;
private FilteredPosition position;
private LocalDate positionDate;
#AggregateMember
private Security security;
#CommandHandler
public HopAggregate(NewHopCommand cmd) {
log.info("creating new position , {}", cmd.getDateId());
apply(new HopEvent(cmd.getHopId(), cmd.getDateId(), cmd.getFilteredPosition(), cmd.getSecurity(), false));
}
#CommandHandler
public void handle(UpdateHopCommand cmd) {
log.info("creating hop update event {}", cmd);
apply(new HopEvent(this.hopId, this.positionDate, cmd.getFilteredPosition(), this.security, true));
}
#CommandHandler
public void handle(SecurityUpdate cmd) {
log.info("updating security {}", cmd);
apply(new SecurityUpdateEvent(this.hopId, cmd.getFilteredSecurity()));
}
#EventSourcingHandler
public void on(HopEvent evt) {
if (evt.getIsUpdate()) {
log.info("updating position {}", evt);
this.position = evt.getFilteredPosition();
} else {
log.info("adding new position to date {}", evt);
this.hopId = evt.getHopId();
this.positionDate = evt.getDate();
this.position = evt.getFilteredPosition();
this.security= evt.getSecurity();
}
}
#EventSourcingHandler
public void on(SecurityUpdateEvent evt) {
log.info("hop id {}, security update {}", this.hopId, evt.getFilteredSecurity().getSecurityId());
}
}
Child entity:
#XSlf4j
#Data
#RequiredArgsConstructor
#NoArgsConstructor
public class IpaSecurity implements Serializable {
#EntityId
#NonNull
private String id;
#NonNull
private FilteredSecurity security;
}
My issue is that when i am pushing and update like this:
#EventHandler
public void handleSecurityEvent(SecurityUpdate securityUpdate) {
log.info("got security event {}", securityUpdate);
commandGateway.send(securityUpdate);
}
and my command being:
#Data
#RequiredArgsConstructor
#NoArgsConstructor
#ToString
public class SecurityUpdate {
#NonNull
#TargetAggregateIdentifier
private String id;
#NonNull
private FilteredSecurity filteredSecurity;
}
I am getting aggregate root not found exception:
Command 'com.hb.apps.ipa.events.SecurityUpdate' resulted in org.axonframework.modelling.command.AggregateNotFoundException(The aggregate was not found in the event store)
I am not sure how to handle this scenario. My requirement is that each aggregate should check whether it contains the security and then update it if the command was issued. What am i missing? let me know if you need any more info on the code.
Thanks for your help.
A Command is always targeted towards a single entity.
This entity can be an Aggregate, an entity contained in an Aggregate (what Axon Framework calls an Aggregate Member) or a simple singleton component.
Important to note though, is that there will only be one entity handling the command.
This is what requires you to set the #TargetAggregateIdentifier in your Command for Axon to be able to route it to a single Aggregate instance if the Command Handler in question is part of it.
The AggregateNotFoundException you're getting signals that the #TargetAggregateIdentifier annotated field in your SecurityUpdate command does no correspond to any existing Aggregate.
I'd thus suspect that the id field in the SecurityUpdate does not correspond to any #AggregateIdentifier annotated field in your HopAggregate aggregates.
A part from the above, I have a couple of other suggestions when looking at your snippets which I'd like to share with you:
#Aggregate is meta-annotated with #AggregateRoot. You're thus not required to specify both on an Aggregate class
For logging messages being handled, you can utilize LoggingInterceptor. You can configure this on any component capable of handling messages, thus providing a universal way of logging. This will omit the necessity to add log lines in your message handling functions
You're publishing a HopEvent on both the create and update commands. Doing so makes your HopEvent very generic. Ideally, your events clarify business operations occurring in your system. My rule of thumb typically is such: "If I tell my business manager/customer about the event class, he/she should know exactly what it does". I'd thus suggest to rename the event to something more specific
Just as with the HopEvent, the UpdateHopCommand is quite generic. Your commands should express the intent to perform an operation in your application. Users will typically not desire an update, they desire an address change for example. Your commands classes ideally reflect this
The suggested naming convention for commands is to start with verb in the present tense. Thus, it should no be SecurityUpdate, but UpdateSecurity. A command is a request expressing intent, the messages ideally reflect this
Hope this helps you out #juggernaut!
I have a Spring 3.2 Controller with basic request mappings like
#RequestMapping("/action")
public String action(#RequestParam("param") String param) {
//do stuff...
return "view";
}
This controller handles links created by non-technical business users. Sometimes the users mess it up and create links with duplicate parameters, e.g.,
www.example.com/action?param=value¶m=value
The parameter is an exact duplicate and probably a copy/paste error.
My problem is that Spring is concatenating these dupes together, so that the url above will give "value,value" for param, when I want only "value".
What is a good way to detect and handle these duplicates? I know I could change all my #RequestParams to List<String>s and go from there, but that's a whole lot of boilerplate over dozens of request mappings.
Ideally there would be a way to intercept and modify the url parameters before Spring attempts to bind them -- but only for this controller.
I found that I can register a custom String property editor to do this.
class DuplicateParameterReducingPropertyEditor extends PropertyEditorSupport {
Object value;
#Override
public void setValue(Object value) {
if (value instanceof String[]) {
String[] strings = (String[])value;
Set<String> unique = Sets.newHashSet(strings);
this.value = unique.toArray();
} else {
this.value = value;
}
}
#Override
public void setAsText(String text) throws IllegalArgumentException {
this.value = text;
}
#Override
public String getAsText() {
return value.toString();
}
#Override
public Object getValue() {
return value;
}
};
I added this to my controller:
#InitBinder
public void initBinder(WebDataBinder binder) {
PropertyEditor stringEditor = new DuplicateParameterReducingPropertyEditor();
binder.registerCustomEditor(String.class, stringEditor);
}
So whenever Spring encounters a #RequestParam-annotated String method argument, the PropertyEditor is invoked to transform the incoming data if needed. In the case of duplicate parameters, Spring passes a String[] of the values to the property editor setValue, which I can then manipulate.
This does have the results I am looking for. I'm not sure of all the implications of this, though, so I can't endorse it as good solution yet. Not having to alter any handler method signatures is a big plus though.
A good idea would be to extend AbstractNamedValueMethodArgumentResolver with your own strategy. Then the strategy could be used wherever you deem necessary.
This strategy only works for Spring 3.1+ which is not a problem for you since you are using Spring 3.2
I faced the same issue in Spring boot. Eventually I came up with this solution using converter, in case it helps anyone.
This method should be added as part of your WebMvcConfigurer class.
#Override
public void addFormatters(FormatterRegistry registry) {
// Duplicate query parameters converter
registry.addConverter(new Converter<String[], String>() {
public String convert(String[] arr) {
return arr[arr.length - 1]; // Return the last value
}
});
}
I use a Notifications interface to update fragments whenever data is changed.
public interface Notifications {
void register(ID id, Listener listener);
void unregister(ID id, Listener listener);
<T> void post(ID id, T value);
interface Listener<T> {
void onEvent(ID id, T value);
}
enum ID {
CustomersUpdated,
ProductsUpdated
}
}
With regards to the Android Lifecycle, what is the best point to register and unregister for notifications?
Here are some scenarios:
Scenario 1:
public class ProductsListFragment extends BaseFragment
implements Notifications.Listener {
#Override
public void onStart() {
mAdapter.notifyDataChanged();
register(Notifications.ID.ProductsUpdated, this)
super.onStart();
}
#Override
public void onStop() {
unregister(Notifications.ID.ProductsUpdated, this)
super.onStop();
}
#Override
public void onEvent(Notifications.ID id, Object value) {
mAdapter.notifyDataChanged();
}
Scenario 2:
public class ProductsListFragment extends BaseFragment
implements Notifications.Listener {
#Override
public void onResume() {
mAdapter.notifyDataChanged();
register(Notifications.ID.ProductsUpdated, this)
super.onResume();
}
#Override
public void onPause() {
unregister(Notifications.ID.ProductsUpdated, this)
super.onPause();
}
#Override
public void onEvent(Notifications.ID id, Object value) {
mAdapter.notifyDataChanged();
}
Please explain why you would suggest using one or the other implementation .. or another!
There isn't a universal answer to this question. onResume/onPause will probably give the expected behaviour most of the time but you might run into cases where you want to do it earlier or later.
On a different note, though, two points on style and functionality - call super.onResume as the first thing in the method (and super.onStop as the last). That way your cycle is entirely nested inside the "super" cycle and you avoid weird bugs and edge cases. Further, it's not a great idea to always call notifyDataSetChanged in onResume. In fact, it's probably a pretty wasteful idea.
I would stick with Scenario 2. Although the order in which onPause() and onResume() is linear for fragments, the same is not true for Activities.
Since the fragments' pause and resume are called whenever the activity's is, broadcasts would be received whenever the activity is active. However, the activity does not call onStop() until it loses visibility. In this case, the fragments would still process broadcasts while the activity it is contained in is inactive, which doesn't sound like a very good idea to me.
I'm pretty new to MVVM light world, and after searches I can't find what I want to do.
My WP7 application contains a pivot, each pivot item content is View1 and viewmodel is VM1.
When loading my application, I'd like to create every pivot item with the same view and view model but with different parameter.
example :
PivotItem 1 -> send param "car" to the view model
PivotItem 2 -> send param "truck" to the view model, etc.
Google told me to use messaging but if I send 2 messages from my MainViewModel, both PivotItem1 and PivotItem2 ViewModel will receive these messages.
Am I wrong with this approach ?
Is there another solution to succeed ?
Thank you in advance for your answer.
PS : be indulgent, english is not my native language, don't hesitate to ask for further information.
Regards,
Aymeric Lagier
To seperate the messages use the second constructor signature whereby you can pass a token. This token can be anything but I generally use an enum to store all my message types within the system.
Create a static class in a common library and reference this in all projects where you need to send or receive messages.
The following code hopefully shows this approach, notice I am sending a string as a value within the message but this can be anything, even a complex object such as one of your business objects.
namespace MyProject.Common
{
public static class AppMessages
{
enum MessageTypes
{
ViewmodelA,
ViewmodelB
}
public static class ViewModelAUpdate
{
public static void Send(string value)
{
Messenger.Default.Send(value, MessageTypes.ViewmodelA);
}
public static void Register(object recipient, Action<string> action)
{
Messenger.Default.Register(recipient, MessageTypes.ViewmodelA, action);
}
}
public static class ViewModelBUpdate
{
public static void Send(string value)
{
Messenger.Default.Send(value, MessageTypes.ViewmodelB);
}
public static void Register(object recipient, Action<string> action)
{
Messenger.Default.Register(recipient, MessageTypes.ViewmodelB, action);
}
}
}
}
How about using a method to set the message you want to receive. (this could be done as a parameter in the constructor or a property as well)
public void RegisterForAppMessage(AppMessages.MessageTypes messageType)
{
switch (messageType)
{
case AppMessages.MessageTypes.PivotViewItem1Message:
AppMessages.PivotViewItem1Message.Register(this,DoSomethingWhenIRecievePivotViewItem1Messages)
break;
case AppMessages.MessageTypes.PivotViewItem2Message:
AppMessages.PivotViewItem2Message.Register(this,DoSomethingWhenIRecievePivotViewItem2Messages)
break;
}
}
private void DoSomethingWhenIRecievePivotViewItem2Messages(string obj)
{
// TODO: Implement this method
throw new NotImplementedException();
}
private void DoSomethingWhenIRecievePivotViewItem1Messages(string obj)
{
// TODO: Implement this method
throw new NotImplementedException();
}
Messaging sounds a bit heavy for this purpose. Could you simply inject a parameter into your ViewModel. If you already have MVVMLight you also have support for SimpleIOC. Maybe let the view locate its ViewModel when the view is resolved and there decide which parameter to use on the view model?
You can see an example of it here