Giter Club home page Giter Club logo

teogor / ceres Goto Github PK

View Code? Open in Web Editor NEW
63.0 63.0 4.0 2.78 MB

๐Ÿช Ceres is a comprehensive Android development framework designed to streamline your app development process. Powered by the latest technologies like Jetpack Compose, Hilt, Coroutines, and Flow, Ceres empowers developers to build modern and efficient Android applications.

Home Page: https://source.teogor.dev/ceres

License: Apache License 2.0

Kotlin 100.00%
ads android binding-adapter bindings ceres components core data-binding dynamic-colors extensions firebase jetpack library source source-teogor-dev teogor teogor-dev viewmodel wear-os widget

ceres's Introduction

๐Ÿ‘‹๐Ÿป Hey there, I'm Teodor Grigor ๐Ÿ‘จ๐Ÿปโ€๐Ÿ’ป

About Me

I'm a dedicated open-source enthusiast, Android engineer, video game developer, and UI/UX designer. My passion lies in crafting innovative solutions and making meaningful contributions to the open-source community.

Current Focus

I'm deeply engaged in developing open-source libraries for Android, with a specific focus on Kotlin Multiplatform (KMP). These libraries are geared towards enhancing the Android development landscape and offering valuable tools to fellow developers.

Supporting My Endeavors

I'm actively involved in contributing to open-source projects during my free time. Sponsorship plays a pivotal role in sustaining my efforts by covering essential expenses, allowing me to allocate more time to open-source work, and ensuring the continuity of my contributions without the constraints of a full-time job. If you'd like to support my endeavors, you can do so by visiting my sponsorship page.

Discover More About Me ๐ŸŒ

  • Dive into my portfolio at teogor.dev
  • Connect with me on LinkedIn ๐Ÿ’ผ
  • Catch up with me on Instagram ๐Ÿ“ธ
  • Explore my professional journey through my resume

Get in Touch ๐Ÿ“ฌ

Don't hesitate to reach out, connect, or delve into my work. I'm enthusiastic about collaborating and sharing knowledge with the open-source community! ๐Ÿš€

GitHub Stats

Teodor's GitHub Stats

Top Languages

ceres's People

Contributors

teogor avatar zeobot[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ceres's Issues

Crash when loading activity that extends BaseActivity

Here is the error

java.lang.NullPointerException: null cannot be cast to non-null type androidx.navigation.fragment.NavHostFragment
  at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:6032)
  at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:6077)
  at android.app.servertransaction.DestroyActivityItem.execute(DestroyActivityItem.java:47)
  at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45)
  at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176)
  at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2443)
  at android.os.Handler.dispatchMessage(Handler.java:106)
  at android.os.Looper.loopOnce(Looper.java:226)
  at android.os.Looper.loop(Looper.java:313)
  at android.app.ActivityThread.main(ActivityThread.java:8751)
  at java.lang.reflect.Method.invoke(Native Method)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135)
  Caused by: java.lang.NullPointerException: null cannot be cast to non-null type androidx.navigation.fragment.NavHostFragment
  at dev.teogor.ceres.extensions.ActivityExtKt.findNavController(ActivityExt.kt:28)
  at dev.teogor.ceres.components.app.BaseActivity.findNavController(BaseActivity.kt:262)
  at dev.teogor.ceres.components.app.BaseActivity.onDestroy(BaseActivity.kt:181)
  at android.app.Activity.performDestroy(Activity.java:8571)
  at android.app.Instrumentation.callActivityOnDestroy(Instrumentation.java:1364)
  at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:6019)
  at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:6077)ย 
  at android.app.servertransaction.DestroyActivityItem.execute(DestroyActivityItem.java:47)ย 
  at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45)ย 
  at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176)ย 
  at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)ย 
  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2443)ย 
  at android.os.Handler.dispatchMessage(Handler.java:106)ย 
  at android.os.Looper.loopOnce(Looper.java:226)ย 
  at android.os.Looper.loop(Looper.java:313)ย 
  at android.app.ActivityThread.main(ActivityThread.java:8751)ย 
  at java.lang.reflect.Method.invoke(Native Method)ย 
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)ย 
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135)ย 

Implement `Toolbar` compatible with M3 Guidelines

Implement Toolbar compatible with M3 Guidelines as follows:

  • when content is at top the color should be the same as the background (alpha 5%)
  • else the color should be lighter by 2 levels (alpha 11%)

Logger is deprecated

A lot of components from the ceres library implement or use the logger which is deprecated

App Toolbar

The toolbar is not shown properly.

The toolbar should have more options.

For instance: showLogoOnly, transparentBackground

AppOpenAd is empty

In some cases the app open as is empty therefore the activity's overlay is shown; this will look like the UI freezes and it requires an onBackPressed event.

Possible reason: it has to do with the cache flow for ad

Make M3 components classes Open

All the classes for M3 components should be opened because otherwise no classes that extend those components can be created.

[`ceres-firebase`] Failed to resolve $firebase-module dependency

Please complete the following information:

  • Library Version: 1.0.0-alpha01

Describe the Bug:

With the ceres-firebase module added the below error is thrown:

Failed to resolve: com.google.firebase:firebase-appindexing

Expected Behavior:

The project should build succesfully

Crash when showing `AppOpenAd`

Here is the log

FATAL EXCEPTION: main
  Process: com.zeoowl.lwp.aquarium.dev, PID: 23798
    java.lang.NullPointerException
    at dev.teogor.ceres.core.global.GlobalData.getActivity(GlobalData.kt:35)
    at dev.teogor.ceres.ads.formats.AppOpenAd.show(AppOpenAd.kt:87)
    at dev.teogor.ceres.ads.startup.AdsProvider.showAd(AdsProvider.kt:111)
    at dev.teogor.ceres.ads.startup.AdsProvider.onActivityStarted(AdsProvider.kt:88)
    at android.app.Application.dispatchActivityStarted(Application.java:405)
    at android.app.Activity.dispatchActivityStarted(Activity.java:1406)
    at android.app.Activity.onStart(Activity.java:1922)
    at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:345)
    at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:248)
    at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1455)
    at android.app.Activity.performStart(Activity.java:8315)
    at android.app.ActivityThread.handleStartActivity(ActivityThread.java:4136)
    at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:221)
    at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:201)
    at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:173)
    at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2443)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loopOnce(Looper.java:226)
    at android.os.Looper.loop(Looper.java:313)
    at android.app.ActivityThread.main(ActivityThread.java:8751)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135)

Crash: `BaseTransientBottomBar.java line 146`

Crash: BaseTransientBottomBar.java line 146

Caused by java.lang.reflect.InvocationTargetException
       at java.lang.reflect.Constructor.newInstance0(Constructor.java)
       at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
       at android.view.LayoutInflater.createView(LayoutInflater.java:858)
       at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:1010)
       at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:965)
       at android.view.LayoutInflater.inflate(LayoutInflater.java:663)
       at android.view.LayoutInflater.inflate(LayoutInflater.java:538)
       at dev.teogor.ceres.m3.snackbar.BaseTransientBottomBar.<init>(BaseTransientBottomBar.java:36)
       at dev.teogor.ceres.m3.snackbar.Snackbar.<init>(Snackbar.java)
       at dev.teogor.ceres.m3.snackbar.Snackbar.makeInternal(Snackbar.java:88)
       at dev.teogor.ceres.m3.SnackbarBetaM3.buildSnackbar(SnackbarBetaM3.java:88)
       at dev.teogor.ceres.m3.SnackbarBetaM3.buildSnackbar$default(SnackbarBetaM3.java:88)
       at dev.teogor.ceres.m3.SnackbarBetaM3.<init>(SnackbarBetaM3.java:88)
       at dev.teogor.ceres.m3.SnackbarBetaM3.<init>(SnackbarBetaM3.java:88)
       at dev.teogor.ceres.m3.SnackbarBetaM3$Builder.prepareSnackbar(SnackbarBetaM3.java:137)
       at dev.teogor.ceres.m3.app.BaseActivityM3.processUiEvent(BaseActivityM3.java:137)
       at dev.teogor.ceres.components.app.BaseFragment.processUiEvent(BaseFragment.java:33)
       at dev.teogor.ceres.components.app.BaseFragment$$InternalSyntheticLambda$1$93e51e306c0a7d91c4b850e5dc67e12181e9dcc370125f1a29100ef6cd2df40f$2.onChanged(BaseFragment.java:33)
       at com.google.android.datatransport.runtime.scheduling.persistence.SQLiteEventStore$$ExternalSyntheticLambda5.d(R8$$SyntheticClass:30)
       at androidx.lifecycle.LiveData.considerNotify(LiveData.java:29)
       at androidx.lifecycle.LiveData.dispatchingValue(LiveData.java:56)
       at androidx.lifecycle.LiveData.setValue(LiveData.java:14)
       at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java)
       at dev.teogor.ceres.components.events.SingleLiveEvent.setValue(SingleLiveEvent.java:6)
       at com.zeoowl.lwp.aquarium.presentation.feature.home.HomeFragment.setupObservers$lambda-0(HomeFragment.java:111)
       at com.zeoowl.lwp.aquarium.presentation.feature.home.HomeFragment$$InternalSyntheticLambda$1$d0691176a84501196567b810ae9f295ada733c275072e27364673e0325f59bb2$0.onChanged(HomeFragment.java:111)
       at androidx.lifecycle.LiveData.considerNotify(LiveData.java:29)
       at androidx.lifecycle.LiveData.dispatchingValue(LiveData.java:56)
       at androidx.lifecycle.LiveData.setValue(LiveData.java:14)
       at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java)
       at dev.teogor.ceres.ads.view.NativeAdView.prepNativeAd$lambda-0(NativeAdView.java:71)
       at dev.teogor.ceres.ads.view.NativeAdView$$InternalSyntheticLambda$1$3a59988f7bfb7fb66125d11b40a4f22e0b10de53fdf4ff5ea0c81630a2e5acf7$0.onChanged(NativeAdView.java:71)
       at androidx.lifecycle.LiveData.considerNotify(LiveData.java:29)
       at androidx.lifecycle.LiveData.dispatchingValue(LiveData.java:56)
       at androidx.lifecycle.LiveData.setValue(LiveData.java:14)
       at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java)
       at androidx.lifecycle.LiveData$1.run(LiveData.java:18)
       at android.os.Handler.handleCallback(Handler.java:938)
       at android.os.Handler.dispatchMessage(Handler.java:99)
       at android.os.Looper.loopOnce(Looper.java:226)
       at android.os.Looper.loop(Looper.java:313)
       at android.app.ActivityThread.main(ActivityThread.java:8751)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135)
