add BML and MIB card freeze/unfreeze

This commit is contained in:
2026-06-13 17:40:09 +05:00
parent 6f8b7130fe
commit 014c002ebe
9 changed files with 552 additions and 19 deletions
+231
View File
@@ -0,0 +1,231 @@
kotlin version: 2.1.21
error message: org.jetbrains.kotlin.util.FileAnalysisException: While analysing /home/shihaam/git/sargit/thijooree/app/src/main/java/sh/sar/basedbank/ui/home/PayWithCardFragment.kt:701:9: java.io.FileNotFoundException: /home/shihaam/git/sargit/thijooree/app/build/tmp/kotlin-classes/debug/sh/sar/basedbank/nfc/BmlHostCardEmulatorService.class (No such file or directory)
at org.jetbrains.kotlin.util.AnalysisExceptionsKt.wrapIntoFileAnalysisExceptionIfNeeded(AnalysisExceptions.kt:57)
at org.jetbrains.kotlin.fir.FirCliExceptionHandler.handleExceptionOnFileAnalysis(Utils.kt:251)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.transformFile(FirDeclarationsResolveTransformer.kt:1757)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBodyResolveTransformerDispatcher.transformFile(FirAbstractBodyResolveTransformerDispatcher.kt:57)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBodyResolveTransformerDispatcher.transformFile(FirAbstractBodyResolveTransformerDispatcher.kt:24)
at org.jetbrains.kotlin.fir.declarations.FirFile.transform(FirFile.kt:46)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirBodyResolveTransformerAdapter.transformFile(FirBodyResolveTransformerAdapters.kt:41)
at org.jetbrains.kotlin.fir.declarations.FirFile.transform(FirFile.kt:46)
at org.jetbrains.kotlin.fir.resolve.transformers.FirTransformerBasedResolveProcessor.processFile(FirResolveProcessor.kt:48)
at org.jetbrains.kotlin.fir.resolve.transformers.FirTotalResolveProcessor.process(FirTotalResolveProcessor.kt:36)
at org.jetbrains.kotlin.fir.pipeline.AnalyseKt.runResolution(analyse.kt:24)
at org.jetbrains.kotlin.fir.pipeline.FirUtilsKt.resolveAndCheckFir(firUtils.kt:76)
at org.jetbrains.kotlin.cli.pipeline.jvm.JvmFrontendPipelinePhase.executePhase(JvmFrontendPipelinePhase.kt:167)
at org.jetbrains.kotlin.cli.pipeline.jvm.JvmFrontendPipelinePhase.executePhase(JvmFrontendPipelinePhase.kt:56)
at org.jetbrains.kotlin.cli.pipeline.PipelinePhase.phaseBody(PipelinePhase.kt:68)
at org.jetbrains.kotlin.cli.pipeline.PipelinePhase.phaseBody(PipelinePhase.kt:58)
at org.jetbrains.kotlin.config.phaser.SimpleNamedCompilerPhase.phaseBody(CompilerPhase.kt:215)
at org.jetbrains.kotlin.config.phaser.NamedCompilerPhase.invoke(CompilerPhase.kt:111)
at org.jetbrains.kotlin.backend.common.phaser.CompositePhase.invoke(PhaseBuilders.kt:28)
at org.jetbrains.kotlin.config.phaser.CompilerPhaseKt.invokeToplevel(CompilerPhase.kt:62)
at org.jetbrains.kotlin.cli.pipeline.AbstractCliPipeline.runPhasedPipeline(AbstractCliPipeline.kt:106)
at org.jetbrains.kotlin.cli.pipeline.AbstractCliPipeline.execute(AbstractCliPipeline.kt:65)
at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecutePhased(K2JVMCompiler.kt:61)
at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecutePhased(K2JVMCompiler.kt:36)
at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:80)
at org.jetbrains.kotlin.cli.common.CLICompiler.exec(CLICompiler.kt:337)
at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:466)
at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:75)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.doCompile(IncrementalCompilerRunner.kt:514)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileImpl(IncrementalCompilerRunner.kt:431)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.tryCompileIncrementally$lambda$10$compile(IncrementalCompilerRunner.kt:258)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.tryCompileIncrementally(IncrementalCompilerRunner.kt:276)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:128)
at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execIncrementalCompiler(CompileServiceImpl.kt:678)
at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execIncrementalCompiler(CompileServiceImpl.kt:92)
at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1805)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:360)
at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:200)
at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:197)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:714)
at java.rmi/sun.rmi.transport.Transport.serviceCall(Transport.java:196)
at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:598)
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:844)
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:721)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:400)
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:720)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: java.io.FileNotFoundException: /home/shihaam/git/sargit/thijooree/app/build/tmp/kotlin-classes/debug/sh/sar/basedbank/nfc/BmlHostCardEmulatorService.class (No such file or directory)
at java.base/java.io.FileInputStream.open0(Native Method)
at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
at org.jetbrains.kotlin.com.intellij.openapi.util.io.FileUtil.loadFileBytes(FileUtil.java:211)
at org.jetbrains.kotlin.cli.common.localfs.KotlinLocalVirtualFile.contentsToByteArray(KotlinLocalVirtualFile.kt:108)
at org.jetbrains.kotlin.cli.jvm.compiler.KotlinCliJavaFileManagerImpl.findClass(KotlinCliJavaFileManagerImpl.kt:141)
at org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade$CliFinder.findClass(KotlinJavaPsiFacade.java:538)
at org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade.findClass(KotlinJavaPsiFacade.java:181)
at org.jetbrains.kotlin.load.java.JavaClassFinderImpl.findClass(JavaClassFinderImpl.kt:46)
at org.jetbrains.kotlin.fir.java.FirJavaFacade.findClass(FirJavaFacade.kt:69)
at org.jetbrains.kotlin.fir.java.deserialization.JvmClassFileBasedSymbolProvider.extractClassMetadata(JvmClassFileBasedSymbolProvider.kt:174)
at org.jetbrains.kotlin.fir.deserialization.AbstractFirDeserializedSymbolProvider.findAndDeserializeClass(AbstractFirDeserializedSymbolProvider.kt:229)
at org.jetbrains.kotlin.fir.deserialization.AbstractFirDeserializedSymbolProvider.classCache$lambda$5(AbstractFirDeserializedSymbolProvider.kt:163)
at org.jetbrains.kotlin.fir.caches.FirThreadUnsafeCacheWithPostCompute.getValue(FirThreadUnsafeCachesFactory.kt:75)
at org.jetbrains.kotlin.fir.deserialization.AbstractFirDeserializedSymbolProvider.getClass(AbstractFirDeserializedSymbolProvider.kt:315)
at org.jetbrains.kotlin.fir.deserialization.AbstractFirDeserializedSymbolProvider.getClass$default(AbstractFirDeserializedSymbolProvider.kt:298)
at org.jetbrains.kotlin.fir.deserialization.AbstractFirDeserializedSymbolProvider.getClassLikeSymbolByClassId(AbstractFirDeserializedSymbolProvider.kt:372)
at org.jetbrains.kotlin.fir.resolve.providers.impl.FirCachingCompositeSymbolProvider.computeClass(FirCachingCompositeSymbolProvider.kt:147)
at org.jetbrains.kotlin.fir.resolve.providers.impl.FirCachingCompositeSymbolProvider.access$computeClass(FirCachingCompositeSymbolProvider.kt:27)
at org.jetbrains.kotlin.fir.resolve.providers.impl.FirCachingCompositeSymbolProvider$special$$inlined$createCache$1.invoke(FirCachesFactory.kt:149)
at org.jetbrains.kotlin.fir.resolve.providers.impl.FirCachingCompositeSymbolProvider$special$$inlined$createCache$1.invoke(FirCachesFactory.kt:147)
at org.jetbrains.kotlin.fir.caches.FirThreadUnsafeCache.getValue(FirThreadUnsafeCachesFactory.kt:57)
at org.jetbrains.kotlin.fir.resolve.providers.impl.FirCachingCompositeSymbolProvider.getClassLikeSymbolByClassId(FirCachingCompositeSymbolProvider.kt:174)
at org.jetbrains.kotlin.fir.scopes.impl.FirAbstractImportingScope.processClassifiersFromImportsByName(FirAbstractImportingScope.kt:53)
at org.jetbrains.kotlin.fir.scopes.impl.FirAbstractSimpleImportingScope.processClassifiersByNameWithSubstitution(FirAbstractSimpleImportingScope.kt:30)
at org.jetbrains.kotlin.fir.resolve.calls.tower.ScopeBasedTowerLevel.processObjectsByName(TowerLevels.kt:538)
at org.jetbrains.kotlin.fir.resolve.calls.tower.TowerLevelHandler.handleLevel(TowerLevelHandler.kt:57)
at org.jetbrains.kotlin.fir.resolve.calls.tower.FirBaseTowerResolveTask.processLevel(FirTowerResolveTask.kt:206)
at org.jetbrains.kotlin.fir.resolve.calls.tower.FirBaseTowerResolveTask.access$processLevel(FirTowerResolveTask.kt:63)
at org.jetbrains.kotlin.fir.resolve.calls.tower.FirTowerResolveTask.runResolverForNoReceiver(FirTowerResolveTask.kt:635)
at org.jetbrains.kotlin.fir.resolve.calls.tower.FirTowerResolveTask.runResolverForNoReceiver$default(FirTowerResolveTask.kt:338)
at org.jetbrains.kotlin.fir.resolve.calls.tower.FirTowerResolver$enqueueResolutionTasks$2.invokeSuspend(FirTowerResolver.kt:79)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at org.jetbrains.kotlin.fir.resolve.calls.tower.TowerResolveManager.resumeTask(TowerResolveManager.kt:77)
at org.jetbrains.kotlin.fir.resolve.calls.tower.TowerResolveManager.runTasks(TowerResolveManager.kt:83)
at org.jetbrains.kotlin.fir.resolve.calls.tower.FirTowerResolver.runResolver(FirTowerResolver.kt:53)
at org.jetbrains.kotlin.fir.resolve.calls.tower.FirTowerResolver.runResolver$default(FirTowerResolver.kt:42)
at org.jetbrains.kotlin.fir.resolve.calls.tower.FirTowerResolver.runResolver(FirTowerResolver.kt:39)
at org.jetbrains.kotlin.fir.resolve.calls.FirCallResolver.collectCandidates(FirCallResolver.kt:209)
at org.jetbrains.kotlin.fir.resolve.calls.FirCallResolver.collectCandidates$default(FirCallResolver.kt:174)
at org.jetbrains.kotlin.fir.resolve.calls.FirCallResolver.resolveVariableAccessAndSelectCandidateImpl$lambda$7(FirCallResolver.kt:282)
at kotlin.UnsafeLazyImpl.getValue(Lazy.kt:98)
at org.jetbrains.kotlin.fir.resolve.calls.FirCallResolver.resolveVariableAccessAndSelectCandidateImpl$lambda$8(FirCallResolver.kt:281)
at org.jetbrains.kotlin.fir.resolve.calls.FirCallResolver.resolveVariableAccessAndSelectCandidateImpl(FirCallResolver.kt:311)
at org.jetbrains.kotlin.fir.resolve.calls.FirCallResolver.resolveVariableAccessAndSelectCandidate(FirCallResolver.kt:258)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirExpressionsResolveTransformer.resolveQualifiedAccessAndSelectCandidate(FirExpressionsResolveTransformer.kt:281)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirExpressionsResolveTransformer.transformQualifiedAccessExpression(FirExpressionsResolveTransformer.kt:187)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirExpressionsResolveTransformer.transformAsExplicitReceiver(FirExpressionsResolveTransformer.kt:260)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirExpressionsResolveTransformer.transformExplicitReceiverOf(FirExpressionsResolveTransformer.kt:248)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirExpressionsResolveTransformer.transformFunctionCallInternal$resolve(FirExpressionsResolveTransformer.kt:508)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirExpressionsResolveTransformer.transformFunctionCall(FirExpressionsResolveTransformer.kt:453)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBodyResolveTransformerDispatcher.transformFunctionCall(FirAbstractBodyResolveTransformerDispatcher.kt:174)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBodyResolveTransformerDispatcher.transformFunctionCall(FirAbstractBodyResolveTransformerDispatcher.kt:24)
at org.jetbrains.kotlin.fir.expressions.FirFunctionCall.transform(FirFunctionCall.kt:45)
at org.jetbrains.kotlin.fir.expressions.FirExpressionUtilKt.transformStatementsIndexed(FirExpressionUtil.kt:198)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirExpressionsResolveTransformer.transformBlockInCurrentScope$resolve(FirExpressionsResolveTransformer.kt:685)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirExpressionsResolveTransformer.transformBlock(FirExpressionsResolveTransformer.kt:676)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBodyResolveTransformerDispatcher.transformBlock(FirAbstractBodyResolveTransformerDispatcher.kt:201)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBodyResolveTransformerDispatcher.transformBlock(FirAbstractBodyResolveTransformerDispatcher.kt:24)
at org.jetbrains.kotlin.fir.expressions.FirBlock.transform(FirBlock.kt:32)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirPartialBodyResolveTransformer.transformElement(FirPartialBodyResolveTransformer.kt:36)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirPartialBodyResolveTransformer.transformElement(FirPartialBodyResolveTransformer.kt:13)
at org.jetbrains.kotlin.fir.visitors.FirTransformer.transformBlock(FirTransformer.kt:183)
at org.jetbrains.kotlin.fir.expressions.FirBlock.transform(FirBlock.kt:32)
at org.jetbrains.kotlin.fir.declarations.impl.FirSimpleFunctionImpl.transformBody(FirSimpleFunctionImpl.kt:114)
at org.jetbrains.kotlin.fir.declarations.impl.FirSimpleFunctionImpl.transformBody(FirSimpleFunctionImpl.kt:32)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.transformFunction(FirDeclarationsResolveTransformer.kt:1004)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.transformFunctionWithGivenSignature(FirDeclarationsResolveTransformer.kt:950)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.access$transformFunctionWithGivenSignature(FirDeclarationsResolveTransformer.kt:63)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.transformSimpleFunction$lambda$63$lambda$62$lambda$61(FirDeclarationsResolveTransformer.kt:942)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.BodyResolveContext.forFunctionBody(BodyResolveContext.kt:1320)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.transformSimpleFunction(FirDeclarationsResolveTransformer.kt:940)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBodyResolveTransformerDispatcher.transformSimpleFunction(FirAbstractBodyResolveTransformerDispatcher.kt:549)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBodyResolveTransformerDispatcher.transformSimpleFunction(FirAbstractBodyResolveTransformerDispatcher.kt:24)
at org.jetbrains.kotlin.fir.declarations.FirSimpleFunction.transform(FirSimpleFunction.kt:55)
at org.jetbrains.kotlin.fir.visitors.FirTransformerUtilKt.transformInplace(FirTransformerUtil.kt:20)
at org.jetbrains.kotlin.fir.declarations.impl.FirRegularClassImpl.transformDeclarations(FirRegularClassImpl.kt:91)
at org.jetbrains.kotlin.fir.declarations.impl.FirRegularClassImpl.transformChildren(FirRegularClassImpl.kt:73)
at org.jetbrains.kotlin.fir.declarations.impl.FirRegularClassImpl.transformChildren(FirRegularClassImpl.kt:30)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBodyResolveTransformerDispatcher.transformElement(FirAbstractBodyResolveTransformerDispatcher.kt:89)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBodyResolveTransformerDispatcher.transformDeclarationContent(FirAbstractBodyResolveTransformerDispatcher.kt:441)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.transformDeclarationContent(FirDeclarationsResolveTransformer.kt:83)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.doTransformRegularClass$lambda$55(FirDeclarationsResolveTransformer.kt:869)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.withRegularClass$lambda$56(FirDeclarationsResolveTransformer.kt:878)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.BodyResolveContext.withRegularClass$lambda$17$lambda$16(BodyResolveContext.kt:1673)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.BodyResolveContext.withScopesForClass(BodyResolveContext.kt:570)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.BodyResolveContext.withRegularClass(BodyResolveContext.kt:468)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.withRegularClass(FirDeclarationsResolveTransformer.kt:877)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.doTransformRegularClass(FirDeclarationsResolveTransformer.kt:868)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.transformRegularClass(FirDeclarationsResolveTransformer.kt:769)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBodyResolveTransformerDispatcher.transformRegularClass(FirAbstractBodyResolveTransformerDispatcher.kt:522)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBodyResolveTransformerDispatcher.transformRegularClass(FirAbstractBodyResolveTransformerDispatcher.kt:24)
at org.jetbrains.kotlin.fir.declarations.FirRegularClass.transform(FirRegularClass.kt:52)
at org.jetbrains.kotlin.fir.visitors.FirTransformerUtilKt.transformInplace(FirTransformerUtil.kt:20)
at org.jetbrains.kotlin.fir.declarations.impl.FirFileImpl.transformDeclarations(FirFileImpl.kt:80)
at org.jetbrains.kotlin.fir.declarations.impl.FirFileImpl.transformChildren(FirFileImpl.kt:65)
at org.jetbrains.kotlin.fir.declarations.impl.FirFileImpl.transformChildren(FirFileImpl.kt:29)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBodyResolveTransformerDispatcher.transformElement(FirAbstractBodyResolveTransformerDispatcher.kt:89)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBodyResolveTransformerDispatcher.transformDeclarationContent(FirAbstractBodyResolveTransformerDispatcher.kt:441)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.transformDeclarationContent(FirDeclarationsResolveTransformer.kt:83)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.doTransformFile$lambda$53(FirDeclarationsResolveTransformer.kt:841)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.withFile(FirDeclarationsResolveTransformer.kt:854)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.doTransformFile(FirDeclarationsResolveTransformer.kt:840)
at org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer.transformFile(FirDeclarationsResolveTransformer.kt:747)
... 48 more
error message: Daemon compilation failed: null
java.lang.Exception
at org.jetbrains.kotlin.daemon.common.CompileService$CallResult$Error.get(CompileService.kt:69)
at org.jetbrains.kotlin.daemon.common.CompileService$CallResult$Error.get(CompileService.kt:65)
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemon(GradleKotlinCompilerWork.kt:240)
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159)
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111)
at org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction.execute(GradleCompilerRunnerWithWorkers.kt:76)
at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:63)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:66)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:62)
at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:100)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:62)
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:59)
at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:174)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:194)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.access$700(DefaultConditionalExecutionQueue.java:127)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner$1.run(DefaultConditionalExecutionQueue.java:169)
at org.gradle.internal.Factories$1.create(Factories.java:31)
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:263)
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:127)
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:132)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:133)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:48)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: java.nio.file.NoSuchFileException: /tmp/kotlin-backups1029090002252849674/278.backup -> /home/shihaam/git/sargit/thijooree/app/build/tmp/kotlin-classes/debug/sh/sar/basedbank/ui/home/TransferFragment$lookupAccount$1.class
at java.base/sun.nio.fs.UnixException.translateToIOException(UnixException.java:92)
at java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:106)
at java.base/sun.nio.fs.UnixFileSystem.move(UnixFileSystem.java:939)
at java.base/sun.nio.fs.UnixFileSystemProvider.move(UnixFileSystemProvider.java:309)
at java.base/java.nio.file.Files.move(Files.java:1431)
at org.jetbrains.kotlin.incremental.RecoverableCompilationTransaction.revertChanges(CompilationTransaction.kt:231)
at org.jetbrains.kotlin.incremental.RecoverableCompilationTransaction.close(CompilationTransaction.kt:256)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.tryCompileIncrementally(IncrementalCompilerRunner.kt:762)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:128)
at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execIncrementalCompiler(CompileServiceImpl.kt:678)
at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execIncrementalCompiler(CompileServiceImpl.kt:92)
at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1805)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:360)
at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:200)
at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:197)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:714)
at java.rmi/sun.rmi.transport.Transport.serviceCall(Transport.java:196)
at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:598)
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:844)
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:721)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:400)
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:720)
... 3 more
@@ -0,0 +1,55 @@
package sh.sar.basedbank.api.bml
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import org.json.JSONObject
import sh.sar.basedbank.api.models.BankServerException
data class BmlCardActionResult(
val success: Boolean,
val message: String
)
class BmlCardClient {
private val client = newBmlApiClient()
/**
* Freezes or unfreezes a BML card.
* @param cardId BML card UUID (BankAccount.internalId)
* @param action "freeze" or "unfreeze"
*/
fun setCardFreezeState(session: BmlSession, cardId: String, action: String): BmlCardActionResult {
val body = JSONObject().apply {
put("card", cardId)
put("action", action)
}.toString().toRequestBody("application/json".toMediaType())
val request = Request.Builder()
.url("$BML_BASE_URL/api/mobile/services/card/freeze")
.post(body)
.header("Authorization", "Bearer ${session.accessToken}")
.header("User-Agent", BML_USER_AGENT)
.header("x-app-version", BML_APP_VERSION)
.header("accept", "application/json")
.build()
val resp = client.newCall(request).execute()
val code = resp.code
val responseBody = resp.body?.string()
resp.close()
if (code == 401 || code == 419) throw AuthExpiredException()
if (code in 500..599) throw BankServerException("BML")
return try {
val json = JSONObject(responseBody ?: "")
val ok = json.optBoolean("success") && json.optInt("code") == 0
BmlCardActionResult(
success = ok,
message = json.optString("payload").ifBlank { json.optString("message") }
)
} catch (_: Exception) {
BmlCardActionResult(success = false, message = "")
}
}
}
@@ -7,10 +7,18 @@ import okhttp3.Request
import org.json.JSONObject import org.json.JSONObject
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
data class MibCardActionResult(
val success: Boolean,
val message: String,
val currentStatusCode: String
)
class MibCardsClient { class MibCardsClient {
private val BASE_WV_URL = "https://faisamobilex-wv.mib.com.mv" private val BASE_WV_URL = "https://faisamobilex-wv.mib.com.mv"
private val USER_AGENT = "Mozilla/5.0 (Linux; Android ${Build.VERSION.RELEASE}; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/129.0.6668.70 Mobile Safari/537.36"
private val client = OkHttpClient.Builder() private val client = OkHttpClient.Builder()
.connectTimeout(20, TimeUnit.SECONDS) .connectTimeout(20, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS) .readTimeout(20, TimeUnit.SECONDS)
@@ -20,7 +28,7 @@ class MibCardsClient {
"mbmodel=IOS-1.0; xxid=${session.xxid}; IBSID=${session.xxid}; " + "mbmodel=IOS-1.0; xxid=${session.xxid}; IBSID=${session.xxid}; " +
"mbnonce=${session.nonceGenerator}; time-tracker=597" "mbnonce=${session.nonceGenerator}; time-tracker=597"
fun fetchCards(session: MibSession, loginTag: String): List<MibCard> { fun fetchCards(session: MibSession, loginTag: String, profileId: String = ""): List<MibCard> {
val body = FormBody.Builder() val body = FormBody.Builder()
.add("name", "") .add("name", "")
.add("start", "1") .add("start", "1")
@@ -32,7 +40,7 @@ class MibCardsClient {
.url("$BASE_WV_URL/ajaxDebitCard/fetchCardInfos") .url("$BASE_WV_URL/ajaxDebitCard/fetchCardInfos")
.post(body) .post(body)
.header("Cookie", cookieHeader(session)) .header("Cookie", cookieHeader(session))
.header("User-Agent", "Mozilla/5.0 (Linux; Android ${Build.VERSION.RELEASE}; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/129.0.6668.70 Mobile Safari/537.36") .header("User-Agent", USER_AGENT)
.header("X-Requested-With", "XMLHttpRequest") .header("X-Requested-With", "XMLHttpRequest")
.header("Accept", "*/*") .header("Accept", "*/*")
.header("Origin", BASE_WV_URL) .header("Origin", BASE_WV_URL)
@@ -55,9 +63,42 @@ class MibCardsClient {
customerId = item.optString("customerId"), customerId = item.optString("customerId"),
phoneNumber = item.optString("phoneNumber"), phoneNumber = item.optString("phoneNumber"),
cardHolderName = item.optString("cardHolderName"), cardHolderName = item.optString("cardHolderName"),
loginTag = loginTag loginTag = loginTag,
profileId = profileId
) )
} }
} }
} }
/** Freezes a MIB card. action = "freeze" or "unfreeze". */
fun setCardFreezeState(session: MibSession, cardId: String, action: String, comments: String): MibCardActionResult {
val body = FormBody.Builder()
.add("cardId", cardId)
.add("comments", comments)
.build()
val request = Request.Builder()
.url("$BASE_WV_URL/ajaxDebitCard/$action")
.post(body)
.header("Cookie", cookieHeader(session))
.header("User-Agent", USER_AGENT)
.header("X-Requested-With", "XMLHttpRequest")
.header("Accept", "*/*")
.header("Origin", BASE_WV_URL)
.header("Referer", "$BASE_WV_URL//debitCards/manage?cardId=$cardId&dashurl=1")
.build()
return client.newCall(request).execute().use { response ->
val bodyStr = response.body?.string()
?: return MibCardActionResult(false, "", "")
val json = try { JSONObject(bodyStr) } catch (_: Exception) {
return MibCardActionResult(false, "", "")
}
MibCardActionResult(
success = json.optBoolean("success"),
message = json.optString("reasonText"),
currentStatusCode = json.optString("currentStatusCode")
)
}
}
} }
@@ -55,7 +55,8 @@ data class MibCard(
val customerId: String, val customerId: String,
val phoneNumber: String, val phoneNumber: String,
val cardHolderName: String, val cardHolderName: String,
val loginTag: String val loginTag: String,
val profileId: String = ""
) )
data class MibFinanceDeal( data class MibFinanceDeal(
@@ -116,7 +116,7 @@ class DashboardFragment : Fragment() {
val credStore = CredentialStore(requireContext()) val credStore = CredentialStore(requireContext())
val hidden = credStore.getHiddenDashboardCardNumbers() val hidden = credStore.getHiddenDashboardCardNumbers()
val mibItems = (viewModel.mibCards.value ?: emptyList()) val mibItems = (viewModel.mibCards.value ?: emptyList())
.filter { !hidden.contains(it.maskedCardNumber) } .filter { CardsFragment.isMibCardActive(it.cardStatus) && !hidden.contains(it.maskedCardNumber) }
.map { CardItem.Mib(it) } .map { CardItem.Mib(it) }
val bmlItems = (viewModel.accounts.value ?: emptyList()) val bmlItems = (viewModel.accounts.value ?: emptyList())
.filter { (it.profileType == "BML_PREPAID" || it.profileType == "BML_CREDIT" || it.profileType == "BML_DEBIT") && it.statusDesc.equals("Active", ignoreCase = true) && !hidden.contains(it.accountNumber) } .filter { (it.profileType == "BML_PREPAID" || it.profileType == "BML_CREDIT" || it.profileType == "BML_DEBIT") && it.statusDesc.equals("Active", ignoreCase = true) && !hidden.contains(it.accountNumber) }
@@ -1130,14 +1130,14 @@ fun applyNavLabelVisibility() {
val fresh = withContext(Dispatchers.IO) { val fresh = withContext(Dispatchers.IO) {
val sess = app.bmlSessionFor(src) ?: return@withContext null val sess = app.bmlSessionFor(src) ?: return@withContext null
try { try {
val accounts = BmlAccountClient().fetchAccounts(sess, src.loginTag) val accounts = BmlAccountClient().fetchAccounts(sess, src.loginTag, src.profileName, src.profileId)
AccountCache.saveBml(this@HomeActivity, loginId, accounts) AccountCache.saveBml(this@HomeActivity, loginId, accounts)
val otherBml = app.bmlAccounts.filter { it.loginTag != src.loginTag } val otherBml = app.bmlAccounts.filter { it.loginTag != src.loginTag || it.profileId != src.profileId }
app.bmlAccounts = otherBml + accounts app.bmlAccounts = otherBml + accounts
accounts accounts
} catch (_: Exception) { null } } catch (_: Exception) { null }
} ?: return@launch } ?: return@launch
val otherAccounts = current.filter { it.bank != "BML" || it.loginTag != src.loginTag } val otherAccounts = current.filter { it.bank != "BML" || it.loginTag != src.loginTag || it.profileId != src.profileId }
viewModel.accounts.postValue(otherAccounts + fresh) viewModel.accounts.postValue(otherAccounts + fresh)
} else { } else {
val loginId = src.loginTag.removePrefix("mib_") val loginId = src.loginTag.removePrefix("mib_")
@@ -1220,7 +1220,7 @@ fun applyNavLabelVisibility() {
for (profile in profiles) { for (profile in profiles) {
try { try {
flow.switchProfile(session, profile) flow.switchProfile(session, profile)
for (card in client.fetchCards(session, "mib_$loginId")) { for (card in client.fetchCards(session, "mib_$loginId", profile.profileId)) {
if (seen.add(card.cardId)) result += card if (seen.add(card.cardId)) result += card
} }
} catch (_: Exception) { } } catch (_: Exception) { }
@@ -37,12 +37,19 @@ import com.google.android.material.button.MaterialButton
import com.google.android.material.color.MaterialColors import com.google.android.material.color.MaterialColors
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import sh.sar.basedbank.BasedBankApp import sh.sar.basedbank.BasedBankApp
import sh.sar.basedbank.R import sh.sar.basedbank.R
import sh.sar.basedbank.api.bml.BmlCardClient
import sh.sar.basedbank.api.bml.BmlTapToPayClient import sh.sar.basedbank.api.bml.BmlTapToPayClient
import sh.sar.basedbank.api.mib.MibCardsClient
import sh.sar.basedbank.nfc.BmlHostCardEmulatorService import sh.sar.basedbank.nfc.BmlHostCardEmulatorService
import sh.sar.basedbank.api.mib.MibCard import sh.sar.basedbank.api.mib.MibCard
import android.text.InputType
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
import sh.sar.basedbank.databinding.FragmentCardsBinding import sh.sar.basedbank.databinding.FragmentCardsBinding
import sh.sar.basedbank.util.CardsCache import sh.sar.basedbank.util.CardsCache
import sh.sar.basedbank.util.CredentialStore import sh.sar.basedbank.util.CredentialStore
@@ -63,6 +70,8 @@ class CardsFragment : Fragment() {
private var cardWidth: Int = 0 private var cardWidth: Int = 0
private var pendingQrCardNumber: String? = null private var pendingQrCardNumber: String? = null
private var isManageMode: Boolean = false private var isManageMode: Boolean = false
private var managedCardKey: String? = null
private var freezeInFlight: Boolean = false
private val qrLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> private val qrLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode != Activity.RESULT_OK) return@registerForActivityResult if (result.resultCode != Activity.RESULT_OK) return@registerForActivityResult
@@ -155,8 +164,14 @@ ViewCompat.setOnApplyWindowInsetsListener(binding.contentLayout) { v, insets ->
insets insets
} }
viewModel.mibCards.observe(viewLifecycleOwner) { rebuildCards() } viewModel.mibCards.observe(viewLifecycleOwner) {
viewModel.accounts.observe(viewLifecycleOwner) { rebuildCards() } rebuildCards()
rebindManagedCardIfNeeded()
}
viewModel.accounts.observe(viewLifecycleOwner) {
rebuildCards()
rebindManagedCardIfNeeded()
}
val cached = CardsCache.load(requireContext()) val cached = CardsCache.load(requireContext())
if (cached.isNotEmpty()) { if (cached.isNotEmpty()) {
@@ -247,20 +262,161 @@ ViewCompat.setOnApplyWindowInsetsListener(binding.contentLayout) { v, insets ->
Toast.makeText(requireContext(), R.string.work_in_progress, Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(), R.string.work_in_progress, Toast.LENGTH_SHORT).show()
} }
binding.btnChangePin.setOnClickListener(wip) binding.btnChangePin.setOnClickListener(wip)
binding.btnFreeze.setOnClickListener(wip) binding.btnFreeze.setOnClickListener {
when (val item = cards.getOrNull(currentCardPosition)) {
is CardItem.Bml -> confirmBmlFreezeToggle(item)
is CardItem.Mib -> confirmMibFreezeToggle(item)
null -> {}
}
}
binding.btnBlock.setOnClickListener(wip) binding.btnBlock.setOnClickListener(wip)
} }
private fun confirmBmlFreezeToggle(item: CardItem.Bml) {
if (freezeInFlight) return
val frozen = isBmlFrozen(item.account.statusDesc)
val titleRes = if (frozen) R.string.card_unfreeze_confirm_title else R.string.card_freeze_confirm_title
val messageRes = if (frozen) R.string.card_unfreeze_confirm_message else R.string.card_freeze_confirm_message
val confirmRes = if (frozen) R.string.card_action_unfreeze else R.string.card_action_freeze
MaterialAlertDialogBuilder(requireContext())
.setTitle(titleRes)
.setMessage(messageRes)
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(confirmRes) { _, _ -> performBmlFreezeToggle(item, freeze = !frozen) }
.show()
}
private fun confirmMibFreezeToggle(item: CardItem.Mib) {
if (freezeInFlight) return
val frozen = isMibCardFrozen(item.card.cardStatus)
val titleRes = if (frozen) R.string.card_unfreeze_confirm_title else R.string.card_freeze_confirm_title
val messageRes = if (frozen) R.string.card_unfreeze_confirm_message else R.string.card_freeze_confirm_message
val confirmRes = if (frozen) R.string.card_action_unfreeze else R.string.card_action_freeze
val ctx = requireContext()
val dp = resources.displayMetrics.density
val inputLayout = TextInputLayout(ctx).apply {
hint = getString(R.string.card_freeze_comments_hint)
val pad = (16 * dp).toInt()
setPadding(pad, pad / 2, pad, 0)
}
val input = TextInputEditText(ctx).apply {
inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
maxLines = 3
}
inputLayout.addView(input)
MaterialAlertDialogBuilder(ctx)
.setTitle(titleRes)
.setMessage(messageRes)
.setView(inputLayout)
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(confirmRes) { _, _ ->
val comments = input.text?.toString()?.trim().orEmpty()
performMibFreezeToggle(item, freeze = !frozen, comments = comments)
}
.show()
}
private fun performMibFreezeToggle(item: CardItem.Mib, freeze: Boolean, comments: String) {
val app = requireActivity().application as BasedBankApp
val action = if (freeze) "freeze" else "unfreeze"
val successRes = if (freeze) R.string.card_freeze_success else R.string.card_unfreeze_success
val loginId = item.card.loginTag.removePrefix("mib_")
val session = app.mibSessions[loginId]
if (session == null) {
Toast.makeText(requireContext(), R.string.transfer_session_unavailable, Toast.LENGTH_SHORT).show()
return
}
val profiles = app.mibProfilesMap[loginId] ?: emptyList()
val ownerProfile = profiles.firstOrNull { it.profileId == item.card.profileId }
?: profiles.firstOrNull { it.customerId == item.card.customerId }
freezeInFlight = true
binding.btnFreeze.isEnabled = false
(activity as? HomeActivity)?.setRefreshing(true)
viewLifecycleOwner.lifecycleScope.launch {
val result = withContext(Dispatchers.IO) {
runCatching {
app.mibMutex.withLock {
if (ownerProfile != null) {
app.mibFlowFor(loginId).switchProfile(session, ownerProfile)
}
MibCardsClient().setCardFreezeState(session, item.card.cardId, action, comments)
}
}
}
freezeInFlight = false
if (!isAdded || _binding == null) {
(activity as? HomeActivity)?.setRefreshing(false)
return@launch
}
binding.btnFreeze.isEnabled = true
(activity as? HomeActivity)?.setRefreshing(false)
val response = result.getOrNull()
if (response?.success == true) {
Toast.makeText(requireContext(), successRes, Toast.LENGTH_SHORT).show()
(activity as? HomeActivity)?.triggerRefreshCards()
} else {
val msg = response?.message?.takeIf { it.isNotBlank() }
?: result.exceptionOrNull()?.message
?: getString(R.string.card_freeze_failed)
Toast.makeText(requireContext(), msg, Toast.LENGTH_SHORT).show()
}
}
}
private fun performBmlFreezeToggle(item: CardItem.Bml, freeze: Boolean) {
val app = requireActivity().application as BasedBankApp
val action = if (freeze) "freeze" else "unfreeze"
val successRes = if (freeze) R.string.card_freeze_success else R.string.card_unfreeze_success
freezeInFlight = true
binding.btnFreeze.isEnabled = false
(activity as? HomeActivity)?.setRefreshing(true)
viewLifecycleOwner.lifecycleScope.launch {
val session = app.bmlSessionFor(item.account)
if (session == null) {
freezeInFlight = false
if (_binding != null) binding.btnFreeze.isEnabled = true
(activity as? HomeActivity)?.setRefreshing(false)
Toast.makeText(requireContext(), R.string.transfer_session_unavailable, Toast.LENGTH_SHORT).show()
return@launch
}
val result = withContext(Dispatchers.IO) {
runCatching { BmlCardClient().setCardFreezeState(session, item.account.internalId, action) }
}
freezeInFlight = false
if (!isAdded || _binding == null) {
(activity as? HomeActivity)?.setRefreshing(false)
return@launch
}
binding.btnFreeze.isEnabled = true
(activity as? HomeActivity)?.setRefreshing(false)
val response = result.getOrNull()
if (response?.success == true) {
Toast.makeText(requireContext(), successRes, Toast.LENGTH_SHORT).show()
(activity as? HomeActivity)?.refreshBalances(item.account)
} else {
val msg = response?.message?.takeIf { it.isNotBlank() }
?: result.exceptionOrNull()?.message
?: getString(R.string.card_freeze_failed)
Toast.makeText(requireContext(), msg, Toast.LENGTH_SHORT).show()
}
}
}
private fun setManageMode(enabled: Boolean) { private fun setManageMode(enabled: Boolean) {
isManageMode = enabled isManageMode = enabled
if (!enabled) managedCardKey = null
requireActivity().title = getString(if (enabled) R.string.card_manage else R.string.nav_pay_with_card) requireActivity().title = getString(if (enabled) R.string.card_manage else R.string.nav_pay_with_card)
if (enabled) enterManageMode() else exitManageMode() if (enabled) enterManageMode() else exitManageMode()
} }
private fun enterManageMode() { private fun cardItemKey(item: CardItem): String = when (item) {
val item = cards.getOrNull(currentCardPosition) ?: return is CardItem.Bml -> "bml:${item.account.accountNumber}"
is CardItem.Mib -> "mib:${item.card.cardId}"
}
// Bind card data private fun bindManageCardData(item: CardItem) {
val cv = binding.manageCardView val cv = binding.manageCardView
when (item) { when (item) {
is CardItem.Mib -> { is CardItem.Mib -> {
@@ -270,7 +426,7 @@ ViewCompat.setOnApplyWindowInsetsListener(binding.contentLayout) { v, insets ->
if (assetPath != null) loadCardImage(cv.ivCardImage, assetPath) if (assetPath != null) loadCardImage(cv.ivCardImage, assetPath)
else cv.ivCardImage.setImageDrawable(null) else cv.ivCardImage.setImageDrawable(null)
bindCardStatus(cv.tvCardStatus, mibCardStatusLabel(item.card.cardStatus)) bindCardStatus(cv.tvCardStatus, mibCardStatusLabel(item.card.cardStatus))
cv.root.alpha = 1f cv.root.alpha = if (isMibCardActive(item.card.cardStatus)) 1f else 0.45f
} }
is CardItem.Bml -> { is CardItem.Bml -> {
cv.tvCardOwner.text = item.account.accountBriefName cv.tvCardOwner.text = item.account.accountBriefName
@@ -281,6 +437,37 @@ ViewCompat.setOnApplyWindowInsetsListener(binding.contentLayout) { v, insets ->
cv.root.alpha = if (isActive) 1f else 0.45f cv.root.alpha = if (isActive) 1f else 0.45f
} }
} }
val isFrozen = when (item) {
is CardItem.Bml -> isBmlFrozen(item.account.statusDesc)
is CardItem.Mib -> isMibCardFrozen(item.card.cardStatus)
}
binding.btnFreeze.setText(if (isFrozen) R.string.card_action_unfreeze else R.string.card_action_freeze)
// MIB doesn't allow change PIN / block while a card is frozen; BML still does.
val mibFrozen = item is CardItem.Mib && isMibCardFrozen(item.card.cardStatus)
binding.btnChangePin.isEnabled = !mibFrozen
binding.btnBlock.isEnabled = !mibFrozen
}
private fun rebindManagedCardIfNeeded() {
if (!isManageMode) return
val key = managedCardKey ?: return
val newPos = cards.indexOfFirst { cardItemKey(it) == key }
if (newPos < 0) return
if (newPos != currentCardPosition) {
currentCardPosition = newPos
binding.rvCards.scrollToPosition(newPos)
}
bindManageCardData(cards[newPos])
}
private fun isBmlFrozen(statusDesc: String): Boolean =
statusDesc.equals("Block Plastic", ignoreCase = true)
private fun enterManageMode() {
val item = cards.getOrNull(currentCardPosition) ?: return
managedCardKey = cardItemKey(item)
bindManageCardData(item)
// Capture positions BEFORE layout changes (for enter animation + exit animation later) // Capture positions BEFORE layout changes (for enter animation + exit animation later)
val contentLoc = IntArray(2).also { binding.contentLayout.getLocationOnScreen(it) } val contentLoc = IntArray(2).also { binding.contentLayout.getLocationOnScreen(it) }
@@ -716,7 +903,9 @@ ViewCompat.setOnApplyWindowInsetsListener(binding.contentLayout) { v, insets ->
.map { CardItem.Bml(it) } .map { CardItem.Bml(it) }
val bmlActive = bmlItems.filter { it.account.statusDesc.equals("Active", ignoreCase = true) } val bmlActive = bmlItems.filter { it.account.statusDesc.equals("Active", ignoreCase = true) }
val bmlInactive = bmlItems.filter { !it.account.statusDesc.equals("Active", ignoreCase = true) } val bmlInactive = bmlItems.filter { !it.account.statusDesc.equals("Active", ignoreCase = true) }
val all: List<CardItem> = bmlActive + mibItems + bmlInactive val mibActive = mibItems.filter { isMibCardActive(it.card.cardStatus) }
val mibInactive = mibItems.filter { !isMibCardActive(it.card.cardStatus) }
val all: List<CardItem> = bmlActive + mibActive + bmlInactive + mibInactive
// Move default BML card to front // Move default BML card to front
cards = if (defaultNum != null) { cards = if (defaultNum != null) {
val def = all.filterIsInstance<CardItem.Bml>().firstOrNull { it.account.accountNumber == defaultNum } val def = all.filterIsInstance<CardItem.Bml>().firstOrNull { it.account.accountNumber == defaultNum }
@@ -903,7 +1092,7 @@ ViewCompat.setOnApplyWindowInsetsListener(binding.contentLayout) { v, insets ->
if (assetPath != null) loadCardImage(ivCardImage, assetPath) if (assetPath != null) loadCardImage(ivCardImage, assetPath)
else ivCardImage.setImageDrawable(null) else ivCardImage.setImageDrawable(null)
bindCardStatus(tvCardStatus, mibCardStatusLabel(item.card.cardStatus)) bindCardStatus(tvCardStatus, mibCardStatusLabel(item.card.cardStatus))
itemView.alpha = 1f itemView.alpha = if (isMibCardActive(item.card.cardStatus)) 1f else 0.45f
} }
is CardItem.Bml -> { is CardItem.Bml -> {
tvCardOwner.text = item.account.accountBriefName tvCardOwner.text = item.account.accountBriefName
@@ -1038,9 +1227,13 @@ ViewCompat.setOnApplyWindowInsetsListener(binding.contentLayout) { v, insets ->
fun mibCardStatusLabel(cardStatus: String): String? = when (cardStatus) { fun mibCardStatusLabel(cardStatus: String): String? = when (cardStatus) {
"CHST0" -> null "CHST0" -> null
"CHST20" -> "Temporary blocked by client"
else -> cardStatus else -> cardStatus
} }
fun isMibCardActive(cardStatus: String): Boolean = cardStatus == "CHST0"
fun isMibCardFrozen(cardStatus: String): Boolean = cardStatus == "CHST20"
fun bindCardStatus(tv: TextView, statusLabel: String?) { fun bindCardStatus(tv: TextView, statusLabel: String?) {
if (statusLabel == null) { tv.visibility = View.GONE; return } if (statusLabel == null) { tv.visibility = View.GONE; return }
tv.visibility = View.VISIBLE tv.visibility = View.VISIBLE
@@ -23,6 +23,7 @@ object CardsCache {
put("phoneNumber", c.phoneNumber) put("phoneNumber", c.phoneNumber)
put("cardHolderName", c.cardHolderName) put("cardHolderName", c.cardHolderName)
put("loginTag", c.loginTag) put("loginTag", c.loginTag)
put("profileId", c.profileId)
}) })
} }
context.getSharedPreferences(PREFS, Context.MODE_PRIVATE) context.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
@@ -45,7 +46,8 @@ object CardsCache {
customerId = o.optString("customerId"), customerId = o.optString("customerId"),
phoneNumber = o.optString("phoneNumber"), phoneNumber = o.optString("phoneNumber"),
cardHolderName = o.optString("cardHolderName"), cardHolderName = o.optString("cardHolderName"),
loginTag = o.optString("loginTag") loginTag = o.optString("loginTag"),
profileId = o.optString("profileId")
) )
} }
} catch (_: Exception) { emptyList() } } catch (_: Exception) { emptyList() }
+10
View File
@@ -369,7 +369,17 @@
<string name="card_hide_from_dashboard">Hide from Dashboard</string> <string name="card_hide_from_dashboard">Hide from Dashboard</string>
<string name="card_action_change_pin">Change PIN</string> <string name="card_action_change_pin">Change PIN</string>
<string name="card_action_freeze">Freeze</string> <string name="card_action_freeze">Freeze</string>
<string name="card_action_unfreeze">Unfreeze</string>
<string name="card_action_block">Block</string> <string name="card_action_block">Block</string>
<string name="card_freeze_confirm_title">Freeze card?</string>
<string name="card_freeze_confirm_message">This will temporarily stop the card from being used. You can unfreeze it anytime you want to use it again.</string>
<string name="card_unfreeze_confirm_title">Unfreeze card?</string>
<string name="card_unfreeze_confirm_message">This will re-enable the card for transactions.</string>
<string name="card_freeze_success">Card frozen</string>
<string name="card_unfreeze_success">Card unfrozen</string>
<string name="card_freeze_failed">Failed to update card status</string>
<string name="card_freeze_comments_hint">Reason (optional)</string>
<string name="card_status_temp_blocked">Temporary blocked by client</string>
<string name="cards_empty">No cards found</string> <string name="cards_empty">No cards found</string>
<!-- Connectivity banner --> <!-- Connectivity banner -->