Is there a simple way to find out whether an Intent is explicit or implicit?
I'm trying to implement a navigation drawer with the behaviour recommended in the Android docs, i.e. that the drawer should open automatically when the app starts, until the user has opened it manually.
However, the activities that use the navigation drawer can be started from a variety of places within my app, as well as outside it. I want to open the drawer only when the activities are launched from outside my app, but I can't find a simple way to find out where an intent came from.
There are a few methods in Intent that initially looked promising:
getComponent() would seem to give you the information (null/not null) on the sending end (where you already know!) but according to the docs is never null on the receiving end.
hasCategory() could be used to find known categories of external intents such as android.intent.category.LAUNCHER. This might be what I end up using but I'd rather have a general solution than try to account for each possibility here.
getPackage() looks like it might do the right thing, but unless I'm not using it correctly it always seems to return null in my tests.
Alternatively, I could always use putExtra() to add a flag to all of my internal intents, but that feels too much like a hack so I'd rather avoid it if there's another option.
Is there a simple way to find out whether an Intent is explicit or implicit?
That's not what you need, given the rest of your question. What you really should be asking is:
How can I determine internal Intent invocations from external ones, with limited hassle?
In that case, for many apps, you can use this algorithm: if getAction() is null, it's an internal explicit Intent. Otherwise, assume it's external.
If, for various reasons, you are using action strings for starting local activities (despite also setting the component on the Intent and making it explicit), then you will need to fall back to the "tack on an extra" thing. Normally, if you are starting a local activity with an explicit Intent, you are not setting an action string, which makes the action string a clear delineator between implicit (action not null) and explicit (action null).
The best way to implement this is to pass extras with the Intent that identify the Activity that called it, like you mentioned. It's not a hack in particular when you consider that Intents are frequently checked for static identifiers when the Activity finishes and the calling activity checks the result code. Yours is the inverse case of checking the calling Activity.
Related
fellow Flutter enthusiasts, pros, and future friends. I've been really banging my head from trying to pass the MethodChannel.Result from the MainActivity to a new activity (to handle the result handler after performing some work there).
In iOS, I've stuffed the FlutterResult into a struct with other pertinent values and passed that into a new ViewController, and have been successfully running the result handler from there.
How do I go about doing so in Android? Since the result isn't serializable I can't add it as an extra in an intent.
In my MainActivity, I'm running 'setMethodCallHandler', and in the proper call.method case, I'm creating a new intent and starting it. I'm successful adding in and pulling out String values in the new activity but it's that Flutter Result that I'm needing to pass over.
What is the recommended way to achieve this? Am I going about it the wrong way?
I ask if I'm doing it incorrectly because when I finish() this new activity, there is a half-second black screen that takes over the screen when the activity is dismissed.
Not sure if it matters, but I'm writing in Kotlin. I would be happy to hear any Java recommendations though.
Thank you all in advance.
Without a little bit more context about what exactly you're doing in the new activity, it's a little bit hard to tell why you'd be getting a black screen. Theoretically, the flutter activity should still be running and should show up when you finish the new activity - unless you're doing something blocking on the UI thread.
However, for passing the result - what you want to be doing is to keep a reference to the result handler wherever you're receiving the message from flutter, and making use of android's startActivityForResult method. See the android docs on getting a result from an activity. Your 'worker' activity should do whatever it needs to do, then pass the result back by calling setResult before finish. The data you pass back must be serializable.
There is a slight added wrinkle - you're not necessarily working with your own activity here (if you're writing a plugin anyways). If that's the case, you'll need to implement ActivityResultListener and call registrar.addActivityResultListener in your plugin's registerWith function. If you're just doing this in an app, you can simply override onActivityResult - just make sure to call super or you might break other flutter plugins.
There is another possible solution to this, with various levels of hacky-ness depending on how in-depth you want to be. The solution is to simply use a Singleton or Global to store what you need between activities. For anyone out there who's reading this - I don't endorse doing this, I'm just providing an alternative. There are caveats to go with this - among them is that globals and to a lesser extent singletons are seen to be a bad idea for many reasons, among them code maintainability. If you absolutely must go down this route, I suggest using a Registry pattern rather than a simple singleton - i.e. you create a key that is serializable and store the Result in essentially a global/singleton map using the key, pass the key to the new activity, then in the new activity retrieve the value with that key (and make sure to remove the object from the map).
Note that the global/singleton/registry option won't work properly if the app is stopped by android as the activity could be recreated from its intent, but the object in memory may not persist. Then again - the flutter callback won't persist anyways so that point might be moot anyways.
If you're still seeing the black screen after finishing the new activity, that sounds more like a bug or something to do with the implementation of your new activity than a flutter problem.
Hi I have problem with planning implementation of specific search interface.
I am making navigation application where you can navigate from point to point.
For this I need two seachviews both with different searchable configuration, where I can define different custom intent action to recognize if user enter source or destination and react properly. I have already created suggestion logic because I am using one SearchView in other activity and already have different configuration there.
So my plan is creating an activity for result which will be in same time SearchActivity where I will setup two different SearchViews components where based on user input there will appear suggestions and after clicking them (first later second) the intent will be picked by CUSTOM1/2 ation intent in onNewIntent() as it will be "singleTop" activity and stored properly. Then i will process stored source&destination data and finish with proper response code to the source activity with extras based on processed data.
I don't know if I explained it properly. The main problem for me here is that I need to have two different SeachViews widgets. How to achieve this?
Eventually I can manage both SearchView suggestion results with default ACTION_VIEW and recognize them by sent data but I think the first plan is more clear.
PS I am planning to put them directly to the layout without appbar.
I've noticed while trying to switch from one Activity to another within a glass app I've built, the menu seems to get messed up and I get left with a ghost activity displayed in my LiveCard that cannot be closed.
I've reviewed a few of the examples (e.g. Compass) but we don't have any advanced examples for navigation it looks like. Can you help out with defining for me the agreed upon convention thus far for building navigation in apps? This will help fix my issue because I realize I'm creating multiple LiveCards which doesn't seem right.
Activities: Should I create multiple activities or should I recycle the same Activity swapping out the UI instead? If multiple activities, is it a good idea to destroy the previous Activity's service upon loading up a new Activity?
Services: In a broad sense, should there be a single service used by all activities to handle the navigation (switching between Activities)? If just one service, should it ever be stopped/restarted? How do you switch between Activities using the service because we don't have a reference to the service from the Activity or the Binder? Or should there be a service for each Activity to handle the navigation?
LiveCards: I believe only one livecard should be created, so then how do you switch between Activities without creating a new one? Is it the convention to keep a global reference for any service to be able to use this LiveCard?
I've asked a lot of questions and I could be way off base for some of them, but I can use any guidance! I can't really find a good guide on google for defining the expected behavior for nagivation. I come from 3 years as an Android dev, so feel free to be complex in your answers! Thanks guys.
EDIT:
In case this helps, here's the code I'm using to go from one Activity to the next:
mLiveCard = new LiveCard(this, GlobalConstants.LIVE_CARD_TAG_ACTIVITY2);
mRenderer = new MainRenderer(this);
mLiveCard.setDirectRenderingEnabled(true).getSurfaceHolder()
.addCallback(mRenderer);
// Display the options menu when the live card is tapped.
Intent menuIntent = new Intent(this, Activity2.class);
menuIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
mLiveCard.setAction(PendingIntent.getActivity(this, 0, menuIntent,
0));
mLiveCard.attach(this);
mLiveCard.publish(PublishMode.REVEAL);
We're probably all pretty new to GDK so I'm certainly not more of an expert then you.
I'll share with you what my experience is so far:
Activities:
I only use these when i want to create Immersions. I think the only reason to decide if you want to have multiple or one activity should be readability. If an immersion is all you need then you can start your activity directly instead of using a service.
Services:
I found it useful to have one central service. Especially in projects where I have state full objects with a lifecycle that spans multiple activities. In case your navigation depends on the state of e.g. a connection it also makes sense to let the service manage the navigation e.g. start new activities. Activities can interact and provide results to the service with whatever method is appropriate e.g. Intent, Broadcast, Binder, Singleton...
I usually go through the list and take the first one that fits the requirement. If i can do what I want to do with intent's then that's usually my first choice.
LiveCards:
A central service would also be a good place to manage a livecard that you want to share across other activities. Again the method to expose the live card interface to activities depends on what kind of interface you want (abstract vs direct).
Let me know what you think.
I need to get the instance of the Activity that was created when startActivity() is called. However, startActivity() returns a void value.
I guess startActivity() doesn't wait until the Activity is created.
Is there a different way for me to get hold of the activity that was just created on the call to startActivity? Or, perhaps I can create an Activity instance myself and register it somewhere so that startActivity can find it.
Is the Activity you're trying to look at in your own app, or in another app?
If they're both your Activities, then it might be OK to do what you're trying to do, although Activities ought to remain separated. You might consider using Fragments instead, since they can "communicate" with each other through their parent Activity.
If they're not both in your app, then you can't get the other Activity instance, because by definition it's in another process. Moreover, the Android system prevents it, because it would be a big security hole.
In general, I squint a bit at attempts to get an instance of something outside of your own component, unless it's an instance of something in the system. Communication between components should be by Intent, bound Service features, or content URIs. Passing around instances leaves you open to memory leaks.
They all transfer info in data and seem like when I start a new intent with them all start a new activity. So I don't really know the difference between them.
When starting a new activity using an intent, you could choose to view some data or to edit it depending on your needs by specifying one of ACTION_EDIT or ACTION_VIEW respectively. It may be the case that both of these intents start the same activity and so the result is the same whatever you choose or it might be that they are mapped to different activities and that ACTION_EDIT will start an activity where the data is editable and ACTION_VIEW will start an activity where it is not. As the name suggests, you should choose the action that best matches your intent. If you are specifying a filter for your activity, you should keep this in mind too and filter the actions that your activity is designed to fulfill.
EDIT: Data refers to what you intend to act on. Normally, an Intent contains two things: an action and the data you want to perform the action on. Here are the brief descriptions from the javadoc (which is very good, I suggest you read the section on Intent Resolution):
action -- The general action to be performed, such as ACTION_VIEW, ACTION_EDIT, ACTION_MAIN, etc.
data -- The data to operate on, such as a person record in the contacts database, expressed as a Uri.
Using that information, Android will attempt to find the most suitable activity to service your intent (one with a filter that matches your intent). Activities in your application take precedence over those outside it, but is is possible and common to invoke an activity in a 3rd party app.