Caused by java.lang.IllegalArgumentException: dev.teogor.ceres.m3.snackbar.Snackbar.SnackbarLayout requires a value for the com.zeoowl.lwp.aquarium:attr/colorSurface attribute to be set in your app theme. You can either set the attribute in your theme or update your theme to inherit from Theme.MaterialComponents (or a descendant).
       at com.google.android.material.resources.MaterialAttributes.resolveTypedValueOrThrow(MaterialAttributes.java:32)
       at com.google.android.material.resources.MaterialAttributes.resolveTypedValueOrThrow(MaterialAttributes.java:17)
       at com.google.android.material.color.MaterialColors.getColor(MaterialColors.java:17)
       at com.google.android.material.color.MaterialColors.layer(MaterialColors.java:146)
       at dev.teogor.ceres.m3.snackbar.BaseTransientBottomBar$SnackbarBaseLayout.createThemedBackground(BaseTransientBottomBar.java:146)
       at dev.teogor.ceres.m3.snackbar.BaseTransientBottomBar$SnackbarBaseLayout.<init>(BaseTransientBottomBar.java:146)
       at dev.teogor.ceres.m3.snackbar.Snackbar$SnackbarLayout.<init>(Snackbar.java)
       at java.lang.reflect.Constructor.newInstance0(Constructor.java)
       at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
       at android.view.LayoutInflater.createView(LayoutInflater.java:858)
       at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:1010)
       at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:965)
       at android.view.LayoutInflater.inflate(LayoutInflater.java:663)
       at android.view.LayoutInflater.inflate(LayoutInflater.java:538)
       at dev.teogor.ceres.m3.snackbar.BaseTransientBottomBar.<init>(BaseTransientBottomBar.java:36)
       at dev.teogor.ceres.m3.snackbar.Snackbar.<init>(Snackbar.java)
       at dev.teogor.ceres.m3.snackbar.Snackbar.makeInternal(Snackbar.java:88)
       at dev.teogor.ceres.m3.SnackbarBetaM3.buildSnackbar(SnackbarBetaM3.java:88)
       at dev.teogor.ceres.m3.SnackbarBetaM3.buildSnackbar$default(SnackbarBetaM3.java:88)
       at dev.teogor.ceres.m3.SnackbarBetaM3.<init>(SnackbarBetaM3.java:88)
       at dev.teogor.ceres.m3.SnackbarBetaM3.<init>(SnackbarBetaM3.java:88)
       at dev.teogor.ceres.m3.SnackbarBetaM3$Builder.prepareSnackbar(SnackbarBetaM3.java:137)
       at dev.teogor.ceres.m3.app.BaseActivityM3.processUiEvent(BaseActivityM3.java:137)
       at dev.teogor.ceres.components.app.BaseFragment.processUiEvent(BaseFragment.java:33)
       at dev.teogor.ceres.components.app.BaseFragment$$InternalSyntheticLambda$1$93e51e306c0a7d91c4b850e5dc67e12181e9dcc370125f1a29100ef6cd2df40f$2.onChanged(BaseFragment.java:33)
       at com.google.android.datatransport.runtime.scheduling.persistence.SQLiteEventStore$$ExternalSyntheticLambda5.d(R8$$SyntheticClass:30)
       at androidx.lifecycle.LiveData.considerNotify(LiveData.java:29)
       at androidx.lifecycle.LiveData.dispatchingValue(LiveData.java:56)
       at androidx.lifecycle.LiveData.setValue(LiveData.java:14)
       at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java)
       at dev.teogor.ceres.components.events.SingleLiveEvent.setValue(SingleLiveEvent.java:6)
       at com.zeoowl.lwp.aquarium.presentation.feature.home.HomeFragment.setupObservers$lambda-0(HomeFragment.java:111)
       at com.zeoowl.lwp.aquarium.presentation.feature.home.HomeFragment$$InternalSyntheticLambda$1$d0691176a84501196567b810ae9f295ada733c275072e27364673e0325f59bb2$0.onChanged(HomeFragment.java:111)
       at androidx.lifecycle.LiveData.considerNotify(LiveData.java:29)
       at androidx.lifecycle.LiveData.dispatchingValue(LiveData.java:56)
       at androidx.lifecycle.LiveData.setValue(LiveData.java:14)
       at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java)
       at dev.teogor.ceres.ads.view.NativeAdView.prepNativeAd$lambda-0(NativeAdView.java:71)
       at dev.teogor.ceres.ads.view.NativeAdView$$InternalSyntheticLambda$1$3a59988f7bfb7fb66125d11b40a4f22e0b10de53fdf4ff5ea0c81630a2e5acf7$0.onChanged(NativeAdView.java:71)
       at androidx.lifecycle.LiveData.considerNotify(LiveData.java:29)
       at androidx.lifecycle.LiveData.dispatchingValue(LiveData.java:56)
       at androidx.lifecycle.LiveData.setValue(LiveData.java:14)
       at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java)
       at androidx.lifecycle.LiveData$1.run(LiveData.java:18)
       at android.os.Handler.handleCallback(Handler.java:938)
       at android.os.Handler.dispatchMessage(Handler.java:99)
       at android.os.Looper.loopOnce(Looper.java:226)
       at android.os.Looper.loop(Looper.java:313)
       at android.app.ActivityThread.main(ActivityThread.java:8751)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135)

Allow Optional Status Bar Padding in FullScreenLayoutBase

Is there an existing issue for this?

  • I have searched the existing issues

Is there a StackOverflow question about this issue?

  • I have searched StackOverflow

What happened?

Currently, the FullScreenLayoutBase function in the dev.teogor.ceres.screen.core.layout package adds status bar padding unconditionally. While this can be useful in some cases, it also prevents the layout from being truly full-screen when padding is not needed.

Proposed Solution:

We suggest making the status bar padding optional, allowing the user to decide whether they want to add padding to the status bar or not. This change will give users more flexibility in creating full-screen layouts.

Steps to Reproduce:

  1. Use the FullScreenLayoutBase function with hasStatusBar set to true.
  2. Observe that the status bar padding is added, which is not always desired.

Expected Behavior:

Users should be able to choose whether to add status bar padding in the FullScreenLayoutBase function.

Actual Behavior:

Status bar padding is added unconditionally, which may not be suitable for all use cases.

Relevant logcat output

No response

Code of Conduct

  • I agree to follow this project's Code of Conduct

[Bug]: Incorrect Display of App Open Ad on Activity Launch

Is there an existing issue for this?

  • I have searched the existing issues

Is there a StackOverflow question about this issue?

  • I have searched StackOverflow

What happened?

Currently, the App Open Ad is being displayed when a new activity or intent is launched within the application. However, it should only be shown when the application is resumed, such as when returning from the background. This behavior is not aligned with user expectations and can lead to a poor user experience.

Expected Behavior:

  • The App Open Ad should be displayed only when the application is resumed after being in the background.

Steps to Reproduce:

  1. Launch the application.
  2. Navigate to a different activity or trigger a new intent.
  3. Observe that the App Open Ad is displayed.

Actual Behavior:

  • The App Open Ad is displayed when a new activity or intent is launched, which is not the intended behavior.

This issue needs to be addressed to ensure that the App Open Ad is shown only when the application is resumed, as expected.

Relevant logcat output

No response

Code of Conduct

  • I agree to follow this project's Code of Conduct

Unwanted logs in the logcat

In BindingMethods from m3 there is a log when the binding on toolbar is set as shown bellow:

BindingMethods - $package - D - is_filled false

Request: Add Configuration Options for SDK Versions in Ceres SDK Components

Is there an existing issue for this?

  • I have searched the existing issues

Describe the problem

I would like to request the addition of configuration options in the Ceres SDK Components to set the ceres.sdkcomponents.minsdk, ceres.sdkcomponents.targetsdk, and ceres.sdkcomponents.compilesdk values programmatically. Currently, these values are set to specific integers, but having the ability to configure them dynamically would provide greater flexibility and compatibility with different Android projects.

Rationale:

  • Dynamic configuration of SDK versions would allow developers to adapt the Ceres SDK Components to different project requirements.
  • It would make it easier to support a wider range of Android SDK versions without code modifications.
  • This feature would align the Ceres SDK Components with industry best practices for SDK version management.

Describe the solution

To implement this feature, we could introduce new configuration properties or methods that allow developers to specify the desired ceres.sdkcomponents.minsdk, ceres.sdkcomponents.targetsdk, and ceres.sdkcomponents.compilesdk values in their project's build.gradle or configuration files.

Additional context

No response

Code of Conduct

  • I agree to follow this project's Code of Conduct

Ads Cache

The interestitial ads/app open ads are not cached properly

[Bug]: Enhancement: Improve Bottom Navigation Bar Title Handling in TopLevelDestination

Is there an existing issue for this?

  • I have searched the existing issues

Is there a StackOverflow question about this issue?

  • I have searched StackOverflow

What happened?

Description:
The TopLevelDestination class currently has two properties, iconText and titleText, meant for different purposes. However, there is an issue with their usage: iconText is incorrectly being used as the content description for the icon, while titleText is used as the text for the navigation bar item. This is a bug and leads to an inconsistent user interface.

Expected Behavior:

  • iconText should be used exclusively as the content description for accessibility purposes.
  • titleText should be used as the text for the navigation bar item to ensure a consistent and accessible user experience.

Steps to Reproduce:

  1. Create a TopLevelDestination instance with iconText and titleText.
  2. Observe that iconText is used as the text in the navigation bar item, and titleText is used as the content description for the icon.

Proposed Solution:

  1. Update the TopLevelDestination class to use iconText as the content description for the icon.
  2. Modify the code to use titleText as the text for the navigation bar item.

Relevant logcat output

No response

Code of Conduct

  • I agree to follow this project's Code of Conduct

[FR]: Enhanced Control over Consent Manager

Is there an existing issue for this?

  • I have searched the existing issues

Describe the problem

The current implementation of the Consent Manager provides valuable functionality for managing user consent. However, to further enhance its utility and customization, it is proposed that more control options be made available, specifically related to when the consent is shown.

Describe the solution

This feature request aims to provide users with more fine-grained control over the behavior of the Consent Manager, specifically regarding when consent is displayed to the user.

Proposed Enhancements:

  1. Custom Consent Trigger: Allow users to define custom triggers or conditions for showing the consent form. This would enable the consent form to be displayed at specific points in the application flow or based on user interactions.

  2. Delay Consent Display: Provide an option to delay the display of the consent form until a certain point in the user's interaction with the application. This can be useful for improving user experience and engagement.

  3. Integration Flexibility: Ensure that the Consent Manager can be easily integrated into various parts of the application, such as specific activities or fragments, giving developers more control over where and when consent is requested.

  4. Custom Consent UI: Allow users to customize the appearance and layout of the consent form to align with the application's design and branding.

Code Sample:

// Example of custom consent trigger
ConsentManager.setCustomConsentTrigger { context ->
    // Implement custom logic to determine when to show the consent form
    val shouldShowConsent = // Custom condition
    if (shouldShowConsent) {
        ConsentManager.loadAndShowConsentFormIfRequired(context)
    }
}

Benefits:

  • Enhanced user experience: Developers can ensure that the consent form is displayed at the most appropriate moments, reducing interruptions.
  • Improved customization: Developers can tailor the Consent Manager to match the application's specific requirements and user flows.
  • Greater flexibility: Users can have more control over the consent management process.

Additional context

No response

Code of Conduct

  • I agree to follow this project's Code of Conduct

[FR]: Feature Request: Allow Custom Composition for Local Providers

Is there an existing issue for this?

  • I have searched the existing issues

Describe the problem

Currently, the Local Providers feature provides predefined compositions. However, there is a need to allow users to provide their own custom compositions for Local Providers to meet specific requirements and use cases.

