Compare commits

..

11 Commits

Author SHA1 Message Date
EricKuck 6a87a4140e minSdk 19 2024-01-17 11:52:24 -05:00
EricKuck ad81c4819b jdk 17 in CI 2024-01-17 11:38:55 -05:00
EricKuck 49018ed84c Fix a few early bugs when disabled 2024-01-17 10:59:50 -05:00
EricKuck fbfe3ce2e9 WIP predictive back change handlers 2024-01-16 18:36:13 -05:00
Steven Schoen 5a1746b2d3 Fix doc mistake in ControllerChangeHandler (#691) 2024-01-11 13:28:13 -05:00
Steven Schoen 2ffafaee79 Add kdoc to ControllerChangeHandler::removesFromViewOnPush (#688) 2023-09-29 23:19:21 -04:00
EricKuck eabfc005d0 Preview version bump 2023-08-15 14:50:42 -04:00
EricKuck a90ca5180e Fix issue with child controllers frozen at detach time not re-attaching 2023-08-15 14:30:19 -04:00
EricKuck 4d4d8bfbd1 Fix ConcurrentModificationException when manipulating backstack during instantiation 2023-05-09 11:09:58 -04:00
EricKuck f69fab6062 Merge LifecycleOwner into Controller, add back handling demo 2023-03-24 11:50:37 -04:00
Eric Kuck 590debf975 Conductor 4.0-preview-1 (#686) 2023-03-22 11:26:52 -05:00
59 changed files with 552 additions and 1132 deletions
+4 -4
View File
@@ -8,11 +8,11 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: set up JDK 11
- name: set up JDK 17
uses: actions/setup-java@v2
with:
distribution: 'adopt'
java-version: '11'
java-version: '17'
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Test with Gradle
@@ -24,11 +24,11 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: set up JDK 11
- name: set up JDK 17
uses: actions/setup-java@v2
with:
distribution: 'adopt'
java-version: '11'
java-version: '17'
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Release with Gradle
+1 -7
View File
@@ -36,16 +36,10 @@ implementation "com.bluelinelabs:conductor-viewpager:$conductorVersion"
// ViewPager2 Adapter:
implementation "com.bluelinelabs:conductor-viewpager2:$conductorVersion"
// RxJava2 Autodispose support:
implementation "com.bluelinelabs:conductor-autodispose:$conductorVersion"
// Lifecycle-aware Controllers (architecture components):
implementation "com.bluelinelabs:conductor-archlifecycle:$conductorVersion"
```
### 4.0 Preview
Use `4.0.0-preview-1` as your version number in any of the dependencies above.
Use `4.0.0-preview-4` as your version number in any of the dependencies above.
### SNAPSHOT
Use `4.0.0-SNAPSHOT` as your version number in any of the dependencies above and add the url to the snapshot repository:
@@ -4,7 +4,12 @@ plugins {
}
android {
compileSdkVersion libs.versions.compilesdk.get() as Integer
compileSdk libs.versions.compilesdk.get() as Integer
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
defaultConfig {
minSdkVersion libs.versions.minsdk.get()
@@ -12,6 +17,8 @@ android {
versionCode Integer.parseInt(project.VERSION_CODE)
versionName project.VERSION_NAME
}
namespace "com.bluelinelabs.conductor.androidxtransition"
}
dependencies {
@@ -1,3 +0,0 @@
<manifest package="com.bluelinelabs.conductor.androidxtransition">
<application />
</manifest>
@@ -1,23 +0,0 @@
plugins {
id("com.android.library")
alias(libs.plugins.mvnpublish)
}
android {
compileSdkVersion libs.versions.compilesdk.get() as Integer
defaultConfig {
minSdkVersion libs.versions.minsdk.get()
targetSdkVersion libs.versions.targetsdk.get()
versionCode Integer.parseInt(project.VERSION_CODE)
versionName project.VERSION_NAME
}
}
dependencies {
implementation project(':conductor')
api libs.androidx.lifecycle.runtime
}
ext.artifactId = 'conductor-arch-components-lifecycle'
@@ -1,3 +0,0 @@
POM_NAME=Conductor Architecture Components Lifecycle Extensions
POM_ARTIFACT_ID=conductor-archlifecycle
POM_PACKAGING=aar
@@ -1,3 +0,0 @@
<manifest package="com.bluelinelabs.conductor.archlifecycle">
<application />
</manifest>
@@ -1,68 +0,0 @@
package com.bluelinelabs.conductor.archlifecycle;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.Lifecycle.Event;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LifecycleRegistry;
import android.content.Context;
import androidx.annotation.NonNull;
import android.view.View;
import com.bluelinelabs.conductor.Controller;
import com.bluelinelabs.conductor.Controller.LifecycleListener;
public class ControllerLifecycleOwner implements LifecycleOwner {
private final LifecycleRegistry lifecycleRegistry;
public <T extends Controller & LifecycleOwner> ControllerLifecycleOwner(@NonNull T lifecycleController) {
lifecycleRegistry = new LifecycleRegistry(lifecycleController); // --> State.INITIALIZED
lifecycleController.addLifecycleListener(new LifecycleListener() {
@Override
public void postContextAvailable(@NonNull Controller controller, @NonNull Context context) {
lifecycleRegistry.handleLifecycleEvent(Event.ON_CREATE); // --> State.CREATED;
}
@Override
public void postCreateView(@NonNull Controller controller, @NonNull View view) {
lifecycleRegistry.handleLifecycleEvent(Event.ON_START); // --> State.STARTED;
}
@Override
public void postAttach(@NonNull Controller controller, @NonNull View view) {
lifecycleRegistry.handleLifecycleEvent(Event.ON_RESUME); // --> State.RESUMED;
}
@Override
public void preDetach(@NonNull Controller controller, @NonNull View view) {
lifecycleRegistry.handleLifecycleEvent(Event.ON_PAUSE); // --> State.STARTED;
}
@Override
public void preDestroyView(@NonNull Controller controller, @NonNull View view) {
lifecycleRegistry.handleLifecycleEvent(Event.ON_STOP); // --> State.CREATED;
}
@Override
public void preContextUnavailable(@NonNull Controller controller, @NonNull Context context) {
// do nothing
}
@Override
public void preDestroy(@NonNull Controller controller) {
// Only act on Controllers that have had at least the onContextAvailable call made on them.
if (lifecycleRegistry.getCurrentState() != Lifecycle.State.INITIALIZED) {
lifecycleRegistry.handleLifecycleEvent(Event.ON_DESTROY); // --> State.DESTROYED;
}
}
});
}
@Override @NonNull
public Lifecycle getLifecycle() {
return lifecycleRegistry;
}
}
@@ -1,28 +0,0 @@
package com.bluelinelabs.conductor.archlifecycle;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.bluelinelabs.conductor.Controller;
public abstract class LifecycleController extends Controller implements LifecycleOwner {
private final ControllerLifecycleOwner lifecycleOwner = new ControllerLifecycleOwner(this);
public LifecycleController() {
super();
}
public LifecycleController(@Nullable Bundle args) {
super(args);
}
@Override @NonNull
public Lifecycle getLifecycle() {
return lifecycleOwner.getLifecycle();
}
}
@@ -1,25 +0,0 @@
plugins {
id("com.android.library")
alias(libs.plugins.mvnpublish)
}
android {
compileSdkVersion libs.versions.compilesdk.get() as Integer
defaultConfig {
minSdkVersion libs.versions.minsdk.get()
targetSdkVersion libs.versions.targetsdk.get()
versionCode Integer.parseInt(project.VERSION_CODE)
versionName project.VERSION_NAME
}
}
dependencies {
api libs.rxjava2
api libs.autodispose
api libs.autodispose.lifecycle
implementation project(':conductor')
}
ext.artifactId = 'conductor-autodispose'
@@ -1,3 +0,0 @@
POM_NAME=Conductor AutoDispose Extensions
POM_ARTIFACT_ID=conductor-autodispose
POM_PACKAGING=aar
@@ -1,3 +0,0 @@
<manifest package="com.bluelinelabs.conductor.autodispose">
<application />
</manifest>
@@ -1,14 +0,0 @@
package com.bluelinelabs.conductor.autodispose;
public enum ControllerEvent {
CREATE,
CONTEXT_AVAILABLE,
CREATE_VIEW,
ATTACH,
DETACH,
DESTROY_VIEW,
CONTEXT_UNAVAILABLE,
DESTROY
}
@@ -1,71 +0,0 @@
package com.bluelinelabs.conductor.autodispose;
import android.content.Context;
import androidx.annotation.NonNull;
import android.view.View;
import com.bluelinelabs.conductor.Controller;
import com.uber.autodispose.OutsideScopeException;
import io.reactivex.subjects.BehaviorSubject;
public class ControllerLifecycleSubjectHelper {
private ControllerLifecycleSubjectHelper() { }
@NonNull
public static BehaviorSubject<ControllerEvent> create(@NonNull Controller controller) {
ControllerEvent initialState;
if (controller.isBeingDestroyed() || controller.isDestroyed()) {
throw new OutsideScopeException("Cannot bind to Controller lifecycle when outside of it.");
} else if (controller.isAttached()) {
initialState = ControllerEvent.ATTACH;
} else if (controller.getView() != null) {
initialState = ControllerEvent.CREATE_VIEW;
} else if (controller.getActivity() != null) {
initialState = ControllerEvent.CONTEXT_AVAILABLE;
} else {
initialState = ControllerEvent.CREATE;
}
final BehaviorSubject<ControllerEvent> subject = BehaviorSubject.createDefault(initialState);
controller.addLifecycleListener(new Controller.LifecycleListener() {
@Override
public void preContextAvailable(@NonNull Controller controller) {
subject.onNext(ControllerEvent.CONTEXT_AVAILABLE);
}
@Override
public void preCreateView(@NonNull Controller controller) {
subject.onNext(ControllerEvent.CREATE_VIEW);
}
@Override
public void preAttach(@NonNull Controller controller, @NonNull View view) {
subject.onNext(ControllerEvent.ATTACH);
}
@Override
public void preDetach(@NonNull Controller controller, @NonNull View view) {
subject.onNext(ControllerEvent.DETACH);
}
@Override
public void preDestroyView(@NonNull Controller controller, @NonNull View view) {
subject.onNext(ControllerEvent.DESTROY_VIEW);
}
@Override
public void preContextUnavailable(@NonNull Controller controller, @NonNull Context context) {
subject.onNext(ControllerEvent.CONTEXT_UNAVAILABLE);
}
@Override
public void preDestroy(@NonNull Controller controller) {
subject.onNext(ControllerEvent.DESTROY);
}
});
return subject;
}
}
@@ -1,81 +0,0 @@
package com.bluelinelabs.conductor.autodispose;
import androidx.annotation.NonNull;
import com.bluelinelabs.conductor.Controller;
import com.uber.autodispose.OutsideScopeException;
import com.uber.autodispose.lifecycle.LifecycleScopeProvider;
import com.uber.autodispose.lifecycle.LifecycleScopes;
import com.uber.autodispose.lifecycle.CorrespondingEventsFunction;
import io.reactivex.CompletableSource;
import io.reactivex.Observable;
import io.reactivex.subjects.BehaviorSubject;
public class ControllerScopeProvider implements LifecycleScopeProvider<ControllerEvent> {
private static final CorrespondingEventsFunction<ControllerEvent> CORRESPONDING_EVENTS =
new CorrespondingEventsFunction<ControllerEvent>() {
@Override
public ControllerEvent apply(ControllerEvent lastEvent) throws OutsideScopeException {
switch (lastEvent) {
case CREATE:
return ControllerEvent.DESTROY;
case CONTEXT_AVAILABLE:
return ControllerEvent.CONTEXT_UNAVAILABLE;
case CREATE_VIEW:
return ControllerEvent.DESTROY_VIEW;
case ATTACH:
return ControllerEvent.DETACH;
case DETACH:
return ControllerEvent.DESTROY;
default:
throw new OutsideScopeException("Cannot bind to Controller lifecycle when outside of it.");
}
}
};
@NonNull private final BehaviorSubject<ControllerEvent> lifecycleSubject;
@NonNull private final CorrespondingEventsFunction<ControllerEvent> correspondingEventsFunction;
public static ControllerScopeProvider from(@NonNull Controller controller) {
return new ControllerScopeProvider(controller, CORRESPONDING_EVENTS);
}
public static ControllerScopeProvider from(@NonNull Controller controller, @NonNull final ControllerEvent untilEvent) {
return new ControllerScopeProvider(controller, new CorrespondingEventsFunction<ControllerEvent>() {
@Override
public ControllerEvent apply(ControllerEvent controllerEvent) {
return untilEvent;
}
});
}
public static ControllerScopeProvider from(@NonNull Controller controller, @NonNull final CorrespondingEventsFunction<ControllerEvent> correspondingEventsFunction) {
return new ControllerScopeProvider(controller, correspondingEventsFunction);
}
private ControllerScopeProvider(@NonNull Controller controller, @NonNull CorrespondingEventsFunction<ControllerEvent> correspondingEventsFunction) {
lifecycleSubject = ControllerLifecycleSubjectHelper.create(controller);
this.correspondingEventsFunction = correspondingEventsFunction;
}
@Override
public Observable<ControllerEvent> lifecycle() {
return lifecycleSubject.hide();
}
@Override
public CorrespondingEventsFunction<ControllerEvent> correspondingEvents() {
return correspondingEventsFunction;
}
@Override
public ControllerEvent peekLifecycle() {
return lifecycleSubject.getValue();
}
@Override
public CompletableSource requestScope() throws Exception {
return LifecycleScopes.resolveScopeFromLifecycle(this);
}
}
+12 -1
View File
@@ -5,7 +5,16 @@ plugins {
}
android {
compileSdkVersion libs.versions.compilesdk.get() as Integer
compileSdk libs.versions.compilesdk.get() as Integer
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "11"
}
defaultConfig {
minSdkVersion libs.versions.minsdk.get()
@@ -13,6 +22,8 @@ android {
versionCode Integer.parseInt(project.VERSION_CODE)
versionName project.VERSION_NAME
}
namespace "com.bluelinelabs.conductor.viewpager"
}
dependencies {
@@ -1,3 +0,0 @@
<manifest package="com.bluelinelabs.conductor.viewpager">
<application />
</manifest>
+12 -1
View File
@@ -6,7 +6,16 @@ plugins {
}
android {
compileSdkVersion libs.versions.compilesdk.get() as Integer
compileSdk libs.versions.compilesdk.get() as Integer
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "11"
}
defaultConfig {
minSdkVersion libs.versions.minsdk.get()
@@ -20,6 +29,8 @@ android {
includeAndroidResources = true
}
}
namespace "com.bluelinelabs.conductor.viewpager2"
}
dependencies {
@@ -1,3 +0,0 @@
<manifest package="com.bluelinelabs.conductor.viewpager2">
<application />
</manifest>
+12 -1
View File
@@ -6,7 +6,16 @@ plugins {
}
android {
compileSdkVersion libs.versions.compilesdk.get() as Integer
compileSdk libs.versions.compilesdk.get() as Integer
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "11"
}
defaultConfig {
minSdkVersion libs.versions.minsdk.get()
@@ -15,6 +24,8 @@ android {
versionName project.VERSION_NAME
consumerProguardFiles 'proguard-rules.txt'
}
namespace "com.bluelinelabs.conductor"
}
dependencies {
-3
View File
@@ -1,3 +0,0 @@
<manifest package="com.bluelinelabs.conductor">
<application />
</manifest>
@@ -21,6 +21,10 @@ public class ActivityHostedRouter extends Router {
private LifecycleHandler lifecycleHandler;
private final TransactionIndexer transactionIndexer = new TransactionIndexer();
public ActivityHostedRouter() {
popRootControllerMode = PopRootControllerMode.NEVER;
}
public final void setHost(@NonNull LifecycleHandler lifecycleHandler, @NonNull ViewGroup container) {
if (this.lifecycleHandler != lifecycleHandler || this.container != container) {
if (this.container != null && this.container instanceof ControllerChangeListener) {
@@ -16,11 +16,11 @@ internal class Backstack : Iterable<RouterTransaction> {
fun root(): RouterTransaction? = backstack.lastOrNull()
override fun iterator(): MutableIterator<RouterTransaction> {
return backstack.iterator()
}
override fun iterator(): Iterator<RouterTransaction> = backstack.toTypedArray().iterator()
fun reverseIterator(): Iterator<RouterTransaction> = backstack.descendingIterator()
fun reverseIterator(): Iterator<RouterTransaction> = backstack.reversed().iterator()
fun remove(transaction: RouterTransaction) = backstack.remove(transaction)
fun popTo(transaction: RouterTransaction): List<RouterTransaction> {
if (transaction in backstack) {
@@ -99,4 +99,4 @@ internal class Backstack : Iterable<RouterTransaction> {
companion object {
private const val KEY_ENTRIES = "Backstack.entries"
}
}
}
@@ -36,5 +36,6 @@ object Conductor {
return LifecycleHandler.install(activity, allowAndroidXBacking = allowExperimentalAndroidXBacking)
.getRouter(container, savedInstanceState)
.also { it.rebindIfNeeded() }
.setPopRootControllerMode(Router.PopRootControllerMode.NEVER)
}
}
@@ -8,6 +8,7 @@ import android.content.res.Resources;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.Log;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -16,14 +17,19 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import androidx.activity.BackEventCompat;
import androidx.activity.ComponentActivity;
import androidx.activity.OnBackPressedCallback;
import androidx.activity.OnBackPressedDispatcher;
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.LifecycleOwner;
import com.bluelinelabs.conductor.internal.BackGestureControllerView;
import com.bluelinelabs.conductor.internal.BackGestureViewState;
import com.bluelinelabs.conductor.internal.ClassUtils;
import com.bluelinelabs.conductor.internal.ControllerLifecycleOwner;
import com.bluelinelabs.conductor.internal.OwnViewTreeLifecycleAndRegistry;
import com.bluelinelabs.conductor.internal.RouterRequiringFunc;
import com.bluelinelabs.conductor.internal.ViewAttachHandler;
@@ -95,6 +101,9 @@ public abstract class Controller {
private boolean isContextAvailable;
final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(true) {
ControllerChangeHandler.ChangeTransaction popTransaction = null;
BackGestureViewState backGestureViewState = null;
@Override
public void handleOnBackPressed() {
// Root-level routers should have PopRootControllerMode.NEVER, and so should never return false here.
@@ -109,8 +118,120 @@ public abstract class Controller {
}
}
}
@Override
public void handleOnBackStarted(@NonNull BackEventCompat backEvent) {
Log.d("KUCK", "it has begun");
ControllerChangeHandler.ChangeTransaction transaction = getPopTransaction();
if (transaction != null && transaction.changeHandler != null && transaction.changeHandler.getEnableOnBackGestureCallbacks()) {
List<BackGestureControllerView> toViews;
if (transaction.to != null && transaction.to.view == null) {
toViews = toViewsForGesture(transaction, new ArrayList<>());
} else {
toViews = Collections.emptyList();
}
for (int i = toViews.size() - 1; i > 0; i--) {
transaction.container.addView(toViews.get(i).getView());
}
backGestureViewState = new BackGestureViewState(transaction.from.view, toViews);
transaction.changeHandler.handleOnBackStarted(
transaction.container,
getToView(),
backGestureViewState.getFromView(),
backEvent
);
}
}
@Override
public void handleOnBackProgressed(@NonNull BackEventCompat backEvent) {
Log.d("KUCK", "it has progressed");
ControllerChangeHandler.ChangeTransaction transaction = getPopTransaction();
if (transaction != null && transaction.changeHandler != null && transaction.changeHandler.getEnableOnBackGestureCallbacks()) {
transaction.changeHandler.handleOnBackProgressed(
transaction.container,
getToView(),
backGestureViewState.getFromView(),
backEvent
);
}
}
@Override
public void handleOnBackCancelled() {
Log.d("KUCK", "it has canceled");
ControllerChangeHandler.ChangeTransaction transaction = getPopTransaction();
if (transaction != null && transaction.changeHandler != null && transaction.changeHandler.getEnableOnBackGestureCallbacks()) {
transaction.changeHandler.handleOnBackCancelled(
transaction.container,
getToView(),
backGestureViewState.getFromView()
);
for (int i = 0; i < backGestureViewState.getToViews().size(); i++) {
BackGestureControllerView state = backGestureViewState.getToViews().get(i);
ViewGroup parent = (ViewGroup) state.getView().getParent();
if (parent != null) {
parent.removeView(state.getView());
}
if (state.getInflatedForGesture()) {
state.getController().removeViewReference(state.getView().getContext());
}
}
}
backGestureViewState = null;
}
private ControllerChangeHandler.ChangeTransaction getPopTransaction() {
if (popTransaction == null) {
popTransaction = router.popTransaction(Controller.this);
}
return popTransaction;
}
private List<BackGestureControllerView> toViewsForGesture(
ControllerChangeHandler.ChangeTransaction transaction,
List<BackGestureControllerView> aggregator
) {
if (transaction.to != null) {
View view = transaction.to.view;
boolean inflated = false;
if (view == null) {
view = transaction.to.inflate(transaction.container);
inflated = true;
} else if (view.getParent() != null) {
return aggregator;
}
aggregator.add(new BackGestureControllerView(transaction.to, view, inflated));
if (!transaction.changeHandler.getRemovesFromViewOnPush()) {
toViewsForGesture(router.popTransaction(transaction.to), aggregator);
}
}
return aggregator;
}
@Nullable
private View getToView() {
if (backGestureViewState.getToViews().size() == 0) {
return null;
} else {
return backGestureViewState.getToViews().get(0).getView();
}
}
};
public final LifecycleOwner lifecycleOwner = new ControllerLifecycleOwner(this);
@NonNull
static Controller newInstance(@NonNull Bundle bundle) {
final String className = bundle.getString(KEY_CLASS_NAME);
@@ -1383,11 +1504,17 @@ public abstract class Controller {
if (isDetachFrozen != frozen) {
isDetachFrozen = frozen;
boolean detach = !frozen && view != null && viewWasDetached;
for (ControllerHostedRouter router : childRouters) {
if (detach) {
router.prepareForHostDetach();
}
router.setDetachFrozen(frozen);
}
if (!frozen && view != null && viewWasDetached) {
if (detach) {
View aView = view;
detach(view, false, false);
if (view == null && aView.getParent() == router.container) {
@@ -3,6 +3,7 @@ package com.bluelinelabs.conductor
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import androidx.activity.BackEventCompat
import androidx.annotation.RestrictTo
import com.bluelinelabs.conductor.changehandler.SimpleSwapChangeHandler
import com.bluelinelabs.conductor.internal.ClassUtils
@@ -23,10 +24,35 @@ abstract class ControllerChangeHandler {
*/
open val isReusable: Boolean = false
/**
* Returns whether or not this handler removes the `from` view from the container when performing a push.
*
* If this is true:
* - This handler's implementation of [performChange] should remove `from` from `container`
* before calling `changeListener.onChangeCompleted()`
* - When a controller is pushed, the previous controller will be detached and its view will be destroyed
*
* If this is false:
* - This handler's implementation of [performChange] should only remove `from` from `container`
* when `isPush` is false
* - When a controller is pushed, the previous controller will stay attached and its view will remain created
* - When a view is recreated (e.g. after a configuration change), any controllers underneath a transaction
* using this handler will have their view recreated and attached, even though they're not the top-most
* controller
*
* If a controller pushed onto the backstack will completely cover the previous controller,
* using a change handler with [removesFromViewOnPush] true should result in no visual interruption
* to the user, while allowing the previous controller's view to be destroyed to reclaim resources.
* If instead, the previous controller should still be visible after the new controller is pushed,
* using a change handler with [removesFromViewOnPush] false will keep the previous controller's
* view in the view hierarchy, where it can still be seen (and even interacted with).
*/
open val removesFromViewOnPush: Boolean = true
private var hasBeenUsed = false
open val enableOnBackGestureCallbacks = false
init {
try {
javaClass.getConstructor()
@@ -89,9 +115,9 @@ abstract class ControllerChangeHandler {
*/
open fun copy(): ControllerChangeHandler = fromBundle(toBundle())!!
open fun handleOnBackStarted(container: ViewGroup, to: View?, from: View, swipeEdge: Int) {}
open fun handleOnBackStarted(container: ViewGroup, to: View?, from: View, event: BackEventCompat) {}
open fun handleOnBackProgressed(container: ViewGroup, to: View?, from: View, progress: Float, swipeEdge: Int) {}
open fun handleOnBackProgressed(container: ViewGroup, to: View?, from: View, event: BackEventCompat) {}
open fun handleOnBackCancelled(container: ViewGroup, to: View?, from: View) {}
@@ -32,9 +32,12 @@ class ControllerHostedRouter extends Router {
private boolean isDetachFrozen;
private boolean boundToContainer;
ControllerHostedRouter() { }
ControllerHostedRouter() {
popRootControllerMode = PopRootControllerMode.POP_ROOT_CONTROLLER_BUT_NOT_VIEW;
}
ControllerHostedRouter(int hostId, @Nullable String tag, boolean boundToContainer) {
this();
if (!boundToContainer && tag == null) {
throw new IllegalStateException("ControllerHostedRouter can't be created without a tag if not bounded to its container");
}
@@ -47,7 +47,7 @@ public abstract class Router {
private final List<ChangeTransaction> pendingControllerChanges = new ArrayList<>();
final List<Controller> destroyingControllers = new ArrayList<>();
PopRootControllerMode popRootControllerMode = PopRootControllerMode.POP_ROOT_CONTROLLER_BUT_NOT_VIEW;
PopRootControllerMode popRootControllerMode;
boolean onBackPressedDispatcherEnabled;
boolean containerFullyAttached = false;
boolean isActivityStopped = false;
@@ -193,7 +193,7 @@ public abstract class Router {
RouterTransaction transaction = iterator.next();
if (transaction.controller() == controller) {
trackDestroyingController(transaction);
iterator.remove();
backstack.remove(transaction);
removedTransaction = transaction;
} else if (removedTransaction != null) {
if (needsNextTransactionAttach && !transaction.controller().isAttached()) {
@@ -320,7 +320,7 @@ public abstract class Router {
/**
* Sets the method this router will use to handle back presses when there is only one controller left in the backstack.
* Defaults to POP_ROOT_CONTROLLER_LEAVING_VIEW so that the developer can either finish its containing Activity or
* Defaults to POP_ROOT_CONTROLLER_BUT_NOT_VIEW so that the developer can either finish its containing Activity or
* otherwise hide its parent view without any strange artifacting.
*/
@NonNull
@@ -804,6 +804,37 @@ public abstract class Router {
}
}
@Nullable
ChangeTransaction popTransaction(@NonNull Controller controller) {
RouterTransaction from = null;
RouterTransaction to = null;
Iterator<RouterTransaction> iterator = backstack.iterator();
while (iterator.hasNext()) {
RouterTransaction transaction = iterator.next();
if (transaction.controller() == controller) {
from = transaction;
if (iterator.hasNext()) {
to = iterator.next();
}
break;
}
}
if (from == null) {
return null;
}
return new ChangeTransaction(
to != null ? to.controller() : null,
from.controller(),
false,
container,
from.popChangeHandler(),
Collections.emptyList()
);
}
void watchContainerAttach() {
container.post(new Runnable() {
@Override
@@ -1109,7 +1140,7 @@ public abstract class Router {
public enum PopRootControllerMode {
/**
* The Router will not pop the final controller left on the backstack when the back button is pressed
* or when pop events are called. This mode should generally be used for Activity-hosted routers.
* or when pop events are called. This mode is the default for Activity-hosted routers.
*/
NEVER,
/**
@@ -0,0 +1,15 @@
package com.bluelinelabs.conductor.internal
import android.view.View
import com.bluelinelabs.conductor.Controller
class BackGestureViewState(
val fromView: View,
val toViews: List<BackGestureControllerView>,
)
class BackGestureControllerView(
val controller: Controller,
val view: View,
val inflatedForGesture: Boolean,
)
@@ -0,0 +1,53 @@
package com.bluelinelabs.conductor.internal
import android.content.Context
import android.view.View
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
import com.bluelinelabs.conductor.Controller
import com.bluelinelabs.conductor.Controller.LifecycleListener
class ControllerLifecycleOwner(lifecycleController: Controller) : LifecycleOwner {
private val lifecycleRegistry: LifecycleRegistry = LifecycleRegistry(this) // --> State.INITIALIZED
override val lifecycle: Lifecycle
get() = lifecycleRegistry
init {
lifecycleController.addLifecycleListener(
object : LifecycleListener() {
override fun postContextAvailable(controller: Controller, context: Context) {
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE) // --> State.CREATED;
}
override fun postCreateView(controller: Controller, view: View) {
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START) // --> State.STARTED;
}
override fun postAttach(controller: Controller, view: View) {
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME) // --> State.RESUMED;
}
override fun preDetach(controller: Controller, view: View) {
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE) // --> State.STARTED;
}
override fun preDestroyView(controller: Controller, view: View) {
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP) // --> State.CREATED;
}
override fun preContextUnavailable(controller: Controller, context: Context) {
// do nothing
}
override fun preDestroy(controller: Controller) {
// Only act on Controllers that have had at least the onContextAvailable call made on them.
if (lifecycleRegistry.currentState != Lifecycle.State.INITIALIZED) {
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY) // --> State.DESTROYED;
}
}
},
)
}
}
@@ -14,7 +14,6 @@ import androidx.savedstate.setViewTreeSavedStateRegistryOwner
import com.bluelinelabs.conductor.Controller
import com.bluelinelabs.conductor.ControllerChangeHandler
import com.bluelinelabs.conductor.ControllerChangeType
import com.bluelinelabs.conductor.R
/**
* This class sets the [ViewTreeLifecycleOwner] and [ViewTreeSavedStateRegistryOwner] which is
@@ -56,8 +55,8 @@ internal class OwnViewTreeLifecycleAndRegistry private constructor(
* it on purpose.
*/
if (
view.getTag(R.id.view_tree_lifecycle_owner) == null &&
view.getTag(R.id.view_tree_saved_state_registry_owner) == null
view.getTag(androidx.lifecycle.runtime.R.id.view_tree_lifecycle_owner) == null &&
view.getTag(androidx.savedstate.R.id.view_tree_saved_state_registry_owner) == null
) {
view.setViewTreeLifecycleOwner(this@OwnViewTreeLifecycleAndRegistry)
view.setViewTreeSavedStateRegistryOwner(this@OwnViewTreeLifecycleAndRegistry)
@@ -343,6 +343,8 @@ class RouterTests {
@Test
fun testRearrangeTransactionBackstack() {
router.setPopRootControllerMode(Router.PopRootControllerMode.POP_ROOT_CONTROLLER_AND_VIEW)
val transaction1 = TestController().asTransaction()
val transaction2 = TestController().asTransaction()
var backstack = listOf(transaction1, transaction2)
@@ -433,4 +435,4 @@ class RouterTests {
Assert.assertFalse(controller1.isBeingDestroyed())
Assert.assertTrue(controller3.isBeingDestroyed())
}
}
}
+6 -8
View File
@@ -27,20 +27,22 @@ android {
compose = true
}
compileSdkVersion 33
compileSdk 34
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "1.8"
jvmTarget = "11"
}
composeOptions {
kotlinCompilerExtensionVersion libs.versions.compose.compiler.get()
}
namespace "com.bluelinelabs.conductor.demo"
}
dependencies {
@@ -51,13 +53,9 @@ dependencies {
implementation libs.picasso
implementation libs.autodispose.ktx
implementation project(':conductor')
implementation project(':conductor-modules:viewpager')
implementation project(':conductor-modules:viewpager2')
implementation project(':conductor-modules:autodispose')
implementation project(':conductor-modules:arch-components-lifecycle')
implementation project(':conductor-modules:androidx-transition')
implementation libs.compose.ui
+23 -23
View File
@@ -1,31 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.bluelinelabs.conductor.demo">
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:fullBackupContent="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
<application
android:allowBackup="true"
android:enableOnBackInvokedCallback="true"
android:fullBackupContent="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
<activity
android:name="com.bluelinelabs.conductor.demo.MainActivity"
android:exported="true"
android:label="@string/app_name"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.bluelinelabs.conductor.demo.MainActivity"
android:exported="true"
android:label="@string/app_name"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</application>
</manifest>
@@ -1,110 +0,0 @@
package com.bluelinelabs.conductor.demo.controllers
import android.content.Context
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.OnLifecycleEvent
import com.bluelinelabs.conductor.RouterTransaction
import com.bluelinelabs.conductor.archlifecycle.LifecycleController
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
import com.bluelinelabs.conductor.demo.R
import com.bluelinelabs.conductor.demo.ToolbarProvider
import com.bluelinelabs.conductor.demo.controllers.base.watchForLeaks
import com.bluelinelabs.conductor.demo.databinding.ControllerLifecycleBinding
class ArchLifecycleController : LifecycleController() {
init {
Log.i(TAG, "Conductor: Constructor called")
watchForLeaks()
lifecycle.addObserver(object : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_ANY)
fun onLifecycleEvent(source: LifecycleOwner, event: Lifecycle.Event) {
Log.d(
TAG,
"Lifecycle: " + source.javaClass.simpleName + " emitted event " + event + " and is now in state " + source.lifecycle.currentState
)
}
})
Log.d(TAG, "Lifecycle: " + javaClass.simpleName + " is now in state " + lifecycle.currentState)
}
override fun onContextAvailable(context: Context) {
Log.i(TAG, "Conductor: onContextAvailable() called")
super.onContextAvailable(context)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup,
savedViewState: Bundle?
): View {
Log.i(TAG, "Conductor: onCreateView() called")
val binding = ControllerLifecycleBinding.inflate(inflater, container, false)
binding.root.setBackgroundColor(ContextCompat.getColor(container.context, R.color.orange_300))
binding.title.text = binding.root.resources.getString(R.string.rxlifecycle_title, TAG)
binding.nextReleaseView.setOnClickListener {
retainViewMode = RetainViewMode.RELEASE_DETACH
router.pushController(
RouterTransaction.with(TextController("Logcat should now report that the Controller's onDetach() and LifecycleObserver's onPause() methods were called, followed by the Controller's onDestroyView() and LifecycleObserver's onStop()."))
.pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler())
)
}
binding.nextRetainView.setOnClickListener {
retainViewMode = RetainViewMode.RETAIN_DETACH
router.pushController(
RouterTransaction.with(TextController("Logcat should now report that the Controller's onDetach() and LifecycleObserver's onPause() methods were called."))
.pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler())
)
}
return binding.root
}
override fun onAttach(view: View) {
Log.i(TAG, "Conductor: onAttach() called")
super.onAttach(view)
(activity as ToolbarProvider).toolbar.title = "Arch Components Lifecycle Demo"
}
override fun onDetach(view: View) {
Log.i(TAG, "Conductor: onDetach() called")
super.onDetach(view)
}
override fun onDestroyView(view: View) {
Log.i(TAG, "Conductor: onDestroyView() called")
super.onDestroyView(view)
}
override fun onContextUnavailable() {
Log.i(TAG, "Conductor: onContextUnavailable() called")
super.onContextUnavailable()
}
override fun onDestroy() {
Log.i(TAG, "Conductor: onDestroy() called")
}
companion object {
private const val TAG = "ArchLifecycleController"
}
}
@@ -1,104 +0,0 @@
package com.bluelinelabs.conductor.demo.controllers
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.bluelinelabs.conductor.Controller
import com.bluelinelabs.conductor.RouterTransaction.Companion.with
import com.bluelinelabs.conductor.autodispose.ControllerScopeProvider
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
import com.bluelinelabs.conductor.demo.R
import com.bluelinelabs.conductor.demo.ToolbarProvider
import com.bluelinelabs.conductor.demo.controllers.base.watchForLeaks
import com.bluelinelabs.conductor.demo.databinding.ControllerLifecycleBinding
import com.uber.autodispose.autoDisposable
import io.reactivex.Observable
import java.util.concurrent.TimeUnit
// Shamelessly borrowed from the official RxLifecycle demo by Trello and adapted for Conductor Controllers
// instead of Activities or Fragments.
class AutodisposeController : Controller() {
private val scopeProvider = ControllerScopeProvider.from(this)
init {
watchForLeaks()
Observable.interval(1, TimeUnit.SECONDS)
.doOnDispose { Log.i(TAG, "Disposing from constructor") }
.autoDisposable(scopeProvider)
.subscribe { num: Long ->
Log.i(TAG, "Started in constructor, running until onDestroy(): $num")
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup,
savedViewState: Bundle?
): View {
Log.i(TAG, "onCreateView() called")
val binding = ControllerLifecycleBinding.inflate(inflater, container, false)
binding.title.text = binding.root.resources.getString(R.string.rxlifecycle_title, TAG)
binding.nextReleaseView.setOnClickListener {
retainViewMode = RetainViewMode.RELEASE_DETACH
router.pushController(
with(TextController("Logcat should now report that the observables from onAttach() and onViewBound() have been disposed of, while the constructor observable is still running."))
.pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler())
)
}
binding.nextRetainView.setOnClickListener {
retainViewMode = RetainViewMode.RETAIN_DETACH
router.pushController(
with(TextController("Logcat should now report that the observables from onAttach() has been disposed of, while the constructor and onViewBound() observables are still running."))
.pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler())
)
}
Observable.interval(1, TimeUnit.SECONDS)
.doOnDispose { Log.i(TAG, "Disposing from onCreateView()") }
.autoDisposable(scopeProvider)
.subscribe { num: Long ->
Log.i(TAG, "Started in onCreateView(), running until onDestroyView(): $num")
}
return binding.root
}
override fun onAttach(view: View) {
super.onAttach(view)
Log.i(TAG, "onAttach() called")
(activity as ToolbarProvider).toolbar.title = "Autodispose Demo"
Observable.interval(1, TimeUnit.SECONDS)
.doOnDispose { Log.i(TAG, "Disposing from onAttach()") }
.autoDisposable(scopeProvider)
.subscribe { num: Long ->
Log.i(TAG, "Started in onAttach(), running until onDetach(): $num")
}
}
override fun onDestroyView(view: View) {
super.onDestroyView(view)
Log.i(TAG, "onDestroyView() called")
}
override fun onDetach(view: View) {
super.onDetach(view)
Log.i(TAG, "onDetach() called")
}
public override fun onDestroy() {
super.onDestroy()
Log.i(TAG, "onDestroy() called")
}
companion object {
private const val TAG = "AutodisposeController"
}
}
@@ -1,121 +0,0 @@
package com.bluelinelabs.conductor.demo.controllers
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.DrawableRes
import androidx.core.os.bundleOf
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
import com.bluelinelabs.conductor.demo.R
import com.bluelinelabs.conductor.demo.controllers.base.BaseController
import com.bluelinelabs.conductor.demo.databinding.ControllerCityDetailBinding
import com.bluelinelabs.conductor.demo.databinding.RowCityDetailBinding
import com.bluelinelabs.conductor.demo.databinding.RowCityHeaderBinding
import com.bluelinelabs.conductor.demo.util.viewBinding
class CityDetailController(args: Bundle) : BaseController(R.layout.controller_city_detail, args) {
private val binding: ControllerCityDetailBinding by viewBinding(ControllerCityDetailBinding::bind)
override val title = args.getString(KEY_TITLE)!!
constructor(@DrawableRes image: Int, title: String) : this(
bundleOf(
KEY_TITLE to title,
KEY_IMAGE to image
)
)
override fun onViewCreated(view: View) {
binding.recyclerView.setHasFixedSize(true)
binding.recyclerView.layoutManager = LinearLayoutManager(view.context)
binding.recyclerView.adapter = CityDetailAdapter(
inflater = LayoutInflater.from(view.context),
title = title,
imageDrawableRes = args.getInt(KEY_IMAGE),
details = LIST_ROWS,
transitionNameBase = title
)
}
companion object {
private const val KEY_TITLE = "CityDetailController.title"
private const val KEY_IMAGE = "CityDetailController.image"
private val LIST_ROWS = listOf(
"• This is a city.",
"• There's some cool stuff about it.",
"• But really this is just a demo, not a city guide app.",
"• This demo is meant to show some nice transitions.",
"• You should have seen some sweet shared element transitions using the ImageView and the TextView in the \"header\" above.",
"• This transition utilized some callbacks to ensure all the necessary rows in the RecyclerView were laid about before the transition occurred.",
"• Just adding some more lines so it scrolls now...\n\n\n\n\n\n\nThe end."
)
}
class CityDetailAdapter(
private val inflater: LayoutInflater,
private val title: String,
@DrawableRes private val imageDrawableRes: Int,
private val details: List<String>,
private val transitionNameBase: String
) : RecyclerView.Adapter<ViewHolder<*>>() {
override fun getItemViewType(position: Int): Int {
return if (position == 0) VIEW_TYPE_HEADER else VIEW_TYPE_DETAIL
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder<*> {
return if (viewType == VIEW_TYPE_HEADER) {
HeaderViewHolder(RowCityHeaderBinding.inflate(inflater, parent, false))
} else {
DetailViewHolder(RowCityDetailBinding.inflate(inflater, parent, false))
}
}
override fun onBindViewHolder(holder: ViewHolder<*>, position: Int) {
when (holder) {
is HeaderViewHolder -> {
holder.bind(
imageDrawableRes = imageDrawableRes,
title = title,
imageTransitionName = inflater.context.resources.getString(R.string.transition_tag_image_named, transitionNameBase),
textViewTransitionName = inflater.context.resources.getString(R.string.transition_tag_title_named, transitionNameBase)
)
}
is DetailViewHolder -> {
holder.bind(details[position - 1])
}
else -> {
throw IllegalStateException("Invalid view holder class ${holder.javaClass.canonicalName}")
}
}
}
override fun getItemCount() = details.size + 1
companion object {
private const val VIEW_TYPE_HEADER = 0
private const val VIEW_TYPE_DETAIL = 1
}
}
open class ViewHolder<Binding : ViewBinding>(binding: Binding) : RecyclerView.ViewHolder(binding.root)
class HeaderViewHolder(private val binding: RowCityHeaderBinding) : ViewHolder<RowCityHeaderBinding>(binding) {
fun bind(@DrawableRes imageDrawableRes: Int, title: String?, imageTransitionName: String?, textViewTransitionName: String?) {
binding.imageView.setImageResource(imageDrawableRes)
binding.textView.text = title
binding.imageView.transitionName = imageTransitionName
binding.textView.transitionName = textViewTransitionName
}
}
class DetailViewHolder(private val binding: RowCityDetailBinding) : ViewHolder<RowCityDetailBinding>(binding) {
fun bind(detail: String) {
binding.textView.text = detail
}
}
}
@@ -1,112 +0,0 @@
package com.bluelinelabs.conductor.demo.controllers
import android.graphics.PorterDuff
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.DrawableRes
import androidx.core.content.ContextCompat
import androidx.core.os.bundleOf
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.bluelinelabs.conductor.RouterTransaction
import com.bluelinelabs.conductor.demo.R
import com.bluelinelabs.conductor.demo.changehandler.CityGridSharedElementTransitionChangeHandler
import com.bluelinelabs.conductor.demo.controllers.base.BaseController
import com.bluelinelabs.conductor.demo.databinding.ControllerCityGridBinding
import com.bluelinelabs.conductor.demo.databinding.RowCityGridBinding
import com.bluelinelabs.conductor.demo.util.viewBinding
class CityGridController(args: Bundle) : BaseController(R.layout.controller_city_grid, args) {
private val binding: ControllerCityGridBinding by viewBinding(ControllerCityGridBinding::bind)
override val title = "Shared Element Demos"
constructor(title: String?, dotColor: Int, fromPosition: Int) : this(
bundleOf(
KEY_TITLE to title,
KEY_DOT_COLOR to dotColor,
KEY_FROM_POSITION to fromPosition
)
)
override fun onViewCreated(view: View) {
super.onViewCreated(view)
binding.title.text = args.getString(KEY_TITLE)!!
binding.dot.drawable.setColorFilter(ContextCompat.getColor(view.context, args.getInt(KEY_DOT_COLOR)), PorterDuff.Mode.SRC_ATOP)
binding.title.transitionName = view.resources.getString(R.string.transition_tag_title_indexed, args.getInt(KEY_FROM_POSITION))
binding.dot.transitionName = view.resources.getString(R.string.transition_tag_dot_indexed, args.getInt(KEY_FROM_POSITION))
binding.recyclerView.setHasFixedSize(true)
binding.recyclerView.layoutManager = GridLayoutManager(view.context, 2)
binding.recyclerView.adapter = CityGridAdapter(LayoutInflater.from(view.context), CITY_MODELS, ::onModelRowClick)
}
private fun onModelRowClick(model: CityModel) {
val names = listOf(
resources!!.getString(R.string.transition_tag_title_named, model.title),
resources!!.getString(R.string.transition_tag_image_named, model.title)
)
router.pushController(
RouterTransaction.with(CityDetailController(model.drawableRes, model.title))
.pushChangeHandler(CityGridSharedElementTransitionChangeHandler(names))
.popChangeHandler(CityGridSharedElementTransitionChangeHandler(names))
)
}
companion object {
private const val KEY_TITLE = "CityGridController.title"
private const val KEY_DOT_COLOR = "CityGridController.dotColor"
private const val KEY_FROM_POSITION = "CityGridController.position"
private val CITY_MODELS = arrayOf(
CityModel(R.drawable.chicago, "Chicago"),
CityModel(R.drawable.jakarta, "Jakarta"),
CityModel(R.drawable.london, "London"),
CityModel(R.drawable.sao_paulo, "Sao Paulo"),
CityModel(R.drawable.tokyo, "Tokyo")
)
}
}
data class CityModel(@DrawableRes val drawableRes: Int, val title: String)
private class CityGridAdapter(
private val inflater: LayoutInflater,
private val items: Array<CityModel>,
private val modelClickListener: (CityModel) -> Unit
) : RecyclerView.Adapter<CityGridAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
RowCityGridBinding.inflate(inflater, parent, false),
modelClickListener
)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(items[position])
}
override fun getItemCount() = items.size
class ViewHolder(
private val binding: RowCityGridBinding,
private val modelClickListener: (CityModel) -> Unit
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: CityModel) {
binding.image.setImageResource(item.drawableRes)
binding.title.text = item.title
binding.image.transitionName = itemView.resources.getString(R.string.transition_tag_image_named, item.title)
binding.image.transitionName = itemView.resources.getString(R.string.transition_tag_title_named, item.title)
itemView.setOnClickListener { modelClickListener(item) }
}
}
}
@@ -1,89 +0,0 @@
package com.bluelinelabs.conductor.demo.controllers
import android.graphics.PorterDuff
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.ColorRes
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.bluelinelabs.conductor.RouterTransaction.Companion.with
import com.bluelinelabs.conductor.changehandler.FadeChangeHandler
import com.bluelinelabs.conductor.demo.R
import com.bluelinelabs.conductor.demo.controllers.base.BaseController
import com.bluelinelabs.conductor.demo.databinding.ControllerAdditionalModulesBinding
import com.bluelinelabs.conductor.demo.databinding.RowHomeBinding
import com.bluelinelabs.conductor.demo.util.viewBinding
class ExternalModulesController : BaseController(R.layout.controller_additional_modules) {
private val binding: ControllerAdditionalModulesBinding by viewBinding(
ControllerAdditionalModulesBinding::bind
)
override val title = "External Module Demos"
override fun onViewCreated(view: View) {
super.onViewCreated(view)
binding.recyclerView.setHasFixedSize(true)
binding.recyclerView.layoutManager = LinearLayoutManager(view.context)
binding.recyclerView.adapter = AdditionalModulesAdapter(
LayoutInflater.from(view.context),
ModuleModel.values(),
::onModelRowClick
)
}
private fun onModelRowClick(model: ModuleModel) {
when (model) {
ModuleModel.AUTODISPOSE -> router.pushController(
with(AutodisposeController())
.pushChangeHandler(FadeChangeHandler())
.popChangeHandler(FadeChangeHandler())
)
ModuleModel.ARCH_LIFECYCLE -> router.pushController(
with(ArchLifecycleController())
.pushChangeHandler(FadeChangeHandler())
.popChangeHandler(FadeChangeHandler())
)
}
}
}
private enum class ModuleModel(val title: String, @ColorRes val color: Int) {
AUTODISPOSE("Autodispose", R.color.purple_300),
ARCH_LIFECYCLE("Arch Components Lifecycle", R.color.orange_300);
}
private class AdditionalModulesAdapter(
private val inflater: LayoutInflater,
private val items: Array<ModuleModel>,
private val modelClickListener: (ModuleModel) -> Unit
) : RecyclerView.Adapter<AdditionalModulesAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(RowHomeBinding.inflate(inflater, parent, false), modelClickListener)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(items[position])
}
override fun getItemCount(): Int {
return items.size
}
class ViewHolder(
private val binding: RowHomeBinding,
private val modelClickListener: (ModuleModel) -> Unit
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: ModuleModel) {
binding.title.text = item.title
binding.dot.drawable.setColorFilter(
ContextCompat.getColor(itemView.context, item.color),
PorterDuff.Mode.SRC_ATOP
)
itemView.setOnClickListener { modelClickListener(item) }
}
}
}
@@ -10,6 +10,7 @@ import android.text.style.URLSpan
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.BackEventCompat
import androidx.annotation.ColorRes
import androidx.appcompat.widget.Toolbar
import androidx.core.content.ContextCompat
@@ -18,12 +19,11 @@ import androidx.core.graphics.BlendModeCompat
import androidx.core.text.buildSpannedString
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.bluelinelabs.conductor.ControllerChangeHandler
import com.bluelinelabs.conductor.RouterTransaction
import com.bluelinelabs.conductor.asTransaction
import com.bluelinelabs.conductor.changehandler.FadeChangeHandler
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
import com.bluelinelabs.conductor.demo.R
import com.bluelinelabs.conductor.demo.changehandler.ArcFadeMoveChangeHandler
import com.bluelinelabs.conductor.demo.changehandler.FabToDialogTransitionChangeHandler
import com.bluelinelabs.conductor.demo.controllers.NavigationDemoController.DisplayUpMode
import com.bluelinelabs.conductor.demo.controllers.base.BaseController
@@ -78,12 +78,13 @@ class HomeController : BaseController(R.layout.controller_home) {
}
private fun onModelRowClick(model: DemoModel, position: Int) {
println("onModelRowClick")
when (model) {
DemoModel.NAVIGATION -> {
router.pushController(
RouterTransaction.with(NavigationDemoController(0, DisplayUpMode.SHOW_FOR_CHILDREN_ONLY))
.pushChangeHandler(FadeChangeHandler())
.popChangeHandler(FadeChangeHandler())
.pushChangeHandler(WhateverChangeHandler())
.popChangeHandler(WhateverChangeHandler())
.tag(NavigationDemoController.TAG_UP_TRANSACTION)
)
}
@@ -118,25 +119,11 @@ class HomeController : BaseController(R.layout.controller_home) {
.popChangeHandler(FadeChangeHandler())
)
}
DemoModel.SHARED_ELEMENT_TRANSITIONS -> {
val titleSharedElementName =
resources!!.getString(R.string.transition_tag_title_indexed, position)
val dotSharedElementName =
resources!!.getString(R.string.transition_tag_dot_indexed, position)
DemoModel.ON_BACK_PRESSED_CALLBACK -> {
router.pushController(
RouterTransaction.with(CityGridController(model.title, model.color, position))
.pushChangeHandler(
ArcFadeMoveChangeHandler(
titleSharedElementName,
dotSharedElementName
)
)
.popChangeHandler(
ArcFadeMoveChangeHandler(
titleSharedElementName,
dotSharedElementName
)
)
RouterTransaction.with(OnBackPressedCallbackController())
.pushChangeHandler(FadeChangeHandler())
.popChangeHandler(FadeChangeHandler())
)
}
DemoModel.DRAG_DISMISS -> {
@@ -146,13 +133,6 @@ class HomeController : BaseController(R.layout.controller_home) {
.popChangeHandler(FadeChangeHandler())
)
}
DemoModel.EXTERNAL_MODULES -> {
router.pushController(
RouterTransaction.with(ExternalModulesController())
.pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler())
)
}
DemoModel.MASTER_DETAIL -> {
router.pushController(
RouterTransaction.with(MasterDetailListController())
@@ -208,14 +188,13 @@ private enum class DemoModel(val title: String, @ColorRes val color: Int) {
COMPOSE("Jetpack Compose", R.color.amber_500),
NAVIGATION("Navigation Demos", R.color.red_300),
TRANSITIONS("Transition Demos", R.color.blue_grey_300),
SHARED_ELEMENT_TRANSITIONS("Shared Element Demos", R.color.purple_300),
ON_BACK_PRESSED_CALLBACK("Back Handling", R.color.purple_300),
CHILD_CONTROLLERS("Child Controllers", R.color.orange_300),
VIEW_PAGER("ViewPager", R.color.green_300),
VIEW_PAGER_2("ViewPager2", R.color.pink_300),
TARGET_CONTROLLER("Target Controller", R.color.deep_orange_300),
MASTER_DETAIL("Master Detail", R.color.lime_300),
DRAG_DISMISS("Drag Dismiss", R.color.teal_300),
EXTERNAL_MODULES("Bonus Modules", R.color.deep_purple_300)
}
private class HomeAdapter(
@@ -257,3 +236,47 @@ private class HomeAdapter(
}
}
}
class WhateverChangeHandler : ControllerChangeHandler() {
override val enableOnBackGestureCallbacks = true
override fun handleOnBackStarted(container: ViewGroup, to: View?, from: View, event: BackEventCompat) {
println("handleOnBackStarted")
to?.let { container.addView(it) }
}
override fun handleOnBackProgressed(container: ViewGroup, to: View?, from: View, event: BackEventCompat) {
if (event.swipeEdge == BackEventCompat.EDGE_LEFT) {
from.translationX = event.progress * container.width
to?.translationX = -container.width + event.progress * container.width
} else {
from.translationX = -event.progress * container.width
to?.translationX = container.width + -event.progress * container.width
}
}
override fun handleOnBackCancelled(container: ViewGroup, to: View?, from: View) {
to?.let { container.removeView(it) }
}
override fun performChange(
container: ViewGroup,
from: View?,
to: View?,
isPush: Boolean,
changeListener: ControllerChangeCompletedListener,
) {
from?.translationX = 0f
to?.translationX = 0f
println("perform change called")
if (isPush) {
to?.let { container.addView(it) }
from?.let { container.removeView(it) }
} else {
to?.let { if (it.parent == null) container.addView(it) }
from?.let { container.removeView(it) }
}
changeListener.onChangeCompleted()
}
}
@@ -20,7 +20,6 @@ class MultipleChildRouterController : BaseController(R.layout.controller_multipl
childContainers.forEach { container ->
val childRouter = getChildRouter(container).setPopRootControllerMode(PopRootControllerMode.POP_ROOT_CONTROLLER_BUT_NOT_VIEW)
.setPopRootControllerMode(PopRootControllerMode.NEVER)
if (!childRouter.hasRootController()) {
childRouter.setRoot(RouterTransaction.with(NavigationDemoController(0, NavigationDemoController.DisplayUpMode.HIDE)))
}
@@ -0,0 +1,117 @@
package com.bluelinelabs.conductor.demo.controllers
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.selection.selectable
import androidx.compose.material.MaterialTheme
import androidx.compose.material.RadioButton
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.unit.dp
import com.bluelinelabs.conductor.Controller
import com.bluelinelabs.conductor.demo.ToolbarProvider
class OnBackPressedCallbackController : Controller() {
private val onBackPressedCallback = object : OnBackPressedCallback(false) {
override fun handleOnBackPressed() {
Toast.makeText(activity!!, "Back handled at the Controller level", Toast.LENGTH_SHORT).show()
}
}
override fun onContextAvailable(context: Context) {
super.onContextAvailable(context)
onBackPressedDispatcher?.addCallback(lifecycleOwner, onBackPressedCallback)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup,
savedViewState: Bundle?
): View {
return ComposeView(container.context).apply {
setContent {
OnBackPressedDemo()
}
}
}
override fun onAttach(view: View) {
super.onAttach(view)
(activity as? ToolbarProvider)?.toolbar?.apply {
title = "OnBackPressed Demo"
menu.clear()
}
}
@Composable
fun OnBackPressedDemo(modifier: Modifier = Modifier) {
val radioOptions = BackOption.values()
val (selectedOption, onOptionSelected) = remember { mutableStateOf(radioOptions[0]) }
BackHandler(enabled = selectedOption == BackOption.Composable) {
Toast.makeText(activity!!, "Back handled at the Composable level", Toast.LENGTH_SHORT).show()
}
MaterialTheme {
Column(
modifier = modifier
.fillMaxSize()
.padding(top = 16.dp),
) {
LaunchedEffect(selectedOption) {
onBackPressedCallback.isEnabled = selectedOption == BackOption.Controller
}
radioOptions.forEach { option ->
Row(
modifier = Modifier
.fillMaxWidth()
.selectable(
selected = (option == selectedOption),
onClick = {
onOptionSelected(option)
},
)
.padding(horizontal = 16.dp),
verticalAlignment = Alignment.CenterVertically,
) {
RadioButton(
selected = (option == selectedOption),
onClick = { onOptionSelected(option) },
)
Text(
text = option.title,
modifier = Modifier.padding(start = 16.dp),
)
}
}
}
}
}
}
private enum class BackOption(val title: String) {
Controller("Handle back in Controller"),
Composable("Handle back in Composable"),
None("Disable back handlers"),
}
@@ -9,13 +9,12 @@ import androidx.appcompat.widget.Toolbar
import com.bluelinelabs.conductor.Controller
import com.bluelinelabs.conductor.ControllerChangeHandler
import com.bluelinelabs.conductor.ControllerChangeType
import com.bluelinelabs.conductor.archlifecycle.LifecycleController
import com.bluelinelabs.conductor.demo.ToolbarProvider
abstract class BaseController(
@LayoutRes private val layoutRes: Int,
args: Bundle? = null
) : LifecycleController(args) {
) : Controller(args) {
init {
watchForLeaks()
@@ -82,4 +81,4 @@ abstract class BaseController(
toolbar.menu.clear()
}
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 487 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 434 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 300 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 338 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 439 KiB

@@ -1,28 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#455A64"
android:gravity="center"
android:paddingBottom="8dp"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="8dp"
android:text="@string/additional_modules_explanation"
android:textColor="@color/white"
android:textSize="14sp"
/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
@@ -1,32 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/city_grid_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingStart="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingEnd="@dimen/activity_horizontal_margin">
<ImageView
android:id="@+id/dot"
android:layout_width="96dp"
android:layout_height="96dp"
android:importantForAccessibility="no"
android:src="@drawable/circle" />
<TextView
android:id="@+id/title"
style="@style/Base.TextAppearance.AppCompat.Large"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="40dp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="16dp" />
</LinearLayout>
@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
/>
@@ -1,26 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="200dp"
android:clipChildren="false"
android:clipToPadding="false"
android:gravity="center_vertical"
android:orientation="vertical"
android:padding="8dp">
<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="16dp"
android:layout_weight="1"
android:importantForAccessibility="no" />
<TextView
android:id="@+id/title"
style="@style/Base.TextAppearance.AppCompat.Large"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" />
</LinearLayout>
@@ -1,25 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="16dp" >
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
/>
<TextView
android:id="@+id/textView"
style="@style/Base.TextAppearance.AppCompat.Large"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
/>
</LinearLayout>
-3
View File
@@ -5,9 +5,6 @@
<!-- Home Controller -->
<string name="about">About</string>
<!-- Additional Modules Controller -->
<string name="additional_modules_explanation">The following modules are provided as additional dependencies, and are not a part of the core library. If you\'ve made a module you want to see included, let us know!</string>
<!-- Navigation Demo -->
<string name="pop_to_root">Pop to Root</string>
<string name="go_up">Go Up</string>
+1 -1
View File
@@ -1,5 +1,5 @@
VERSION_CODE=4
VERSION_NAME=4.0.0-SNAPSHOT
VERSION_NAME=4.0.0-SNAPSHOT-PREDICTIVE
android.useAndroidX=true
org.gradle.jvmargs=-Xmx1536m
+21 -29
View File
@@ -1,32 +1,30 @@
[versions]
minsdk = "16"
compilesdk = "33"
minsdk = "19"
compilesdk = "34"
targetsdk = "28"
agp = "7.4.2" # Note: update lint version whenever this is updated
androidx-activity = "1.6.1"
androidx-annotation = "1.1.0"
androidx-appcompat = "1.4.2"
androidx-collection = "1.1.0"
androidx-core = "1.3.2"
androidx-lifecycle = "2.6.0"
androidx-savedstate = "1.2.0"
androidx-transition = "1.3.1"
agp = "8.1.4" # Note: update lint version whenever this is updated
androidx-activity = "1.8.2"
androidx-annotation = "1.7.1"
androidx-appcompat = "1.6.1"
androidx-collection = "1.3.0"
androidx-core = "1.12.0"
androidx-lifecycle = "2.7.0"
androidx-savedstate = "1.2.1"
androidx-transition = "1.4.1"
androidx-viewpager2 = "1.0.0"
autodispose = "1.0.0"
compose = "1.3.1"
compose-compiler = "1.4.3"
dokka = "1.8.10"
junit = "4.13"
compose = "1.5.4"
compose-compiler = "1.5.3"
dokka = "1.9.0"
junit = "4.13.2"
kotest = "4.6.0"
kotlin = "1.8.10"
leakCanary = "2.7"
lint = "30.4.2" # Should always be agp + 23
material = "1.2.1"
mvnpublish = "0.23.2"
kotlin = "1.9.10"
leakCanary = "2.12"
lint = "31.1.4" # Should always be agp + 23
material = "1.11.0"
mvnpublish = "0.27.0"
picasso = "2.5.2"
robolectric = "4.5.1"
rxjava2 = "2.1.14"
robolectric = "4.11.1"
[libraries]
activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity" }
@@ -37,17 +35,12 @@ androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "a
androidx-collection = { module = "androidx.collection:collection", version.ref = "androidx-collection" }
androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "androidx-core" }
androidx-lifecycle-runtime = { module = "androidx.lifecycle:lifecycle-runtime", version.ref = "androidx-lifecycle" }
androidx-lifecycle-livedata-core = { module = "androidx.lifecycle:lifecycle-livedata-core", version.ref = "androidx-lifecycle" }
androidx-savedstate-ktx = { module = "androidx.savedstate:savedstate-ktx", version.ref = "androidx-savedstate" }
androidx-transition = { module = "androidx.transition:transition", version.ref = "androidx-transition" }
androidx-viewpager2 = { module = "androidx.viewpager2:viewpager2", version.ref = "androidx-viewpager2" }
autodispose = { module = "com.uber.autodispose:autodispose", version.ref = "autodispose" }
autodispose-lifecycle = { module = "com.uber.autodispose:autodispose-lifecycle", version.ref = "autodispose" }
autodispose-ktx = { module = "com.uber.autodispose:autodispose-ktx", version.ref = "autodispose" }
compose-foundation = { module = "androidx.compose.foundation:foundation", version.ref = "compose" }
compose-ui = { module = "androidx.compose.ui:ui", version.ref = "compose" }
compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "compose" }
compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview", version.ref = "compose" }
compose-material = { module = "androidx.compose.material:material", version.ref = "compose" }
dokka = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref = "dokka" }
junit = { module = "junit:junit", version.ref = "junit" }
@@ -62,7 +55,6 @@ lint-tests = { module = "com.android.tools.lint:lint-tests", version.ref = "lint
material = { module = "com.google.android.material:material", version.ref = "material" }
picasso = { module = "com.squareup.picasso:picasso", version.ref = "picasso" }
robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectric" }
rxjava2 = { module = "io.reactivex.rxjava2:rxjava", version.ref = "rxjava2" }
[plugins]
mvnpublish = { id = "com.vanniktech.maven.publish", version.ref = "mvnpublish" }
+1 -1
View File
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-2
View File
@@ -17,7 +17,5 @@ include ':conductor'
include ':conductor-lint'
include ':conductor-modules:viewpager'
include ':conductor-modules:viewpager2'
include ':conductor-modules:autodispose'
include ':conductor-modules:arch-components-lifecycle'
include ':conductor-modules:androidx-transition'
include ':demo'