Migration to AAC lifecycle, simplification of some logic

This commit is contained in:
Denis Karmyshakov 2018-03-14 15:44:17 +03:00
parent 44af99f9cf
commit 9b1275ceec
28 changed files with 493 additions and 4351 deletions

View File

@ -1,4 +1,5 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android {
compileSdkVersion compileSdk
@ -16,6 +17,8 @@ android {
dependencies {
api project(':libraries:core')
compileOnly "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
compileOnly "com.android.support:appcompat-v7:$supportLibraryVersion"
compileOnly "com.android.support:design:$supportLibraryVersion"
compileOnly "com.android.support:recyclerview-v7:$supportLibraryVersion"

View File

@ -19,46 +19,35 @@
package ru.touchin.roboswag.components.adapters;
import android.arch.lifecycle.LifecycleOwner;
import android.support.annotation.NonNull;
import android.support.v4.view.ViewCompat;
import android.view.ViewGroup;
import io.reactivex.Completable;
import io.reactivex.Maybe;
import io.reactivex.Observable;
import io.reactivex.Single;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Action;
import io.reactivex.functions.Consumer;
import ru.touchin.roboswag.components.utils.LifecycleBindable;
import ru.touchin.roboswag.components.utils.UiUtils;
/**
* Objects of such class controls creation and binding of specific type of RecyclerView's ViewHolders.
* Default {@link #getItemViewType} is generating on construction of object.
*
* @param <TViewHolder> Type of {@link BindableViewHolder} of delegate.
*/
@SuppressWarnings("PMD.TooManyMethods")
//TooManyMethods: it's ok
public abstract class AdapterDelegate<TViewHolder extends BindableViewHolder> implements LifecycleBindable {
public abstract class AdapterDelegate<TViewHolder extends BindableViewHolder> {
private final int defaultItemViewType = ViewCompat.generateViewId();
@NonNull
private final LifecycleBindable parentLifecycleBindable;
private final int defaultItemViewType;
private final LifecycleOwner lifecycleOwner;
public AdapterDelegate(@NonNull final LifecycleBindable parentLifecycleBindable) {
this.parentLifecycleBindable = parentLifecycleBindable;
this.defaultItemViewType = UiUtils.OfViews.generateViewId();
public AdapterDelegate(@NonNull final LifecycleOwner lifecycleOwner) {
this.lifecycleOwner = lifecycleOwner;
}
/**
* Returns parent {@link LifecycleBindable} that this delegate created from (e.g. Activity or ViewController).
* Returns parent {@link LifecycleOwner} that this delegate created from (e.g. Activity, Fragment or ViewController).
*
* @return Parent {@link LifecycleBindable}.
* @return Parent {@link LifecycleOwner}.
*/
@NonNull
public LifecycleBindable getParentLifecycleBindable() {
return parentLifecycleBindable;
public LifecycleOwner getLifecycleOwner() {
return lifecycleOwner;
}
/**
@ -79,184 +68,4 @@ public abstract class AdapterDelegate<TViewHolder extends BindableViewHolder> im
@NonNull
public abstract TViewHolder onCreateViewHolder(@NonNull final ViewGroup parent);
@SuppressWarnings("CPD-START")
//CPD: it is same as in other implementation based on BaseLifecycleBindable
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Observable<T> observable) {
return parentLifecycleBindable.untilStop(observable);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Observable<T> observable, @NonNull final Consumer<T> onNextAction) {
return parentLifecycleBindable.untilStop(observable, onNextAction);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Observable<T> observable,
@NonNull final Consumer<T> onNextAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return parentLifecycleBindable.untilStop(observable, onNextAction, onErrorAction);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Observable<T> observable,
@NonNull final Consumer<T> onNextAction,
@NonNull final Consumer<Throwable> onErrorAction,
@NonNull final Action onCompletedAction) {
return parentLifecycleBindable.untilStop(observable, onNextAction, onErrorAction, onCompletedAction);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Single<T> single) {
return parentLifecycleBindable.untilStop(single);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Single<T> single, @NonNull final Consumer<T> onSuccessAction) {
return parentLifecycleBindable.untilStop(single, onSuccessAction);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Single<T> single,
@NonNull final Consumer<T> onSuccessAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return parentLifecycleBindable.untilStop(single, onSuccessAction, onErrorAction);
}
@NonNull
@Override
public Disposable untilStop(@NonNull final Completable completable) {
return parentLifecycleBindable.untilStop(completable);
}
@NonNull
@Override
public Disposable untilStop(@NonNull final Completable completable, @NonNull final Action onCompletedAction) {
return parentLifecycleBindable.untilStop(completable, onCompletedAction);
}
@NonNull
@Override
public Disposable untilStop(@NonNull final Completable completable,
@NonNull final Action onCompletedAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return parentLifecycleBindable.untilStop(completable, onCompletedAction, onErrorAction);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Maybe<T> maybe) {
return parentLifecycleBindable.untilStop(maybe);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Maybe<T> maybe, @NonNull final Consumer<T> onSuccessAction) {
return parentLifecycleBindable.untilStop(maybe, onSuccessAction);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Maybe<T> maybe,
@NonNull final Consumer<T> onSuccessAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return parentLifecycleBindable.untilStop(maybe, onSuccessAction, onErrorAction);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Observable<T> observable) {
return parentLifecycleBindable.untilDestroy(observable);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Observable<T> observable, @NonNull final Consumer<T> onNextAction) {
return parentLifecycleBindable.untilDestroy(observable, onNextAction);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Observable<T> observable,
@NonNull final Consumer<T> onNextAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return parentLifecycleBindable.untilDestroy(observable, onNextAction, onErrorAction);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Observable<T> observable,
@NonNull final Consumer<T> onNextAction,
@NonNull final Consumer<Throwable> onErrorAction,
@NonNull final Action onCompletedAction) {
return parentLifecycleBindable.untilDestroy(observable, onNextAction, onErrorAction, onCompletedAction);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Single<T> single) {
return parentLifecycleBindable.untilDestroy(single);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Single<T> single, @NonNull final Consumer<T> onSuccessAction) {
return parentLifecycleBindable.untilDestroy(single, onSuccessAction);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Single<T> single,
@NonNull final Consumer<T> onSuccessAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return parentLifecycleBindable.untilDestroy(single, onSuccessAction, onErrorAction);
}
@NonNull
@Override
public Disposable untilDestroy(@NonNull final Completable completable) {
return parentLifecycleBindable.untilDestroy(completable);
}
@NonNull
@Override
public Disposable untilDestroy(@NonNull final Completable completable, @NonNull final Action onCompletedAction) {
return parentLifecycleBindable.untilDestroy(completable, onCompletedAction);
}
@NonNull
@Override
public Disposable untilDestroy(@NonNull final Completable completable,
@NonNull final Action onCompletedAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return parentLifecycleBindable.untilDestroy(completable, onCompletedAction, onErrorAction);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Maybe<T> maybe) {
return parentLifecycleBindable.untilDestroy(maybe);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Maybe<T> maybe, @NonNull final Consumer<T> onSuccessAction) {
return parentLifecycleBindable.untilDestroy(maybe, onSuccessAction);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Maybe<T> maybe,
@NonNull final Consumer<T> onSuccessAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return parentLifecycleBindable.untilDestroy(maybe, onSuccessAction, onErrorAction);
}
}

View File

@ -1,307 +0,0 @@
/*
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package ru.touchin.roboswag.components.adapters;
import android.graphics.drawable.Drawable;
import android.support.annotation.ColorInt;
import android.support.annotation.ColorRes;
import android.support.annotation.DrawableRes;
import android.support.annotation.IdRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import io.reactivex.Completable;
import io.reactivex.Maybe;
import io.reactivex.Observable;
import io.reactivex.Single;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Action;
import io.reactivex.functions.Consumer;
import ru.touchin.roboswag.components.utils.LifecycleBindable;
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
/**
* Created by Gavriil Sitnikov on 12/8/2016.
* ViewHolder that implements {@link LifecycleBindable} and uses parent bindable object as bridge (Activity, ViewController etc.).
*/
@SuppressWarnings("PMD.TooManyMethods")
public class BindableViewHolder extends RecyclerView.ViewHolder implements LifecycleBindable {
@NonNull
private final LifecycleBindable baseLifecycleBindable;
public BindableViewHolder(@NonNull final LifecycleBindable baseLifecycleBindable, @NonNull final View itemView) {
super(itemView);
this.baseLifecycleBindable = baseLifecycleBindable;
}
/**
* Look for a child view with the given id. If this view has the given id, return this view.
*
* @param id The id to search for;
* @return The view that has the given id in the hierarchy.
*/
@NonNull
@SuppressWarnings("unchecked")
public <T extends View> T findViewById(@IdRes final int id) {
final T viewById = (T) itemView.findViewById(id);
if (viewById == null) {
throw new ShouldNotHappenException("No view for id=" + itemView.getResources().getResourceName(id));
}
return viewById;
}
/**
* Return the string value associated with a particular resource ID. It
* will be stripped of any styled text information.
*
* @param resId The resource id to search for data;
* @return String The string data associated with the resource.
*/
@NonNull
public String getString(@StringRes final int resId) {
return itemView.getResources().getString(resId);
}
/**
* Return the string value associated with a particular resource ID. It
* will be stripped of any styled text information.
*
* @param resId The resource id to search for data;
* @param formatArgs The format arguments that will be used for substitution.
* @return String The string data associated with the resource.
*/
@NonNull
public String getString(@StringRes final int resId, @Nullable final Object... formatArgs) {
return itemView.getResources().getString(resId, formatArgs);
}
/**
* Return the color value associated with a particular resource ID.
* Starting in {@link android.os.Build.VERSION_CODES#M}, the returned
* color will be styled for the specified Context's theme.
*
* @param resId The resource id to search for data;
* @return int A single color value in the form 0xAARRGGBB.
*/
@ColorInt
public int getColor(@ColorRes final int resId) {
return ContextCompat.getColor(itemView.getContext(), resId);
}
/**
* Returns a drawable object associated with a particular resource ID.
* Starting in {@link android.os.Build.VERSION_CODES#LOLLIPOP}, the
* returned drawable will be styled for the specified Context's theme.
*
* @param resId The resource id to search for data;
* @return Drawable An object that can be used to draw this resource.
*/
@NonNull
public Drawable getDrawable(@DrawableRes final int resId) {
return ContextCompat.getDrawable(itemView.getContext(), resId);
}
@SuppressWarnings("CPD-START")
//CPD: it's ok as it's LifecycleBindable
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Observable<T> observable) {
return baseLifecycleBindable.untilStop(observable);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Observable<T> observable, @NonNull final Consumer<T> onNextAction) {
return baseLifecycleBindable.untilStop(observable, onNextAction);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Observable<T> observable,
@NonNull final Consumer<T> onNextAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return baseLifecycleBindable.untilStop(observable, onNextAction, onErrorAction);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Observable<T> observable,
@NonNull final Consumer<T> onNextAction,
@NonNull final Consumer<Throwable> onErrorAction,
@NonNull final Action onCompletedAction) {
return baseLifecycleBindable.untilStop(observable, onNextAction, onErrorAction, onCompletedAction);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Single<T> single) {
return baseLifecycleBindable.untilStop(single);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Single<T> single, @NonNull final Consumer<T> onSuccessAction) {
return baseLifecycleBindable.untilStop(single, onSuccessAction);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Single<T> single,
@NonNull final Consumer<T> onSuccessAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return baseLifecycleBindable.untilStop(single, onSuccessAction, onErrorAction);
}
@NonNull
@Override
public Disposable untilStop(@NonNull final Completable completable) {
return baseLifecycleBindable.untilStop(completable);
}
@NonNull
@Override
public Disposable untilStop(@NonNull final Completable completable, @NonNull final Action onCompletedAction) {
return baseLifecycleBindable.untilStop(completable, onCompletedAction);
}
@NonNull
@Override
public Disposable untilStop(@NonNull final Completable completable,
@NonNull final Action onCompletedAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return baseLifecycleBindable.untilStop(completable, onCompletedAction, onErrorAction);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Maybe<T> maybe) {
return baseLifecycleBindable.untilStop(maybe);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Maybe<T> maybe, @NonNull final Consumer<T> onSuccessAction) {
return baseLifecycleBindable.untilStop(maybe, onSuccessAction);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Maybe<T> maybe,
@NonNull final Consumer<T> onSuccessAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return baseLifecycleBindable.untilStop(maybe, onSuccessAction, onErrorAction);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Observable<T> observable) {
return baseLifecycleBindable.untilDestroy(observable);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Observable<T> observable, @NonNull final Consumer<T> onNextAction) {
return baseLifecycleBindable.untilDestroy(observable, onNextAction);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Observable<T> observable,
@NonNull final Consumer<T> onNextAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return baseLifecycleBindable.untilDestroy(observable, onNextAction, onErrorAction);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Observable<T> observable,
@NonNull final Consumer<T> onNextAction,
@NonNull final Consumer<Throwable> onErrorAction,
@NonNull final Action onCompletedAction) {
return baseLifecycleBindable.untilDestroy(observable, onNextAction, onErrorAction, onCompletedAction);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Single<T> single) {
return baseLifecycleBindable.untilDestroy(single);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Single<T> single, @NonNull final Consumer<T> onSuccessAction) {
return baseLifecycleBindable.untilDestroy(single, onSuccessAction);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Single<T> single,
@NonNull final Consumer<T> onSuccessAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return baseLifecycleBindable.untilDestroy(single, onSuccessAction, onErrorAction);
}
@NonNull
@Override
public Disposable untilDestroy(@NonNull final Completable completable) {
return baseLifecycleBindable.untilDestroy(completable);
}
@NonNull
@Override
public Disposable untilDestroy(@NonNull final Completable completable, @NonNull final Action onCompletedAction) {
return baseLifecycleBindable.untilDestroy(completable, onCompletedAction);
}
@NonNull
@Override
public Disposable untilDestroy(@NonNull final Completable completable,
@NonNull final Action onCompletedAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return baseLifecycleBindable.untilDestroy(completable, onCompletedAction, onErrorAction);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Maybe<T> maybe) {
return baseLifecycleBindable.untilDestroy(maybe);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Maybe<T> maybe, @NonNull final Consumer<T> onSuccessAction) {
return baseLifecycleBindable.untilDestroy(maybe, onSuccessAction);
}
@SuppressWarnings("CPD-END")
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Maybe<T> maybe,
@NonNull final Consumer<T> onSuccessAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return baseLifecycleBindable.untilDestroy(maybe, onSuccessAction, onErrorAction);
}
}

View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package ru.touchin.roboswag.components.adapters
import android.arch.lifecycle.LifecycleOwner
import android.support.v7.widget.RecyclerView
import android.view.View
/**
* Created by Denis Karmyshakov 14.03.2018.
* ViewHolder that implements {@link LifecycleOwner} and uses parent lifecycle
* object as bridge ([android.app.Activity], [android.support.v4.app.Fragment] etc.).
*/
open class BindableViewHolder(
private val lifecycleOwner: LifecycleOwner,
itemView: View
) : RecyclerView.ViewHolder(itemView), LifecycleOwner by lifecycleOwner

View File

@ -1,24 +1,23 @@
package ru.touchin.roboswag.components.adapters;
import android.arch.lifecycle.LifecycleOwner;
import android.support.annotation.NonNull;
import android.view.ViewGroup;
import java.util.List;
import ru.touchin.roboswag.components.utils.LifecycleBindable;
/**
* Objects of such class controls creation and binding of specific type of RecyclerView's ViewHolders.
* Such delegates are creating and binding ViewHolders for specific items.
* Default {@link #getItemViewType} is generating on construction of object.
*
* @param <TViewHolder> Type of {@link BindableViewHolder} of delegate;
* @param <TItem> Type of items to bind to {@link BindableViewHolder}s.
* @param <TViewHolder> Type of {@link LifecycleOwner} of delegate;
* @param <TItem> Type of items to bind to {@link LifecycleOwner}s.
*/
public abstract class ItemAdapterDelegate<TViewHolder extends BindableViewHolder, TItem> extends AdapterDelegate<TViewHolder> {
public ItemAdapterDelegate(@NonNull final LifecycleBindable parentLifecycleBindable) {
super(parentLifecycleBindable);
public ItemAdapterDelegate(@NonNull final LifecycleOwner lifecycleOwner) {
super(lifecycleOwner);
}
/**
@ -61,8 +60,12 @@ public abstract class ItemAdapterDelegate<TViewHolder extends BindableViewHolder
* @param positionInAdapter Position of item in adapter;
* @param positionInCollection Position of item in collection that contains item;
*/
public abstract void onBindViewHolder(@NonNull final TViewHolder holder, @NonNull final TItem item,
final int positionInAdapter, final int positionInCollection);
public abstract void onBindViewHolder(
@NonNull final TViewHolder holder,
@NonNull final TItem item,
final int positionInAdapter,
final int positionInCollection
);
/**
* Binds item with payloads to created by this object ViewHolder.
@ -73,8 +76,13 @@ public abstract class ItemAdapterDelegate<TViewHolder extends BindableViewHolder
* @param positionInAdapter Position of item in adapter;
* @param positionInCollection Position of item in collection that contains item;
*/
public void onBindViewHolder(@NonNull final TViewHolder holder, @NonNull final TItem item, @NonNull final List<Object> payloads,
final int positionInAdapter, final int positionInCollection) {
public void onBindViewHolder(
@NonNull final TViewHolder holder,
@NonNull final TItem item,
@NonNull final List<Object> payloads,
final int positionInAdapter,
final int positionInCollection
) {
//do nothing by default
}

View File

@ -1,697 +0,0 @@
/*
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package ru.touchin.roboswag.components.adapters;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import io.reactivex.Observable;
import io.reactivex.functions.BiConsumer;
import io.reactivex.functions.Consumer;
import io.reactivex.subjects.BehaviorSubject;
import ru.touchin.roboswag.components.utils.LifecycleBindable;
import ru.touchin.roboswag.components.utils.UiUtils;
import ru.touchin.roboswag.core.log.Lc;
import ru.touchin.roboswag.core.observables.collections.ObservableCollection;
import ru.touchin.roboswag.core.observables.collections.ObservableList;
import ru.touchin.roboswag.core.observables.collections.changes.Change;
import ru.touchin.roboswag.core.observables.collections.changes.ChangePayloadProducer;
import ru.touchin.roboswag.core.observables.collections.changes.CollectionChanges;
import ru.touchin.roboswag.core.observables.collections.changes.SameItemsPredicate;
import ru.touchin.roboswag.core.observables.collections.loadable.LoadingMoreList;
import ru.touchin.roboswag.core.utils.Optional;
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
/**
* Created by Gavriil Sitnikov on 20/11/2015.
* Adapter based on {@link ObservableCollection} and providing some useful features like:
* - item-based binding method;
* - delegates by {@link AdapterDelegate} over itemViewType logic;
* - item click listener setup by {@link #setOnItemClickListener(OnItemClickListener)};
* - allows to inform about footers/headers by overriding base create/bind methods and {@link #getHeadersCount()} plus {@link #getFootersCount()};
* - by default it is pre-loading items for collections like {@link ru.touchin.roboswag.core.observables.collections.loadable.LoadingMoreList}.
*
* @param <TItem> Type of items to bind to ViewHolders;
* @param <TItemViewHolder> Type of ViewHolders to show items.
*/
@SuppressWarnings({"unchecked", "PMD.TooManyMethods"})
//TooManyMethods: it's ok
public abstract class ObservableCollectionAdapter<TItem, TItemViewHolder extends BindableViewHolder>
extends RecyclerView.Adapter<BindableViewHolder> {
private static final int PRE_LOADING_COUNT = 20;
private static boolean inDebugMode;
/**
* Enables debugging features like checking concurrent delegates.
*/
public static void setInDebugMode() {
inDebugMode = true;
}
@NonNull
private final BehaviorSubject<Optional<ObservableCollection<TItem>>> observableCollectionSubject
= BehaviorSubject.createDefault(new Optional<>(null));
@NonNull
private final BehaviorSubject<Boolean> moreAutoLoadingRequested = BehaviorSubject.create();
@NonNull
private final LifecycleBindable lifecycleBindable;
@Nullable
private Object onItemClickListener;
private int lastUpdatedChangeNumber = -1;
@NonNull
private final ObservableList<TItem> innerCollection = new ObservableList<>();
private boolean anyChangeApplied;
private long itemClickDelayMillis;
@NonNull
private final List<RecyclerView> attachedRecyclerViews = new LinkedList<>();
@NonNull
private final List<AdapterDelegate<? extends BindableViewHolder>> delegates = new ArrayList<>();
public ObservableCollectionAdapter(@NonNull final LifecycleBindable lifecycleBindable) {
super();
this.lifecycleBindable = lifecycleBindable;
lifecycleBindable.untilDestroy(innerCollection.observeChanges(), this::onItemsChanged);
lifecycleBindable.untilDestroy(observableCollectionSubject
.switchMap(optional -> {
final ObservableCollection<TItem> collection = optional.get();
if (collection instanceof ObservableList) {
innerCollection.setDiffUtilsSource((ObservableList<TItem>) collection);
} else {
innerCollection.setDiffUtilsSource(null);
}
return collection != null ? collection.observeItems() : Observable.just(Collections.emptyList());
}), innerCollection::set);
lifecycleBindable.untilDestroy(createMoreAutoLoadingObservable());
}
@NonNull
private Observable createMoreAutoLoadingObservable() {
return observableCollectionSubject
.switchMap(collectionOptional -> {
final ObservableCollection<TItem> collection = collectionOptional.get();
if (!(collection instanceof LoadingMoreList)) {
return Observable.empty();
}
return moreAutoLoadingRequested
.distinctUntilChanged()
.switchMap(requested -> {
if (!requested) {
return Observable.empty();
}
final int size = collection.size();
return ((LoadingMoreList<?, ?, ?>) collection)
.loadRange(size, size + PRE_LOADING_COUNT)
.onErrorReturnItem(new ArrayList<>())
.toObservable()
.doOnComplete(() -> moreAutoLoadingRequested.onNext(false));
});
});
}
/**
* Returns if any change of source collection applied to adapter.
* It's important to not show some footers or headers before first change have applied.
*
* @return True id any change applied.
*/
public boolean isAnyChangeApplied() {
return anyChangeApplied;
}
@Override
public void onAttachedToRecyclerView(@NonNull final RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
attachedRecyclerViews.add(recyclerView);
}
private boolean anyRecyclerViewShown() {
for (final RecyclerView recyclerView : attachedRecyclerViews) {
if (recyclerView.isShown()) {
return true;
}
}
return false;
}
@Override
public void onDetachedFromRecyclerView(@NonNull final RecyclerView recyclerView) {
super.onDetachedFromRecyclerView(recyclerView);
attachedRecyclerViews.remove(recyclerView);
}
/**
* Returns parent {@link LifecycleBindable} (Activity/ViewController etc.).
*
* @return Parent {@link LifecycleBindable}.
*/
@NonNull
public LifecycleBindable getLifecycleBindable() {
return lifecycleBindable;
}
/**
* Returns {@link ObservableCollection} which provides items and it's changes.
*
* @return Inner {@link ObservableCollection}.
*/
@Nullable
public ObservableCollection<TItem> getObservableCollection() {
return observableCollectionSubject.getValue().get();
}
/**
* Method to observe {@link ObservableCollection} which provides items and it's changes.
*
* @return Observable of inner {@link ObservableCollection}.
*/
@NonNull
public Observable<Optional<ObservableCollection<TItem>>> observeObservableCollection() {
return observableCollectionSubject;
}
/**
* Sets {@link ObservableCollection} which will provide items and it's changes.
*
* @param observableCollection Inner {@link ObservableCollection}.
*/
public void setObservableCollection(@Nullable final ObservableCollection<TItem> observableCollection) {
this.observableCollectionSubject.onNext(new Optional<>(observableCollection));
}
/**
* Simply sets items.
*
* @param items Items to set.
*/
public void setItems(@NonNull final Collection<TItem> items) {
setObservableCollection(new ObservableList<>(items));
}
/**
* Calls when collection changes.
*
* @param collectionChanges Changes of collection.
*/
protected void onItemsChanged(@NonNull final CollectionChanges<TItem> collectionChanges) {
if (Looper.myLooper() != Looper.getMainLooper()) {
Lc.assertion("Items changes called on not main thread");
return;
}
if (!anyChangeApplied || !anyRecyclerViewShown()) {
anyChangeApplied = true;
refreshUpdate();
return;
}
if (collectionChanges.getNumber() != innerCollection.getChangesCount()
|| collectionChanges.getNumber() != lastUpdatedChangeNumber + 1) {
if (lastUpdatedChangeNumber < collectionChanges.getNumber()) {
refreshUpdate();
}
return;
}
notifyAboutChanges(collectionChanges.getChanges());
lastUpdatedChangeNumber = innerCollection.getChangesCount();
}
private void refreshUpdate() {
notifyDataSetChanged();
lastUpdatedChangeNumber = innerCollection.getChangesCount();
}
private void notifyAboutChanges(@NonNull final Collection<Change> changes) {
for (final Change change : changes) {
if (change instanceof Change.Inserted) {
final Change.Inserted castedChange = (Change.Inserted) change;
notifyItemRangeInserted(castedChange.getPosition() + getHeadersCount(), castedChange.getCount());
} else if (change instanceof Change.Removed) {
if (getItemCount() - getHeadersCount() == 0) {
//TODO: bug of recyclerview?
notifyDataSetChanged();
} else {
final Change.Removed castedChange = (Change.Removed) change;
notifyItemRangeRemoved(castedChange.getPosition() + getHeadersCount(), castedChange.getCount());
}
} else if (change instanceof Change.Moved) {
final Change.Moved castedChange = (Change.Moved) change;
notifyItemMoved(castedChange.getFromPosition() + getHeadersCount(), castedChange.getToPosition() + getHeadersCount());
} else if (change instanceof Change.Changed) {
final Change.Changed castedChange = (Change.Changed) change;
notifyItemRangeChanged(
castedChange.getPosition() + getHeadersCount(),
castedChange.getCount(),
castedChange.getPayload());
} else {
Lc.assertion("Not supported " + change);
}
}
}
/**
* Returns headers count goes before items.
*
* @return Headers count.
*/
protected int getHeadersCount() {
return 0;
}
/**
* Returns footers count goes after items and headers.
*
* @return Footers count.
*/
protected int getFootersCount() {
return 0;
}
/**
* Returns list of added delegates.
*
* @return List of {@link AdapterDelegate}.
*/
@NonNull
public List<AdapterDelegate<? extends BindableViewHolder>> getDelegates() {
return Collections.unmodifiableList(delegates);
}
/**
* Adds {@link ItemAdapterDelegate} to adapter.
*
* @param delegate Delegate to add.
*/
public void addDelegate(@NonNull final ItemAdapterDelegate<? extends TItemViewHolder, ? extends TItem> delegate) {
addDelegateInternal(delegate);
}
/**
* Adds {@link PositionAdapterDelegate} to adapter.
*
* @param delegate Delegate to add.
*/
public void addDelegate(@NonNull final PositionAdapterDelegate<? extends BindableViewHolder> delegate) {
addDelegateInternal(delegate);
}
private void addDelegateInternal(@NonNull final AdapterDelegate<? extends BindableViewHolder> delegate) {
if (inDebugMode) {
for (final AdapterDelegate addedDelegate : delegates) {
if (addedDelegate.getItemViewType() == delegate.getItemViewType()) {
Lc.assertion("AdapterDelegate with viewType=" + delegate.getItemViewType() + " already added");
return;
}
}
}
delegates.add(delegate);
notifyDataSetChanged();
}
/**
* Removes {@link AdapterDelegate} from adapter.
*
* @param delegate Delegate to remove.
*/
public void removeDelegate(@NonNull final AdapterDelegate<? extends BindableViewHolder> delegate) {
delegates.remove(delegate);
notifyDataSetChanged();
}
private void checkDelegates(@Nullable final AdapterDelegate alreadyPickedDelegate, @NonNull final AdapterDelegate currentDelegate) {
if (alreadyPickedDelegate != null) {
throw new ShouldNotHappenException("Concurrent delegates: " + currentDelegate + " and " + alreadyPickedDelegate);
}
}
private int getItemPositionInCollection(final int positionInAdapter) {
final int shiftedPosition = positionInAdapter - getHeadersCount();
return shiftedPosition >= 0 && shiftedPosition < innerCollection.size() ? shiftedPosition : -1;
}
@SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.ModifiedCyclomaticComplexity", "PMD.StdCyclomaticComplexity", "PMD.NPathComplexity"})
//Complexity: because of debug code
@Override
public int getItemViewType(final int positionInAdapter) {
AdapterDelegate delegateOfViewType = null;
final int positionInCollection = getItemPositionInCollection(positionInAdapter);
final TItem item = positionInCollection >= 0 ? innerCollection.get(positionInCollection) : null;
for (final AdapterDelegate<?> delegate : delegates) {
if (delegate instanceof ItemAdapterDelegate) {
if (item != null && ((ItemAdapterDelegate) delegate).isForViewType(item, positionInAdapter, positionInCollection)) {
checkDelegates(delegateOfViewType, delegate);
delegateOfViewType = delegate;
if (!inDebugMode) {
break;
}
}
} else if (delegate instanceof PositionAdapterDelegate) {
if (((PositionAdapterDelegate) delegate).isForViewType(positionInAdapter)) {
checkDelegates(delegateOfViewType, delegate);
delegateOfViewType = delegate;
if (!inDebugMode) {
break;
}
}
} else {
Lc.assertion("Delegate of type " + delegate.getClass());
}
}
return delegateOfViewType != null ? delegateOfViewType.getItemViewType() : super.getItemViewType(positionInAdapter);
}
@Override
public long getItemId(final int positionInAdapter) {
final LongContainer result = new LongContainer();
tryDelegateAction(positionInAdapter,
(itemAdapterDelegate, positionInCollection) ->
result.value = itemAdapterDelegate.getItemId(innerCollection.get(positionInCollection),
positionInAdapter, positionInCollection),
positionAdapterDelegate -> result.value = positionAdapterDelegate.getItemId(positionInAdapter),
(positionInCollection) -> result.value = super.getItemId(positionInAdapter));
return result.value;
}
@SuppressWarnings("PMD.CyclomaticComplexity")
private void tryDelegateAction(final int positionInAdapter,
@NonNull final BiConsumer<ItemAdapterDelegate, Integer> itemAdapterDelegateAction,
@NonNull final Consumer<PositionAdapterDelegate> positionAdapterDelegateAction,
@NonNull final Consumer<Integer> defaultAction) {
final int viewType = getItemViewType(positionInAdapter);
final int positionInCollection = getItemPositionInCollection(positionInAdapter);
for (final AdapterDelegate<?> delegate : delegates) {
if (delegate instanceof ItemAdapterDelegate) {
if (positionInCollection >= 0 && viewType == delegate.getItemViewType()) {
try {
itemAdapterDelegateAction.accept((ItemAdapterDelegate) delegate, positionInCollection);
} catch (final Exception exception) {
Lc.assertion(exception);
}
return;
}
} else if (delegate instanceof PositionAdapterDelegate) {
if (viewType == delegate.getItemViewType()) {
try {
positionAdapterDelegateAction.accept((PositionAdapterDelegate) delegate);
} catch (final Exception exception) {
Lc.assertion(exception);
}
return;
}
} else {
Lc.assertion("Delegate of type " + delegate.getClass());
}
}
try {
defaultAction.accept(positionInCollection);
} catch (final Exception exception) {
Lc.assertion(exception);
}
}
@Override
public int getItemCount() {
return getHeadersCount() + innerCollection.size() + getFootersCount();
}
@NonNull
@Override
public BindableViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
for (final AdapterDelegate<?> delegate : delegates) {
if (delegate.getItemViewType() == viewType) {
return delegate.onCreateViewHolder(parent);
}
}
throw new ShouldNotHappenException("Add some AdapterDelegate or override this method");
}
@Override
public void onBindViewHolder(@NonNull final BindableViewHolder holder, final int positionInAdapter) {
lastUpdatedChangeNumber = innerCollection.getChangesCount();
tryDelegateAction(positionInAdapter,
(itemAdapterDelegate, positionInCollection) -> {
bindItemViewHolder(itemAdapterDelegate, holder, innerCollection.get(positionInCollection),
null, positionInAdapter, positionInCollection);
updateMoreAutoLoadingRequest(positionInCollection);
},
positionAdapterDelegate -> positionAdapterDelegate.onBindViewHolder(holder, positionInAdapter),
(positionInCollection) -> {
if (positionInCollection >= 0) {
bindItemViewHolder(null, holder, innerCollection.get(positionInCollection), null, positionInAdapter, positionInCollection);
}
});
}
@Override
public void onBindViewHolder(@NonNull final BindableViewHolder holder, final int positionInAdapter, @NonNull final List<Object> payloads) {
super.onBindViewHolder(holder, positionInAdapter, payloads);
tryDelegateAction(positionInAdapter,
(itemAdapterDelegate, positionInCollection) -> {
bindItemViewHolder(itemAdapterDelegate, holder, innerCollection.get(positionInCollection),
payloads, positionInAdapter, positionInCollection);
updateMoreAutoLoadingRequest(positionInCollection);
},
positionAdapterDelegate -> positionAdapterDelegate.onBindViewHolder(holder, positionInAdapter),
(positionInCollection) -> {
if (positionInCollection >= 0) {
bindItemViewHolder(null, holder, innerCollection.get(positionInCollection),
payloads, positionInAdapter, positionInCollection);
}
});
}
private void bindItemViewHolder(@Nullable final ItemAdapterDelegate<TItemViewHolder, TItem> itemAdapterDelegate,
@NonNull final BindableViewHolder holder, @NonNull final TItem item, @Nullable final List<Object> payloads,
final int positionInAdapter, final int positionInCollection) {
final TItemViewHolder itemViewHolder;
try {
itemViewHolder = (TItemViewHolder) holder;
} catch (final ClassCastException exception) {
Lc.assertion(exception);
return;
}
updateClickListener(holder, item, positionInAdapter, positionInCollection);
if (itemAdapterDelegate != null) {
if (payloads == null) {
itemAdapterDelegate.onBindViewHolder(itemViewHolder, item, positionInAdapter, positionInCollection);
} else {
itemAdapterDelegate.onBindViewHolder(itemViewHolder, item, payloads, positionInAdapter, positionInCollection);
}
} else {
if (payloads == null) {
onBindItemToViewHolder(itemViewHolder, positionInAdapter, item);
} else {
onBindItemToViewHolder(itemViewHolder, positionInAdapter, item, payloads);
}
}
}
private void updateClickListener(@NonNull final BindableViewHolder holder, @NonNull final TItem item,
final int positionInAdapter, final int positionInCollection) {
if (onItemClickListener != null && !isOnClickListenerDisabled(item, positionInAdapter, positionInCollection)) {
UiUtils.setOnRippleClickListener(holder.itemView,
() -> {
if (onItemClickListener instanceof OnItemClickListener) {
((OnItemClickListener) onItemClickListener).onItemClicked(item);
} else if (onItemClickListener instanceof OnItemWithPositionClickListener) {
((OnItemWithPositionClickListener) onItemClickListener).onItemClicked(item, positionInAdapter, positionInCollection);
} else {
Lc.assertion("Unexpected onItemClickListener type " + onItemClickListener);
}
},
itemClickDelayMillis);
}
}
private void updateMoreAutoLoadingRequest(final int positionInCollection) {
if (positionInCollection > innerCollection.size() - PRE_LOADING_COUNT) {
return;
}
moreAutoLoadingRequested.onNext(true);
}
/**
* Method to bind item (from {@link #getObservableCollection()}) to item-specific ViewHolder.
* It is not calling for headers and footer which counts are returned by {@link #getHeadersCount()} and @link #getFootersCount()}.
* You don't need to override this method if you have delegates for every view type.
*
* @param holder ViewHolder to bind item to;
* @param positionInAdapter Position of ViewHolder (NOT item!);
* @param item Item returned by position (WITH HEADER OFFSET!).
*/
protected void onBindItemToViewHolder(@NonNull final TItemViewHolder holder, final int positionInAdapter, @NonNull final TItem item) {
// do nothing by default - let delegates do it
}
/**
* Method to bind item (from {@link #getObservableCollection()}) to item-specific ViewHolder with payloads.
* It is not calling for headers and footer which counts are returned by {@link #getHeadersCount()} and @link #getFootersCount()}.
*
* @param holder ViewHolder to bind item to;
* @param positionInAdapter Position of ViewHolder in adapter (NOT item!);
* @param item Item returned by position (WITH HEADER OFFSET!);
* @param payloads Payloads.
*/
protected void onBindItemToViewHolder(@NonNull final TItemViewHolder holder, final int positionInAdapter, @NonNull final TItem item,
@NonNull final List<Object> payloads) {
// do nothing by default - let delegates do it
}
@Nullable
public TItem getItem(final int positionInAdapter) {
final int positionInCollection = getItemPositionInCollection(positionInAdapter);
return positionInCollection >= 0 ? innerCollection.get(positionInCollection) : null;
}
/**
* Sets item click listener.
*
* @param onItemClickListener Item click listener.
*/
public void setOnItemClickListener(@Nullable final OnItemClickListener<TItem> onItemClickListener) {
this.setOnItemClickListener(onItemClickListener, UiUtils.RIPPLE_EFFECT_DELAY);
}
/**
* Sets item click listener.
*
* @param onItemClickListener Item click listener;
* @param itemClickDelayMillis Delay of calling click listener.
*/
public void setOnItemClickListener(@Nullable final OnItemClickListener<TItem> onItemClickListener, final long itemClickDelayMillis) {
this.onItemClickListener = onItemClickListener;
this.itemClickDelayMillis = itemClickDelayMillis;
refreshUpdate();
}
/**
* Sets item click listener.
*
* @param onItemClickListener Item click listener.
*/
public void setOnItemClickListener(@Nullable final OnItemWithPositionClickListener<TItem> onItemClickListener) {
this.setOnItemClickListener(onItemClickListener, UiUtils.RIPPLE_EFFECT_DELAY);
}
/**
* Sets item click listener.
*
* @param onItemClickListener Item click listener;
* @param itemClickDelayMillis Delay of calling click listener.
*/
public void setOnItemClickListener(@Nullable final OnItemWithPositionClickListener<TItem> onItemClickListener, final long itemClickDelayMillis) {
this.onItemClickListener = onItemClickListener;
this.itemClickDelayMillis = itemClickDelayMillis;
refreshUpdate();
}
/**
* Returns if click listening disabled or not for specific item.
*
* @param item Item to check click availability;
* @param positionInAdapter Position of clicked item in adapter (with headers);
* @param positionInCollection Position of clicked item in inner collection;
* @return True if click listener enabled for such item.
*/
public boolean isOnClickListenerDisabled(@NonNull final TItem item, final int positionInAdapter, final int positionInCollection) {
return false;
}
/**
* Enable diff utils algorithm in collection changes.
*
* @param detectMoves The flag that determines whether the {@link Change.Moved} changes will be generated or not;
* @param sameItemsPredicate Predicate for the determination of the same elements;
* @param changePayloadProducer Function that calculate change payload when items the same but contents are different.
*/
public void enableDiffUtils(final boolean detectMoves,
@NonNull final SameItemsPredicate<TItem> sameItemsPredicate,
@Nullable final ChangePayloadProducer<TItem> changePayloadProducer) {
innerCollection.enableDiffUtils(detectMoves, sameItemsPredicate, changePayloadProducer);
}
/**
* Disable diff utils algorithm.
*/
public void disableDiffUtils() {
innerCollection.disableDiffUtils();
}
/**
* Returns enabled flag of diff utils.
*
* @return true if diff utils is enabled.
*/
public boolean diffUtilsIsEnabled() {
return innerCollection.diffUtilsIsEnabled();
}
/**
* Interface to simply add item click listener.
*
* @param <TItem> Type of item
*/
public interface OnItemClickListener<TItem> {
/**
* Calls when item have clicked.
*
* @param item Clicked item.
*/
void onItemClicked(@NonNull TItem item);
}
/**
* Interface to simply add item click listener based on item position in adapter and collection.
*
* @param <TItem> Type of item
*/
public interface OnItemWithPositionClickListener<TItem> {
/**
* Calls when item have clicked.
*
* @param item Clicked item;
* @param positionInAdapter Position of clicked item in adapter (with headers);
* @param positionInCollection Position of clicked item in inner collection.
*/
void onItemClicked(@NonNull TItem item, final int positionInAdapter, final int positionInCollection);
}
private class LongContainer {
private long value;
}
}

View File

@ -1,12 +1,11 @@
package ru.touchin.roboswag.components.adapters;
import android.arch.lifecycle.LifecycleOwner;
import android.support.annotation.NonNull;
import android.view.ViewGroup;
import java.util.List;
import ru.touchin.roboswag.components.utils.LifecycleBindable;
/**
* Objects of such class controls creation and binding of specific type of RecyclerView's ViewHolders.
* Such delegates are creating and binding ViewHolders by position in adapter.
@ -16,8 +15,8 @@ import ru.touchin.roboswag.components.utils.LifecycleBindable;
*/
public abstract class PositionAdapterDelegate<TViewHolder extends BindableViewHolder> extends AdapterDelegate<TViewHolder> {
public PositionAdapterDelegate(@NonNull final LifecycleBindable parentLifecycleBindable) {
super(parentLifecycleBindable);
public PositionAdapterDelegate(@NonNull final LifecycleOwner lifecycleOwner) {
super(lifecycleOwner);
}
/**

View File

@ -1,52 +0,0 @@
/*
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package ru.touchin.roboswag.components.navigation;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import java.io.Serializable;
/**
* Created by Ilia Kurtov on 13/04/2016.
* Basic state of {@link ru.touchin.roboswag.components.navigation.fragments.ViewControllerFragment}.
* This object is saving as serializable in {@link android.os.Bundle} at {@link Fragment#onSaveInstanceState(Bundle)} point.
* Also this object is passing into {@link Fragment#getArguments()} on fragment instantiation.
* Do NOT store such object in fields outside of it's {@link ru.touchin.roboswag.components.navigation.fragments.ViewControllerFragment}:
* 1) it should be used as state of fragment but not state of other fragments or parts of logic;
* 2) if you want to modify such object then you should pass it's fragment as {@link Fragment#getTargetFragment()};
* 3) if you are using {@link ViewControllerNavigation} then just use ***ForResult methods to pass target;
* 4) as it is serializable object then all initialization logic (like binding) should NOT be in constructor. Use {@link #onCreate()} method.
*/
@SuppressWarnings("PMD.AbstractClassWithoutAbstractMethod")
//AbstractClassWithoutAbstractMethod: objects of this class actually shouldn't exist
public abstract class AbstractState implements Serializable {
private static final long serialVersionUID = 1L;
/**
* Calls right after construction. All inner object's instantiation logic should be in this method.
* Do NOT do some instantiation logic in constructor except fields setup.
*/
public void onCreate() {
// do nothing
}
}

View File

@ -19,39 +19,27 @@
package ru.touchin.roboswag.components.navigation;
import android.arch.lifecycle.Lifecycle;
import android.arch.lifecycle.LifecycleOwner;
import android.arch.lifecycle.LifecycleRegistry;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.annotation.CallSuper;
import android.support.annotation.ColorInt;
import android.support.annotation.ColorRes;
import android.support.annotation.DrawableRes;
import android.support.annotation.IdRes;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import io.reactivex.Completable;
import io.reactivex.Maybe;
import io.reactivex.Observable;
import io.reactivex.Single;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Action;
import io.reactivex.functions.Consumer;
import ru.touchin.roboswag.components.navigation.activities.ViewControllerActivity;
import ru.touchin.roboswag.components.navigation.fragments.ViewControllerFragment;
import ru.touchin.roboswag.components.utils.BaseLifecycleBindable;
import ru.touchin.roboswag.components.utils.LifecycleBindable;
import ru.touchin.roboswag.components.utils.UiUtils;
import ru.touchin.roboswag.core.log.Lc;
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
/**
* Created by Gavriil Sitnikov on 21/10/2015.
@ -60,20 +48,16 @@ import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
* @param <TActivity> Type of activity where such {@link ViewController} could be;
* @param <TFragment> Type of fragment where such {@link ViewController} could be;
*/
@SuppressWarnings({"PMD.TooManyMethods", "PMD.ExcessivePublicCount"})
public class ViewController<TActivity extends ViewControllerActivity<?>,
TFragment extends ViewControllerFragment<?, TActivity>>
implements LifecycleBindable {
public class ViewController<TActivity extends FragmentActivity, TFragment extends ViewControllerFragment<?, TActivity>> implements LifecycleOwner {
@NonNull
private final LifecycleRegistry lifecycleRegistry = new LifecycleRegistry(this);
@NonNull
private final TActivity activity;
@NonNull
private final TFragment fragment;
@NonNull
private final ViewGroup container;
@NonNull
private final BaseLifecycleBindable baseLifecycleBindable = new BaseLifecycleBindable();
private boolean destroyed;
@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter"})
//UnusedFormalParameter: savedInstanceState could be used by children
@ -83,6 +67,12 @@ public class ViewController<TActivity extends ViewControllerActivity<?>,
this.container = creationContext.container;
}
@NonNull
@Override
public Lifecycle getLifecycle() {
return lifecycleRegistry;
}
/**
* Returns activity where {@link ViewController} could be.
*
@ -114,37 +104,6 @@ public class ViewController<TActivity extends ViewControllerActivity<?>,
return container;
}
/**
* Returns if {@link ViewController} destroyed or not.
*
* @return True if it is destroyed.
*/
public final boolean isDestroyed() {
return destroyed;
}
/**
* Return a localized string from the application's package's default string table.
*
* @param resId Resource id for the string
*/
@NonNull
public final String getString(@StringRes final int resId) {
return getActivity().getString(resId);
}
/**
* Return a localized formatted string from the application's package's default string table, substituting the format arguments as defined in
* {@link java.util.Formatter} and {@link java.lang.String#format}.
*
* @param resId Resource id for the format string
* @param formatArgs The format arguments that will be used for substitution.
*/
@NonNull
public final String getString(@StringRes final int resId, @NonNull final Object... formatArgs) {
return getActivity().getString(resId, formatArgs);
}
/**
* Set the view controller content from a layout resource.
* This layout is placed directly into the container's ({@link #getContainer()}) view hierarchy.
@ -189,39 +148,8 @@ public class ViewController<TActivity extends ViewControllerActivity<?>,
* @return The view that has the given id in the hierarchy.
*/
@NonNull
@SuppressWarnings("unchecked")
public <T extends View> T findViewById(@IdRes final int id) {
final T viewById = (T) getContainer().findViewById(id);
if (viewById == null) {
throw new ShouldNotHappenException("No view for id=" + getActivity().getResources().getResourceName(id));
}
return viewById;
}
/**
* Return the color value associated with a particular resource ID.
* Starting in {@link android.os.Build.VERSION_CODES#M}, the returned
* color will be styled for the specified Context's theme.
*
* @param resId The resource id to search for data;
* @return int A single color value in the form 0xAARRGGBB.
*/
@ColorInt
public int getColor(@ColorRes final int resId) {
return getActivity().getColorCompat(resId);
}
/**
* Returns a drawable object associated with a particular resource ID.
* Starting in {@link android.os.Build.VERSION_CODES#LOLLIPOP}, the
* returned drawable will be styled for the specified Context's theme.
*
* @param resId The resource id to search for data;
* @return Drawable An object that can be used to draw this resource.
*/
@NonNull
public Drawable getDrawable(@DrawableRes final int resId) {
return getActivity().getDrawableCompat(resId);
public final <T extends View> T findViewById(@IdRes final int id) {
return getContainer().findViewById(id);
}
/**
@ -231,28 +159,28 @@ public class ViewController<TActivity extends ViewControllerActivity<?>,
* @param menu The options menu in which you place your items;
* @param inflater Helper to inflate menu items.
*/
public void onConfigureNavigation(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {
public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {
// do nothing
}
/**
* Calls right after construction of {@link ViewController}.
* Happens at {@link ViewControllerFragment#onActivityCreated(View, ViewControllerActivity, Bundle)}.
* Happens at {@link ViewControllerFragment#onActivityCreated(Bundle)}.
*/
@CallSuper
public void onCreate() {
UiUtils.UI_LIFECYCLE_LC_GROUP.i(Lc.getCodePoint(this));
baseLifecycleBindable.onCreate();
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
}
/**
* Calls when {@link ViewController} have started.
* Happens at {@link ViewControllerFragment#onStart(View, ViewControllerActivity)}.
* Happens at {@link ViewControllerFragment#onStart()}.
*/
@CallSuper
public void onStart() {
UiUtils.UI_LIFECYCLE_LC_GROUP.i(Lc.getCodePoint(this));
baseLifecycleBindable.onStart();
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
}
/**
@ -265,12 +193,12 @@ public class ViewController<TActivity extends ViewControllerActivity<?>,
/**
* Calls when {@link ViewController} have resumed.
* Happens at {@link ViewControllerFragment#onResume(View, ViewControllerActivity)}.
* Happens at {@link ViewControllerFragment#onResume()}.
*/
@CallSuper
public void onResume() {
UiUtils.UI_LIFECYCLE_LC_GROUP.i(Lc.getCodePoint(this));
baseLifecycleBindable.onResume();
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
}
/**
@ -284,11 +212,12 @@ public class ViewController<TActivity extends ViewControllerActivity<?>,
/**
* Calls when {@link ViewController} have paused.
* Happens at {@link ViewControllerFragment#onPause(View, ViewControllerActivity)}.
* Happens at {@link ViewControllerFragment#onPause()}.
*/
@CallSuper
public void onPause() {
UiUtils.UI_LIFECYCLE_LC_GROUP.i(Lc.getCodePoint(this));
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE);
}
/**
@ -298,7 +227,6 @@ public class ViewController<TActivity extends ViewControllerActivity<?>,
*/
@CallSuper
public void onSaveInstanceState(@NonNull final Bundle savedInstanceState) {
baseLifecycleBindable.onSaveInstanceState();
UiUtils.UI_LIFECYCLE_LC_GROUP.i(Lc.getCodePoint(this));
}
@ -312,30 +240,29 @@ public class ViewController<TActivity extends ViewControllerActivity<?>,
/**
* Calls when {@link ViewController} have stopped.
* Happens at {@link ViewControllerFragment#onStop(View, ViewControllerActivity)}.
* Happens at {@link ViewControllerFragment#onStop()}.
*/
@CallSuper
public void onStop() {
UiUtils.UI_LIFECYCLE_LC_GROUP.i(Lc.getCodePoint(this));
baseLifecycleBindable.onStop();
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
}
/**
* Calls when {@link ViewController} have destroyed.
* Happens usually at {@link ViewControllerFragment#onDestroyView(View)}. In some cases at {@link ViewControllerFragment#onDestroy()}.
* Happens usually at {@link ViewControllerFragment#onDestroyView()}. In some cases at {@link ViewControllerFragment#onDestroy()}.
*/
@CallSuper
public void onDestroy() {
UiUtils.UI_LIFECYCLE_LC_GROUP.i(Lc.getCodePoint(this));
baseLifecycleBindable.onDestroy();
destroyed = true;
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
}
/**
* Callback from parent fragment.
*/
public void onActivityResult(final int requestCode, final int resultCode, @Nullable final Intent data) {
// Do nothing
UiUtils.UI_LIFECYCLE_LC_GROUP.i(Lc.getCodePoint(this));
}
/**
@ -348,200 +275,23 @@ public class ViewController<TActivity extends ViewControllerActivity<?>,
return false;
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Observable<T> observable) {
return baseLifecycleBindable.untilStop(observable);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Observable<T> observable, @NonNull final Consumer<T> onNextAction) {
return baseLifecycleBindable.untilStop(observable, onNextAction);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Observable<T> observable,
@NonNull final Consumer<T> onNextAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return baseLifecycleBindable.untilStop(observable, onNextAction, onErrorAction);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Observable<T> observable,
@NonNull final Consumer<T> onNextAction,
@NonNull final Consumer<Throwable> onErrorAction,
@NonNull final Action onCompletedAction) {
return baseLifecycleBindable.untilStop(observable, onNextAction, onErrorAction, onCompletedAction);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Single<T> single) {
return baseLifecycleBindable.untilStop(single);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Single<T> single, @NonNull final Consumer<T> onSuccessAction) {
return baseLifecycleBindable.untilStop(single, onSuccessAction);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Single<T> single,
@NonNull final Consumer<T> onSuccessAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return baseLifecycleBindable.untilStop(single, onSuccessAction, onErrorAction);
}
@NonNull
@Override
public Disposable untilStop(@NonNull final Completable completable) {
return baseLifecycleBindable.untilStop(completable);
}
@NonNull
@Override
public Disposable untilStop(@NonNull final Completable completable, @NonNull final Action onCompletedAction) {
return baseLifecycleBindable.untilStop(completable, onCompletedAction);
}
@NonNull
@Override
public Disposable untilStop(@NonNull final Completable completable,
@NonNull final Action onCompletedAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return baseLifecycleBindable.untilStop(completable, onCompletedAction, onErrorAction);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Maybe<T> maybe) {
return baseLifecycleBindable.untilStop(maybe);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Maybe<T> maybe, @NonNull final Consumer<T> onSuccessAction) {
return baseLifecycleBindable.untilStop(maybe, onSuccessAction);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Maybe<T> maybe,
@NonNull final Consumer<T> onSuccessAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return baseLifecycleBindable.untilStop(maybe, onSuccessAction, onErrorAction);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Observable<T> observable) {
return baseLifecycleBindable.untilDestroy(observable);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Observable<T> observable, @NonNull final Consumer<T> onNextAction) {
return baseLifecycleBindable.untilDestroy(observable, onNextAction);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Observable<T> observable,
@NonNull final Consumer<T> onNextAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return baseLifecycleBindable.untilDestroy(observable, onNextAction, onErrorAction);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Observable<T> observable,
@NonNull final Consumer<T> onNextAction,
@NonNull final Consumer<Throwable> onErrorAction,
@NonNull final Action onCompletedAction) {
return baseLifecycleBindable.untilDestroy(observable, onNextAction, onErrorAction, onCompletedAction);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Single<T> single) {
return baseLifecycleBindable.untilDestroy(single);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Single<T> single, @NonNull final Consumer<T> onSuccessAction) {
return baseLifecycleBindable.untilDestroy(single, onSuccessAction);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Single<T> single,
@NonNull final Consumer<T> onSuccessAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return baseLifecycleBindable.untilDestroy(single, onSuccessAction, onErrorAction);
}
@NonNull
@Override
public Disposable untilDestroy(@NonNull final Completable completable) {
return baseLifecycleBindable.untilDestroy(completable);
}
@NonNull
@Override
public Disposable untilDestroy(@NonNull final Completable completable, @NonNull final Action onCompletedAction) {
return baseLifecycleBindable.untilDestroy(completable, onCompletedAction);
}
@NonNull
@Override
public Disposable untilDestroy(@NonNull final Completable completable,
@NonNull final Action onCompletedAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return baseLifecycleBindable.untilDestroy(completable, onCompletedAction, onErrorAction);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Maybe<T> maybe) {
return baseLifecycleBindable.untilDestroy(maybe);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Maybe<T> maybe, @NonNull final Consumer<T> onCompletedAction) {
return baseLifecycleBindable.untilDestroy(maybe, onCompletedAction);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Maybe<T> maybe,
@NonNull final Consumer<T> onCompletedAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return baseLifecycleBindable.untilDestroy(maybe, onCompletedAction, onErrorAction);
}
@SuppressWarnings("CPD-END")
/*
* Helper class to simplify constructor override.
*/
public static class CreationContext {
@NonNull
private final ViewControllerActivity activity;
private final FragmentActivity activity;
@NonNull
private final ViewControllerFragment fragment;
@NonNull
private final ViewGroup container;
public CreationContext(@NonNull final ViewControllerActivity activity,
@NonNull final ViewControllerFragment fragment,
@NonNull final ViewGroup container) {
public CreationContext(
@NonNull final FragmentActivity activity,
@NonNull final ViewControllerFragment fragment,
@NonNull final ViewGroup container
) {
this.activity = activity;
this.fragment = fragment;
this.container = container;
@ -549,4 +299,4 @@ public class ViewController<TActivity extends ViewControllerActivity<?>,
}
}
}

View File

@ -1,504 +0,0 @@
/*
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package ru.touchin.roboswag.components.navigation;
import android.content.Context;
import android.support.annotation.IdRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import io.reactivex.functions.Function;
import ru.touchin.roboswag.components.navigation.activities.ViewControllerActivity;
import ru.touchin.roboswag.components.navigation.fragments.SimpleViewControllerFragment;
import ru.touchin.roboswag.components.navigation.fragments.StatelessTargetedViewControllerFragment;
import ru.touchin.roboswag.components.navigation.fragments.StatelessViewControllerFragment;
import ru.touchin.roboswag.components.navigation.fragments.TargetedViewControllerFragment;
import ru.touchin.roboswag.components.navigation.fragments.ViewControllerFragment;
/**
* Created by Gavriil Sitnikov on 07/03/2016.
* Navigation based on {@link ViewController}s which are creating by {@link Fragment}s.
* So basically it is just {@link FragmentNavigation} where most of fragments should be inherited from {@link ViewControllerFragment}.
*
* @param <TActivity> Type of activity where {@link ViewController}s should be showed.
*/
public class ViewControllerNavigation<TActivity extends ViewControllerActivity<?>> extends FragmentNavigation {
public ViewControllerNavigation(@NonNull final Context context,
@NonNull final FragmentManager fragmentManager,
@IdRes final int containerViewId) {
super(context, fragmentManager, containerViewId);
}
/**
* Pushes {@link ViewControllerFragment} on top of stack.
*
* @param fragmentClass Class of {@link ViewControllerFragment} to instantiate;
* @param state Specific {@link AbstractState} of {@link ViewControllerFragment};
* @param <TState> Type of state of fragment.
*/
public <TState extends AbstractState> void push(@NonNull final Class<? extends ViewControllerFragment<TState, TActivity>> fragmentClass,
@NonNull final TState state) {
addToStack(fragmentClass, null, true, ViewControllerFragment.createState(state), null, null);
}
/**
* Pushes {@link ViewControllerFragment} on top of stack with specific transaction setup.
*
* @param fragmentClass Class of {@link ViewControllerFragment} to instantiate;
* @param state Specific {@link AbstractState} of {@link ViewControllerFragment};
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info;
* @param <TState> Type of state of fragment.
*/
public <TState extends AbstractState> void push(@NonNull final Class<? extends ViewControllerFragment<TState, TActivity>> fragmentClass,
@Nullable final TState state,
@Nullable final Function<FragmentTransaction, FragmentTransaction> transactionSetup) {
addToStack(fragmentClass, null, true, ViewControllerFragment.createState(state), null, transactionSetup);
}
/**
* Pushes {@link ViewControllerFragment} on top of stack with specific target fragment.
*
* @param fragmentClass Class of {@link ViewControllerFragment} to instantiate;
* @param targetFragment Target fragment to be set as {@link Fragment#getTargetFragment()} of instantiated {@link Fragment};
* @param state Specific {@link AbstractState} of {@link ViewControllerFragment};
* @param <TState> Type of state of fragment.
*/
public <TState extends AbstractState> void pushForResult(@NonNull final Class<? extends ViewControllerFragment<TState, TActivity>> fragmentClass,
@NonNull final Fragment targetFragment,
@NonNull final TState state) {
addToStack(fragmentClass, targetFragment, true, ViewControllerFragment.createState(state),
fragmentClass.getName() + ';' + WITH_TARGET_FRAGMENT_TAG_MARK, null);
}
/**
* Pushes {@link ViewControllerFragment} on top of stack with specific target fragment and specific transaction setup.
*
* @param fragmentClass Class of {@link ViewControllerFragment} to instantiate;
* @param targetFragment Target fragment to be set as {@link Fragment#getTargetFragment()} of instantiated {@link Fragment};
* @param state Specific {@link AbstractState} of {@link ViewControllerFragment};
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info;
* @param <TState> Type of state of fragment.
*/
public <TState extends AbstractState> void pushForResult(@NonNull final Class<? extends ViewControllerFragment<TState, TActivity>> fragmentClass,
@NonNull final Fragment targetFragment,
@Nullable final TState state,
@Nullable final Function<FragmentTransaction, FragmentTransaction> transactionSetup) {
addToStack(fragmentClass, targetFragment, true, ViewControllerFragment.createState(state),
fragmentClass.getName() + ';' + WITH_TARGET_FRAGMENT_TAG_MARK, transactionSetup);
}
/**
* Pushes {@link ViewControllerFragment} on top of stack with {@link #TOP_FRAGMENT_TAG_MARK} tag used for simple up/back navigation.
*
* @param fragmentClass Class of {@link ViewControllerFragment} to instantiate.
* @param state Specific {@link AbstractState} of {@link ViewControllerFragment};
* @param <TState> Type of state of fragment.
*/
public <TState extends AbstractState> void setAsTop(@NonNull final Class<? extends ViewControllerFragment<TState, TActivity>> fragmentClass,
@NonNull final TState state) {
setAsTop(fragmentClass, ViewControllerFragment.createState(state), null);
}
/**
* Pushes {@link ViewControllerFragment} on top of stack with specific transaction setup
* and with {@link #TOP_FRAGMENT_TAG_MARK} tag used for simple up/back navigation.
*
* @param fragmentClass Class of {@link ViewControllerFragment} to instantiate.
* @param state Specific {@link AbstractState} of {@link ViewControllerFragment};
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info;
* @param <TState> Type of state of fragment.
*/
public <TState extends AbstractState> void setAsTop(@NonNull final Class<? extends ViewControllerFragment<TState, TActivity>> fragmentClass,
@Nullable final TState state,
@Nullable final Function<FragmentTransaction, FragmentTransaction> transactionSetup) {
setAsTop(fragmentClass, ViewControllerFragment.createState(state), transactionSetup);
}
/**
* Pops all {@link Fragment}s and places new initial {@link ViewControllerFragment} on top of stack.
*
* @param fragmentClass Class of {@link ViewControllerFragment} to instantiate;
* @param state Specific {@link AbstractState} of {@link ViewControllerFragment};
* @param <TState> Type of state of fragment.
*/
public <TState extends AbstractState> void setInitial(@NonNull final Class<? extends ViewControllerFragment<TState, TActivity>> fragmentClass,
@NonNull final TState state) {
setInitial(fragmentClass, ViewControllerFragment.createState(state), null);
}
/**
* Pops all {@link Fragment}s and places new initial {@link ViewControllerFragment} on top of stack with specific transaction setup.
*
* @param fragmentClass Class of {@link ViewControllerFragment} to instantiate;
* @param state Specific {@link AbstractState} of {@link ViewControllerFragment};
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info;
* @param <TState> Type of state of fragment.
*/
public <TState extends AbstractState> void setInitial(@NonNull final Class<? extends ViewControllerFragment<TState, TActivity>> fragmentClass,
@Nullable final TState state,
@Nullable final Function<FragmentTransaction, FragmentTransaction> transactionSetup) {
setInitial(fragmentClass, ViewControllerFragment.createState(state), transactionSetup);
}
/**
* Pushes {@link ViewController} on top of stack.
*
* @param viewControllerClass Class of {@link ViewController} to be pushed.
*/
public void pushViewController(@NonNull final Class<? extends ViewController<TActivity,
StatelessViewControllerFragment<TActivity>>> viewControllerClass) {
addStatelessViewControllerToStack(viewControllerClass, null, null, null);
}
/**
* Pushes {@link ViewController} on top of stack with specific {@link ViewControllerFragment#getState()}.
*
* @param viewControllerClass Class of {@link ViewController} to be pushed;
* @param state {@link AbstractState} of {@link ViewController}'s fragment;
* @param <TState> Type of state of fragment.
*/
public <TState extends AbstractState> void pushViewController(@NonNull final Class<? extends ViewController<TActivity,
SimpleViewControllerFragment<TState, TActivity>>> viewControllerClass,
@NonNull final TState state) {
addViewControllerToStack(viewControllerClass, null, state, null, null);
}
/**
* Pushes {@link ViewController} on top of stack with specific transaction setup.
*
* @param viewControllerClass Class of {@link ViewController} to be pushed;
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info;.
*/
public void pushViewController(
@NonNull final Class<? extends ViewController<TActivity, StatelessViewControllerFragment<TActivity>>> viewControllerClass,
@Nullable final Function<FragmentTransaction, FragmentTransaction> transactionSetup) {
addStatelessViewControllerToStack(viewControllerClass, null, null, transactionSetup);
}
/**
* Pushes {@link ViewController} on top of stack with specific {@link ViewControllerFragment#getState()} and with specific transaction setup.
*
* @param viewControllerClass Class of {@link ViewController} to be pushed;
* @param state {@link AbstractState} of {@link ViewController}'s fragment;
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info;
* @param <TState> Type of state of fragment.
*/
public <TState extends AbstractState> void pushViewController(
@NonNull final Class<? extends ViewController<TActivity, SimpleViewControllerFragment<TState, TActivity>>> viewControllerClass,
@NonNull final TState state,
@Nullable final Function<FragmentTransaction, FragmentTransaction> transactionSetup) {
addViewControllerToStack(viewControllerClass, null, state, null, transactionSetup);
}
/**
* Pushes {@link ViewController} without adding to stack.
*
* @param viewControllerClass Class of {@link ViewController} to be pushed.
*/
public void pushSingleViewController(@NonNull final Class<? extends ViewController<TActivity,
StatelessViewControllerFragment<TActivity>>> viewControllerClass) {
addToStack(StatelessViewControllerFragment.class, null, false, StatelessViewControllerFragment.createState(viewControllerClass), null, null);
}
/**
* Pushes {@link ViewController} without adding to stack and with specific {@link ViewControllerFragment#getState()}.
*
* @param viewControllerClass Class of {@link ViewController} to be pushed;
* @param state {@link AbstractState} of {@link ViewController}'s fragment;
* @param <TState> Type of state of fragment.
*/
public <TState extends AbstractState> void pushSingleViewController(@NonNull final Class<? extends ViewController<TActivity,
SimpleViewControllerFragment<TState, TActivity>>> viewControllerClass, @NonNull final TState state) {
addToStack(SimpleViewControllerFragment.class, null, false, SimpleViewControllerFragment.createState(viewControllerClass, state), null, null);
}
/**
* Pushes {@link ViewController} on top of stack with specific {@link StatelessTargetedViewControllerFragment#getTarget()}.
*
* @param viewControllerClass Class of {@link ViewController} to be pushed;
* @param targetFragment {@link ViewControllerFragment} to be set as target;
* @param <TTargetState> Type of state of target fragment. State is using to affect on that fragment;
* @param <TTargetFragment> Type of target fragment.
*/
public <TTargetState extends AbstractState,
TTargetFragment extends ViewControllerFragment<? extends TTargetState, TActivity>> void pushViewControllerForResult(
@NonNull final Class<? extends ViewController<TActivity,
StatelessTargetedViewControllerFragment<TTargetState, TActivity>>> viewControllerClass,
@NonNull final TTargetFragment targetFragment) {
addTargetedStatelessViewControllerToStack(viewControllerClass, targetFragment,
viewControllerClass.getName() + ';' + WITH_TARGET_FRAGMENT_TAG_MARK, null);
}
/**
* Pushes {@link ViewController} on top of stack with specific {@link StatelessTargetedViewControllerFragment#getTarget()} and transaction setup.
*
* @param viewControllerClass Class of {@link ViewController} to be pushed;
* @param targetFragment {@link ViewControllerFragment} to be set as target;
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info;
* @param <TTargetState> Type of state of target fragment. State is using to affect on that fragment;
* @param <TTargetFragment> Type of target fragment.
*/
public <TTargetState extends AbstractState,
TTargetFragment extends ViewControllerFragment<? extends TTargetState, TActivity>> void pushViewControllerForResult(
@NonNull final Class<? extends ViewController<TActivity,
StatelessTargetedViewControllerFragment<TTargetState, TActivity>>> viewControllerClass,
@NonNull final TTargetFragment targetFragment,
@Nullable final Function<FragmentTransaction, FragmentTransaction> transactionSetup) {
addTargetedStatelessViewControllerToStack(viewControllerClass, targetFragment,
viewControllerClass.getName() + ';' + WITH_TARGET_FRAGMENT_TAG_MARK, transactionSetup);
}
/**
* Pushes {@link ViewController} on top of stack with specific with specific {@link ViewControllerFragment#getState()}
* and with specific {@link TargetedViewControllerFragment#getTarget()}.
*
* @param viewControllerClass Class of {@link ViewController} to be pushed;
* @param targetFragment {@link ViewControllerFragment} to be set as target;
* @param state {@link AbstractState} of {@link ViewController}'s fragment;
* @param <TState> Type of state of fragment;
* @param <TTargetState> Type of state of target fragment. State is using to affect on that fragment;
* @param <TTargetFragment> Type of target fragment.
*/
@SuppressWarnings("CPD-START")
public <TState extends AbstractState, TTargetState extends AbstractState,
TTargetFragment extends ViewControllerFragment<? extends TTargetState, TActivity>> void pushViewControllerForResult(
@NonNull final Class<? extends ViewController<TActivity,
TargetedViewControllerFragment<TState, TTargetState, TActivity>>> viewControllerClass,
@NonNull final TTargetFragment targetFragment,
@NonNull final TState state) {
addTargetedViewControllerToStack(viewControllerClass, targetFragment, state,
viewControllerClass.getName() + ';' + WITH_TARGET_FRAGMENT_TAG_MARK, null);
}
/**
* Pushes {@link ViewController} on top of stack with specific {@link ViewControllerFragment#getState()}
* and with specific {@link TargetedViewControllerFragment#getTarget()} and transaction setup.
*
* @param viewControllerClass Class of {@link ViewController} to be pushed;
* @param targetFragment {@link ViewControllerFragment} to be set as target;
* @param state {@link AbstractState} of {@link ViewController}'s fragment;
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info;
* @param <TState> Type of state of fragment;
* @param <TTargetState> Type of state of target fragment. State is using to affect on that fragment;
* @param <TTargetFragment> Type of target fragment.
*/
@SuppressWarnings("CPD-END")
public <TState extends AbstractState, TTargetState extends AbstractState,
TTargetFragment extends ViewControllerFragment<? extends TTargetState, TActivity>> void pushViewControllerForResult(
@NonNull final Class<? extends ViewController<TActivity,
TargetedViewControllerFragment<TState, TTargetState, TActivity>>> viewControllerClass,
@NonNull final TTargetFragment targetFragment,
@NonNull final TState state,
@Nullable final Function<FragmentTransaction, FragmentTransaction> transactionSetup) {
addTargetedViewControllerToStack(viewControllerClass, targetFragment, state,
viewControllerClass.getName() + ';' + WITH_TARGET_FRAGMENT_TAG_MARK, transactionSetup);
}
/**
* Pushes {@link ViewController} on top of stack with {@link #TOP_FRAGMENT_TAG_MARK} tag used for simple up/back navigation.
*
* @param viewControllerClass Class of {@link ViewController} to be pushed.
*/
public void setViewControllerAsTop(
@NonNull final Class<? extends ViewController<TActivity, StatelessViewControllerFragment<TActivity>>> viewControllerClass) {
addStatelessViewControllerToStack(viewControllerClass, null, viewControllerClass.getName() + ' ' + TOP_FRAGMENT_TAG_MARK, null);
}
/**
* Pushes {@link ViewController} on top of stack with specific {@link ViewControllerFragment#getState()}
* and with {@link #TOP_FRAGMENT_TAG_MARK} tag used for simple up/back navigation.
*
* @param viewControllerClass Class of {@link ViewController} to be pushed;
* @param state {@link AbstractState} of {@link ViewController}'s fragment;
* @param <TState> Type of state of fragment.
*/
public <TState extends AbstractState> void setViewControllerAsTop(
@NonNull final Class<? extends ViewController<TActivity, SimpleViewControllerFragment<TState, TActivity>>> viewControllerClass,
@NonNull final TState state) {
addViewControllerToStack(viewControllerClass, null, state, viewControllerClass.getName() + ' ' + TOP_FRAGMENT_TAG_MARK, null);
}
/**
* Pushes {@link ViewController} on top of stack with specific transaction setup
* and with {@link #TOP_FRAGMENT_TAG_MARK} tag used for simple up/back navigation.
*
* @param viewControllerClass Class of {@link ViewController} to be pushed;
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info.
*/
public void setViewControllerAsTop(
@NonNull final Class<? extends ViewController<TActivity, StatelessViewControllerFragment<TActivity>>> viewControllerClass,
@Nullable final Function<FragmentTransaction, FragmentTransaction> transactionSetup) {
addStatelessViewControllerToStack(viewControllerClass, null, viewControllerClass.getName() + ' ' + TOP_FRAGMENT_TAG_MARK, transactionSetup);
}
/**
* Pushes {@link ViewController} on top of stack with specific {@link ViewControllerFragment#getState()} and with specific transaction setup
* and with {@link #TOP_FRAGMENT_TAG_MARK} tag used for simple up/back navigation.
*
* @param viewControllerClass Class of {@link ViewController} to be pushed;
* @param state {@link AbstractState} of {@link ViewController}'s fragment;
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info;
* @param <TState> Type of state of fragment.
*/
public <TState extends AbstractState> void setViewControllerAsTop(
@NonNull final Class<? extends ViewController<TActivity, SimpleViewControllerFragment<TState, TActivity>>> viewControllerClass,
@NonNull final TState state,
@Nullable final Function<FragmentTransaction, FragmentTransaction> transactionSetup) {
addViewControllerToStack(viewControllerClass, null, state, viewControllerClass.getName() + ' ' + TOP_FRAGMENT_TAG_MARK, transactionSetup);
}
/**
* Pops all {@link Fragment}s and places new initial {@link ViewController} on top of stack.
*
* @param viewControllerClass Class of {@link ViewController} to be pushed;
*/
public void setInitialViewController(
@NonNull final Class<? extends ViewController<TActivity, StatelessViewControllerFragment<TActivity>>> viewControllerClass) {
beforeSetInitialActions();
setViewControllerAsTop(viewControllerClass);
}
/**
* Pops all {@link Fragment}s and places new initial {@link ViewController} on top of stack
* with specific {@link ViewControllerFragment#getState()}.
*
* @param viewControllerClass Class of {@link ViewController} to be pushed;
* @param state {@link AbstractState} of {@link ViewController}'s fragment;
* @param <TState> Type of state of fragment.
*/
public <TState extends AbstractState> void setInitialViewController(
@NonNull final Class<? extends ViewController<TActivity, SimpleViewControllerFragment<TState, TActivity>>> viewControllerClass,
@NonNull final TState state) {
setInitialViewController(viewControllerClass, state, null);
}
/**
* Pops all {@link Fragment}s and places new initial {@link ViewController} on top of stack with specific transaction setup.
*
* @param viewControllerClass Class of {@link ViewController} to be pushed;
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info;
*/
public void setInitialViewController(
@NonNull final Class<? extends ViewController<TActivity, StatelessViewControllerFragment<TActivity>>> viewControllerClass,
@Nullable final Function<FragmentTransaction, FragmentTransaction> transactionSetup) {
beforeSetInitialActions();
setViewControllerAsTop(viewControllerClass, transactionSetup);
}
/**
* Pops all {@link Fragment}s and places new initial {@link ViewController} on top of stack
* with specific {@link ViewControllerFragment#getState()} and specific transaction setup.
*
* @param viewControllerClass Class of {@link ViewController} to be pushed;
* @param state {@link AbstractState} of {@link ViewController}'s fragment;
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info;
* @param <TState> Type of state of fragment.
*/
public <TState extends AbstractState> void setInitialViewController(
@NonNull final Class<? extends ViewController<TActivity, SimpleViewControllerFragment<TState, TActivity>>> viewControllerClass,
@NonNull final TState state,
@Nullable final Function<FragmentTransaction, FragmentTransaction> transactionSetup) {
beforeSetInitialActions();
setViewControllerAsTop(viewControllerClass, state, transactionSetup);
}
/**
* Base method to push stateless {@link ViewControllerFragment} to stack.
*
* @param viewControllerClass Class of {@link ViewController} to be pushed;
* @param targetFragment {@link ViewControllerFragment} to be set as target;
* @param backStackTag Tag of {@link ViewControllerFragment} in back stack;
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info.
*/
protected void addStatelessViewControllerToStack(
@NonNull final Class<? extends ViewController<TActivity, ? extends StatelessViewControllerFragment<TActivity>>> viewControllerClass,
@Nullable final Fragment targetFragment,
@Nullable final String backStackTag,
@Nullable final Function<FragmentTransaction, FragmentTransaction> transactionSetup) {
addToStack(StatelessViewControllerFragment.class, targetFragment, true,
StatelessViewControllerFragment.createState(viewControllerClass), backStackTag, transactionSetup);
}
/**
* Base method to push stateful {@link ViewControllerFragment} with target to stack.
*
* @param viewControllerClass Class of {@link ViewController} to be pushed;
* @param targetFragment {@link ViewControllerFragment} to be set as target;
* @param state {@link AbstractState} of {@link ViewController}'s fragment;
* @param backStackTag Tag of {@link ViewControllerFragment} in back stack;
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info;
* @param <TState> Type of state of fragment.
* @param <TTargetState> Type of state of target fragment. State is using to affect on that fragment;
*/
protected <TState extends AbstractState, TTargetState extends AbstractState> void addTargetedViewControllerToStack(
@NonNull final Class<? extends ViewController<TActivity,
? extends TargetedViewControllerFragment<TState, TTargetState, TActivity>>> viewControllerClass,
@NonNull final Fragment targetFragment,
@NonNull final TState state,
@Nullable final String backStackTag,
@Nullable final Function<FragmentTransaction, FragmentTransaction> transactionSetup) {
addToStack(TargetedViewControllerFragment.class, targetFragment, true,
TargetedViewControllerFragment.createState(viewControllerClass, state), backStackTag, transactionSetup);
}
/**
* Base method to push stateless {@link ViewControllerFragment} with target to stack.
*
* @param viewControllerClass Class of {@link ViewController} to be pushed;
* @param targetFragment {@link ViewControllerFragment} to be set as target;
* @param backStackTag Tag of {@link ViewControllerFragment} in back stack;
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info;
* @param <TState> Type of state of fragment.
*/
protected <TState extends AbstractState> void addTargetedStatelessViewControllerToStack(
@NonNull final Class<? extends ViewController<TActivity,
? extends StatelessTargetedViewControllerFragment<TState, TActivity>>> viewControllerClass,
@NonNull final Fragment targetFragment,
@Nullable final String backStackTag,
@Nullable final Function<FragmentTransaction, FragmentTransaction> transactionSetup) {
addToStack(StatelessTargetedViewControllerFragment.class, targetFragment, true,
StatelessTargetedViewControllerFragment.createState(viewControllerClass), backStackTag, transactionSetup);
}
/**
* Base method to push stateful {@link ViewControllerFragment} to stack.
*
* @param viewControllerClass Class of {@link ViewController} to be pushed;
* @param targetFragment {@link ViewControllerFragment} to be set as target;
* @param state {@link AbstractState} of {@link ViewController}'s fragment;
* @param backStackTag Tag of {@link ViewControllerFragment} in back stack;
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info;
* @param <TState> Type of state of fragment.
*/
protected <TState extends AbstractState> void addViewControllerToStack(
@NonNull final Class<? extends ViewController<TActivity, ? extends SimpleViewControllerFragment<TState, TActivity>>> viewControllerClass,
@Nullable final Fragment targetFragment,
@NonNull final TState state,
@Nullable final String backStackTag,
@Nullable final Function<FragmentTransaction, FragmentTransaction> transactionSetup) {
addToStack(SimpleViewControllerFragment.class, targetFragment, true,
SimpleViewControllerFragment.createState(viewControllerClass, state), backStackTag, transactionSetup);
}
}

View File

@ -0,0 +1,174 @@
/*
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package ru.touchin.roboswag.components.navigation
import android.content.Context
import android.os.Parcelable
import android.support.annotation.IdRes
import android.support.v4.app.Fragment
import android.support.v4.app.FragmentActivity
import android.support.v4.app.FragmentManager
import android.support.v4.app.FragmentTransaction
import io.reactivex.functions.Function
import ru.touchin.roboswag.components.navigation.fragments.ViewControllerFragment
/**
* Created by Gavriil Sitnikov on 07/03/2016.
* Navigation based on [ViewController]s which are creating by [Fragment]s.
* So basically it is just [FragmentNavigation] where most of fragments should be inherited from [ViewControllerFragment].
*
* @param TActivity Type of activity where [ViewController]s should be showed.
*/
open class ViewControllerNavigation<TActivity : FragmentActivity>(
context: Context,
fragmentManager: FragmentManager,
@IdRes containerViewId: Int
) : FragmentNavigation(context, fragmentManager, containerViewId) {
/**
* Pushes [ViewController] on top of stack with specific [ViewControllerFragment.getState] and with specific transaction setup.
*
* @param viewControllerClass Class of [ViewController] to be pushed;
* @param state [Parcelable] of [ViewController]'s fragment;
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info;
* @param TState Type of state of fragment.
*/
fun <TState : Parcelable> pushViewController(
viewControllerClass: Class<out ViewController<TActivity, ViewControllerFragment<TState, TActivity>>>,
state: TState,
transactionSetup: Function<FragmentTransaction, FragmentTransaction>? = null
) {
addViewControllerToStack(viewControllerClass, null, true, state, null, transactionSetup)
}
/**
* Pushes [ViewController] without adding to stack and with specific [ViewControllerFragment.getState].
*
* @param viewControllerClass Class of [ViewController] to be pushed;
* @param state [Parcelable] of [ViewController]'s fragment;
* @param TState Type of state of fragment.
*/
fun <TState : Parcelable> pushSingleViewController(
viewControllerClass: Class<out ViewController<TActivity, ViewControllerFragment<TState, TActivity>>>,
state: TState,
transactionSetup: Function<FragmentTransaction, FragmentTransaction>? = null
) {
addViewControllerToStack(viewControllerClass, null, false, state, null, transactionSetup)
}
/**
* Pushes [ViewController] on top of stack with specific [ViewControllerFragment.getState]
* and with specific [TTargetFragment] and transaction setup.
*
* @param viewControllerClass Class of [ViewController] to be pushed;
* @param targetFragment [ViewControllerFragment] to be set as target;
* @param state [Parcelable] of [ViewController]'s fragment;
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info;
* @param TState Type of state of fragment;
* @param TTargetFragment Type of target fragment.
*/
fun <TState : Parcelable, TTargetFragment : Fragment> pushViewControllerForResult(
viewControllerClass: Class<out ViewController<TActivity, ViewControllerFragment<TState, TActivity>>>,
state: TState,
targetFragment: TTargetFragment,
transactionSetup: Function<FragmentTransaction, FragmentTransaction>? = null
) {
addViewControllerToStack(
viewControllerClass,
targetFragment,
true,
state,
viewControllerClass.name + ';'.toString() + FragmentNavigation.WITH_TARGET_FRAGMENT_TAG_MARK,
transactionSetup
)
}
/**
* Pushes [ViewController] on top of stack with specific [ViewControllerFragment.getState] and with specific transaction setup
* and with [.TOP_FRAGMENT_TAG_MARK] tag used for simple up/back navigation.
*
* @param viewControllerClass Class of [ViewController] to be pushed;
* @param state [Parcelable] of [ViewController]'s fragment;
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info;
* @param TState Type of state of fragment.
*/
fun <TState : Parcelable> setViewControllerAsTop(
viewControllerClass: Class<out ViewController<TActivity, ViewControllerFragment<TState, TActivity>>>,
state: TState,
transactionSetup: Function<FragmentTransaction, FragmentTransaction>? = null
) {
addViewControllerToStack(
viewControllerClass,
null,
true,
state,
viewControllerClass.name + ';'.toString() + FragmentNavigation.TOP_FRAGMENT_TAG_MARK,
transactionSetup
)
}
/**
* Pops all [Fragment]s and places new initial [ViewController] on top of stack
* with specific [ViewControllerFragment.getState] and specific transaction setup.
*
* @param viewControllerClass Class of [ViewController] to be pushed;
* @param state [Parcelable] of [ViewController]'s fragment;
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info;
* @param TState Type of state of fragment.
*/
fun <TState : Parcelable> setInitialViewController(
viewControllerClass: Class<out ViewController<TActivity, ViewControllerFragment<TState, TActivity>>>,
state: TState,
transactionSetup: Function<FragmentTransaction, FragmentTransaction>? = null
) {
beforeSetInitialActions()
setViewControllerAsTop(viewControllerClass, state, transactionSetup)
}
/**
* Base method to push stateful [ViewControllerFragment] to stack.
*
* @param viewControllerClass Class of [ViewController] to be pushed;
* @param targetFragment [ViewControllerFragment] to be set as target;
* @param state [Parcelable] of [ViewController]'s fragment;
* @param backStackTag Tag of [ViewControllerFragment] in back stack;
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info;
* @param TState Type of state of fragment.
*/
protected fun <TState : Parcelable> addViewControllerToStack(
viewControllerClass: Class<out ViewController<TActivity, out ViewControllerFragment<TState, TActivity>>>,
targetFragment: Fragment?,
addToStack: Boolean,
state: TState,
backStackTag: String?,
transactionSetup: Function<FragmentTransaction, FragmentTransaction>?
) {
addToStack(
ViewControllerFragment::class.java,
targetFragment,
addToStack,
ViewControllerFragment.args(viewControllerClass, state),
backStackTag,
transactionSetup
)
}
}

View File

@ -35,130 +35,52 @@ import android.view.inputmethod.InputMethodManager;
import java.util.ArrayList;
import io.reactivex.Completable;
import io.reactivex.Maybe;
import io.reactivex.Observable;
import io.reactivex.Single;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Action;
import io.reactivex.functions.Consumer;
import io.reactivex.subjects.BehaviorSubject;
import ru.touchin.roboswag.components.utils.BaseLifecycleBindable;
import ru.touchin.roboswag.components.utils.LifecycleBindable;
import ru.touchin.roboswag.components.utils.UiUtils;
import ru.touchin.roboswag.core.log.Lc;
import ru.touchin.roboswag.core.utils.Optional;
import ru.touchin.roboswag.core.utils.pairs.HalfNullablePair;
/**
* Created by Gavriil Sitnikov on 08/03/2016.
* Base activity to use in components repository.
*/
@SuppressWarnings("PMD.TooManyMethods")
public abstract class BaseActivity extends AppCompatActivity
implements LifecycleBindable {
private static final String ACTIVITY_RESULT_CODE_EXTRA = "ACTIVITY_RESULT_CODE_EXTRA";
private static final String ACTIVITY_RESULT_DATA_EXTRA = "ACTIVITY_RESULT_DATA_EXTRA";
public abstract class BaseActivity extends AppCompatActivity {
@NonNull
private final ArrayList<OnBackPressedListener> onBackPressedListeners = new ArrayList<>();
@NonNull
private final BaseLifecycleBindable baseLifecycleBindable = new BaseLifecycleBindable();
private boolean resumed;
@NonNull
private final BehaviorSubject<Optional<HalfNullablePair<Integer, Intent>>> lastActivityResult
= BehaviorSubject.createDefault(new Optional<HalfNullablePair<Integer, Intent>>(null));
/**
* Returns if activity resumed.
*
* @return True if resumed.
*/
public boolean isActuallyResumed() {
return resumed;
}
@Override
protected void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
UiUtils.UI_LIFECYCLE_LC_GROUP.i(Lc.getCodePoint(this));
baseLifecycleBindable.onCreate();
restoreLastActivityResult(savedInstanceState);
}
private void restoreLastActivityResult(@Nullable final Bundle savedInstanceState) {
if (savedInstanceState == null) {
return;
}
lastActivityResult.onNext(new Optional<>(new HalfNullablePair<>(savedInstanceState.getInt(ACTIVITY_RESULT_CODE_EXTRA),
savedInstanceState.getParcelable(ACTIVITY_RESULT_DATA_EXTRA))));
}
@Override
protected void onActivityResult(final int requestCode, final int resultCode, @Nullable final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
UiUtils.UI_LIFECYCLE_LC_GROUP.i(Lc.getCodePoint(this) + " requestCode: " + requestCode + "; resultCode: " + resultCode);
if (resultCode == RESULT_OK) {
lastActivityResult.onNext(new Optional<>(new HalfNullablePair<>(requestCode, data)));
}
}
/**
* Observes activity result by request code coming from {@link #onActivityResult(int, int, Intent)}
*
* @param requestCode Unique code to identify activity result;
* @return {@link Observable} which will emit data (Intents) from other activities (endlessly).
*/
@NonNull
public Observable<Intent> observeActivityResult(final int requestCode) {
return lastActivityResult
.concatMap(optional -> {
final HalfNullablePair<Integer, Intent> activityResult = optional.get();
if (activityResult == null || activityResult.getFirst() != requestCode) {
return Observable.empty();
}
return Observable.just(activityResult.getSecond() != null ? activityResult.getSecond() : new Intent())
.doOnNext(result -> lastActivityResult.onNext(new Optional<>(null)));
});
}
@Override
protected void onStart() {
super.onStart();
UiUtils.UI_LIFECYCLE_LC_GROUP.i(Lc.getCodePoint(this));
baseLifecycleBindable.onStart();
}
@Override
protected void onResume() {
super.onResume();
UiUtils.UI_LIFECYCLE_LC_GROUP.i(Lc.getCodePoint(this));
resumed = true;
baseLifecycleBindable.onResume();
}
@Override
protected void onPause() {
UiUtils.UI_LIFECYCLE_LC_GROUP.i(Lc.getCodePoint(this));
resumed = false;
super.onPause();
}
@Override
protected void onSaveInstanceState(@NonNull final Bundle stateToSave) {
super.onSaveInstanceState(stateToSave);
baseLifecycleBindable.onSaveInstanceState();
UiUtils.UI_LIFECYCLE_LC_GROUP.i(Lc.getCodePoint(this));
final HalfNullablePair<Integer, Intent> activityResult = lastActivityResult.getValue().get();
if (activityResult != null) {
stateToSave.putInt(ACTIVITY_RESULT_CODE_EXTRA, activityResult.getFirst());
if (activityResult.getSecond() != null) {
stateToSave.putParcelable(ACTIVITY_RESULT_DATA_EXTRA, activityResult.getSecond());
}
}
}
@Override
@ -170,14 +92,12 @@ public abstract class BaseActivity extends AppCompatActivity
@Override
protected void onStop() {
UiUtils.UI_LIFECYCLE_LC_GROUP.i(Lc.getCodePoint(this));
baseLifecycleBindable.onStop();
super.onStop();
}
@Override
protected void onDestroy() {
UiUtils.UI_LIFECYCLE_LC_GROUP.i(Lc.getCodePoint(this));
baseLifecycleBindable.onDestroy();
super.onDestroy();
}
@ -229,7 +149,7 @@ public abstract class BaseActivity extends AppCompatActivity
* @param resId The resource id to search for data;
* @return Drawable An object that can be used to draw this resource.
*/
@NonNull
@Nullable
public Drawable getDrawableCompat(@DrawableRes final int resId) {
return ContextCompat.getDrawable(this, resId);
}
@ -257,187 +177,6 @@ public abstract class BaseActivity extends AppCompatActivity
}
}
@SuppressWarnings("CPD-START")
//CPD: it's ok as it's LifecycleBindable
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Observable<T> observable) {
return baseLifecycleBindable.untilStop(observable);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Observable<T> observable, @NonNull final Consumer<T> onNextAction) {
return baseLifecycleBindable.untilStop(observable, onNextAction);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Observable<T> observable,
@NonNull final Consumer<T> onNextAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return baseLifecycleBindable.untilStop(observable, onNextAction, onErrorAction);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Observable<T> observable,
@NonNull final Consumer<T> onNextAction,
@NonNull final Consumer<Throwable> onErrorAction,
@NonNull final Action onCompletedAction) {
return baseLifecycleBindable.untilStop(observable, onNextAction, onErrorAction, onCompletedAction);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Single<T> single) {
return baseLifecycleBindable.untilStop(single);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Single<T> single, @NonNull final Consumer<T> onSuccessAction) {
return baseLifecycleBindable.untilStop(single, onSuccessAction);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Single<T> single,
@NonNull final Consumer<T> onSuccessAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return baseLifecycleBindable.untilStop(single, onSuccessAction, onErrorAction);
}
@NonNull
@Override
public Disposable untilStop(@NonNull final Completable completable) {
return baseLifecycleBindable.untilStop(completable);
}
@NonNull
@Override
public Disposable untilStop(@NonNull final Completable completable, @NonNull final Action onCompletedAction) {
return baseLifecycleBindable.untilStop(completable, onCompletedAction);
}
@NonNull
@Override
public Disposable untilStop(@NonNull final Completable completable,
@NonNull final Action onCompletedAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return baseLifecycleBindable.untilStop(completable, onCompletedAction, onErrorAction);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Maybe<T> maybe) {
return baseLifecycleBindable.untilStop(maybe);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Maybe<T> maybe, @NonNull final Consumer<T> onSuccessAction) {
return baseLifecycleBindable.untilStop(maybe, onSuccessAction);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Maybe<T> maybe,
@NonNull final Consumer<T> onSuccessAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return baseLifecycleBindable.untilStop(maybe, onSuccessAction, onErrorAction);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Observable<T> observable) {
return baseLifecycleBindable.untilDestroy(observable);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Observable<T> observable, @NonNull final Consumer<T> onNextAction) {
return baseLifecycleBindable.untilDestroy(observable, onNextAction);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Observable<T> observable,
@NonNull final Consumer<T> onNextAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return baseLifecycleBindable.untilDestroy(observable, onNextAction, onErrorAction);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Observable<T> observable,
@NonNull final Consumer<T> onNextAction,
@NonNull final Consumer<Throwable> onErrorAction,
@NonNull final Action onCompletedAction) {
return baseLifecycleBindable.untilDestroy(observable, onNextAction, onErrorAction, onCompletedAction);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Single<T> single) {
return baseLifecycleBindable.untilDestroy(single);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Single<T> single, @NonNull final Consumer<T> onSuccessAction) {
return baseLifecycleBindable.untilDestroy(single, onSuccessAction);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Single<T> single,
@NonNull final Consumer<T> onSuccessAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return baseLifecycleBindable.untilDestroy(single, onSuccessAction, onErrorAction);
}
@NonNull
@Override
public Disposable untilDestroy(@NonNull final Completable completable) {
return baseLifecycleBindable.untilDestroy(completable);
}
@NonNull
@Override
public Disposable untilDestroy(@NonNull final Completable completable, @NonNull final Action onCompletedAction) {
return baseLifecycleBindable.untilDestroy(completable, onCompletedAction);
}
@NonNull
@Override
public Disposable untilDestroy(@NonNull final Completable completable,
@NonNull final Action onCompletedAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return baseLifecycleBindable.untilDestroy(completable, onCompletedAction, onErrorAction);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Maybe<T> maybe) {
return baseLifecycleBindable.untilDestroy(maybe);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Maybe<T> maybe, @NonNull final Consumer<T> onCompletedAction) {
return baseLifecycleBindable.untilDestroy(maybe, onCompletedAction);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Maybe<T> maybe,
@NonNull final Consumer<T> onCompletedAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return baseLifecycleBindable.untilDestroy(maybe, onCompletedAction, onErrorAction);
}
@SuppressWarnings("CPD-END")
/*
* Interface to be implemented for someone who want to intercept device back button pressing event.
*/

View File

@ -1,114 +0,0 @@
/*
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package ru.touchin.roboswag.components.navigation.activities;
import android.support.annotation.IdRes;
import android.support.annotation.NonNull;
import android.view.Menu;
import android.view.View;
import ru.touchin.roboswag.components.utils.Logic;
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
/**
* Created by Gavriil Sitnikov on 07/03/2016.
* Activity which is containing specific {@link Logic}
* to support navigation based on {@link ru.touchin.roboswag.components.navigation.ViewController}s.
*
* @param <TLogic> Type of application's {@link Logic}.
*/
public abstract class ViewControllerActivity<TLogic extends Logic> extends BaseActivity {
//it is needed to hold strong reference to logic
private TLogic reference;
/**
* It should return specific class where all logic will be.
*
* @return Returns class of specific {@link Logic}.
*/
@NonNull
protected abstract Class<TLogic> getLogicClass();
/**
* Returns (and creates if needed) application's logic.
*
* @return Object which represents application's logic.
*/
@NonNull
public TLogic getLogic() {
synchronized (ViewControllerActivity.class) {
if (reference == null) {
reference = Logic.getInstance(this, getLogicClass());
}
}
return reference;
}
@Override
@Deprecated
// use {@link #reconfigureNavigation}
public void invalidateOptionsMenu() {
super.invalidateOptionsMenu();
}
@Override
@Deprecated
// use {@link #reconfigureNavigation}
public void supportInvalidateOptionsMenu() {
super.supportInvalidateOptionsMenu();
}
/**
* Invalidates navigation and calls {@link #onConfigureNavigation} for all navigation elements.
*/
public void reconfigureNavigation() {
super.supportInvalidateOptionsMenu();
}
@Override
@Deprecated
// use {@link #onConfigureNavigation}
public boolean onCreateOptionsMenu(@NonNull final Menu menu) {
onConfigureNavigation(menu);
return super.onCreateOptionsMenu(menu);
}
/**
* Calls when activity configuring ActionBar, Toolbar, Sidebar, AppBar etc.
* It is calling before it's {@link ru.touchin.roboswag.components.navigation.ViewController}'s.
*
* @param menu The options menu in which you place your menu items.
*/
public void onConfigureNavigation(@NonNull final Menu menu) {
// do nothing
}
@NonNull
@Override
public <T extends View> T findViewById(@IdRes final int id) {
final T viewById = super.findViewById(id);
if (viewById == null) {
throw new ShouldNotHappenException("No view for id=" + getResources().getResourceName(id));
}
return viewById;
}
}

View File

@ -1,84 +0,0 @@
/*
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package ru.touchin.roboswag.components.navigation.fragments;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import ru.touchin.roboswag.components.navigation.AbstractState;
import ru.touchin.roboswag.components.navigation.ViewController;
import ru.touchin.roboswag.components.navigation.activities.ViewControllerActivity;
/**
* Created by Gavriil Sitnikov on 07/03/2016.
* Simple {@link ViewControllerFragment} which is using by {@link ru.touchin.roboswag.components.navigation.ViewControllerNavigation}.
*
* @param <TState> Type of object which is representing it's fragment state;
* @param <TActivity> Type of {@link ViewControllerActivity} where fragment could be attached to.
*/
public class SimpleViewControllerFragment<TState extends AbstractState, TActivity extends ViewControllerActivity<?>>
extends ViewControllerFragment<TState, TActivity> {
private static final String VIEW_CONTROLLER_CLASS_EXTRA = "VIEW_CONTROLLER_CLASS_EXTRA";
/**
* Creates {@link Bundle} which will store state and {@link ViewController}'s class.
*
* @param viewControllerClass Class of {@link ViewController} which will be instantiated inside this fragment;
* @param state State to use into {@link ViewController};
* @return Returns {@link Bundle} with state inside.
*/
@NonNull
public static Bundle createState(@NonNull final Class<? extends ViewController> viewControllerClass,
@NonNull final AbstractState state) {
final Bundle result = createState(state);
result.putSerializable(VIEW_CONTROLLER_CLASS_EXTRA, viewControllerClass);
return result;
}
private Class<? extends ViewController<TActivity,
? extends ViewControllerFragment<TState, TActivity>>> viewControllerClass;
@NonNull
@Override
public Class<? extends ViewController<TActivity,
? extends ViewControllerFragment<TState, TActivity>>> getViewControllerClass() {
return viewControllerClass;
}
@Override
protected boolean isStateRequired() {
return true;
}
@SuppressWarnings("unchecked")
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
viewControllerClass = (Class<? extends ViewController<TActivity,
? extends ViewControllerFragment<TState, TActivity>>>) getArguments().getSerializable(VIEW_CONTROLLER_CLASS_EXTRA);
}
protected static class DefaultState extends AbstractState {
// just default implementation
}
}

View File

@ -1,66 +0,0 @@
/*
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package ru.touchin.roboswag.components.navigation.fragments;
import android.os.Bundle;
import android.support.annotation.NonNull;
import ru.touchin.roboswag.components.navigation.AbstractState;
import ru.touchin.roboswag.components.navigation.ViewController;
import ru.touchin.roboswag.components.navigation.activities.ViewControllerActivity;
import ru.touchin.roboswag.core.log.Lc;
/**
* Created by Gavriil Sitnikov on 11/04/2016.
* Simple {@link ViewControllerFragment} with no state and with attached {@link #getTargetFragment()}
* which is using by {@link ru.touchin.roboswag.components.navigation.ViewControllerNavigation}.
*
* @param <TActivity> Type of {@link ViewControllerActivity} where fragment could be attached to.
*/
@SuppressWarnings("PMD.UseUtilityClass")
//UseUtilityClass: PMD bug
public class StatelessTargetedViewControllerFragment<TTargetState extends AbstractState,
TActivity extends ViewControllerActivity<?>>
extends TargetedViewControllerFragment<AbstractState, TTargetState, TActivity> {
/**
* Creates {@link Bundle} which will store state and {@link ViewController}'s class.
*
* @param viewControllerClass Class of {@link ViewController} which will be instantiated inside this fragment;
* @return Returns {@link Bundle} with state inside.
*/
@NonNull
public static Bundle createState(@NonNull final Class<? extends ViewController> viewControllerClass) {
return createState(viewControllerClass, new DefaultState());
}
@Override
protected boolean isStateRequired() {
return false;
}
@NonNull
@Override
public AbstractState getState() {
Lc.assertion("Trying to access to state of stateless fragment of " + getViewControllerClass());
return super.getState();
}
}

View File

@ -1,64 +0,0 @@
/*
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package ru.touchin.roboswag.components.navigation.fragments;
import android.os.Bundle;
import android.support.annotation.NonNull;
import ru.touchin.roboswag.components.navigation.AbstractState;
import ru.touchin.roboswag.components.navigation.ViewController;
import ru.touchin.roboswag.components.navigation.activities.ViewControllerActivity;
import ru.touchin.roboswag.core.log.Lc;
/**
* Created by Gavriil Sitnikov on 12/03/2016.
* Simple {@link ViewControllerFragment} with no state which is using by {@link ru.touchin.roboswag.components.navigation.ViewControllerNavigation}.
*
* @param <TActivity> Type of {@link ViewControllerActivity} where fragment could be attached to.
*/
@SuppressWarnings("PMD.UseUtilityClass")
//UseUtilityClass: PMD bug
public class StatelessViewControllerFragment<TActivity extends ViewControllerActivity<?>>
extends SimpleViewControllerFragment<AbstractState, TActivity> {
/**
* Creates {@link Bundle} which will store state and {@link ViewController}'s class.
*
* @param viewControllerClass Class of {@link ViewController} which will be instantiated inside this fragment;
* @return Returns {@link Bundle} with state inside.
*/
@NonNull
public static Bundle createState(@NonNull final Class<? extends ViewController> viewControllerClass) {
return createState(viewControllerClass, new DefaultState());
}
@NonNull
@Override
public AbstractState getState() {
Lc.assertion("Trying to access to state of stateless fragment of " + getViewControllerClass());
return super.getState();
}
@Override
protected boolean isStateRequired() {
return false;
}
}

View File

@ -1,55 +0,0 @@
/*
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package ru.touchin.roboswag.components.navigation.fragments;
import android.support.annotation.NonNull;
import ru.touchin.roboswag.components.navigation.AbstractState;
import ru.touchin.roboswag.components.navigation.activities.ViewControllerActivity;
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
/**
* Created by Gavriil Sitnikov on 11/04/2016.
* Simple {@link ViewControllerFragment} with attached {@link #getTargetFragment()}
* which is using by {@link ru.touchin.roboswag.components.navigation.ViewControllerNavigation}.
*
* @param <TState> Type of object which is representing it's fragment state;
* @param <TActivity> Type of {@link ViewControllerActivity} where fragment could be attached to.
*/
public class TargetedViewControllerFragment<TState extends AbstractState,
TTargetState extends AbstractState,
TActivity extends ViewControllerActivity<?>>
extends SimpleViewControllerFragment<TState, TActivity> {
/**
* Returns specific {@link ViewControllerFragment} which is attached to this fragment as {@link #getTargetFragment()}.
*
* @return Target fragment.
*/
@SuppressWarnings("unchecked")
@NonNull
public ViewControllerFragment<TTargetState, TActivity> getTarget() {
if (!(getTargetFragment() instanceof ViewControllerFragment)) {
throw new ShouldNotHappenException();
}
return (ViewControllerFragment<TTargetState, TActivity>) getTargetFragment();
}
}

View File

@ -24,10 +24,11 @@ import android.content.Intent;
import android.graphics.Canvas;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.InflateException;
import android.support.v4.app.FragmentActivity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@ -36,33 +37,23 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import io.reactivex.Observable;
import io.reactivex.disposables.Disposable;
import io.reactivex.subjects.BehaviorSubject;
import ru.touchin.roboswag.components.navigation.AbstractState;
import ru.touchin.roboswag.components.navigation.ViewController;
import ru.touchin.roboswag.components.navigation.activities.ViewControllerActivity;
import ru.touchin.roboswag.components.utils.UiUtils;
import ru.touchin.roboswag.core.log.Lc;
import ru.touchin.roboswag.core.utils.Optional;
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
import ru.touchin.roboswag.core.utils.pairs.NullablePair;
/**
* Created by Gavriil Sitnikov on 21/10/2015.
* Fragment instantiated in specific activity of {@link TActivity} type that is holding {@link ViewController} inside.
*
* @param <TState> Type of object which is representing it's fragment state;
* @param <TActivity> Type of {@link ViewControllerActivity} where fragment could be attached to.
* @param <TActivity> Type of {@link FragmentActivity} where fragment could be attached to.
*/
@SuppressWarnings("PMD.TooManyMethods")
public abstract class ViewControllerFragment<TState extends AbstractState, TActivity extends ViewControllerActivity<?>>
extends ViewFragment<TActivity> {
public abstract class ViewControllerFragment<TState extends Parcelable, TActivity extends FragmentActivity> extends ViewFragment<TActivity> {
private static final String VIEW_CONTROLLER_CLASS_EXTRA = "VIEW_CONTROLLER_CLASS_EXTRA";
private static final String VIEW_CONTROLLER_STATE_EXTRA = "VIEW_CONTROLLER_STATE_EXTRA";
private static boolean inDebugMode;
@ -84,17 +75,16 @@ public abstract class ViewControllerFragment<TState extends AbstractState, TActi
ViewControllerFragment.acceptableUiCalculationTime = acceptableUiCalculationTime;
}
@SuppressWarnings("unchecked")
@NonNull
private static <T extends Serializable> T reserialize(@NonNull final T serializable) {
private static <T extends Parcelable> T reserialize(@NonNull final T parcelable) {
Parcel parcel = Parcel.obtain();
parcel.writeSerializable(serializable);
parcel.writeParcelable(parcelable, 0);
final byte[] serializableBytes = parcel.marshall();
parcel.recycle();
parcel = Parcel.obtain();
parcel.unmarshall(serializableBytes, 0, serializableBytes.length);
parcel.setDataPosition(0);
final T result = (T) parcel.readSerializable();
final T result = parcel.readParcelable(parcelable.getClass().getClassLoader());
parcel.recycle();
return result;
}
@ -106,32 +96,20 @@ public abstract class ViewControllerFragment<TState extends AbstractState, TActi
* @return Returns bundle with state inside.
*/
@NonNull
public static Bundle createState(@Nullable final AbstractState state) {
public static Bundle args(@NonNull final Class<? extends ViewController> viewControllerClass, @Nullable final Parcelable state) {
final Bundle result = new Bundle();
result.putSerializable(VIEW_CONTROLLER_STATE_EXTRA, state);
result.putSerializable(VIEW_CONTROLLER_CLASS_EXTRA, viewControllerClass);
result.putParcelable(VIEW_CONTROLLER_STATE_EXTRA, state);
return result;
}
@NonNull
private final BehaviorSubject<Optional<TActivity>> activitySubject = BehaviorSubject.create();
@NonNull
private final BehaviorSubject<NullablePair<PlaceholderView, Bundle>> viewSubject = BehaviorSubject.create();
@Nullable
private ViewController viewController;
private Disposable viewControllerSubscription;
private Class<ViewController<TActivity, ViewControllerFragment<TState, TActivity>>> viewControllerClass;
private TState state;
private boolean started;
private boolean stateCreated;
private void tryCreateState(@Nullable final Context context) {
if (!stateCreated && state != null && context != null) {
state.onCreate();
stateCreated = true;
}
}
/**
* Returns specific {@link AbstractState} which contains state of fragment and it's {@link ViewController}.
* Returns specific {@link Parcelable} which contains state of fragment and it's {@link ViewController}.
*
* @return Object represents state.
*/
@ -140,64 +118,37 @@ public abstract class ViewControllerFragment<TState extends AbstractState, TActi
return state;
}
/**
* It should return specific {@link ViewController} class to control instantiated view by logic after activity creation.
*
* @return Returns class of specific {@link ViewController}.
*/
@NonNull
public abstract Class<? extends ViewController<TActivity,
? extends ViewControllerFragment<TState, TActivity>>> getViewControllerClass();
/**
* Returns if ViewControllerFragment requires state or not.
*
* @return true if state is required
*/
protected abstract boolean isStateRequired();
@SuppressWarnings("unchecked")
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(!isChildFragment());
//noinspection unchecked
viewControllerClass = (Class<ViewController<TActivity, ViewControllerFragment<TState, TActivity>>>)
getArguments().getSerializable(VIEW_CONTROLLER_CLASS_EXTRA);
state = savedInstanceState != null
? (TState) savedInstanceState.getSerializable(VIEW_CONTROLLER_STATE_EXTRA)
: (getArguments() != null ? (TState) getArguments().getSerializable(VIEW_CONTROLLER_STATE_EXTRA) : null);
? savedInstanceState.getParcelable(VIEW_CONTROLLER_STATE_EXTRA)
: (getArguments() != null ? getArguments().getParcelable(VIEW_CONTROLLER_STATE_EXTRA) : null);
if (state != null) {
if (inDebugMode) {
state = reserialize(state);
}
tryCreateState(getContext());
} else if (isStateRequired()) {
} else {
Lc.assertion("State is required and null");
}
viewControllerSubscription = Observable
.combineLatest(activitySubject.distinctUntilChanged(), viewSubject.distinctUntilChanged(),
(activityOptional, viewInfo) -> {
final TActivity activity = activityOptional.get();
final PlaceholderView container = viewInfo.getFirst();
if (activity == null || container == null) {
return new Optional<ViewController>(null);
}
final ViewController newViewController = createViewController(activity, container, viewInfo.getSecond());
newViewController.onCreate();
return new Optional<>(newViewController);
})
.subscribe(this::onViewControllerChanged,
throwable -> Lc.cutAssertion(throwable, InvocationTargetException.class, InflateException.class));
}
@NonNull
private ViewController createViewController(@NonNull final TActivity activity, @NonNull final PlaceholderView view,
@Nullable final Bundle savedInstanceState) {
if (getViewControllerClass().getConstructors().length != 1) {
throw new ShouldNotHappenException("There should be single constructor for " + getViewControllerClass());
private ViewController createViewController(
@NonNull final FragmentActivity activity,
@NonNull final PlaceholderView view,
@Nullable final Bundle savedInstanceState
) {
if (viewControllerClass.getConstructors().length != 1) {
throw new ShouldNotHappenException("There should be single constructor for " + viewControllerClass);
}
final Constructor<?> constructor = getViewControllerClass().getConstructors()[0];
final Constructor<?> constructor = viewControllerClass.getConstructors()[0];
final ViewController.CreationContext creationContext = new ViewController.CreationContext(activity, this, view);
final long creationTime = inDebugMode ? SystemClock.elapsedRealtime() : 0;
try {
@ -209,7 +160,7 @@ public abstract class ViewControllerFragment<TState extends AbstractState, TActi
default:
throw new ShouldNotHappenException("Wrong constructor parameters count: " + constructor.getParameterTypes().length);
}
} catch (final Exception exception) {
} catch (@NonNull final Exception exception) {
throw new ShouldNotHappenException(exception);
} finally {
checkCreationTime(creationTime);
@ -220,46 +171,33 @@ public abstract class ViewControllerFragment<TState extends AbstractState, TActi
if (inDebugMode) {
final long creationPeriod = SystemClock.elapsedRealtime() - creationTime;
if (creationPeriod > acceptableUiCalculationTime) {
UiUtils.UI_METRICS_LC_GROUP.w("Creation of %s took too much: %dms", getViewControllerClass(), creationPeriod);
UiUtils.UI_METRICS_LC_GROUP.w("Creation of %s took too much: %dms", viewControllerClass, creationPeriod);
}
}
}
@Override
public void onAttach(@NonNull final Context context) {
super.onAttach(context);
tryCreateState(context);
}
@Deprecated
@NonNull
@Override
public View onCreateView(@NonNull final LayoutInflater inflater,
@Nullable final ViewGroup container,
@Nullable final Bundle savedInstanceState) {
return new PlaceholderView(inflater.getContext(), getViewControllerClass().getName());
public final View onCreateView(
@NonNull final LayoutInflater inflater,
@Nullable final ViewGroup container,
@Nullable final Bundle savedInstanceState
) {
return new PlaceholderView(inflater.getContext(), viewControllerClass.getName());
}
@Override
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (view instanceof PlaceholderView) {
viewSubject.onNext(new NullablePair<>((PlaceholderView) view, savedInstanceState));
} else {
Lc.assertion("View should be instanceof PlaceholderView");
}
}
@Override
public void onActivityCreated(@NonNull final View view, @NonNull final TActivity activity, @Nullable final Bundle savedInstanceState) {
super.onActivityCreated(view, activity, savedInstanceState);
activitySubject.onNext(new Optional<>(activity));
public void onActivityCreated(@Nullable final Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
//noinspection ConstantConditions
viewController = createViewController(requireActivity(), (PlaceholderView) getView(), savedInstanceState);
viewController.onCreate();
requireActivity().invalidateOptionsMenu();
}
@Override
protected void onStart(@NonNull final View view, @NonNull final TActivity activity) {
super.onStart(view, activity);
started = true;
if (viewController != null) {
viewController.onStart();
}
@ -289,23 +227,12 @@ public abstract class ViewControllerFragment<TState extends AbstractState, TActi
}
}
/**
* Calls when activity configuring ActionBar, Toolbar, Sidebar etc.
* If it will be called or not depends on {@link #hasOptionsMenu()} and {@link #isMenuVisible()}.
*
* @param menu The options menu in which you place your items;
* @param inflater Helper to inflate menu items.
*/
protected void onConfigureNavigation(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {
if (viewController != null) {
viewController.onConfigureNavigation(menu, inflater);
}
}
@Override
public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
onConfigureNavigation(menu, inflater);
if (viewController != null) {
viewController.onCreateOptionsMenu(menu, inflater);
}
}
@Override
@ -313,19 +240,6 @@ public abstract class ViewControllerFragment<TState extends AbstractState, TActi
return (viewController != null && viewController.onOptionsItemSelected(item)) || super.onOptionsItemSelected(item);
}
private void onViewControllerChanged(@NonNull final Optional<ViewController> viewControllerOptional) {
if (this.viewController != null) {
this.viewController.onDestroy();
}
this.viewController = viewControllerOptional.get();
if (this.viewController != null) {
if (started) {
this.viewController.onStart();
}
this.viewController.getActivity().reconfigureNavigation();
}
}
@Override
protected void onPause(@NonNull final View view, @NonNull final TActivity activity) {
super.onPause(view, activity);
@ -340,7 +254,7 @@ public abstract class ViewControllerFragment<TState extends AbstractState, TActi
if (viewController != null) {
viewController.onSaveInstanceState(savedInstanceState);
}
savedInstanceState.putSerializable(VIEW_CONTROLLER_STATE_EXTRA, state);
savedInstanceState.putParcelable(VIEW_CONTROLLER_STATE_EXTRA, state);
}
@Override
@ -353,7 +267,6 @@ public abstract class ViewControllerFragment<TState extends AbstractState, TActi
@Override
protected void onStop(@NonNull final View view, @NonNull final TActivity activity) {
started = false;
if (viewController != null) {
viewController.onStop();
}
@ -361,25 +274,12 @@ public abstract class ViewControllerFragment<TState extends AbstractState, TActi
}
@Override
protected void onDestroyView(@NonNull final View view) {
viewSubject.onNext(new NullablePair<>(null, null));
super.onDestroyView(view);
}
@Override
public void onDetach() {
activitySubject.onNext(new Optional<>(null));
super.onDetach();
}
@Override
public void onDestroy() {
viewControllerSubscription.dispose();
if (viewController != null && !viewController.isDestroyed()) {
public void onDestroyView() {
if (viewController != null) {
viewController.onDestroy();
viewController = null;
}
super.onDestroy();
super.onDestroyView();
}
@Override

View File

@ -24,7 +24,7 @@ import android.support.annotation.CallSuper;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.app.AppCompatActivity;
import android.support.v4.app.FragmentActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -39,8 +39,7 @@ import ru.touchin.roboswag.core.log.Lc;
*
* @param <TActivity> Type of activity which to such fragment could be attached.
*/
public abstract class ViewFragment<TActivity extends AppCompatActivity> extends Fragment
implements OnFragmentStartedListener {
public abstract class ViewFragment<TActivity extends FragmentActivity> extends Fragment implements OnFragmentStartedListener {
private boolean appeared;
private boolean started;
@ -88,27 +87,12 @@ public abstract class ViewFragment<TActivity extends AppCompatActivity> extends
//do nothing
}
@Deprecated
@Override
public void onActivityCreated(@Nullable final Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (getView() == null || getBaseActivity() == null) {
Lc.assertion("View and activity shouldn't be null");
return;
}
onActivityCreated(getView(), getBaseActivity(), savedInstanceState);
}
/**
* Replacement of {@link #onActivityCreated} with non null activity as first parameter.
*
* @param view Instantiated view.
* @param activity Activity which fragment attached to.
* @param savedInstanceState If the fragment is being re-created from a previous saved state, this is the state.
*/
@CallSuper
public void onActivityCreated(@NonNull final View view, @NonNull final TActivity activity, @Nullable final Bundle savedInstanceState) {
//do nothing
}
private void callMethodAfterInstantiation(@NonNull final BiConsumer<View, TActivity> action) {
@ -243,25 +227,4 @@ public abstract class ViewFragment<TActivity extends AppCompatActivity> extends
}
}
@Deprecated
@Override
public void onDestroyView() {
if (getView() == null) {
Lc.assertion("View shouldn't be null");
return;
}
onDestroyView(getView());
super.onDestroyView();
}
/**
* Replacement of {@link #onDestroyView} with non null activity as first parameter.
*
* @param view Instantiated view.
*/
@CallSuper
protected void onDestroyView(@NonNull final View view) {
//do nothing
}
}

View File

@ -1,336 +0,0 @@
/*
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package ru.touchin.roboswag.components.utils;
import android.support.annotation.NonNull;
import io.reactivex.Completable;
import io.reactivex.Maybe;
import io.reactivex.Observable;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Action;
import io.reactivex.functions.Consumer;
import io.reactivex.internal.functions.Functions;
import io.reactivex.subjects.BehaviorSubject;
import ru.touchin.roboswag.core.log.Lc;
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
/**
* Created by Gavriil Sitnikov on 18/04/16.
* Simple implementation of {@link LifecycleBindable}. Could be used to not implement interface but use such object inside.
*/
@SuppressWarnings("PMD.TooManyMethods")
public class BaseLifecycleBindable implements LifecycleBindable {
private static final String UNTIL_DESTROY_METHOD = "untilDestroy";
private static final String UNTIL_STOP_METHOD = "untilStop";
@NonNull
private final BehaviorSubject<Boolean> isCreatedSubject = BehaviorSubject.create();
@NonNull
private final BehaviorSubject<Boolean> isStartedSubject = BehaviorSubject.create();
@NonNull
private final BehaviorSubject<Boolean> isInAfterSaving = BehaviorSubject.createDefault(false);
/**
* Call it on parent's onCreate method.
*/
public void onCreate() {
isCreatedSubject.onNext(true);
}
/**
* Call it on parent's onStart method.
*/
public void onStart() {
isStartedSubject.onNext(true);
}
/**
* Call it on parent's onResume method.
* It is needed as sometimes onSaveInstanceState() calling after onPause() with no onStop call. So lifecycle object going in stopped state.
* In that case onResume will be called after onSaveInstanceState so lifecycle object is becoming started.
*/
public void onResume() {
isInAfterSaving.onNext(false);
}
/**
* Call it on parent's onSaveInstanceState method.
*/
public void onSaveInstanceState() {
isInAfterSaving.onNext(true);
}
/**
* Call it on parent's onStop method.
*/
public void onStop() {
isStartedSubject.onNext(false);
}
/**
* Call it on parent's onDestroy method.
*/
public void onDestroy() {
isCreatedSubject.onNext(false);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Observable<T> observable) {
final String codePoint = Lc.getCodePoint(this, 2);
return untilStop(observable, Functions.emptyConsumer(), getActionThrowableForAssertion(codePoint, UNTIL_STOP_METHOD), Functions.EMPTY_ACTION);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Observable<T> observable, @NonNull final Consumer<T> onNextAction) {
final String codePoint = Lc.getCodePoint(this, 2);
return untilStop(observable, onNextAction, getActionThrowableForAssertion(codePoint, UNTIL_STOP_METHOD), Functions.EMPTY_ACTION);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Observable<T> observable,
@NonNull final Consumer<T> onNextAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return untilStop(observable, onNextAction, onErrorAction, Functions.EMPTY_ACTION);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Observable<T> observable,
@NonNull final Consumer<T> onNextAction,
@NonNull final Consumer<Throwable> onErrorAction,
@NonNull final Action onCompletedAction) {
return until(observable, isStartedSubject.map(started -> !started)
.delay(item -> isInAfterSaving.filter(inAfterSaving -> !inAfterSaving)),
onNextAction, onErrorAction, onCompletedAction);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Single<T> single) {
final String codePoint = Lc.getCodePoint(this, 2);
return untilStop(single, Functions.emptyConsumer(), getActionThrowableForAssertion(codePoint, UNTIL_STOP_METHOD));
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Single<T> single, @NonNull final Consumer<T> onSuccessAction) {
final String codePoint = Lc.getCodePoint(this, 2);
return untilStop(single, onSuccessAction, getActionThrowableForAssertion(codePoint, UNTIL_STOP_METHOD));
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Single<T> single,
@NonNull final Consumer<T> onSuccessAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return until(single.toObservable(), isStartedSubject.map(started -> !started)
.delay(item -> isInAfterSaving.filter(inAfterSaving -> !inAfterSaving)),
onSuccessAction, onErrorAction, Functions.EMPTY_ACTION);
}
@NonNull
@Override
public Disposable untilStop(@NonNull final Completable completable) {
final String codePoint = Lc.getCodePoint(this, 2);
return untilStop(completable, Functions.EMPTY_ACTION, getActionThrowableForAssertion(codePoint, UNTIL_STOP_METHOD));
}
@NonNull
@Override
public Disposable untilStop(@NonNull final Completable completable,
@NonNull final Action onCompletedAction) {
final String codePoint = Lc.getCodePoint(this, 2);
return untilStop(completable, onCompletedAction, getActionThrowableForAssertion(codePoint, UNTIL_STOP_METHOD));
}
@NonNull
@Override
public Disposable untilStop(@NonNull final Completable completable,
@NonNull final Action onCompletedAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return until(completable.toObservable(), isStartedSubject.map(started -> !started)
.delay(item -> isInAfterSaving.filter(inAfterSaving -> !inAfterSaving)),
Functions.emptyConsumer(), onErrorAction, onCompletedAction);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Maybe<T> maybe) {
final String codePoint = Lc.getCodePoint(this, 2);
return untilStop(maybe, Functions.emptyConsumer(), getActionThrowableForAssertion(codePoint, UNTIL_STOP_METHOD));
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Maybe<T> maybe, @NonNull final Consumer<T> onSuccessAction) {
final String codePoint = Lc.getCodePoint(this, 2);
return untilStop(maybe, onSuccessAction, getActionThrowableForAssertion(codePoint, UNTIL_STOP_METHOD));
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Maybe<T> maybe,
@NonNull final Consumer<T> onSuccessAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return until(maybe.toObservable(), isStartedSubject.map(started -> !started), onSuccessAction, onErrorAction, Functions.EMPTY_ACTION);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Observable<T> observable) {
final String codePoint = Lc.getCodePoint(this, 2);
return untilDestroy(observable, Functions.emptyConsumer(),
getActionThrowableForAssertion(codePoint, UNTIL_DESTROY_METHOD), Functions.EMPTY_ACTION);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Observable<T> observable,
@NonNull final Consumer<T> onNextAction) {
final String codePoint = Lc.getCodePoint(this, 2);
return untilDestroy(observable, onNextAction, getActionThrowableForAssertion(codePoint, UNTIL_DESTROY_METHOD), Functions.EMPTY_ACTION);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Observable<T> observable,
@NonNull final Consumer<T> onNextAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return untilDestroy(observable, onNextAction, onErrorAction, Functions.EMPTY_ACTION);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Observable<T> observable,
@NonNull final Consumer<T> onNextAction,
@NonNull final Consumer<Throwable> onErrorAction,
@NonNull final Action onCompletedAction) {
return until(observable, isCreatedSubject.map(created -> !created), onNextAction, onErrorAction, onCompletedAction);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Single<T> single) {
final String codePoint = Lc.getCodePoint(this, 2);
return untilDestroy(single, Functions.emptyConsumer(), getActionThrowableForAssertion(codePoint, UNTIL_DESTROY_METHOD));
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Single<T> single, @NonNull final Consumer<T> onSuccessAction) {
final String codePoint = Lc.getCodePoint(this, 2);
return untilDestroy(single, onSuccessAction, getActionThrowableForAssertion(codePoint, UNTIL_DESTROY_METHOD));
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Single<T> single,
@NonNull final Consumer<T> onSuccessAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return until(single.toObservable(), isCreatedSubject.map(created -> !created), onSuccessAction, onErrorAction, Functions.EMPTY_ACTION);
}
@NonNull
@Override
public Disposable untilDestroy(@NonNull final Completable completable) {
final String codePoint = Lc.getCodePoint(this, 2);
return untilDestroy(completable, Functions.EMPTY_ACTION, getActionThrowableForAssertion(codePoint, UNTIL_DESTROY_METHOD));
}
@NonNull
@Override
public Disposable untilDestroy(@NonNull final Completable completable, @NonNull final Action onCompletedAction) {
final String codePoint = Lc.getCodePoint(this, 2);
return untilDestroy(completable, onCompletedAction, getActionThrowableForAssertion(codePoint, UNTIL_DESTROY_METHOD));
}
@NonNull
@Override
public Disposable untilDestroy(@NonNull final Completable completable,
@NonNull final Action onCompletedAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return until(completable.toObservable(), isCreatedSubject.map(created -> !created),
Functions.emptyConsumer(), onErrorAction, onCompletedAction);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Maybe<T> maybe) {
final String codePoint = Lc.getCodePoint(this, 2);
return untilDestroy(maybe, Functions.emptyConsumer(), getActionThrowableForAssertion(codePoint, UNTIL_DESTROY_METHOD));
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Maybe<T> maybe, @NonNull final Consumer<T> onSuccessAction) {
final String codePoint = Lc.getCodePoint(this, 2);
return untilDestroy(maybe, onSuccessAction, getActionThrowableForAssertion(codePoint, UNTIL_DESTROY_METHOD));
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Maybe<T> maybe,
@NonNull final Consumer<T> onSuccessAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return until(maybe.toObservable(), isCreatedSubject.map(created -> !created), onSuccessAction, onErrorAction, Functions.EMPTY_ACTION);
}
@NonNull
private <T> Disposable until(@NonNull final Observable<T> observable,
@NonNull final Observable<Boolean> conditionSubject,
@NonNull final Consumer<T> onNextAction,
@NonNull final Consumer<Throwable> onErrorAction,
@NonNull final Action onCompletedAction) {
final Observable<T> actualObservable;
if (onNextAction == Functions.emptyConsumer() && onErrorAction == (Consumer) Functions.emptyConsumer()
&& onCompletedAction == Functions.EMPTY_ACTION) {
actualObservable = observable;
} else {
actualObservable = observable.observeOn(AndroidSchedulers.mainThread())
.doOnComplete(onCompletedAction)
.doOnNext(onNextAction)
.doOnError(onErrorAction);
}
return isCreatedSubject.firstOrError()
.flatMapObservable(created -> created ? actualObservable : Observable.empty())
.takeUntil(conditionSubject.filter(condition -> condition))
.onErrorResumeNext(throwable -> {
if (throwable instanceof RuntimeException) {
Lc.assertion(throwable);
}
return Observable.empty();
})
.subscribe();
}
@NonNull
private Consumer<Throwable> getActionThrowableForAssertion(@NonNull final String codePoint, @NonNull final String method) {
return throwable -> Lc.assertion(new ShouldNotHappenException("Unexpected error on " + method + " at " + codePoint, throwable));
}
}

View File

@ -1,391 +0,0 @@
/*
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package ru.touchin.roboswag.components.utils;
import android.support.annotation.NonNull;
import io.reactivex.Completable;
import io.reactivex.CompletableEmitter;
import io.reactivex.Emitter;
import io.reactivex.Maybe;
import io.reactivex.MaybeEmitter;
import io.reactivex.Observable;
import io.reactivex.Single;
import io.reactivex.SingleEmitter;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Action;
import io.reactivex.functions.Consumer;
/**
* Created by Gavriil Sitnikov on 15/04/16.
* Interface that should be implemented by lifecycle-based elements ({@link android.app.Activity}, {@link android.support.v4.app.Fragment} etc.)
* to not manually manage subscriptions.
* Use {@link #untilStop(Observable)} method to subscribe to observable where you want and unsubscribe onStop.
* Use {@link #untilDestroy(Observable)} method to subscribe to observable where you want and unsubscribe onDestroy.
*/
@SuppressWarnings("PMD.TooManyMethods")
public interface LifecycleBindable {
/**
* Method should be used to guarantee that observable won't be subscribed after onStop.
* It is automatically subscribing to the observable.
* Usually it is using to stop requests/execution while element is off or to not do illegal actions after onStop like fragment's stack changing.
* Don't forget to process errors if observable can emit them.
*
* @param observable {@link Observable} to subscribe until onStop;
* @param <T> Type of emitted by observable items;
* @return {@link Disposable} which will unsubscribes from observable onStop.
*/
@NonNull
<T> Disposable untilStop(@NonNull Observable<T> observable);
/**
* Method should be used to guarantee that observable won't be subscribed after onStop.
* It is automatically subscribing to the observable and calls onNextAction on every emitted item.
* Usually it is using to stop requests/execution while element is off or to not do illegal actions after onStop like fragment's stack changing.
* Don't forget to process errors if observable can emit them.
*
* @param observable {@link Observable} to subscribe until onStop;
* @param onNextAction Action which will raise on every {@link Emitter#onNext(Object)} item;
* @param <T> Type of emitted by observable items;
* @return {@link Disposable} which will unsubscribes from observable onStop.
*/
@NonNull
<T> Disposable untilStop(@NonNull Observable<T> observable, @NonNull Consumer<T> onNextAction);
/**
* Method should be used to guarantee that observable won't be subscribed after onStop.
* It is automatically subscribing to the observable and calls onNextAction and onErrorAction on observable events.
* Usually it is using to stop requests/execution while element is off or to not do illegal actions after onStop like fragment's stack changing.
* Don't forget to process errors if observable can emit them.
*
* @param observable {@link Observable} to subscribe until onStop;
* @param onNextAction Action which will raise on every {@link Emitter#onNext(Object)} item;
* @param onErrorAction Action which will raise on every {@link Emitter#onError(Throwable)} throwable;
* @param <T> Type of emitted by observable items;
* @return {@link Disposable} which will unsubscribes from observable onStop.
*/
@NonNull
<T> Disposable untilStop(@NonNull Observable<T> observable, @NonNull Consumer<T> onNextAction, @NonNull Consumer<Throwable> onErrorAction);
/**
* Method should be used to guarantee that observable won't be subscribed after onStop.
* It is automatically subscribing to the observable and calls onNextAction, onErrorAction and onCompletedAction on observable events.
* Usually it is using to stop requests/execution while element is off or to not do illegal actions after onStop like fragment's stack changing.
* Don't forget to process errors if observable can emit them.
*
* @param observable {@link Observable} to subscribe until onStop;
* @param onNextAction Action which will raise on every {@link Emitter#onNext(Object)} item;
* @param onErrorAction Action which will raise on every {@link Emitter#onError(Throwable)} throwable;
* @param onCompletedAction Action which will raise at {@link Emitter#onComplete()} on completion of observable;
* @param <T> Type of emitted by observable items;
* @return {@link Disposable} which is wrapping source observable to unsubscribe from it onStop.
*/
@NonNull
<T> Disposable untilStop(@NonNull Observable<T> observable,
@NonNull Consumer<T> onNextAction, @NonNull Consumer<Throwable> onErrorAction, @NonNull Action onCompletedAction);
/**
* Method should be used to guarantee that single won't be subscribed after onStop.
* It is automatically subscribing to the single.
* Usually it is using to stop requests/execution while element is off or to not do illegal actions after onStop like fragment's stack changing.
* Don't forget to process errors if single can emit them.
*
* @param single {@link Single} to subscribe until onStop;
* @param <T> Type of emitted by single item;
* @return {@link Disposable} which will unsubscribes from single onStop.
*/
@NonNull
<T> Disposable untilStop(@NonNull Single<T> single);
/**
* Method should be used to guarantee that single won't be subscribed after onStop.
* It is automatically subscribing to the single and calls onSuccessAction on the emitted item.
* Usually it is using to stop requests/execution while element is off or to not do illegal actions after onStop like fragment's stack changing.
* Don't forget to process errors if single can emit them.
*
* @param single {@link Single} to subscribe until onStop;
* @param onSuccessAction Action which will raise on every {@link SingleEmitter#onSuccess(Object)} item;
* @param <T> Type of emitted by single item;
* @return {@link Disposable} which will unsubscribes from single onStop.
*/
@NonNull
<T> Disposable untilStop(@NonNull Single<T> single, @NonNull Consumer<T> onSuccessAction);
/**
* Method should be used to guarantee that single won't be subscribed after onStop.
* It is automatically subscribing to the single and calls onSuccessAction and onErrorAction on single events.
* Usually it is using to stop requests/execution while element is off or to not do illegal actions after onStop like fragment's stack changing.
* Don't forget to process errors if single can emit them.
*
* @param single {@link Single} to subscribe until onStop;
* @param onSuccessAction Action which will raise on every {@link SingleEmitter#onSuccess(Object)} item;
* @param onErrorAction Action which will raise on every {@link SingleEmitter#onError(Throwable)} throwable;
* @param <T> Type of emitted by observable items;
* @return {@link Disposable} which is wrapping source single to unsubscribe from it onStop.
*/
@NonNull
<T> Disposable untilStop(@NonNull Single<T> single, @NonNull Consumer<T> onSuccessAction, @NonNull Consumer<Throwable> onErrorAction);
/**
* Method should be used to guarantee that completable won't be subscribed after onStop.
* It is automatically subscribing to the completable.
* Usually it is using to stop requests/execution while element is off or to not do illegal actions after onStop like fragment's stack changing.
* Don't forget to process errors if completable can emit them.
*
* @param completable {@link Completable} to subscribe until onStop;
* @return {@link Disposable} which will unsubscribes from completable onStop.
*/
@NonNull
Disposable untilStop(@NonNull Completable completable);
/**
* Method should be used to guarantee that completable won't be subscribed after onStop.
* It is automatically subscribing to the completable and calls onCompletedAction on completable item.
* Usually it is using to stop requests/execution while element is off or to not do illegal actions after onStop like fragment's stack changing.
* Don't forget to process errors if completable can emit them.
*
* @param completable {@link Completable} to subscribe until onStop;
* @param onCompletedAction Action which will raise at {@link CompletableEmitter#onComplete()} on completion of observable;
* @return {@link Disposable} which is wrapping source completable to unsubscribe from it onStop.
*/
@NonNull
Disposable untilStop(@NonNull Completable completable, @NonNull Action onCompletedAction);
/**
* Method should be used to guarantee that completable won't be subscribed after onStop.
* It is automatically subscribing to the completable and calls onCompletedAction and onErrorAction on completable item.
* Usually it is using to stop requests/execution while element is off or to not do illegal actions after onStop like fragment's stack changing.
* Don't forget to process errors if completable can emit them.
*
* @param completable {@link Completable} to subscribe until onStop;
* @param onCompletedAction Action which will raise at {@link CompletableEmitter#onComplete()} on completion of observable;
* @param onErrorAction Action which will raise on every {@link CompletableEmitter#onError(Throwable)} throwable;
* @return {@link Disposable} which is wrapping source completable to unsubscribe from it onStop.
*/
@NonNull
Disposable untilStop(@NonNull Completable completable, @NonNull Action onCompletedAction, @NonNull Consumer<Throwable> onErrorAction);
/**
* Method should be used to guarantee that maybe won't be subscribed after onStop.
* It is automatically subscribing to the maybe.
* Usually it is using to stop requests/execution while element is off or to not do illegal actions after onStop like fragment's stack changing.
* Don't forget to process errors if completable can emit them.
*
* @param maybe {@link Maybe} to subscribe until onStop;
* @return {@link Disposable} which will unsubscribes from completable onStop.
*/
@NonNull
<T> Disposable untilStop(@NonNull Maybe<T> maybe);
/**
* Method should be used to guarantee that maybe won't be subscribed after onStop.
* It is automatically subscribing to the maybe and calls onCompletedAction on maybe item.
* Usually it is using to stop requests/execution while element is off or to not do illegal actions after onStop like fragment's stack changing.
* Don't forget to process errors if completable can emit them.
*
* @param maybe {@link Maybe} to subscribe until onStop;
* @param onSuccessAction Action which will raise at {@link MaybeEmitter#onSuccess(Object)} ()} on completion of observable;
* @return {@link Disposable} which is wrapping source maybe to unsubscribe from it onStop.
*/
@NonNull
<T> Disposable untilStop(@NonNull Maybe<T> maybe, @NonNull Consumer<T> onSuccessAction);
/**
* Method should be used to guarantee that maybe won't be subscribed after onStop.
* It is automatically subscribing to the maybe and calls onCompletedAction and onErrorAction on maybe item.
* Usually it is using to stop requests/execution while element is off or to not do illegal actions after onStop like fragment's stack changing.
* Don't forget to process errors if completable can emit them.
*
* @param maybe {@link Maybe} to subscribe until onStop;
* @param onSuccessAction Action which will raise at {@link MaybeEmitter#onSuccess(Object)} ()} on completion of observable;
* @param onErrorAction Action which will raise on every {@link MaybeEmitter#onError(Throwable)} throwable;
* @return {@link Disposable} which is wrapping source maybe to unsubscribe from it onStop.
*/
@NonNull
<T> Disposable untilStop(@NonNull Maybe<T> maybe, @NonNull Consumer<T> onSuccessAction, @NonNull Consumer<Throwable> onErrorAction);
/**
* Method should be used to guarantee that observable won't be subscribed after onDestroy.
* It is automatically subscribing to the observable.
* Don't forget to process errors if observable can emit them.
*
* @param observable {@link Observable} to subscribe until onDestroy;
* @param <T> Type of emitted by observable items;
* @return {@link Disposable} which is wrapping source maybe to unsubscribe from it onDestroy.
*/
@NonNull
<T> Disposable untilDestroy(@NonNull Observable<T> observable);
/**
* Method should be used to guarantee that observable won't be subscribed after onDestroy.
* It is automatically subscribing to the observable and calls onNextAction on every emitted item.
* Don't forget to process errors if observable can emit them.
*
* @param observable {@link Observable} to subscribe until onDestroy;
* @param onNextAction Action which will raise on every {@link Emitter#onNext(Object)} item;
* @param <T> Type of emitted by observable items;
* @return {@link Disposable} which is wrapping source observable to unsubscribe from it onDestroy.
*/
@NonNull
<T> Disposable untilDestroy(@NonNull Observable<T> observable, @NonNull Consumer<T> onNextAction);
/**
* Method should be used to guarantee that observable won't be subscribed after onDestroy.
* It is automatically subscribing to the observable and calls onNextAction and onErrorAction on observable events.
* Don't forget to process errors if observable can emit them.
*
* @param observable {@link Observable} to subscribe until onDestroy;
* @param onNextAction Action which will raise on every {@link Emitter#onNext(Object)} item;
* @param onErrorAction Action which will raise on every {@link Emitter#onError(Throwable)} throwable;
* @param <T> Type of emitted by observable items;
* @return {@link Disposable} which is wrapping source observable to unsubscribe from it onDestroy.
*/
@NonNull
<T> Disposable untilDestroy(@NonNull Observable<T> observable, @NonNull Consumer<T> onNextAction, @NonNull Consumer<Throwable> onErrorAction);
/**
* Method should be used to guarantee that observable won't be subscribed after onDestroy.
* It is automatically subscribing to the observable and calls onNextAction, onErrorAction and onCompletedAction on observable events.
* Don't forget to process errors if observable can emit them.
*
* @param observable {@link Observable} to subscribe until onDestroy;
* @param onNextAction Action which will raise on every {@link Emitter#onNext(Object)} item;
* @param onErrorAction Action which will raise on every {@link Emitter#onError(Throwable)} throwable;
* @param onCompletedAction Action which will raise at {@link Emitter#onComplete()} on completion of observable;
* @param <T> Type of emitted by observable items;
* @return {@link Disposable} which is wrapping source observable to unsubscribe from it onDestroy.
*/
@NonNull
<T> Disposable untilDestroy(@NonNull Observable<T> observable,
@NonNull Consumer<T> onNextAction, @NonNull Consumer<Throwable> onErrorAction, @NonNull Action onCompletedAction);
/**
* Method should be used to guarantee that single won't be subscribed after onDestroy.
* It is automatically subscribing to the single.
* Don't forget to process errors if single can emit them.
*
* @param single {@link Single} to subscribe until onDestroy;
* @param <T> Type of emitted by single items;
* @return {@link Disposable} which is wrapping source single to unsubscribe from it onDestroy.
*/
@NonNull
<T> Disposable untilDestroy(@NonNull Single<T> single);
/**
* Method should be used to guarantee that single won't be subscribed after onDestroy.
* It is automatically subscribing to the single and calls onSuccessAction on every emitted item.
* Don't forget to process errors if single can emit them.
*
* @param single {@link Single} to subscribe until onDestroy;
* @param onSuccessAction Action which will raise on every {@link SingleEmitter#onSuccess(Object)} item;
* @param <T> Type of emitted by single items;
* @return {@link Disposable} which is wrapping source single to unsubscribe from it onDestroy.
*/
@NonNull
<T> Disposable untilDestroy(@NonNull Single<T> single, @NonNull Consumer<T> onSuccessAction);
/**
* Method should be used to guarantee that single won't be subscribed after onDestroy.
* It is automatically subscribing to the single and calls onSuccessAction and onErrorAction on single events.
* Don't forget to process errors if single can emit them.
*
* @param single {@link Single} to subscribe until onDestroy;
* @param onSuccessAction Action which will raise on every {@link SingleEmitter#onSuccess(Object)} item;
* @param onErrorAction Action which will raise on every {@link SingleEmitter#onError(Throwable)} throwable;
* @param <T> Type of emitted by single items;
* @return {@link Disposable} which is wrapping source single to unsubscribe from it onDestroy.
*/
@NonNull
<T> Disposable untilDestroy(@NonNull Single<T> single, @NonNull Consumer<T> onSuccessAction, @NonNull Consumer<Throwable> onErrorAction);
/**
* Method should be used to guarantee that completable won't be subscribed after onDestroy.
* It is automatically subscribing to the completable.
* Don't forget to process errors if completable can emit them.
*
* @param completable {@link Completable} to subscribe until onDestroy;
* @return {@link Disposable} which is wrapping source completable to unsubscribe from it onDestroy.
*/
@NonNull
Disposable untilDestroy(@NonNull Completable completable);
/**
* Method should be used to guarantee that completable won't be subscribed after onDestroy.
* It is automatically subscribing to the completable and calls onCompletedAction on completable item.
* Don't forget to process errors if completable can emit them.
*
* @param completable {@link Completable} to subscribe until onDestroy;
* @param onCompletedAction Action which will raise on every {@link CompletableEmitter#onComplete()} item;
* @return {@link Disposable} which is wrapping source single to unsubscribe from it onDestroy.
*/
@NonNull
Disposable untilDestroy(@NonNull Completable completable, @NonNull Action onCompletedAction);
/**
* Method should be used to guarantee that completable won't be subscribed after onDestroy.
* It is automatically subscribing to the completable and calls onCompletedAction and onErrorAction on completable events.
* Don't forget to process errors if completable can emit them.
*
* @param completable {@link Completable} to subscribe until onDestroy;
* @param onCompletedAction Action which will raise on every {@link CompletableEmitter#onComplete()} item;
* @param onErrorAction Action which will raise on every {@link CompletableEmitter#onError(Throwable)} throwable;
* @return {@link Disposable} which is wrapping source completable to unsubscribe from it onDestroy.
*/
@NonNull
Disposable untilDestroy(@NonNull Completable completable, @NonNull Action onCompletedAction, @NonNull Consumer<Throwable> onErrorAction);
/**
* Method should be used to guarantee that maybe won't be subscribed after onDestroy.
* It is automatically subscribing to the maybe.
* Don't forget to process errors if maybe can emit them.
*
* @param maybe {@link Maybe} to subscribe until onDestroy;
* @return {@link Disposable} which is wrapping source maybe to unsubscribe from it onDestroy.
*/
@NonNull
<T> Disposable untilDestroy(@NonNull Maybe<T> maybe);
/**
* Method should be used to guarantee that maybe won't be subscribed after onDestroy.
* It is automatically subscribing to the maybe and calls onCompletedAction on maybe item.
* Don't forget to process errors if maybe can emit them.
*
* @param maybe {@link Maybe} to subscribe until onDestroy;
* @param onSuccessAction Action which will raise on every {@link MaybeEmitter#onSuccess(Object)} ()} item;
* @return {@link Disposable} which is wrapping source maybe to unsubscribe from it onDestroy.
*/
@NonNull
<T> Disposable untilDestroy(@NonNull Maybe<T> maybe, @NonNull Consumer<T> onSuccessAction);
/**
* Method should be used to guarantee that maybe won't be subscribed after onDestroy.
* It is automatically subscribing to the maybe and calls onSuccessAction and onErrorAction on maybe events.
* Don't forget to process errors if completable can emit them.
*
* @param maybe {@link Maybe} to subscribe until onDestroy;
* @param onSuccessAction Action which will raise on every {@link MaybeEmitter#onSuccess(Object)} ()} item;
* @param onErrorAction Action which will raise on every {@link MaybeEmitter#onError(Throwable)} throwable;
* @return {@link Disposable} which is wrapping source maybe to unsubscribe from it onDestroy.
*/
@NonNull
<T> Disposable untilDestroy(@NonNull Maybe<T> maybe, @NonNull Consumer<T> onSuccessAction, @NonNull Consumer<Throwable> onErrorAction);
}

View File

@ -1,110 +0,0 @@
/*
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package ru.touchin.roboswag.components.utils;
import android.content.Context;
import android.support.annotation.NonNull;
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
import io.reactivex.Observable;
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
/**
* Created by Gavriil Sitnikov on 24/03/16.
* Base class representing application's logic.
* In specific application it should be child of it (usually one) which contains all methods/objects related to logic.
* It should contains interface to work with API/preferences/database/file system/system parameters etc.
* Be sure that all objects/instances/services created to represents logic are not getting a lot of time to be instantiated, if they take a lot time
* for instantiation then it is wrong logic and it should be moved into asynchronous operations via {@link Observable} or so.
* Also it shouldn't create massive data objects and a lot of objects instantly. Basically it should just create bunch of interfaces inside
* which will allows to access to some logic methods.
* In fact it is similar to dependency injection pattern but with full control of instantiation and only one single instance of {@link Logic} per app.
* If you want to use it then just create getter in {@link android.app.Service}/{@link android.app.Activity}/{@link android.content.BroadcastReceiver}
* or any else context-based elements and do not forget to store reference to {@link Logic} into field because else it will be consumed by GC.
* Sample of {@link Logic} using is in {@link ru.touchin.roboswag.components.navigation.activities.ViewControllerActivity}.
* NOTE: Ideally creation of logic should be asynchronous and stored in specific {@link android.app.Service} so it should be accessed
* asynchronously via {@link Observable} or so. But in fact it requires {@link android.app.Service} plus more complex methods to access to logic.
* So currently it is more simple to access via simple bridge based on singletons stored into {@link WeakReference} because anyway instantiation of
* logic have to be as fast as it can. If it's not then it is just a bug and problem of optimization.
*/
public class Logic {
private static final Map<Class<? extends Logic>, WeakReference<Logic>> LOGIC_INSTANCES = new HashMap<>();
/**
* Returns instance of {@link Logic} depends on class. There should be no more than one instance per class.
*
* @param context Context of application where this {@link Logic} related to;
* @param logicClass Class of {@link Logic};
* @param <T> Type of class of {@link Logic};
* @return Instance of {@link Logic}.
*/
@SuppressWarnings({"unchecked", "PMD.SingletonClassReturningNewInstance"})
//SingletonClassReturningNewInstance: it is OK to create instance every time if WeakReference have died
@NonNull
public static <T extends Logic> T getInstance(@NonNull final Context context, @NonNull final Class<T> logicClass) {
T result;
synchronized (LOGIC_INSTANCES) {
final WeakReference<Logic> reference = LOGIC_INSTANCES.get(logicClass);
result = reference != null ? (T) reference.get() : null;
if (result == null) {
result = constructLogic(context.getApplicationContext(), logicClass);
LOGIC_INSTANCES.put(logicClass, new WeakReference<>(result));
}
}
return result;
}
@NonNull
@SuppressWarnings("unchecked")
private static <T extends Logic> T constructLogic(@NonNull final Context context, @NonNull final Class<T> logicClass) {
if (logicClass.getConstructors().length != 1 || logicClass.getConstructors()[0].getParameterTypes().length != 1) {
throw new ShouldNotHappenException("There should be only one public constructor(Context) for class " + logicClass);
}
final Constructor<?> constructor = logicClass.getConstructors()[0];
try {
return (T) constructor.newInstance(context);
} catch (final Exception exception) {
throw new ShouldNotHappenException(exception);
}
}
@NonNull
private final Context context;
public Logic(@NonNull final Context context) {
this.context = context;
}
/**
* Returns {@link android.app.Application}'s context.
*
* @return Context (possibly application).
*/
@NonNull
public Context getContext() {
return context;
}
}

View File

@ -21,13 +21,14 @@ package ru.touchin.roboswag.components.utils;
import android.app.Activity;
import android.app.Application;
import android.arch.lifecycle.Lifecycle;
import android.arch.lifecycle.LifecycleOwner;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.IdRes;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@ -41,11 +42,8 @@ import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import java.util.concurrent.atomic.AtomicInteger;
import io.reactivex.functions.Action;
import io.reactivex.functions.Consumer;
import ru.touchin.roboswag.components.navigation.activities.BaseActivity;
import ru.touchin.roboswag.core.log.Lc;
import ru.touchin.roboswag.core.log.LcGroup;
@ -130,9 +128,11 @@ public final class UiUtils {
}
final Runnable runnable = () -> {
final Context context = targetView.getContext();
if (targetView.getWindowVisibility() != View.VISIBLE
|| !targetView.hasWindowFocus()
|| (targetView.getContext() instanceof BaseActivity && !((BaseActivity) targetView.getContext()).isActuallyResumed())) {
|| (context instanceof LifecycleOwner
&& !((LifecycleOwner) context).getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED))) {
return;
}
try {
@ -292,35 +292,6 @@ public final class UiUtils {
*/
public static class OfViews {
private static final int GENERATED_ID_THRESHOLD = 0x00FFFFFF;
private static final AtomicInteger NEXT_GENERATED_ID = new AtomicInteger(1);
/**
* Generates unique ID for view. See android {@link View#generateViewId()}.
*
* @return Unique ID.
*/
@IdRes
public static int generateViewId() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
return View.generateViewId();
}
int result = 0;
boolean isGenerated = false;
while (!isGenerated) {
result = NEXT_GENERATED_ID.get();
// aapt-generated IDs have the high byte nonzero; clamp to the range under that.
int newValue = result + 1;
if (newValue > GENERATED_ID_THRESHOLD) {
newValue = 1; // Roll over to 1, not 0.
}
if (NEXT_GENERATED_ID.compareAndSet(result, newValue)) {
isGenerated = true;
}
}
return result;
}
/**
* Returns string representation of {@link View}'s ID.
*

View File

@ -0,0 +1,62 @@
package ru.touchin.roboswag.components.utils.destroyable
import io.reactivex.Completable
import io.reactivex.Maybe
import io.reactivex.Observable
import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable
/**
* Created by Oksana Pokrovskaya on 7/03/18.
* Simple implementation of [Destroyable]. Could be used to not implement interface but use such object inside.
*/
open class BaseDestroyable : Destroyable {
private val subscriptions = CompositeDisposable()
/**
* Call it on parent's onDestroy method.
*/
fun onDestroy() = subscriptions.dispose()
override fun <T> untilDestroy(
observable: Observable<T>,
onNextAction: (T) -> Unit,
onErrorAction: (Throwable) -> Unit,
onCompletedAction: () -> Unit
): Disposable = observable
.observeOn(AndroidSchedulers.mainThread())
.subscribe(onNextAction, onErrorAction, onCompletedAction)
.also { subscriptions.add(it) }
override fun <T> untilDestroy(
single: Single<T>,
onSuccessAction: (T) -> Unit,
onErrorAction: (Throwable) -> Unit
): Disposable = single
.observeOn(AndroidSchedulers.mainThread())
.subscribe(onSuccessAction, onErrorAction)
.also { subscriptions.add(it) }
override fun untilDestroy(
completable: Completable,
onCompletedAction: () -> Unit,
onErrorAction: (Throwable) -> Unit
): Disposable = completable
.observeOn(AndroidSchedulers.mainThread())
.subscribe(onCompletedAction, onErrorAction)
.also { subscriptions.add(it) }
override fun <T> untilDestroy(
maybe: Maybe<T>,
onSuccessAction: (T) -> Unit,
onErrorAction: (Throwable) -> Unit,
onCompletedAction: () -> Unit
): Disposable = maybe
.observeOn(AndroidSchedulers.mainThread())
.subscribe(onSuccessAction, onErrorAction, onCompletedAction)
.also { subscriptions.add(it) }
}

View File

@ -0,0 +1,94 @@
package ru.touchin.roboswag.components.utils.destroyable
import io.reactivex.Completable
import io.reactivex.Maybe
import io.reactivex.Observable
import io.reactivex.Single
import io.reactivex.disposables.Disposable
import io.reactivex.internal.functions.Functions
import ru.touchin.roboswag.core.log.Lc
import ru.touchin.roboswag.core.utils.ShouldNotHappenException
/**
* Created by Oksana Pokrovskaya on 7/03/18.
* Interface that should be implemented by ([android.arch.lifecycle.ViewModel] etc.)
* to not manually manage subscriptions.
* Use [.untilDestroy] method to subscribe to observable where you want and unsubscribe onDestroy.
*/
interface Destroyable {
companion object {
private fun getActionThrowableForAssertion(codePoint: String, method: String = "untilDestroy"): (Throwable) -> Unit = { throwable ->
Lc.assertion(ShouldNotHappenException("Unexpected error on $method at $codePoint", throwable))
}
}
/**
* Method should be used to guarantee that observable won't be subscribed after onDestroy.
* It is automatically subscribing to the observable and calls onNextAction and onErrorAction on observable events.
* Don't forget to process errors if observable can emit them.
*
* @param observable [Observable] to subscribe until onDestroy;
* @param onNextAction Action which will raise on every [io.reactivex.Emitter.onNext] item;
* @param onErrorAction Action which will raise on every [io.reactivex.Emitter.onError] throwable;
* @param T Type of emitted by observable items;
* @return [Disposable] which is wrapping source observable to unsubscribe from it onDestroy.
*/
fun <T> untilDestroy(
observable: Observable<T>,
onNextAction: (T) -> Unit = Functions.emptyConsumer<T>()::accept,
onErrorAction: (Throwable) -> Unit = getActionThrowableForAssertion(Lc.getCodePoint(this, 2)),
onCompletedAction: () -> Unit = Functions.EMPTY_ACTION::run
): Disposable
/**
* Method should be used to guarantee that single won't be subscribed after onDestroy.
* It is automatically subscribing to the single and calls onSuccessAction and onErrorAction on single events.
* Don't forget to process errors if single can emit them.
*
* @param single [Single] to subscribe until onDestroy;
* @param onSuccessAction Action which will raise on every [io.reactivex.SingleEmitter.onSuccess] item;
* @param onErrorAction Action which will raise on every [io.reactivex.SingleEmitter.onError] throwable;
* @param T Type of emitted by single items;
* @return [Disposable] which is wrapping source single to unsubscribe from it onDestroy.
*/
fun <T> untilDestroy(
single: Single<T>,
onSuccessAction: (T) -> Unit = Functions.emptyConsumer<T>()::accept,
onErrorAction: (Throwable) -> Unit = getActionThrowableForAssertion(Lc.getCodePoint(this, 2))
): Disposable
/**
* Method should be used to guarantee that completable won't be subscribed after onDestroy.
* It is automatically subscribing to the completable and calls onCompletedAction and onErrorAction on completable events.
* Don't forget to process errors if completable can emit them.
*
* @param completable [Completable] to subscribe until onDestroy;
* @param onCompletedAction Action which will raise on every [io.reactivex.CompletableEmitter.onComplete] item;
* @param onErrorAction Action which will raise on every [io.reactivex.CompletableEmitter.onError] throwable;
* @return [Disposable] which is wrapping source completable to unsubscribe from it onDestroy.
*/
fun untilDestroy(
completable: Completable,
onCompletedAction: () -> Unit = Functions.EMPTY_ACTION::run,
onErrorAction: (Throwable) -> Unit = getActionThrowableForAssertion(Lc.getCodePoint(this, 2))
): Disposable
/**
* Method should be used to guarantee that maybe won't be subscribed after onDestroy.
* It is automatically subscribing to the maybe and calls onSuccessAction and onErrorAction on maybe events.
* Don't forget to process errors if completable can emit them.
*
* @param maybe [Maybe] to subscribe until onDestroy;
* @param onSuccessAction Action which will raise on every [io.reactivex.MaybeEmitter.onSuccess] ()} item;
* @param onErrorAction Action which will raise on every [io.reactivex.MaybeEmitter.onError] throwable;
* @return [Disposable] which is wrapping source maybe to unsubscribe from it onDestroy.
*/
fun <T> untilDestroy(
maybe: Maybe<T>,
onSuccessAction: (T) -> Unit = Functions.emptyConsumer<T>()::accept,
onErrorAction: (Throwable) -> Unit = getActionThrowableForAssertion(Lc.getCodePoint(this, 2)),
onCompletedAction: () -> Unit = Functions.EMPTY_ACTION::run
): Disposable
}

View File

@ -1,254 +0,0 @@
/*
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package ru.touchin.roboswag.components.views;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Point;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import ru.touchin.roboswag.components.R;
/**
* Created by Gavriil Sitnikov on 01/07/14.
* FrameLayout that holds specific aspect ratio sizes.
* For example if aspect ratio equals 1.0 then this view will layout as square.
*/
public class AspectRatioFrameLayout extends FrameLayout {
private static final float DEFAULT_ASPECT_RATIO = 1.0f;
private static final float EPSILON = 0.0000001f;
private float aspectRatio;
private boolean wrapToContent;
public AspectRatioFrameLayout(@NonNull final Context context) {
this(context, null);
}
public AspectRatioFrameLayout(@NonNull final Context context, @Nullable final AttributeSet attrs) {
this(context, attrs, 0);
}
public AspectRatioFrameLayout(@NonNull final Context context, @Nullable final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
if (attrs == null) {
wrapToContent = false;
aspectRatio = DEFAULT_ASPECT_RATIO;
} else {
final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.AspectRatioFrameLayout);
wrapToContent = typedArray.getBoolean(R.styleable.AspectRatioFrameLayout_wrapToContent, false);
aspectRatio = typedArray.getFloat(R.styleable.AspectRatioFrameLayout_aspectRatio, DEFAULT_ASPECT_RATIO);
typedArray.recycle();
}
}
/* Returns aspect ratio of layout */
public float getAspectRatio() {
return aspectRatio;
}
/* Sets aspect ratio of layout */
public void setAspectRatio(final float aspectRatio) {
if (Math.abs(aspectRatio - this.aspectRatio) < EPSILON) {
return;
}
this.aspectRatio = aspectRatio;
requestLayout();
}
/* Returns if layout is wrapping to content but holds aspect ratio */
/**
* Returns if layout is wrapping to content but holds aspect ratio.
* If it is true it means that minimum size of view will equals to maximum size of it's child (biggest width or height) depends on aspect ratio.
* Else maximum size of view will equals to minimum available size which parent could give to this view depends on aspect ratio.
*
* @return True if wrapping to content.
*/
public boolean isWrapToContent() {
return wrapToContent;
}
/**
* Sets if layout is wrapping to content but holds aspect ratio.
*
* @param wrapToContent True if wrapping to content.
*/
public void setWrapToContent(final boolean wrapToContent) {
if (wrapToContent == this.wrapToContent) {
return;
}
this.wrapToContent = wrapToContent;
requestLayout();
}
private void setMeasuredDimensionWithAspectOfLesser(final int measuredWidth, final int measuredHeight) {
final float heightBasedOnMw = measuredWidth / aspectRatio;
if (heightBasedOnMw > measuredHeight) {
setMeasuredDimension((int) (measuredHeight * aspectRatio), measuredHeight);
} else {
setMeasuredDimension(measuredWidth, (int) heightBasedOnMw);
}
}
private void setMeasuredDimensionWithAspectOfHigher(final int measuredWidth, final int measuredHeight) {
final float heightBasedOnMw = measuredWidth / aspectRatio;
if (heightBasedOnMw < measuredHeight) {
setMeasuredDimension((int) (measuredHeight * aspectRatio), measuredHeight);
} else {
setMeasuredDimension(measuredWidth, (int) heightBasedOnMw);
}
}
@NonNull
private Point measureWrapChildren(final int widthMeasureSpec, final int heightMeasureSpec) {
final Point result = new Point();
for (int i = 0; i < getChildCount(); i++) {
final View child = getChildAt(i);
child.measure(widthMeasureSpec, heightMeasureSpec);
if (result.x < child.getMeasuredWidth()) {
result.x = child.getMeasuredWidth();
}
if (result.y < child.getMeasuredHeight()) {
result.y = child.getMeasuredHeight();
}
}
return result;
}
@Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (wrapToContent) {
final Point bounds = measureWrapChildren(widthMeasureSpec, heightMeasureSpec);
width = widthMode == MeasureSpec.UNSPECIFIED ? bounds.x : Math.min(bounds.x, width);
height = heightMode == MeasureSpec.UNSPECIFIED ? bounds.y : Math.min(bounds.y, height);
}
if (widthMode == MeasureSpec.UNSPECIFIED) {
if (heightMode == MeasureSpec.UNSPECIFIED) {
measureBothUnspecified(width, height);
} else {
measureOnlyUnspecifiedWidth(width, height);
}
} else if (heightMode == MeasureSpec.UNSPECIFIED) {
measureOnlyUnspecifiedHeight(width, height);
} else {
measureBothSpecified(width, height);
}
}
private void measureBothSpecified(final int width, final int height) {
if (wrapToContent) {
setMeasuredDimensionWithAspectOfHigher(width, height);
} else {
setMeasuredDimensionWithAspectOfLesser(width, height);
}
}
private void measureOnlyUnspecifiedHeight(final int width, final int height) {
if (wrapToContent) {
measureWrapToContent(width, height);
} else {
setMeasuredDimension(width, (int) (width / aspectRatio));
}
}
private void measureWrapToContent(final int width, final int height) {
if (width < (int) (height * aspectRatio)) {
setMeasuredDimension((int) (height * aspectRatio), height);
} else {
setMeasuredDimension(width, (int) (width / aspectRatio));
}
}
private void measureOnlyUnspecifiedWidth(final int width, final int height) {
if (wrapToContent) {
measureWrapToContent(width, height);
} else {
setMeasuredDimension((int) (height * aspectRatio), height);
}
}
private void measureBothUnspecified(final int width, final int height) {
if (wrapToContent) {
setMeasuredDimensionWithAspectOfHigher(width, height);
} else {
final DisplayMetrics metrics = getResources().getDisplayMetrics();
setMeasuredDimensionWithAspectOfLesser(metrics.widthPixels, metrics.heightPixels);
}
}
@Override
protected void onLayout(final boolean changed, final int left, final int top, final int right, final int bottom) {
for (int i = 0; i < getChildCount(); i++) {
final View child = getChildAt(i);
final ViewGroup.LayoutParams lp = child.getLayoutParams();
final int widthMeasureSpec;
final int heightMeasureSpec;
final int width = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
final int height = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
switch (lp.width) {
case ViewGroup.LayoutParams.MATCH_PARENT:
widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
break;
case ViewGroup.LayoutParams.WRAP_CONTENT:
widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST);
break;
default:
widthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
break;
}
switch (lp.height) {
case ViewGroup.LayoutParams.MATCH_PARENT:
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
break;
case ViewGroup.LayoutParams.WRAP_CONTENT:
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
break;
default:
heightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
break;
}
getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
}
super.onLayout(changed, left, top, right, bottom);
}
}

View File

@ -1,325 +0,0 @@
/*
* Copyright (c) 2017 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
*
* This file is part of RoboSwag library.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package ru.touchin.roboswag.components.views;
import android.content.Context;
import android.os.Parcelable;
import android.support.annotation.AttrRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.widget.FrameLayout;
import io.reactivex.Maybe;
import ru.touchin.roboswag.components.utils.BaseLifecycleBindable;
import ru.touchin.roboswag.components.utils.LifecycleBindable;
import io.reactivex.Completable;
import io.reactivex.Observable;
import io.reactivex.Single;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Action;
import io.reactivex.functions.Consumer;
/**
* Created by Gavriil Sitnikov on 18/05/17.
* FrameLayout that realizes LifecycleBindable interface.
*/
@SuppressWarnings({"CPD-START", "PMD.TooManyMethods"})
public class LifecycleView extends FrameLayout implements LifecycleBindable {
@NonNull
private final BaseLifecycleBindable baseLifecycleBindable;
private boolean created;
private boolean started;
public LifecycleView(@NonNull final Context context) {
super(context);
baseLifecycleBindable = new BaseLifecycleBindable();
}
public LifecycleView(@NonNull final Context context, @Nullable final AttributeSet attrs) {
super(context, attrs);
baseLifecycleBindable = new BaseLifecycleBindable();
}
public LifecycleView(@NonNull final Context context, @Nullable final AttributeSet attrs, @AttrRes final int defStyleAttr) {
super(context, attrs, defStyleAttr);
baseLifecycleBindable = new BaseLifecycleBindable();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
onCreate();
if (!started && getWindowSystemUiVisibility() == VISIBLE) {
onStart();
}
}
/**
* Calls when view attached to window and ready to use.
*/
protected void onCreate() {
created = true;
baseLifecycleBindable.onCreate();
}
/**
* Calls when view's window showed or state restored.
*/
protected void onStart() {
started = true;
baseLifecycleBindable.onStart();
}
@Override
protected void onRestoreInstanceState(@NonNull final Parcelable state) {
super.onRestoreInstanceState(state);
if (created && !started) {
onStart();
}
}
@NonNull
@Override
protected Parcelable onSaveInstanceState() {
started = false;
baseLifecycleBindable.onSaveInstanceState();
return super.onSaveInstanceState();
}
/**
* Calls when view's window hided or state saved.
*/
protected void onStop() {
started = false;
baseLifecycleBindable.onStop();
}
/**
* Calls when view detached from window.
*/
protected void onDestroy() {
if (started) {
onStop();
}
created = false;
baseLifecycleBindable.onDestroy();
}
@Override
protected void onDetachedFromWindow() {
onDestroy();
super.onDetachedFromWindow();
}
@Override
protected void onWindowVisibilityChanged(final int visibility) {
super.onWindowVisibilityChanged(visibility);
if (visibility == VISIBLE) {
if (created && !started) {
baseLifecycleBindable.onStart();
}
} else if (started) {
baseLifecycleBindable.onStop();
}
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Observable<T> observable) {
return baseLifecycleBindable.untilStop(observable);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Observable<T> observable, @NonNull final Consumer<T> onNextAction) {
return baseLifecycleBindable.untilStop(observable, onNextAction);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Observable<T> observable,
@NonNull final Consumer<T> onNextAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return baseLifecycleBindable.untilStop(observable, onNextAction, onErrorAction);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Observable<T> observable,
@NonNull final Consumer<T> onNextAction,
@NonNull final Consumer<Throwable> onErrorAction,
@NonNull final Action onCompletedAction) {
return baseLifecycleBindable.untilStop(observable, onNextAction, onErrorAction, onCompletedAction);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Single<T> single) {
return baseLifecycleBindable.untilStop(single);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Single<T> single, @NonNull final Consumer<T> onSuccessAction) {
return baseLifecycleBindable.untilStop(single, onSuccessAction);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Single<T> single,
@NonNull final Consumer<T> onSuccessAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return baseLifecycleBindable.untilStop(single, onSuccessAction, onErrorAction);
}
@NonNull
@Override
public Disposable untilStop(@NonNull final Completable completable) {
return baseLifecycleBindable.untilStop(completable);
}
@NonNull
@Override
public Disposable untilStop(@NonNull final Completable completable, @NonNull final Action onCompletedAction) {
return baseLifecycleBindable.untilStop(completable, onCompletedAction);
}
@NonNull
@Override
public Disposable untilStop(@NonNull final Completable completable,
@NonNull final Action onCompletedAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return baseLifecycleBindable.untilStop(completable, onCompletedAction, onErrorAction);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Maybe<T> maybe) {
return baseLifecycleBindable.untilStop(maybe);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Maybe<T> maybe, @NonNull final Consumer<T> onSuccessAction) {
return baseLifecycleBindable.untilStop(maybe, onSuccessAction);
}
@NonNull
@Override
public <T> Disposable untilStop(@NonNull final Maybe<T> maybe,
@NonNull final Consumer<T> onSuccessAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return baseLifecycleBindable.untilStop(maybe, onSuccessAction, onErrorAction);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Observable<T> observable) {
return baseLifecycleBindable.untilDestroy(observable);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Observable<T> observable, @NonNull final Consumer<T> onNextAction) {
return baseLifecycleBindable.untilDestroy(observable, onNextAction);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Observable<T> observable,
@NonNull final Consumer<T> onNextAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return baseLifecycleBindable.untilDestroy(observable, onNextAction, onErrorAction);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Observable<T> observable,
@NonNull final Consumer<T> onNextAction,
@NonNull final Consumer<Throwable> onErrorAction,
@NonNull final Action onCompletedAction) {
return baseLifecycleBindable.untilDestroy(observable, onNextAction, onErrorAction, onCompletedAction);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Single<T> single) {
return baseLifecycleBindable.untilDestroy(single);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Single<T> single, @NonNull final Consumer<T> onSuccessAction) {
return baseLifecycleBindable.untilDestroy(single, onSuccessAction);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Single<T> single,
@NonNull final Consumer<T> onSuccessAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return baseLifecycleBindable.untilDestroy(single, onSuccessAction, onErrorAction);
}
@NonNull
@Override
public Disposable untilDestroy(@NonNull final Completable completable) {
return baseLifecycleBindable.untilDestroy(completable);
}
@NonNull
@Override
public Disposable untilDestroy(@NonNull final Completable completable, @NonNull final Action onCompletedAction) {
return baseLifecycleBindable.untilDestroy(completable, onCompletedAction);
}
@NonNull
@Override
public Disposable untilDestroy(@NonNull final Completable completable,
@NonNull final Action onCompletedAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return baseLifecycleBindable.untilDestroy(completable, onCompletedAction, onErrorAction);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Maybe<T> maybe) {
return baseLifecycleBindable.untilDestroy(maybe);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Maybe<T> maybe, @NonNull final Consumer<T> onSuccessAction) {
return baseLifecycleBindable.untilDestroy(maybe, onSuccessAction);
}
@NonNull
@Override
public <T> Disposable untilDestroy(@NonNull final Maybe<T> maybe,
@NonNull final Consumer<T> onSuccessAction,
@NonNull final Consumer<Throwable> onErrorAction) {
return baseLifecycleBindable.untilDestroy(maybe, onSuccessAction, onErrorAction);
}
}

View File

@ -19,11 +19,6 @@
<attr name="isMultiline" format="boolean"/>
</declare-styleable>
<declare-styleable name="AspectRatioFrameLayout">
<attr name="aspectRatio" format="float" />
<attr name="wrapToContent" format="boolean" />
</declare-styleable>
<declare-styleable name="MaterialLoadingBar">
<attr name="strokeWidth" format="dimension"/>
<attr name="color" format="color"/>
@ -31,4 +26,4 @@
<attr name="materialLoadingBarStyle" format="reference"/>
</declare-styleable>
</resources>
</resources>