Describe the solution

This feature request aims to enhance the Local Providers feature by allowing users to define their own compositions. Users should be able to provide a custom composition that best suits their needs, enabling greater flexibility and customization in handling local data.

Use Case:

  • Users may have unique data handling requirements that cannot be fully accommodated by the predefined compositions. Allowing custom compositions empowers users to tailor the behavior of Local Providers to their specific use cases.

Benefits:

  • Enhanced flexibility: Users can define custom compositions to meet their specific data management needs.
  • Improved compatibility: Custom compositions enable seamless integration with diverse data sources and services.
  • Increased control: Users can fine-tune the behavior of Local Providers for optimal performance and functionality.

Implementation Considerations:

  • Provide documentation and guidelines for creating custom compositions.
  • Ensure backward compatibility with existing predefined compositions.
  • Consider potential security and validation mechanisms for custom compositions.

This feature request aims to enhance the Local Providers feature and provide users with the ability to define their own compositions, thereby expanding the capabilities and versatility of the feature.

Additional context

No response

Code of Conduct

  • I agree to follow this project's Code of Conduct

[FR]: Feature Request: Add Extensions for Icon Handling with ImageVector and DrawableRes

Is there an existing issue for this?

  • I have searched the existing issues

Describe the problem

Currently, dealing with icons in Jetpack Compose can involve handling both ImageVector and DrawableRes icons separately. It would be convenient to have some extensions or utility functions that simplify working with icons, allowing users to seamlessly switch between ImageVector and DrawableRes icons without the need for explicit conversions.

Describe the solution

Add extensions or utility functions to the Icon class in the dev.teogor.ceres.ui.foundation.graphics package to provide easy access to icons using both ImageVector and DrawableRes.

Example Usage:

// Create an Icon using ImageVector
val imageVectorIcon = Icons.Filled.Wallpaper.asImageVectorIcon()

// Create an Icon using DrawableRes
val drawableResIcon = R.drawable.ic_wallpaper.asDrawableResourceIcon()

Additional context

No response

Code of Conduct

  • I agree to follow this project's Code of Conduct

Update Minimum SDK to 24

Is there an existing issue for this?

  • I have searched the existing issues

Is there a StackOverflow question about this issue?

  • I have searched StackOverflow

What happened?

Google has recently increased the recommended minimum SDK version for Android app development from 21 to 24. To stay up-to-date with these changes and ensure compatibility with the latest Android features and security updates, we should consider updating the minimum SDK version for our project.

This update will help us provide a better user experience and take advantage of the latest Android platform improvements. It's essential to keep our app aligned with industry standards and user expectations.

Let's discuss and plan the update to ensure a smooth transition while maintaining support for our existing user base.

Relevant logcat output

No response

Code of Conduct

  • I agree to follow this project's Code of Conduct

Dependency on `splash-screen`

Currently the project depends on splash-screen library.

override fun drawEdgeToEdge(splashScreen: SplashScreen?) {
    super.drawEdgeToEdge(splashScreen)

    binding.bottomNavigation.applyInsetter {
      type(navigationBars = true) {
        padding()
      }
    }

    val startTime = System.currentTimeMillis()
    val elapsed = System.currentTimeMillis() - startTime
    val keepSplashOnScreen =
      elapsed <= SPLASH_MIN_DURATION || !ready && elapsed <= SPLASH_MAX_DURATION
    // splashScreen?.setKeepOnScreenCondition{keepSplashOnScreen}

    setSplashScreenExitAnimation(splashScreen)
  }

[FR]: Consider Ad Expiration for AppOpenAdManager

Is there an existing issue for this?

  • I have searched the existing issues

Describe the problem

The AppOpenAdManager class in the codebase handles the loading and management of app open ads. According to the Google AdMob documentation, app open ads have a timeout period of four hours. This means that ads rendered more than four hours after the request time will no longer be valid and may not earn revenue.

To ensure that we don't display expired ads and provide a better user experience, we should consider ad expiration and implement a mechanism to check the validity of the loaded app open ad.

Suggested Solution

To address this issue, we can implement the following changes in the AppOpenAdManager class:

  1. Introduce a private variable loadTime to keep track of the time when an app open ad was loaded.

  2. Create a method, e.g., wasLoadTimeLessThanNHoursAgo, to calculate the time difference between the current time and the loadTime in hours.

  3. Modify the isAdAvailable method to check if an app open ad is loaded and if it has been less than four hours since it was loaded.

By implementing these changes, we can ensure that we don't show expired ads to users and maintain the quality of ad delivery.

Describe the solution

Additional Information

Additional context

No response

Code of Conduct

  • I agree to follow this project's Code of Conduct

[Bug]: Consent Form Displayed Multiple Times

Is there an existing issue for this?

  • I have searched the existing issues

Is there a StackOverflow question about this issue?

  • I have searched StackOverflow

What happened?

The current implementation of the Consent Manager may result in the consent form being displayed multiple times in certain scenarios. This issue report aims to address this behavior and suggests potential solutions to prevent the repeated display of the consent form.

Proposed Solutions:

  1. Boolean Check: Implement a boolean flag to track whether the consent form has already been displayed. Only trigger the loadAndShowConsentFormIfRequired function if the flag indicates that the form has not been shown.

  2. Queue Mechanism: Utilize a queue to manage the requests to display the consent form. When a request is made, it can be added to the queue, and the form is displayed only if the queue is empty. This ensures that the form is shown once per user session.

  3. Callback Handling: Ensure that callbacks from the consent form dismissal do not trigger the display of the form again. Check whether the form has already been dismissed before initiating another display request.

Code Sample (Boolean Check):

