Below I summarize some thoughts/recommendations/opinions for working with Fragments in an ATAK plugin, which are reflective of my implementation in this example plugin:
-
Manage navigating between different fragments with one main fragment that has a single view element: a
FrameLayout
, which is used to host other Fragments' views. In this example I refer to this as the ChoreographerFragment. -
When your plugin's
DropDownReceiver
receives the intent to actually launch your plugin, instantiate an instance (or use a previously initialized instance) of yourChoreographer
fragment and pass it to one of the (many) overloadedDropDownReceiver::showDropDown(Fragment, ...)
implementations. -
Have your
Choreographer
fragment handle showing the true main layout for your plugin. One decent place to do this is in theChroeographerFragment::onResume
lifecycle method. Combining fragment puppeteering logic with your main layout view management in one fragment can be done, but risks an unmaintainable mess. -
Use intents to trigger forward navigation to other fragments, and override your
DropDownReceiver::onBackButtonPressed
method to handle backward navigation by processing click events on the device's back button. Both forward and backward navigation should, ideally, be delegated to yourChoreographer
. -
For forward navigation, your
DropDownReceiver
can either handle instantiating the target fragment to submit to theChoreographer
for showing, or call a more specific method on theChoreographer
to do it. The former prevents theDropDownReceiver
from worrying as much about view logic, but the latter prevents easy re-use of theChoreographer
since it will inevitably contain logic specific to each of yourFragment
implementations. The choice is personal preference. -
For backward navigation consider an interface that each Fragment can implement. This can allow the visible fragment to perform cleanup logic when backward navigation is triggered (but before the view is changed out), or consume the back button press instead of propagating it.
- In this implementation, I experimented with having a persistent ribbon that is present above whichever fragment is showing. But managing the state of anything in that ribbon quickly becomes a nightmare.
I also figure that providing an API for child fragments to supplement or otherwise modify that ribbon would be ugly.
If you want a ribbon/title bar to consistently show on each screen, consider the following:- A reusable layout resource. Consuming layout resources would utilize Android's
<include>
or<merge>
tags their XML. - A reusable control with a nice API (preferably this one). This is achievable through subclassing a layout widget like
LinearLayout
orConstraintLayout
.
- A reusable layout resource. Consuming layout resources would utilize Android's
- I have to admit: I hate intents. I might hate them more than Android's prefrences! In this case, intents are a fine mechanism, but if you come up with something that doesn't rely on them then 👍. Please feel free to contribute it here to help others!
Because of quirks like
- ATAK Context vs Plugin Context
- Using the Drop Down for views vs owning the Activity
design using Fragments doesn't follow directly from examples in the Android Developer Documentation or any non-TAK-related blog you'll probably find.
The overall pattern of managing fragment navigation for ATAK plugins by using a single "root" fragment was pioneered by the Combat Swim plugin (RIP). Many of the thoughts and recommendations presented here are the result of a lot of experimentation and iterating on that example for the TacticalRoute plugin.
But I don't know everything!
The implementation here is a way it can be accomplished. If experimentation yields more canonical, more efficient, or more maintainable ways of accomplishing this then please contribute to this example!
- This was built against the ATAK 4.2.0 Plugin API and should be incremented to match whatever Plugin API version ATAK is on if you want to run this plugin.
- This plugin was adapted from the PluginTemplate project, but abandoned the TakDev Gradle Plugin that the template uses. It was causing excruciating Gradle sync times > 20 minutes, and still wouldn't successfully build. Once the TakDev Gradle plugin implementation is ironed out, it can be incorportaed here by replacing the
build.gradle
file.