Introduce GenericMultiProfilePagerAdapter
This is an intermediate step in simplifying the full class hierarchy
around the MultiProfilePagerAdapters in support of our general
interest in clarifying/decoupling the chooser/resolver relationship,
and especially in support of our specific goals in being able to
build these UIs outside of the chooser/resolver activities (for the
embedded sharesheet project).
The new class takes over (almost) all of the responsibilities of the
two legacy adapter implementations, leaving those components (almost)
trivially implemented in terms of this new unified implementation.
The remaining responsibilities are extracted to delegate interfaces
with corresponding implementations in the legacy components that get
injected in the initialization of the new "Generic" base class. (This
is just one possible abstraction that I've chosen for now because it
trivially preserves the behavior while setting up a structure that's
easier to manipulate in our refactoring. In the future, we could
consider alternatives -- maybe using simpler "setter" APIs, or
adjusting our design to pull these responsibilities out of the
adapter altogether.)
Following this CL, we should merge this new component up into the base
`AbstractMultiProfilePagerAdapter`, then close that API to further
extension. We may also remove the use of inheritance as the model
for pre-specifying "chooser" vs. "resolver" configurations; we'd
only have a single (generic) class, with any behavioral variation
configured at initialization time.
Notably, the use of generics in the new class replaces a lot of
duplication where the chooser implementation just needed to provide
overrides to specify the narrower chooser-specific types. I expect
that the legacy code has several other places where the
chooser-specific version of a class exists largely for these "glue
code" responsibilities of switching the entire design over to using
the chooser "family" of concrete classes. There's lots of problems
with that approach. It's unnecessarily flexible/complex (our
application doesn't require runtime polymorphism to select between chooser and resolver implementations); it creates a lot of
complex/error-prone situations where the safety of our downcasts
relies on our knowledge of how the system is configured at runtime
(and our trust in the participating components to honor their
[undocumented] contracts); it creates unnecessary coupling and
duplication as the configuration has to be pushed through to
indirect dependents; and in practice we often have chooser extend
resolver instead of pulling up a base class, so the chooser API
surfaces grow to accommodate every customization we ever support for
the resolver case (often without any particular documentation or
contracts for subclass implementors). As in this CL, generics are
often the simplest way to preserve the legacy APIs while reducing
duplication, and they provide all the flexibility we need since our
"app configurations" are static at compile-time. When more runtime
flexibility is required, GoF "creational" and "behavioral" patterns
specify well-defined alternatives that we should target strictly in
our refactorings to prevent the kinds of ad-hoc scope creep that
results in our legacy components' current incoherent mix of
responsibilities.
Test: atest IntentResolverUnitTests
Bug: 202167050
Change-Id: I2cde7ee2e0cbed43499bfe457b6b99f239bdfb51
5 files changed