diff --git a/.kotlin/errors/errors-1762296211384.log b/.kotlin/errors/errors-1762296211384.log new file mode 100644 index 0000000..b44ddf8 --- /dev/null +++ b/.kotlin/errors/errors-1762296211384.log @@ -0,0 +1,76 @@ +kotlin version: 2.0.21 +error message: org.jetbrains.kotlin.util.FileAnalysisException: While analysing C:/source/SmoothBottomBar/app/src/main/java/me/ibrahimsn/smoothbottombar/FirstFragment.kt:15:5: java.lang.IllegalArgumentException: source must not be null + at org.jetbrains.kotlin.util.AnalysisExceptionsKt.wrapIntoFileAnalysisExceptionIfNeeded(AnalysisExceptions.kt:57) + at org.jetbrains.kotlin.fir.FirCliExceptionHandler.handleExceptionOnFileAnalysis(Utils.kt:249) + at org.jetbrains.kotlin.fir.pipeline.AnalyseKt.runCheckers(analyse.kt:46) + at org.jetbrains.kotlin.fir.pipeline.FirUtilsKt.resolveAndCheckFir(firUtils.kt:77) + at org.jetbrains.kotlin.fir.pipeline.FirUtilsKt.buildResolveAndCheckFirViaLightTree(firUtils.kt:88) + at org.jetbrains.kotlin.cli.jvm.compiler.pipeline.JvmCompilerPipelineKt.compileModuleToAnalyzedFir(jvmCompilerPipeline.kt:319) + at org.jetbrains.kotlin.cli.jvm.compiler.pipeline.JvmCompilerPipelineKt.compileModulesUsingFrontendIrAndLightTree(jvmCompilerPipeline.kt:118) + at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:148) + at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:43) + at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:103) + at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:49) + at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:101) + at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:464) + at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:73) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.doCompile(IncrementalCompilerRunner.kt:506) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileImpl(IncrementalCompilerRunner.kt:423) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileNonIncrementally(IncrementalCompilerRunner.kt:301) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:129) + at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execIncrementalCompiler(CompileServiceImpl.kt:675) + at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execIncrementalCompiler(CompileServiceImpl.kt:92) + at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1660) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source) + at java.base/java.lang.reflect.Method.invoke(Unknown Source) + at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(Unknown Source) + at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source) + at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source) + at java.base/java.security.AccessController.doPrivileged(Unknown Source) + at java.rmi/sun.rmi.transport.Transport.serviceCall(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(Unknown Source) + at java.base/java.security.AccessController.doPrivileged(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(Unknown Source) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) + at java.base/java.lang.Thread.run(Unknown Source) +Caused by: java.lang.IllegalArgumentException: source must not be null + at org.jetbrains.kotlin.diagnostics.KtDiagnosticReportHelpersKt.requireNotNull(KtDiagnosticReportHelpers.kt:68) + at org.jetbrains.kotlin.diagnostics.KtDiagnosticReportHelpersKt.reportOn(KtDiagnosticReportHelpers.kt:39) + at org.jetbrains.kotlin.diagnostics.KtDiagnosticReportHelpersKt.reportOn$default(KtDiagnosticReportHelpers.kt:31) + at org.jetbrains.kotlin.fir.analysis.checkers.expression.FirIncompatibleClassExpressionChecker.checkSourceElement(FirIncompatibleClassExpressionChecker.kt:50) + at org.jetbrains.kotlin.fir.analysis.checkers.expression.FirIncompatibleClassExpressionChecker.checkType$checkers(FirIncompatibleClassExpressionChecker.kt:42) + at org.jetbrains.kotlin.fir.analysis.checkers.type.FirIncompatibleClassTypeChecker.check(FirIncompatibleClassTypeChecker.kt:17) + at org.jetbrains.kotlin.fir.analysis.collectors.components.TypeCheckersDiagnosticComponent.check(TypeCheckersDiagnosticComponent.kt:81) + at org.jetbrains.kotlin.fir.analysis.collectors.components.TypeCheckersDiagnosticComponent.visitResolvedTypeRef(TypeCheckersDiagnosticComponent.kt:53) + at org.jetbrains.kotlin.fir.analysis.collectors.components.TypeCheckersDiagnosticComponent.visitResolvedTypeRef(TypeCheckersDiagnosticComponent.kt:19) + at org.jetbrains.kotlin.fir.types.FirResolvedTypeRef.accept(FirResolvedTypeRef.kt:28) + at org.jetbrains.kotlin.fir.analysis.collectors.CheckerRunningDiagnosticCollectorVisitor.checkElement(CheckerRunningDiagnosticCollectorVisitor.kt:24) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitResolvedTypeRef(AbstractDiagnosticCollectorVisitor.kt:248) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitResolvedTypeRef(AbstractDiagnosticCollectorVisitor.kt:30) + at org.jetbrains.kotlin.fir.types.FirResolvedTypeRef.accept(FirResolvedTypeRef.kt:28) + at org.jetbrains.kotlin.fir.declarations.impl.FirSimpleFunctionImpl.acceptChildren(FirSimpleFunctionImpl.kt:63) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitNestedElements(AbstractDiagnosticCollectorVisitor.kt:38) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitWithDeclarationAndReceiver(AbstractDiagnosticCollectorVisitor.kt:311) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitSimpleFunction(AbstractDiagnosticCollectorVisitor.kt:118) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitSimpleFunction(AbstractDiagnosticCollectorVisitor.kt:30) + at org.jetbrains.kotlin.fir.declarations.FirSimpleFunction.accept(FirSimpleFunction.kt:51) + at org.jetbrains.kotlin.fir.declarations.impl.FirRegularClassImpl.acceptChildren(FirRegularClassImpl.kt:63) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitNestedElements(AbstractDiagnosticCollectorVisitor.kt:38) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitWithDeclarationAndReceiver(AbstractDiagnosticCollectorVisitor.kt:311) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitClassAndChildren(AbstractDiagnosticCollectorVisitor.kt:87) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitRegularClass(AbstractDiagnosticCollectorVisitor.kt:92) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitRegularClass(AbstractDiagnosticCollectorVisitor.kt:30) + at org.jetbrains.kotlin.fir.declarations.FirRegularClass.accept(FirRegularClass.kt:48) + at org.jetbrains.kotlin.fir.declarations.impl.FirFileImpl.acceptChildren(FirFileImpl.kt:57) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitNestedElements(AbstractDiagnosticCollectorVisitor.kt:38) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitFile(AbstractDiagnosticCollectorVisitor.kt:1151) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitFile(AbstractDiagnosticCollectorVisitor.kt:30) + at org.jetbrains.kotlin.fir.declarations.FirFile.accept(FirFile.kt:42) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollector.collectDiagnostics(AbstractDiagnosticCollector.kt:36) + at org.jetbrains.kotlin.fir.pipeline.AnalyseKt.runCheckers(analyse.kt:34) + ... 33 more + + diff --git a/.kotlin/errors/errors-1762296347665.log b/.kotlin/errors/errors-1762296347665.log new file mode 100644 index 0000000..b44ddf8 --- /dev/null +++ b/.kotlin/errors/errors-1762296347665.log @@ -0,0 +1,76 @@ +kotlin version: 2.0.21 +error message: org.jetbrains.kotlin.util.FileAnalysisException: While analysing C:/source/SmoothBottomBar/app/src/main/java/me/ibrahimsn/smoothbottombar/FirstFragment.kt:15:5: java.lang.IllegalArgumentException: source must not be null + at org.jetbrains.kotlin.util.AnalysisExceptionsKt.wrapIntoFileAnalysisExceptionIfNeeded(AnalysisExceptions.kt:57) + at org.jetbrains.kotlin.fir.FirCliExceptionHandler.handleExceptionOnFileAnalysis(Utils.kt:249) + at org.jetbrains.kotlin.fir.pipeline.AnalyseKt.runCheckers(analyse.kt:46) + at org.jetbrains.kotlin.fir.pipeline.FirUtilsKt.resolveAndCheckFir(firUtils.kt:77) + at org.jetbrains.kotlin.fir.pipeline.FirUtilsKt.buildResolveAndCheckFirViaLightTree(firUtils.kt:88) + at org.jetbrains.kotlin.cli.jvm.compiler.pipeline.JvmCompilerPipelineKt.compileModuleToAnalyzedFir(jvmCompilerPipeline.kt:319) + at org.jetbrains.kotlin.cli.jvm.compiler.pipeline.JvmCompilerPipelineKt.compileModulesUsingFrontendIrAndLightTree(jvmCompilerPipeline.kt:118) + at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:148) + at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:43) + at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:103) + at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:49) + at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:101) + at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:464) + at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:73) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.doCompile(IncrementalCompilerRunner.kt:506) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileImpl(IncrementalCompilerRunner.kt:423) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileNonIncrementally(IncrementalCompilerRunner.kt:301) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:129) + at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execIncrementalCompiler(CompileServiceImpl.kt:675) + at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execIncrementalCompiler(CompileServiceImpl.kt:92) + at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1660) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source) + at java.base/java.lang.reflect.Method.invoke(Unknown Source) + at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(Unknown Source) + at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source) + at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source) + at java.base/java.security.AccessController.doPrivileged(Unknown Source) + at java.rmi/sun.rmi.transport.Transport.serviceCall(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(Unknown Source) + at java.base/java.security.AccessController.doPrivileged(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(Unknown Source) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) + at java.base/java.lang.Thread.run(Unknown Source) +Caused by: java.lang.IllegalArgumentException: source must not be null + at org.jetbrains.kotlin.diagnostics.KtDiagnosticReportHelpersKt.requireNotNull(KtDiagnosticReportHelpers.kt:68) + at org.jetbrains.kotlin.diagnostics.KtDiagnosticReportHelpersKt.reportOn(KtDiagnosticReportHelpers.kt:39) + at org.jetbrains.kotlin.diagnostics.KtDiagnosticReportHelpersKt.reportOn$default(KtDiagnosticReportHelpers.kt:31) + at org.jetbrains.kotlin.fir.analysis.checkers.expression.FirIncompatibleClassExpressionChecker.checkSourceElement(FirIncompatibleClassExpressionChecker.kt:50) + at org.jetbrains.kotlin.fir.analysis.checkers.expression.FirIncompatibleClassExpressionChecker.checkType$checkers(FirIncompatibleClassExpressionChecker.kt:42) + at org.jetbrains.kotlin.fir.analysis.checkers.type.FirIncompatibleClassTypeChecker.check(FirIncompatibleClassTypeChecker.kt:17) + at org.jetbrains.kotlin.fir.analysis.collectors.components.TypeCheckersDiagnosticComponent.check(TypeCheckersDiagnosticComponent.kt:81) + at org.jetbrains.kotlin.fir.analysis.collectors.components.TypeCheckersDiagnosticComponent.visitResolvedTypeRef(TypeCheckersDiagnosticComponent.kt:53) + at org.jetbrains.kotlin.fir.analysis.collectors.components.TypeCheckersDiagnosticComponent.visitResolvedTypeRef(TypeCheckersDiagnosticComponent.kt:19) + at org.jetbrains.kotlin.fir.types.FirResolvedTypeRef.accept(FirResolvedTypeRef.kt:28) + at org.jetbrains.kotlin.fir.analysis.collectors.CheckerRunningDiagnosticCollectorVisitor.checkElement(CheckerRunningDiagnosticCollectorVisitor.kt:24) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitResolvedTypeRef(AbstractDiagnosticCollectorVisitor.kt:248) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitResolvedTypeRef(AbstractDiagnosticCollectorVisitor.kt:30) + at org.jetbrains.kotlin.fir.types.FirResolvedTypeRef.accept(FirResolvedTypeRef.kt:28) + at org.jetbrains.kotlin.fir.declarations.impl.FirSimpleFunctionImpl.acceptChildren(FirSimpleFunctionImpl.kt:63) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitNestedElements(AbstractDiagnosticCollectorVisitor.kt:38) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitWithDeclarationAndReceiver(AbstractDiagnosticCollectorVisitor.kt:311) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitSimpleFunction(AbstractDiagnosticCollectorVisitor.kt:118) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitSimpleFunction(AbstractDiagnosticCollectorVisitor.kt:30) + at org.jetbrains.kotlin.fir.declarations.FirSimpleFunction.accept(FirSimpleFunction.kt:51) + at org.jetbrains.kotlin.fir.declarations.impl.FirRegularClassImpl.acceptChildren(FirRegularClassImpl.kt:63) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitNestedElements(AbstractDiagnosticCollectorVisitor.kt:38) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitWithDeclarationAndReceiver(AbstractDiagnosticCollectorVisitor.kt:311) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitClassAndChildren(AbstractDiagnosticCollectorVisitor.kt:87) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitRegularClass(AbstractDiagnosticCollectorVisitor.kt:92) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitRegularClass(AbstractDiagnosticCollectorVisitor.kt:30) + at org.jetbrains.kotlin.fir.declarations.FirRegularClass.accept(FirRegularClass.kt:48) + at org.jetbrains.kotlin.fir.declarations.impl.FirFileImpl.acceptChildren(FirFileImpl.kt:57) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitNestedElements(AbstractDiagnosticCollectorVisitor.kt:38) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitFile(AbstractDiagnosticCollectorVisitor.kt:1151) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitFile(AbstractDiagnosticCollectorVisitor.kt:30) + at org.jetbrains.kotlin.fir.declarations.FirFile.accept(FirFile.kt:42) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollector.collectDiagnostics(AbstractDiagnosticCollector.kt:36) + at org.jetbrains.kotlin.fir.pipeline.AnalyseKt.runCheckers(analyse.kt:34) + ... 33 more + + diff --git a/.kotlin/errors/errors-1762296420451.log b/.kotlin/errors/errors-1762296420451.log new file mode 100644 index 0000000..b44ddf8 --- /dev/null +++ b/.kotlin/errors/errors-1762296420451.log @@ -0,0 +1,76 @@ +kotlin version: 2.0.21 +error message: org.jetbrains.kotlin.util.FileAnalysisException: While analysing C:/source/SmoothBottomBar/app/src/main/java/me/ibrahimsn/smoothbottombar/FirstFragment.kt:15:5: java.lang.IllegalArgumentException: source must not be null + at org.jetbrains.kotlin.util.AnalysisExceptionsKt.wrapIntoFileAnalysisExceptionIfNeeded(AnalysisExceptions.kt:57) + at org.jetbrains.kotlin.fir.FirCliExceptionHandler.handleExceptionOnFileAnalysis(Utils.kt:249) + at org.jetbrains.kotlin.fir.pipeline.AnalyseKt.runCheckers(analyse.kt:46) + at org.jetbrains.kotlin.fir.pipeline.FirUtilsKt.resolveAndCheckFir(firUtils.kt:77) + at org.jetbrains.kotlin.fir.pipeline.FirUtilsKt.buildResolveAndCheckFirViaLightTree(firUtils.kt:88) + at org.jetbrains.kotlin.cli.jvm.compiler.pipeline.JvmCompilerPipelineKt.compileModuleToAnalyzedFir(jvmCompilerPipeline.kt:319) + at org.jetbrains.kotlin.cli.jvm.compiler.pipeline.JvmCompilerPipelineKt.compileModulesUsingFrontendIrAndLightTree(jvmCompilerPipeline.kt:118) + at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:148) + at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:43) + at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:103) + at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:49) + at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:101) + at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:464) + at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:73) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.doCompile(IncrementalCompilerRunner.kt:506) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileImpl(IncrementalCompilerRunner.kt:423) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileNonIncrementally(IncrementalCompilerRunner.kt:301) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:129) + at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execIncrementalCompiler(CompileServiceImpl.kt:675) + at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execIncrementalCompiler(CompileServiceImpl.kt:92) + at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1660) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source) + at java.base/java.lang.reflect.Method.invoke(Unknown Source) + at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(Unknown Source) + at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source) + at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source) + at java.base/java.security.AccessController.doPrivileged(Unknown Source) + at java.rmi/sun.rmi.transport.Transport.serviceCall(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(Unknown Source) + at java.base/java.security.AccessController.doPrivileged(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(Unknown Source) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) + at java.base/java.lang.Thread.run(Unknown Source) +Caused by: java.lang.IllegalArgumentException: source must not be null + at org.jetbrains.kotlin.diagnostics.KtDiagnosticReportHelpersKt.requireNotNull(KtDiagnosticReportHelpers.kt:68) + at org.jetbrains.kotlin.diagnostics.KtDiagnosticReportHelpersKt.reportOn(KtDiagnosticReportHelpers.kt:39) + at org.jetbrains.kotlin.diagnostics.KtDiagnosticReportHelpersKt.reportOn$default(KtDiagnosticReportHelpers.kt:31) + at org.jetbrains.kotlin.fir.analysis.checkers.expression.FirIncompatibleClassExpressionChecker.checkSourceElement(FirIncompatibleClassExpressionChecker.kt:50) + at org.jetbrains.kotlin.fir.analysis.checkers.expression.FirIncompatibleClassExpressionChecker.checkType$checkers(FirIncompatibleClassExpressionChecker.kt:42) + at org.jetbrains.kotlin.fir.analysis.checkers.type.FirIncompatibleClassTypeChecker.check(FirIncompatibleClassTypeChecker.kt:17) + at org.jetbrains.kotlin.fir.analysis.collectors.components.TypeCheckersDiagnosticComponent.check(TypeCheckersDiagnosticComponent.kt:81) + at org.jetbrains.kotlin.fir.analysis.collectors.components.TypeCheckersDiagnosticComponent.visitResolvedTypeRef(TypeCheckersDiagnosticComponent.kt:53) + at org.jetbrains.kotlin.fir.analysis.collectors.components.TypeCheckersDiagnosticComponent.visitResolvedTypeRef(TypeCheckersDiagnosticComponent.kt:19) + at org.jetbrains.kotlin.fir.types.FirResolvedTypeRef.accept(FirResolvedTypeRef.kt:28) + at org.jetbrains.kotlin.fir.analysis.collectors.CheckerRunningDiagnosticCollectorVisitor.checkElement(CheckerRunningDiagnosticCollectorVisitor.kt:24) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitResolvedTypeRef(AbstractDiagnosticCollectorVisitor.kt:248) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitResolvedTypeRef(AbstractDiagnosticCollectorVisitor.kt:30) + at org.jetbrains.kotlin.fir.types.FirResolvedTypeRef.accept(FirResolvedTypeRef.kt:28) + at org.jetbrains.kotlin.fir.declarations.impl.FirSimpleFunctionImpl.acceptChildren(FirSimpleFunctionImpl.kt:63) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitNestedElements(AbstractDiagnosticCollectorVisitor.kt:38) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitWithDeclarationAndReceiver(AbstractDiagnosticCollectorVisitor.kt:311) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitSimpleFunction(AbstractDiagnosticCollectorVisitor.kt:118) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitSimpleFunction(AbstractDiagnosticCollectorVisitor.kt:30) + at org.jetbrains.kotlin.fir.declarations.FirSimpleFunction.accept(FirSimpleFunction.kt:51) + at org.jetbrains.kotlin.fir.declarations.impl.FirRegularClassImpl.acceptChildren(FirRegularClassImpl.kt:63) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitNestedElements(AbstractDiagnosticCollectorVisitor.kt:38) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitWithDeclarationAndReceiver(AbstractDiagnosticCollectorVisitor.kt:311) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitClassAndChildren(AbstractDiagnosticCollectorVisitor.kt:87) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitRegularClass(AbstractDiagnosticCollectorVisitor.kt:92) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitRegularClass(AbstractDiagnosticCollectorVisitor.kt:30) + at org.jetbrains.kotlin.fir.declarations.FirRegularClass.accept(FirRegularClass.kt:48) + at org.jetbrains.kotlin.fir.declarations.impl.FirFileImpl.acceptChildren(FirFileImpl.kt:57) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitNestedElements(AbstractDiagnosticCollectorVisitor.kt:38) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitFile(AbstractDiagnosticCollectorVisitor.kt:1151) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitFile(AbstractDiagnosticCollectorVisitor.kt:30) + at org.jetbrains.kotlin.fir.declarations.FirFile.accept(FirFile.kt:42) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollector.collectDiagnostics(AbstractDiagnosticCollector.kt:36) + at org.jetbrains.kotlin.fir.pipeline.AnalyseKt.runCheckers(analyse.kt:34) + ... 33 more + + diff --git a/.kotlin/errors/errors-1762296462947.log b/.kotlin/errors/errors-1762296462947.log new file mode 100644 index 0000000..b44ddf8 --- /dev/null +++ b/.kotlin/errors/errors-1762296462947.log @@ -0,0 +1,76 @@ +kotlin version: 2.0.21 +error message: org.jetbrains.kotlin.util.FileAnalysisException: While analysing C:/source/SmoothBottomBar/app/src/main/java/me/ibrahimsn/smoothbottombar/FirstFragment.kt:15:5: java.lang.IllegalArgumentException: source must not be null + at org.jetbrains.kotlin.util.AnalysisExceptionsKt.wrapIntoFileAnalysisExceptionIfNeeded(AnalysisExceptions.kt:57) + at org.jetbrains.kotlin.fir.FirCliExceptionHandler.handleExceptionOnFileAnalysis(Utils.kt:249) + at org.jetbrains.kotlin.fir.pipeline.AnalyseKt.runCheckers(analyse.kt:46) + at org.jetbrains.kotlin.fir.pipeline.FirUtilsKt.resolveAndCheckFir(firUtils.kt:77) + at org.jetbrains.kotlin.fir.pipeline.FirUtilsKt.buildResolveAndCheckFirViaLightTree(firUtils.kt:88) + at org.jetbrains.kotlin.cli.jvm.compiler.pipeline.JvmCompilerPipelineKt.compileModuleToAnalyzedFir(jvmCompilerPipeline.kt:319) + at org.jetbrains.kotlin.cli.jvm.compiler.pipeline.JvmCompilerPipelineKt.compileModulesUsingFrontendIrAndLightTree(jvmCompilerPipeline.kt:118) + at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:148) + at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:43) + at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:103) + at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:49) + at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:101) + at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:464) + at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:73) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.doCompile(IncrementalCompilerRunner.kt:506) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileImpl(IncrementalCompilerRunner.kt:423) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileNonIncrementally(IncrementalCompilerRunner.kt:301) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:129) + at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execIncrementalCompiler(CompileServiceImpl.kt:675) + at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execIncrementalCompiler(CompileServiceImpl.kt:92) + at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1660) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source) + at java.base/java.lang.reflect.Method.invoke(Unknown Source) + at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(Unknown Source) + at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source) + at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source) + at java.base/java.security.AccessController.doPrivileged(Unknown Source) + at java.rmi/sun.rmi.transport.Transport.serviceCall(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(Unknown Source) + at java.base/java.security.AccessController.doPrivileged(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(Unknown Source) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) + at java.base/java.lang.Thread.run(Unknown Source) +Caused by: java.lang.IllegalArgumentException: source must not be null + at org.jetbrains.kotlin.diagnostics.KtDiagnosticReportHelpersKt.requireNotNull(KtDiagnosticReportHelpers.kt:68) + at org.jetbrains.kotlin.diagnostics.KtDiagnosticReportHelpersKt.reportOn(KtDiagnosticReportHelpers.kt:39) + at org.jetbrains.kotlin.diagnostics.KtDiagnosticReportHelpersKt.reportOn$default(KtDiagnosticReportHelpers.kt:31) + at org.jetbrains.kotlin.fir.analysis.checkers.expression.FirIncompatibleClassExpressionChecker.checkSourceElement(FirIncompatibleClassExpressionChecker.kt:50) + at org.jetbrains.kotlin.fir.analysis.checkers.expression.FirIncompatibleClassExpressionChecker.checkType$checkers(FirIncompatibleClassExpressionChecker.kt:42) + at org.jetbrains.kotlin.fir.analysis.checkers.type.FirIncompatibleClassTypeChecker.check(FirIncompatibleClassTypeChecker.kt:17) + at org.jetbrains.kotlin.fir.analysis.collectors.components.TypeCheckersDiagnosticComponent.check(TypeCheckersDiagnosticComponent.kt:81) + at org.jetbrains.kotlin.fir.analysis.collectors.components.TypeCheckersDiagnosticComponent.visitResolvedTypeRef(TypeCheckersDiagnosticComponent.kt:53) + at org.jetbrains.kotlin.fir.analysis.collectors.components.TypeCheckersDiagnosticComponent.visitResolvedTypeRef(TypeCheckersDiagnosticComponent.kt:19) + at org.jetbrains.kotlin.fir.types.FirResolvedTypeRef.accept(FirResolvedTypeRef.kt:28) + at org.jetbrains.kotlin.fir.analysis.collectors.CheckerRunningDiagnosticCollectorVisitor.checkElement(CheckerRunningDiagnosticCollectorVisitor.kt:24) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitResolvedTypeRef(AbstractDiagnosticCollectorVisitor.kt:248) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitResolvedTypeRef(AbstractDiagnosticCollectorVisitor.kt:30) + at org.jetbrains.kotlin.fir.types.FirResolvedTypeRef.accept(FirResolvedTypeRef.kt:28) + at org.jetbrains.kotlin.fir.declarations.impl.FirSimpleFunctionImpl.acceptChildren(FirSimpleFunctionImpl.kt:63) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitNestedElements(AbstractDiagnosticCollectorVisitor.kt:38) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitWithDeclarationAndReceiver(AbstractDiagnosticCollectorVisitor.kt:311) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitSimpleFunction(AbstractDiagnosticCollectorVisitor.kt:118) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitSimpleFunction(AbstractDiagnosticCollectorVisitor.kt:30) + at org.jetbrains.kotlin.fir.declarations.FirSimpleFunction.accept(FirSimpleFunction.kt:51) + at org.jetbrains.kotlin.fir.declarations.impl.FirRegularClassImpl.acceptChildren(FirRegularClassImpl.kt:63) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitNestedElements(AbstractDiagnosticCollectorVisitor.kt:38) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitWithDeclarationAndReceiver(AbstractDiagnosticCollectorVisitor.kt:311) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitClassAndChildren(AbstractDiagnosticCollectorVisitor.kt:87) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitRegularClass(AbstractDiagnosticCollectorVisitor.kt:92) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitRegularClass(AbstractDiagnosticCollectorVisitor.kt:30) + at org.jetbrains.kotlin.fir.declarations.FirRegularClass.accept(FirRegularClass.kt:48) + at org.jetbrains.kotlin.fir.declarations.impl.FirFileImpl.acceptChildren(FirFileImpl.kt:57) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitNestedElements(AbstractDiagnosticCollectorVisitor.kt:38) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitFile(AbstractDiagnosticCollectorVisitor.kt:1151) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitFile(AbstractDiagnosticCollectorVisitor.kt:30) + at org.jetbrains.kotlin.fir.declarations.FirFile.accept(FirFile.kt:42) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollector.collectDiagnostics(AbstractDiagnosticCollector.kt:36) + at org.jetbrains.kotlin.fir.pipeline.AnalyseKt.runCheckers(analyse.kt:34) + ... 33 more + + diff --git a/.kotlin/errors/errors-1762296578501.log b/.kotlin/errors/errors-1762296578501.log new file mode 100644 index 0000000..b44ddf8 --- /dev/null +++ b/.kotlin/errors/errors-1762296578501.log @@ -0,0 +1,76 @@ +kotlin version: 2.0.21 +error message: org.jetbrains.kotlin.util.FileAnalysisException: While analysing C:/source/SmoothBottomBar/app/src/main/java/me/ibrahimsn/smoothbottombar/FirstFragment.kt:15:5: java.lang.IllegalArgumentException: source must not be null + at org.jetbrains.kotlin.util.AnalysisExceptionsKt.wrapIntoFileAnalysisExceptionIfNeeded(AnalysisExceptions.kt:57) + at org.jetbrains.kotlin.fir.FirCliExceptionHandler.handleExceptionOnFileAnalysis(Utils.kt:249) + at org.jetbrains.kotlin.fir.pipeline.AnalyseKt.runCheckers(analyse.kt:46) + at org.jetbrains.kotlin.fir.pipeline.FirUtilsKt.resolveAndCheckFir(firUtils.kt:77) + at org.jetbrains.kotlin.fir.pipeline.FirUtilsKt.buildResolveAndCheckFirViaLightTree(firUtils.kt:88) + at org.jetbrains.kotlin.cli.jvm.compiler.pipeline.JvmCompilerPipelineKt.compileModuleToAnalyzedFir(jvmCompilerPipeline.kt:319) + at org.jetbrains.kotlin.cli.jvm.compiler.pipeline.JvmCompilerPipelineKt.compileModulesUsingFrontendIrAndLightTree(jvmCompilerPipeline.kt:118) + at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:148) + at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:43) + at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:103) + at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:49) + at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:101) + at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:464) + at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:73) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.doCompile(IncrementalCompilerRunner.kt:506) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileImpl(IncrementalCompilerRunner.kt:423) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileNonIncrementally(IncrementalCompilerRunner.kt:301) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:129) + at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execIncrementalCompiler(CompileServiceImpl.kt:675) + at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execIncrementalCompiler(CompileServiceImpl.kt:92) + at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1660) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source) + at java.base/java.lang.reflect.Method.invoke(Unknown Source) + at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(Unknown Source) + at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source) + at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source) + at java.base/java.security.AccessController.doPrivileged(Unknown Source) + at java.rmi/sun.rmi.transport.Transport.serviceCall(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(Unknown Source) + at java.base/java.security.AccessController.doPrivileged(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(Unknown Source) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) + at java.base/java.lang.Thread.run(Unknown Source) +Caused by: java.lang.IllegalArgumentException: source must not be null + at org.jetbrains.kotlin.diagnostics.KtDiagnosticReportHelpersKt.requireNotNull(KtDiagnosticReportHelpers.kt:68) + at org.jetbrains.kotlin.diagnostics.KtDiagnosticReportHelpersKt.reportOn(KtDiagnosticReportHelpers.kt:39) + at org.jetbrains.kotlin.diagnostics.KtDiagnosticReportHelpersKt.reportOn$default(KtDiagnosticReportHelpers.kt:31) + at org.jetbrains.kotlin.fir.analysis.checkers.expression.FirIncompatibleClassExpressionChecker.checkSourceElement(FirIncompatibleClassExpressionChecker.kt:50) + at org.jetbrains.kotlin.fir.analysis.checkers.expression.FirIncompatibleClassExpressionChecker.checkType$checkers(FirIncompatibleClassExpressionChecker.kt:42) + at org.jetbrains.kotlin.fir.analysis.checkers.type.FirIncompatibleClassTypeChecker.check(FirIncompatibleClassTypeChecker.kt:17) + at org.jetbrains.kotlin.fir.analysis.collectors.components.TypeCheckersDiagnosticComponent.check(TypeCheckersDiagnosticComponent.kt:81) + at org.jetbrains.kotlin.fir.analysis.collectors.components.TypeCheckersDiagnosticComponent.visitResolvedTypeRef(TypeCheckersDiagnosticComponent.kt:53) + at org.jetbrains.kotlin.fir.analysis.collectors.components.TypeCheckersDiagnosticComponent.visitResolvedTypeRef(TypeCheckersDiagnosticComponent.kt:19) + at org.jetbrains.kotlin.fir.types.FirResolvedTypeRef.accept(FirResolvedTypeRef.kt:28) + at org.jetbrains.kotlin.fir.analysis.collectors.CheckerRunningDiagnosticCollectorVisitor.checkElement(CheckerRunningDiagnosticCollectorVisitor.kt:24) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitResolvedTypeRef(AbstractDiagnosticCollectorVisitor.kt:248) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitResolvedTypeRef(AbstractDiagnosticCollectorVisitor.kt:30) + at org.jetbrains.kotlin.fir.types.FirResolvedTypeRef.accept(FirResolvedTypeRef.kt:28) + at org.jetbrains.kotlin.fir.declarations.impl.FirSimpleFunctionImpl.acceptChildren(FirSimpleFunctionImpl.kt:63) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitNestedElements(AbstractDiagnosticCollectorVisitor.kt:38) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitWithDeclarationAndReceiver(AbstractDiagnosticCollectorVisitor.kt:311) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitSimpleFunction(AbstractDiagnosticCollectorVisitor.kt:118) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitSimpleFunction(AbstractDiagnosticCollectorVisitor.kt:30) + at org.jetbrains.kotlin.fir.declarations.FirSimpleFunction.accept(FirSimpleFunction.kt:51) + at org.jetbrains.kotlin.fir.declarations.impl.FirRegularClassImpl.acceptChildren(FirRegularClassImpl.kt:63) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitNestedElements(AbstractDiagnosticCollectorVisitor.kt:38) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitWithDeclarationAndReceiver(AbstractDiagnosticCollectorVisitor.kt:311) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitClassAndChildren(AbstractDiagnosticCollectorVisitor.kt:87) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitRegularClass(AbstractDiagnosticCollectorVisitor.kt:92) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitRegularClass(AbstractDiagnosticCollectorVisitor.kt:30) + at org.jetbrains.kotlin.fir.declarations.FirRegularClass.accept(FirRegularClass.kt:48) + at org.jetbrains.kotlin.fir.declarations.impl.FirFileImpl.acceptChildren(FirFileImpl.kt:57) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitNestedElements(AbstractDiagnosticCollectorVisitor.kt:38) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitFile(AbstractDiagnosticCollectorVisitor.kt:1151) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitFile(AbstractDiagnosticCollectorVisitor.kt:30) + at org.jetbrains.kotlin.fir.declarations.FirFile.accept(FirFile.kt:42) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollector.collectDiagnostics(AbstractDiagnosticCollector.kt:36) + at org.jetbrains.kotlin.fir.pipeline.AnalyseKt.runCheckers(analyse.kt:34) + ... 33 more + + diff --git a/.kotlin/errors/errors-1762296779307.log b/.kotlin/errors/errors-1762296779307.log new file mode 100644 index 0000000..b44ddf8 --- /dev/null +++ b/.kotlin/errors/errors-1762296779307.log @@ -0,0 +1,76 @@ +kotlin version: 2.0.21 +error message: org.jetbrains.kotlin.util.FileAnalysisException: While analysing C:/source/SmoothBottomBar/app/src/main/java/me/ibrahimsn/smoothbottombar/FirstFragment.kt:15:5: java.lang.IllegalArgumentException: source must not be null + at org.jetbrains.kotlin.util.AnalysisExceptionsKt.wrapIntoFileAnalysisExceptionIfNeeded(AnalysisExceptions.kt:57) + at org.jetbrains.kotlin.fir.FirCliExceptionHandler.handleExceptionOnFileAnalysis(Utils.kt:249) + at org.jetbrains.kotlin.fir.pipeline.AnalyseKt.runCheckers(analyse.kt:46) + at org.jetbrains.kotlin.fir.pipeline.FirUtilsKt.resolveAndCheckFir(firUtils.kt:77) + at org.jetbrains.kotlin.fir.pipeline.FirUtilsKt.buildResolveAndCheckFirViaLightTree(firUtils.kt:88) + at org.jetbrains.kotlin.cli.jvm.compiler.pipeline.JvmCompilerPipelineKt.compileModuleToAnalyzedFir(jvmCompilerPipeline.kt:319) + at org.jetbrains.kotlin.cli.jvm.compiler.pipeline.JvmCompilerPipelineKt.compileModulesUsingFrontendIrAndLightTree(jvmCompilerPipeline.kt:118) + at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:148) + at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:43) + at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:103) + at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:49) + at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:101) + at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:464) + at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:73) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.doCompile(IncrementalCompilerRunner.kt:506) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileImpl(IncrementalCompilerRunner.kt:423) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileNonIncrementally(IncrementalCompilerRunner.kt:301) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:129) + at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execIncrementalCompiler(CompileServiceImpl.kt:675) + at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execIncrementalCompiler(CompileServiceImpl.kt:92) + at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1660) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source) + at java.base/java.lang.reflect.Method.invoke(Unknown Source) + at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(Unknown Source) + at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source) + at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source) + at java.base/java.security.AccessController.doPrivileged(Unknown Source) + at java.rmi/sun.rmi.transport.Transport.serviceCall(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(Unknown Source) + at java.base/java.security.AccessController.doPrivileged(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(Unknown Source) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) + at java.base/java.lang.Thread.run(Unknown Source) +Caused by: java.lang.IllegalArgumentException: source must not be null + at org.jetbrains.kotlin.diagnostics.KtDiagnosticReportHelpersKt.requireNotNull(KtDiagnosticReportHelpers.kt:68) + at org.jetbrains.kotlin.diagnostics.KtDiagnosticReportHelpersKt.reportOn(KtDiagnosticReportHelpers.kt:39) + at org.jetbrains.kotlin.diagnostics.KtDiagnosticReportHelpersKt.reportOn$default(KtDiagnosticReportHelpers.kt:31) + at org.jetbrains.kotlin.fir.analysis.checkers.expression.FirIncompatibleClassExpressionChecker.checkSourceElement(FirIncompatibleClassExpressionChecker.kt:50) + at org.jetbrains.kotlin.fir.analysis.checkers.expression.FirIncompatibleClassExpressionChecker.checkType$checkers(FirIncompatibleClassExpressionChecker.kt:42) + at org.jetbrains.kotlin.fir.analysis.checkers.type.FirIncompatibleClassTypeChecker.check(FirIncompatibleClassTypeChecker.kt:17) + at org.jetbrains.kotlin.fir.analysis.collectors.components.TypeCheckersDiagnosticComponent.check(TypeCheckersDiagnosticComponent.kt:81) + at org.jetbrains.kotlin.fir.analysis.collectors.components.TypeCheckersDiagnosticComponent.visitResolvedTypeRef(TypeCheckersDiagnosticComponent.kt:53) + at org.jetbrains.kotlin.fir.analysis.collectors.components.TypeCheckersDiagnosticComponent.visitResolvedTypeRef(TypeCheckersDiagnosticComponent.kt:19) + at org.jetbrains.kotlin.fir.types.FirResolvedTypeRef.accept(FirResolvedTypeRef.kt:28) + at org.jetbrains.kotlin.fir.analysis.collectors.CheckerRunningDiagnosticCollectorVisitor.checkElement(CheckerRunningDiagnosticCollectorVisitor.kt:24) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitResolvedTypeRef(AbstractDiagnosticCollectorVisitor.kt:248) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitResolvedTypeRef(AbstractDiagnosticCollectorVisitor.kt:30) + at org.jetbrains.kotlin.fir.types.FirResolvedTypeRef.accept(FirResolvedTypeRef.kt:28) + at org.jetbrains.kotlin.fir.declarations.impl.FirSimpleFunctionImpl.acceptChildren(FirSimpleFunctionImpl.kt:63) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitNestedElements(AbstractDiagnosticCollectorVisitor.kt:38) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitWithDeclarationAndReceiver(AbstractDiagnosticCollectorVisitor.kt:311) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitSimpleFunction(AbstractDiagnosticCollectorVisitor.kt:118) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitSimpleFunction(AbstractDiagnosticCollectorVisitor.kt:30) + at org.jetbrains.kotlin.fir.declarations.FirSimpleFunction.accept(FirSimpleFunction.kt:51) + at org.jetbrains.kotlin.fir.declarations.impl.FirRegularClassImpl.acceptChildren(FirRegularClassImpl.kt:63) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitNestedElements(AbstractDiagnosticCollectorVisitor.kt:38) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitWithDeclarationAndReceiver(AbstractDiagnosticCollectorVisitor.kt:311) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitClassAndChildren(AbstractDiagnosticCollectorVisitor.kt:87) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitRegularClass(AbstractDiagnosticCollectorVisitor.kt:92) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitRegularClass(AbstractDiagnosticCollectorVisitor.kt:30) + at org.jetbrains.kotlin.fir.declarations.FirRegularClass.accept(FirRegularClass.kt:48) + at org.jetbrains.kotlin.fir.declarations.impl.FirFileImpl.acceptChildren(FirFileImpl.kt:57) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitNestedElements(AbstractDiagnosticCollectorVisitor.kt:38) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitFile(AbstractDiagnosticCollectorVisitor.kt:1151) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitFile(AbstractDiagnosticCollectorVisitor.kt:30) + at org.jetbrains.kotlin.fir.declarations.FirFile.accept(FirFile.kt:42) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollector.collectDiagnostics(AbstractDiagnosticCollector.kt:36) + at org.jetbrains.kotlin.fir.pipeline.AnalyseKt.runCheckers(analyse.kt:34) + ... 33 more + + diff --git a/.kotlin/errors/errors-1762297506729.log b/.kotlin/errors/errors-1762297506729.log new file mode 100644 index 0000000..b44ddf8 --- /dev/null +++ b/.kotlin/errors/errors-1762297506729.log @@ -0,0 +1,76 @@ +kotlin version: 2.0.21 +error message: org.jetbrains.kotlin.util.FileAnalysisException: While analysing C:/source/SmoothBottomBar/app/src/main/java/me/ibrahimsn/smoothbottombar/FirstFragment.kt:15:5: java.lang.IllegalArgumentException: source must not be null + at org.jetbrains.kotlin.util.AnalysisExceptionsKt.wrapIntoFileAnalysisExceptionIfNeeded(AnalysisExceptions.kt:57) + at org.jetbrains.kotlin.fir.FirCliExceptionHandler.handleExceptionOnFileAnalysis(Utils.kt:249) + at org.jetbrains.kotlin.fir.pipeline.AnalyseKt.runCheckers(analyse.kt:46) + at org.jetbrains.kotlin.fir.pipeline.FirUtilsKt.resolveAndCheckFir(firUtils.kt:77) + at org.jetbrains.kotlin.fir.pipeline.FirUtilsKt.buildResolveAndCheckFirViaLightTree(firUtils.kt:88) + at org.jetbrains.kotlin.cli.jvm.compiler.pipeline.JvmCompilerPipelineKt.compileModuleToAnalyzedFir(jvmCompilerPipeline.kt:319) + at org.jetbrains.kotlin.cli.jvm.compiler.pipeline.JvmCompilerPipelineKt.compileModulesUsingFrontendIrAndLightTree(jvmCompilerPipeline.kt:118) + at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:148) + at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:43) + at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:103) + at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:49) + at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:101) + at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:464) + at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:73) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.doCompile(IncrementalCompilerRunner.kt:506) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileImpl(IncrementalCompilerRunner.kt:423) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileNonIncrementally(IncrementalCompilerRunner.kt:301) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:129) + at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execIncrementalCompiler(CompileServiceImpl.kt:675) + at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execIncrementalCompiler(CompileServiceImpl.kt:92) + at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1660) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source) + at java.base/java.lang.reflect.Method.invoke(Unknown Source) + at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(Unknown Source) + at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source) + at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source) + at java.base/java.security.AccessController.doPrivileged(Unknown Source) + at java.rmi/sun.rmi.transport.Transport.serviceCall(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(Unknown Source) + at java.base/java.security.AccessController.doPrivileged(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(Unknown Source) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) + at java.base/java.lang.Thread.run(Unknown Source) +Caused by: java.lang.IllegalArgumentException: source must not be null + at org.jetbrains.kotlin.diagnostics.KtDiagnosticReportHelpersKt.requireNotNull(KtDiagnosticReportHelpers.kt:68) + at org.jetbrains.kotlin.diagnostics.KtDiagnosticReportHelpersKt.reportOn(KtDiagnosticReportHelpers.kt:39) + at org.jetbrains.kotlin.diagnostics.KtDiagnosticReportHelpersKt.reportOn$default(KtDiagnosticReportHelpers.kt:31) + at org.jetbrains.kotlin.fir.analysis.checkers.expression.FirIncompatibleClassExpressionChecker.checkSourceElement(FirIncompatibleClassExpressionChecker.kt:50) + at org.jetbrains.kotlin.fir.analysis.checkers.expression.FirIncompatibleClassExpressionChecker.checkType$checkers(FirIncompatibleClassExpressionChecker.kt:42) + at org.jetbrains.kotlin.fir.analysis.checkers.type.FirIncompatibleClassTypeChecker.check(FirIncompatibleClassTypeChecker.kt:17) + at org.jetbrains.kotlin.fir.analysis.collectors.components.TypeCheckersDiagnosticComponent.check(TypeCheckersDiagnosticComponent.kt:81) + at org.jetbrains.kotlin.fir.analysis.collectors.components.TypeCheckersDiagnosticComponent.visitResolvedTypeRef(TypeCheckersDiagnosticComponent.kt:53) + at org.jetbrains.kotlin.fir.analysis.collectors.components.TypeCheckersDiagnosticComponent.visitResolvedTypeRef(TypeCheckersDiagnosticComponent.kt:19) + at org.jetbrains.kotlin.fir.types.FirResolvedTypeRef.accept(FirResolvedTypeRef.kt:28) + at org.jetbrains.kotlin.fir.analysis.collectors.CheckerRunningDiagnosticCollectorVisitor.checkElement(CheckerRunningDiagnosticCollectorVisitor.kt:24) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitResolvedTypeRef(AbstractDiagnosticCollectorVisitor.kt:248) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitResolvedTypeRef(AbstractDiagnosticCollectorVisitor.kt:30) + at org.jetbrains.kotlin.fir.types.FirResolvedTypeRef.accept(FirResolvedTypeRef.kt:28) + at org.jetbrains.kotlin.fir.declarations.impl.FirSimpleFunctionImpl.acceptChildren(FirSimpleFunctionImpl.kt:63) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitNestedElements(AbstractDiagnosticCollectorVisitor.kt:38) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitWithDeclarationAndReceiver(AbstractDiagnosticCollectorVisitor.kt:311) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitSimpleFunction(AbstractDiagnosticCollectorVisitor.kt:118) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitSimpleFunction(AbstractDiagnosticCollectorVisitor.kt:30) + at org.jetbrains.kotlin.fir.declarations.FirSimpleFunction.accept(FirSimpleFunction.kt:51) + at org.jetbrains.kotlin.fir.declarations.impl.FirRegularClassImpl.acceptChildren(FirRegularClassImpl.kt:63) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitNestedElements(AbstractDiagnosticCollectorVisitor.kt:38) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitWithDeclarationAndReceiver(AbstractDiagnosticCollectorVisitor.kt:311) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitClassAndChildren(AbstractDiagnosticCollectorVisitor.kt:87) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitRegularClass(AbstractDiagnosticCollectorVisitor.kt:92) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitRegularClass(AbstractDiagnosticCollectorVisitor.kt:30) + at org.jetbrains.kotlin.fir.declarations.FirRegularClass.accept(FirRegularClass.kt:48) + at org.jetbrains.kotlin.fir.declarations.impl.FirFileImpl.acceptChildren(FirFileImpl.kt:57) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitNestedElements(AbstractDiagnosticCollectorVisitor.kt:38) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitFile(AbstractDiagnosticCollectorVisitor.kt:1151) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitFile(AbstractDiagnosticCollectorVisitor.kt:30) + at org.jetbrains.kotlin.fir.declarations.FirFile.accept(FirFile.kt:42) + at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollector.collectDiagnostics(AbstractDiagnosticCollector.kt:36) + at org.jetbrains.kotlin.fir.pipeline.AnalyseKt.runCheckers(analyse.kt:34) + ... 33 more + + diff --git a/README.md b/README.md index b43c51b..d017efe 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,13 @@ A lightweight Android material bottom navigation bar library +exposed setBadge and removeBadge for jvm +implementation 'com.github.Cherdenko:SmoothBottomBar:-SNAPSHOT' + [![](https://jitpack.io/v/ibrahimsn98/SmoothBottomBar.svg)](https://jitpack.io/#ibrahimsn98/SmoothBottomBar) -[![API](https://img.shields.io/badge/API-22%2B-brightgreen.svg?style=flat)](https://android-arsenal.com/api?level=22) +[![API](https://img.shields.io/badge/API-16%2B-brightgreen.svg?style=flat)](https://android-arsenal.com/api?level=16) [![Android Arsenal]( https://img.shields.io/badge/Android%20Arsenal-SmoothBottomBar-green.svg?style=flat )]( https://android-arsenal.com/details/1/7932 ) +[![Android Weekly](https://androidweekly.net/issues/issue-385/badge)](https://androidweekly.net/issues/issue-385) ## GIF @@ -12,7 +16,7 @@ A lightweight Android material bottom navigation bar library ## Design Credits -All design and inspiration credits belongs to [Alejandro Ausejo](https://dribbble.com/shots/6251784-Navigation-Menu-Animation). +All design and inspiration credits belong to [Alejandro Ausejo](https://dribbble.com/shots/6251784-Navigation-Menu-Animation). ## Usage @@ -51,6 +55,7 @@ All design and inspiration credits belongs to [Alejandro Ausejo](https://dribbbl android:layout_width="match_parent" android:layout_height="70dp" app:backgroundColor="@color/colorPrimary" + app:badgeColor="@color/colorBadge" app:menu="@menu/menu_bottom"/> ``` @@ -65,6 +70,275 @@ bottomBar.onItemReselected = { } ``` +OR + +```kotlin +bottomBar.setOnItemSelectedListener(object: OnItemSelectedListener { + override fun onItemSelect(pos: Int) { + status.text = "Item $pos selected" + } +}) + +bottomBar.setOnItemReselectedListener(object: OnItemReselectedListener { + override fun onItemReselect(pos: Int) { + status.text = "Item $pos re-selected" + } +}) +``` + + >**Note:** For projects without kotlin, you may need to add `org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion` to your dependencies since this is a Kotlin library. + +## **Use SmoothBottomBar with [Navigation Components](https://developer.android.com/guide/navigation/).** + +Coupled with the Navigation Component from the [Android Jetpack](https://developer.android.com/jetpack), SmoothBottomBar offers easier navigation within your application by designating navigation to the Navigation Component. This works best when using fragments, as the Navigation component helps to handle your fragment transactions. + +- Setup Navigation Component i.e. Add dependenccy to your project, create a Navigation Graph etc. +- For each Fragment in your Navigation Graph, ensure that the Fragment's `id` is the same as the MenuItems in your Menu i.e res/menu/ folder +```xml + + + + + + + + + + + + +``` + +Navigation Graph i.e res/navigation/ folder +```xml + + + + + + + + + + + + + + +``` + +- In your activity i.e `MainActivity`, create an instance of `PopupMenu` which takes a `context` and an `anchor`(pass in null) and then inflate this `PopupMenu` with the layout menu for the `SmoothBottomBar`. +- Get a reference to your `SmoothBottomBar` and call `setupWithNavController()` which takes in a `Menu` and `NavController`, pass in the menu of the previously instantiated `PopupMenu` i.e.(`popUpMenu.menu`) and your `NavController`. +- Preferably set this up in a function as shown below and call this function i.e. (`setupSmoothBottomMenu()`) in the `onCreate` method of your activity. + +**N.B:** Sample app makes use of [ViewBinding](https://developer.android.com/topic/libraries/view-binding) to get reference to views in the layout. + +```kotlin + private fun setupSmoothBottomMenu() { + binding.bottomBar.setupWithNavController(navController) + } +``` + + +### ActionBar +You can also setup your `ActionBar` with the Navigation Component by calling `setupActionBarWithNavController` and pass in your `NavController`. + +**N.B:** Your app needs to have a `Toolbar` for `setupActionBarWithNavController` to work. If your app theme doesn't have a `Toolbar` i.e. (Theme.AppCompat.Light.NoActionBar) you would need to: + +- Add one to your layout i.e. + +```xml + + + +``` +- Set the support action bar to your `Toolbar` using `setSupportActionBar(your_toolbar_id)` in this case `setSupportActionBar(binding.toolBar)` + +We now have something like this: + +```kotlin + private lateinit var navController: NavController + private lateinit var binding: ActivityMainBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityMainBinding.inflate(layoutInflater) + setContentView(binding.root) + navController = findNavController(R.id.main_fragment) + setupActionBarWithNavController(navController) + setupSmoothBottomMenu() + } + + private fun setupSmoothBottomMenu() { + binding.bottomBar.setupWithNavController(navController) + } + + //set an active fragment programmatically + fun setSelectedItem(pos:Int){ + binding.bottomBar.setSelectedItem(pos) + } + //set badge indicator + fun setBadge(pos:Int){ + binding.bottomBar.setBadge(pos) + } + //remove badge indicator + fun removeBadge(pos:Int){ + binding.bottomBar.removeBadge(pos) + } + + override fun onSupportNavigateUp(): Boolean { + return navController.navigateUp() || super.onSupportNavigateUp() + } +``` + +### Result Demo: + +

+ +### Update [8th May, 2021] +Prior to the [initial addition of this feature](https://github.com/ibrahimsn98/SmoothBottomBar/pull/33), you can now inflate separate menu items for the `SmoothBottomBar` and your `Toolbar`. +- Create the menu item you want to inflate in the `Toolbar` i.e. + +```xml + + + + + + +``` +- Override `OnCreateOptionsMenu` and `onOptionsItemSelected` (ensure it returns `super.onOptionsItemSelected(item)`). Now we have: + +```kotlin + class MainActivity : AppCompatActivity() { + private lateinit var navController: NavController + private lateinit var binding: ActivityMainBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityMainBinding.inflate(layoutInflater) + setContentView(binding.root) + navController = findNavController(R.id.main_fragment) + setupActionBarWithNavController(navController) + setupSmoothBottomMenu() + } + + override fun onCreateOptionsMenu(menu: Menu?): Boolean { + menuInflater.inflate(R.menu.another_menu, menu) + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.another_item_1 -> { + showToast("Another Menu Item 1 Selected") + } + + R.id.another_item_2 -> { + showToast("Another Menu Item 2 Selected") + } + + R.id.another_item_3 -> { + showToast("Another Menu Item 3 Selected") + } + } + return super.onOptionsItemSelected(item) + } + + + private fun showToast(msg: String) { + Toast.makeText(this, msg, Toast.LENGTH_SHORT).show() + } + + private fun setupSmoothBottomMenu() { + binding.bottomBar.setupWithNavController(navController) + } + + //set an active fragment programmatically + fun setSelectedItem(pos:Int){ + binding.bottomBar.setSelectedItem(pos) + } + //set badge indicator + fun setBadge(pos:Int){ + binding.bottomBar.setBadge(pos) + } + //remove badge indicator + fun removeBadge(pos:Int){ + binding.bottomBar.removeBadge(pos) + } + + override fun onSupportNavigateUp(): Boolean { + return navController.navigateUp() || super.onSupportNavigateUp() + } +} +``` + +### Select Bottom Item from any fragment +```kotlin + buttonId.setOnClickListener { + (requireActivity() as MainActivity).setSelectedItem(2) + } +``` + + +### Result Demo: + +

+ + ## Customization ```xml @@ -72,35 +346,107 @@ bottomBar.onItemReselected = { android:id="@+id/bottomBar" android:layout_width="match_parent" android:layout_height="70dp" + app:menu="" app:backgroundColor="" - app:textColor="" - app:textSize="" - app:iconSize="" app:indicatorColor="" + app:indicatorRadius="" + app:cornerRadius="" + app:corners="" app:sideMargins="" app:itemPadding="" + app:textColor="" + app:badgeColor="" + app:itemFontFamily="" + app:textSize="" + app:iconSize="" app:iconTint="" app:iconTintActive="" app:activeItem="" - app:duration="" - app:menu=""/> + app:duration="" /> ``` ## Setup +> Follow me on Twitter [@ibrahimsn98](https://twitter.com/ibrahimsn98) + ```gradle - allprojects { - repositories { - ... - maven { url 'https://jitpack.io' } - } - } - - dependencies { - implementation 'com.github.ibrahimsn98:SmoothBottomBar:1.4' - } +//project label build.gradle +buildscript { + repositories { + .... + maven { url 'https://jitpack.io' } + } +} + +allprojects { + repositories { + ....... + maven { url 'https://www.jitpack.io' } + } +} +//app label build.gradle +dependencies { + implementation 'com.github.ibrahimsn98:SmoothBottomBar:1.7.9' +} ``` +## Contributors ✨ + + + + + + + + + + + + + +
+ +
+ brookmg +
+
+ +
+ rezaepsilon0 +
+
+ +
+ amitdash291 +
+
+ +
+ tobiasschuerg +
+
+ +
+ mayokunthefirst +
+
+ +
+ FannyDemey +
+
+ +
+ Milad akarie +
+
+ +
+ Milon27 +
+
+ + ## License ``` diff --git a/app/build.gradle b/app/build.gradle index 59ea6c9..53bdf12 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,37 +1,62 @@ apply plugin: 'com.android.application' - apply plugin: 'kotlin-android' -apply plugin: 'kotlin-android-extensions' +def nav_version = "2.9.5" android { - compileSdkVersion 28 + namespace 'me.ibrahimsn.smoothbottombar' + compileSdk = 36 + defaultConfig { applicationId "me.ibrahimsn.smoothbottombar" - minSdkVersion 22 - targetSdkVersion 28 + minSdkVersion 24 + targetSdkVersion 36 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + multiDexEnabled true } + buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_21 + targetCompatibility JavaVersion.VERSION_21 + } + + kotlinOptions { + jvmTarget = '21' + } + + buildFeatures { + viewBinding true + dataBinding true + } } dependencies { - implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'androidx.appcompat:appcompat:1.1.0' - implementation 'androidx.core:core-ktx:1.1.0' - implementation 'androidx.constraintlayout:constraintlayout:1.1.3' - implementation project(":lib") - testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test.ext:junit:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' + // ✅ Use a valid Kotlin version + implementation "org.jetbrains.kotlin:kotlin-stdlib:2.2.21" + + implementation 'androidx.appcompat:appcompat:1.7.1' + implementation 'androidx.core:core-ktx:1.17.0' + implementation 'androidx.constraintlayout:constraintlayout:2.2.1' + implementation "androidx.multidex:multidex:2.0.1" + implementation project(":lib") + androidTestImplementation project(path: ':app') + implementation 'androidx.legacy:legacy-support-v4:1.0.0' + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.2' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' + + // Navigation Architecture Component + implementation "androidx.navigation:navigation-fragment-ktx:$nav_version" + implementation "androidx.navigation:navigation-ui-ktx:$nav_version" } diff --git a/app/src/androidTest/java/me/ibrahimsn/smoothbottombar/ExampleInstrumentedTest.kt b/app/src/androidTest/java/me/ibrahimsn/smoothbottombar/ExampleInstrumentedTest.kt deleted file mode 100644 index e48a601..0000000 --- a/app/src/androidTest/java/me/ibrahimsn/smoothbottombar/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,24 +0,0 @@ -package me.ibrahimsn.smoothbottombar - -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.ext.junit.runners.AndroidJUnit4 - -import org.junit.Test -import org.junit.runner.RunWith - -import org.junit.Assert.* - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("me.ibrahimsn.smoothbottombar", appContext.packageName) - } -} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 98c63d9..1693803 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,15 +1,16 @@ + +> - + diff --git a/app/src/main/java/me/ibrahimsn/smoothbottombar/FirstFragment.kt b/app/src/main/java/me/ibrahimsn/smoothbottombar/FirstFragment.kt new file mode 100644 index 0000000..da573f1 --- /dev/null +++ b/app/src/main/java/me/ibrahimsn/smoothbottombar/FirstFragment.kt @@ -0,0 +1,29 @@ +package me.ibrahimsn.smoothbottombar + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView + +/** + * A simple [Fragment] subclass. + */ +class FirstFragment : Fragment(R.layout.fragment_first) { + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + val textView=view.findViewById(R.id.textView) + textView.setOnClickListener { + (requireActivity() as MainActivity).setSelectedItem(2) + (requireActivity() as MainActivity).removeBadge(2) + } + + + (requireActivity() as MainActivity).setBadge(2) + (requireActivity() as MainActivity).setBadge(0) + } + +} diff --git a/app/src/main/java/me/ibrahimsn/smoothbottombar/FourthFragment.kt b/app/src/main/java/me/ibrahimsn/smoothbottombar/FourthFragment.kt new file mode 100644 index 0000000..9927e9f --- /dev/null +++ b/app/src/main/java/me/ibrahimsn/smoothbottombar/FourthFragment.kt @@ -0,0 +1,22 @@ +package me.ibrahimsn.smoothbottombar + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup + +/** + * A simple [Fragment] subclass. + */ +class FourthFragment : Fragment() { + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + // Inflate the layout for this fragment + return inflater.inflate(R.layout.fragment_fourth, container, false) + } + +} diff --git a/app/src/main/java/me/ibrahimsn/smoothbottombar/MainActivity.kt b/app/src/main/java/me/ibrahimsn/smoothbottombar/MainActivity.kt index 4232648..4b7ea73 100644 --- a/app/src/main/java/me/ibrahimsn/smoothbottombar/MainActivity.kt +++ b/app/src/main/java/me/ibrahimsn/smoothbottombar/MainActivity.kt @@ -1,20 +1,79 @@ package me.ibrahimsn.smoothbottombar import android.os.Bundle +import android.view.Menu +import android.view.MenuItem +import android.widget.PopupMenu +import android.widget.Toast import androidx.appcompat.app.AppCompatActivity -import kotlinx.android.synthetic.main.activity_main.* +import androidx.navigation.NavController +import androidx.navigation.findNavController +import androidx.navigation.ui.setupActionBarWithNavController -class MainActivity : AppCompatActivity(R.layout.activity_main) { +import me.ibrahimsn.smoothbottombar.databinding.ActivityMainBinding +import me.ibrahimsn.lib.* +class MainActivity : AppCompatActivity() { + private lateinit var navController: NavController + private lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + binding = ActivityMainBinding.inflate(layoutInflater) + setContentView(binding.root) + navController = findNavController(R.id.main_fragment) + setupActionBarWithNavController(navController) + setupSmoothBottomMenu() + } - bottomBar.onItemSelected = { - status.text = "Item $it selected" - } - bottomBar.onItemReselected = { - status.text = "Item $it re-selected" + override fun onCreateOptionsMenu(menu: Menu?): Boolean { + menuInflater.inflate(R.menu.another_menu, menu) + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.another_item_1 -> { + showToast("Another Menu Item 1 Selected") + } + + R.id.another_item_2 -> { + showToast("Another Menu Item 2 Selected") + } + + R.id.another_item_3 -> { + showToast("Another Menu Item 3 Selected") + } } + return super.onOptionsItemSelected(item) + } + + //set an active fragment programmatically + fun setSelectedItem(pos:Int){ + binding.bottomBar.setSelectedItem(pos) + } + //set badge indicator + fun setBadge(pos:Int){ + binding.bottomBar.setBadge(pos) + } + //remove badge indicator + fun removeBadge(pos:Int){ + binding.bottomBar.removeBadge(pos) + } + + private fun showToast(msg: String) { + Toast.makeText(this, msg, Toast.LENGTH_SHORT).show() + } + + private fun setupSmoothBottomMenu() { + val popupMenu = PopupMenu(this, null) + popupMenu.inflate(R.menu.menu_bottom) + val menu = popupMenu.menu + //binding.bottomBar.setupWithNavController(menu, navController) + binding.bottomBar.setupWithNavController( navController) + } + + override fun onSupportNavigateUp(): Boolean { + return navController.navigateUp() || super.onSupportNavigateUp() } } diff --git a/app/src/main/java/me/ibrahimsn/smoothbottombar/SecondFragment.kt b/app/src/main/java/me/ibrahimsn/smoothbottombar/SecondFragment.kt new file mode 100644 index 0000000..27eaafa --- /dev/null +++ b/app/src/main/java/me/ibrahimsn/smoothbottombar/SecondFragment.kt @@ -0,0 +1,22 @@ +package me.ibrahimsn.smoothbottombar + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup + +/** + * A simple [Fragment] subclass. + */ +class SecondFragment : Fragment() { + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + // Inflate the layout for this fragment + return inflater.inflate(R.layout.fragment_second, container, false) + } + +} diff --git a/app/src/main/java/me/ibrahimsn/smoothbottombar/ThirdFragment.kt b/app/src/main/java/me/ibrahimsn/smoothbottombar/ThirdFragment.kt new file mode 100644 index 0000000..ac095a5 --- /dev/null +++ b/app/src/main/java/me/ibrahimsn/smoothbottombar/ThirdFragment.kt @@ -0,0 +1,22 @@ +package me.ibrahimsn.smoothbottombar + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup + +/** + * A simple [Fragment] subclass. + */ +class ThirdFragment : Fragment() { + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + // Inflate the layout for this fragment + return inflater.inflate(R.layout.fragment_third, container, false) + } + +} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index f75e671..bcca3f5 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -6,11 +6,13 @@ android:layout_height="match_parent" tools:context=".MainActivity"> - diff --git a/app/src/main/res/layout/fragment_first.xml b/app/src/main/res/layout/fragment_first.xml new file mode 100644 index 0000000..d332a6e --- /dev/null +++ b/app/src/main/res/layout/fragment_first.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_fourth.xml b/app/src/main/res/layout/fragment_fourth.xml new file mode 100644 index 0000000..11750b7 --- /dev/null +++ b/app/src/main/res/layout/fragment_fourth.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_second.xml b/app/src/main/res/layout/fragment_second.xml new file mode 100644 index 0000000..13e8e06 --- /dev/null +++ b/app/src/main/res/layout/fragment_second.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_third.xml b/app/src/main/res/layout/fragment_third.xml new file mode 100644 index 0000000..f77dada --- /dev/null +++ b/app/src/main/res/layout/fragment_third.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/another_menu.xml b/app/src/main/res/menu/another_menu.xml new file mode 100644 index 0000000..d79bd4d --- /dev/null +++ b/app/src/main/res/menu/another_menu.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_bottom.xml b/app/src/main/res/menu/menu_bottom.xml index 8c4dfe3..5fccd25 100644 --- a/app/src/main/res/menu/menu_bottom.xml +++ b/app/src/main/res/menu/menu_bottom.xml @@ -2,23 +2,27 @@ \ No newline at end of file diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml new file mode 100644 index 0000000..2686232 --- /dev/null +++ b/app/src/main/res/navigation/nav_graph.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml new file mode 100644 index 0000000..01569c9 --- /dev/null +++ b/app/src/main/res/values-ar/strings.xml @@ -0,0 +1,8 @@ + + + SmoothBottomBar + الرئيسية + حسابي + المتجر + المتصدرين + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index ad39811..52acda9 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -6,4 +6,5 @@ #ffffff #A3FFFFFF + #f4d415 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b567c90..52be47b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -5,4 +5,7 @@ Leaderboard Store Profile + + + Hello blank fragment diff --git a/build.gradle b/build.gradle index f08173b..baae2e5 100644 --- a/build.gradle +++ b/build.gradle @@ -1,26 +1,39 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. +def nav_version = "2.9.5" +def agp_version = '8.13.0' buildscript { - ext.kotlin_version = '1.3.50' + ext.kotlin_version = '2.2.21' + ext { + agp_version = '8.13.0' + } repositories { - google() - jcenter() + google() + mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:3.5.1' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "com.android.tools.build:gradle:$agp_version" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:2.2.21" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } -} + +} +configurations.all { + resolutionStrategy { + force "org.jetbrains.kotlin:kotlin-stdlib:1.9.10" + } +} allprojects { repositories { google() - jcenter() + mavenCentral() + } + } task clean(type: Delete) { diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index f6b961f..980502d 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 951e07d..e3f9171 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,8 @@ -#Fri Oct 25 12:42:27 EET 2019 +#Tue Nov 04 23:36:10 CET 2025 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip diff --git a/gradlew b/gradlew index cccdd3d..faf9300 100755 --- a/gradlew +++ b/gradlew @@ -1,78 +1,129 @@ -#!/usr/bin/env sh +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -81,92 +132,120 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=$((i+1)) + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" fi +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index f955316..9d21a21 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,4 +1,22 @@ -@if "%DEBUG%" == "" @echo off +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -9,25 +27,29 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -35,48 +57,36 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/lib/build.gradle b/lib/build.gradle index 423e727..353e6d4 100644 --- a/lib/build.gradle +++ b/lib/build.gradle @@ -1,18 +1,19 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-android-extensions' -android { - compileSdkVersion 28 + + apply plugin: 'com.android.library' + apply plugin: 'kotlin-android' +def nav_version = "2.9.5" +android { + compileSdkVersion 36 + namespace 'me.ibrahimsn.lib' defaultConfig { - minSdkVersion 22 - targetSdkVersion 28 - versionCode 1 - versionName "1.0" + minSdkVersion 24 + targetSdkVersion 36 + multiDexEnabled true + + - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles 'consumer-rules.pro' } buildTypes { @@ -21,15 +22,24 @@ android { proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } + compileOptions { + sourceCompatibility JavaVersion.VERSION_21 + targetCompatibility JavaVersion.VERSION_21 + } + kotlinOptions { + jvmTarget = JavaVersion.VERSION_21 + } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'androidx.appcompat:appcompat:1.1.0' - implementation 'androidx.core:core-ktx:1.1.0' - testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test.ext:junit:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' + implementation "org.jetbrains.kotlin:kotlin-stdlib:1.9.10" + implementation 'androidx.appcompat:appcompat:1.7.1' + implementation 'androidx.core:core-ktx:1.17.0' + implementation "androidx.multidex:multidex:2.0.1" + + //Navigation Architecture Component + implementation "androidx.navigation:navigation-fragment-ktx:$nav_version" + implementation "androidx.navigation:navigation-ui-ktx:$nav_version" } diff --git a/lib/src/main/AndroidManifest.xml b/lib/src/main/AndroidManifest.xml index 556d76f..1f5c0c5 100644 --- a/lib/src/main/AndroidManifest.xml +++ b/lib/src/main/AndroidManifest.xml @@ -1,2 +1,10 @@ + > + + + + + + diff --git a/lib/src/main/java/me/ibrahimsn/lib/AccessibleExploreByTouchHelper.kt b/lib/src/main/java/me/ibrahimsn/lib/AccessibleExploreByTouchHelper.kt new file mode 100644 index 0000000..f486fd2 --- /dev/null +++ b/lib/src/main/java/me/ibrahimsn/lib/AccessibleExploreByTouchHelper.kt @@ -0,0 +1,70 @@ +package me.ibrahimsn.lib + +import android.graphics.Rect +import android.os.Bundle +import androidx.core.view.accessibility.AccessibilityNodeInfoCompat +import androidx.customview.widget.ExploreByTouchHelper + +class AccessibleExploreByTouchHelper( + private val host : SmoothBottomBar, + private val bottomBarItems : List, + private val onClickAction : (id : Int) -> Unit +) : ExploreByTouchHelper(host) { + + override fun getVisibleVirtualViews(virtualViewIds: MutableList) { + // defining simple ids for each item of the bottom bar + for (i in bottomBarItems.indices) { + virtualViewIds.add(i) + } + } + + override fun getVirtualViewAt(x: Float, y: Float): Int { + val itemWidth = host.width / bottomBarItems.size + return (x / itemWidth).toInt() + } + + /** + * setBoundsInParent is required by [ExploreByTouchHelper] + */ + @Suppress("DEPRECATION") + override fun onPopulateNodeForVirtualView( + virtualViewId: Int, + node: AccessibilityNodeInfoCompat + ) { + node.className = BottomBarItem::class.simpleName + node.contentDescription = bottomBarItems[virtualViewId].contentDescription + node.isClickable = true + node.isFocusable = true + node.isScreenReaderFocusable = true + + node.addAction(AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_CLICK) + + node.isSelected = host.itemActiveIndex == virtualViewId + + val bottomItemBoundRect = updateBoundsForBottomItem(virtualViewId) + node.setBoundsInParent(bottomItemBoundRect) + } + + override fun onPerformActionForVirtualView( + virtualViewId: Int, + action: Int, + arguments: Bundle? + ): Boolean { + if (action == AccessibilityNodeInfoCompat.ACTION_CLICK) { + onClickAction.invoke(virtualViewId) + return true + } + return false + } + + private fun updateBoundsForBottomItem(index: Int): Rect { + val itemBounds = Rect() + val itemWidth = host.width / bottomBarItems.size + val left = index * itemWidth + itemBounds.left = left + itemBounds.top = 0 + itemBounds.right = (left + itemWidth) + itemBounds.bottom = host.height + return itemBounds + } +} diff --git a/lib/src/main/java/me/ibrahimsn/lib/BottomBarItem.kt b/lib/src/main/java/me/ibrahimsn/lib/BottomBarItem.kt index 74758e4..e353724 100644 --- a/lib/src/main/java/me/ibrahimsn/lib/BottomBarItem.kt +++ b/lib/src/main/java/me/ibrahimsn/lib/BottomBarItem.kt @@ -3,4 +3,10 @@ package me.ibrahimsn.lib import android.graphics.RectF import android.graphics.drawable.Drawable -data class BottomBarItem(var title: String, val icon: Drawable, var rect: RectF = RectF(), var alpha: Int) \ No newline at end of file +data class BottomBarItem ( + var title: String, + var contentDescription : String, + val icon: Drawable, + var rect: RectF = RectF(), + var alpha: Int +) diff --git a/lib/src/main/java/me/ibrahimsn/lib/BottomBarParser.kt b/lib/src/main/java/me/ibrahimsn/lib/BottomBarParser.kt index 2be07a6..6f3acb9 100644 --- a/lib/src/main/java/me/ibrahimsn/lib/BottomBarParser.kt +++ b/lib/src/main/java/me/ibrahimsn/lib/BottomBarParser.kt @@ -1,14 +1,13 @@ package me.ibrahimsn.lib import android.content.Context +import android.content.res.Resources import android.content.res.XmlResourceParser import android.graphics.drawable.Drawable +import androidx.annotation.XmlRes import androidx.core.content.ContextCompat -import me.ibrahimsn.lib.Constants.ICON_ATTRIBUTE -import me.ibrahimsn.lib.Constants.ITEM_TAG -import me.ibrahimsn.lib.Constants.TITLE_ATTRIBUTE -class BottomBarParser(private val context: Context, res: Int) { +internal class BottomBarParser(private val context: Context, @XmlRes res: Int) { private val parser: XmlResourceParser = context.resources.getXml(res) @@ -18,33 +17,55 @@ class BottomBarParser(private val context: Context, res: Int) { do { eventType = parser.next() - - if (eventType == XmlResourceParser.START_TAG && parser.name == ITEM_TAG) + if (eventType == XmlResourceParser.START_TAG && parser.name == ITEM_TAG) { items.add(getTabConfig(parser)) - + } } while (eventType != XmlResourceParser.END_DOCUMENT) - return items.toList() + return items } private fun getTabConfig(parser: XmlResourceParser): BottomBarItem { val attributeCount = parser.attributeCount var itemText: String? = null var itemDrawable: Drawable? = null + var contentDescription : String? = null - for (i in 0 until attributeCount) - when (parser.getAttributeName(i)) { - ICON_ATTRIBUTE -> itemDrawable = - ContextCompat.getDrawable(context, parser.getAttributeResourceValue(i, 0)) - TITLE_ATTRIBUTE -> { - itemText = try { - context.getString(parser.getAttributeResourceValue(i, 0)) - } catch (e: Exception) { - parser.getAttributeValue(i) - } + for (index in 0 until attributeCount) { + when (parser.getAttributeName(index)) { + ICON_ATTRIBUTE -> itemDrawable = ContextCompat.getDrawable( + context, + parser.getAttributeResourceValue(index, 0) + ) + TITLE_ATTRIBUTE -> itemText = try { + context.getString(parser.getAttributeResourceValue(index, 0)) + } catch (notFoundException: Resources.NotFoundException) { + parser.getAttributeValue(index) + } + CONTENT_DESCRIPTION_ATTRIBUTE -> contentDescription = try { + context.getString(parser.getAttributeResourceValue(index, 0)) + } catch (notFoundException: Resources.NotFoundException) { + parser.getAttributeValue(index) } } + } + + if (itemDrawable == null) { + throw Throwable("Item icon can not be null!") + } + + return BottomBarItem( + itemText.toString(), + contentDescription ?: itemText.toString(), + itemDrawable, + alpha = 0 + ) + } - return BottomBarItem(itemText ?: "", itemDrawable!!, alpha = 0) + companion object { + private const val ITEM_TAG = "item" + private const val ICON_ATTRIBUTE = "icon" + private const val TITLE_ATTRIBUTE = "title" + private const val CONTENT_DESCRIPTION_ATTRIBUTE = "contentDescription" } -} \ No newline at end of file +} diff --git a/lib/src/main/java/me/ibrahimsn/lib/Constants.kt b/lib/src/main/java/me/ibrahimsn/lib/Constants.kt deleted file mode 100644 index 6bee9a1..0000000 --- a/lib/src/main/java/me/ibrahimsn/lib/Constants.kt +++ /dev/null @@ -1,12 +0,0 @@ -package me.ibrahimsn.lib - -/** - * This is a static class to hold constants - */ -object Constants { - const val ITEM_TAG = "item" - const val ICON_ATTRIBUTE = "icon" - const val TITLE_ATTRIBUTE = "title" - const val WHITE_COLOR_HEX = "#FFFFFF" -} - diff --git a/lib/src/main/java/me/ibrahimsn/lib/NavigationComponentHelper.kt b/lib/src/main/java/me/ibrahimsn/lib/NavigationComponentHelper.kt new file mode 100644 index 0000000..d4b565c --- /dev/null +++ b/lib/src/main/java/me/ibrahimsn/lib/NavigationComponentHelper.kt @@ -0,0 +1,73 @@ +package me.ibrahimsn.lib + +import android.os.Bundle +import android.view.Menu +import androidx.annotation.IdRes +import androidx.navigation.NavController +import androidx.navigation.NavDestination +import androidx.navigation.ui.NavigationUI +import java.lang.ref.WeakReference + +/** + * Created by Mayokun Adeniyi on 24/04/2020. + */ +class NavigationComponentHelper { + + companion object { + + fun setupWithNavController( + menu: Menu, + smoothBottomBar: SmoothBottomBar, + navController: NavController + ) { + smoothBottomBar.onItemSelectedListener = object : OnItemSelectedListener { + override fun onItemSelect(pos: Int): Boolean { + return NavigationUI.onNavDestinationSelected(menu.getItem(pos), navController) + } + } + + val weakReference = WeakReference(smoothBottomBar) + + navController.addOnDestinationChangedListener(object : + NavController.OnDestinationChangedListener { + + override fun onDestinationChanged( + controller: NavController, + destination: NavDestination, + arguments: Bundle? + ) { + val view = weakReference.get() + + if (view == null) { + navController.removeOnDestinationChangedListener(this) + return + } + + for (h in 0 until menu.size()) { + val menuItem = menu.getItem(h) + if (matchDestination(destination, menuItem.itemId)) { + menuItem.isChecked = true + smoothBottomBar.itemActiveIndex = h + } + } + } + }) + } + + /** + * Determines whether the given `destId` matches the NavDestination. This handles + * both the default case (the destination's id matches the given id) and the nested case where + * the given id is a parent/grandparent/etc of the destination. + */ + fun matchDestination( + destination: NavDestination, + @IdRes destId: Int + ): Boolean { + var currentDestination: NavDestination? = destination + while (currentDestination!!.id != destId && currentDestination.parent != null) { + currentDestination = currentDestination.parent + } + return currentDestination.id == destId + } + } +} diff --git a/lib/src/main/java/me/ibrahimsn/lib/OnItemReselectedListener.kt b/lib/src/main/java/me/ibrahimsn/lib/OnItemReselectedListener.kt new file mode 100644 index 0000000..df38116 --- /dev/null +++ b/lib/src/main/java/me/ibrahimsn/lib/OnItemReselectedListener.kt @@ -0,0 +1,6 @@ +package me.ibrahimsn.lib + +interface OnItemReselectedListener { + + fun onItemReselect(pos: Int) +} diff --git a/lib/src/main/java/me/ibrahimsn/lib/OnItemSelectedListener.kt b/lib/src/main/java/me/ibrahimsn/lib/OnItemSelectedListener.kt new file mode 100644 index 0000000..93660aa --- /dev/null +++ b/lib/src/main/java/me/ibrahimsn/lib/OnItemSelectedListener.kt @@ -0,0 +1,6 @@ +package me.ibrahimsn.lib + +interface OnItemSelectedListener { + + fun onItemSelect(pos: Int): Boolean +} diff --git a/lib/src/main/java/me/ibrahimsn/lib/SmoothBottomBar.kt b/lib/src/main/java/me/ibrahimsn/lib/SmoothBottomBar.kt index f708152..d38228b 100644 --- a/lib/src/main/java/me/ibrahimsn/lib/SmoothBottomBar.kt +++ b/lib/src/main/java/me/ibrahimsn/lib/SmoothBottomBar.kt @@ -8,48 +8,295 @@ import android.graphics.Canvas import android.graphics.Color import android.graphics.Paint import android.graphics.RectF +import android.os.Build import android.util.AttributeSet +import android.view.Menu import android.view.MotionEvent import android.view.View +import android.view.accessibility.AccessibilityEvent import android.view.animation.DecelerateInterpolator +import android.widget.PopupMenu +import androidx.annotation.* import androidx.core.content.res.ResourcesCompat import androidx.core.graphics.drawable.DrawableCompat -import me.ibrahimsn.lib.Constants.WHITE_COLOR_HEX -import kotlin.math.abs +import androidx.core.view.ViewCompat +import androidx.navigation.NavController +import androidx.navigation.Navigation +import androidx.navigation.findNavController +import androidx.navigation.ui.NavigationUI +import me.ibrahimsn.lib.ext.d2p +import kotlin.math.roundToInt + +class SmoothBottomBar @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = R.attr.SmoothBottomBarStyle +) : View(context, attrs, defStyleAttr) { + // Dynamic Variables + private var itemWidth: Float = 0F -class SmoothBottomBar : View { - - /** - * Default attribute values - */ - private var barBackgroundColor = Color.parseColor(WHITE_COLOR_HEX) - private var barIndicatorColor = Color.parseColor("#2DFFFFFF") - private var barSideMargins = d2p(10f) + private var currentIconTint = itemIconTintActive - private var itemPadding = d2p(10f) - private var itemAnimDuration = 300L + private var indicatorLocation = barSideMargins + private var _iconBackgroundColor: Int = Color.TRANSPARENT + private val iconBackgroundPaint = Paint().apply { + isAntiAlias = true + style = Paint.Style.FILL + color = _iconBackgroundColor + } + // New padding backing (in PX) + @Dimension + private var _iconBackgroundPadding: Float = context.d2p(DEFAULT_ICON_BG_PADDING) + + // Public accessors + var iconBackgroundColor: Int + @ColorInt get() = _iconBackgroundColor + set(@ColorInt value) { + _iconBackgroundColor = value + iconBackgroundPaint.color = value + invalidate() + } - private var itemIconSize = d2p(18f) - private var itemIconMargin = d2p(4f) - private var itemIconTint = Color.parseColor("#C8FFFFFF") - private var itemIconTintActive = Color.parseColor(WHITE_COLOR_HEX) + var iconBackgroundPadding: Float + @Dimension get() = _iconBackgroundPadding + set(@Dimension value) { + _iconBackgroundPadding = value + invalidate() + } - private var itemTextColor = Color.parseColor(WHITE_COLOR_HEX) - private var itemTextSize = d2p(11.0f) - private var itemFontFamily = 0 + private val rect = RectF() - /** - * Dynamic variables - */ - private var itemWidth = 0f - private var activeItem = 0 - private var currentIconTint = itemIconTintActive - private var indicatorLocation = barSideMargins + // Cache for performance optimization + private val iconBackgroundRect = RectF() + private var cachedTextHeight: Float = 0f + private var needsRecalculation = true private var items = listOf() - var onItemSelected: (Int) -> Unit = {} - var onItemReselected: (Int) -> Unit = {} + // Attribute Defaults + @ColorInt + private var _barBackgroundColor = Color.WHITE + + @ColorInt + private var _barIndicatorColor = Color.parseColor(DEFAULT_INDICATOR_COLOR) + + @Dimension + private var _barIndicatorRadius = context.d2p(DEFAULT_CORNER_RADIUS) + + @Dimension + private var _barSideMargins = context.d2p(DEFAULT_SIDE_MARGIN) + + @Dimension + private var _barCornerRadius = context.d2p(DEFAULT_BAR_CORNER_RADIUS) + + private var _barCorners = DEFAULT_BAR_CORNERS + + @Dimension + private var _itemPadding = context.d2p(DEFAULT_ITEM_PADDING) + + @Dimension + private var _itemSpacing = context.d2p(DEFAULT_ITEM_SPACING) + + private var _itemAnimDuration = DEFAULT_ANIM_DURATION + + @Dimension + private var _itemIconSize = context.d2p(DEFAULT_ICON_SIZE) + + @Dimension + private var _itemIconMargin = context.d2p(DEFAULT_ICON_MARGIN) + + @ColorInt + private var _itemIconTint = Color.parseColor(DEFAULT_TINT) + + @ColorInt + private var _itemIconTintActive = Color.WHITE + + @ColorInt + private var _itemTextColor = Color.WHITE + + @ColorInt + private var _itemBadgeColor = Color.RED + + @Dimension + private var _itemTextSize = context.d2p(DEFAULT_TEXT_SIZE) + + @FontRes + private var _itemFontFamily: Int = INVALID_RES + + @XmlRes + private var _itemMenuRes: Int = INVALID_RES + + private var _itemActiveIndex: Int = 0 + + lateinit var menu: Menu + + + private val badge_arr = HashSet() + + // Core Attributes + var barBackgroundColor: Int + @ColorInt get() = _barBackgroundColor + set(@ColorInt value) { + _barBackgroundColor = value + paintBackground.color = value + invalidate() + } + + var barIndicatorColor: Int + @ColorInt get() = _barIndicatorColor + set(@ColorInt value) { + _barIndicatorColor = value + paintIndicator.color = value + invalidate() + } + + var barIndicatorRadius: Float + @Dimension get() = _barIndicatorRadius + set(@Dimension value) { + _barIndicatorRadius = value + invalidate() + } + + var barSideMargins: Float + @Dimension get() = _barSideMargins + set(@Dimension value) { + _barSideMargins = value + invalidate() + } + + var barCornerRadius: Float + @Dimension get() = _barCornerRadius + set(@Dimension value) { + _barCornerRadius = value + invalidate() + } + + var barCorners: Int + get() = _barCorners + set(value) { + _barCorners = value + invalidate() + } + + var itemTextSize: Float + @Dimension get() = _itemTextSize + set(@Dimension value) { + _itemTextSize = value + paintText.textSize = value + invalidate() + } + + var itemTextColor: Int + @ColorInt get() = _itemTextColor + set(@ColorInt value) { + _itemTextColor = value + paintText.color = value + invalidate() + } + var itemBadgeColor: Int + @ColorInt get() = _itemBadgeColor + set(@ColorInt value) { + _itemBadgeColor = value + badgePaint.color = value + invalidate() + } + + var itemPadding: Float + @Dimension get() = _itemPadding + set(@Dimension value) { + _itemPadding = value + needsRecalculation = true + invalidate() + } + + var itemSpacing: Float + @Dimension get() = _itemSpacing + set(@Dimension value) { + _itemSpacing = value + needsRecalculation = true + invalidate() + } + + var itemAnimDuration: Long + get() = _itemAnimDuration + set(value) { + _itemAnimDuration = value + } + + var itemIconSize: Float + @Dimension get() = _itemIconSize + set(@Dimension value) { + _itemIconSize = value + invalidate() + } + + var itemIconMargin: Float + @Dimension get() = _itemIconMargin + set(@Dimension value) { + _itemIconMargin = value + invalidate() + } + + var itemIconTint: Int + @ColorInt get() = _itemIconTint + set(@ColorInt value) { + _itemIconTint = value + invalidate() + } + + var itemIconTintActive: Int + @ColorInt get() = _itemIconTintActive + set(@ColorInt value) { + _itemIconTintActive = value + invalidate() + } + + var itemFontFamily: Int + @FontRes get() = _itemFontFamily + set(@FontRes value) { + _itemFontFamily = value + if (value != INVALID_RES) { + paintText.typeface = ResourcesCompat.getFont(context, value) + invalidate() + } + } + + var itemMenuRes: Int + @XmlRes get() = _itemMenuRes + set(value) { + _itemMenuRes = value + val popupMenu = PopupMenu(context, null) + popupMenu.inflate(value) + this.menu = popupMenu.menu + if (value != INVALID_RES) { + items = BottomBarParser(context, value).parse() + invalidate() + } + } + + var itemActiveIndex: Int + get() = _itemActiveIndex + set(value) { + _itemActiveIndex = value + applyItemActiveIndex() + } + + + // Listeners + var onItemSelectedListener: OnItemSelectedListener? = null + + var onItemReselectedListener: OnItemReselectedListener? = null + + var onItemSelected: ((Int) -> Unit)? = null + + var onItemReselected: ((Int) -> Unit)? = null + + // Paints + private val paintBackground = Paint().apply { + isAntiAlias = true + style = Paint.Style.FILL + color = barIndicatorColor + } private val paintIndicator = Paint().apply { isAntiAlias = true @@ -57,7 +304,13 @@ class SmoothBottomBar : View { color = barIndicatorColor } - private val paintText= Paint().apply { + private val badgePaint = Paint().apply { + isAntiAlias = true + style = Paint.Style.FILL + color = itemBadgeColor + } + + private val paintText = Paint().apply { isAntiAlias = true style = Paint.Style.FILL color = itemTextColor @@ -66,158 +319,559 @@ class SmoothBottomBar : View { isFakeBoldText = true } - constructor(context: Context) : super(context) - constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) { - val typedArray = context.theme.obtainStyledAttributes(attrs, R.styleable.SmoothBottomBar, 0, 0) - barBackgroundColor = typedArray.getColor(R.styleable.SmoothBottomBar_backgroundColor, this.barBackgroundColor) - barIndicatorColor = typedArray.getColor(R.styleable.SmoothBottomBar_indicatorColor, this.barIndicatorColor) - barSideMargins = typedArray.getDimension(R.styleable.SmoothBottomBar_sideMargins, this.barSideMargins) - itemPadding = typedArray.getDimension(R.styleable.SmoothBottomBar_itemPadding, this.itemPadding) - itemTextColor = typedArray.getColor(R.styleable.SmoothBottomBar_textColor, this.itemTextColor) - itemTextSize = typedArray.getDimension(R.styleable.SmoothBottomBar_textSize, this.itemTextSize) - itemIconSize = typedArray.getDimension(R.styleable.SmoothBottomBar_iconSize, this.itemIconSize) - itemIconTint = typedArray.getColor(R.styleable.SmoothBottomBar_iconTint, this.itemIconTint) - itemIconTintActive = typedArray.getColor(R.styleable.SmoothBottomBar_iconTintActive, this.itemIconTintActive) - activeItem = typedArray.getInt(R.styleable.SmoothBottomBar_activeItem, this.activeItem) - itemFontFamily = typedArray.getResourceId(R.styleable.SmoothBottomBar_itemFontFamily, this.itemFontFamily) - itemAnimDuration = typedArray.getInt(R.styleable.SmoothBottomBar_duration, this.itemAnimDuration.toInt()).toLong() - items = BottomBarParser(context, typedArray.getResourceId(R.styleable.SmoothBottomBar_menu, 0)).parse() - typedArray.recycle() + private var exploreByTouchHelper: AccessibleExploreByTouchHelper - setBackgroundColor(barBackgroundColor) + init { + obtainStyledAttributes(attrs, defStyleAttr) + exploreByTouchHelper = AccessibleExploreByTouchHelper(this, items, ::onClickAction) - // Update default attribute values - paintIndicator.color = barIndicatorColor - paintText.color = itemTextColor - paintText.textSize = itemTextSize + ViewCompat.setAccessibilityDelegate(this, exploreByTouchHelper) - if (itemFontFamily != 0) - paintText.typeface = ResourcesCompat.getFont(context, itemFontFamily) + // Enable hardware acceleration for better performance + setLayerType(LAYER_TYPE_HARDWARE, null) + } + + private fun obtainStyledAttributes(attrs: AttributeSet?, defStyleAttr: Int) { + val typedArray = context.theme.obtainStyledAttributes( + attrs, + R.styleable.SmoothBottomBar, + defStyleAttr, + 0 + ) + + try { + iconBackgroundColor = typedArray.getColor( + R.styleable.SmoothBottomBar_iconBackgroundColor, + iconBackgroundColor + ) + iconBackgroundPadding = typedArray.getDimension( + R.styleable.SmoothBottomBar_iconBackgroundPadding, + iconBackgroundPadding + ) + barBackgroundColor = typedArray.getColor( + R.styleable.SmoothBottomBar_backgroundColor, + barBackgroundColor + ) + barIndicatorColor = typedArray.getColor( + R.styleable.SmoothBottomBar_indicatorColor, + barIndicatorColor + ) + barIndicatorRadius = typedArray.getDimension( + R.styleable.SmoothBottomBar_indicatorRadius, + barIndicatorRadius + ) + barSideMargins = typedArray.getDimension( + R.styleable.SmoothBottomBar_sideMargins, + barSideMargins + ) + barCornerRadius = typedArray.getDimension( + R.styleable.SmoothBottomBar_cornerRadius, + barCornerRadius + ) + barCorners = typedArray.getInteger( + R.styleable.SmoothBottomBar_corners, + barCorners + ) + itemPadding = typedArray.getDimension( + R.styleable.SmoothBottomBar_itemPadding, + itemPadding + ) + itemSpacing = typedArray.getDimension( + R.styleable.SmoothBottomBar_itemSpacing, + itemSpacing + ) + itemTextColor = typedArray.getColor( + R.styleable.SmoothBottomBar_textColor, + itemTextColor + ) + itemTextSize = typedArray.getDimension( + R.styleable.SmoothBottomBar_textSize, + itemTextSize + ) + itemIconSize = typedArray.getDimension( + R.styleable.SmoothBottomBar_iconSize, + itemIconSize + ) + itemIconMargin = typedArray.getDimension( + R.styleable.SmoothBottomBar_iconMargin, + itemIconMargin + ) + itemIconTint = typedArray.getColor( + R.styleable.SmoothBottomBar_iconTint, + itemIconTint + ) + itemBadgeColor = typedArray.getColor( + R.styleable.SmoothBottomBar_badgeColor, + itemBadgeColor + ) + itemIconTintActive = typedArray.getColor( + R.styleable.SmoothBottomBar_iconTintActive, + itemIconTintActive + ) + itemActiveIndex = typedArray.getInt( + R.styleable.SmoothBottomBar_activeItem, + itemActiveIndex + ) + itemFontFamily = typedArray.getResourceId( + R.styleable.SmoothBottomBar_itemFontFamily, + itemFontFamily + ) + itemAnimDuration = typedArray.getInt( + R.styleable.SmoothBottomBar_duration, + itemAnimDuration.toInt() + ).toLong() + itemMenuRes = typedArray.getResourceId( + R.styleable.SmoothBottomBar_menu, + itemMenuRes + ) + } catch (e: Exception) { + e.printStackTrace() + } finally { + typedArray.recycle() + } } override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { super.onSizeChanged(w, h, oldw, oldh) + calculateItemBounds() - var lastX = barSideMargins - itemWidth = (width - barSideMargins * 2) / items.size - - for (item in items) { - // Prevent text overflow by shortening the item title - var shorted = false - while (paintText.measureText(item.title) > itemWidth - itemIconSize - itemIconMargin - (itemPadding*2)) { - item.title = item.title.dropLast(1) - shorted = true + // Set initial active item state without animation + if (items.isNotEmpty()) { + for ((index, item) in items.withIndex()) { + item.alpha = if (index == itemActiveIndex) OPAQUE else TRANSPARENT } + indicatorLocation = items[itemActiveIndex].rect.left + currentIconTint = itemIconTintActive + invalidate() + } + } + + private fun calculateItemBounds() { + if (items.isEmpty() || width == 0 || height == 0) return - // Add ellipsis character to item text if it is shorted - if (shorted) { - item.title = item.title.dropLast(1) - item.title += context.getString(R.string.ellipsis) + // Calculate total spacing between items + val totalSpacing = if (items.size > 1) itemSpacing * (items.size - 1) else 0f + + // Calculate available width after margins and spacing + val totalAvailableWidth = width - (barSideMargins * 2) - totalSpacing + + // Calculate minimum width for inactive items (icon + padding only) + val inactiveItemWidth = itemIconSize + (itemPadding * 2) + + // Calculate total width needed for inactive items + val totalInactiveWidth = inactiveItemWidth * (items.size - 1) + + // Active item gets remaining space (ensures text fits and spreads across bar) + val activeItemWidth = totalAvailableWidth - totalInactiveWidth + + var lastX = barSideMargins + + // reverse items layout order if layout direction is RTL + val isRTL = Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 + && layoutDirection == LAYOUT_DIRECTION_RTL + val itemsToLayout = if (isRTL) items.reversed() else items + + for ((index, item) in itemsToLayout.withIndex()) { + val actualIndex = if (isRTL) items.size - 1 - index else index + + // Assign width based on active state + val currentItemWidth = if (actualIndex == itemActiveIndex) { + activeItemWidth + } else { + inactiveItemWidth } - item.rect = RectF(lastX, 0f, itemWidth + lastX, height.toFloat()) - lastX += itemWidth + item.rect.set(lastX, 0f, currentItemWidth + lastX, height.toFloat()) + lastX += currentItemWidth + itemSpacing + } + + // Update itemWidth to match active item width for indicator + if (_itemActiveIndex in items.indices) { + itemWidth = items[_itemActiveIndex].rect.width() } - // Set initial active item - setActiveItem(activeItem) + // Cache text height calculation + cachedTextHeight = (paintText.descent() + paintText.ascent()) / 2 + needsRecalculation = false } - override fun onDraw(canvas: Canvas) { - super.onDraw(canvas) + @JvmName("setBadge") + fun setBadge(pos: Int) { + badge_arr.add(pos) + invalidate() + } - val textHeight = (paintText.descent() + paintText.ascent()) / 2 + @JvmName("removeBadge") + fun removeBadge(pos: Int) { + badge_arr.remove(pos) + invalidate() + } - for ((i, item) in items.withIndex()) { - val textLength = paintText.measureText(item.title) - item.icon.mutate() - item.icon.setBounds(item.rect.centerX().toInt() - itemIconSize.toInt() / 2 - ((textLength/2) * (1-(255 - item.alpha) / 255f)).toInt(), - height / 2 - itemIconSize.toInt() / 2, - item.rect.centerX().toInt() + itemIconSize.toInt() / 2 - ((textLength/2) * (1-(255 - item.alpha) / 255f)).toInt(), - height / 2 + itemIconSize.toInt() / 2) - this.paintText.alpha = item.alpha + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + + // Draw background + if (barCornerRadius > 0) { + canvas.drawRoundRect( + 0f, 0f, + width.toFloat(), + height.toFloat(), + minOf(barCornerRadius.toFloat(), height.toFloat() / 2), + minOf(barCornerRadius.toFloat(), height.toFloat() / 2), + paintBackground + ) + + if (barCorners != ALL_CORNERS) { + + if ((barCorners and TOP_LEFT_CORNER) != TOP_LEFT_CORNER) { + // Draw a box to cover the curve on the top left + canvas.drawRect( + 0f, 0f, width.toFloat() / 2, + height.toFloat() / 2, paintBackground + ) + } + + if ((barCorners and TOP_RIGHT_CORNER) != TOP_RIGHT_CORNER) { + // Draw a box to cover the curve on the top right + canvas.drawRect( + width.toFloat() / 2, 0f, width.toFloat(), + height.toFloat() / 2, paintBackground + ) + } + + if ((barCorners and BOTTOM_LEFT_CORNER) != BOTTOM_LEFT_CORNER) { + // Draw a box to cover the curve on the bottom left + canvas.drawRect( + 0f, height.toFloat() / 2, width.toFloat() / 2, + height.toFloat(), paintBackground + ) + } + + if ((barCorners and BOTTOM_RIGHT_CORNER) != BOTTOM_RIGHT_CORNER) { + // Draw a box to cover the curve on the bottom right + canvas.drawRect( + width.toFloat() / 2, height.toFloat() / 2, width.toFloat(), + height.toFloat(), paintBackground + ) + } - DrawableCompat.setTint(item.icon , if (i == activeItem) currentIconTint else itemIconTint) - item.icon.draw(canvas) + } - canvas.drawText(item.title, item.rect.centerX() + itemIconSize/2 + itemIconMargin, - item.rect.centerY() - textHeight, paintText) + } else { + canvas.drawRect( + 0f, 0f, + width.toFloat(), + height.toFloat(), + paintBackground + ) } // Draw indicator - canvas.drawRoundRect(indicatorLocation, - items[activeItem].rect.centerY() - itemIconSize/2 - itemPadding, - indicatorLocation + itemWidth, - items[activeItem].rect.centerY() + itemIconSize/2 + itemPadding, - 20f, 20f, paintIndicator) + rect.left = indicatorLocation + rect.top = items[itemActiveIndex].rect.centerY() - itemIconSize / 2 - itemPadding + rect.right = indicatorLocation + itemWidth + rect.bottom = items[itemActiveIndex].rect.centerY() + itemIconSize / 2 + itemPadding + + canvas.drawRoundRect( + rect, + barIndicatorRadius, + barIndicatorRadius, + paintIndicator + ) + + // Use cached text height + val textHeight = cachedTextHeight + + // Pre-calculate common values + val halfHeight = height / 2 + val halfIconSize = itemIconSize.toInt() / 2 + val opaqueFloat = OPAQUE.toFloat() + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 + && layoutDirection == LAYOUT_DIRECTION_RTL + ) { + for ((index, item) in items.withIndex()) { + val textLength = paintText.measureText(item.title) + val alphaFactor = (1 - (OPAQUE - item.alpha) / opaqueFloat) + val textOffset = ((textLength / 2) * alphaFactor).toInt() + val centerX = item.rect.centerX().toInt() + + item.icon.mutate() + item.icon.setBounds( + centerX - halfIconSize + textOffset, + halfHeight - halfIconSize, + centerX + halfIconSize + textOffset, + halfHeight + halfIconSize + ) + if (index != itemActiveIndex) { + drawIconBackground(item, index, canvas) + } + tintAndDrawIcon(item, index, canvas) + + paintText.alpha = item.alpha + canvas.drawText( + item.title, + item.rect.centerX() - (itemIconSize / 2 + itemIconMargin), + item.rect.centerY() - textHeight, paintText + ) + } + + } else { + for ((index, item) in items.withIndex()) { + val textLength = paintText.measureText(item.title) + val alphaFactor = (1 - (OPAQUE - item.alpha) / opaqueFloat) + val textOffset = ((textLength / 2) * alphaFactor).toInt() + val centerX = item.rect.centerX().toInt() + + item.icon.mutate() + item.icon.setBounds( + centerX - halfIconSize - textOffset, + halfHeight - halfIconSize, + centerX + halfIconSize - textOffset, + halfHeight + halfIconSize + ) + //set badge indicator + + if (index != itemActiveIndex) { + drawIconBackground(item, index, canvas) + } + tintAndDrawIcon(item, index, canvas) + if (badge_arr.contains(index)) { + canvas.drawCircle( + centerX - halfIconSize.toFloat() - ((textLength / 2) * alphaFactor), + halfHeight.toFloat() - halfIconSize.toFloat(), + 10f, + badgePaint + ) + } + paintText.alpha = item.alpha + canvas.drawText( + item.title, + item.rect.centerX() + itemIconSize / 2 + itemIconMargin, + item.rect.centerY() - textHeight, paintText + ) + } + } + } + // New: draw icon background behind non-active icons + + private fun drawIconBackground( + item: BottomBarItem, + index: Int, + canvas: Canvas + ) { + // Skip if not requested + if (iconBackgroundColor == Color.TRANSPARENT) return + + val cx = item.rect.centerX() + val cy = height / 2f + val half = itemIconSize / 2f + + val left = cx - half - _iconBackgroundPadding + val top = cy - half - _iconBackgroundPadding + val right = cx + half + _iconBackgroundPadding + val bottom = cy + half + _iconBackgroundPadding + val radius = half + _iconBackgroundPadding + + // Reuse cached RectF object to avoid allocation + iconBackgroundRect.set(left, top, right, bottom) + canvas.drawRoundRect(iconBackgroundRect, radius, radius, iconBackgroundPaint) + } + private fun tintAndDrawIcon( + item: BottomBarItem, + index: Int, + canvas: Canvas + ) { + DrawableCompat.setTint( + item.icon, + if (index == itemActiveIndex) currentIconTint else itemIconTint + ) + + item.icon.draw(canvas) } /** * Handle item clicks */ @SuppressLint("ClickableViewAccessibility") - override fun onTouchEvent(event: MotionEvent): Boolean { - if (event.action == MotionEvent.ACTION_UP && abs(event.downTime - event.eventTime) < 500) - for ((itemId, item) in items.withIndex()) - if (item.rect.contains(event.x, event.y)) - if (itemId != this.activeItem) { - setActiveItem(itemId) - onItemSelected(itemId) - } else - onItemReselected(itemId) + override fun onTouchEvent(event: MotionEvent?): Boolean { + when (event?.action) { + MotionEvent.ACTION_DOWN -> { + return true + } - return true + MotionEvent.ACTION_UP -> { + for ((i, item) in items.withIndex()) { + if (item.rect.contains(event.x, event.y)) { + onClickAction(i) + break + } + } + } + } + return super.onTouchEvent(event) } - fun setActiveItem(pos: Int) { - activeItem = pos + override fun dispatchHoverEvent(event: MotionEvent): Boolean { + return exploreByTouchHelper.dispatchHoverEvent(event) || super.dispatchHoverEvent(event) + } - animateIndicator(pos) + private fun onClickAction(viewId: Int) { + exploreByTouchHelper.invalidateVirtualView(viewId) + if (viewId != itemActiveIndex) { + itemActiveIndex = viewId + onItemSelected?.invoke(viewId) + onItemSelectedListener?.onItemSelect(viewId) + } else { + onItemReselected?.invoke(viewId) + onItemReselectedListener?.onItemReselect(viewId) + } + exploreByTouchHelper.sendEventForVirtualView( + viewId, + AccessibilityEvent.TYPE_VIEW_CLICKED + ) + } - for ((i, item) in items.withIndex()) - animateAlpha(item, if (i == pos) 255 else 0) + private fun applyItemActiveIndex() { + if (items.isNotEmpty()) { + // Store old indicator location before recalculating bounds + val oldIndicatorLocation = indicatorLocation + val oldItemWidth = itemWidth + + // Recalculate item bounds with new active index + calculateItemBounds() + + for ((index, item) in items.withIndex()) { + if (index == itemActiveIndex) { + // Set initial state immediately if not animated + if (item.alpha == TRANSPARENT) { + item.alpha = OPAQUE + } + animateAlpha(item, OPAQUE) + } else { + animateAlpha(item, TRANSPARENT) + } + } + + // Animate indicator position and width smoothly + ValueAnimator.ofFloat(0f, 1f).apply { + duration = itemAnimDuration + interpolator = DecelerateInterpolator() + addUpdateListener { animation -> + val progress = animation.animatedValue as Float + indicatorLocation = oldIndicatorLocation + + (items[itemActiveIndex].rect.left - oldIndicatorLocation) * progress + invalidate() + } + start() + } - animateIconTint() + ValueAnimator.ofObject(ArgbEvaluator(), itemIconTint, itemIconTintActive).apply { + duration = itemAnimDuration + addUpdateListener { + currentIconTint = it.animatedValue as Int + } + start() + } + } } private fun animateAlpha(item: BottomBarItem, to: Int) { - val animator = ValueAnimator.ofInt(item.alpha, to) - animator.duration = itemAnimDuration - - animator.addUpdateListener { - val value = it.animatedValue as Int - item.alpha = value - invalidate() + ValueAnimator.ofInt(item.alpha, to).apply { + duration = itemAnimDuration + addUpdateListener { + val value = it.animatedValue as Int + item.alpha = value + invalidate() + } + start() } + } - animator.start() + fun setupWithNavController(menu: Menu, navController: NavController) { + NavigationComponentHelper.setupWithNavController(menu, this, navController) } - private fun animateIndicator(pos: Int) { - val animator = ValueAnimator.ofFloat(indicatorLocation, items[pos].rect.left) - animator.duration = itemAnimDuration - animator.interpolator = DecelerateInterpolator() + fun setupWithNavController(navController: NavController) { + NavigationComponentHelper.setupWithNavController(this.menu, this, navController) + Navigation.setViewNavController(this, navController) + } - animator.addUpdateListener { animation -> - indicatorLocation = animation.animatedValue as Float + fun setSelectedItem(pos: Int) { + try { + this.itemActiveIndex = pos + NavigationUI.onNavDestinationSelected(this.menu.getItem(pos), this.findNavController()) + invalidate() + } catch (e: Exception) { + throw Exception("set menu using PopupMenu") } - - animator.start() } - private fun animateIconTint() { - val animator = ValueAnimator.ofObject(ArgbEvaluator(), itemIconTint, itemIconTintActive) - animator.duration = itemAnimDuration - animator.addUpdateListener { - currentIconTint = it.animatedValue as Int + /** + * Created by Vladislav Perevedentsev on 29.07.2020. + * + * Just call [SmoothBottomBar.setOnItemSelectedListener] to override [onItemSelectedListener] + * + * @sample + * setOnItemSelectedListener { position -> + * //TODO: Something + * } + */ + fun setOnItemSelectedListener(listener: (position: Int) -> Unit) { + onItemSelectedListener = object : OnItemSelectedListener { + override fun onItemSelect(pos: Int): Boolean { + listener.invoke(pos) + return true + } } + } - animator.start() + /** + * Created by Vladislav Perevedentsev on 29.07.2020. + * + * Just call [SmoothBottomBar.setOnItemReselectedListener] to override [onItemReselectedListener] + * + * @sample + * setOnItemReselectedListener { position -> + * //TODO: Something + * } + */ + fun setOnItemReselectedListener(listener: (position: Int) -> Unit) { + onItemReselectedListener = object : OnItemReselectedListener { + override fun onItemReselect(pos: Int) { + listener.invoke(pos) + } + } } - private fun d2p(dp: Float): Float { - return resources.displayMetrics.densityDpi.toFloat() / 160.toFloat() * dp + companion object { + private const val INVALID_RES = -1 + private const val DEFAULT_INDICATOR_COLOR = "#2DFFFFFF" + private const val DEFAULT_TINT = "#C8FFFFFF" + private const val DEFAULT_ICON_BG_PADDING = 6f + + // corner flags + private const val NO_CORNERS = 0; + private const val TOP_LEFT_CORNER = 1; + private const val TOP_RIGHT_CORNER = 2; + private const val BOTTOM_RIGHT_CORNER = 4; + private const val BOTTOM_LEFT_CORNER = 8; + private const val ALL_CORNERS = 15; + + private const val DEFAULT_SIDE_MARGIN = 10f + private const val DEFAULT_ITEM_PADDING = 10f + private const val DEFAULT_ITEM_SPACING = 8f + private const val DEFAULT_ANIM_DURATION = 200L + private const val DEFAULT_ICON_SIZE = 18F + private const val DEFAULT_ICON_MARGIN = 4F + private const val DEFAULT_TEXT_SIZE = 11F + private const val DEFAULT_CORNER_RADIUS = 20F + private const val DEFAULT_BAR_CORNER_RADIUS = 0F + private const val DEFAULT_BAR_CORNERS = TOP_LEFT_CORNER or TOP_RIGHT_CORNER + + private const val OPAQUE = 255 + private const val TRANSPARENT = 0 } -} \ No newline at end of file +} diff --git a/lib/src/main/java/me/ibrahimsn/lib/ext/ContextExt.kt b/lib/src/main/java/me/ibrahimsn/lib/ext/ContextExt.kt new file mode 100644 index 0000000..5dd6535 --- /dev/null +++ b/lib/src/main/java/me/ibrahimsn/lib/ext/ContextExt.kt @@ -0,0 +1,8 @@ +package me.ibrahimsn.lib.ext + +import android.content.Context +import kotlin.math.roundToInt + +internal fun Context.d2p(dp: Float): Float { + return (dp * resources.displayMetrics.density).roundToInt().toFloat() +} diff --git a/lib/src/main/res/values/attrs.xml b/lib/src/main/res/values/attrs.xml index 876f981..29bb1da 100644 --- a/lib/src/main/res/values/attrs.xml +++ b/lib/src/main/res/values/attrs.xml @@ -1,20 +1,36 @@ - + + + + + + + + + + + + + + + + + - - \ No newline at end of file + + diff --git a/lib/src/test/java/me/ibrahimsn/lib/ExampleUnitTest.kt b/lib/src/test/java/me/ibrahimsn/lib/ExampleUnitTest.kt deleted file mode 100644 index c72a784..0000000 --- a/lib/src/test/java/me/ibrahimsn/lib/ExampleUnitTest.kt +++ /dev/null @@ -1,17 +0,0 @@ -package me.ibrahimsn.lib - -import org.junit.Test - -import org.junit.Assert.* - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) - } -}