var isConsentFormDisplayed = false

fun loadAndShowConsentFormIfRequired() {
    activity?.let {
        if (!isConsentFormDisplayed) {
            UserMessagingPlatform.loadAndShowConsentFormIfRequired(
                it,
            ) { formError ->
                isConsentFormDisplayed = true
                state.value = ConsentResult.ConsentFormDismissed(
                    canRequestAds = consentInformation.canRequestAds(),
                    requirementStatus = consentInformation.privacyOptionsRequirementStatus,
                    formError = formError,
                )
            }
        }
    }
}

Benefits:

  • Prevents the repeated display of the consent form, improving the user experience.
  • Ensures that the consent form is shown only when necessary, reducing interruptions for the user.

Relevant logcat output

No response

Code of Conduct

  • I agree to follow this project's Code of Conduct

[Bug]: Remove `about-libraries` Plugin Configuration from `ceres-screen-ui` Library

Is there an existing issue for this?

  • I have searched the existing issues

Is there a StackOverflow question about this issue?

  • I have searched StackOverflow

What happened?

The ceres-screen-ui library currently includes configuration elements related to the about-libraries plugin:

about-libraries = { id = "$id", version.ref = "aboutLibraries" }
alias(libs.plugins.about.libraries) apply true

These elements are responsible for generating the 'aboutlibraries.json' file. However, it is more appropriate for users to declare these elements in their own projects rather than bundling them with the library.

To resolve this issue, we should remove the 'about-libraries' plugin configuration from the 'ceres-screen-ui' library and provide guidance in the documentation on how users can declare these elements in their own project configurations.

Steps to Reproduce:

  1. Open the 'ceres-screen-ui' library project.
  2. Inspect the build.gradle file for the 'about-libraries' plugin configuration.

Expected Behavior:
The 'about-libraries' plugin configuration should not be present in the 'ceres-screen-ui' library.

Proposed Solution:

  1. Remove the 'about-libraries' plugin configuration from the 'ceres-screen-ui' library's build.gradle.kts.
  2. Update the documentation to guide users on how to declare the 'about-libraries' plugin configuration in their own project's build.gradle.kts file.
  3. Ensure that users are aware of the importance of declaring these elements to generate the 'aboutlibraries.json' file in their projects.

Relevant logcat output

No response

Code of Conduct

  • I agree to follow this project's Code of Conduct

[Bug]: Full-Screen Layout Padding Issue with 'hasStatusBar' Flag

Is there an existing issue for this?

  • I have searched the existing issues

Is there a StackOverflow question about this issue?

  • I have searched StackOverflow

What happened?

The FullScreenLayoutBase method in the dev.teogor.ceres.screen.core.layout package provides a convenient way to create full-screen layouts in Jetpack Compose applications. However, it seems that when the hasStatusBar parameter is set to true, the status bar padding is not being added as expected.

Steps to Reproduce:

  1. Use the FullScreenLayoutBase composable with the hasStatusBar parameter set to true.
  2. Observe that the status bar padding is not added to the layout.

Expected Behavior:
When hasStatusBar is set to true, the FullScreenLayoutBase should add the necessary status bar padding to ensure content is not obscured by the status bar.

Actual Behavior:
The status bar padding is not being added when hasStatusBar is set to true, which may lead to content being obscured by the status bar.

Relevant logcat output

No response

Code of Conduct

  • I agree to follow this project's Code of Conduct

Binding adapter already exists

This is the message from console-log

warning: Binding adapter AK(dev.teogor.ceres.m3.widgets.bar.ToolBar, dev.teogor.ceres.components.toolbar.ToolbarType) already exists for type! Overriding dev.teogor.ceres.m3.binding.BindingMethods.Companion#toolbarBindingType with dev.teogor.ceres.m3.binding.BindingMethods#toolbarBindingType
warning: Binding adapter AK(dev.teogor.ceres.m3.widgets.bar.ToolBar, dev.teogor.ceres.components.toolbar.ToolbarType) already exists for type! Overriding dev.teogor.ceres.m3.binding.BindingMethods.Companion#toolbarBindingType with dev.teogor.ceres.m3.binding.BindingMethods#toolbarBindingType
warning: Binding adapter AK(dev.teogor.ceres.m3.widgets.bar.ToolBar, boolean) already exists for is_transparent! Overriding dev.teogor.ceres.m3.binding.BindingMethods.Companion#toolbarBindingIsTransparent with dev.teogor.ceres.m3.binding.BindingMethods#toolbarBindingIsTransparent
warning: Binding adapter AK(dev.teogor.ceres.m3.widgets.bar.ToolBar, boolean) already exists for is_transparent! Overriding dev.teogor.ceres.m3.binding.BindingMethods.Companion#toolbarBindingIsTransparent with dev.teogor.ceres.m3.binding.BindingMethods#toolbarBindingIsTransparent
warning: Binding adapter AK(dev.teogor.ceres.m3.SwitchComponentM3, dev.teogor.ceres.m3.SwitchComponentM3.OnCheckedChangeListener) already exists for onCheckedChange! Overriding dev.teogor.ceres.m3.binding.BindingMethods.Companion#onContentElementCheck with dev.teogor.ceres.m3.binding.BindingMethods#onContentElementCheck
warning: Binding adapter AK(dev.teogor.ceres.m3.SwitchComponentM3, dev.teogor.ceres.m3.SwitchComponentM3.OnCheckedChangeListener) already exists for onCheckedChange! Overriding dev.teogor.ceres.m3.binding.BindingMethods.Companion#onContentElementCheck with dev.teogor.ceres.m3.binding.BindingMethods#onContentElementCheck
warning: Binding adapter AK(dev.teogor.ceres.m3.SwitchComponentM3, dev.teogor.ceres.m3.SwitchComponentM3.OnClickedChangeListener) already exists for onClicked! Overriding dev.teogor.ceres.m3.binding.BindingMethods.Companion#onContentElementClick with dev.teogor.ceres.m3.binding.BindingMethods#onContentElementClick
warning: Binding adapter AK(dev.teogor.ceres.m3.SwitchComponentM3, dev.teogor.ceres.m3.SwitchComponentM3.OnClickedChangeListener) already exists for onClicked! Overriding dev.teogor.ceres.m3.binding.BindingMethods.Companion#onContentElementClick with dev.teogor.ceres.m3.binding.BindingMethods#onContentElementClick
warning: Binding adapter AK(dev.teogor.ceres.m3.SwitchComponentM3, androidx.lifecycle.MutableLiveData<java.lang.String>) already exists for subtitle! Overriding dev.teogor.ceres.m3.binding.BindingMethods.Companion#onContentElementSubtitle with dev.teogor.ceres.m3.binding.BindingMethods#onContentElementSubtitle
warning: Binding adapter AK(dev.teogor.ceres.m3.SwitchComponentM3, androidx.lifecycle.MutableLiveData<java.lang.String>) already exists for subtitle! Overriding dev.teogor.ceres.m3.binding.BindingMethods.Companion#onContentElementSubtitle with dev.teogor.ceres.m3.binding.BindingMethods#onContentElementSubtitle
warning: Binding adapter AK(dev.teogor.ceres.m3.SwitchComponentM3, androidx.lifecycle.MutableLiveData<java.lang.String>) already exists for title! Overriding dev.teogor.ceres.m3.binding.BindingMethods.Companion#onContentElementTitle with dev.teogor.ceres.m3.binding.BindingMethods#onContentElementTitle
warning: Binding adapter AK(dev.teogor.ceres.m3.SwitchComponentM3, androidx.lifecycle.MutableLiveData<java.lang.String>) already exists for title! Overriding dev.teogor.ceres.m3.binding.BindingMethods.Companion#onContentElementTitle with dev.teogor.ceres.m3.binding.BindingMethods#onContentElementTitle
warning: Binding adapter AK(dev.teogor.ceres.m3.SwitchComponentM3, androidx.lifecycle.MutableLiveData<java.lang.Boolean>) already exists for checked! Overriding dev.teogor.ceres.m3.binding.BindingMethods.Companion#onContentElementChecked with dev.teogor.ceres.m3.binding.BindingMethods#onContentElementChecked
warning: Binding adapter AK(dev.teogor.ceres.m3.SwitchComponentM3, androidx.lifecycle.MutableLiveData<java.lang.Boolean>) already exists for checked! Overriding dev.teogor.ceres.m3.binding.BindingMethods.Companion#onContentElementChecked with dev.teogor.ceres.m3.binding.BindingMethods#onContentElementChecked
warning: Binding adapter AK(dev.teogor.ceres.m3.ColorsContainerM3, dev.teogor.ceres.m3.ColorsContainerM3.OnColorPickListener) already exists for onColorPick! Overriding dev.teogor.ceres.m3.binding.BindingMethods.Companion#onContentElementChecked with dev.teogor.ceres.m3.binding.BindingMethods#onContentElementChecked
warning: Binding adapter AK(dev.teogor.ceres.m3.ColorsContainerM3, dev.teogor.ceres.m3.ColorsContainerM3.OnColorPickListener) already exists for onColorPick! Overriding dev.teogor.ceres.m3.binding.BindingMethods.Companion#onContentElementChecked with dev.teogor.ceres.m3.binding.BindingMethods#onContentElementChecked
warning: Binding adapter AK(dev.teogor.ceres.m3.ColorsContainerM3, androidx.lifecycle.MutableLiveData<java.lang.Integer>) already exists for pickedColor! Overriding dev.teogor.ceres.m3.binding.BindingMethods.Companion#onContentElementChecked with dev.teogor.ceres.m3.binding.BindingMethods#onContentElementChecked
warning: Binding adapter AK(dev.teogor.ceres.m3.ColorsContainerM3, androidx.lifecycle.MutableLiveData<java.lang.Integer>) already exists for pickedColor! Overriding dev.teogor.ceres.m3.binding.BindingMethods.Companion#onContentElementChecked with dev.teogor.ceres.m3.binding.BindingMethods#onContentElementChecked
warning: Binding adapter AK(dev.teogor.ceres.m3.ImageComponentM3, dev.teogor.ceres.m3.ImageComponentM3.OnClickedChangeListener) already exists for onClicked! Overriding dev.teogor.ceres.m3.binding.BindingMethods.Companion#onContentElementClick with dev.teogor.ceres.m3.binding.BindingMethods#onContentElementClick
warning: Binding adapter AK(dev.teogor.ceres.m3.ImageComponentM3, dev.teogor.ceres.m3.ImageComponentM3.OnClickedChangeListener) already exists for onClicked! Overriding dev.teogor.ceres.m3.binding.BindingMethods.Companion#onContentElementClick with dev.teogor.ceres.m3.binding.BindingMethods#onContentElementClick

Crash when attempting to show the same native ad in the same fragment

When the NativeAdView is added multiple times in the same fragment that implements the same ad and adbinder crashes.

TODO: check if it crashes when we apply different implementations

