I'm trying to create a new Matcher for espresso in order to be able to select a list item. This works fine for a simple class like Office. See this example.
private fun withOffice(title: String): Matcher<Any> {
return object : BoundedMatcher<Any, Office>(Office::class.java) {
override fun describeTo(description: Description?) {
description?.appendText("with title '$title'");
}
public override fun matchesSafely(office: Office): Boolean {
return office.name == title
}
}
}
However things get more difficult when bringing in generics, like in this class.
class KeyTranslationPair<F, S> extends Pair<F, S>
Trying to create a Matcher like this
private fun withCompanyType(companyType: CompanyType): Matcher<Any> {
return object : BoundedMatcher<Any, KeyTranslationPair<CompanyType, String>>(KeyTranslationPair<CompanyType, String>::class.java) {
override fun describeTo(description: Description?) {
description?.appendText("with companyType '$companyType'");
}
public override fun matchesSafely(keyTranslationPair: KeyTranslationPair<CompanyType, String>): Boolean {
return keyTranslationPair.key == companyType
}
}
}
results in the following error
My assumption is that kotlin get things mixed up with the java type system.
Maybe someone has an idea here.
That's because KeyTranslationPair<CompanyType,Strnig> is not a class, when is saying class means KeyTranslationPair::class.java, so, you can do it like :
return object : BoundedMatcher<Any, KeyTranslationPair<*,*>>(KeyTranslationPair::class.java)
And you are saying that you don't know what's inside of the KeyTranslationPair, and since it's a Generic you'll have to change the matchesSafely to :
override fun matchesSafely(item: KeyTranslationPair<*, *>?): Boolean {
return item?.key == companyType
}
And also you can check if Key is an instance of CompanyType doing :
override fun matchesSafely(item: KeyTranslationPair<*, *>?): Boolean {
if(item?.key is CompanyType){
return item.key == companyType
}
return false
}
Hope it helps.
Related
I have created a Generic Fragment class to handle all type of responses from server. I want to do some sort of DataTableProvider<*> to hanle any type of response.
How could I achieve this.
class TestFragmentActivity : AppCompatActivity(), DataTableProvider<Any> {
protected val mTabPatientsFragment = TabPatientsFragment()
protected val mTabObservationsFragment = TabObservationsFragment()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test_fragment)
replaceFragment()
}
private fun replaceFragment(){
supportFragmentManager.beginTransaction().replace(R.id.frame_container,
mTabPatientsFragment).commit()
}
override fun getDataTableListener(mTableFragment: DataTableFragment<Any>): DataTableListener<Any> {
val dataTableId = mTableFragment.dataTableId
if (dataTableId.equals("observations"))
return mTabObservationsFragment
else return mTabPatientsFragment
}
override fun getDataTableConfig(mTableFragment: DataTableFragment<Any>): DataTableConfig {
val dataTableId = mTableFragment.dataTableId
val config = DataTableConfig()
config.noRecordCell = R.layout.cell_no_record
config.showSearch = false
config.showAddButton = false
if (dataTableId.equals("observations"))
{
config.cellResourceId = R.layout.home_observation_cell
} else config.cellResourceId = R.layout.home_patient_cell
return config
}
}
getDataTableListener callback in above fragment has error type mismatch required DataTableListener found TabObservationFragment
TabObservationFragment
class TabObservationFragment : AppBaseFragment(),DataTableListener<Observation>
TabPatientFragment
class TabPatientFragment : AppBaseFragment(),DataTableListener<Patient>
How could I set it to work for all type of responses.
I tried DataTableListener<*> but could not achieve
The error states
projections are not allowed for immediate arguments of a supertype
How could I use DataTableProvider<*> to work for all type of responses
Edit
I have couple of fragment with fragmentViewpager inside TestFragmentActivity .
I have got a structure that helps to implement pagination ,search and implement everything in a fragment. But according to that structure DataTableProvider must be handle in activity and basis of tableId I updated callback of getDataTableListener and getDataTableListener
The above callback should return some type of
Is there a way to achieve callback like below
override fun getDataTableConfig(mTableFragment: DataTableFragment<*>?): DataTableConfig?
override fun getDataTableListener(mTableFragment: DataTableFragment<*>?): DataTableListener<*>?
Quick answer, use "out" modifier:
fun getDataTableListener(mTableFragment: DataTableFragment<Any>): DataTableListener<out Any>
Long answer:
What you are looking for is variance, which can you read about in official kotlin docs:
https://kotlinlang.org/docs/reference/generics.html
Because for example List interface looks like this:
public interface List<out E>
You can do assigement like this:
val list: List<Any> = listOf(1,2,3)
But it is not possible to do:
val mutableList : MutableList<Any> = listOf(1,2,3)
Because MutableList doesn't have "out" modifier. It makes sense, because MutableList can be changed, to MutableList you could add for example String, but it already points to List.
In your example you can use "out" modifier, if, and only if, your DataTableListener doesn't use generic type as input. For example:
interface DataTableListener<T>{
fun assignValue(t:T)
fun getValue():T
}
With interface like above, you still could use "out" modifier in your function, but you won't be able to execute "assignValue" function.
Whole example:
class Patient
class Observation
class DataTableFragment<T>
interface DataTableListener<T> {
fun assignValue(t: T)
fun getValue(): T
}
class TabObservationFragment : DataTableListener<Observation> {
override fun getValue(): Observation {
TODO("Not yet implemented")
}
override fun assignValue(t: Observation) {
TODO("Not yet implemented")
}
}
class TabPatientFragment : DataTableListener<Patient> {
override fun getValue(): Patient {
}
override fun assignValue(t: Patient) {
TODO("Not yet implemented")
}
}
val mTabObservationsFragment = TabObservationFragment()
val mTabPatientsFragment = TabPatientFragment()
fun getDataTableListener(mTableFragment: DataTableFragment<Any>): DataTableListener<out Any> {
val test = "observations"
if (test == "observations")
return mTabObservationsFragment
else return mTabPatientsFragment
}
fun getIt() {
val listener = getDataTableListener(DataTableFragment())
listener.assignValue("test")
}
I can transform a list with map in one line like this:
override suspend fun getAllTxtFile(): List<TxtFileModel> {
return someDao.getAllTxtFile().map { with(txtFileDataMapper) { it.fromEntityToDomain() } }
}
But I don't know how to do it for single class object:
override suspend fun getTxtFile(txtFileName: String?): TxtFileModel {
val txtFile = someDao.getTxtFile(txtFileName)
val txtFileModel = with(txtFileDataMapper) { txtFile.fromEntityToDomain() }
return txtFileModel
}
So, the difference is that for list I can return a value in one line but for a single object only in 3 lines.
That is not big deal but I wonder if exist some anologue for .map but for a single object. I want something like .mapSingleObject:
override suspend fun getTxtFile(txtFileName: String?): TxtFileModel {
return someDao.getTxtFile(txtFileName).mapSingleObject{ with(txtFileDataMapper) { it.fromEntityToDomain() } }
}
You can use some functions from Kotlin standard library, such as let, run, or with.
If function fromEntityToDomain() is a TxtFile extension function defined within the txtFileDataMapper's class, you can use functions mentioned above:
override suspend fun getTxtFile(txtFileName: String?): TxtFileModel? =
someDao.getTxtFile(txtFileName)?.let {
with(txtFileDataMapper) { it.fromEntityToDomain() }
}
How can I create a class which could be more reusable with enum classes, as I might have few more classes later on? My point is to make it more reusable, flexible and global for other usage.
enum class PaymentMethodType(val type: String) {
PAYPAL("Paypal"),
VISA("Visa"),
MASTERCARD("MasterCard"),
VISA_DEBIT("VISA Debit"),
LPQ_CREDIT("Lpq Credit");
companion object {
private val TAG: String = this::class.java.simpleName
fun fromString(name: String): PaymentMethodType? {
return getEnumFromString(PaymentMethodType::class.java, name)
}
private inline fun <reified T : Enum<T>> getEnumFromString(c: Class<T>?, string: String?): T? {
if (c != null && string != null) {
try {
return enumValueOf<T>(
string.trim()
.toUpperCase(Locale.getDefault()).replace(" ", "_")
)
} catch (e: IllegalArgumentException) {
Log.e(TAG, e.message)
}
}
return null
}
}
}
You can generalize your getEnumFromString function by creating an interface and having your companion object implementing it. An extension on this interface will let you call the function directly on the companion of your enum class.
This will do the trick:
interface EnumWithKey<T : Enum<T>, K> {
val T.key: K
}
/* The reified type parameter lets you call the function without explicitly
* passing the Class-object.
*/
inline fun <reified T : Enum<T>, K> EnumWithKey<T, K>.getByKey(key: K): T? {
return enumValues<T>().find { it.key == key }
}
Now you can create your PaymentMethodType like this:
enum class PaymentMethodType(val type: String) {
PAYPAL("Paypal"),
VISA("Visa"),
MASTERCARD("MasterCard"),
VISA_DEBIT("VISA Debit"),
LPQ_CREDIT("Lpq Credit");
companion object : EnumWithKey<PaymentMethodType, String> {
// Just define what the key is
override val PaymentMethodType.key
get() = type
}
}
And voila, now you can do this:
println(PaymentMethodType.getByKey("Paypal")) // Prints PAYPAL
The EnumWithKey interface can now be reused by just having the companion object of an enum implementing it.
Well? How about this code?
enum class PaymentMethodType(val type: String) {
PAYPAL("Paypal"),
VISA("Visa"),
MASTERCARD("MasterCard"),
VISA_DEBIT("VISA Debit"),
LPQ_CREDIT("Lpq Credit");
companion object {
private val TAG: String = PaymentMethodType::class.simpleName
fun fromString(name: String?): PaymentMethodType? {
val maybeType = PaymentMethodType.values().firstOrNull { it.type == name }
if (maybeType == null) {
Log.e(TAG, "No corresponding PaymentMethodType for $name")
}
return maybeType
}
}
}
Just made getEnumFromString method simpler like this way.
Moreover, if you want to make your PaymentMethodType more "reusable, flexible and global", add some abstract method onto your PaymentMethodType or consider using Sealed class in this case. We can guess that many payment methods require their own protocols, and implementing it by enum requires an externalised when or if-else branch to do so. For example, the code should be looks like this:
fun paymentProcessor(payment: PaymentMethodType): Boolean {
return when (payment) {
PAYPAL -> { processPaypalPayment() }
VISA -> { processVisaPayment() }
// ...
}
}
which is not bad unless numbers of payment methods are limited but not quite desirable. We can remove this insidious if or when keyword like this way(retaining enum class approach):
enum class PaymentMethodType(val type: String) {
PAYPAL("Paypal") {
override fun processPayment(): Boolean {
TODO("Not implemented.")
}
},
VISA("Visa") {
override fun processPayment(): Boolean {
TODO("Not implemented.")
}
},
// ... more types ...
;
abstract fun processPayment(): Boolean
// ...
}
With either approach, we can eliminate when keyword in paymentProcessor method I demonstrated like this:
fun paymentProcessor(payment: PaymentMethodType): Boolean {
return payment.processPayment()
}
I don't explain sealed class approach since the code is not much different compare to enum class approach in this case. The official document may help.
Hope this helps.
Get all enum values with PaymentMethodType.values(), then use find() to get the one you need:
fun fromString(type: String): PaymentMethodType? = PaymentMethodType.values().find { it.type.toLowerCase() == type.toLowerCase() }
(This question is asked under an Android context, but answerable with only Kotlin knowledge.)
(Also, I found some answers in SO regarding same question in Java8 Lambda, but I can't find one regarding Kotlin.)
Like many people do - I am now using RxJava with Retrofit to do network calls.
And recently I have switched to using Kotlin.
I have declared my own Observer for centralizing error handling.
This is my APIResponseObserver<T> extends Observer<T>: (Simplified)
abstract class APIResponseObserver<T> : Observer<T> {
constructor(fragment: BaseFragment) : super(fragment)
constructor(activity: BaseActivity) : super(activity)
override fun onSubscribe(d: Disposable) {
fragment?.addToCompositeDisposable(d)
activity?.addToCompositeDisposable(d)
}
override fun onError(e: Throwable) {
val fm = if (activity == null) fragment?.childFragmentManager else activity?.supportFragmentManager
if (e is APIManager.APIException && e.message != null) {
fm?.showSimpleTextDialog(e.message!!)
} else {
val errorMsg = if (activity == null) fragment?.getString(R.string.network_error) else activity?.getString(R.string.network_error)
errorMsg?.let {
fm?.showSimpleTextDialog(it)
}
}
}
override fun onComplete() {
}
}
As you can see, only onNext() is not overridden.
And this is how I am using it now:
APIManager.getSomeBoolean().subscribe(object : APIResponseObserver<Boolean>(this) {
override fun onNext(t: Boolean) {
if (context == null) return
//Do something with the boolean
}
})
After using one week of Kotlin, the above usage really feels clumsy to me.
What I expect is using lambda like below:
APIManager.getSomeBoolean().subscribe(APIResponseObserver<Boolean>(this) {t: Boolean
if (context == null) return
//Do something with the boolean
})
So that I don't need to write object : and also override fun onNext(t: Boolean) {.
But, is this possible? If yes, how can I achieve this?
You can make you class non-abstract and add lamdba for observing onNext events as last parameter of your constructors:
class APIResponseObserver<T> : Observer<T> {
private val onNextCallback: (T) -> Unit
constructor(fragment: BaseFragment, onNext: (T) -> Unit) : super(fragment) {
onNextCallback = onNext
}
constructor(activity: BaseActivity, onNext: (T) -> Unit) : super(activity) {
onNextCallback = onNext
}
... other methods ...
override fun onNext(t: T) {
onNextCallback.invoke(t)
}
}
Kotlin allows to put lambda implementation outside parentheses in case if it is last argument in your method/constructor. All you need to do is to call passed lambda inside overridden onNext method.
In the end it allows you to write exactly same code that you expect:
APIResponseObserver<Boolean>(this) { t: Boolean ->
if (context == null) return#APIResponseObserver
//Do something with the boolean
}
I am using https://github.com/mkoslacz/Moviper for my app for Android Tv Box. There is only one activity with multiple child fragments. I want to propagate the onKeyDown to child fragments only if event is not already handled. It was easy to do without using RxJava. I could return a boolean to tell that event is already handled or not. Is there a way I can handle it using RxJava? I am using Kotlin as source language. Here is something I am trying to do.
class MainActivity : ViperAiPassiveActivity<HomeContract.View>(), HomeContract.View {
private val keyPressSubject = BehaviorSubject.create<KeyEvent>()
private lateinit var predicate: (KeyEvent) -> Boolean
override fun keyPresses(predicate: (KeyEvent) -> Boolean): Observable<KeyEvent> {
this.predicate = predicate
return keyPressSubject
}
override fun createPresenter() = HomePresenter()
override fun getLayoutId(): Int {
return R.layout.activity_main
}
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
event?.run {
if (predicate(event)) {
keyPressSubject.onNext(event)
return true
}
}
return super.onKeyDown(keyCode, event)
}
}
I ended up creating my own Event Handler (although its not using RxJava but it does the job in my case)
object KeyEventManager {
private val handlers: MutableMap<Int, (KeyEvent?) -> Boolean> = ArrayMap()
fun register(handler: (KeyEvent?) -> Boolean): Int {
val id = (handlers.keys.max() ?: 0) + 1
handlers.put(id, handler)
return id
}
fun unregister(id: Int) {
handlers.remove(id)
}
fun postEvent(event: KeyEvent?): Boolean {
for (key in handlers.keys) {
handlers.get(key)?.run {
if (invoke(event)) {
return true
}
}
}
return false
}
}