Commit c9b24789 authored by Vladislav Bogdashkin's avatar Vladislav Bogdashkin 🎣

integrate butterknife

added base conductor classes
parent 18f761cd
......@@ -103,6 +103,10 @@ dependencies {
//conductor support
implementation "com.bluelinelabs:conductor-support:$conductorVersion"
//Butterknife
implementation "com.jakewharton:butterknife:$butterKnifeVersion"
kapt "com.jakewharton:butterknife-compiler:$butterKnifeVersion"
//Tests
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
......
package com.biganto.visual.roompark.conductor
import com.hannesdorfmann.mosby3.mvp.MvpView
/**
* Created by Vladislav Bogdashkin on 28.05.2018.
*/
interface BigantoBaseContract<in VS : BigantoBaseViewState> : MvpView {
fun render(viewState: VS)
}
abstract class BigantoBaseViewState
\ No newline at end of file
package com.biganto.visual.roompark.conductor
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import androidx.annotation.LayoutRes
import androidx.annotation.StringRes
import androidx.appcompat.app.ActionBar
import androidx.appcompat.app.AppCompatActivity
import butterknife.ButterKnife
import com.biganto.visual.roompark.R
import com.biganto.visual.roompark.conductor.mosby.mvi.BigantoMviController
import com.biganto.visual.roompark.view_utils.snackbar.ISnackBarProvider
import com.hannesdorfmann.mosby3.mvi.MviBasePresenter
import io.reactivex.disposables.CompositeDisposable
/**
* Created by Vladislav Bogdashkin on 28.05.2018.
*/
abstract class BigantoBaseController<VS : BigantoBaseViewState,V: BigantoBaseContract<VS>,P : MviBasePresenter<V,VS>>
: BigantoMviController<V, P> {
constructor():super()
constructor(args: Bundle):super(args)
protected val detachDisposable = CompositeDisposable()
abstract override fun createPresenter(): P
@LayoutRes
protected abstract fun getLayoutId(): Int
@StringRes
protected abstract fun getToolbarTitleId(): Int
protected abstract fun showBottomAppBar(): Boolean
// protected lateinit var toolBars:TopBottomActionBarsSupported
protected lateinit var snackbar: ISnackBarProvider
private fun setToolbarTitle() {
// if the Activity happens to be non-AppCompatActivity or it does not have ActionBar, simply do not set the title
(activity as? AppCompatActivity)?.supportActionBar?.apply {
title = resources?.getString(getToolbarTitleId())
setDisplayHomeAsUpEnabled(router.backstackSize > 1)
}
}
//TODO(injection)
private fun bottomToolbar() : ActionBar?=(activity as? AppCompatActivity)?.supportActionBar
override fun onDetach(view: View) {
detachDisposable.clear()
super.onDetach(view)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup): View {
val view = inflater.inflate(getLayoutId(), container, false)
// instantiate the view
ButterKnife.bind(this, view)
// toolBars=(activity as TopBottomActionBarsSupported)
snackbar = activity as ISnackBarProvider
//TODO(remove duplicated code:)
if (showBottomAppBar())
bottomToolbar()?.show()
if (!showBottomAppBar())
bottomToolbar()?.hide()
return view
}
protected fun View.hideKeyboard() {
val inputMethodManager = applicationContext?.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.hideSoftInputFromWindow(this.windowToken, 0)
}
protected fun View.showKeyboard() {
val inputMethodManager = applicationContext?.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.showSoftInput(this, 0)
}
protected fun showMessage(@StringRes msgResId: Int) {
Toast.makeText(this.applicationContext, msgResId, Toast.LENGTH_SHORT).show()
}
override fun handleBack(): Boolean {
return super.handleBack()
}
protected val isTablet
get() = resources?.getBoolean(R.bool.isTablet)?:false
protected val catalogSpansCount
get() = resources?.getInteger(R.integer.catalogSpans)?:1
protected val toursSpansCount
get() = resources?.getInteger(R.integer.catalogSpans)?:1
}
\ No newline at end of file
package com.biganto.visual.androidplayer.conductor.dialogs
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.LinearLayout
import android.widget.TextView
import androidx.annotation.LayoutRes
import com.biganto.visual.roompark.R
import com.bluelinelabs.conductor.Controller
/**
* Created by Vladislav Bogdashkin on 09.04.2019.
*/
const val ALERT_DIALOG_MESSAGE_KEY = "BIGANTO_ALERT_MESSAGE"
const val ALERT_DIALOG_BUTTON_TEXT_KEY = "BIGANTO_ALERT_OK_BUTTON_TEXT"
const val ALERT_DIALOG_BUTTON_CANCEL_TEXT_KEY = "BIGANTO_ALERT_CANCEL_BUTTON_TEXT"
fun formBundle(message:String): Bundle {
val b = Bundle()
b.putString(ALERT_DIALOG_MESSAGE_KEY,message)
return b
}
fun formBundle(message:String,buttonText:String): Bundle {
val b = Bundle()
b.putString(ALERT_DIALOG_MESSAGE_KEY,message)
b.putString(ALERT_DIALOG_BUTTON_TEXT_KEY,buttonText)
return b
}
fun formBundle(message:String,buttonText:String,cancelButtonText:String): Bundle {
val b = Bundle()
b.putString(ALERT_DIALOG_MESSAGE_KEY,message)
b.putString(ALERT_DIALOG_BUTTON_TEXT_KEY,buttonText)
b.putString(ALERT_DIALOG_BUTTON_CANCEL_TEXT_KEY,cancelButtonText)
return b
}
open class AlertDialogController : Controller {
constructor():super()
constructor(args: Bundle):super(args)
public constructor(alertMessage:String):super(formBundle(alertMessage))
public constructor(alertMessage:String,buttonText: String):super(formBundle(alertMessage, buttonText))
public constructor(alertMessage:String,buttonText: String,cancelText: String)
:super(formBundle(alertMessage, buttonText,cancelText))
public open var dismissActionDelegate = {router.popController(this);Unit}
@LayoutRes
fun getLayoutId() = R.layout.alert_dialog_screen_view
private lateinit var messageTextView : TextView
private lateinit var okButton : Button
private lateinit var dismissButton : Button
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup): View {
val view = inflater.inflate(getLayoutId(), container, false)
messageTextView=view.findViewById(R.id.alert_message_text_view)
val alertMessage = args.getString(ALERT_DIALOG_MESSAGE_KEY)
messageTextView.text=alertMessage
okButton=view.findViewById(R.id.alert_ok_button)
if (args.containsKey(ALERT_DIALOG_BUTTON_TEXT_KEY))
okButton.text=args.getString(ALERT_DIALOG_BUTTON_TEXT_KEY)
dismissButton=view.findViewById(R.id.alert_dismiss_button)
val bContainer=view.findViewById<LinearLayout>(R.id.buttons_container)
if (args.containsKey(ALERT_DIALOG_BUTTON_CANCEL_TEXT_KEY)){
bContainer.weightSum=2f
dismissButton.visibility=View.VISIBLE
dismissButton.text=args.getString(ALERT_DIALOG_BUTTON_CANCEL_TEXT_KEY)
dismissButton.setOnClickListener { router.popController(this) }
}else{
bContainer.weightSum=1f
dismissButton.visibility=View.GONE
}
okButton.setOnClickListener {
router.popController(this)
dismissActionDelegate.invoke()
}
return view
}
}
\ No newline at end of file
package com.biganto.visual.roompark.conductor.dialogs
import android.os.Bundle
import com.biganto.visual.androidplayer.conductor.dialogs.AlertDialogController
/**
* Created by Vladislav Bogdashkin on 09.04.2019.
*/
class ExceptionAlertDialogController: AlertDialogController {
constructor(args: Bundle) :super(args)
constructor(message: String, buttonText: String):super(message, buttonText)
override var dismissActionDelegate = {activity?.finish();Unit}
}
\ No newline at end of file
package com.biganto.visual.roompark.conductor.dialogs
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import com.biganto.visual.androidplayer.conductor.dialogs.AlertDialogController
import timber.log.Timber
/**
* Created by Vladislav Bogdashkin on 09.04.2019.
*/
class UpdateAppAlertDialogController: AlertDialogController {
constructor(args: Bundle) : super(args)
constructor(message: String, buttonText: String) : super(message, buttonText)
override var dismissActionDelegate = {
val appPackageName = activity?.packageName
var uri = Uri.parse("https://cdn.fishki.net/upload/post/201502/17/1432283/659d2f29de589d5d41252f18e9beb292.jpg")
// uri = Uri.parse("market://details?id=$appPackageName");
try {
startActivity(Intent(Intent.ACTION_VIEW, uri))
} catch (e: Exception) {
when (e) {
is android.content.ActivityNotFoundException -> {
uri = Uri.parse("https://play.google.com/store/apps/details?id=$appPackageName")
startActivity(Intent(Intent.ACTION_VIEW, uri))
}
else -> {
Timber.e("Fatal exception occured, while try to update app version!: $e")
activity?.finish()
}
}
}
Unit
}
}
\ No newline at end of file
package com.biganto.visual.roompark.conductor.dialogs.change_handler;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.bluelinelabs.conductor.changehandler.AnimatorChangeHandler;
/**
* Created by Vladislav Bogdashkin on 09.04.2019.
*/
public class DialogChangeHandler extends AnimatorChangeHandler {
private static final int FadeInDuration = 240;
public DialogChangeHandler() {
super(false);
}
@NonNull
@Override
protected Animator getAnimator(@NonNull ViewGroup container, @Nullable View from, @Nullable View to, boolean isPush, boolean toAddedToContainer) {
AnimatorSet animator = new AnimatorSet();
if (to != null && toAddedToContainer) {
ObjectAnimator anim = ObjectAnimator.ofFloat(to, View.ALPHA, 0, 1f);
anim.setDuration(FadeInDuration);
animator.play(anim);
}
return animator;
}
@Override
protected void resetFromView(@NonNull View from) {
// empty - Nothing to do with parent view
}
}
package com.biganto.visual.roompark.conductor.mosby.mvi;
import android.os.Bundle;
import com.hannesdorfmann.mosby3.MviConductorDelegateCallback;
import com.hannesdorfmann.mosby3.MviController;
import com.hannesdorfmann.mosby3.mvi.MviPresenter;
import com.hannesdorfmann.mosby3.mvp.MvpView;
public abstract class BigantoMviController<V extends MvpView, P extends MviPresenter<V, ?>>
extends MviController<V,P> implements MvpView, MviConductorDelegateCallback<V, P> {
public BigantoMviController() {super();}
public BigantoMviController(Bundle args) {
super(args);
}
/**
* Переопределяем метод, чтобы подставить кастомную реализацию LifeCycleListener'a, с учетом проверки связанного
* презентера на null - значение
*/
protected LifecycleListener getMosbyLifecycleListener() {
return new BigantoMviConductorLifecycleListener<>(this);
}
}
package com.biganto.visual.roompark.conductor.mosby.mvi
import android.util.Log
import android.view.View
import androidx.annotation.NonNull
import com.bluelinelabs.conductor.Controller
import com.hannesdorfmann.mosby3.MviConductorDelegateCallback
import com.hannesdorfmann.mosby3.mvi.MviPresenter
import com.hannesdorfmann.mosby3.mvp.MvpView
/**
* В связи с тем, что в исходной реализации MviConductorLifeCycleListener презентер приватное поле,
* есть необходимость рконструировать дефолтный класс библиотеки с внесением изменений - проверкой поля presenter на
* null значение при вызове методов attachView и destroy.
*
* Суть проблемы кроется в том, что при реализации некоторых кейсов (например, когда мы желаем сохранять стек
* навигации среди нескольких роутеров одновроеменно), мы сохраняем ссылку на контроллер, даже в тех случаях когда
* презентер уничтожен (вьюха в mosbyMVI у нас и так отвязана от жизненного цикла контроллера) и при каскадном
* удалении бэкстека, у нас может получиться таким образом, что презентер уже был уничтожен ранее. Т.о. необходима
* проверка на наличие инстанса привязанного к контроллеру презентера.
*
* * **Детально по ссылке:** [mosby issue](https://github.com/sockeqwe/mosby-conductor/issues/38)
*/
class BigantoMviConductorLifecycleListener<V : MvpView, P : MviPresenter<V, *>>
/**
* Instantiate a new Mosby MVP Listener
*
* @param callback [MviConductorDelegateCallback] to set presenter. Typically the
* controller
* himself.
*/
@JvmOverloads constructor(private var callback: MviConductorDelegateCallback<V, P>?,
private val keepPresenterInstance: Boolean = true) : Controller.LifecycleListener() {
private var presenter: P? = null
override fun postCreateView(@NonNull controller: Controller, @NonNull view: View) {
var viewStateWillBeRestored = false
if (presenter == null) {
// Process death,
// hence no presenter with the given viewState id stored, although we have a viewState id
presenter = callback!!.createPresenter()
if (DEBUG) {
Log.d(
DEBUG_TAG,
"New Presenter instance created for $controller. Presenter: $presenter")
}
} else {
viewStateWillBeRestored = true
if (DEBUG) {
Log.d(
DEBUG_TAG, "Reusing Presenter instance for controller "
+ controller
+ ". Presenter: "
+ presenter)
}
}
val viewMpv = callback?.mvpView ?: throw NullPointerException(
"MvpView returned from getMvpView() is null. Returned by " + controller.activity!!)
if (viewStateWillBeRestored) {
callback!!.setRestoringViewState(true)
}
presenter?.attachView(viewMpv)
if (viewStateWillBeRestored) {
callback!!.setRestoringViewState(false)
}
if (DEBUG) {
Log.d(
DEBUG_TAG,
"MvpView attached to Presenter. MvpView: $view Presenter: $presenter")
}
}
override fun preDestroyView(@NonNull controller: Controller, @NonNull view: View) {
presenter?.detachView()
if (DEBUG) {
Log.d(
DEBUG_TAG, "detached MvpView from Presenter. MvpView "
+ callback!!.mvpView
+ " Presenter: "
+ presenter)
}
}
override fun postDestroy(@NonNull controller: Controller) {
if (DEBUG) {
Log.d(
DEBUG_TAG, "Presenter destroyed because View destroyed. MvpView "
+ callback!!.mvpView
+ " Presenter: "
+ presenter)
}
presenter?.destroy()
presenter = null
callback = null
}
companion object {
private var DEBUG = false
private const val DEBUG_TAG = "MviLifecycleListener"
/**
* Determines whether or not a Presenter Instance should be kept
*
* @param keepPresenterInstance true, if the delegate has enabled keep
*/
internal fun retainPresenterInstance(keepPresenterInstance: Boolean, controller: Controller): Boolean {
return keepPresenterInstance && (controller.activity!!.isChangingConfigurations
|| !controller.activity!!.isFinishing) && !controller.isBeingDestroyed
}
}
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/alert_holder"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorOpacityBackground"
android:clickable="true"
android:focusable="auto"
android:focusableInTouchMode="true"
android:orientation="vertical">
<androidx.cardview.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="64dp"
android:layout_marginTop="64dp"
android:layout_marginEnd="64dp"
android:layout_marginBottom="64dp"
app:cardCornerRadius="6dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias=".4">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/alert_message_text_view"
style="@style/Default_TextView.Error_Text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="16dp" />
<LinearLayout
android:id="@+id/buttons_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="2">
<Button
android:id="@+id/alert_ok_button"
style="@style/DefaultButton.DismissButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="asdasdasd" />
<Button
android:id="@+id/alert_dismiss_button"
style="@style/DefaultButton.DismissButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:visibility="visible" />
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="isTablet">true</bool>
<integer name="catalogSpans">3</integer>
<integer name="toursCeilSpans">4</integer>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="isTablet">true</bool>
<integer name="catalogSpans">2</integer>
<integer name="toursCeilSpans">2</integer>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="isTablet">true</bool>
<integer name="catalogSpans">6</integer>
<integer name="toursCeilSpans">5</integer>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="isTablet">true</bool>
<integer name="catalogSpans">3</integer>
<integer name="toursCeilSpans">4</integer>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="isTablet">false</bool>
<integer name="catalogSpans">1</integer>
<integer name="toursCeilSpans">2</integer>
</resources>
\ No newline at end of file
......@@ -100,6 +100,22 @@
</style>
<style name="DefaultButton" parent="Widget.MaterialComponents.Button.OutlinedButton">
<item name="strokeColor">@color/colorAccent</item>
<item name="strokeWidth">0dp</item>
<item name="cornerRadius">0dp</item>
<item name="rippleColor">@color/colorAccentSecondary</item>
<item name="boxBackgroundColor">@color/colorCommonBackground</item>
<item name="android:textSize">@dimen/common_text</item>
<item name="android:textColor">@color/colorAccent</item>
<item name="errorTextAppearance">@style/Auth.EditText</item>
<item name="hintTextColor">@color/colorNoticeText</item>
</style>
<style name="DefaultButton.DismissButton">
<item name="android:text">"СКРЫТЬ"</item>
</style>
<style name="AuthButton" parent="Widget.MaterialComponents.Button.OutlinedButton">
<item name="strokeColor">@color/colorAccent</item>
<item name="strokeWidth">4dp</item>
......@@ -246,6 +262,10 @@
<item name="android:textColor">@color/colorHeaderText</item>
</style>
<style name="Default_TextView.Error_Text">
<item name="android:textColor">@color/colorError</item>
</style>
<style name="Default_TextView.Inverted_Text">
<item name="android:textColor">@color/colorInvertedText</item>
</style>
......
......@@ -20,4 +20,5 @@ ext {
picassoVersion = '2.71828'
rxJavaVersion = '3.0.0-RC3'
mosbyMviConductorVersion = '3.1.0'
butterKnifeVersion = '10.2.0'
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment