Compare commits
128 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
bcb22fa542 | |
|
|
1770e03302 | |
|
|
3581f0bb5e | |
|
|
06a5157010 | |
|
|
52c618a1bf | |
|
|
45cf82bf10 | |
|
|
ca46bc290a | |
|
|
5e2d3431dc | |
|
|
ea3013baec | |
|
|
a9987b89ff | |
|
|
6677b0d0b7 | |
|
|
e68809010e | |
|
|
72a2412f41 | |
|
|
726f466c02 | |
|
|
b2e3d6b0f0 | |
|
|
1ffe50bdae | |
|
|
6ff1409d02 | |
|
|
f9cac685ac | |
|
|
598dad199d | |
|
|
e5a0942ea5 | |
|
|
7d377614cb | |
|
|
276e697d83 | |
|
|
448501aa06 | |
|
|
42750812dd | |
|
|
9453856713 | |
|
|
f3d076aa6c | |
|
|
5b3ccdbf91 | |
|
|
a55510f8f1 | |
|
|
a789ae9efe | |
|
|
d9981592af | |
|
|
2cfa072319 | |
|
|
dc773302f0 | |
|
|
78d213abc5 | |
|
|
b528ec185e | |
|
|
818d8aedaf | |
|
|
a3c113125d | |
|
|
77a0160fce | |
|
|
d71938c87f | |
|
|
89377c3fea | |
|
|
0815cdd421 | |
|
|
e2107e9990 | |
|
|
90c7c25f6c | |
|
|
400ae4f00d | |
|
|
996fefe395 | |
|
|
fbdaad47ca | |
|
|
ebdba5a90b | |
|
|
7b15f41968 | |
|
|
7edc1fabe1 | |
|
|
496e0da5da | |
|
|
9508946b5a | |
|
|
b64452b086 | |
|
|
ed7cd8bd42 | |
|
|
b06a1232b7 | |
|
|
0cbf55d81b | |
|
|
399ec7d7f5 | |
|
|
496abde610 | |
|
|
cd9db1203a | |
|
|
bbc73d3d12 | |
|
|
7e35087c0a | |
|
|
5f6152050b | |
|
|
69f1c9af4a | |
|
|
e3e978b7f7 | |
|
|
b64e1c9e35 | |
|
|
dc87783252 | |
|
|
9b1275ceec | |
|
|
44af99f9cf | |
|
|
8eaa50d891 | |
|
|
5805c63288 | |
|
|
a3dca51488 | |
|
|
426a213a44 | |
|
|
352ff5a8b4 | |
|
|
03689b5382 | |
|
|
e3c3c95767 | |
|
|
6a6ea0ec08 | |
|
|
43ad3d62d9 | |
|
|
3a3f416b8c | |
|
|
bab8ceb74f | |
|
|
d3191cd472 | |
|
|
6d0f74a972 | |
|
|
37f5664187 | |
|
|
46d00ffc5f | |
|
|
dc94030420 | |
|
|
527de35579 | |
|
|
dd2732c129 | |
|
|
6faabed582 | |
|
|
c9448639d5 | |
|
|
fda49db855 | |
|
|
99e1046907 | |
|
|
de52c32e98 | |
|
|
0a82b7a57c | |
|
|
ea35a1dc5d | |
|
|
3f8955f31b | |
|
|
64ae48ae39 | |
|
|
8405c76bf0 | |
|
|
97c9a7571b | |
|
|
ef7cf6d50d | |
|
|
70e6b99e79 | |
|
|
00d3433b87 | |
|
|
ffe587a8f5 | |
|
|
a517b16334 | |
|
|
c32eb4b835 | |
|
|
155ad932f6 | |
|
|
dfc7615187 | |
|
|
bb7473e839 | |
|
|
2398b4fc10 | |
|
|
6faf786ba4 | |
|
|
d3bcd89743 | |
|
|
540e573888 | |
|
|
06f9b1d2cc | |
|
|
9da0e059b5 | |
|
|
8953170dca | |
|
|
e9f7815ca3 | |
|
|
e23d9d55b0 | |
|
|
16624b3657 | |
|
|
660065490d | |
|
|
88113ef0ee | |
|
|
c57f30aca1 | |
|
|
e847fcedc5 | |
|
|
a2b69d64a1 | |
|
|
38f8a8018e | |
|
|
5d0847370d | |
|
|
d8ca0e610b | |
|
|
e2d584b5f7 | |
|
|
5f13aefa84 | |
|
|
949025a106 | |
|
|
a4a0e0d4b8 | |
|
|
68f7c73ff9 | |
|
|
fd68f1ffd4 |
18
build.gradle
18
build.gradle
|
|
@ -1,9 +1,8 @@
|
|||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'me.tatarka.retrolambda'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
android {
|
||||
compileSdkVersion 25
|
||||
buildToolsVersion '25.0.3'
|
||||
compileSdkVersion compileSdk
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
|
|
@ -16,11 +15,14 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':libraries:core')
|
||||
api project(':libraries:core')
|
||||
|
||||
provided 'com.android.support:appcompat-v7:25.4.0'
|
||||
provided 'com.android.support:recyclerview-v7:25.4.0'
|
||||
compileOnly "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
|
||||
provided 'io.reactivex:rxandroid:1.2.1'
|
||||
provided 'io.reactivex:rxjava:1.3.0'
|
||||
compileOnly "com.android.support:appcompat-v7:$supportLibraryVersion"
|
||||
compileOnly "com.android.support:design:$supportLibraryVersion"
|
||||
compileOnly "com.android.support:recyclerview-v7:$supportLibraryVersion"
|
||||
|
||||
compileOnly "io.reactivex.rxjava2:rxandroid:$rxAndroidVersion"
|
||||
compileOnly "io.reactivex.rxjava2:rxjava:$rxJavaVersion"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,45 +20,21 @@
|
|||
package ru.touchin.roboswag.components.adapters;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import ru.touchin.roboswag.components.utils.LifecycleBindable;
|
||||
import ru.touchin.roboswag.components.utils.UiUtils;
|
||||
import rx.Completable;
|
||||
import rx.Observable;
|
||||
import rx.Single;
|
||||
import rx.Subscription;
|
||||
import rx.functions.Action0;
|
||||
import rx.functions.Action1;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param <TViewHolder> Type of {@link RecyclerView.ViewHolder} of delegate.
|
||||
*/
|
||||
@SuppressWarnings("PMD.TooManyMethods")
|
||||
//TooManyMethods: it's ok
|
||||
public abstract class AdapterDelegate<TViewHolder extends BindableViewHolder> implements LifecycleBindable {
|
||||
public abstract class AdapterDelegate<TViewHolder extends RecyclerView.ViewHolder> {
|
||||
|
||||
@NonNull
|
||||
private final LifecycleBindable parentLifecycleBindable;
|
||||
private final int defaultItemViewType;
|
||||
|
||||
public AdapterDelegate(@NonNull final LifecycleBindable parentLifecycleBindable) {
|
||||
this.parentLifecycleBindable = parentLifecycleBindable;
|
||||
this.defaultItemViewType = UiUtils.OfViews.generateViewId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns parent {@link LifecycleBindable} that this delegate created from (e.g. Activity or ViewController).
|
||||
*
|
||||
* @return Parent {@link LifecycleBindable}.
|
||||
*/
|
||||
@NonNull
|
||||
public LifecycleBindable getParentLifecycleBindable() {
|
||||
return parentLifecycleBindable;
|
||||
}
|
||||
private final int defaultItemViewType = ViewCompat.generateViewId();
|
||||
|
||||
/**
|
||||
* Unique ID of AdapterDelegate.
|
||||
|
|
@ -69,6 +45,28 @@ public abstract class AdapterDelegate<TViewHolder extends BindableViewHolder> im
|
|||
return defaultItemViewType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if object is processable by this delegate.
|
||||
*
|
||||
* @param items Items to check;
|
||||
* @param adapterPosition Position of item in adapter;
|
||||
* @param collectionPosition Position of item in collection;
|
||||
* @return True if item is processable by this delegate.
|
||||
*/
|
||||
public abstract boolean isForViewType(@NonNull final List<Object> items, final int adapterPosition, final int collectionPosition);
|
||||
|
||||
/**
|
||||
* Returns unique ID of item to support stable ID's logic of RecyclerView's adapter.
|
||||
*
|
||||
* @param items Items in adapter;
|
||||
* @param adapterPosition Position of item in adapter;
|
||||
* @param collectionPosition Position of item in collection;
|
||||
* @return Unique item ID.
|
||||
*/
|
||||
public long getItemId(@NonNull final List<Object> items, final int adapterPosition, final int collectionPosition) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates ViewHolder to bind item to it later.
|
||||
*
|
||||
|
|
@ -78,144 +76,21 @@ 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> Subscription untilStop(@NonNull final Observable<T> observable) {
|
||||
return parentLifecycleBindable.untilStop(observable);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Observable<T> observable, @NonNull final Action1<T> onNextAction) {
|
||||
return parentLifecycleBindable.untilStop(observable, onNextAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Observable<T> observable,
|
||||
@NonNull final Action1<T> onNextAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction) {
|
||||
return parentLifecycleBindable.untilStop(observable, onNextAction, onErrorAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Observable<T> observable,
|
||||
@NonNull final Action1<T> onNextAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction,
|
||||
@NonNull final Action0 onCompletedAction) {
|
||||
return parentLifecycleBindable.untilStop(observable, onNextAction, onErrorAction, onCompletedAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Single<T> single) {
|
||||
return parentLifecycleBindable.untilStop(single);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Single<T> single, @NonNull final Action1<T> onSuccessAction) {
|
||||
return parentLifecycleBindable.untilStop(single, onSuccessAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Single<T> single,
|
||||
@NonNull final Action1<T> onSuccessAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction) {
|
||||
return parentLifecycleBindable.untilStop(single, onSuccessAction, onErrorAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Subscription untilStop(@NonNull final Completable completable) {
|
||||
return parentLifecycleBindable.untilStop(completable);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Subscription untilStop(@NonNull final Completable completable, @NonNull final Action0 onCompletedAction) {
|
||||
return parentLifecycleBindable.untilStop(completable, onCompletedAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Subscription untilStop(@NonNull final Completable completable,
|
||||
@NonNull final Action0 onCompletedAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction) {
|
||||
return parentLifecycleBindable.untilStop(completable, onCompletedAction, onErrorAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Observable<T> observable) {
|
||||
return parentLifecycleBindable.untilDestroy(observable);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Observable<T> observable, @NonNull final Action1<T> onNextAction) {
|
||||
return parentLifecycleBindable.untilDestroy(observable, onNextAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Observable<T> observable,
|
||||
@NonNull final Action1<T> onNextAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction) {
|
||||
return parentLifecycleBindable.untilDestroy(observable, onNextAction, onErrorAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Observable<T> observable,
|
||||
@NonNull final Action1<T> onNextAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction,
|
||||
@NonNull final Action0 onCompletedAction) {
|
||||
return parentLifecycleBindable.untilDestroy(observable, onNextAction, onErrorAction, onCompletedAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Single<T> single) {
|
||||
return parentLifecycleBindable.untilDestroy(single);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Single<T> single, @NonNull final Action1<T> onSuccessAction) {
|
||||
return parentLifecycleBindable.untilDestroy(single, onSuccessAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Single<T> single,
|
||||
@NonNull final Action1<T> onSuccessAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction) {
|
||||
return parentLifecycleBindable.untilDestroy(single, onSuccessAction, onErrorAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Subscription untilDestroy(@NonNull final Completable completable) {
|
||||
return parentLifecycleBindable.untilDestroy(completable);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Subscription untilDestroy(@NonNull final Completable completable, @NonNull final Action0 onCompletedAction) {
|
||||
return parentLifecycleBindable.untilDestroy(completable, onCompletedAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Subscription untilDestroy(@NonNull final Completable completable,
|
||||
@NonNull final Action0 onCompletedAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction) {
|
||||
return parentLifecycleBindable.untilDestroy(completable, onCompletedAction, onErrorAction);
|
||||
}
|
||||
/**
|
||||
* Binds item to created by this object ViewHolder.
|
||||
*
|
||||
* @param holder ViewHolder to bind item to;
|
||||
* @param items Items in adapter;
|
||||
* @param adapterPosition Position of item in adapter;
|
||||
* @param collectionPosition Position of item in collection that contains item;
|
||||
* @param payloads Payloads;
|
||||
*/
|
||||
public abstract void onBindViewHolder(
|
||||
@NonNull final RecyclerView.ViewHolder holder,
|
||||
@NonNull final List<Object> items,
|
||||
final int adapterPosition,
|
||||
final int collectionPosition,
|
||||
@NonNull final List<Object> payloads
|
||||
);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,263 +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 ru.touchin.roboswag.components.utils.LifecycleBindable;
|
||||
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
|
||||
import rx.Completable;
|
||||
import rx.Observable;
|
||||
import rx.Single;
|
||||
import rx.Subscription;
|
||||
import rx.functions.Action0;
|
||||
import rx.functions.Action1;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Observable<T> observable) {
|
||||
return baseLifecycleBindable.untilStop(observable);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Observable<T> observable, @NonNull final Action1<T> onNextAction) {
|
||||
return baseLifecycleBindable.untilStop(observable, onNextAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Observable<T> observable,
|
||||
@NonNull final Action1<T> onNextAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction) {
|
||||
return baseLifecycleBindable.untilStop(observable, onNextAction, onErrorAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Observable<T> observable,
|
||||
@NonNull final Action1<T> onNextAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction,
|
||||
@NonNull final Action0 onCompletedAction) {
|
||||
return baseLifecycleBindable.untilStop(observable, onNextAction, onErrorAction, onCompletedAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Single<T> single) {
|
||||
return baseLifecycleBindable.untilStop(single);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Single<T> single, @NonNull final Action1<T> onSuccessAction) {
|
||||
return baseLifecycleBindable.untilStop(single, onSuccessAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Single<T> single,
|
||||
@NonNull final Action1<T> onSuccessAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction) {
|
||||
return baseLifecycleBindable.untilStop(single, onSuccessAction, onErrorAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Subscription untilStop(@NonNull final Completable completable) {
|
||||
return baseLifecycleBindable.untilStop(completable);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Subscription untilStop(@NonNull final Completable completable, @NonNull final Action0 onCompletedAction) {
|
||||
return baseLifecycleBindable.untilStop(completable, onCompletedAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Subscription untilStop(@NonNull final Completable completable,
|
||||
@NonNull final Action0 onCompletedAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction) {
|
||||
return baseLifecycleBindable.untilStop(completable, onCompletedAction, onErrorAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Observable<T> observable) {
|
||||
return baseLifecycleBindable.untilDestroy(observable);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Observable<T> observable, @NonNull final Action1<T> onNextAction) {
|
||||
return baseLifecycleBindable.untilDestroy(observable, onNextAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Observable<T> observable,
|
||||
@NonNull final Action1<T> onNextAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction) {
|
||||
return baseLifecycleBindable.untilDestroy(observable, onNextAction, onErrorAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Observable<T> observable,
|
||||
@NonNull final Action1<T> onNextAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction,
|
||||
@NonNull final Action0 onCompletedAction) {
|
||||
return baseLifecycleBindable.untilDestroy(observable, onNextAction, onErrorAction, onCompletedAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Single<T> single) {
|
||||
return baseLifecycleBindable.untilDestroy(single);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Single<T> single, @NonNull final Action1<T> onSuccessAction) {
|
||||
return baseLifecycleBindable.untilDestroy(single, onSuccessAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Single<T> single,
|
||||
@NonNull final Action1<T> onSuccessAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction) {
|
||||
return baseLifecycleBindable.untilDestroy(single, onSuccessAction, onErrorAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Subscription untilDestroy(@NonNull final Completable completable) {
|
||||
return baseLifecycleBindable.untilDestroy(completable);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Subscription untilDestroy(@NonNull final Completable completable, @NonNull final Action0 onCompletedAction) {
|
||||
return baseLifecycleBindable.untilDestroy(completable, onCompletedAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Subscription untilDestroy(@NonNull final Completable completable,
|
||||
@NonNull final Action0 onCompletedAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction) {
|
||||
return baseLifecycleBindable.untilDestroy(completable, onCompletedAction, onErrorAction);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package ru.touchin.roboswag.components.adapters
|
||||
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import android.util.SparseArray
|
||||
import android.view.ViewGroup
|
||||
|
||||
/**
|
||||
* Manager for delegation callbacks from [RecyclerView.Adapter] to delegates.
|
||||
*/
|
||||
class DelegatesManager {
|
||||
|
||||
private val delegates = SparseArray<AdapterDelegate<*>>()
|
||||
|
||||
fun getItemViewType(items: List<*>, adapterPosition: Int, collectionPosition: Int): Int {
|
||||
for (index in 0 until delegates.size()) {
|
||||
val delegate = delegates.valueAt(index)
|
||||
if (delegate.isForViewType(items, adapterPosition, collectionPosition)) {
|
||||
return delegate.itemViewType
|
||||
}
|
||||
}
|
||||
throw IllegalStateException("Delegate not found for adapterPosition: $adapterPosition")
|
||||
}
|
||||
|
||||
fun getItemId(items: List<*>, adapterPosition: Int, collectionPosition: Int): Long {
|
||||
val delegate = getDelegate(getItemViewType(items, adapterPosition, collectionPosition))
|
||||
return delegate.getItemId(items, adapterPosition, collectionPosition)
|
||||
}
|
||||
|
||||
fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder = getDelegate(viewType).onCreateViewHolder(parent)
|
||||
|
||||
fun onBindViewHolder(holder: RecyclerView.ViewHolder, items: List<*>, adapterPosition: Int, collectionPosition: Int, payloads: List<Any>) {
|
||||
val delegate = getDelegate(getItemViewType(items, adapterPosition, collectionPosition))
|
||||
delegate.onBindViewHolder(holder, items, adapterPosition, collectionPosition, payloads)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds [PositionAdapterDelegate] to adapter.
|
||||
*
|
||||
* @param delegate Delegate to add.
|
||||
*/
|
||||
fun addDelegate(delegate: AdapterDelegate<*>) = delegates.put(delegate.itemViewType, delegate)
|
||||
|
||||
/**
|
||||
* Removes [AdapterDelegate] from adapter.
|
||||
*
|
||||
* @param delegate Delegate to remove.
|
||||
*/
|
||||
fun removeDelegate(delegate: AdapterDelegate<*>) = delegates.remove(delegate.itemViewType)
|
||||
|
||||
private fun getDelegate(viewType: Int) = delegates[viewType] ?: throw IllegalStateException("No AdapterDelegate added for view type: $viewType")
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
package ru.touchin.roboswag.components.adapters
|
||||
|
||||
import android.support.v7.recyclerview.extensions.AsyncDifferConfig
|
||||
import android.support.v7.recyclerview.extensions.AsyncListDiffer
|
||||
import android.support.v7.util.DiffUtil
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import android.view.ViewGroup
|
||||
import ru.touchin.roboswag.components.extensions.setOnRippleClickListener
|
||||
|
||||
/**
|
||||
* Base adapter with delegation and diff computing on background thread.
|
||||
*/
|
||||
open class DelegationListAdapter<TItem>(config: AsyncDifferConfig<TItem>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
|
||||
constructor(diffCallback: DiffUtil.ItemCallback<TItem>) : this(AsyncDifferConfig.Builder<TItem>(diffCallback).build())
|
||||
|
||||
var itemClickListener: ((TItem, RecyclerView.ViewHolder) -> Unit)? = null
|
||||
|
||||
private val delegatesManager = DelegatesManager()
|
||||
private var differ = AsyncListDiffer(OffsetAdapterUpdateCallback(this, ::getHeadersCount), config)
|
||||
|
||||
open fun getHeadersCount() = 0
|
||||
|
||||
open fun getFootersCount() = 0
|
||||
|
||||
override fun getItemCount() = getHeadersCount() + getList().size + getFootersCount()
|
||||
|
||||
override fun getItemViewType(position: Int) = delegatesManager.getItemViewType(getList(), position, getCollectionPosition(position))
|
||||
|
||||
override fun getItemId(position: Int) = delegatesManager.getItemId(getList(), position, getCollectionPosition(position))
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = delegatesManager.onCreateViewHolder(parent, viewType)
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int, payloads: List<Any>) {
|
||||
val collectionPosition = getCollectionPosition(position)
|
||||
if (collectionPosition in 0 until getList().size) {
|
||||
if (itemClickListener != null) {
|
||||
holder.itemView.setOnRippleClickListener {
|
||||
itemClickListener?.invoke(getList()[getCollectionPosition(holder.adapterPosition)], holder)
|
||||
}
|
||||
} else {
|
||||
holder.itemView.setOnClickListener(null)
|
||||
}
|
||||
}
|
||||
delegatesManager.onBindViewHolder(holder, getList(), position, collectionPosition, payloads)
|
||||
}
|
||||
|
||||
final override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) = Unit
|
||||
|
||||
/**
|
||||
* Adds [AdapterDelegate] to adapter.
|
||||
*
|
||||
* @param delegate Delegate to add.
|
||||
*/
|
||||
fun addDelegate(delegate: AdapterDelegate<*>) = delegatesManager.addDelegate(delegate)
|
||||
|
||||
/**
|
||||
* Removes [AdapterDelegate] from adapter.
|
||||
*
|
||||
* @param delegate Delegate to remove.
|
||||
*/
|
||||
fun removeDelegate(delegate: AdapterDelegate<*>) = delegatesManager.removeDelegate(delegate)
|
||||
|
||||
/**
|
||||
* Submits a new list to be diffed, and displayed.
|
||||
*
|
||||
* If a list is already being displayed, a diff will be computed on a background thread, which
|
||||
* will dispatch Adapter.notifyItem events on the main thread.
|
||||
*
|
||||
* @param list The new list to be displayed.
|
||||
*/
|
||||
fun submitList(list: List<TItem>) = differ.submitList(list)
|
||||
|
||||
/**
|
||||
* Get the current List - any diffing to present this list has already been computed and
|
||||
* dispatched via the ListUpdateCallback.
|
||||
* <p>
|
||||
* If a <code>null</code> List, or no List has been submitted, an empty list will be returned.
|
||||
* <p>
|
||||
* The returned list may not be mutated - mutations to content must be done through
|
||||
* {@link #submitList(List)}.
|
||||
*
|
||||
* @return current List.
|
||||
*/
|
||||
fun getList(): List<TItem> = differ.currentList
|
||||
|
||||
fun getCollectionPosition(adapterPosition: Int) = adapterPosition - getHeadersCount()
|
||||
|
||||
}
|
||||
|
|
@ -1,81 +1,85 @@
|
|||
package ru.touchin.roboswag.components.adapters;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.ViewGroup;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
|
||||
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 RecyclerView.ViewHolder} of delegate;
|
||||
* @param <TItem> Type of items to bind to {@link RecyclerView.ViewHolder}s.
|
||||
*/
|
||||
public abstract class ItemAdapterDelegate<TViewHolder extends BindableViewHolder, TItem> extends AdapterDelegate<TViewHolder> {
|
||||
public abstract class ItemAdapterDelegate<TViewHolder extends RecyclerView.ViewHolder, TItem> extends AdapterDelegate<TViewHolder> {
|
||||
|
||||
public ItemAdapterDelegate(@NonNull final LifecycleBindable parentLifecycleBindable) {
|
||||
super(parentLifecycleBindable);
|
||||
@Override
|
||||
public boolean isForViewType(@NonNull final List<Object> items, final int adapterPosition, final int collectionPosition) {
|
||||
return collectionPosition >= 0
|
||||
&& collectionPosition < items.size()
|
||||
&& isForViewType(items.get(collectionPosition), adapterPosition, collectionPosition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if object is processable by this delegate.
|
||||
* This item will be casted to {@link TItem} and passes to {@link #onBindViewHolder(TViewHolder, TItem, int, int)}.
|
||||
* This item will be casted to {@link TItem} and passes to {@link #onBindViewHolder(TViewHolder, TItem, int, int, List)}.
|
||||
*
|
||||
* @param item Item to check;
|
||||
* @param positionInAdapter Position of item in adapter;
|
||||
* @param itemCollectionPosition Position of item in collection that contains item;
|
||||
* @param item Item to check;
|
||||
* @param adapterPosition Position of item in adapter;
|
||||
* @param collectionPosition Position of item in collection that contains item;
|
||||
* @return True if item is processable by this delegate.
|
||||
*/
|
||||
public abstract boolean isForViewType(@NonNull final Object item, final int positionInAdapter, final int itemCollectionPosition);
|
||||
public boolean isForViewType(@NonNull final Object item, final int adapterPosition, final int collectionPosition) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(@NonNull final List<Object> items, final int adapterPosition, final int collectionPosition) {
|
||||
//noinspection unchecked
|
||||
return getItemId((TItem) items.get(collectionPosition), adapterPosition, collectionPosition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns unique ID of item to support stable ID's logic of RecyclerView's adapter.
|
||||
*
|
||||
* @param item Item to check;
|
||||
* @param positionInAdapter Position of item in adapter;
|
||||
* @param positionInCollection Position of item in collection that contains item;
|
||||
* @param item Item in adapter;
|
||||
* @param adapterPosition Position of item in adapter;
|
||||
* @param collectionPosition Position of item in collection that contains item;
|
||||
* @return Unique item ID.
|
||||
*/
|
||||
public long getItemId(@NonNull final TItem item, final int positionInAdapter, final int positionInCollection) {
|
||||
public long getItemId(@NonNull final TItem item, final int adapterPosition, final int collectionPosition) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates ViewHolder to bind item to it later.
|
||||
*
|
||||
* @param parent Container of ViewHolder's view.
|
||||
* @return New ViewHolder.
|
||||
*/
|
||||
@NonNull
|
||||
public abstract TViewHolder onCreateViewHolder(@NonNull final ViewGroup parent);
|
||||
|
||||
/**
|
||||
* Binds item to created by this object ViewHolder.
|
||||
*
|
||||
* @param holder ViewHolder to bind item to;
|
||||
* @param item Item to check;
|
||||
* @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);
|
||||
@Override
|
||||
public void onBindViewHolder(
|
||||
@NonNull final RecyclerView.ViewHolder holder,
|
||||
@NonNull final List<Object> items,
|
||||
final int adapterPosition,
|
||||
final int collectionPosition,
|
||||
@NonNull final List<Object> payloads
|
||||
) {
|
||||
//noinspection unchecked
|
||||
onBindViewHolder((TViewHolder) holder, (TItem) items.get(collectionPosition), adapterPosition, collectionPosition, payloads);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds item with payloads to created by this object ViewHolder.
|
||||
*
|
||||
* @param holder ViewHolder to bind item to;
|
||||
* @param item Item to check;
|
||||
* @param payloads Payloads;
|
||||
* @param positionInAdapter Position of item in adapter;
|
||||
* @param positionInCollection Position of item in collection that contains item;
|
||||
* @param holder ViewHolder to bind item to;
|
||||
* @param item Item in adapter;
|
||||
* @param adapterPosition Position of item in adapter;
|
||||
* @param collectionPosition Position of item in collection that contains item;
|
||||
* @param payloads Payloads;
|
||||
*/
|
||||
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
|
||||
}
|
||||
public abstract void onBindViewHolder(
|
||||
@NonNull final TViewHolder holder,
|
||||
@NonNull final TItem item,
|
||||
final int adapterPosition,
|
||||
final int collectionPosition,
|
||||
@NonNull final List<Object> payloads
|
||||
);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,676 +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 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;
|
||||
import rx.Observable;
|
||||
import rx.functions.Action1;
|
||||
import rx.functions.Action2;
|
||||
import rx.functions.Action3;
|
||||
import rx.subjects.BehaviorSubject;
|
||||
|
||||
/**
|
||||
* 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.create(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();
|
||||
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)
|
||||
.onErrorResumeNext(Observable.empty())
|
||||
.doOnCompleted(() -> 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, item, positionInCollection) ->
|
||||
result.value = itemAdapterDelegate.getItemId(item, positionInAdapter, positionInCollection),
|
||||
positionAdapterDelegate -> result.value = positionAdapterDelegate.getItemId(positionInAdapter),
|
||||
(item, positionInCollection) -> result.value = super.getItemId(positionInAdapter));
|
||||
return result.value;
|
||||
}
|
||||
|
||||
private void tryDelegateAction(final int positionInAdapter,
|
||||
@NonNull final Action3<ItemAdapterDelegate, TItem, Integer> itemAdapterDelegateAction,
|
||||
@NonNull final Action1<PositionAdapterDelegate> positionAdapterDelegateAction,
|
||||
@NonNull final Action2<TItem, Integer> defaultAction) {
|
||||
final int viewType = getItemViewType(positionInAdapter);
|
||||
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 && viewType == delegate.getItemViewType()) {
|
||||
itemAdapterDelegateAction.call((ItemAdapterDelegate) delegate, item, positionInCollection);
|
||||
return;
|
||||
}
|
||||
} else if (delegate instanceof PositionAdapterDelegate) {
|
||||
if (viewType == delegate.getItemViewType()) {
|
||||
positionAdapterDelegateAction.call((PositionAdapterDelegate) delegate);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
Lc.assertion("Delegate of type " + delegate.getClass());
|
||||
}
|
||||
}
|
||||
defaultAction.call(item, positionInCollection);
|
||||
}
|
||||
|
||||
@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, item, positionInCollection) -> {
|
||||
bindItemViewHolder(itemAdapterDelegate, holder, item, null, positionInAdapter, positionInCollection);
|
||||
updateMoreAutoLoadingRequest(positionInCollection);
|
||||
},
|
||||
positionAdapterDelegate -> positionAdapterDelegate.onBindViewHolder(holder, positionInAdapter),
|
||||
(item, positionInCollection) -> {
|
||||
if (item != null) {
|
||||
bindItemViewHolder(null, holder, item, 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, item, positionInCollection) -> {
|
||||
bindItemViewHolder(itemAdapterDelegate, holder, item, payloads, positionInAdapter, positionInCollection);
|
||||
updateMoreAutoLoadingRequest(positionInCollection);
|
||||
},
|
||||
positionAdapterDelegate -> positionAdapterDelegate.onBindViewHolder(holder, positionInAdapter),
|
||||
(item, positionInCollection) -> {
|
||||
if (item != null) {
|
||||
bindItemViewHolder(null, holder, item, 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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package ru.touchin.roboswag.components.adapters
|
||||
|
||||
import android.support.v7.util.ListUpdateCallback
|
||||
import android.support.v7.widget.RecyclerView
|
||||
|
||||
class OffsetAdapterUpdateCallback(private val adapter: RecyclerView.Adapter<*>, private val offsetProvider: () -> Int) : ListUpdateCallback {
|
||||
|
||||
override fun onInserted(position: Int, count: Int) {
|
||||
adapter.notifyItemRangeInserted(position + offsetProvider(), count)
|
||||
}
|
||||
|
||||
override fun onRemoved(position: Int, count: Int) {
|
||||
adapter.notifyItemRangeRemoved(position + offsetProvider(), count)
|
||||
}
|
||||
|
||||
override fun onMoved(fromPosition: Int, toPosition: Int) {
|
||||
adapter.notifyItemMoved(fromPosition + offsetProvider(), toPosition + offsetProvider())
|
||||
}
|
||||
|
||||
override fun onChanged(position: Int, count: Int, payload: Any?) {
|
||||
adapter.notifyItemRangeChanged(position + offsetProvider(), count, payload)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,68 +1,67 @@
|
|||
package ru.touchin.roboswag.components.adapters;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.ViewGroup;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
|
||||
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.
|
||||
* Default {@link #getItemViewType} is generating on construction of object.
|
||||
*
|
||||
* @param <TViewHolder> Type of {@link BindableViewHolder} of delegate.
|
||||
* @param <TViewHolder> Type of {@link RecyclerView.ViewHolder} of delegate.
|
||||
*/
|
||||
public abstract class PositionAdapterDelegate<TViewHolder extends BindableViewHolder> extends AdapterDelegate<TViewHolder> {
|
||||
public abstract class PositionAdapterDelegate<TViewHolder extends RecyclerView.ViewHolder> extends AdapterDelegate<TViewHolder> {
|
||||
|
||||
public PositionAdapterDelegate(@NonNull final LifecycleBindable parentLifecycleBindable) {
|
||||
super(parentLifecycleBindable);
|
||||
@Override
|
||||
public boolean isForViewType(@NonNull final List<Object> items, final int adapterPosition, final int collectionPosition) {
|
||||
return isForViewType(adapterPosition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if object is processable by this delegate.
|
||||
*
|
||||
* @param positionInAdapter Position of item in adapter;
|
||||
* @param adapterPosition Position of item in adapter;
|
||||
* @return True if item is processable by this delegate.
|
||||
*/
|
||||
public abstract boolean isForViewType(final int positionInAdapter);
|
||||
public abstract boolean isForViewType(final int adapterPosition);
|
||||
|
||||
@Override
|
||||
public long getItemId(@NonNull final List<Object> objects, final int adapterPosition, final int itemsOffset) {
|
||||
return getItemId(adapterPosition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns unique ID of item to support stable ID's logic of RecyclerView's adapter.
|
||||
*
|
||||
* @param positionInAdapter Position of item in adapter;
|
||||
* @param adapterPosition Position of item in adapter;
|
||||
* @return Unique item ID.
|
||||
*/
|
||||
public long getItemId(final int positionInAdapter) {
|
||||
public long getItemId(final int adapterPosition) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates ViewHolder to bind position to it later.
|
||||
*
|
||||
* @param parent Container of ViewHolder's view.
|
||||
* @return New ViewHolder.
|
||||
*/
|
||||
@NonNull
|
||||
public abstract TViewHolder onCreateViewHolder(@NonNull final ViewGroup parent);
|
||||
|
||||
/**
|
||||
* Binds position to ViewHolder.
|
||||
*
|
||||
* @param holder ViewHolder to bind position to;
|
||||
* @param positionInAdapter Position of item in adapter.
|
||||
*/
|
||||
public abstract void onBindViewHolder(@NonNull final TViewHolder holder, final int positionInAdapter);
|
||||
@Override
|
||||
public void onBindViewHolder(
|
||||
@NonNull final RecyclerView.ViewHolder holder,
|
||||
@NonNull final List<Object> items,
|
||||
final int adapterPosition,
|
||||
final int collectionPosition,
|
||||
@NonNull final List<Object> payloads
|
||||
) {
|
||||
//noinspection unchecked
|
||||
onBindViewHolder((TViewHolder) holder, adapterPosition, payloads);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds position with payloads to ViewHolder.
|
||||
*
|
||||
* @param holder ViewHolder to bind position to;
|
||||
* @param payloads Payloads;
|
||||
* @param positionInAdapter Position of item in adapter.
|
||||
* @param holder ViewHolder to bind position to;
|
||||
* @param adapterPosition Position of item in adapter;
|
||||
* @param payloads Payloads.
|
||||
*/
|
||||
public void onBindViewHolder(@NonNull final TViewHolder holder, @NonNull final List<Object> payloads, final int positionInAdapter) {
|
||||
public void onBindViewHolder(@NonNull final TViewHolder holder, final int adapterPosition, @NonNull final List<Object> payloads) {
|
||||
//do nothing by default
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
package ru.touchin.roboswag.components.extensions
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
|
||||
fun Context.safeStartActivity(intent: Intent, flags: Int = 0): Boolean =
|
||||
if (packageManager.resolveActivity(intent, flags) != null) {
|
||||
startActivity(intent)
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package ru.touchin.roboswag.components.extensions
|
||||
|
||||
import kotlin.properties.Delegates
|
||||
import kotlin.properties.ObservableProperty
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
/**
|
||||
* Simple observable delegate only for notification of new value.
|
||||
*/
|
||||
inline fun <T> Delegates.observable(
|
||||
initialValue: T,
|
||||
crossinline onChange: (newValue: T) -> Unit
|
||||
): ReadWriteProperty<Any?, T> = object : ObservableProperty<T>(initialValue) {
|
||||
override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) = onChange(newValue)
|
||||
}
|
||||
|
||||
inline fun <T> Delegates.distinctUntilChanged(
|
||||
initialValue: T,
|
||||
crossinline onChange: (newValue: T) -> Unit
|
||||
): ReadWriteProperty<Any?, T> = object : ObservableProperty<T>(initialValue) {
|
||||
override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) =
|
||||
if (newValue != null && oldValue != newValue) onChange(newValue) else Unit
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package ru.touchin.roboswag.components.extensions
|
||||
|
||||
import android.os.Build
|
||||
import android.view.View
|
||||
|
||||
private const val RIPPLE_EFFECT_DELAY = 150L
|
||||
|
||||
/**
|
||||
* Sets click listener to view. On click it will call something after delay.
|
||||
*
|
||||
* @param delay Delay after which click listener will be called;
|
||||
* @param listener Click listener.
|
||||
*/
|
||||
fun View.setOnRippleClickListener(delay: Long = RIPPLE_EFFECT_DELAY, listener: (View) -> Unit) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
setOnClickListener { view -> postDelayed({ if (hasWindowFocus()) listener(view) }, delay) }
|
||||
} else {
|
||||
setOnClickListener(listener)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package ru.touchin.roboswag.components.extensions
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
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.StringRes
|
||||
import android.support.v4.content.ContextCompat
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import android.view.View
|
||||
|
||||
fun <T : View> RecyclerView.ViewHolder.findViewById(@IdRes resId: Int): T = itemView.findViewById(resId)
|
||||
|
||||
val RecyclerView.ViewHolder.context: Context
|
||||
get() = itemView.context
|
||||
|
||||
fun RecyclerView.ViewHolder.getText(@StringRes resId: Int): CharSequence = context.getText(resId)
|
||||
|
||||
fun RecyclerView.ViewHolder.getString(@StringRes resId: Int): String = context.getString(resId)
|
||||
|
||||
@SuppressWarnings("SpreadOperator") // it's OK for small arrays
|
||||
fun RecyclerView.ViewHolder.getString(@StringRes resId: Int, vararg args: Any): String = context.getString(resId, *args)
|
||||
|
||||
@ColorInt
|
||||
fun RecyclerView.ViewHolder.getColor(@ColorRes resId: Int): Int = ContextCompat.getColor(context, resId)
|
||||
|
||||
fun RecyclerView.ViewHolder.getColorStateList(@ColorRes resId: Int): ColorStateList? = ContextCompat.getColorStateList(context, resId)
|
||||
|
||||
fun RecyclerView.ViewHolder.getDrawable(@DrawableRes resId: Int): Drawable? = ContextCompat.getDrawable(context, resId)
|
||||
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,419 +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.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
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 android.view.MenuItem;
|
||||
|
||||
import ru.touchin.roboswag.core.log.Lc;
|
||||
import rx.functions.Func1;
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 07/03/2016.
|
||||
* Navigation which is controlling fragments on activity using {@link android.support.v4.app.FragmentManager}.
|
||||
* Basically there are 4 main actions to add fragments to activity.
|
||||
* 1) {@link #setInitial} means to set fragment on top and remove all previously added fragments from stack;
|
||||
* 2) {@link #push} means to simply add fragment on top of the stack;
|
||||
* 3) {@link #setAsTop} means to push fragment on top of the stack with specific {@link #TOP_FRAGMENT_TAG_MARK} tag.
|
||||
* It is useful to realize up/back navigation: if {@link #up()} method will be called then stack will go to nearest fragment with TOP tag.
|
||||
* If {@link #back()} method will be called then stack will go to previous fragment.
|
||||
* Usually such logic using to set as top fragments from sidebar and show hamburger when some of them appeared;
|
||||
* 4) {@link #pushForResult} means to push fragment with target fragment. It is also adding {@link #WITH_TARGET_FRAGMENT_TAG_MARK} tag.
|
||||
* Also if such up/back navigation logic is not OK then {@link #backTo(Func1)} method could be used with any condition to back to.
|
||||
* In that case in any stack-change method it is allowed to setup fragment transactions.
|
||||
*/
|
||||
public class FragmentNavigation {
|
||||
|
||||
protected static final String TOP_FRAGMENT_TAG_MARK = "TOP_FRAGMENT";
|
||||
protected static final String WITH_TARGET_FRAGMENT_TAG_MARK = "FRAGMENT_WITH_TARGET";
|
||||
|
||||
@NonNull
|
||||
private final Context context;
|
||||
@NonNull
|
||||
private final FragmentManager fragmentManager;
|
||||
@IdRes
|
||||
private final int containerViewId;
|
||||
|
||||
public FragmentNavigation(@NonNull final Context context, @NonNull final FragmentManager fragmentManager, @IdRes final int containerViewId) {
|
||||
this.context = context;
|
||||
this.fragmentManager = fragmentManager;
|
||||
this.containerViewId = containerViewId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link Context} that is using to instantiate fragments.
|
||||
*
|
||||
* @return {@link Context}.
|
||||
*/
|
||||
@NonNull
|
||||
public Context getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link FragmentManager} using for navigation.
|
||||
*
|
||||
* @return {@link FragmentManager}.
|
||||
*/
|
||||
@NonNull
|
||||
public FragmentManager getFragmentManager() {
|
||||
return fragmentManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if last fragment in stack is top (added by {@link #setAsTop} or {@link #setInitial}) like fragment from sidebar menu.
|
||||
*
|
||||
* @return True if last fragment on stack has TOP_FRAGMENT_TAG_MARK.
|
||||
*/
|
||||
public boolean isCurrentFragmentTop() {
|
||||
if (fragmentManager.getBackStackEntryCount() == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final String topFragmentTag = fragmentManager
|
||||
.getBackStackEntryAt(fragmentManager.getBackStackEntryCount() - 1)
|
||||
.getName();
|
||||
return topFragmentTag != null && topFragmentTag.contains(TOP_FRAGMENT_TAG_MARK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allowed to react on {@link android.app.Activity}'s menu item selection.
|
||||
*
|
||||
* @param item Selected menu item;
|
||||
* @return True if reaction fired.
|
||||
*/
|
||||
@SuppressLint("InlinedApi")
|
||||
//InlinedApi: it is ok as android.R.id.home contains in latest SDK
|
||||
public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
|
||||
return item.getItemId() == android.R.id.home && back();
|
||||
}
|
||||
|
||||
/**
|
||||
* Base method which is adding fragment to stack.
|
||||
*
|
||||
* @param fragmentClass Class of {@link Fragment} to instantiate;
|
||||
* @param targetFragment Target fragment to be set as {@link Fragment#getTargetFragment()} of instantiated {@link Fragment};
|
||||
* @param args Bundle to be set as {@link Fragment#getArguments()} of instantiated {@link Fragment};
|
||||
* @param backStackTag Tag of {@link Fragment} in back stack;
|
||||
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info.
|
||||
*/
|
||||
@SuppressLint("CommitTransaction")
|
||||
//CommitTransaction: it is ok as we could setup transaction before commit
|
||||
protected void addToStack(@NonNull final Class<? extends Fragment> fragmentClass,
|
||||
@Nullable final Fragment targetFragment,
|
||||
@Nullable final Bundle args,
|
||||
@Nullable final String backStackTag,
|
||||
@Nullable final Func1<FragmentTransaction, FragmentTransaction> transactionSetup) {
|
||||
if (fragmentManager.isDestroyed()) {
|
||||
Lc.assertion("FragmentManager is destroyed");
|
||||
return;
|
||||
}
|
||||
|
||||
final Fragment fragment = Fragment.instantiate(context, fragmentClass.getName(), args);
|
||||
if (targetFragment != null) {
|
||||
if (fragmentManager != targetFragment.getFragmentManager()) {
|
||||
Lc.assertion("FragmentManager of target is differ then of creating fragment. Target will be lost after restoring activity. "
|
||||
+ targetFragment.getFragmentManager() + " != " + fragmentManager);
|
||||
}
|
||||
fragment.setTargetFragment(targetFragment, 0);
|
||||
}
|
||||
|
||||
final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction()
|
||||
.replace(containerViewId, fragment, null)
|
||||
.addToBackStack(backStackTag);
|
||||
if (fragmentManager.getBackStackEntryCount() != 0) {
|
||||
fragmentTransaction.setTransition(getDefaultTransition());
|
||||
}
|
||||
if (transactionSetup != null) {
|
||||
transactionSetup.call(fragmentTransaction).commit();
|
||||
} else {
|
||||
fragmentTransaction.commit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns default transition animation.
|
||||
*
|
||||
* @return {@link FragmentTransaction#TRANSIT_FRAGMENT_OPEN}.
|
||||
*/
|
||||
protected int getDefaultTransition() {
|
||||
return FragmentTransaction.TRANSIT_FRAGMENT_OPEN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simply calls {@link FragmentManager#popBackStack()}.
|
||||
*
|
||||
* @return True if it have back to some entry in stack.
|
||||
*/
|
||||
public boolean back() {
|
||||
if (fragmentManager.getBackStackEntryCount() > 1) {
|
||||
fragmentManager.popBackStack();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Backs to fragment which back stack's entry satisfy to specific condition.
|
||||
*
|
||||
* @param condition Condition of back stack entry to be satisfied;
|
||||
* @return True if it have back to some entry in stack.
|
||||
*/
|
||||
public boolean backTo(@NonNull final Func1<FragmentManager.BackStackEntry, Boolean> condition) {
|
||||
final int stackSize = fragmentManager.getBackStackEntryCount();
|
||||
Integer id = null;
|
||||
for (int i = stackSize - 2; i >= 0; i--) {
|
||||
final FragmentManager.BackStackEntry backStackEntry = fragmentManager.getBackStackEntryAt(i);
|
||||
id = backStackEntry.getId();
|
||||
if (condition.call(backStackEntry)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (id != null) {
|
||||
fragmentManager.popBackStack(id, 0);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Backs to fragment with specific {@link #TOP_FRAGMENT_TAG_MARK} tag.
|
||||
* This tag is adding if fragment added to stack via {@link #setInitial} or {@link #setAsTop(Class)} methods.
|
||||
* It can be used to create simple up/back navigation.
|
||||
*
|
||||
* @return True if it have back to some entry in stack.
|
||||
*/
|
||||
@SuppressWarnings("PMD.ShortMethodName")
|
||||
//ShortMethodName: it is ok because method name is good!
|
||||
public boolean up() {
|
||||
return backTo(backStackEntry ->
|
||||
backStackEntry.getName() != null && backStackEntry.getName().endsWith(TOP_FRAGMENT_TAG_MARK));
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes {@link Fragment} on top of stack.
|
||||
*
|
||||
* @param fragmentClass Class of {@link Fragment} to instantiate.
|
||||
*/
|
||||
public void push(@NonNull final Class<? extends Fragment> fragmentClass) {
|
||||
addToStack(fragmentClass, null, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes {@link Fragment} on top of stack with specific arguments.
|
||||
*
|
||||
* @param fragmentClass Class of {@link Fragment} to instantiate;
|
||||
* @param args Bundle to be set as {@link Fragment#getArguments()} of instantiated {@link Fragment}.
|
||||
*/
|
||||
public void push(@NonNull final Class<? extends Fragment> fragmentClass,
|
||||
@NonNull final Bundle args) {
|
||||
addToStack(fragmentClass, null, args, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes {@link Fragment} on top of stack with specific transaction setup.
|
||||
*
|
||||
* @param fragmentClass Class of {@link Fragment} to instantiate;
|
||||
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info.
|
||||
*/
|
||||
public void push(@NonNull final Class<? extends Fragment> fragmentClass,
|
||||
@NonNull final Func1<FragmentTransaction, FragmentTransaction> transactionSetup) {
|
||||
addToStack(fragmentClass, null, null, null, transactionSetup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes {@link Fragment} on top of stack with specific arguments and transaction setup.
|
||||
*
|
||||
* @param fragmentClass Class of {@link Fragment} to instantiate;
|
||||
* @param args Bundle to be set as {@link Fragment#getArguments()} of instantiated {@link Fragment};
|
||||
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info.
|
||||
*/
|
||||
public void push(@NonNull final Class<? extends Fragment> fragmentClass,
|
||||
@Nullable final Bundle args,
|
||||
@Nullable final Func1<FragmentTransaction, FragmentTransaction> transactionSetup) {
|
||||
addToStack(fragmentClass, null, args, null, transactionSetup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes {@link Fragment} on top of stack with specific target fragment.
|
||||
*
|
||||
* @param fragmentClass Class of {@link Fragment} to instantiate;
|
||||
* @param targetFragment Target fragment to be set as {@link Fragment#getTargetFragment()} of instantiated {@link Fragment}.
|
||||
*/
|
||||
public void pushForResult(@NonNull final Class<? extends Fragment> fragmentClass,
|
||||
@NonNull final Fragment targetFragment) {
|
||||
addToStack(fragmentClass, targetFragment, null, fragmentClass.getName() + ';' + WITH_TARGET_FRAGMENT_TAG_MARK, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes {@link Fragment} on top of stack with specific target fragment and arguments.
|
||||
*
|
||||
* @param fragmentClass Class of {@link Fragment} to instantiate;
|
||||
* @param targetFragment Target fragment to be set as {@link Fragment#getTargetFragment()} of instantiated {@link Fragment};
|
||||
* @param args Bundle to be set as {@link Fragment#getArguments()} of instantiated {@link Fragment}.
|
||||
*/
|
||||
public void pushForResult(@NonNull final Class<? extends Fragment> fragmentClass,
|
||||
@NonNull final Fragment targetFragment,
|
||||
@NonNull final Bundle args) {
|
||||
addToStack(fragmentClass, targetFragment, args, fragmentClass.getName() + ';' + WITH_TARGET_FRAGMENT_TAG_MARK, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes {@link Fragment} on top of stack with specific target fragment and transaction setup.
|
||||
*
|
||||
* @param fragmentClass Class of {@link Fragment} to instantiate;
|
||||
* @param targetFragment Target fragment to be set as {@link Fragment#getTargetFragment()} of instantiated {@link Fragment};
|
||||
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info.
|
||||
*/
|
||||
public void pushForResult(@NonNull final Class<? extends Fragment> fragmentClass,
|
||||
@NonNull final Fragment targetFragment,
|
||||
@NonNull final Func1<FragmentTransaction, FragmentTransaction> transactionSetup) {
|
||||
addToStack(fragmentClass, targetFragment, null, fragmentClass.getName() + ';' + WITH_TARGET_FRAGMENT_TAG_MARK, transactionSetup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes {@link Fragment} on top of stack with specific target fragment, arguments and transaction setup.
|
||||
*
|
||||
* @param fragmentClass Class of {@link Fragment} to instantiate;
|
||||
* @param targetFragment Target fragment to be set as {@link Fragment#getTargetFragment()} of instantiated {@link Fragment};
|
||||
* @param args Bundle to be set as {@link Fragment#getArguments()} of instantiated {@link Fragment};
|
||||
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info.
|
||||
*/
|
||||
public void pushForResult(@NonNull final Class<? extends Fragment> fragmentClass,
|
||||
@NonNull final Fragment targetFragment,
|
||||
@Nullable final Bundle args,
|
||||
@Nullable final Func1<FragmentTransaction, FragmentTransaction> transactionSetup) {
|
||||
addToStack(fragmentClass, targetFragment, args, fragmentClass.getName() + ';' + WITH_TARGET_FRAGMENT_TAG_MARK, transactionSetup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes {@link Fragment} on top of stack with {@link #TOP_FRAGMENT_TAG_MARK} tag used for simple up/back navigation.
|
||||
*
|
||||
* @param fragmentClass Class of {@link Fragment} to instantiate.
|
||||
*/
|
||||
public void setAsTop(@NonNull final Class<? extends Fragment> fragmentClass) {
|
||||
addToStack(fragmentClass, null, null, fragmentClass.getName() + ';' + TOP_FRAGMENT_TAG_MARK, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes {@link Fragment} on top of stack with specific arguments and with {@link #TOP_FRAGMENT_TAG_MARK} tag used for simple up/back navigation.
|
||||
*
|
||||
* @param fragmentClass Class of {@link Fragment} to instantiate;
|
||||
* @param args Bundle to be set as {@link Fragment#getArguments()} of instantiated {@link Fragment}.
|
||||
*/
|
||||
public void setAsTop(@NonNull final Class<? extends Fragment> fragmentClass,
|
||||
@NonNull final Bundle args) {
|
||||
addToStack(fragmentClass, null, args, fragmentClass.getName() + ';' + TOP_FRAGMENT_TAG_MARK, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes {@link Fragment} 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 Fragment} to instantiate;
|
||||
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info.
|
||||
*/
|
||||
public void setAsTop(@NonNull final Class<? extends Fragment> fragmentClass,
|
||||
@NonNull final Func1<FragmentTransaction, FragmentTransaction> transactionSetup) {
|
||||
addToStack(fragmentClass, null, null, fragmentClass.getName() + ';' + TOP_FRAGMENT_TAG_MARK, transactionSetup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes {@link Fragment} on top of stack with specific transaction setup, arguments
|
||||
* and with {@link #TOP_FRAGMENT_TAG_MARK} tag used for simple up/back navigation.
|
||||
*
|
||||
* @param fragmentClass Class of {@link Fragment} to instantiate;
|
||||
* @param args Bundle to be set as {@link Fragment#getArguments()} of instantiated {@link Fragment};
|
||||
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info.
|
||||
*/
|
||||
public void setAsTop(@NonNull final Class<? extends Fragment> fragmentClass,
|
||||
@Nullable final Bundle args,
|
||||
@Nullable final Func1<FragmentTransaction, FragmentTransaction> transactionSetup) {
|
||||
addToStack(fragmentClass, null, args, fragmentClass.getName() + ';' + TOP_FRAGMENT_TAG_MARK, transactionSetup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pops all {@link Fragment}s and places new initial {@link Fragment} on top of stack.
|
||||
*
|
||||
* @param fragmentClass Class of {@link Fragment} to instantiate.
|
||||
*/
|
||||
public void setInitial(@NonNull final Class<? extends Fragment> fragmentClass) {
|
||||
setInitial(fragmentClass, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pops all {@link Fragment}s and places new initial {@link Fragment} on top of stack with specific arguments.
|
||||
*
|
||||
* @param fragmentClass Class of {@link Fragment} to instantiate;
|
||||
* @param args Bundle to be set as {@link Fragment#getArguments()} of instantiated {@link Fragment}.
|
||||
*/
|
||||
public void setInitial(@NonNull final Class<? extends Fragment> fragmentClass,
|
||||
@NonNull final Bundle args) {
|
||||
setInitial(fragmentClass, args, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pops all {@link Fragment}s and places new initial {@link Fragment} on top of stack with specific transaction setup.
|
||||
*
|
||||
* @param fragmentClass Class of {@link Fragment} to instantiate;
|
||||
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info.
|
||||
*/
|
||||
public void setInitial(@NonNull final Class<? extends Fragment> fragmentClass,
|
||||
@NonNull final Func1<FragmentTransaction, FragmentTransaction> transactionSetup) {
|
||||
setInitial(fragmentClass, null, transactionSetup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pops all {@link Fragment}s and places new initial {@link Fragment} on top of stack with specific transaction setup and arguments.
|
||||
*
|
||||
* @param fragmentClass Class of {@link Fragment} to instantiate;
|
||||
* @param args Bundle to be set as {@link Fragment#getArguments()} of instantiated {@link Fragment};
|
||||
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info.
|
||||
*/
|
||||
public void setInitial(@NonNull final Class<? extends Fragment> fragmentClass,
|
||||
@Nullable final Bundle args,
|
||||
@Nullable final Func1<FragmentTransaction, FragmentTransaction> transactionSetup) {
|
||||
beforeSetInitialActions();
|
||||
setAsTop(fragmentClass, args, transactionSetup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method calls every time before initial {@link Fragment} will be placed.
|
||||
*/
|
||||
protected void beforeSetInitialActions() {
|
||||
if (fragmentManager.isDestroyed()) {
|
||||
Lc.assertion("FragmentManager is destroyed");
|
||||
return;
|
||||
}
|
||||
|
||||
if (fragmentManager.getBackStackEntryCount() > 0) {
|
||||
fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,257 @@
|
|||
/*
|
||||
* 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.Bundle
|
||||
import android.support.annotation.IdRes
|
||||
import android.support.v4.app.Fragment
|
||||
import android.support.v4.app.FragmentManager
|
||||
import android.support.v4.app.FragmentTransaction
|
||||
import android.view.MenuItem
|
||||
|
||||
import ru.touchin.roboswag.core.log.Lc
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 07/03/2016.
|
||||
* Navigation which is controlling fragments on activity using [android.support.v4.app.FragmentManager].
|
||||
* Basically there are 4 main actions to add fragments to activity.
|
||||
* 1) [.setInitial] means to set fragment on top and remove all previously added fragments from stack;
|
||||
* 2) [.push] means to simply add fragment on top of the stack;
|
||||
* 3) [.setAsTop] means to push fragment on top of the stack with specific [.TOP_FRAGMENT_TAG_MARK] tag.
|
||||
* It is useful to realize up/back navigation: if [.up] method will be called then stack will go to nearest fragment with TOP tag.
|
||||
* If [.back] method will be called then stack will go to previous fragment.
|
||||
* Usually such logic using to set as top fragments from sidebar and show hamburger when some of them appeared;
|
||||
* 4) [.pushForResult] means to push fragment with target fragment. It is also adding [.WITH_TARGET_FRAGMENT_TAG_MARK] tag.
|
||||
* Also if such up/back navigation logic is not OK then [.backTo] method could be used with any condition to back to.
|
||||
* In that case in any stack-change method it is allowed to setup fragment transactions.
|
||||
*/
|
||||
open class FragmentNavigation(
|
||||
private val context: Context,
|
||||
private val fragmentManager: FragmentManager,
|
||||
@IdRes private val containerViewId: Int,
|
||||
private val transition: Int = FragmentTransaction.TRANSIT_FRAGMENT_OPEN
|
||||
) {
|
||||
|
||||
companion object {
|
||||
const val TOP_FRAGMENT_TAG_MARK = "TOP_FRAGMENT"
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if last fragment in stack is top (added by [.setAsTop] or [.setInitial]) like fragment from sidebar menu.
|
||||
*
|
||||
* @return True if last fragment on stack has TOP_FRAGMENT_TAG_MARK.
|
||||
*/
|
||||
fun isCurrentFragmentTop(): Boolean {
|
||||
if (fragmentManager.backStackEntryCount == 0) {
|
||||
return true
|
||||
}
|
||||
val topFragmentTag = fragmentManager
|
||||
.getBackStackEntryAt(fragmentManager.backStackEntryCount - 1)
|
||||
.name
|
||||
return topFragmentTag != null && topFragmentTag.contains(TOP_FRAGMENT_TAG_MARK)
|
||||
}
|
||||
|
||||
/**
|
||||
* Allowed to react on [android.app.Activity]'s menu item selection.
|
||||
*
|
||||
* @param item Selected menu item;
|
||||
* @return True if reaction fired.
|
||||
*/
|
||||
fun onOptionsItemSelected(item: MenuItem): Boolean = item.itemId == android.R.id.home && back()
|
||||
|
||||
/**
|
||||
* Base method which is adding fragment to stack.
|
||||
*
|
||||
* @param fragmentClass Class of [Fragment] to instantiate;
|
||||
* @param targetFragment Target fragment to be set as [Fragment.getTargetFragment] of instantiated [Fragment];
|
||||
* @param addToStack Flag to add this transaction to the back stack;
|
||||
* @param args Bundle to be set as [Fragment.getArguments] of instantiated [Fragment];
|
||||
* @param backStackName Name of [Fragment] in back stack;
|
||||
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info.
|
||||
*/
|
||||
fun addToStack(
|
||||
fragmentClass: Class<out Fragment>,
|
||||
targetFragment: Fragment?,
|
||||
targetRequestCode: Int,
|
||||
addToStack: Boolean,
|
||||
args: Bundle?,
|
||||
backStackName: String?,
|
||||
transactionSetup: ((FragmentTransaction) -> Unit)?
|
||||
) {
|
||||
if (fragmentManager.isDestroyed) {
|
||||
Lc.assertion("FragmentManager is destroyed")
|
||||
return
|
||||
}
|
||||
|
||||
val fragment = Fragment.instantiate(context, fragmentClass.name, args)
|
||||
fragment.setTargetFragment(targetFragment, targetRequestCode)
|
||||
|
||||
val fragmentTransaction = fragmentManager.beginTransaction()
|
||||
transactionSetup?.invoke(fragmentTransaction)
|
||||
fragmentTransaction.replace(containerViewId, fragment, null)
|
||||
if (addToStack) {
|
||||
fragmentTransaction
|
||||
.addToBackStack(backStackName)
|
||||
.setTransition(transition)
|
||||
}
|
||||
fragmentTransaction
|
||||
.setPrimaryNavigationFragment(fragment)
|
||||
.commit()
|
||||
}
|
||||
|
||||
/**
|
||||
* Simply calls [FragmentManager.popBackStack].
|
||||
*
|
||||
* @return True if it have back to some entry in stack.
|
||||
*/
|
||||
fun back(): Boolean {
|
||||
if (fragmentManager.backStackEntryCount >= 1) {
|
||||
fragmentManager.popBackStack()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Backs to fragment which back stack's entry satisfy to specific condition.
|
||||
*
|
||||
* @param condition Condition of back stack entry to be satisfied;
|
||||
* @return True if it have back to some entry in stack.
|
||||
*/
|
||||
fun backTo(condition: (FragmentManager.BackStackEntry) -> Boolean): Boolean {
|
||||
val stackSize = fragmentManager.backStackEntryCount
|
||||
var id: Int? = null
|
||||
for (i in stackSize - 2 downTo 0) {
|
||||
val backStackEntry = fragmentManager.getBackStackEntryAt(i)
|
||||
if (condition(backStackEntry)) {
|
||||
id = backStackEntry.id
|
||||
break
|
||||
}
|
||||
}
|
||||
if (id != null) {
|
||||
fragmentManager.popBackStack(id, 0)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Backs to fragment with specific [.TOP_FRAGMENT_TAG_MARK] tag.
|
||||
* This tag is adding if fragment added to stack via [.setInitial] or [.setAsTop] methods.
|
||||
* It can be used to create simple up/back navigation.
|
||||
*
|
||||
* @return True if it have back to some entry in stack.
|
||||
*/
|
||||
fun up(name: String? = null, inclusive: Boolean = false) {
|
||||
fragmentManager.popBackStack(name, if (inclusive) FragmentManager.POP_BACK_STACK_INCLUSIVE else 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes [Fragment] on top of stack with specific arguments and transaction setup.
|
||||
*
|
||||
* @param fragmentClass Class of [Fragment] to instantiate;
|
||||
* @param args Bundle to be set as [Fragment.getArguments] of instantiated [Fragment];
|
||||
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info.
|
||||
*/
|
||||
fun push(
|
||||
fragmentClass: Class<out Fragment>,
|
||||
args: Bundle? = null,
|
||||
addToStack: Boolean = true,
|
||||
transactionSetup: ((FragmentTransaction) -> Unit)? = null
|
||||
) {
|
||||
addToStack(fragmentClass, null, 0, addToStack, args, null, transactionSetup)
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes [Fragment] on top of stack with specific target fragment, arguments and transaction setup.
|
||||
*
|
||||
* @param fragmentClass Class of [Fragment] to instantiate;
|
||||
* @param targetFragment Target fragment to be set as [Fragment.getTargetFragment] of instantiated [Fragment];
|
||||
* @param args Bundle to be set as [Fragment.getArguments] of instantiated [Fragment];
|
||||
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info.
|
||||
*/
|
||||
fun pushForResult(
|
||||
fragmentClass: Class<out Fragment>,
|
||||
targetFragment: Fragment,
|
||||
targetRequestCode: Int,
|
||||
args: Bundle? = null,
|
||||
transactionSetup: ((FragmentTransaction) -> Unit)? = null
|
||||
) {
|
||||
addToStack(
|
||||
fragmentClass,
|
||||
targetFragment,
|
||||
targetRequestCode,
|
||||
true,
|
||||
args,
|
||||
null,
|
||||
transactionSetup
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes [Fragment] on top of stack with specific transaction setup, arguments
|
||||
* and with [.TOP_FRAGMENT_TAG_MARK] tag used for simple up/back navigation.
|
||||
*
|
||||
* @param fragmentClass Class of [Fragment] to instantiate;
|
||||
* @param args Bundle to be set as [Fragment.getArguments] of instantiated [Fragment];
|
||||
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info.
|
||||
*/
|
||||
fun setAsTop(
|
||||
fragmentClass: Class<out Fragment>,
|
||||
args: Bundle? = null,
|
||||
addToStack: Boolean = true,
|
||||
transactionSetup: ((FragmentTransaction) -> Unit)? = null
|
||||
) {
|
||||
addToStack(fragmentClass, null, 0, addToStack, args, TOP_FRAGMENT_TAG_MARK, transactionSetup)
|
||||
}
|
||||
|
||||
/**
|
||||
* Pops all [Fragment]s and places new initial [Fragment] on top of stack with specific transaction setup and arguments.
|
||||
*
|
||||
* @param fragmentClass Class of [Fragment] to instantiate;
|
||||
* @param args Bundle to be set as [Fragment.getArguments] of instantiated [Fragment];
|
||||
* @param transactionSetup Function to setup transaction before commit. It is useful to specify transition animations or additional info.
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun setInitial(
|
||||
fragmentClass: Class<out Fragment>,
|
||||
args: Bundle? = null,
|
||||
transactionSetup: ((FragmentTransaction) -> Unit)? = null
|
||||
) {
|
||||
beforeSetInitialActions()
|
||||
setAsTop(fragmentClass, args, false, transactionSetup)
|
||||
}
|
||||
|
||||
/**
|
||||
* Method calls every time before initial [Fragment] will be placed.
|
||||
*/
|
||||
protected fun beforeSetInitialActions() {
|
||||
if (fragmentManager.isDestroyed) {
|
||||
Lc.assertion("FragmentManager is destroyed")
|
||||
return
|
||||
}
|
||||
|
||||
if (fragmentManager.backStackEntryCount > 0) {
|
||||
fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -29,6 +29,7 @@ import android.view.MenuItem;
|
|||
import android.view.View;
|
||||
|
||||
import ru.touchin.roboswag.components.navigation.activities.BaseActivity;
|
||||
import ru.touchin.roboswag.components.utils.UiUtils;
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 11/03/16.
|
||||
|
|
@ -206,7 +207,7 @@ public class SimpleActionBarDrawerToggle extends ActionBarDrawerToggle
|
|||
@Override
|
||||
public void onDrawerClosed(@NonNull final View view) {
|
||||
if (isInvalidateOptionsMenuSupported) {
|
||||
activity.supportInvalidateOptionsMenu();
|
||||
activity.invalidateOptionsMenu();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -221,9 +222,9 @@ public class SimpleActionBarDrawerToggle extends ActionBarDrawerToggle
|
|||
|
||||
@Override
|
||||
public void onDrawerOpened(@NonNull final View drawerView) {
|
||||
activity.hideSoftInput();
|
||||
UiUtils.OfViews.hideSoftInput(activity);
|
||||
if (isInvalidateOptionsMenuSupported) {
|
||||
activity.supportInvalidateOptionsMenu();
|
||||
activity.invalidateOptionsMenu();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -245,4 +246,4 @@ public class SimpleActionBarDrawerToggle extends ActionBarDrawerToggle
|
|||
super.onDrawerSlide(drawerView, this.slideOffset);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,506 +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.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.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
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;
|
||||
import rx.Completable;
|
||||
import rx.Observable;
|
||||
import rx.Single;
|
||||
import rx.Subscription;
|
||||
import rx.functions.Action0;
|
||||
import rx.functions.Action1;
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 21/10/2015.
|
||||
* Class to control view of specific fragment, activity and application by logic bridge.
|
||||
*
|
||||
* @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 {
|
||||
|
||||
@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
|
||||
public ViewController(@NonNull final CreationContext creationContext, @Nullable final Bundle savedInstanceState) {
|
||||
this.activity = (TActivity) creationContext.activity;
|
||||
this.fragment = (TFragment) creationContext.fragment;
|
||||
this.container = creationContext.container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns activity where {@link ViewController} could be.
|
||||
*
|
||||
* @return Returns activity.
|
||||
*/
|
||||
@NonNull
|
||||
public final TActivity getActivity() {
|
||||
return activity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns fragment where {@link ViewController} could be.
|
||||
*
|
||||
* @return Returns fragment.
|
||||
*/
|
||||
@NonNull
|
||||
public final TFragment getFragment() {
|
||||
return fragment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns view instantiated in {@link #getFragment()} fragment attached to {@link #getActivity()} activity.
|
||||
* Use it to inflate your views into at construction of this {@link ViewController}.
|
||||
*
|
||||
* @return Returns view.
|
||||
*/
|
||||
@NonNull
|
||||
public final ViewGroup getContainer() {
|
||||
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.
|
||||
*
|
||||
* @param layoutResId Resource ID to be inflated.
|
||||
*/
|
||||
public void setContentView(@LayoutRes final int layoutResId) {
|
||||
if (getContainer().getChildCount() > 0) {
|
||||
getContainer().removeAllViews();
|
||||
}
|
||||
UiUtils.inflateAndAdd(layoutResId, getContainer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the view controller content to an explicit view.
|
||||
* This view is placed directly into the container's ({@link #getContainer()}) view hierarchy.
|
||||
*
|
||||
* @param view The desired content to display.
|
||||
*/
|
||||
public void setContentView(@NonNull final View view) {
|
||||
setContentView(view, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the view controller content to an explicit view with specific layout parameters.
|
||||
* This view is placed directly into the container's ({@link #getContainer()}) view hierarchy.
|
||||
*
|
||||
* @param view The desired content to display;
|
||||
* @param layoutParams Layout parameters for the view.
|
||||
*/
|
||||
public void setContentView(@NonNull final View view, @NonNull final ViewGroup.LayoutParams layoutParams) {
|
||||
if (getContainer().getChildCount() > 0) {
|
||||
getContainer().removeAllViews();
|
||||
}
|
||||
getContainer().addView(view, layoutParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls when activity configuring ActionBar, Toolbar, Sidebar etc.
|
||||
* If it will be called or not depends on {@link Fragment#hasOptionsMenu()} and {@link Fragment#isMenuVisible()}.
|
||||
*
|
||||
* @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) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls right after construction of {@link ViewController}.
|
||||
* Happens at {@link ViewControllerFragment#onActivityCreated(View, ViewControllerActivity, Bundle)}.
|
||||
*/
|
||||
@CallSuper
|
||||
public void onCreate() {
|
||||
UiUtils.UI_LIFECYCLE_LC_GROUP.i(Lc.getCodePoint(this));
|
||||
baseLifecycleBindable.onCreate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls when {@link ViewController} have started.
|
||||
* Happens at {@link ViewControllerFragment#onStart(View, ViewControllerActivity)}.
|
||||
*/
|
||||
@CallSuper
|
||||
public void onStart() {
|
||||
UiUtils.UI_LIFECYCLE_LC_GROUP.i(Lc.getCodePoint(this));
|
||||
baseLifecycleBindable.onStart();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when fragment is moved in started state and it's {@link #getFragment().isMenuVisible()} sets to true.
|
||||
* Usually it is indicating that user can't see fragment on screen and useful to track analytics events.
|
||||
*/
|
||||
public void onAppear() {
|
||||
UiUtils.UI_LIFECYCLE_LC_GROUP.i(Lc.getCodePoint(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls when {@link ViewController} have resumed.
|
||||
* Happens at {@link ViewControllerFragment#onResume(View, ViewControllerActivity)}.
|
||||
*/
|
||||
@CallSuper
|
||||
public void onResume() {
|
||||
UiUtils.UI_LIFECYCLE_LC_GROUP.i(Lc.getCodePoint(this));
|
||||
baseLifecycleBindable.onResume();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls when {@link ViewController} have goes near out of memory state.
|
||||
* Happens at {@link ViewControllerFragment#onLowMemory()}.
|
||||
*/
|
||||
@CallSuper
|
||||
public void onLowMemory() {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls when {@link ViewController} have paused.
|
||||
* Happens at {@link ViewControllerFragment#onPause(View, ViewControllerActivity)}.
|
||||
*/
|
||||
@CallSuper
|
||||
public void onPause() {
|
||||
UiUtils.UI_LIFECYCLE_LC_GROUP.i(Lc.getCodePoint(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls when {@link ViewController} should save it's state.
|
||||
* Happens at {@link ViewControllerFragment#onSaveInstanceState(Bundle)}.
|
||||
* Try not to use such method for saving state but use {@link ViewControllerFragment#getState()} from {@link #getFragment()}.
|
||||
*/
|
||||
@CallSuper
|
||||
public void onSaveInstanceState(@NonNull final Bundle savedInstanceState) {
|
||||
baseLifecycleBindable.onSaveInstanceState();
|
||||
UiUtils.UI_LIFECYCLE_LC_GROUP.i(Lc.getCodePoint(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when fragment is moved in stopped state or it's {@link #getFragment().isMenuVisible()} sets to false.
|
||||
* Usually it is indicating that user can't see fragment on screen and useful to track analytics events.
|
||||
*/
|
||||
public void onDisappear() {
|
||||
UiUtils.UI_LIFECYCLE_LC_GROUP.i(Lc.getCodePoint(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls when {@link ViewController} have stopped.
|
||||
* Happens at {@link ViewControllerFragment#onStop(View, ViewControllerActivity)}.
|
||||
*/
|
||||
@CallSuper
|
||||
public void onStop() {
|
||||
UiUtils.UI_LIFECYCLE_LC_GROUP.i(Lc.getCodePoint(this));
|
||||
baseLifecycleBindable.onStop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls when {@link ViewController} have destroyed.
|
||||
* Happens usually at {@link ViewControllerFragment#onDestroyView(View)}. In some cases at {@link ViewControllerFragment#onDestroy()}.
|
||||
*/
|
||||
@CallSuper
|
||||
public void onDestroy() {
|
||||
UiUtils.UI_LIFECYCLE_LC_GROUP.i(Lc.getCodePoint(this));
|
||||
baseLifecycleBindable.onDestroy();
|
||||
destroyed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to {@link ViewControllerFragment#onOptionsItemSelected(MenuItem)}.
|
||||
*
|
||||
* @param item Selected menu item;
|
||||
* @return True if selection processed.
|
||||
*/
|
||||
public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("CPD-START")
|
||||
//CPD: it is same as in other implementation based on BaseLifecycleBindable
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Observable<T> observable) {
|
||||
return baseLifecycleBindable.untilStop(observable);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Observable<T> observable, @NonNull final Action1<T> onNextAction) {
|
||||
return baseLifecycleBindable.untilStop(observable, onNextAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Observable<T> observable,
|
||||
@NonNull final Action1<T> onNextAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction) {
|
||||
return baseLifecycleBindable.untilStop(observable, onNextAction, onErrorAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Observable<T> observable,
|
||||
@NonNull final Action1<T> onNextAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction,
|
||||
@NonNull final Action0 onCompletedAction) {
|
||||
return baseLifecycleBindable.untilStop(observable, onNextAction, onErrorAction, onCompletedAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Single<T> single) {
|
||||
return baseLifecycleBindable.untilStop(single);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Single<T> single, @NonNull final Action1<T> onSuccessAction) {
|
||||
return baseLifecycleBindable.untilStop(single, onSuccessAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Single<T> single,
|
||||
@NonNull final Action1<T> onSuccessAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction) {
|
||||
return baseLifecycleBindable.untilStop(single, onSuccessAction, onErrorAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Subscription untilStop(@NonNull final Completable completable) {
|
||||
return baseLifecycleBindable.untilStop(completable);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Subscription untilStop(@NonNull final Completable completable, @NonNull final Action0 onCompletedAction) {
|
||||
return baseLifecycleBindable.untilStop(completable, onCompletedAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Subscription untilStop(@NonNull final Completable completable,
|
||||
@NonNull final Action0 onCompletedAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction) {
|
||||
return baseLifecycleBindable.untilStop(completable, onCompletedAction, onErrorAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Observable<T> observable) {
|
||||
return baseLifecycleBindable.untilDestroy(observable);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Observable<T> observable, @NonNull final Action1<T> onNextAction) {
|
||||
return baseLifecycleBindable.untilDestroy(observable, onNextAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Observable<T> observable,
|
||||
@NonNull final Action1<T> onNextAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction) {
|
||||
return baseLifecycleBindable.untilDestroy(observable, onNextAction, onErrorAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Observable<T> observable,
|
||||
@NonNull final Action1<T> onNextAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction,
|
||||
@NonNull final Action0 onCompletedAction) {
|
||||
return baseLifecycleBindable.untilDestroy(observable, onNextAction, onErrorAction, onCompletedAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Single<T> single) {
|
||||
return baseLifecycleBindable.untilDestroy(single);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Single<T> single, @NonNull final Action1<T> onSuccessAction) {
|
||||
return baseLifecycleBindable.untilDestroy(single, onSuccessAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Single<T> single,
|
||||
@NonNull final Action1<T> onSuccessAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction) {
|
||||
return baseLifecycleBindable.untilDestroy(single, onSuccessAction, onErrorAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Subscription untilDestroy(@NonNull final Completable completable) {
|
||||
return baseLifecycleBindable.untilDestroy(completable);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Subscription untilDestroy(@NonNull final Completable completable, @NonNull final Action0 onCompletedAction) {
|
||||
return baseLifecycleBindable.untilDestroy(completable, onCompletedAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Subscription untilDestroy(@NonNull final Completable completable,
|
||||
@NonNull final Action0 onCompletedAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction) {
|
||||
return baseLifecycleBindable.untilDestroy(completable, onCompletedAction, onErrorAction);
|
||||
}
|
||||
|
||||
@SuppressWarnings("CPD-END")
|
||||
//CPD: it is same as in other implementation based on BaseLifecycleBindable
|
||||
/**
|
||||
* Helper class to simplify constructor override.
|
||||
*/
|
||||
public static class CreationContext {
|
||||
|
||||
@NonNull
|
||||
private final ViewControllerActivity 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) {
|
||||
this.activity = activity;
|
||||
this.fragment = fragment;
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,482 +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 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;
|
||||
import rx.functions.Func1;
|
||||
|
||||
/**
|
||||
* 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, 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 Func1<FragmentTransaction, FragmentTransaction> transactionSetup) {
|
||||
addToStack(fragmentClass, null, 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, 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 Func1<FragmentTransaction, FragmentTransaction> transactionSetup) {
|
||||
addToStack(fragmentClass, targetFragment, 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 Func1<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 Func1<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 Func1<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 Func1<FragmentTransaction, FragmentTransaction> transactionSetup) {
|
||||
addViewControllerToStack(viewControllerClass, null, state, null, transactionSetup);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 Func1<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 Func1<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 Func1<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 Func1<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 Func1<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 Func1<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 Func1<FragmentTransaction, FragmentTransaction> transactionSetup) {
|
||||
addToStack(StatelessViewControllerFragment.class, targetFragment,
|
||||
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 Func1<FragmentTransaction, FragmentTransaction> transactionSetup) {
|
||||
addToStack(TargetedViewControllerFragment.class, targetFragment,
|
||||
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 Func1<FragmentTransaction, FragmentTransaction> transactionSetup) {
|
||||
addToStack(StatelessTargetedViewControllerFragment.class, targetFragment,
|
||||
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 Func1<FragmentTransaction, FragmentTransaction> transactionSetup) {
|
||||
addToStack(SimpleViewControllerFragment.class, targetFragment,
|
||||
SimpleViewControllerFragment.createState(viewControllerClass, state), backStackTag, transactionSetup);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -19,145 +19,62 @@
|
|||
|
||||
package ru.touchin.roboswag.components.navigation.activities;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.ColorInt;
|
||||
import android.support.annotation.ColorRes;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v4.util.ArraySet;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Set;
|
||||
|
||||
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;
|
||||
import rx.Completable;
|
||||
import rx.Observable;
|
||||
import rx.Single;
|
||||
import rx.Subscription;
|
||||
import rx.functions.Action0;
|
||||
import rx.functions.Action1;
|
||||
import rx.subjects.BehaviorSubject;
|
||||
|
||||
/**
|
||||
* 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.create(new Optional<HalfNullablePair<Integer, Intent>>(null));
|
||||
|
||||
/**
|
||||
* Returns if activity resumed.
|
||||
*
|
||||
* @return True if resumed.
|
||||
*/
|
||||
public boolean isActuallyResumed() {
|
||||
return resumed;
|
||||
}
|
||||
private final Set<OnBackPressedListener> onBackPressedListeners = new ArraySet<>();
|
||||
|
||||
@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
|
||||
.switchMap(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
|
||||
|
|
@ -169,68 +86,23 @@ 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides device keyboard that is showing over {@link Activity}.
|
||||
* Do NOT use it if keyboard is over {@link android.app.Dialog} - it won't work as they have different {@link Activity#getWindow()}.
|
||||
*/
|
||||
public void hideSoftInput() {
|
||||
if (getCurrentFocus() == null) {
|
||||
return;
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
onBackPressed();
|
||||
return true;
|
||||
} else {
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
final InputMethodManager inputManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
|
||||
inputManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
|
||||
getWindow().getDecorView().requestFocus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows device keyboard over {@link Activity} and focuses {@link View}.
|
||||
* Do NOT use it if keyboard is over {@link android.app.Dialog} - it won't work as they have different {@link Activity#getWindow()}.
|
||||
* Do NOT use it if you are not sure that view is already added on screen.
|
||||
* Better use it onStart of element if view is part of it or onConfigureNavigation if view is part of navigation.
|
||||
*
|
||||
* @param view View to get focus for input from keyboard.
|
||||
*/
|
||||
public void showSoftInput(@NonNull final View view) {
|
||||
view.requestFocus();
|
||||
final InputMethodManager inputManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
|
||||
inputManager.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 getColorCompat(@ColorRes final int resId) {
|
||||
return ContextCompat.getColor(this, 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 getDrawableCompat(@DrawableRes final int resId) {
|
||||
return ContextCompat.getDrawable(this, resId);
|
||||
}
|
||||
|
||||
public void addOnBackPressedListener(@NonNull final OnBackPressedListener onBackPressedListener) {
|
||||
|
|
@ -241,6 +113,10 @@ public abstract class BaseActivity extends AppCompatActivity
|
|||
onBackPressedListeners.remove(onBackPressedListener);
|
||||
}
|
||||
|
||||
public void removeAllOnBackPressedListeners() {
|
||||
onBackPressedListeners.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
for (final OnBackPressedListener onBackPressedListener : onBackPressedListeners) {
|
||||
|
|
@ -248,155 +124,9 @@ public abstract class BaseActivity extends AppCompatActivity
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (getSupportFragmentManager().getBackStackEntryCount() <= 1) {
|
||||
supportFinishAfterTransition();
|
||||
} else {
|
||||
getSupportFragmentManager().popBackStack();
|
||||
}
|
||||
super.onBackPressed();
|
||||
}
|
||||
|
||||
@SuppressWarnings("CPD-START")
|
||||
//CPD: it is same as in other implementation based on BaseLifecycleBindable
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Observable<T> observable,
|
||||
@NonNull final Action1<T> onNextAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction) {
|
||||
return baseLifecycleBindable.untilStop(observable, onNextAction, onErrorAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Observable<T> observable,
|
||||
@NonNull final Action1<T> onNextAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction,
|
||||
@NonNull final Action0 onCompletedAction) {
|
||||
return baseLifecycleBindable.untilStop(observable, onNextAction, onErrorAction, onCompletedAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Single<T> single) {
|
||||
return baseLifecycleBindable.untilStop(single);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Single<T> single, @NonNull final Action1<T> onSuccessAction) {
|
||||
return baseLifecycleBindable.untilStop(single, onSuccessAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Single<T> single,
|
||||
@NonNull final Action1<T> onSuccessAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction) {
|
||||
return baseLifecycleBindable.untilStop(single, onSuccessAction, onErrorAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Subscription untilStop(@NonNull final Completable completable) {
|
||||
return baseLifecycleBindable.untilStop(completable);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Subscription untilStop(@NonNull final Completable completable, @NonNull final Action0 onCompletedAction) {
|
||||
return baseLifecycleBindable.untilStop(completable, onCompletedAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Subscription untilStop(@NonNull final Completable completable,
|
||||
@NonNull final Action0 onCompletedAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction) {
|
||||
return baseLifecycleBindable.untilStop(completable, onCompletedAction, onErrorAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Observable<T> observable) {
|
||||
return baseLifecycleBindable.untilStop(observable);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Observable<T> observable, @NonNull final Action1<T> onNextAction) {
|
||||
return baseLifecycleBindable.untilStop(observable, onNextAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Observable<T> observable) {
|
||||
return baseLifecycleBindable.untilDestroy(observable);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Observable<T> observable, @NonNull final Action1<T> onNextAction) {
|
||||
return baseLifecycleBindable.untilDestroy(observable, onNextAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Observable<T> observable,
|
||||
@NonNull final Action1<T> onNextAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction) {
|
||||
return baseLifecycleBindable.untilDestroy(observable, onNextAction, onErrorAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Observable<T> observable,
|
||||
@NonNull final Action1<T> onNextAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction,
|
||||
@NonNull final Action0 onCompletedAction) {
|
||||
return baseLifecycleBindable.untilDestroy(observable, onNextAction, onErrorAction, onCompletedAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Single<T> single) {
|
||||
return baseLifecycleBindable.untilDestroy(single);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Single<T> single, @NonNull final Action1<T> onSuccessAction) {
|
||||
return baseLifecycleBindable.untilDestroy(single, onSuccessAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Single<T> single,
|
||||
@NonNull final Action1<T> onSuccessAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction) {
|
||||
return baseLifecycleBindable.untilDestroy(single, onSuccessAction, onErrorAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Subscription untilDestroy(@NonNull final Completable completable) {
|
||||
return baseLifecycleBindable.untilDestroy(completable);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Subscription untilDestroy(@NonNull final Completable completable, @NonNull final Action0 onCompletedAction) {
|
||||
return baseLifecycleBindable.untilDestroy(completable, onCompletedAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Subscription untilDestroy(@NonNull final Completable completable,
|
||||
@NonNull final Action0 onCompletedAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction) {
|
||||
return baseLifecycleBindable.untilDestroy(completable, onCompletedAction, onErrorAction);
|
||||
}
|
||||
|
||||
@SuppressWarnings("CPD-END")
|
||||
/*
|
||||
* Interface to be implemented for someone who want to intercept device back button pressing event.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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 View findViewById(@IdRes final int id) {
|
||||
final View viewById = super.findViewById(id);
|
||||
if (viewById == null) {
|
||||
throw new ShouldNotHappenException("No view for id=" + getResources().getResourceName(id));
|
||||
}
|
||||
return viewById;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -19,50 +19,41 @@
|
|||
|
||||
package ru.touchin.roboswag.components.navigation.fragments;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.animation.Animator;
|
||||
import android.content.Intent;
|
||||
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;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.view.animation.Animation;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
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.navigation.viewcontrollers.ViewController;
|
||||
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;
|
||||
import rx.Observable;
|
||||
import rx.Subscription;
|
||||
import rx.exceptions.OnErrorThrowable;
|
||||
import rx.subjects.BehaviorSubject;
|
||||
|
||||
/**
|
||||
* 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 class ViewControllerFragment<TActivity extends FragmentActivity, TState extends Parcelable> 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(Thread.currentThread().getContextClassLoader());
|
||||
parcel.recycle();
|
||||
return result;
|
||||
}
|
||||
|
|
@ -106,32 +96,22 @@ 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 Subscription viewControllerSubscription;
|
||||
private Class<ViewController<TActivity, TState>> 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;
|
||||
}
|
||||
}
|
||||
@Nullable
|
||||
private ActivityResult pendingActivityResult;
|
||||
|
||||
/**
|
||||
* 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,66 +120,40 @@ 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();
|
||||
public Class<ViewController<TActivity, TState>> getViewControllerClass() {
|
||||
return viewControllerClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, TState>>) 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 null;
|
||||
}
|
||||
final ViewController newViewController = createViewController(activity, container, viewInfo.getSecond());
|
||||
newViewController.onCreate();
|
||||
return newViewController;
|
||||
})
|
||||
.subscribe(this::onViewControllerChanged,
|
||||
throwable -> Lc.cutAssertion(throwable,
|
||||
OnErrorThrowable.class, 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 OnErrorThrowable.from(new ShouldNotHappenException("There should be single constructor for " + getViewControllerClass()));
|
||||
private ViewController createViewController(
|
||||
@NonNull final ViewController.CreationContext creationContext,
|
||||
@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 ViewController.CreationContext creationContext = new ViewController.CreationContext(activity, this, view);
|
||||
final Constructor<?> constructor = viewControllerClass.getConstructors()[0];
|
||||
final long creationTime = inDebugMode ? SystemClock.elapsedRealtime() : 0;
|
||||
try {
|
||||
switch (constructor.getParameterTypes().length) {
|
||||
|
|
@ -208,11 +162,10 @@ public abstract class ViewControllerFragment<TState extends AbstractState, TActi
|
|||
case 3:
|
||||
return (ViewController) constructor.newInstance(this, creationContext, savedInstanceState);
|
||||
default:
|
||||
throw OnErrorThrowable
|
||||
.from(new ShouldNotHappenException("Wrong constructor parameters count: " + constructor.getParameterTypes().length));
|
||||
throw new ShouldNotHappenException("Wrong constructor parameters count: " + constructor.getParameterTypes().length);
|
||||
}
|
||||
} catch (final Exception exception) {
|
||||
throw OnErrorThrowable.from(exception);
|
||||
} catch (@NonNull final Exception exception) {
|
||||
throw new ShouldNotHappenException(exception);
|
||||
} finally {
|
||||
checkCreationTime(creationTime);
|
||||
}
|
||||
|
|
@ -222,46 +175,64 @@ 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
|
||||
) {
|
||||
viewController = createViewController(
|
||||
new ViewController.CreationContext(requireActivity(), this, inflater, container), savedInstanceState);
|
||||
viewController.onCreate();
|
||||
return viewController.getView();
|
||||
}
|
||||
|
||||
@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));
|
||||
public void onActivityCreated(@Nullable final Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
if (viewController != null && pendingActivityResult != null) {
|
||||
viewController.onActivityResult(pendingActivityResult.requestCode, pendingActivityResult.resultCode, pendingActivityResult.data);
|
||||
pendingActivityResult = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Animation onCreateAnimation(final int transit, final boolean enter, final int nextAnim) {
|
||||
if (viewController != null) {
|
||||
return viewController.onCreateAnimation(transit, enter, nextAnim);
|
||||
} else {
|
||||
Lc.assertion("View should be instanceof PlaceholderView");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Animator onCreateAnimator(final int transit, final boolean enter, final int nextAnim) {
|
||||
if (viewController != null) {
|
||||
return viewController.onCreateAnimator(transit, enter, nextAnim);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@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 onViewStateRestored(@Nullable final Bundle savedInstanceState) {
|
||||
super.onViewStateRestored(savedInstanceState);
|
||||
if (viewController != null) {
|
||||
viewController.onViewStateRestored(savedInstanceState);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart(@NonNull final View view, @NonNull final TActivity activity) {
|
||||
super.onStart(view, activity);
|
||||
started = true;
|
||||
if (viewController != null) {
|
||||
viewController.onStart();
|
||||
}
|
||||
|
|
@ -291,23 +262,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
|
||||
|
|
@ -315,19 +275,6 @@ public abstract class ViewControllerFragment<TState extends AbstractState, TActi
|
|||
return (viewController != null && viewController.onOptionsItemSelected(item)) || super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private void onViewControllerChanged(@Nullable final ViewController viewController) {
|
||||
if (this.viewController != null) {
|
||||
this.viewController.onDestroy();
|
||||
}
|
||||
this.viewController = viewController;
|
||||
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);
|
||||
|
|
@ -342,7 +289,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
|
||||
|
|
@ -355,7 +302,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();
|
||||
}
|
||||
|
|
@ -363,58 +309,42 @@ 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.unsubscribe();
|
||||
if (viewController != null && !viewController.isDestroyed()) {
|
||||
public void onDestroyView() {
|
||||
if (viewController != null) {
|
||||
viewController.onDestroy();
|
||||
viewController = null;
|
||||
}
|
||||
super.onDestroy();
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
private static class PlaceholderView extends FrameLayout {
|
||||
|
||||
@NonNull
|
||||
private final String tagName;
|
||||
private long lastMeasureTime;
|
||||
|
||||
public PlaceholderView(@NonNull final Context context, @NonNull final String tagName) {
|
||||
super(context);
|
||||
this.tagName = tagName;
|
||||
@Override
|
||||
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
|
||||
if (viewController != null) {
|
||||
viewController.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
}
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
if (inDebugMode && lastMeasureTime == 0) {
|
||||
lastMeasureTime = SystemClock.uptimeMillis();
|
||||
}
|
||||
@Override
|
||||
public void onActivityResult(final int requestCode, final int resultCode, @Nullable final Intent data) {
|
||||
if (viewController != null) {
|
||||
viewController.onActivityResult(requestCode, resultCode, data);
|
||||
} else {
|
||||
pendingActivityResult = new ActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(@NonNull final Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
if (inDebugMode && lastMeasureTime > 0) {
|
||||
final long layoutTime = SystemClock.uptimeMillis() - lastMeasureTime;
|
||||
if (layoutTime > acceptableUiCalculationTime) {
|
||||
UiUtils.UI_METRICS_LC_GROUP.w("Measure and layout of %s took too much: %dms", tagName, layoutTime);
|
||||
}
|
||||
lastMeasureTime = 0;
|
||||
}
|
||||
private static class ActivityResult {
|
||||
public final int requestCode;
|
||||
public final int resultCode;
|
||||
@Nullable
|
||||
public final Intent data;
|
||||
|
||||
ActivityResult(final int requestCode, final int resultCode, @Nullable final Intent data) {
|
||||
this.requestCode = requestCode;
|
||||
this.resultCode = resultCode;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,19 +19,20 @@
|
|||
|
||||
package ru.touchin.roboswag.components.navigation.fragments;
|
||||
|
||||
import android.arch.lifecycle.Lifecycle;
|
||||
import android.os.Bundle;
|
||||
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;
|
||||
|
||||
import io.reactivex.functions.BiConsumer;
|
||||
import ru.touchin.roboswag.components.navigation.OnFragmentStartedListener;
|
||||
import ru.touchin.roboswag.core.log.Lc;
|
||||
import rx.functions.Action2;
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 21/10/2015.
|
||||
|
|
@ -39,11 +40,9 @@ import rx.functions.Action2;
|
|||
*
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* Returns if fragment have parent fragment.
|
||||
|
|
@ -88,42 +87,30 @@ 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 Action2<View, TActivity> action) {
|
||||
private void callMethodAfterInstantiation(@NonNull final BiConsumer<View, TActivity> action) {
|
||||
if (getView() == null || getBaseActivity() == null) {
|
||||
Lc.assertion("View and activity shouldn't be null");
|
||||
return;
|
||||
}
|
||||
action.call(getView(), getBaseActivity());
|
||||
try {
|
||||
action.accept(getView(), getBaseActivity());
|
||||
} catch (final Exception exception) {
|
||||
Lc.assertion(exception);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
started = true;
|
||||
callMethodAfterInstantiation(this::onStart);
|
||||
}
|
||||
|
||||
|
|
@ -180,6 +167,7 @@ public abstract class ViewFragment<TActivity extends AppCompatActivity> extends
|
|||
public void setMenuVisibility(final boolean menuVisible) {
|
||||
super.setMenuVisibility(menuVisible);
|
||||
if (getBaseActivity() != null && getView() != null) {
|
||||
final boolean started = getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED);
|
||||
if (!appeared && menuVisible && started) {
|
||||
onAppear(getView(), getBaseActivity());
|
||||
}
|
||||
|
|
@ -221,7 +209,6 @@ public abstract class ViewFragment<TActivity extends AppCompatActivity> extends
|
|||
@Deprecated
|
||||
@Override
|
||||
public void onStop() {
|
||||
started = false;
|
||||
callMethodAfterInstantiation(this::onStop);
|
||||
super.onStop();
|
||||
}
|
||||
|
|
@ -239,25 +226,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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
package ru.touchin.roboswag.components.navigation.viewcontrollers
|
||||
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
object EmptyState : Parcelable {
|
||||
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) = Unit
|
||||
|
||||
override fun describeContents() = 0
|
||||
|
||||
@JvmField
|
||||
val CREATOR = object : Parcelable.Creator<EmptyState> {
|
||||
override fun createFromParcel(parcel: Parcel) = EmptyState
|
||||
|
||||
override fun newArray(size: Int): Array<EmptyState?> = arrayOfNulls(size)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,442 @@
|
|||
/*
|
||||
* 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.viewcontrollers;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.arch.lifecycle.Lifecycle;
|
||||
import android.arch.lifecycle.LifecycleOwner;
|
||||
import android.arch.lifecycle.LifecycleRegistry;
|
||||
import android.content.Intent;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
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.support.v4.app.FragmentTransaction;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import android.view.animation.Animation;
|
||||
import ru.touchin.roboswag.components.navigation.fragments.ViewControllerFragment;
|
||||
import ru.touchin.roboswag.components.utils.UiUtils;
|
||||
import ru.touchin.roboswag.core.log.Lc;
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 21/10/2015.
|
||||
* Class to control view of specific fragment, activity and application by logic bridge.
|
||||
*
|
||||
* @param <TActivity> Type of activity where such {@link ViewController} could be;
|
||||
* @param <TState> Type of state;
|
||||
*/
|
||||
public abstract class ViewController<TActivity extends FragmentActivity, TState extends Parcelable> implements LifecycleOwner {
|
||||
|
||||
@NonNull
|
||||
private final LifecycleRegistry lifecycleRegistry = new LifecycleRegistry(this);
|
||||
@NonNull
|
||||
private final TActivity activity;
|
||||
@NonNull
|
||||
private final ViewControllerFragment<TActivity, TState> fragment;
|
||||
@NonNull
|
||||
private final View view;
|
||||
|
||||
@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter"})
|
||||
//UnusedFormalParameter: savedInstanceState could be used by children
|
||||
public ViewController(@NonNull final CreationContext creationContext, @Nullable final Bundle savedInstanceState, @LayoutRes final int layoutRes) {
|
||||
this.activity = (TActivity) creationContext.activity;
|
||||
this.fragment = creationContext.fragment;
|
||||
view = creationContext.inflater.inflate(layoutRes, creationContext.container, false);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Lifecycle getLifecycle() {
|
||||
return lifecycleRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns activity where {@link ViewController} could be.
|
||||
*
|
||||
* @return Returns activity.
|
||||
*/
|
||||
@NonNull
|
||||
public final TActivity getActivity() {
|
||||
return activity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns fragment where {@link ViewController} could be.
|
||||
*
|
||||
* @return Returns fragment.
|
||||
*/
|
||||
@NonNull
|
||||
public final ViewControllerFragment<TActivity, TState> getFragment() {
|
||||
return fragment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns state from fragment.
|
||||
*
|
||||
* @return Returns state.
|
||||
*/
|
||||
@NonNull
|
||||
public final TState getState() {
|
||||
return fragment.getState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns view instantiated in {@link #getFragment()} fragment attached to {@link #getActivity()} activity.
|
||||
* Use it to inflate your views into at construction of this {@link ViewController}.
|
||||
*
|
||||
* @return Returns view.
|
||||
*/
|
||||
@NonNull
|
||||
public final View getView() {
|
||||
return view;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
public final <T extends View> T findViewById(@IdRes final int id) {
|
||||
return getView().findViewById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a localized, styled CharSequence from the application's package's
|
||||
* default string table.
|
||||
*
|
||||
* @param resId Resource id for the CharSequence text
|
||||
*/
|
||||
@NonNull
|
||||
public final CharSequence getText(@StringRes final int resId) {
|
||||
return activity.getText(resId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 activity.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 activity.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 final int getColor(@ColorRes final int resId) {
|
||||
return ContextCompat.getColor(activity, resId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a color state list associated with a particular resource ID.
|
||||
*
|
||||
* <p>Starting in {@link android.os.Build.VERSION_CODES#M}, the returned
|
||||
* color state list will be styled for the specified Context's theme.
|
||||
*
|
||||
* @param resId The desired resource identifier, as generated by the aapt
|
||||
* tool. This integer encodes the package, type, and resource
|
||||
* entry. The value 0 is an invalid identifier.
|
||||
* @return A color state list, or {@code null} if the resource could not be resolved.
|
||||
* @throws android.content.res.Resources.NotFoundException if the given ID
|
||||
* does not exist.
|
||||
*/
|
||||
@Nullable
|
||||
public final ColorStateList getColorStateList(@ColorRes final int resId) {
|
||||
return ContextCompat.getColorStateList(activity, 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.
|
||||
*/
|
||||
@Nullable
|
||||
public final Drawable getDrawable(@DrawableRes final int resId) {
|
||||
return ContextCompat.getDrawable(activity, resId);
|
||||
}
|
||||
|
||||
public final void startActivity(@NonNull final Intent intent) {
|
||||
fragment.startActivity(intent);
|
||||
}
|
||||
|
||||
public final void startActivityForResult(@NonNull final Intent intent, final int requestCode) {
|
||||
fragment.startActivityForResult(intent, requestCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls when activity configuring ActionBar, Toolbar, Sidebar etc.
|
||||
* If it will be called or not depends on {@link Fragment#hasOptionsMenu()} and {@link Fragment#isMenuVisible()}.
|
||||
*
|
||||
* @param menu The options menu in which you place your items;
|
||||
* @param inflater Helper to inflate menu items.
|
||||
*/
|
||||
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(Bundle)}.
|
||||
*/
|
||||
@CallSuper
|
||||
public void onCreate() {
|
||||
UiUtils.UI_LIFECYCLE_LC_GROUP.i(Lc.getCodePoint(this));
|
||||
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a fragment loads an animation. Note that if
|
||||
* {@link FragmentTransaction#setCustomAnimations(int, int)} was called with
|
||||
* {@link Animator} resources instead of {@link Animation} resources, {@code nextAnim}
|
||||
* will be an animator resource.
|
||||
*
|
||||
* @param transit The value set in {@link FragmentTransaction#setTransition(int)} or 0 if not
|
||||
* set.
|
||||
* @param enter {@code true} when the fragment is added/attached/shown or {@code false} when
|
||||
* the fragment is removed/detached/hidden.
|
||||
* @param nextAnim The resource set in
|
||||
* {@link FragmentTransaction#setCustomAnimations(int, int)},
|
||||
* {@link FragmentTransaction#setCustomAnimations(int, int, int, int)}, or
|
||||
* 0 if neither was called. The value will depend on the current operation.
|
||||
*/
|
||||
@Nullable
|
||||
public Animation onCreateAnimation(final int transit, final boolean enter, final int nextAnim) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a fragment loads an animator. This will be called when
|
||||
* {@link #onCreateAnimation(int, boolean, int)} returns null. Note that if
|
||||
* {@link FragmentTransaction#setCustomAnimations(int, int)} was called with
|
||||
* {@link Animation} resources instead of {@link Animator} resources, {@code nextAnim}
|
||||
* will be an animation resource.
|
||||
*
|
||||
* @param transit The value set in {@link FragmentTransaction#setTransition(int)} or 0 if not
|
||||
* set.
|
||||
* @param enter {@code true} when the fragment is added/attached/shown or {@code false} when
|
||||
* the fragment is removed/detached/hidden.
|
||||
* @param nextAnim The resource set in
|
||||
* {@link FragmentTransaction#setCustomAnimations(int, int)},
|
||||
* {@link FragmentTransaction#setCustomAnimations(int, int, int, int)}, or
|
||||
* 0 if neither was called. The value will depend on the current operation.
|
||||
*/
|
||||
@Nullable
|
||||
public Animator onCreateAnimator(final int transit, final boolean enter, final int nextAnim) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls when {@link ViewController} saved state has been restored into the view hierarchy.
|
||||
* Happens at {@link ViewControllerFragment#onViewStateRestored}.
|
||||
*/
|
||||
@CallSuper
|
||||
public void onViewStateRestored(@Nullable final Bundle savedInstanceState) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls when {@link ViewController} have started.
|
||||
* Happens at {@link ViewControllerFragment#onStart()}.
|
||||
*/
|
||||
@CallSuper
|
||||
public void onStart() {
|
||||
UiUtils.UI_LIFECYCLE_LC_GROUP.i(Lc.getCodePoint(this));
|
||||
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
|
||||
UiUtils.OfViews.hideSoftInput(getView());
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when fragment is moved in started state and it's {@link #getFragment().isMenuVisible()} sets to true.
|
||||
* Usually it is indicating that user can't see fragment on screen and useful to track analytics events.
|
||||
*/
|
||||
public void onAppear() {
|
||||
UiUtils.UI_LIFECYCLE_LC_GROUP.i(Lc.getCodePoint(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls when {@link ViewController} have resumed.
|
||||
* Happens at {@link ViewControllerFragment#onResume()}.
|
||||
*/
|
||||
@CallSuper
|
||||
public void onResume() {
|
||||
UiUtils.UI_LIFECYCLE_LC_GROUP.i(Lc.getCodePoint(this));
|
||||
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls when {@link ViewController} have goes near out of memory state.
|
||||
* Happens at {@link ViewControllerFragment#onLowMemory()}.
|
||||
*/
|
||||
@CallSuper
|
||||
public void onLowMemory() {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls when {@link ViewController} have paused.
|
||||
* Happens at {@link ViewControllerFragment#onPause()}.
|
||||
*/
|
||||
@CallSuper
|
||||
public void onPause() {
|
||||
UiUtils.UI_LIFECYCLE_LC_GROUP.i(Lc.getCodePoint(this));
|
||||
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls when {@link ViewController} should save it's state.
|
||||
* Happens at {@link ViewControllerFragment#onSaveInstanceState(Bundle)}.
|
||||
* Try not to use such method for saving state but use {@link ViewControllerFragment#getState()} from {@link #getFragment()}.
|
||||
*/
|
||||
@CallSuper
|
||||
public void onSaveInstanceState(@NonNull final Bundle savedInstanceState) {
|
||||
UiUtils.UI_LIFECYCLE_LC_GROUP.i(Lc.getCodePoint(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when fragment is moved in stopped state or it's {@link #getFragment().isMenuVisible()} sets to false.
|
||||
* Usually it is indicating that user can't see fragment on screen and useful to track analytics events.
|
||||
*/
|
||||
public void onDisappear() {
|
||||
UiUtils.UI_LIFECYCLE_LC_GROUP.i(Lc.getCodePoint(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls when {@link ViewController} have stopped.
|
||||
* Happens at {@link ViewControllerFragment#onStop()}.
|
||||
*/
|
||||
@CallSuper
|
||||
public void onStop() {
|
||||
UiUtils.UI_LIFECYCLE_LC_GROUP.i(Lc.getCodePoint(this));
|
||||
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls when {@link ViewController} have destroyed.
|
||||
* 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));
|
||||
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calls when {@link ViewController} have requested permissions results.
|
||||
* Happens at {@link ViewControllerFragment#onRequestPermissionsResult(int, String[], int[])} ()}.
|
||||
*/
|
||||
@CallSuper
|
||||
@SuppressWarnings("PMD.UseVarargs")
|
||||
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback from parent fragment.
|
||||
*/
|
||||
public void onActivityResult(final int requestCode, final int resultCode, @Nullable final Intent data) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to {@link ViewControllerFragment#onOptionsItemSelected(MenuItem)}.
|
||||
*
|
||||
* @param item Selected menu item;
|
||||
* @return True if selection processed.
|
||||
*/
|
||||
public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper class to simplify constructor override.
|
||||
*/
|
||||
public static class CreationContext {
|
||||
|
||||
@NonNull
|
||||
private final FragmentActivity activity;
|
||||
@NonNull
|
||||
private final ViewControllerFragment fragment;
|
||||
@NonNull
|
||||
private final LayoutInflater inflater;
|
||||
@Nullable
|
||||
private final ViewGroup container;
|
||||
|
||||
public CreationContext(
|
||||
@NonNull final FragmentActivity activity,
|
||||
@NonNull final ViewControllerFragment fragment,
|
||||
@NonNull final LayoutInflater inflater,
|
||||
@Nullable final ViewGroup container
|
||||
) {
|
||||
this.activity = activity;
|
||||
this.fragment = fragment;
|
||||
this.inflater = inflater;
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ViewGroup getContainer() {
|
||||
return container;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* 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.viewcontrollers
|
||||
|
||||
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 ru.touchin.roboswag.components.navigation.FragmentNavigation
|
||||
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,
|
||||
transition: Int = FragmentTransaction.TRANSIT_FRAGMENT_OPEN
|
||||
) : FragmentNavigation(context, fragmentManager, containerViewId, transition) {
|
||||
|
||||
/**
|
||||
* 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 addToStack Flag to add this transaction to the back stack;
|
||||
* @param backStackName Name of [Fragment] 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.
|
||||
*/
|
||||
fun <TState : Parcelable> pushViewController(
|
||||
viewControllerClass: Class<out ViewController<out TActivity, TState>>,
|
||||
state: TState,
|
||||
addToStack: Boolean = true,
|
||||
backStackName: String? = null,
|
||||
transactionSetup: ((FragmentTransaction) -> Unit)? = null
|
||||
) {
|
||||
addToStack(
|
||||
ViewControllerFragment::class.java,
|
||||
null,
|
||||
0,
|
||||
addToStack,
|
||||
ViewControllerFragment.args(viewControllerClass, state),
|
||||
backStackName,
|
||||
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<out TActivity, TState>>,
|
||||
state: TState,
|
||||
targetFragment: TTargetFragment,
|
||||
targetRequestCode: Int,
|
||||
transactionSetup: ((FragmentTransaction) -> Unit)? = null
|
||||
) {
|
||||
addToStack(
|
||||
ViewControllerFragment::class.java,
|
||||
targetFragment,
|
||||
targetRequestCode,
|
||||
true,
|
||||
ViewControllerFragment.args(viewControllerClass, state),
|
||||
null,
|
||||
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<out TActivity, TState>>,
|
||||
state: TState,
|
||||
addToStack: Boolean = true,
|
||||
transactionSetup: ((FragmentTransaction) -> Unit)? = null
|
||||
) {
|
||||
addToStack(
|
||||
ViewControllerFragment::class.java,
|
||||
null,
|
||||
0,
|
||||
addToStack,
|
||||
ViewControllerFragment.args(viewControllerClass, state),
|
||||
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<out TActivity, TState>>,
|
||||
state: TState,
|
||||
transactionSetup: ((FragmentTransaction) -> Unit)? = null
|
||||
) {
|
||||
beforeSetInitialActions()
|
||||
setViewControllerAsTop(viewControllerClass, state, false, transactionSetup)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,295 +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 ru.touchin.roboswag.core.log.Lc;
|
||||
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
|
||||
import rx.Completable;
|
||||
import rx.Observable;
|
||||
import rx.Single;
|
||||
import rx.Subscription;
|
||||
import rx.android.schedulers.AndroidSchedulers;
|
||||
import rx.exceptions.OnErrorThrowable;
|
||||
import rx.functions.Action0;
|
||||
import rx.functions.Action1;
|
||||
import rx.functions.Actions;
|
||||
import rx.subjects.BehaviorSubject;
|
||||
|
||||
/**
|
||||
* 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.create(false);
|
||||
|
||||
/**
|
||||
* Call it on parent's onCreate method.
|
||||
*/
|
||||
public void onCreate() {
|
||||
isCreatedSubject.onNext(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call it on parent's onStart method.
|
||||
*/
|
||||
public void onStart() {
|
||||
if (!isStartedSubject.hasValue() || !isStartedSubject.getValue()) {
|
||||
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() {
|
||||
if (!isStartedSubject.hasValue() || isStartedSubject.getValue()) {
|
||||
isStartedSubject.onNext(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call it on parent's onDestroy method.
|
||||
*/
|
||||
public void onDestroy() {
|
||||
isCreatedSubject.onNext(false);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Observable<T> observable) {
|
||||
final String codePoint = Lc.getCodePoint(this, 2);
|
||||
return untilStop(observable, Actions.empty(), getActionThrowableForAssertion(codePoint, UNTIL_STOP_METHOD), Actions.empty());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Observable<T> observable, @NonNull final Action1<T> onNextAction) {
|
||||
final String codePoint = Lc.getCodePoint(this, 2);
|
||||
return untilStop(observable, onNextAction, getActionThrowableForAssertion(codePoint, UNTIL_STOP_METHOD), Actions.empty());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Observable<T> observable,
|
||||
@NonNull final Action1<T> onNextAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction) {
|
||||
return untilStop(observable, onNextAction, onErrorAction, Actions.empty());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Observable<T> observable,
|
||||
@NonNull final Action1<T> onNextAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction,
|
||||
@NonNull final Action0 onCompletedAction) {
|
||||
return until(observable, isStartedSubject.map(started -> !started)
|
||||
.delay(item -> isInAfterSaving.filter(inAfterSaving -> !inAfterSaving)),
|
||||
onNextAction, onErrorAction, onCompletedAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Single<T> single) {
|
||||
final String codePoint = Lc.getCodePoint(this, 2);
|
||||
return untilStop(single, Actions.empty(), getActionThrowableForAssertion(codePoint, UNTIL_STOP_METHOD));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Single<T> single, @NonNull final Action1<T> onSuccessAction) {
|
||||
final String codePoint = Lc.getCodePoint(this, 2);
|
||||
return untilStop(single, onSuccessAction, getActionThrowableForAssertion(codePoint, UNTIL_STOP_METHOD));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Single<T> single,
|
||||
@NonNull final Action1<T> onSuccessAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction) {
|
||||
return until(single.toObservable(), isStartedSubject.map(started -> !started)
|
||||
.delay(item -> isInAfterSaving.filter(inAfterSaving -> !inAfterSaving)),
|
||||
onSuccessAction, onErrorAction, Actions.empty());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Subscription untilStop(@NonNull final Completable completable) {
|
||||
final String codePoint = Lc.getCodePoint(this, 2);
|
||||
return untilStop(completable, Actions.empty(), getActionThrowableForAssertion(codePoint, UNTIL_STOP_METHOD));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Subscription untilStop(@NonNull final Completable completable,
|
||||
@NonNull final Action0 onCompletedAction) {
|
||||
final String codePoint = Lc.getCodePoint(this, 2);
|
||||
return untilStop(completable, onCompletedAction, getActionThrowableForAssertion(codePoint, UNTIL_STOP_METHOD));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Subscription untilStop(@NonNull final Completable completable,
|
||||
@NonNull final Action0 onCompletedAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction) {
|
||||
return until(completable.toObservable(), isStartedSubject.map(started -> !started)
|
||||
.delay(item -> isInAfterSaving.filter(inAfterSaving -> !inAfterSaving)),
|
||||
Actions.empty(), onErrorAction, onCompletedAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Observable<T> observable) {
|
||||
final String codePoint = Lc.getCodePoint(this, 2);
|
||||
return untilDestroy(observable, Actions.empty(), getActionThrowableForAssertion(codePoint, UNTIL_DESTROY_METHOD), Actions.empty());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Observable<T> observable,
|
||||
@NonNull final Action1<T> onNextAction) {
|
||||
final String codePoint = Lc.getCodePoint(this, 2);
|
||||
return untilDestroy(observable, onNextAction, getActionThrowableForAssertion(codePoint, UNTIL_DESTROY_METHOD), Actions.empty());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Observable<T> observable,
|
||||
@NonNull final Action1<T> onNextAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction) {
|
||||
return untilDestroy(observable, onNextAction, onErrorAction, Actions.empty());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Observable<T> observable,
|
||||
@NonNull final Action1<T> onNextAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction,
|
||||
@NonNull final Action0 onCompletedAction) {
|
||||
return until(observable, isCreatedSubject.map(created -> !created), onNextAction, onErrorAction, onCompletedAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Single<T> single) {
|
||||
final String codePoint = Lc.getCodePoint(this, 2);
|
||||
return untilDestroy(single, Actions.empty(), getActionThrowableForAssertion(codePoint, UNTIL_DESTROY_METHOD));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Single<T> single, @NonNull final Action1<T> onSuccessAction) {
|
||||
final String codePoint = Lc.getCodePoint(this, 2);
|
||||
return untilDestroy(single, onSuccessAction, getActionThrowableForAssertion(codePoint, UNTIL_DESTROY_METHOD));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Single<T> single,
|
||||
@NonNull final Action1<T> onSuccessAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction) {
|
||||
return until(single.toObservable(), isCreatedSubject.map(created -> !created), onSuccessAction, onErrorAction, Actions.empty());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Subscription untilDestroy(@NonNull final Completable completable) {
|
||||
final String codePoint = Lc.getCodePoint(this, 2);
|
||||
return untilDestroy(completable, Actions.empty(), getActionThrowableForAssertion(codePoint, UNTIL_DESTROY_METHOD));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Subscription untilDestroy(@NonNull final Completable completable, @NonNull final Action0 onCompletedAction) {
|
||||
final String codePoint = Lc.getCodePoint(this, 2);
|
||||
return untilDestroy(completable, onCompletedAction, getActionThrowableForAssertion(codePoint, UNTIL_DESTROY_METHOD));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Subscription untilDestroy(@NonNull final Completable completable,
|
||||
@NonNull final Action0 onCompletedAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction) {
|
||||
return until(completable.toObservable(), isCreatedSubject.map(created -> !created), Actions.empty(), onErrorAction, onCompletedAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private <T> Subscription until(@NonNull final Observable<T> observable,
|
||||
@NonNull final Observable<Boolean> conditionSubject,
|
||||
@NonNull final Action1<T> onNextAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction,
|
||||
@NonNull final Action0 onCompletedAction) {
|
||||
final Observable<T> actualObservable;
|
||||
if (onNextAction == Actions.empty() && onErrorAction == (Action1) Actions.empty() && onCompletedAction == Actions.empty()) {
|
||||
actualObservable = observable;
|
||||
} else {
|
||||
actualObservable = observable.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnCompleted(onCompletedAction)
|
||||
.doOnNext(onNextAction)
|
||||
.doOnError(onErrorAction);
|
||||
}
|
||||
|
||||
return isCreatedSubject.first()
|
||||
.switchMap(created -> created ? actualObservable : Observable.empty())
|
||||
.takeUntil(conditionSubject.filter(condition -> condition))
|
||||
.onErrorResumeNext(throwable -> {
|
||||
final boolean isRxError = throwable instanceof OnErrorThrowable;
|
||||
if ((!isRxError && throwable instanceof RuntimeException)
|
||||
|| (isRxError && throwable.getCause() instanceof RuntimeException)) {
|
||||
Lc.assertion(throwable);
|
||||
}
|
||||
return Observable.empty();
|
||||
})
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Action1<Throwable> getActionThrowableForAssertion(@NonNull final String codePoint, @NonNull final String method) {
|
||||
return throwable -> Lc.assertion(new ShouldNotHappenException("Unexpected error on " + method + " at " + codePoint, throwable));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,314 +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 rx.Completable;
|
||||
import rx.CompletableSubscriber;
|
||||
import rx.Observable;
|
||||
import rx.Single;
|
||||
import rx.SingleSubscriber;
|
||||
import rx.Subscriber;
|
||||
import rx.Subscription;
|
||||
import rx.functions.Action0;
|
||||
import rx.functions.Action1;
|
||||
|
||||
/**
|
||||
* 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 Subscription} which will unsubscribes from observable onStop.
|
||||
*/
|
||||
@NonNull
|
||||
<T> Subscription 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 Subscriber#onNext(Object)} item;
|
||||
* @param <T> Type of emitted by observable items;
|
||||
* @return {@link Subscription} which will unsubscribes from observable onStop.
|
||||
*/
|
||||
@NonNull
|
||||
<T> Subscription untilStop(@NonNull Observable<T> observable, @NonNull Action1<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 Subscriber#onNext(Object)} item;
|
||||
* @param onErrorAction Action which will raise on every {@link Subscriber#onError(Throwable)} throwable;
|
||||
* @param <T> Type of emitted by observable items;
|
||||
* @return {@link Subscription} which will unsubscribes from observable onStop.
|
||||
*/
|
||||
@NonNull
|
||||
<T> Subscription untilStop(@NonNull Observable<T> observable, @NonNull Action1<T> onNextAction, @NonNull Action1<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 Subscriber#onNext(Object)} item;
|
||||
* @param onErrorAction Action which will raise on every {@link Subscriber#onError(Throwable)} throwable;
|
||||
* @param onCompletedAction Action which will raise at {@link Subscriber#onCompleted()} on completion of observable;
|
||||
* @param <T> Type of emitted by observable items;
|
||||
* @return {@link Subscription} which is wrapping source observable to unsubscribe from it onStop.
|
||||
*/
|
||||
@NonNull
|
||||
<T> Subscription untilStop(@NonNull Observable<T> observable,
|
||||
@NonNull Action1<T> onNextAction, @NonNull Action1<Throwable> onErrorAction, @NonNull Action0 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 Subscription} which will unsubscribes from single onStop.
|
||||
*/
|
||||
@NonNull
|
||||
<T> Subscription 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 SingleSubscriber#onSuccess(Object)} item;
|
||||
* @param <T> Type of emitted by single item;
|
||||
* @return {@link Subscription} which will unsubscribes from single onStop.
|
||||
*/
|
||||
@NonNull
|
||||
<T> Subscription untilStop(@NonNull Single<T> single, @NonNull Action1<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 SingleSubscriber#onSuccess(Object)} item;
|
||||
* @param onErrorAction Action which will raise on every {@link SingleSubscriber#onError(Throwable)} throwable;
|
||||
* @param <T> Type of emitted by observable items;
|
||||
* @return {@link Subscription} which is wrapping source single to unsubscribe from it onStop.
|
||||
*/
|
||||
@NonNull
|
||||
<T> Subscription untilStop(@NonNull Single<T> single, @NonNull Action1<T> onSuccessAction, @NonNull Action1<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 Subscription} which will unsubscribes from completable onStop.
|
||||
*/
|
||||
@NonNull
|
||||
Subscription 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 CompletableSubscriber#onCompleted()} on completion of observable;
|
||||
* @return {@link Subscription} which is wrapping source completable to unsubscribe from it onStop.
|
||||
*/
|
||||
@NonNull
|
||||
Subscription untilStop(@NonNull Completable completable, @NonNull Action0 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 CompletableSubscriber#onCompleted()} on completion of observable;
|
||||
* @param onErrorAction Action which will raise on every {@link CompletableSubscriber#onError(Throwable)} throwable;
|
||||
* @return {@link Subscription} which is wrapping source completable to unsubscribe from it onStop.
|
||||
*/
|
||||
@NonNull
|
||||
Subscription untilStop(@NonNull Completable completable, @NonNull Action0 onCompletedAction, @NonNull Action1<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 Subscription} which is wrapping source observable to unsubscribe from it onDestroy.
|
||||
*/
|
||||
@NonNull
|
||||
<T> Subscription 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 Subscriber#onNext(Object)} item;
|
||||
* @param <T> Type of emitted by observable items;
|
||||
* @return {@link Subscription} which is wrapping source observable to unsubscribe from it onDestroy.
|
||||
*/
|
||||
@NonNull
|
||||
<T> Subscription untilDestroy(@NonNull Observable<T> observable, @NonNull Action1<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 Subscriber#onNext(Object)} item;
|
||||
* @param onErrorAction Action which will raise on every {@link Subscriber#onError(Throwable)} throwable;
|
||||
* @param <T> Type of emitted by observable items;
|
||||
* @return {@link Subscription} which is wrapping source observable to unsubscribe from it onDestroy.
|
||||
*/
|
||||
@NonNull
|
||||
<T> Subscription untilDestroy(@NonNull Observable<T> observable, @NonNull Action1<T> onNextAction, @NonNull Action1<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 Subscriber#onNext(Object)} item;
|
||||
* @param onErrorAction Action which will raise on every {@link Subscriber#onError(Throwable)} throwable;
|
||||
* @param onCompletedAction Action which will raise at {@link Subscriber#onCompleted()} on completion of observable;
|
||||
* @param <T> Type of emitted by observable items;
|
||||
* @return {@link Subscription} which is wrapping source observable to unsubscribe from it onDestroy.
|
||||
*/
|
||||
@NonNull
|
||||
<T> Subscription untilDestroy(@NonNull Observable<T> observable,
|
||||
@NonNull Action1<T> onNextAction, @NonNull Action1<Throwable> onErrorAction, @NonNull Action0 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 Subscription} which is wrapping source single to unsubscribe from it onDestroy.
|
||||
*/
|
||||
@NonNull
|
||||
<T> Subscription 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 SingleSubscriber#onSuccess(Object)} item;
|
||||
* @param <T> Type of emitted by single items;
|
||||
* @return {@link Subscription} which is wrapping source single to unsubscribe from it onDestroy.
|
||||
*/
|
||||
@NonNull
|
||||
<T> Subscription untilDestroy(@NonNull Single<T> single, @NonNull Action1<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 SingleSubscriber#onSuccess(Object)} item;
|
||||
* @param onErrorAction Action which will raise on every {@link SingleSubscriber#onError(Throwable)} throwable;
|
||||
* @param <T> Type of emitted by single items;
|
||||
* @return {@link Subscription} which is wrapping source single to unsubscribe from it onDestroy.
|
||||
*/
|
||||
@NonNull
|
||||
<T> Subscription untilDestroy(@NonNull Single<T> single, @NonNull Action1<T> onSuccessAction, @NonNull Action1<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 Subscription} which is wrapping source completable to unsubscribe from it onDestroy.
|
||||
*/
|
||||
@NonNull
|
||||
Subscription 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 single can emit them.
|
||||
*
|
||||
* @param completable {@link Completable} to subscribe until onDestroy;
|
||||
* @param onCompletedAction Action which will raise on every {@link CompletableSubscriber#onCompleted()} item;
|
||||
* @return {@link Subscription} which is wrapping source single to unsubscribe from it onDestroy.
|
||||
*/
|
||||
@NonNull
|
||||
Subscription untilDestroy(@NonNull Completable completable, @NonNull Action0 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 CompletableSubscriber#onCompleted()} item;
|
||||
* @param onErrorAction Action which will raise on every {@link CompletableSubscriber#onError(Throwable)} throwable;
|
||||
* @return {@link Subscription} which is wrapping source completable to unsubscribe from it onDestroy.
|
||||
*/
|
||||
@NonNull
|
||||
Subscription untilDestroy(@NonNull Completable completable, @NonNull Action0 onCompletedAction, @NonNull Action1<Throwable> onErrorAction);
|
||||
|
||||
}
|
||||
|
|
@ -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 ru.touchin.roboswag.core.utils.ShouldNotHappenException;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,105 +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.content.res.AssetManager;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Typeface;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.StyleableRes;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import ru.touchin.roboswag.core.log.Lc;
|
||||
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 18/07/2014.
|
||||
* Manager for typefaces stored in 'assets/fonts' folder.
|
||||
*/
|
||||
public final class Typefaces {
|
||||
|
||||
private static final Map<String, Typeface> TYPEFACES_MAP = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Returns {@link Typeface} by name from assets 'fonts' folder.
|
||||
*
|
||||
* @param context Context of assets where typeface file stored in;
|
||||
* @param name Full name of typeface (without extension, e.g. 'Roboto-Regular');
|
||||
* @return {@link Typeface} from assets.
|
||||
*/
|
||||
@NonNull
|
||||
public static Typeface getByName(@NonNull final Context context, @NonNull final String name) {
|
||||
synchronized (TYPEFACES_MAP) {
|
||||
Typeface result = TYPEFACES_MAP.get(name);
|
||||
if (result == null) {
|
||||
final AssetManager assetManager = context.getAssets();
|
||||
result = Typeface.DEFAULT;
|
||||
try {
|
||||
final List<String> fonts = Arrays.asList(assetManager.list("fonts"));
|
||||
if (fonts.contains(name + ".ttf")) {
|
||||
result = Typeface.createFromAsset(assetManager, "fonts/" + name + ".ttf");
|
||||
} else if (fonts.contains(name + ".otf")) {
|
||||
result = Typeface.createFromAsset(assetManager, "fonts/" + name + ".otf");
|
||||
} else {
|
||||
Lc.assertion("Can't find .otf or .ttf file in assets folder 'fonts' with name: " + name);
|
||||
}
|
||||
} catch (final IOException exception) {
|
||||
Lc.assertion(new ShouldNotHappenException("Can't get font " + name + '.'
|
||||
+ "Did you forget to create assets folder named 'fonts'?", exception));
|
||||
}
|
||||
TYPEFACES_MAP.put(name, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link Typeface} by name from assets 'fonts' folder.
|
||||
*
|
||||
* @param context Context of assets where typeface file stored in;
|
||||
* @param attrs Attributes of view to get font from;
|
||||
* @param styleableId Id of attribute set;
|
||||
* @param attributeId Id of attribute;
|
||||
* @return {@link Typeface} from assets.
|
||||
*/
|
||||
@NonNull
|
||||
public static Typeface getFromAttributes(@NonNull final Context context, @NonNull final AttributeSet attrs,
|
||||
@NonNull @StyleableRes final int[] styleableId, @StyleableRes final int attributeId) {
|
||||
final TypedArray typedArray = context.obtainStyledAttributes(attrs, styleableId);
|
||||
final String customTypeface = typedArray.getString(attributeId);
|
||||
typedArray.recycle();
|
||||
if (customTypeface != null) {
|
||||
return getByName(context, customTypeface);
|
||||
}
|
||||
Lc.w("Couldn't find custom typeface. Returns default");
|
||||
return Typeface.DEFAULT;
|
||||
}
|
||||
|
||||
private Typefaces() {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -25,12 +25,8 @@ 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;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Display;
|
||||
|
|
@ -40,12 +36,9 @@ import android.view.LayoutInflater;
|
|||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import ru.touchin.roboswag.components.navigation.activities.BaseActivity;
|
||||
import ru.touchin.roboswag.core.log.LcGroup;
|
||||
import rx.functions.Action0;
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 13/11/2015.
|
||||
|
|
@ -61,12 +54,6 @@ public final class UiUtils {
|
|||
* Logging group to log UI lifecycle (onCreate, onStart, onResume etc.).
|
||||
*/
|
||||
public static final LcGroup UI_LIFECYCLE_LC_GROUP = new LcGroup("UI_LIFECYCLE");
|
||||
/**
|
||||
* Delay to let user view ripple effect before screen changed.
|
||||
*/
|
||||
public static final long RIPPLE_EFFECT_DELAY = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? 150 : 0;
|
||||
|
||||
private static final Handler RIPPLE_HANDLER = new Handler(Looper.getMainLooper());
|
||||
|
||||
/**
|
||||
* Method to inflate view with right layout parameters based on container and add inflated view as a child to it.
|
||||
|
|
@ -93,67 +80,6 @@ public final class UiUtils {
|
|||
return LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets click listener to view. On click it will call something after delay.
|
||||
*
|
||||
* @param targetView View to set click listener to;
|
||||
* @param onClickListener Click listener;
|
||||
* @param delay Delay after which click listener will be called.
|
||||
*/
|
||||
public static void setOnRippleClickListener(@NonNull final View targetView, @Nullable final Action0 onClickListener, final long delay) {
|
||||
setOnRippleClickListener(targetView, onClickListener != null ? v -> onClickListener.call() : null, delay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets click listener to view. On click it will call something with {@link #RIPPLE_EFFECT_DELAY}.
|
||||
*
|
||||
* @param targetView View to set click listener to;
|
||||
* @param onClickListener Click listener.
|
||||
*/
|
||||
public static void setOnRippleClickListener(@NonNull final View targetView, @Nullable final Action0 onClickListener) {
|
||||
setOnRippleClickListener(targetView, onClickListener != null ? v -> onClickListener.call() : null, RIPPLE_EFFECT_DELAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets click listener to view. On click it will call something with {@link #RIPPLE_EFFECT_DELAY}.
|
||||
*
|
||||
* @param targetView View to set click listener to;
|
||||
* @param onClickListener Click listener.
|
||||
*/
|
||||
public static void setOnRippleClickListener(@NonNull final View targetView, @Nullable final View.OnClickListener onClickListener) {
|
||||
setOnRippleClickListener(targetView, onClickListener, RIPPLE_EFFECT_DELAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets click listener to view. On click it will call something after delay.
|
||||
*
|
||||
* @param targetView View to set click listener to;
|
||||
* @param onClickListener Click listener;
|
||||
* @param delay Delay after which click listener will be called.
|
||||
*/
|
||||
public static void setOnRippleClickListener(@NonNull final View targetView,
|
||||
@Nullable final View.OnClickListener onClickListener,
|
||||
final long delay) {
|
||||
if (onClickListener == null) {
|
||||
targetView.setOnClickListener(null);
|
||||
return;
|
||||
}
|
||||
|
||||
final Runnable runnable = () -> {
|
||||
if (targetView.getWindowVisibility() != View.VISIBLE
|
||||
|| !targetView.hasWindowFocus()
|
||||
|| (targetView.getContext() instanceof BaseActivity && !((BaseActivity) targetView.getContext()).isActuallyResumed())) {
|
||||
return;
|
||||
}
|
||||
onClickListener.onClick(targetView);
|
||||
};
|
||||
|
||||
targetView.setOnClickListener(v -> {
|
||||
RIPPLE_HANDLER.removeCallbacksAndMessages(null);
|
||||
RIPPLE_HANDLER.postDelayed(runnable, delay);
|
||||
});
|
||||
}
|
||||
|
||||
private UiUtils() {
|
||||
}
|
||||
|
||||
|
|
@ -298,35 +224,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.
|
||||
*
|
||||
|
|
@ -342,6 +239,42 @@ public final class UiUtils {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides device keyboard for target activity.
|
||||
*/
|
||||
public static void hideSoftInput(@NonNull final Activity activity) {
|
||||
final View focusedView = activity.getCurrentFocus();
|
||||
if (focusedView != null) {
|
||||
hideSoftInput(focusedView);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides device keyboard for target view.
|
||||
*/
|
||||
public static void hideSoftInput(@NonNull final View view) {
|
||||
view.clearFocus();
|
||||
final InputMethodManager inputManager = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
if (inputManager != null) {
|
||||
inputManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows device keyboard over {@link Activity} and focuses {@link View}.
|
||||
* Do NOT use it if keyboard is over {@link android.app.Dialog} - it won't work as they have different {@link Activity#getWindow()}.
|
||||
* Do NOT use it if you are not sure that view is already added on screen.
|
||||
*
|
||||
* @param view View to get focus for input from keyboard.
|
||||
*/
|
||||
public static void showSoftInput(@NonNull final View view) {
|
||||
view.requestFocus();
|
||||
final InputMethodManager inputManager = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
if (inputManager != null) {
|
||||
inputManager.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
|
||||
}
|
||||
}
|
||||
|
||||
private OfViews() {
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ import android.content.IntentFilter;
|
|||
import android.media.AudioManager;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.subjects.BehaviorSubject;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.subjects.BehaviorSubject;
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 02/11/2015.
|
||||
|
|
@ -50,12 +50,12 @@ public final class HeadsetStateObserver {
|
|||
isConnectedReceiver.isWirelessConnectedChangedEvent,
|
||||
(isWiredConnected, isWirelessConnected) -> isWiredConnected || isWirelessConnected)
|
||||
.distinctUntilChanged()
|
||||
.doOnSubscribe(() -> {
|
||||
.doOnSubscribe(disposable -> {
|
||||
final IntentFilter headsetStateIntentFilter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);
|
||||
headsetStateIntentFilter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
|
||||
context.registerReceiver(isConnectedReceiver, headsetStateIntentFilter);
|
||||
})
|
||||
.doOnUnsubscribe(() -> context.unregisterReceiver(isConnectedReceiver)))
|
||||
.doOnDispose(() -> context.unregisterReceiver(isConnectedReceiver)))
|
||||
.replay(1)
|
||||
.refCount();
|
||||
}
|
||||
|
|
@ -90,8 +90,8 @@ public final class HeadsetStateObserver {
|
|||
@SuppressWarnings("deprecation")
|
||||
public IsConnectedReceiver(@NonNull final AudioManager audioManager) {
|
||||
super();
|
||||
isWiredConnectedChangedEvent = BehaviorSubject.create(audioManager.isWiredHeadsetOn());
|
||||
isWirelessConnectedChangedEvent = BehaviorSubject.create(audioManager.isBluetoothA2dpOn());
|
||||
isWiredConnectedChangedEvent = BehaviorSubject.createDefault(audioManager.isWiredHeadsetOn());
|
||||
isWirelessConnectedChangedEvent = BehaviorSubject.createDefault(audioManager.isBluetoothA2dpOn());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -27,10 +27,10 @@ import android.os.Looper;
|
|||
import android.provider.Settings;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.subjects.PublishSubject;
|
||||
import ru.touchin.roboswag.core.log.Lc;
|
||||
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
|
||||
import rx.Observable;
|
||||
import rx.subjects.PublishSubject;
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 02/11/2015.
|
||||
|
|
@ -58,9 +58,9 @@ public final class VolumeController {
|
|||
.switchMap(volumeObserver -> selfVolumeChangedEvent
|
||||
.mergeWith(volumeObserver.systemVolumeChangedEvent
|
||||
.map(event -> getVolume())
|
||||
.doOnSubscribe(() -> context.getContentResolver()
|
||||
.doOnSubscribe(disposable -> context.getContentResolver()
|
||||
.registerContentObserver(Settings.System.CONTENT_URI, true, volumeObserver))
|
||||
.doOnUnsubscribe(() -> context.getContentResolver()
|
||||
.doOnDispose(() -> context.getContentResolver()
|
||||
.unregisterContentObserver(volumeObserver)))
|
||||
.startWith(getVolume()))
|
||||
.distinctUntilChanged()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
package ru.touchin.roboswag.components.utils.destroyable
|
||||
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.Flowable
|
||||
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()
|
||||
|
||||
override fun clearSubscriptions() = subscriptions.clear()
|
||||
|
||||
/**
|
||||
* Call it on parent's onDestroy method.
|
||||
*/
|
||||
fun onDestroy() = subscriptions.dispose()
|
||||
|
||||
override fun <T> Flowable<T>.untilDestroy(
|
||||
onNext: (T) -> Unit,
|
||||
onError: (Throwable) -> Unit,
|
||||
onComplete: () -> Unit
|
||||
): Disposable = observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(onNext, onError, onComplete)
|
||||
.also { subscriptions.add(it) }
|
||||
|
||||
override fun <T> Observable<T>.untilDestroy(
|
||||
onNext: (T) -> Unit,
|
||||
onError: (Throwable) -> Unit,
|
||||
onComplete: () -> Unit
|
||||
): Disposable = observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(onNext, onError, onComplete)
|
||||
.also { subscriptions.add(it) }
|
||||
|
||||
override fun <T> Single<T>.untilDestroy(
|
||||
onSuccess: (T) -> Unit,
|
||||
onError: (Throwable) -> Unit
|
||||
): Disposable = observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(onSuccess, onError)
|
||||
.also { subscriptions.add(it) }
|
||||
|
||||
override fun Completable.untilDestroy(
|
||||
onComplete: () -> Unit,
|
||||
onError: (Throwable) -> Unit
|
||||
): Disposable = observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(onComplete, onError)
|
||||
.also { subscriptions.add(it) }
|
||||
|
||||
override fun <T> Maybe<T>.untilDestroy(
|
||||
onSuccess: (T) -> Unit,
|
||||
onError: (Throwable) -> Unit,
|
||||
onComplete: () -> Unit
|
||||
): Disposable = observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(onSuccess, onError, onComplete)
|
||||
.also { subscriptions.add(it) }
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
package ru.touchin.roboswag.components.utils.destroyable
|
||||
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.Flowable
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all subscriptions
|
||||
*/
|
||||
fun clearSubscriptions()
|
||||
|
||||
/**
|
||||
* 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 onNext Action which will raise on every [io.reactivex.Emitter.onNext] item;
|
||||
* @param onError Action which will raise on every [io.reactivex.Emitter.onError] throwable;
|
||||
* @param onComplete Action which will raise on every [io.reactivex.Emitter.onComplete] item;
|
||||
* @return [Disposable] which is wrapping source observable to unsubscribe from it onDestroy.
|
||||
*/
|
||||
fun <T> Flowable<T>.untilDestroy(
|
||||
onNext: (T) -> Unit = Functions.emptyConsumer<T>()::accept,
|
||||
onError: (Throwable) -> Unit = getActionThrowableForAssertion(Lc.getCodePoint(this, 2)),
|
||||
onComplete: () -> Unit = Functions.EMPTY_ACTION::run
|
||||
): Disposable
|
||||
|
||||
/**
|
||||
* 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 onNext Action which will raise on every [io.reactivex.Emitter.onNext] item;
|
||||
* @param onError Action which will raise on every [io.reactivex.Emitter.onError] throwable;
|
||||
* @param onComplete Action which will raise on every [io.reactivex.Emitter.onComplete] item;
|
||||
* @return [Disposable] which is wrapping source observable to unsubscribe from it onDestroy.
|
||||
*/
|
||||
fun <T> Observable<T>.untilDestroy(
|
||||
onNext: (T) -> Unit = Functions.emptyConsumer<T>()::accept,
|
||||
onError: (Throwable) -> Unit = getActionThrowableForAssertion(Lc.getCodePoint(this, 2)),
|
||||
onComplete: () -> 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 onSuccess Action which will raise on every [io.reactivex.SingleEmitter.onSuccess] item;
|
||||
* @param onError Action which will raise on every [io.reactivex.SingleEmitter.onError] throwable;
|
||||
* @return [Disposable] which is wrapping source single to unsubscribe from it onDestroy.
|
||||
*/
|
||||
fun <T> Single<T>.untilDestroy(
|
||||
onSuccess: (T) -> Unit = Functions.emptyConsumer<T>()::accept,
|
||||
onError: (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 onComplete Action which will raise on every [io.reactivex.CompletableEmitter.onComplete] item;
|
||||
* @param onError Action which will raise on every [io.reactivex.CompletableEmitter.onError] throwable;
|
||||
* @return [Disposable] which is wrapping source completable to unsubscribe from it onDestroy.
|
||||
*/
|
||||
fun Completable.untilDestroy(
|
||||
onComplete: () -> Unit = Functions.EMPTY_ACTION::run,
|
||||
onError: (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 onSuccess Action which will raise on every [io.reactivex.MaybeEmitter.onSuccess] ()} item;
|
||||
* @param onError Action which will raise on every [io.reactivex.MaybeEmitter.onError] throwable;
|
||||
* @param onComplete Action which will raise on every [io.reactivex.MaybeEmitter.onComplete] item;
|
||||
* @return [Disposable] which is wrapping source maybe to unsubscribe from it onDestroy.
|
||||
*/
|
||||
fun <T> Maybe<T>.untilDestroy(
|
||||
onSuccess: (T) -> Unit = Functions.emptyConsumer<T>()::accept,
|
||||
onError: (Throwable) -> Unit = getActionThrowableForAssertion(Lc.getCodePoint(this, 2)),
|
||||
onComplete: () -> Unit = Functions.EMPTY_ACTION::run
|
||||
): Disposable
|
||||
|
||||
}
|
||||
|
|
@ -28,8 +28,8 @@ import java.lang.reflect.Type;
|
|||
import ru.touchin.roboswag.core.log.Lc;
|
||||
import ru.touchin.roboswag.core.observables.storable.Store;
|
||||
import ru.touchin.roboswag.core.utils.Optional;
|
||||
import rx.Completable;
|
||||
import rx.Single;
|
||||
import io.reactivex.Completable;
|
||||
import io.reactivex.Single;
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ import java.lang.reflect.Type;
|
|||
import ru.touchin.roboswag.core.observables.storable.Converter;
|
||||
import ru.touchin.roboswag.core.observables.storable.SameTypesConverter;
|
||||
import ru.touchin.roboswag.core.observables.storable.Storable;
|
||||
import ru.touchin.roboswag.core.observables.storable.concrete.NonNullStorable;
|
||||
import ru.touchin.roboswag.core.observables.storable.NonNullStorable;
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 01/09/2016.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,284 +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 ru.touchin.roboswag.components.utils.BaseLifecycleBindable;
|
||||
import ru.touchin.roboswag.components.utils.LifecycleBindable;
|
||||
import rx.Completable;
|
||||
import rx.Observable;
|
||||
import rx.Single;
|
||||
import rx.Subscription;
|
||||
import rx.functions.Action0;
|
||||
import rx.functions.Action1;
|
||||
|
||||
|
||||
/**
|
||||
* 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> Subscription untilStop(@NonNull final Observable<T> observable) {
|
||||
return baseLifecycleBindable.untilStop(observable);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Observable<T> observable, @NonNull final Action1<T> onNextAction) {
|
||||
return baseLifecycleBindable.untilStop(observable, onNextAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Observable<T> observable,
|
||||
@NonNull final Action1<T> onNextAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction) {
|
||||
return baseLifecycleBindable.untilStop(observable, onNextAction, onErrorAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Observable<T> observable,
|
||||
@NonNull final Action1<T> onNextAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction,
|
||||
@NonNull final Action0 onCompletedAction) {
|
||||
return baseLifecycleBindable.untilStop(observable, onNextAction, onErrorAction, onCompletedAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Single<T> single) {
|
||||
return baseLifecycleBindable.untilStop(single);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Single<T> single, @NonNull final Action1<T> onSuccessAction) {
|
||||
return baseLifecycleBindable.untilStop(single, onSuccessAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilStop(@NonNull final Single<T> single,
|
||||
@NonNull final Action1<T> onSuccessAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction) {
|
||||
return baseLifecycleBindable.untilStop(single, onSuccessAction, onErrorAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Subscription untilStop(@NonNull final Completable completable) {
|
||||
return baseLifecycleBindable.untilStop(completable);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Subscription untilStop(@NonNull final Completable completable, @NonNull final Action0 onCompletedAction) {
|
||||
return baseLifecycleBindable.untilStop(completable, onCompletedAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Subscription untilStop(@NonNull final Completable completable,
|
||||
@NonNull final Action0 onCompletedAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction) {
|
||||
return baseLifecycleBindable.untilStop(completable, onCompletedAction, onErrorAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Observable<T> observable) {
|
||||
return baseLifecycleBindable.untilDestroy(observable);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Observable<T> observable, @NonNull final Action1<T> onNextAction) {
|
||||
return baseLifecycleBindable.untilDestroy(observable, onNextAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Observable<T> observable,
|
||||
@NonNull final Action1<T> onNextAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction) {
|
||||
return baseLifecycleBindable.untilDestroy(observable, onNextAction, onErrorAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Observable<T> observable,
|
||||
@NonNull final Action1<T> onNextAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction,
|
||||
@NonNull final Action0 onCompletedAction) {
|
||||
return baseLifecycleBindable.untilDestroy(observable, onNextAction, onErrorAction, onCompletedAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Single<T> single) {
|
||||
return baseLifecycleBindable.untilDestroy(single);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Single<T> single, @NonNull final Action1<T> onSuccessAction) {
|
||||
return baseLifecycleBindable.untilDestroy(single, onSuccessAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> Subscription untilDestroy(@NonNull final Single<T> single,
|
||||
@NonNull final Action1<T> onSuccessAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction) {
|
||||
return baseLifecycleBindable.untilDestroy(single, onSuccessAction, onErrorAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Subscription untilDestroy(@NonNull final Completable completable) {
|
||||
return baseLifecycleBindable.untilDestroy(completable);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Subscription untilDestroy(@NonNull final Completable completable, @NonNull final Action0 onCompletedAction) {
|
||||
return baseLifecycleBindable.untilDestroy(completable, onCompletedAction);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Subscription untilDestroy(@NonNull final Completable completable,
|
||||
@NonNull final Action0 onCompletedAction,
|
||||
@NonNull final Action1<Throwable> onErrorAction) {
|
||||
return baseLifecycleBindable.untilDestroy(completable, onCompletedAction, onErrorAction);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -22,6 +22,7 @@ package ru.touchin.roboswag.components.views;
|
|||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.ColorInt;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.widget.AppCompatImageView;
|
||||
|
|
@ -89,7 +90,7 @@ public class MaterialLoadingBar extends AppCompatImageView {
|
|||
typedArray.recycle();
|
||||
|
||||
progressDrawable = new MaterialProgressDrawable(context, size);
|
||||
progressDrawable.setColor(color);
|
||||
setColor(color);
|
||||
progressDrawable.setStrokeWidth(strokeWidth);
|
||||
setScaleType(ScaleType.CENTER);
|
||||
setImageDrawable(progressDrawable);
|
||||
|
|
@ -107,4 +108,13 @@ public class MaterialLoadingBar extends AppCompatImageView {
|
|||
super.onDetachedFromWindow();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set color of loader.
|
||||
*
|
||||
* @param colorInt Color of loader to be set.
|
||||
*/
|
||||
public void setColor(@ColorInt final int colorInt) {
|
||||
progressDrawable.setColor(colorInt);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -23,6 +23,7 @@ import android.content.Context;
|
|||
import android.content.res.TypedArray;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.design.widget.TextInputLayout;
|
||||
import android.support.v7.widget.AppCompatEditText;
|
||||
import android.text.Editable;
|
||||
import android.text.InputType;
|
||||
|
|
@ -31,12 +32,15 @@ import android.text.TextWatcher;
|
|||
import android.text.method.SingleLineTransformationMethod;
|
||||
import android.text.method.TransformationMethod;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewParent;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ru.touchin.roboswag.components.R;
|
||||
import ru.touchin.roboswag.components.utils.Typefaces;
|
||||
import ru.touchin.roboswag.components.views.internal.AttributesUtils;
|
||||
import ru.touchin.roboswag.core.log.Lc;
|
||||
|
||||
|
|
@ -96,10 +100,6 @@ public class TypefacedEditText extends AppCompatEditText {
|
|||
} else {
|
||||
setSingleLine();
|
||||
}
|
||||
|
||||
if (!isInEditMode()) {
|
||||
setTypeface(Typefaces.getFromAttributes(context, attrs, R.styleable.TypefacedEditText, R.styleable.TypefacedEditText_customTypeface));
|
||||
}
|
||||
typedArray.recycle();
|
||||
if (inDebugMode) {
|
||||
checkAttributes(context, attrs);
|
||||
|
|
@ -107,12 +107,25 @@ public class TypefacedEditText extends AppCompatEditText {
|
|||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public InputConnection onCreateInputConnection(@NonNull final EditorInfo attrs) {
|
||||
final InputConnection inputConnection = super.onCreateInputConnection(attrs);
|
||||
if (inputConnection != null && attrs.hintText == null) {
|
||||
for (ViewParent parent = getParent(); parent instanceof View; parent = parent.getParent()) {
|
||||
if (parent instanceof TextInputLayout) {
|
||||
attrs.hintText = ((TextInputLayout) parent).getHint();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return inputConnection;
|
||||
}
|
||||
|
||||
private void checkAttributes(@NonNull final Context context, @NonNull final AttributeSet attrs) {
|
||||
final List<String> errors = new ArrayList<>();
|
||||
Boolean multiline = null;
|
||||
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.TypefacedEditText);
|
||||
AttributesUtils.checkAttribute(typedArray, errors, R.styleable.TypefacedEditText_customTypeface, true,
|
||||
"customTypeface required parameter");
|
||||
AttributesUtils.checkAttribute(typedArray, errors, R.styleable.TypefacedEditText_isMultiline, true,
|
||||
"isMultiline required parameter");
|
||||
if (typedArray.hasValue(R.styleable.TypefacedEditText_isMultiline)) {
|
||||
|
|
@ -139,12 +152,6 @@ public class TypefacedEditText extends AppCompatEditText {
|
|||
private void checkEditTextSpecificAttributes(@NonNull final TypedArray typedArray, @NonNull final Class androidRes,
|
||||
@NonNull final List<String> errors)
|
||||
throws NoSuchFieldException, IllegalAccessException {
|
||||
AttributesUtils.checkAttribute(typedArray, errors, AttributesUtils.getField(androidRes, "TextView_typeface"), false,
|
||||
"remove typeface and use customTypeface");
|
||||
AttributesUtils.checkAttribute(typedArray, errors, AttributesUtils.getField(androidRes, "TextView_textStyle"), false,
|
||||
"remove textStyle and use customTypeface");
|
||||
AttributesUtils.checkAttribute(typedArray, errors, AttributesUtils.getField(androidRes, "TextView_fontFamily"), false,
|
||||
"remove fontFamily and use customTypeface");
|
||||
AttributesUtils.checkAttribute(typedArray, errors, AttributesUtils.getField(androidRes, "TextView_singleLine"), false,
|
||||
"remove singleLine and use isMultiline");
|
||||
AttributesUtils.checkAttribute(typedArray, errors, AttributesUtils.getField(androidRes, "TextView_includeFontPadding"), false,
|
||||
|
|
@ -317,15 +324,6 @@ public class TypefacedEditText extends AppCompatEditText {
|
|||
super.setInputType(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets typeface from 'assets/fonts' folder by name.
|
||||
*
|
||||
* @param name Full name of typeface (without extension, e.g. 'Roboto-Regular').
|
||||
*/
|
||||
public void setTypeface(@NonNull final String name) {
|
||||
setTypeface(Typefaces.getByName(getContext(), name));
|
||||
}
|
||||
|
||||
public void setOnTextChangedListener(@Nullable final OnTextChangedListener onTextChangedListener) {
|
||||
this.onTextChangedListener = onTextChangedListener;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
|
||||
import ru.touchin.roboswag.components.R;
|
||||
import ru.touchin.roboswag.components.utils.Typefaces;
|
||||
import ru.touchin.roboswag.components.utils.UiUtils;
|
||||
import ru.touchin.roboswag.components.views.internal.AttributesUtils;
|
||||
import ru.touchin.roboswag.core.log.Lc;
|
||||
|
|
@ -93,9 +92,6 @@ public class TypefacedTextView extends AppCompatTextView {
|
|||
} else {
|
||||
setLineStrategy(lineStrategy);
|
||||
}
|
||||
if (!isInEditMode()) {
|
||||
setTypeface(Typefaces.getFromAttributes(context, attrs, R.styleable.TypefacedTextView, R.styleable.TypefacedTextView_customTypeface));
|
||||
}
|
||||
typedArray.recycle();
|
||||
if (inDebugMode) {
|
||||
checkAttributes(context, attrs);
|
||||
|
|
@ -107,8 +103,6 @@ public class TypefacedTextView extends AppCompatTextView {
|
|||
final List<String> errors = new ArrayList<>();
|
||||
LineStrategy lineStrategy = null;
|
||||
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.TypefacedTextView);
|
||||
AttributesUtils.checkAttribute(typedArray, errors, R.styleable.TypefacedTextView_customTypeface, true,
|
||||
"customTypeface required parameter");
|
||||
AttributesUtils.checkAttribute(typedArray, errors, R.styleable.TypefacedTextView_lineStrategy, true,
|
||||
"lineStrategy required parameter");
|
||||
if (typedArray.hasValue(R.styleable.TypefacedTextView_lineStrategy)) {
|
||||
|
|
@ -311,15 +305,6 @@ public class TypefacedTextView extends AppCompatTextView {
|
|||
Lc.assertion(new IllegalStateException(AttributesUtils.viewError(this, "Do not specify ellipsize use setLineStrategy instead")));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets typeface from 'assets/fonts' folder by name.
|
||||
*
|
||||
* @param name Full name of typeface (without extension, e.g. 'Roboto-Regular').
|
||||
*/
|
||||
public void setTypeface(@NonNull final String name) {
|
||||
setTypeface(Typefaces.getByName(getContext(), name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setText(@Nullable final CharSequence text, @Nullable final BufferType type) {
|
||||
super.setText(text, type);
|
||||
|
|
|
|||
|
|
@ -94,9 +94,7 @@ public final class AttributesUtils {
|
|||
public static void checkRegularTextViewAttributes(@NonNull final TypedArray typedArray, @NonNull final Class androidRes,
|
||||
@NonNull final Collection<String> errors, @NonNull final String lineStrategyParameterName)
|
||||
throws NoSuchFieldException, IllegalAccessException {
|
||||
checkAttribute(typedArray, errors, getField(androidRes, "TextView_typeface"), false, "remove typeface and use customTypeface");
|
||||
checkAttribute(typedArray, errors, getField(androidRes, "TextView_textStyle"), false, "remove textStyle and use customTypeface");
|
||||
checkAttribute(typedArray, errors, getField(androidRes, "TextView_fontFamily"), false, "remove fontFamily and use customTypeface");
|
||||
checkAttribute(typedArray, errors, getField(androidRes, "TextView_fontFamily"), true, "fontFamily required parameter");
|
||||
checkAttribute(typedArray, errors, getField(androidRes, "TextView_includeFontPadding"), false, "includeFontPadding forbid parameter");
|
||||
checkAttribute(typedArray, errors, getField(androidRes, "TextView_singleLine"), false,
|
||||
"remove singleLine and use " + lineStrategyParameterName);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,80 @@
|
|||
package ru.touchin.roboswag.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.support.annotation.IdRes;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import ru.touchin.roboswag.components.R;
|
||||
|
||||
public class Switcher extends FrameLayout {
|
||||
|
||||
@IdRes
|
||||
private final int defaultChild;
|
||||
|
||||
@Nullable
|
||||
private Animation inAnimation;
|
||||
|
||||
@Nullable
|
||||
private Animation outAnimation;
|
||||
|
||||
public Switcher(@NonNull final Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public Switcher(@NonNull final Context context, @Nullable final AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
final TypedArray array = getContext().obtainStyledAttributes(attrs, R.styleable.Switcher, 0, R.style.Switcher);
|
||||
inAnimation = AnimationUtils.loadAnimation(context, array.getResourceId(R.styleable.Switcher_android_inAnimation, View.NO_ID));
|
||||
outAnimation = AnimationUtils.loadAnimation(context, array.getResourceId(R.styleable.Switcher_android_outAnimation, View.NO_ID));
|
||||
defaultChild = array.getResourceId(R.styleable.Switcher_defaultChild, View.NO_ID);
|
||||
array.recycle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addView(@NonNull final View child, final int index, @Nullable final ViewGroup.LayoutParams params) {
|
||||
if (child.getId() == defaultChild || defaultChild == View.NO_ID && getChildCount() == 0) {
|
||||
child.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
child.setVisibility(View.GONE);
|
||||
}
|
||||
super.addView(child, index, params);
|
||||
}
|
||||
|
||||
public void showChild(@IdRes final int id) {
|
||||
boolean found = false;
|
||||
for (int index = 0; index < getChildCount(); index++) {
|
||||
final View child = getChildAt(index);
|
||||
if (child.getId() == id) {
|
||||
found = true;
|
||||
setVisibilityWithAnimation(child, View.VISIBLE);
|
||||
} else {
|
||||
setVisibilityWithAnimation(child, View.GONE);
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
}
|
||||
|
||||
private void setVisibilityWithAnimation(@NonNull final View view, final int targetVisibility) {
|
||||
final Animation animation = targetVisibility == View.VISIBLE ? inAnimation : outAnimation;
|
||||
if (view.getVisibility() != targetVisibility) {
|
||||
if (ViewCompat.isLaidOut(this) && animation != null) {
|
||||
view.startAnimation(animation);
|
||||
}
|
||||
view.setVisibility(targetVisibility);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<translate
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:duration="@integer/fragmentTransitionTime"
|
||||
android:fromXDelta="-50%p"
|
||||
android:interpolator="@android:anim/accelerate_interpolator"
|
||||
android:toXDelta="0"/>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<translate
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:duration="@integer/fragmentTransitionTime"
|
||||
android:fromXDelta="100%p"
|
||||
android:interpolator="@android:anim/decelerate_interpolator"
|
||||
android:toXDelta="0"/>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<translate
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:duration="@integer/fragmentTransitionTime"
|
||||
android:fromXDelta="0"
|
||||
android:interpolator="@android:anim/decelerate_interpolator"
|
||||
android:toXDelta="-50%p"/>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<translate
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:duration="@integer/fragmentTransitionTime"
|
||||
android:fromXDelta="0"
|
||||
android:interpolator="@android:anim/accelerate_interpolator"
|
||||
android:toXDelta="100%p"/>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<alpha
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:duration="@android:integer/config_mediumAnimTime"
|
||||
android:fromAlpha="0.0"
|
||||
android:interpolator="@android:anim/decelerate_interpolator"
|
||||
android:toAlpha="1.0"/>
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2013 The Android Open Source Project
|
||||
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.
|
||||
-->
|
||||
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:interpolator="@android:anim/decelerate_interpolator"
|
||||
android:fromAlpha="0.0" android:toAlpha="1.0"
|
||||
android:duration="@android:integer/config_mediumAnimTime" />
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<alpha
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:duration="@android:integer/config_mediumAnimTime"
|
||||
android:fromAlpha="1.0"
|
||||
android:interpolator="@android:anim/decelerate_interpolator"
|
||||
android:toAlpha="0.0"/>
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2013 The Android Open Source Project
|
||||
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.
|
||||
-->
|
||||
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:interpolator="@android:anim/decelerate_interpolator"
|
||||
android:fromAlpha="1.0" android:toAlpha="0.0"
|
||||
android:duration="@android:integer/config_mediumAnimTime" />
|
||||
|
|
@ -4,7 +4,6 @@
|
|||
<attr name="customTypeface" format="string"/>
|
||||
|
||||
<declare-styleable name="TypefacedTextView">
|
||||
<attr name="customTypeface"/>
|
||||
<attr name="lineStrategy" format="enum">
|
||||
<enum name="singleLineEllipsize" value="0"/>
|
||||
<enum name="singleLineMarquee" value="1"/>
|
||||
|
|
@ -17,15 +16,9 @@
|
|||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="TypefacedEditText">
|
||||
<attr name="customTypeface"/>
|
||||
<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"/>
|
||||
|
|
@ -33,4 +26,10 @@
|
|||
<attr name="materialLoadingBarStyle" format="reference"/>
|
||||
</declare-styleable>
|
||||
|
||||
</resources>
|
||||
<declare-styleable name="Switcher">
|
||||
<attr name="android:inAnimation" format="reference"/>
|
||||
<attr name="android:outAnimation" format="reference"/>
|
||||
<attr name="defaultChild" format="reference"/>
|
||||
</declare-styleable>
|
||||
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<integer name="fragmentTransitionTime">250</integer>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="Switcher">
|
||||
<item name="android:inAnimation">@anim/global_fade_in</item>
|
||||
<item name="android:outAnimation">@anim/global_fade_out</item>
|
||||
</style>
|
||||
</resources>
|
||||
Loading…
Reference in New Issue