Categories
discuss

FragmentStateAdapter not recreating currentFragment after notifyDataSetChanged

I have a FragmentStateAdapter as the adapter of a ViewPager2.

class DefaultFragmentStateAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {

    var items = listOf<Fragment>()
       set(value) {
           field = value
           notifyDateSetChanged()
       }

    override fun getItemCount(): Int = items.size

    override fun createFragment(position: Int): Fragment = items[position]
}

The adapters item can change depending on the date a user has selected, so it needs to be dynamical. What happens is that after I call adapter.items = {new_fragments} current fragment is not reloaded, only after I go to one of the last pages in the ViewPager and return to the first page can I see the updated fragment. The current fragment does not update when notifyDataSetChanged is called.

How can I manage to update current displayed fragment?

Answer

I had to override the following two methods…

override fun getItemId(position: Int): Long {
    return items[position].id
}

override fun containsItem(itemId: Long): Boolean = items.any { it.id == itemId }

… to make it work!

Categories
discuss

What is the purpose of kotlin contract

Was reading the apply function code source and found

contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }

and contract has an empty body, experimental

@ContractsDsl
@ExperimentalContracts
@InlineOnly
@SinceKotlin("1.3")
@Suppress("UNUSED_PARAMETER")
public inline fun contract(builder: ContractBuilder.() -> Unit) { }

what is the real purpose of contract and is it here to stay in the next versions?

Answer

What is the real purpose of contract

The real purpose of Kotlin contracts is to help the compiler to make some assumptions which can’t be made by itself. Sometimes the developer knows more than the compiler about the usage of a certain feature and that particular usage can be taught to the compiler.

I’ll make an example with callsInPlace since you mentioned it.

Imagine to have the following function:

fun executeOnce(block: () -> Unit) {
  block()
}

And invoke it in this way:

fun caller() {
  val value: String 
  executeOnce {
      // It doesn't compile since the compiler doesn't know that the lambda 
      // will be executed once and the reassignment of a val is forbidden.
      value = "dummy-string"
  }
}

Here Kotlin contracts come in help. You can use callsInPlace to teach the compiler about how many times that lambda will be invoked.

@OptIn(ExperimentalContracts::class)
fun executeOnce(block: ()-> Unit) {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
}

@OptIn(ExperimentalContracts::class)
fun caller() {
  val value: String 
  executeOnce {
      // Compiles since the val will be assigned once.
      value = "dummy-string"
  }
}

is it here to stay in the next versions?

Who knows. They are still experimental after one year, which is normal for a major feature. You can’t be 100% sure they will be out of experimental, but since they are useful and they are here since one year, in my opinion, likely they’ll go out of experimental.

Categories
discuss

HowTo Nest ViewPager2 within a fragment

I am trying to put a ViewPager2 with 2 tabs(2 fragments) within my main fragment.

My fragment class extends Fragment and I need it to contain a ViewPager2 with fragments implemented inside.
I couldn’t find guides explaining this clearly on the web as all of them are being implemented within classes extending FragmentActivity and not Fragment.

For example: https://developer.android.com/training/animation/screen-slide-2#fragment

Answer

ViewPager2 is an improved version of the ViewPager library that offers enhanced functionality and addresses common difficulties with using ViewPager.
Such implementations for nesting a ViewPager2 within a fragment are missing on the web as Android Developer guide shows an implementation within activity.

1) Your main fragment layout must have a ViewPager2 added to its XML file as follows:

<com.google.android.material.tabs.TabLayout
        android:id="@+id/tabs"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
<!-- Tabs widget can be removed if you don't need tabs on top of pager-->
    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/myPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/tabs" />

2) The next step is to prepare our FragmentStateAdapter to set it as the ViewPager2 adapter:

public class ViewPagerAdapter extends FragmentStateAdapter {

    private final Fragment[] mFragments = new Fragment[] {//Initialize fragments views
//Fragment views are initialized like any other fragment (Extending Fragment)
                new FirstPagerFrag(),//First fragment to be displayed within the pager tab number 1
                new SecondPagerFrag(),
        };
        public final String[] mFragmentNames = new String[] {//Tabs names array
                "First Tab",
                "SecondTab"
        };

        public ViewPagerAdapter(FragmentActivity fa){//Pager constructor receives Activity instance
            super(fa);
    }

    @Override
    public int getItemCount() {
        return mFragments.length;//Number of fragments displayed
    }

    @Override
    public long getItemId(int position) {
        return super.getItemId(position);
    }

    @NonNull
    @Override
    public Fragment createFragment(int position) {
        return mFragments[position];
    }
}

3) Attaching the adapter to ViewPager2 This part is implemented within our fragment that contains the ViewPager2:

private ViewPager2 mViewPager;
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

    mViewPager = view.findViewById(R.id.queueContainer);//Get ViewPager2 view
    mViewPager.setAdapter(new ViewPagerAdapter(getActivity()));//Attach the adapter with our ViewPagerAdapter passing the host activity

    TabLayout tabLayout = view.findViewById(R.id.tabs);
    new TabLayoutMediator(tabLayout, mViewPager,
            new TabLayoutMediator.TabConfigurationStrategy() {
                @Override
                public void onConfigureTab(@NonNull TabLayout.Tab tab, int position) {
                    tab.setText(((ViewPagerAdapter)(mViewPager.getAdapter())).mFragmentNames[position]);//Sets tabs names as mentioned in ViewPagerAdapter fragmentNames array, this can be implemented in many different ways.
                }
            }
    ).attach();
}
Categories
discuss

Java 11 requirements check failed for JDK 1.8 or greater with Cordova

When I run ““cordova run android“` in the path of my project I get

Android Studio project detected
cordova-android-support-gradle-release: Android platform: V7+
cordova-android-support-gradle-release: Wrote custom version '27.+' to /path/to/platforms/android/app/build.gradle
cordova-android-support-gradle-release: Wrote custom version '27.+' to /path/to/cordova-android-support-gradle-release/norsan-cordova-android-support-gradle-release.gradle
ANDROID_HOME=/usr/lib/android-sdk
JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64
Requirements check failed for JDK 1.8 or greater

My version of Java

$ java --version
openjdk 11.0.5 2019-10-15
OpenJDK Runtime Environment (build 11.0.5+10-post-Ubuntu-0ubuntu1.119.04)
OpenJDK 64-Bit Server VM (build 11.0.5+10-post-Ubuntu-0ubuntu1.119.04, mixed mode, sharing)

My version of javac

$ javac --version
javac 11.0.5

My version of Cordova

$ cordova --version
9.0.0 (cordova-lib@9.0.1)

My version of Node

$ node --version
v10.15.2

My version of Nodejs

$ nodejs --version
v10.15.2

My version of NPM

$ npm --version
5.8.0

My version of Gradle

$ gradle -version
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.codehaus.groovy.reflection.CachedClass (file:/usr/share/java/groovy-all.jar) to method java.lang.Object.finalize()
WARNING: Please consider reporting this to the maintainers of org.codehaus.groovy.reflection.CachedClass
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

------------------------------------------------------------
Gradle 4.4.1
------------------------------------------------------------

Build time:   2012-12-21 00:00:00 UTC
Revision:     none

Groovy:       2.4.16
Ant:          Apache Ant(TM) version 1.10.5 compiled on March 28 2019
JVM:          11.0.5 (Private Build 11.0.5+10-post-Ubuntu-0ubuntu1.119.04)
OS:           Linux 5.0.0-38-generic amd64

I found that android-studio --version didn’t do anything but silently wait, but when I open the program through the graphical user interface I see that the version is 3.6.1.

My version of androidsdk is Unknown… Not sure why.

$ androidsdk --version
SDK_ROOT=/home/galen/snap/androidsdk/21/AndroidSDK
Picked up _JAVA_OPTIONS: -Duser.home=/home/galen/snap/androidsdk/21
Unknown version

My version of Ubuntu

$ cat /etc/*release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=19.04
DISTRIB_CODENAME=disco
DISTRIB_DESCRIPTION="Ubuntu 19.04"
NAME="Ubuntu"
VERSION="19.04 (Disco Dingo)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 19.04"
VERSION_ID="19.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=disco
UBUNTU_CODENAME=disco

My understanding is that Java 11 comes later than JDK 1.8, so why does the requirements check fail? And, how do I fix this requirements check failure?

Answer

You are correct that Java 11 is more recent than JDK 1.8 and that it should work in theory.

However, cordova explicitly requires JDK 1.8 still:

Android doesn’t use oracles java but their own implementation, which at the moment is like java 8. So this is an android limitation, not something cordova can fix.

(cf. also issue 510)

Their development guide explicitly mentions JDK 1.8 as well:

Java Development Kit (JDK)

Install Java Development Kit (JDK) 8.

So the fix for now would be installing JDK 1.8 (if you go for Oracle note their recently changed license terms). You may watch PR 928 that attempts to ease that restriction, though.

Categories
discuss

Why are types in dto not visible in swagger?

I’m setting up swagger document in my small Nest.js app according to this documentation: https://docs.nestjs.com/recipes/swagger

How do I setup dto to correctly show schema in swagger? To be more specific, nested types. It shows only top level keys. If one of the keys is of type of something, it shows it just as empty object. Here is what I mean:

dto:

export class HealthCheckDataDto {
    serverStatus: {} // dont have it typed yet;
    dbStatus: MongoConnectionStateT;
} 

swagger:

[
  {
    "serverStatus": {},
    "dbStatus": {}
  }
]

expected result in swagger example value:

[
  {
    "serverStatus": {},
    "dbStatus": {
      "isOnline": true,
      "msg": "string"
    }
  }
]

This is the function:

@ApiResponse({ status: 200, description: 'blabla', type: [HealthCheckDataDto] })
@ApiResponse({ status: 500, description: 'blabla, but bad', type: [HealthCheckDataDto] })
@Get('/api/healthcheck')
healthCheckApp(@Res() res: Response<HealthCheckDataDto>) {

    // check HCs and setup status code
    const healthCheck: HealthCheckI = this.healthcheckService.getFullHealthCheck();
    const statusCode = (healthCheck.dbStatus.isOnline) ? HttpStatus.OK : HttpStatus.INTERNAL_SERVER_ERROR;

    // return that response
    res.status(statusCode).json(healthCheck);
}

What I tried:

  • When I replaced type for exact params in dto, it shows it properly in swagger.
  • I did cross-check of dto against interface, where I added wrong field into ‘isOnline’ and it finds it and marks it, that it isnt good.
  • Schema is displayed in swagger, but also only top level, not the typed part. So its not just example value.
  • Checked Stack Overflow; found two related threads, but neither one solved it. One suggested to manually create sub-dtos instead of types. Well… I better not do that.

I’m doing something wrong, or missed something in documentation. Or maybe parser of that swagger module is unable to extract type/interface when generating json.

Answer

I missed one spot in NestJS Documentation: Generics and interfaces:

Since TypeScript does not store metadata about generics or interfaces, when you use them in your DTOs, SwaggerModule may not be able to properly generate model definitions at runtime.

Well, it makes sense.

In some specific scenarios (e.g. deeply nested arrays, matrices), you may want to describe your type by hand.

So, the final setup that works for me is following

  • Create DTO without types, but matches type/interface structure just like in ‘expected result’ in the original question
  • request/response should be typed with dto, eg Result<SomeDto>
  • when you handle data in that function, type it with interface/type, not dto and you have crosscheck.

Like this data are valid, swagger is properly generated. For additional swagger informations, use decorators directly in DTO.

Source: stackoverflow
Text is available under the Creative Commons Attribution-ShareAlike License; additional terms may apply. By using this site, you agree to the Privacy Policy, and Copyright Policy. Content is available under CC BY-SA 3.0 unless otherwise noted. The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 © No Copyrights, All Questions are retrived from public domain..