java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
  at android.view.ViewGroup.addViewInner(ViewGroup.java:6084)
  at android.view.ViewGroup.addView(ViewGroup.java:5903)
  at android.view.ViewGroup.addView(ViewGroup.java:5843)
  at android.view.ViewGroup.addView(ViewGroup.java:5815)
  at dev.teogor.ceres.ads.view.NativeAdView.addAd(NativeAdView.kt:115)
  at dev.teogor.ceres.ads.view.NativeAdView.prepNativeAd$lambda-0(NativeAdView.kt:79)
  at dev.teogor.ceres.ads.view.NativeAdView.$r8$lambda$xEyKj0WPgw_LDaJKFeGv9QmCnFU(Unknown Source:0)
  at dev.teogor.ceres.ads.view.NativeAdView$$ExternalSyntheticLambda0.onChanged(Unknown Source:6)
  at androidx.lifecycle.LiveData.considerNotify(LiveData.java:133)
  at androidx.lifecycle.LiveData.dispatchingValue(LiveData.java:151)
  at androidx.lifecycle.LiveData.setValue(LiveData.java:309)
  at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java:50)
  at androidx.lifecycle.LiveData$1.run(LiveData.java:93)
  at android.os.Handler.handleCallback(Handler.java:938)
  at android.os.Handler.dispatchMessage(Handler.java:99)
  at android.os.Looper.loopOnce(Looper.java:226)
  at android.os.Looper.loop(Looper.java:313)
  at android.app.ActivityThread.main(ActivityThread.java:8751)
  at java.lang.reflect.Method.invoke(Native Method)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135)

Invalid class extender - `Logger`

Currently there are multiple classes that implement or extend the Logger. Even though this adds the option to log($message) from the base class, in case that it is not used it adds a new function to each of its parents.

I would recommend to look into kotlin-extensions

Crash: `GlobalData.java line 19`

Crash: GlobalData.java line 19

Caused by java.lang.NullPointerException
       at dev.teogor.ceres.core.global.GlobalData.getActivity(GlobalData.java:19)
       at dev.teogor.ceres.ads.formats.AppOpenAd.show(AppOpenAd.java:19)
       at dev.teogor.ceres.ads.startup.AdsProvider.showAd(AdsProvider.java:69)
       at android.app.Application.dispatchActivityStarted(Application.java:405)
       at android.app.Activity.dispatchActivityStarted(Activity.java:1406)
       at android.app.Activity.onStart(Activity.java:1922)
       at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:6)
       at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java)
       at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1455)
       at android.app.Activity.performStart(Activity.java:8315)
       at android.app.ActivityThread.handleStartActivity(ActivityThread.java:4136)
       at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:221)
       at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:201)
       at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:173)
       at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2443)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loopOnce(Looper.java:226)
       at android.os.Looper.loop(Looper.java:313)
       at android.app.ActivityThread.main(ActivityThread.java:8751)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135)

`NullPointerException` for `BaseActivity.getBinding(BaseActivity.kt:62)`

Here is the error:

java.lang.NullPointerException
  at dev.teogor.ceres.components.app.BaseActivity.getBinding(BaseActivity.kt:62)
  at dev.teogor.ceres.components.app.BaseActivity.setSplashScreenExitAnimation$lambda-10$lambda-6$lambda-5(BaseActivity.kt:226)
  at dev.teogor.ceres.components.app.BaseActivity.$r8$lambda$K6HtujfAO6UnHkTEMAPvvXIWmtg(BaseActivity.kt)
  at dev.teogor.ceres.components.app.BaseActivity$$ExternalSyntheticLambda5.onAnimationUpdate(D8$$SyntheticClass)
  at android.animation.ValueAnimator.animateValue(ValueAnimator.java:1283)
  at android.animation.ValueAnimator.animationFrame(ValueAnimator.java:1207)
  at android.animation.ValueAnimator.doAnimationFrame(ValueAnimator.java:1248)
  at android.animation.ValueAnimator.setCurrentPlayTime(ValueAnimator.java:546)
  at android.animation.ValueAnimator.start(ValueAnimator.java:959)
  at android.animation.ValueAnimator.start(ValueAnimator.java:969)
  at dev.teogor.ceres.components.app.BaseActivity.setSplashScreenExitAnimation$lambda-10(BaseActivity.kt:245)
  at dev.teogor.ceres.components.app.BaseActivity.$r8$lambda$KR9LJo7aGUe8b5StElwh-6Y5XQk(BaseActivity.kt)
  at dev.teogor.ceres.components.app.BaseActivity$$ExternalSyntheticLambda0.onSplashScreenExit(D8$$SyntheticClass)
  at androidx.core.splashscreen.SplashScreen$Impl.dispatchOnExitAnimation$lambda-3(SplashScreen.kt:377)
  at androidx.core.splashscreen.SplashScreen$Impl.$r8$lambda$-PV0PfxsoH5D7yqG9wGMqLSg-T0(SplashScreen.kt)
  at androidx.core.splashscreen.SplashScreen$Impl$$ExternalSyntheticLambda0.run(D8$$SyntheticClass)
  at android.view.Choreographer$CallbackRecord.run(Choreographer.java:767)
  at android.view.Choreographer.doCallbacks(Choreographer.java:580)
  at android.view.Choreographer.doFrame(Choreographer.java:549)
  at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:753)
  at android.os.Handler.handleCallback(Handler.java:739)
  at android.os.Handler.dispatchMessage(Handler.java:95)
  at android.os.Looper.loop(Looper.java:135)
  at android.app.ActivityThread.main(ActivityThread.java:5221)
  at java.lang.reflect.Method.invoke(Native Method)
  at java.lang.reflect.Method.invoke(Method.java:372)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)

API - 21

Fragment container should be WindowInsets-aware

for instance we should be able to set if we want to add padding:

  • to the top based on StatusBar and/or ToolBar
  • to the bottom based on NavigationBar and/or BottomNavBar and/or FloatingButton

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.