diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bde60d..245a2fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Build process using flavours mechanism replaced by standard single build process. - Major refactoring of the source code. ### Upgraded +- `keyple-demo-common-lib:1.0.0-SNAPSHOT` - `calypsonet-terminal-calypso-java-api:1.2.+` - `keyple-service-java-lib:2.1.0` - `keyple-service-resource-java-lib:2.0.2` diff --git a/README.md b/README.md index c9f7797..148b5f7 100644 --- a/README.md +++ b/README.md @@ -2,105 +2,44 @@ This is the repository for the Keyple Android Validation Demo application. -This demo is an open source project provided by [Calypso Networks Association](https://calypsonet.org), -you can adapt the demo for your cards, terminals, projects, etc. +This demo is an open source project provided by the [Calypso Networks Association](https://calypsonet.org) implementing +the [Eclipse Keyple SDK](https://keyple.org) in a typical use case that can serve as a basis for building a ticketing +ecosystem based on contactless cards and/or NFC smartphones. -This demo shows how to easily validate a contract (Season Pass and/or Multi-trip ticket) written on a Calypso card -using the [Eclipse Keyple](https://keyple.org) components. +The source code and APK are available at [calypsonet/keyple-android-demo-validation/releases](https://github.com/calypsonet/keyple-android-demo-validation/releases) -The demo application runs on the following devices: -- `Bluebird` via the proprietary plugin [Bluebird](https://github.com/calypsonet/keyple-plugin-cna-bluebird-specific-nfc-java-lib). -- `Coppernic` via the open source plugin [Coppernic](https://github.com/calypsonet/keyple-android-plugin-coppernic). -- `Famoco` via the open source plugins [Famoco](https://github.com/calypsonet/keyple-famoco) (for SAM access) and [Android NFC](https://keyple.org/components-java/plugins/nfc/) (for card access). -- `Flowbird` via the proprietary plugin [Flowbird](https://github.com/calypsonet/keyple-android-plugin-flowbird). +The code can be easily adapted to other cards, terminals and business logic. -The source code and APK are available at [calypsonet/keyple-android-demo-validation/releases](https://github.com/calypsonet/keyple-android-demo-validation/releases) +It shows how to check if a card is authorized to enter a controlled area (entering the transport network with +a Season Pass and/or Multi-trip ticket), a validation event is added in the event log to be checked by the +[Keyple Demo Control](https://github.com/calypsonet/keyple-android-demo-control) application. +The contracts are loaded in the Calypso card with the Android application of the [Keyple Reload Demo package](https://github.com/calypsonet/keyple-java-demo-remote). -By default, proprietary plugins are deactivated. -If you want to activate them, then here is the procedure to follow: -1. make an explicit request to CNA to obtain the desired plugin, -2. copy the plugin into the `/app/libs/` directory, -3. delete in the `/app/libs/` directory the plugin with the same name but suffixed with `-mock` (e.g. xxx-mock.aar), -4. compile the project via the gradle `build` command, -5. deploy the new apk on the device. +The demo application was tested on the following terminals: +- `Famoco FX205` via the open source plugins [Famoco](https://github.com/calypsonet/keyple-famoco) (for SAM access) and [Android NFC](https://keyple.org/components-java/plugins/nfc/) (for card access). +- `Coppernic C-One 2` via the open source plugin [Coppernic](https://github.com/calypsonet/keyple-android-plugin-coppernic). + +The following terminals have also been tested but as they require non-open source libraries, they are not active by default (see [Using proprietary plugins](#using-proprietary-plugins)) +- `Bluebird EF501` via the proprietary plugin [Bluebird](https://github.com/calypsonet/keyple-plugin-cna-bluebird-specific-nfc-java-lib). +- `Flowbird Axio 2` via the proprietary plugin [Flowbird](https://github.com/calypsonet/keyple-android-plugin-flowbird). + +As all the exchanges made with the card are cryptographically secured by a security module (SAM), it is mandatory to install it in the terminal for the application to work properly. ## Keyple Demos This demo is part of a set of three demos: -* [Keyple Remote Demo](https://github.com/calypsonet/keyple-java-demo-remote) +* [Keyple Reload Demo](https://github.com/calypsonet/keyple-java-demo-remote) * [Keyple Validation Demo](https://github.com/calypsonet/keyple-android-demo-validation) * [Keyple Control Demo](https://github.com/calypsonet/keyple-android-demo-control) -## Calypso Card Applications +These demos are all based on a common library that defines elements such as constants and data structures implemented +for the logic of the ticketing application: [Keyple Demo Common Library](https://github.com/calypsonet/keyple-demo-common-lib). -The demo works with the cards provided in the [Test kit](https://calypsonet.org/technical-support-documentation/) - -This demo can be used with Calypso cards with the following configurations: -* AID 315449432E49434131h - File Structure 05h (CD Light/GTML Compatibility) -* AID 315449432E49434131h - File Structure 02h (Revision 2 Minimum with MF files) -* AID 315449432E49434133h - File Structure 32h (Calypso Light Classic) -* AID A0000004040125090101h - File Structure 05h (CD Light/GTML Compatibility) +Please refer to the [README](https://github.com/calypsonet/keyple-demo-common-lib/blob/main/README.md) +file of this library to discover these data structures. ## Validation Procedure -### Data Structures - -#### Environment/Holder structure - -| Field Name | Bits | Description | Type | Status | -|:---------------------|-----:|:---------------------------------------------------|:-------------:|:---------:| -| EnvVersionNumber | 8 | Data structure version number | VersionNumber | Mandatory | -| EnvApplicationNumber | 32 | Card application number (unique system identifier) | Int | Mandatory | -| EnvIssuingDate | 16 | Card application issuing date | DateCompact | Mandatory | -| EnvEndDate | 16 | Card application expiration date | DateCompact | Mandatory | -| HolderCompany | 8 | Holder company | Int | Optional | -| HolderIdNumber | 32 | Holder Identifier within HolderCompany | Int | Optional | -| EnvPadding | 120 | Padding (bits to 0) | Binary | Optional | - -#### Event structure - -| Field Name | Bits | Description | Type | Status | -|:-------------------|-----:|:----------------------------------------------|:-------------:|:---------:| -| EventVersionNumber | 8 | Data structure version number | VersionNumber | Mandatory | -| EventDateStamp | 16 | Date of the event | DateCompact | Mandatory | -| EventTimeStamp | 16 | Time of the event | TimeCompact | Mandatory | -| EventLocation | 32 | Location identifier | Int | Mandatory | -| EventContractUsed | 8 | Index of the contract used for the validation | Int | Mandatory | -| ContractPriority1 | 8 | Priority for contract #1 | PriorityCode | Mandatory | -| ContractPriority2 | 8 | Priority for contract #2 | PriorityCode | Mandatory | -| ContractPriority3 | 8 | Priority for contract #3 | PriorityCode | Mandatory | -| ContractPriority4 | 8 | Priority for contract #4 | PriorityCode | Mandatory | -| EventPadding | 120 | Padding (bits to 0) | Binary | Optional | - -#### Contract structure - -| Field Name | Bits | Description | Type | Status | -|:------------------------|-----:|:-------------------------------------|:-------------------:|:---------:| -| ContractVersionNumber | 8 | Data structure version number | VersionNumber | Mandatory | -| ContractTariff | 8 | Contract Type | PriorityCode | Mandatory | -| ContractSaleDate | 16 | Sale date of the contract | DateCompact | Mandatory | -| ContractValidityEndDate | 16 | Last day of validity of the contract | DateCompact | Mandatory | -| ContractSaleSam | 32 | SAM which loaded the contract | Int | Optional | -| ContractSaleCounter | 24 | SAM auth key counter value | Int | Optional | -| ContractAuthKvc | 8 | SAM auth key KVC | Int | Optional | -| ContractAuthenticator | 24 | Security authenticator | Authenticator (Int) | Optional | -| ContractPadding | 96 | Padding (bits to 0) | Binary | Optional | - -#### Counter structure - -| Field Name | Bits | Description | Type | Status | -|:-------------|-----:|:----------------|:----:|:---------:| -| CounterValue | 24 | Number of trips | Int | Mandatory | - -### Data Types - -| Name | Bits | Description | -|:--------------|-----:|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| DateCompact | 16 | Number of days since January 1st, 2010 (being date 0). Maximum value is 16,383, last complete year being 2053. All dates are in legal local time. | -| PriorityCode | 8 | Types of contracts defined:
0 Forbidden (present in clean records only)
1 Season Pass
2 Multi-trip ticket
3 Stored Value
4 to 30 RFU
31 Expired | -| TimeCompact | 16 | Time in minutes, value = hour*60+minute (0 to 1,439) | -| VersionNumber | 8 | Data model version:
0 Forbidden (undefined)
1 Current version
2..254 RFU
255 Forbidden (reserved) | - ### Validation Use Case This use case searches for the best candidate for validation from the existing contracts in the card. @@ -125,7 +64,7 @@ Steps: ### Process For this validation demo application, a simple example validation procedure has been implemented. -This procedure is implemented in the `ValidationProcedure` class. +This procedure is implemented in the `CardRepository` class. Opening a Calypso secure session is mandatory for this procedure since we need to write a new event in the event log. If no Calypso SAM is present, then the procedure will not execute and display an error. @@ -195,20 +134,20 @@ This procedure's main steps are as follows: - Season Pass: End of validity. - MultiTrip Ticket: number of tickets left. - If the validation fails, display the reason. - - ## Ticketing implementation Below are the classes useful for implementing the ticketing layer: -- `MainService` -- `ReaderService` -- `ReaderActivity.CardReaderObserver` - `TicketingService` +- `ReaderRepository` +- `ReaderActivity.CardReaderObserver` +- `CardRepository` -### MainService +### TicketingService + +This service is the orchestrator of the ticketing process. -Mainly used to manage the lifecycle of the Keyple plugin. +Mainly used to manage the lifecycle of the Keyple plugin. This service is used to initialize the plugin and manage the card detection phase. It is called on the different steps of the reader activity lifecycle: - onResume: @@ -219,8 +158,16 @@ It is called on the different steps of the reader activity lifecycle: - Stop NFC detection - onDestroy: - Clear the Keyple plugin (remove observers and unregister plugin) + +It prepares and scheduled the selection scenario that will be sent to the card when a card is detected by setting +the AID(s) and the reader protocol(s) of the cards we want to detect and read. -### ReaderService +Once a card is detected, the service processes the selection scenario by retrieving the current `CalypsoCard` object. +This object contains information about the card (serial number, card revision...) + +Finally, this class is responsible for launching the validation procedure and returning its result. + +### ReaderRepository This service is the interface between the business layer and the reader. @@ -231,15 +178,17 @@ This class is the reader observer and inherits from Keyple class `CardReaderObse It is invoked each time a new `CardReaderEvent` (`CARD_INSERTED`, `CARD_MATCHED`...) is launched by the Keyple plugin. This reader is registered when the reader is registered and removed when the reader is unregistered. -### TicketingService - -This service is the orchestrator of the ticketing process. +### CardRepository -First it prepares and scheduled the selection scenario that will be sent to the card when a card is detected by setting -the AID(s) and the reader protocol(s) of the cards we want to detect and read. +This class contains the implementation of the "Validation" procedure. -Once a card is detected, the service processes the selection scenario by retrieving the current `CalypsoCard` object. -This object contains information about the card (serial number, card revision...) +## Using proprietary plugins -Finally, this class is responsible for launching the validation procedure and returning its result. +By default, proprietary plugins are deactivated. +If you want to activate them, then here is the procedure to follow: +1. make an explicit request to [CNA](https://calypsonet.org/contact-us/) to obtain the desired plugin, +2. copy the plugin into the `/app/libs/` directory, +3. delete in the `/app/libs/` directory the plugin with the same name but suffixed with `-mock` (e.g. xxx-mock.aar), +4. compile the project via the gradle `build` command, +5. deploy the new apk on the device. diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 43e82fd..ec434f2 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -1,5 +1,5 @@ # Keep demo app classes --keep class org.calypsonet.keyple.demo.validation.service.ticketing.model.Location { *; } +-keep class org.calypsonet.keyple.demo.validation.data.model.Location { *; } -keep class org.joda.time.** { *; } # Keep Keyple library classes diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index eb6d39d..0976a3c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -15,7 +15,7 @@ android:theme="@style/AppTheme" > @@ -38,34 +38,28 @@ - - - - - - diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/Application.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/Application.kt index 5e43bfa..e59fae8 100644 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/Application.kt +++ b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/Application.kt @@ -14,8 +14,8 @@ package org.calypsonet.keyple.demo.validation import android.content.Context import androidx.multidex.MultiDex import dagger.android.DaggerApplication -import org.calypsonet.keyple.demo.validation.android.di.AppComponent -import org.calypsonet.keyple.demo.validation.android.di.DaggerAppComponent +import org.calypsonet.keyple.demo.validation.di.AppComponent +import org.calypsonet.keyple.demo.validation.di.DaggerAppComponent import timber.log.Timber import timber.log.Timber.DebugTree diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/ApplicationSettings.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/ApplicationSettings.kt deleted file mode 100644 index 2b93b1c..0000000 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/ApplicationSettings.kt +++ /dev/null @@ -1,21 +0,0 @@ -/* ************************************************************************************** - * Copyright (c) 2021 Calypso Networks Association https://calypsonet.org/ - * - * See the NOTICE file(s) distributed with this work for additional information - * regarding copyright ownership. - * - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation - -import org.calypsonet.keyple.demo.validation.service.reader.ReaderType -import org.calypsonet.keyple.demo.validation.service.ticketing.model.Location - -object ApplicationSettings { - lateinit var readerType: ReaderType - lateinit var location: Location - var batteryPowered: Boolean = true -} diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/di/SchedulerModule.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/di/SchedulerModule.kt deleted file mode 100644 index b4dc135..0000000 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/di/SchedulerModule.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* ************************************************************************************** - * Copyright (c) 2021 Calypso Networks Association https://calypsonet.org/ - * - * See the NOTICE file(s) distributed with this work for additional information - * regarding copyright ownership. - * - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.android.di - -import dagger.Module -import dagger.Provides -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.schedulers.Schedulers -import org.calypsonet.keyple.demo.validation.android.di.scope.AppScoped -import org.calypsonet.keyple.demo.validation.android.rx.SchedulerProvider - -@Suppress("unused") -@Module -class SchedulerModule { - - @Provides - @AppScoped - fun provideSchedulerProvider(): SchedulerProvider = - SchedulerProvider(Schedulers.io(), AndroidSchedulers.mainThread()) -} diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/receiver/StartUpReceiver.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/receiver/StartUpReceiver.kt deleted file mode 100644 index f3dd001..0000000 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/receiver/StartUpReceiver.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* ************************************************************************************** - * Copyright (c) 2021 Calypso Networks Association https://calypsonet.org/ - * - * See the NOTICE file(s) distributed with this work for additional information - * regarding copyright ownership. - * - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.android.receiver - -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import org.calypsonet.keyple.demo.validation.android.activity.SplashScreenActivity - -/** - * This class is used only on the Flowbird (Axio2) device. You can use it if you want to launch this - * Demo app when the device starts up. - */ -class StartUpReceiver : BroadcastReceiver() { - - override fun onReceive(context: Context, intent: Intent) { - startApp(context) - } - - private fun startApp(context: Context) { - val dialogIntent = Intent(context, SplashScreenActivity::class.java) - dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - context.startActivity(dialogIntent) - } -} diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/rx/SchedulerProvider.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/rx/SchedulerProvider.kt deleted file mode 100644 index 37e43ac..0000000 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/rx/SchedulerProvider.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* ************************************************************************************** - * Copyright (c) 2021 Calypso Networks Association https://calypsonet.org/ - * - * See the NOTICE file(s) distributed with this work for additional information - * regarding copyright ownership. - * - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.android.rx - -import io.reactivex.Scheduler -import javax.inject.Inject - -class SchedulerProvider -@Inject -constructor(private val backScheduler: Scheduler, private val foreScheduler: Scheduler) { - - /** IO thread pool scheduler */ - fun io(): Scheduler { - return backScheduler - } - - /** Main Thread scheduler */ - fun ui(): Scheduler { - return foreScheduler - } -} diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/procedure/ValidationProcedure.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/data/CardRepository.kt similarity index 77% rename from app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/procedure/ValidationProcedure.kt rename to app/src/main/kotlin/org/calypsonet/keyple/demo/validation/data/CardRepository.kt index 12fef19..aefdc29 100644 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/procedure/ValidationProcedure.kt +++ b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/data/CardRepository.kt @@ -9,11 +9,12 @@ * * SPDX-License-Identifier: EPL-2.0 ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.service.ticketing.procedure +package org.calypsonet.keyple.demo.validation.data import android.content.Context import java.util.Calendar import java.util.Date +import org.calypsonet.keyple.demo.common.constant.CardConstant import org.calypsonet.keyple.demo.common.model.EventStructure import org.calypsonet.keyple.demo.common.model.type.DateCompact import org.calypsonet.keyple.demo.common.model.type.PriorityCode @@ -22,28 +23,13 @@ import org.calypsonet.keyple.demo.common.model.type.VersionNumber import org.calypsonet.keyple.demo.common.parser.ContractStructureParser import org.calypsonet.keyple.demo.common.parser.EnvironmentHolderStructureParser import org.calypsonet.keyple.demo.common.parser.EventStructureParser -import org.calypsonet.keyple.demo.validation.ApplicationSettings import org.calypsonet.keyple.demo.validation.R -import org.calypsonet.keyple.demo.validation.service.ticketing.CalypsoInfo.RECORD_NUMBER_1 -import org.calypsonet.keyple.demo.validation.service.ticketing.CalypsoInfo.RECORD_NUMBER_2 -import org.calypsonet.keyple.demo.validation.service.ticketing.CalypsoInfo.RECORD_NUMBER_3 -import org.calypsonet.keyple.demo.validation.service.ticketing.CalypsoInfo.RECORD_NUMBER_4 -import org.calypsonet.keyple.demo.validation.service.ticketing.CalypsoInfo.SFI_CONTRACTS -import org.calypsonet.keyple.demo.validation.service.ticketing.CalypsoInfo.SFI_COUNTER -import org.calypsonet.keyple.demo.validation.service.ticketing.CalypsoInfo.SFI_ENVIRONMENT_AND_HOLDER -import org.calypsonet.keyple.demo.validation.service.ticketing.CalypsoInfo.SFI_EVENTS_LOG -import org.calypsonet.keyple.demo.validation.service.ticketing.exception.ContractVersionNumberErrorException -import org.calypsonet.keyple.demo.validation.service.ticketing.exception.EnvironmentException -import org.calypsonet.keyple.demo.validation.service.ticketing.exception.EnvironmentExceptionKey -import org.calypsonet.keyple.demo.validation.service.ticketing.exception.EventException -import org.calypsonet.keyple.demo.validation.service.ticketing.exception.EventExceptionKey -import org.calypsonet.keyple.demo.validation.service.ticketing.exception.NoContractAvailableException -import org.calypsonet.keyple.demo.validation.service.ticketing.exception.ValidationException -import org.calypsonet.keyple.demo.validation.service.ticketing.model.CardReaderResponse -import org.calypsonet.keyple.demo.validation.service.ticketing.model.Location -import org.calypsonet.keyple.demo.validation.service.ticketing.model.Status -import org.calypsonet.keyple.demo.validation.service.ticketing.model.Validation -import org.calypsonet.keyple.demo.validation.service.ticketing.model.mapper.ValidationMapper +import org.calypsonet.keyple.demo.validation.data.model.AppSettings +import org.calypsonet.keyple.demo.validation.data.model.CardReaderResponse +import org.calypsonet.keyple.demo.validation.data.model.Location +import org.calypsonet.keyple.demo.validation.data.model.Status +import org.calypsonet.keyple.demo.validation.data.model.Validation +import org.calypsonet.keyple.demo.validation.data.model.mapper.ValidationMapper import org.calypsonet.terminal.calypso.WriteAccessLevel import org.calypsonet.terminal.calypso.card.CalypsoCard import org.calypsonet.terminal.calypso.transaction.CardSecuritySetting @@ -53,9 +39,9 @@ import org.eclipse.keyple.card.calypso.CalypsoExtensionService import org.joda.time.DateTime import timber.log.Timber -class ValidationProcedure { +class CardRepository { - fun launch( + fun executeValidationProcedure( now: DateTime, context: Context, validationAmount: Int, @@ -92,11 +78,11 @@ class ValidationProcedure { // ***************** Event and Environment Analysis // Step 1 - Open a Validation session reading the environment record. - cardTransaction.prepareReadRecord(SFI_ENVIRONMENT_AND_HOLDER, RECORD_NUMBER_1) + cardTransaction.prepareReadRecord(CardConstant.SFI_ENVIRONMENT_AND_HOLDER, 1) cardTransaction.processOpening(WriteAccessLevel.DEBIT) // Step 2 - Unpack environment structure from the binary present in the environment record. - val efEnvironmentHolder = calypsoCard.getFileBySfi(SFI_ENVIRONMENT_AND_HOLDER) + val efEnvironmentHolder = calypsoCard.getFileBySfi(CardConstant.SFI_ENVIRONMENT_AND_HOLDER) val environmentContent = efEnvironmentHolder.data.content val environment = EnvironmentHolderStructureParser().parse(environmentContent) @@ -104,22 +90,22 @@ class ValidationProcedure { // for the current version) reject the card. if (environment.envVersionNumber != VersionNumber.CURRENT_VERSION) { status = Status.INVALID_CARD - throw EnvironmentException(EnvironmentExceptionKey.WRONG_VERSION_NUMBER) + throw RuntimeException("Environment Error: wrong version number") } // Step 4 - If EnvEndDate points to a date in the past reject the card. - val envEndDate = DateTime(environment.envEndDate) + val envEndDate = DateTime(environment.envEndDate.date) if (envEndDate.isBefore(now)) { status = Status.INVALID_CARD - throw EnvironmentException(EnvironmentExceptionKey.EXPIRED) + throw RuntimeException("Environment Error: end date expired") } // Step 5 - Read and unpack the last event record. - cardTransaction.prepareReadRecord(SFI_EVENTS_LOG, RECORD_NUMBER_1) + cardTransaction.prepareReadRecord(CardConstant.SFI_EVENTS_LOG, 1) cardTransaction.processCommands() - val efEventLog = calypsoCard.getFileBySfi(SFI_EVENTS_LOG) + val efEventLog = calypsoCard.getFileBySfi(CardConstant.SFI_EVENTS_LOG) val eventContent = efEventLog.data.content val event = EventStructureParser().parse(eventContent) @@ -130,10 +116,10 @@ class ValidationProcedure { if (eventVersionNumber != VersionNumber.CURRENT_VERSION) { if (eventVersionNumber == VersionNumber.UNDEFINED) { status = Status.EMPTY_CARD - throw EventException(EventExceptionKey.CLEAN_CARD) + throw RuntimeException("Event - Card is empty") } else { status = Status.INVALID_CARD - throw EventException(EventExceptionKey.WRONG_VERSION_NUMBER) + throw RuntimeException("Event - Wrong version number") } } @@ -141,7 +127,8 @@ class ValidationProcedure { val contractPriorities = mutableListOf>() // ***************** Best Contract Search - // Step 7 - Create a list of PriorityCode fields that are different from 0 or 31. + // Step 7 - Create a list of PriorityCode fields that are different from FORBIDDEN and + // EXPIRED. if (event.contractPriority1 != PriorityCode.FORBIDDEN && event.contractPriority1 != PriorityCode.EXPIRED) { contractPriorities.add(Pair(1, event.contractPriority1)) @@ -162,7 +149,7 @@ class ValidationProcedure { if (contractPriorities.isEmpty()) { // Step 9 - If the list is empty go to END. status = Status.EMPTY_CARD - throw NoContractAvailableException() + throw RuntimeException("No valid title detected") } var priority1 = event.contractPriority1 @@ -181,11 +168,11 @@ class ValidationProcedure { val contractPriority = it.second // Step 11.1 - Read and unpack the contract record for the index being iterated. - cardTransaction.prepareReadRecord(SFI_CONTRACTS, record) + cardTransaction.prepareReadRecord(CardConstant.SFI_CONTRACTS, record) cardTransaction.processCommands() - val efContractParser = calypsoCard.getFileBySfi(SFI_CONTRACTS) + val efContractParser = calypsoCard.getFileBySfi(CardConstant.SFI_CONTRACTS) val contractContent = efContractParser.data.allRecordsContent[record]!! val contract = ContractStructureParser().parse(contractContent) @@ -193,7 +180,7 @@ class ValidationProcedure { // version) reject the card. if (contract.contractVersionNumber != VersionNumber.CURRENT_VERSION) { status = Status.INVALID_CARD - throw ContractVersionNumberErrorException() + throw RuntimeException("Contract Version Number error (!= CURRENT_VERSION)") } // Step 11.3 - ' If ContractAuthenticator is not 0 perform the verification of the value @@ -209,13 +196,13 @@ class ValidationProcedure { // Step 11.4 - If ContractValidityEndDate points to a date in the past update the // associated ContractPriorty field present in the persistent object to 31 and move to the // next element in the list - val contractValidityEndDate = DateTime(contract.contractValidityEndDate) + val contractValidityEndDate = DateTime(contract.contractValidityEndDate.date) if (contractValidityEndDate.isBefore(now)) { when (record) { - RECORD_NUMBER_1 -> priority1 = PriorityCode.EXPIRED - RECORD_NUMBER_2 -> priority2 = PriorityCode.EXPIRED - RECORD_NUMBER_3 -> priority3 = PriorityCode.EXPIRED - RECORD_NUMBER_4 -> priority4 = PriorityCode.EXPIRED + 1 -> priority1 = PriorityCode.EXPIRED + 2 -> priority2 = PriorityCode.EXPIRED + 3 -> priority3 = PriorityCode.EXPIRED + 4 -> priority4 = PriorityCode.EXPIRED } status = Status.EMPTY_CARD errorMessage = context.getString(R.string.expired_title) @@ -229,20 +216,20 @@ class ValidationProcedure { // Step 11.5.1 - Read and unpack the counter associated to the contract (1st counter for // Contract #1 and so forth). - cardTransaction.prepareReadCounter(SFI_COUNTER, COUNTER_RECORDS_NB) + cardTransaction.prepareReadCounter(CardConstant.SFI_COUNTERS, COUNTER_RECORDS_NB) cardTransaction.processCommands() - val efCounter = calypsoCard.getFileBySfi(SFI_COUNTER) + val efCounter = calypsoCard.getFileBySfi(CardConstant.SFI_COUNTERS) val counterValue = efCounter.data.getContentAsCounterValue(record) // Step 11.5.2 - If the counter value is 0 update the associated ContractPriorty field // present in the persistent object to 31 and move to the next element in the list if (counterValue == 0) { when (record) { - RECORD_NUMBER_1 -> priority1 = PriorityCode.EXPIRED - RECORD_NUMBER_2 -> priority2 = PriorityCode.EXPIRED - RECORD_NUMBER_3 -> priority3 = PriorityCode.EXPIRED - RECORD_NUMBER_4 -> priority4 = PriorityCode.EXPIRED + 1 -> priority1 = PriorityCode.EXPIRED + 2 -> priority2 = PriorityCode.EXPIRED + 3 -> priority3 = PriorityCode.EXPIRED + 4 -> priority4 = PriorityCode.EXPIRED } status = Status.EMPTY_CARD errorMessage = context.getString(R.string.no_trips_left) @@ -269,7 +256,7 @@ class ValidationProcedure { else -> 0 } if (decrement > 0) { - cardTransaction.prepareDecreaseCounter(SFI_COUNTER, record, decrement) + cardTransaction.prepareDecreaseCounter(CardConstant.SFI_COUNTERS, record, decrement) cardTransaction.processCommands() nbTicketsLeft = counterValue - decrement @@ -299,7 +286,7 @@ class ValidationProcedure { eventVersionNumber = VersionNumber.CURRENT_VERSION, eventDateStamp = DateCompact(eventDate), eventTimeStamp = TimeCompact(eventDate), - eventLocation = ApplicationSettings.location.id, + eventLocation = AppSettings.location.id, eventContractUsed = contractUsed, contractPriority1 = priority1, contractPriority2 = priority2, @@ -327,7 +314,7 @@ class ValidationProcedure { // Step 13 - Pack the Event structure and append it to the event file val eventBytesToWrite = EventStructureParser().generate(eventToWrite) - cardTransaction.prepareUpdateRecord(SFI_EVENTS_LOG, RECORD_NUMBER_1, eventBytesToWrite) + cardTransaction.prepareUpdateRecord(CardConstant.SFI_EVENTS_LOG, 1, eventBytesToWrite) cardTransaction.processCommands() } else { Timber.i("Validation procedure result : Failed - No valid contract found") @@ -335,14 +322,10 @@ class ValidationProcedure { errorMessage = context.getString(R.string.no_valid_title_detected) } } - } catch (e: ValidationException) { - Timber.e(e) - errorMessage = e.message } catch (e: Exception) { Timber.e(e) errorMessage = e.message } finally { - // Step 14 - END: Close the session try { if (status == Status.SUCCESS) { @@ -350,7 +333,6 @@ class ValidationProcedure { } else { cardTransaction.processCancel() } - if (status == Status.LOADING) { status = Status.ERROR } diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/file/LocationFileService.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/data/LocationRepository.kt similarity index 83% rename from app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/file/LocationFileService.kt rename to app/src/main/kotlin/org/calypsonet/keyple/demo/validation/data/LocationRepository.kt index abe6cae..0281174 100644 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/file/LocationFileService.kt +++ b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/data/LocationRepository.kt @@ -9,7 +9,7 @@ * * SPDX-License-Identifier: EPL-2.0 ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.service.file +package org.calypsonet.keyple.demo.validation.data import android.content.Context import com.google.gson.Gson @@ -19,9 +19,9 @@ import java.io.IOException import java.io.InputStream import java.io.InputStreamReader import javax.inject.Inject -import org.calypsonet.keyple.demo.validation.service.ticketing.model.Location +import org.calypsonet.keyple.demo.validation.data.model.Location -class LocationFileService @Inject constructor(context: Context) { +class LocationRepository @Inject constructor(context: Context) { private val locationList: List @@ -38,7 +38,7 @@ class LocationFileService @Inject constructor(context: Context) { /** Get file from raw embedded directory */ private fun getFileFromResources(context: Context): String { - val resId = context.resources.getIdentifier(LOCATION_FILE_NAME, "raw", context.packageName) + val resId = context.resources.getIdentifier("locations", "raw", context.packageName) val inputStream = context.resources.openRawResource(resId) return parseFile(inputStream) } @@ -64,8 +64,4 @@ class LocationFileService @Inject constructor(context: Context) { gsonBuilder.setLenient() return gsonBuilder.create() } - - companion object { - const val LOCATION_FILE_NAME = "locations" - } } diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/reader/ReaderService.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/data/ReaderRepository.kt similarity index 98% rename from app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/reader/ReaderService.kt rename to app/src/main/kotlin/org/calypsonet/keyple/demo/validation/data/ReaderRepository.kt index ee8f962..7862760 100644 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/reader/ReaderService.kt +++ b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/data/ReaderRepository.kt @@ -9,7 +9,7 @@ * * SPDX-License-Identifier: EPL-2.0 ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.service.reader +package org.calypsonet.keyple.demo.validation.data import android.app.Activity import android.media.MediaPlayer @@ -18,6 +18,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext import org.calypsonet.keyple.demo.validation.R +import org.calypsonet.keyple.demo.validation.data.model.ReaderType import org.calypsonet.keyple.plugin.bluebird.BluebirdContactReader import org.calypsonet.keyple.plugin.bluebird.BluebirdContactlessReader import org.calypsonet.keyple.plugin.bluebird.BluebirdPlugin @@ -46,7 +47,7 @@ import org.eclipse.keyple.plugin.android.nfc.AndroidNfcPlugin import org.eclipse.keyple.plugin.android.nfc.AndroidNfcPluginFactoryProvider import org.eclipse.keyple.plugin.android.nfc.AndroidNfcReader -class ReaderService +class ReaderRepository @Inject constructor( private val readerObservationExceptionHandler: CardReaderObservationExceptionHandlerSpi diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/exception/ValidationException.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/data/model/AppSettings.kt similarity index 75% rename from app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/exception/ValidationException.kt rename to app/src/main/kotlin/org/calypsonet/keyple/demo/validation/data/model/AppSettings.kt index 8d69f75..1759d3a 100644 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/exception/ValidationException.kt +++ b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/data/model/AppSettings.kt @@ -9,7 +9,10 @@ * * SPDX-License-Identifier: EPL-2.0 ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.service.ticketing.exception +package org.calypsonet.keyple.demo.validation.data.model -abstract class ValidationException protected constructor(message: String?) : - RuntimeException(message) +object AppSettings { + lateinit var readerType: ReaderType + lateinit var location: Location + var batteryPowered: Boolean = true +} diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/model/CardReaderResponse.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/data/model/CardReaderResponse.kt similarity index 93% rename from app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/model/CardReaderResponse.kt rename to app/src/main/kotlin/org/calypsonet/keyple/demo/validation/data/model/CardReaderResponse.kt index 5141f29..05ecc10 100644 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/model/CardReaderResponse.kt +++ b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/data/model/CardReaderResponse.kt @@ -9,7 +9,7 @@ * * SPDX-License-Identifier: EPL-2.0 ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.service.ticketing.model +package org.calypsonet.keyple.demo.validation.data.model import android.os.Parcelable import java.util.Date diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/model/Location.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/data/model/Location.kt similarity index 92% rename from app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/model/Location.kt rename to app/src/main/kotlin/org/calypsonet/keyple/demo/validation/data/model/Location.kt index e6cda83..9f006cd 100644 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/model/Location.kt +++ b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/data/model/Location.kt @@ -9,7 +9,7 @@ * * SPDX-License-Identifier: EPL-2.0 ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.service.ticketing.model +package org.calypsonet.keyple.demo.validation.data.model import android.os.Parcelable import kotlinx.android.parcel.Parcelize diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/reader/ReaderType.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/data/model/ReaderType.kt similarity index 91% rename from app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/reader/ReaderType.kt rename to app/src/main/kotlin/org/calypsonet/keyple/demo/validation/data/model/ReaderType.kt index 3bbe594..f2dbcfa 100644 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/reader/ReaderType.kt +++ b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/data/model/ReaderType.kt @@ -9,7 +9,7 @@ * * SPDX-License-Identifier: EPL-2.0 ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.service.reader +package org.calypsonet.keyple.demo.validation.data.model enum class ReaderType { BLUEBIRD, diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/model/Status.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/data/model/Status.kt similarity index 94% rename from app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/model/Status.kt rename to app/src/main/kotlin/org/calypsonet/keyple/demo/validation/data/model/Status.kt index 68f0c6c..102804b 100644 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/model/Status.kt +++ b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/data/model/Status.kt @@ -9,7 +9,7 @@ * * SPDX-License-Identifier: EPL-2.0 ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.service.ticketing.model +package org.calypsonet.keyple.demo.validation.data.model import java.util.Locale diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/model/Validation.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/data/model/Validation.kt similarity index 92% rename from app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/model/Validation.kt rename to app/src/main/kotlin/org/calypsonet/keyple/demo/validation/data/model/Validation.kt index 5c26af3..0a00f92 100644 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/model/Validation.kt +++ b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/data/model/Validation.kt @@ -9,7 +9,7 @@ * * SPDX-License-Identifier: EPL-2.0 ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.service.ticketing.model +package org.calypsonet.keyple.demo.validation.data.model import android.os.Parcelable import java.util.Date diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/model/mapper/LocationMapper.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/data/model/mapper/LocationMapper.kt similarity index 84% rename from app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/model/mapper/LocationMapper.kt rename to app/src/main/kotlin/org/calypsonet/keyple/demo/validation/data/model/mapper/LocationMapper.kt index b582df8..fb22e30 100644 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/model/mapper/LocationMapper.kt +++ b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/data/model/mapper/LocationMapper.kt @@ -9,10 +9,10 @@ * * SPDX-License-Identifier: EPL-2.0 ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.service.ticketing.model.mapper +package org.calypsonet.keyple.demo.validation.data.model.mapper import org.calypsonet.keyple.demo.common.model.EventStructure -import org.calypsonet.keyple.demo.validation.service.ticketing.model.Location +import org.calypsonet.keyple.demo.validation.data.model.Location object LocationMapper { fun map(locations: List, event: EventStructure): Location { diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/model/mapper/ValidationMapper.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/data/model/mapper/ValidationMapper.kt similarity index 80% rename from app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/model/mapper/ValidationMapper.kt rename to app/src/main/kotlin/org/calypsonet/keyple/demo/validation/data/model/mapper/ValidationMapper.kt index 94b6177..f9478a7 100644 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/model/mapper/ValidationMapper.kt +++ b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/data/model/mapper/ValidationMapper.kt @@ -9,11 +9,11 @@ * * SPDX-License-Identifier: EPL-2.0 ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.service.ticketing.model.mapper +package org.calypsonet.keyple.demo.validation.data.model.mapper import org.calypsonet.keyple.demo.common.model.EventStructure -import org.calypsonet.keyple.demo.validation.service.ticketing.model.Location -import org.calypsonet.keyple.demo.validation.service.ticketing.model.Validation +import org.calypsonet.keyple.demo.validation.data.model.Location +import org.calypsonet.keyple.demo.validation.data.model.Validation object ValidationMapper { fun map(event: EventStructure, locations: List): Validation { diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/di/AppComponent.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/di/AppComponent.kt similarity index 88% rename from app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/di/AppComponent.kt rename to app/src/main/kotlin/org/calypsonet/keyple/demo/validation/di/AppComponent.kt index 871c14d..027d438 100644 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/di/AppComponent.kt +++ b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/di/AppComponent.kt @@ -9,14 +9,14 @@ * * SPDX-License-Identifier: EPL-2.0 ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.android.di +package org.calypsonet.keyple.demo.validation.di import dagger.BindsInstance import dagger.Component import dagger.android.AndroidInjector import dagger.android.support.AndroidSupportInjectionModule import org.calypsonet.keyple.demo.validation.Application -import org.calypsonet.keyple.demo.validation.android.di.scope.AppScoped +import org.calypsonet.keyple.demo.validation.di.scope.AppScoped @AppScoped @Component( @@ -24,7 +24,6 @@ import org.calypsonet.keyple.demo.validation.android.di.scope.AppScoped [ AppModule::class, UIModule::class, - SchedulerModule::class, AndroidSupportInjectionModule::class, ReaderModule::class, LocationModule::class]) diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/di/AppModule.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/di/AppModule.kt similarity index 94% rename from app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/di/AppModule.kt rename to app/src/main/kotlin/org/calypsonet/keyple/demo/validation/di/AppModule.kt index c60c635..9ac883d 100644 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/di/AppModule.kt +++ b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/di/AppModule.kt @@ -9,7 +9,7 @@ * * SPDX-License-Identifier: EPL-2.0 ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.android.di +package org.calypsonet.keyple.demo.validation.di import android.content.Context import dagger.Binds diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/di/LocationModule.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/di/LocationModule.kt similarity index 70% rename from app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/di/LocationModule.kt rename to app/src/main/kotlin/org/calypsonet/keyple/demo/validation/di/LocationModule.kt index 7931009..f04cff9 100644 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/di/LocationModule.kt +++ b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/di/LocationModule.kt @@ -9,13 +9,13 @@ * * SPDX-License-Identifier: EPL-2.0 ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.android.di +package org.calypsonet.keyple.demo.validation.di import android.content.Context import dagger.Module import dagger.Provides -import org.calypsonet.keyple.demo.validation.android.di.scope.AppScoped -import org.calypsonet.keyple.demo.validation.service.file.LocationFileService +import org.calypsonet.keyple.demo.validation.data.LocationRepository +import org.calypsonet.keyple.demo.validation.di.scope.AppScoped @Suppress("unused") @Module @@ -23,6 +23,5 @@ class LocationModule { @Provides @AppScoped - fun providesLocationFileService(context: Context): LocationFileService = - LocationFileService(context) + fun providesLocationRepository(context: Context): LocationRepository = LocationRepository(context) } diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/di/ReaderModule.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/di/ReaderModule.kt similarity index 79% rename from app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/di/ReaderModule.kt rename to app/src/main/kotlin/org/calypsonet/keyple/demo/validation/di/ReaderModule.kt index 4e3c021..8cb3d87 100644 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/di/ReaderModule.kt +++ b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/di/ReaderModule.kt @@ -9,12 +9,12 @@ * * SPDX-License-Identifier: EPL-2.0 ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.android.di +package org.calypsonet.keyple.demo.validation.di import dagger.Module import dagger.Provides -import org.calypsonet.keyple.demo.validation.android.di.scope.AppScoped -import org.calypsonet.keyple.demo.validation.service.reader.ReaderService +import org.calypsonet.keyple.demo.validation.data.ReaderRepository +import org.calypsonet.keyple.demo.validation.di.scope.AppScoped import org.calypsonet.terminal.reader.spi.CardReaderObservationExceptionHandlerSpi import timber.log.Timber @@ -24,9 +24,9 @@ class ReaderModule { @Provides @AppScoped - fun provideReaderService( + fun provideReaderRepository( cardReaderObservationExceptionHandlerSpi: CardReaderObservationExceptionHandlerSpi - ): ReaderService = ReaderService(cardReaderObservationExceptionHandlerSpi) + ): ReaderRepository = ReaderRepository(cardReaderObservationExceptionHandlerSpi) @Provides @AppScoped diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/di/UIModule.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/di/UIModule.kt similarity index 78% rename from app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/di/UIModule.kt rename to app/src/main/kotlin/org/calypsonet/keyple/demo/validation/di/UIModule.kt index f3c3f0a..d7a3409 100644 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/di/UIModule.kt +++ b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/di/UIModule.kt @@ -9,20 +9,19 @@ * * SPDX-License-Identifier: EPL-2.0 ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.android.di +package org.calypsonet.keyple.demo.validation.di import dagger.Module import dagger.android.ContributesAndroidInjector -import org.calypsonet.keyple.demo.validation.android.activity.* -import org.calypsonet.keyple.demo.validation.android.di.scope.ActivityScoped +import org.calypsonet.keyple.demo.validation.di.scope.ActivityScoped +import org.calypsonet.keyple.demo.validation.ui.* +import org.calypsonet.keyple.demo.validation.ui.deviceselection.DeviceSelectionActivity @Suppress("unused") @Module abstract class UIModule { - @ActivityScoped - @ContributesAndroidInjector - abstract fun splashScreenActivity(): SplashScreenActivity + @ActivityScoped @ContributesAndroidInjector abstract fun mainActivity(): MainActivity @ActivityScoped @ContributesAndroidInjector abstract fun settingsActivity(): SettingsActivity diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/di/scope/ActivityScoped.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/di/scope/ActivityScoped.kt similarity index 94% rename from app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/di/scope/ActivityScoped.kt rename to app/src/main/kotlin/org/calypsonet/keyple/demo/validation/di/scope/ActivityScoped.kt index b68d346..3197287 100644 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/di/scope/ActivityScoped.kt +++ b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/di/scope/ActivityScoped.kt @@ -9,7 +9,7 @@ * * SPDX-License-Identifier: EPL-2.0 ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.android.di.scope +package org.calypsonet.keyple.demo.validation.di.scope import java.lang.annotation.Documented import java.lang.annotation.Retention diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/di/scope/AppScoped.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/di/scope/AppScoped.kt similarity index 92% rename from app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/di/scope/AppScoped.kt rename to app/src/main/kotlin/org/calypsonet/keyple/demo/validation/di/scope/AppScoped.kt index ec16cc5..df66a56 100644 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/di/scope/AppScoped.kt +++ b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/di/scope/AppScoped.kt @@ -9,7 +9,7 @@ * * SPDX-License-Identifier: EPL-2.0 ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.android.di.scope +package org.calypsonet.keyple.demo.validation.di.scope import java.lang.annotation.Documented import java.lang.annotation.Retention diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/di/scope/FragmentScoped.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/di/scope/FragmentScoped.kt similarity index 93% rename from app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/di/scope/FragmentScoped.kt rename to app/src/main/kotlin/org/calypsonet/keyple/demo/validation/di/scope/FragmentScoped.kt index cf20180..de86750 100644 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/di/scope/FragmentScoped.kt +++ b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/di/scope/FragmentScoped.kt @@ -9,7 +9,7 @@ * * SPDX-License-Identifier: EPL-2.0 ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.android.di.scope +package org.calypsonet.keyple.demo.validation.di.scope import java.lang.annotation.Retention import java.lang.annotation.RetentionPolicy diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/domain/TicketingService.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/domain/TicketingService.kt new file mode 100644 index 0000000..e9feab9 --- /dev/null +++ b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/domain/TicketingService.kt @@ -0,0 +1,256 @@ +/* ************************************************************************************** + * Copyright (c) 2021 Calypso Networks Association https://calypsonet.org/ + * + * See the NOTICE file(s) distributed with this work for additional information + * regarding copyright ownership. + * + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + ************************************************************************************** */ +package org.calypsonet.keyple.demo.validation.domain + +import android.app.Activity +import android.content.Context +import javax.inject.Inject +import org.calypsonet.keyple.demo.common.constant.CardConstant +import org.calypsonet.keyple.demo.validation.data.CardRepository +import org.calypsonet.keyple.demo.validation.data.ReaderRepository +import org.calypsonet.keyple.demo.validation.data.model.CardReaderResponse +import org.calypsonet.keyple.demo.validation.data.model.Location +import org.calypsonet.keyple.demo.validation.data.model.ReaderType +import org.calypsonet.keyple.demo.validation.di.scope.AppScoped +import org.calypsonet.terminal.calypso.WriteAccessLevel +import org.calypsonet.terminal.calypso.card.CalypsoCard +import org.calypsonet.terminal.calypso.sam.CalypsoSam +import org.calypsonet.terminal.calypso.transaction.CardSecuritySetting +import org.calypsonet.terminal.reader.CardReader +import org.calypsonet.terminal.reader.ObservableCardReader +import org.calypsonet.terminal.reader.selection.CardSelectionManager +import org.calypsonet.terminal.reader.selection.CardSelectionResult +import org.calypsonet.terminal.reader.selection.ScheduledCardSelectionsResponse +import org.calypsonet.terminal.reader.spi.CardReaderObserverSpi +import org.eclipse.keyple.card.calypso.CalypsoExtensionService +import org.eclipse.keyple.core.service.KeyplePluginException +import org.eclipse.keyple.core.service.SmartCardServiceProvider +import org.eclipse.keyple.core.util.HexUtil +import org.joda.time.DateTime +import timber.log.Timber + +@AppScoped +class TicketingService @Inject constructor(private var readerRepository: ReaderRepository) { + + private val calypsoExtensionService: CalypsoExtensionService = + CalypsoExtensionService.getInstance() + + private lateinit var calypsoSam: CalypsoSam + private lateinit var calypsoCard: CalypsoCard + private lateinit var cardSelectionManager: CardSelectionManager + var readersInitialized = false + private set + + private var indexOfKeypleGenericCardSelection = 0 + private var indexOfCdLightGtmlCardSelection = 0 + private var indexOfCalypsoLightCardSelection = 0 + private var indexOfNavigoIdfCardSelection = 0 + + @Throws(KeyplePluginException::class, IllegalStateException::class, Exception::class) + fun init(observer: CardReaderObserverSpi?, activity: Activity, readerType: ReaderType) { + // Register plugin + try { + readerRepository.registerPlugin(activity, readerType) + } catch (e: Exception) { + Timber.e(e) + throw IllegalStateException(e.message) + } + // Init card reader + val cardReader: CardReader? + try { + cardReader = readerRepository.initCardReader() + } catch (e: Exception) { + Timber.e(e) + throw IllegalStateException(e.message) + } + // Init SAM reader + var samReaders: List? = null + try { + samReaders = readerRepository.initSamReaders() + } catch (e: Exception) { + Timber.e(e) + } + if (samReaders.isNullOrEmpty()) { + throw IllegalStateException("No SAM reader available") + } + // Register a card event observer and init the ticketing session + cardReader?.let { reader -> + (reader as ObservableCardReader).addObserver(observer) + // attempts to select a SAM if any, sets the isSecureSessionMode flag accordingly + val samReader = readerRepository.getSamReader() + if (samReader == null || !selectSam(samReader)) { + throw IllegalStateException("SAM reader or SAM not available") + } + } + readersInitialized = true + } + + fun startNfcDetection() { + // Provide the CardReader with the selection operation to be processed when a Card is inserted. + prepareAndScheduleCardSelectionScenario() + (readerRepository.getCardReader() as ObservableCardReader).startCardDetection( + ObservableCardReader.DetectionMode.REPEATING) + } + + fun stopNfcDetection() { + try { + // notify reader that se detection has been switched off + (readerRepository.getCardReader() as ObservableCardReader).stopCardDetection() + } catch (e: KeyplePluginException) { + Timber.e(e, "NFC Plugin not found") + } catch (e: Exception) { + Timber.e(e) + } + } + + fun onDestroy(observer: CardReaderObserverSpi?) { + readersInitialized = false + readerRepository.clear() + if (observer != null && readerRepository.getCardReader() != null) { + (readerRepository.getCardReader() as ObservableCardReader).removeObserver(observer) + } + val smartCardService = SmartCardServiceProvider.getService() + smartCardService.plugins.forEach { smartCardService.unregisterPlugin(it.name) } + } + + fun displayResultSuccess(): Boolean = readerRepository.displayResultSuccess() + + fun displayResultFailed(): Boolean = readerRepository.displayResultFailed() + + fun prepareAndScheduleCardSelectionScenario() { + + // Get the Keyple main service + val smartCardService = SmartCardServiceProvider.getService() + + // Check the Calypso card extension + smartCardService.checkCardExtension(calypsoExtensionService) + + // Get a new card selection manager + cardSelectionManager = smartCardService.createCardSelectionManager() + + // Prepare card selection case #1: Keyple generic + indexOfKeypleGenericCardSelection = + cardSelectionManager.prepareSelection( + calypsoExtensionService + .createCardSelection() + .filterByDfName(CardConstant.AID_KEYPLE_GENERIC) + .filterByCardProtocol(readerRepository.getCardReaderProtocolLogicalName())) + + // Prepare card selection case #2: CD LIGHT/GTML + indexOfCdLightGtmlCardSelection = + cardSelectionManager.prepareSelection( + calypsoExtensionService + .createCardSelection() + .filterByDfName(CardConstant.AID_CD_LIGHT_GTML) + .filterByCardProtocol(readerRepository.getCardReaderProtocolLogicalName())) + + // Prepare card selection case #3: CALYPSO LIGHT + indexOfCalypsoLightCardSelection = + cardSelectionManager.prepareSelection( + calypsoExtensionService + .createCardSelection() + .filterByDfName(CardConstant.AID_CALYPSO_LIGHT) + .filterByCardProtocol(readerRepository.getCardReaderProtocolLogicalName())) + + // Prepare card selection case #4: Navigo IDF + indexOfNavigoIdfCardSelection = + cardSelectionManager.prepareSelection( + calypsoExtensionService + .createCardSelection() + .filterByDfName(CardConstant.AID_NORMALIZED_IDF) + .filterByCardProtocol(readerRepository.getCardReaderProtocolLogicalName())) + + // Schedule the execution of the prepared card selection scenario as soon as a card is presented + cardSelectionManager.scheduleCardSelectionScenario( + readerRepository.getCardReader() as ObservableCardReader, + ObservableCardReader.DetectionMode.REPEATING, + ObservableCardReader.NotificationMode.ALWAYS) + } + + fun analyseSelectionResult( + scheduledCardSelectionsResponse: ScheduledCardSelectionsResponse + ): String? { + Timber.i("selectionResponse = $scheduledCardSelectionsResponse") + val cardSelectionResult: CardSelectionResult = + cardSelectionManager.parseScheduledCardSelectionsResponse(scheduledCardSelectionsResponse) + if (cardSelectionResult.activeSelectionIndex == -1) { + return "No active card" + } + calypsoCard = cardSelectionResult.activeSmartCard as CalypsoCard + // check is the DF name is the expected one (Req. TL-SEL-AIDMATCH.1) + if ((cardSelectionResult.activeSelectionIndex == indexOfKeypleGenericCardSelection && + !calypsoCard.dfName.contentEquals(CardConstant.DFNAME_KEYPLE_GENERIC)) || + (cardSelectionResult.activeSelectionIndex == indexOfCdLightGtmlCardSelection && + !calypsoCard.dfName.contentEquals(CardConstant.DFNAME_CD_LIGHT_GTML)) || + (cardSelectionResult.activeSelectionIndex == indexOfCalypsoLightCardSelection && + !calypsoCard.dfName.contentEquals(CardConstant.DFNAME_CALYPSO_LIGHT)) || + (cardSelectionResult.activeSelectionIndex == indexOfNavigoIdfCardSelection && + !calypsoCard.dfName.contentEquals(CardConstant.DFNAME_NORMALIZED_IDF))) { + return "Unexpected DF name." + } + if (calypsoCard.applicationSubtype !in CardConstant.ALLOWED_FILE_STRUCTURES) { + return "File structure " + HexUtil.toHex(calypsoCard.applicationSubtype) + "h not supported" + } + Timber.i("Card DF Name = %s", HexUtil.toHex(calypsoCard.dfName)) + return null + } + + fun executeValidationProcedure(context: Context, locations: List): CardReaderResponse { + return CardRepository() + .executeValidationProcedure( + context = context, + validationAmount = 1, + cardReader = readerRepository.getCardReader()!!, + calypsoCard = calypsoCard, + cardSecuritySettings = getSecuritySettings()!!, + locations = locations, + now = DateTime.now()) + } + + private fun getSecuritySettings(): CardSecuritySetting? { + return calypsoExtensionService + .createCardSecuritySetting() + .setControlSamResource(readerRepository.getSamReader(), calypsoSam) + .assignDefaultKif( + WriteAccessLevel.PERSONALIZATION, CardConstant.DEFAULT_KIF_PERSONALIZATION) + .assignDefaultKif(WriteAccessLevel.LOAD, CardConstant.DEFAULT_KIF_LOAD) + .assignDefaultKif(WriteAccessLevel.DEBIT, CardConstant.DEFAULT_KIF_DEBIT) + .enableRatificationMechanism() + .enableMultipleSession() + } + + private fun selectSam(samReader: CardReader): Boolean { + // Get the Keyple main service + val smartCardService = SmartCardServiceProvider.getService() + + // Create a SAM selection manager. + val samSelectionManager: CardSelectionManager = smartCardService.createCardSelectionManager() + + // Create a SAM selection using the Calypso card extension. + samSelectionManager.prepareSelection( + calypsoExtensionService + .createSamSelection() + .filterByProductType(CalypsoSam.ProductType.SAM_C1)) + try { + // SAM communication: run the selection scenario. + val samSelectionResult = samSelectionManager.processCardSelectionScenario(samReader) + + // Get the Calypso SAM SmartCard resulting of the selection. + calypsoSam = samSelectionResult.activeSmartCard!! as CalypsoSam + return true + } catch (e: Exception) { + Timber.e(e) + Timber.e("An exception occurred while selecting the SAM. ${e.message}") + } + return false + } +} diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/MainService.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/MainService.kt deleted file mode 100644 index 654c7e2..0000000 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/MainService.kt +++ /dev/null @@ -1,100 +0,0 @@ -/* ************************************************************************************** - * Copyright (c) 2021 Calypso Networks Association https://calypsonet.org/ - * - * See the NOTICE file(s) distributed with this work for additional information - * regarding copyright ownership. - * - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.service - -import android.app.Activity -import javax.inject.Inject -import org.calypsonet.keyple.demo.validation.android.di.scope.AppScoped -import org.calypsonet.keyple.demo.validation.service.reader.ReaderService -import org.calypsonet.keyple.demo.validation.service.reader.ReaderType -import org.calypsonet.keyple.demo.validation.service.ticketing.TicketingService -import org.calypsonet.terminal.reader.CardReader -import org.calypsonet.terminal.reader.ObservableCardReader -import org.calypsonet.terminal.reader.spi.CardReaderObserverSpi -import org.eclipse.keyple.core.service.KeyplePluginException -import org.eclipse.keyple.core.service.SmartCardServiceProvider -import timber.log.Timber - -@AppScoped -class MainService @Inject constructor(private var readerService: ReaderService) { - - var ticketingService: TicketingService? = null - private set - var readersInitialized = false - - @Throws(KeyplePluginException::class, IllegalStateException::class, Exception::class) - fun init(observer: CardReaderObserverSpi?, activity: Activity, readerType: ReaderType) { - // Register plugin - try { - readerService.registerPlugin(activity, readerType) - } catch (e: Exception) { - Timber.e(e) - throw IllegalStateException(e.message) - } - // Init card reader - val cardReader: CardReader? - try { - cardReader = readerService.initCardReader() - } catch (e: Exception) { - Timber.e(e) - throw IllegalStateException(e.message) - } - // Init SAM reader - var samReaders: List? = null - try { - samReaders = readerService.initSamReaders() - } catch (e: Exception) { - Timber.e(e) - } - if (samReaders.isNullOrEmpty()) { - Timber.w("No SAM reader available") - } - // Register a card event observer and init the ticketing session - cardReader?.let { reader -> - (reader as ObservableCardReader).addObserver(observer) - ticketingService = TicketingService(readerService) - } - } - - fun startNfcDetection() { - // Provide the CardReader with the selection operation to be processed when a Card is inserted. - ticketingService?.prepareAndScheduleCardSelectionScenario() - (readerService.getCardReader() as ObservableCardReader).startCardDetection( - ObservableCardReader.DetectionMode.REPEATING) - } - - fun stopNfcDetection() { - try { - // notify reader that se detection has been switched off - (readerService.getCardReader() as ObservableCardReader).stopCardDetection() - } catch (e: KeyplePluginException) { - Timber.e(e, "NFC Plugin not found") - } catch (e: Exception) { - Timber.e(e) - } - } - - fun onDestroy(observer: CardReaderObserverSpi?) { - readersInitialized = false - readerService.clear() - if (observer != null && readerService.getCardReader() != null) { - (readerService.getCardReader() as ObservableCardReader).removeObserver(observer) - } - val smartCardService = SmartCardServiceProvider.getService() - smartCardService.plugins.forEach { smartCardService.unregisterPlugin(it.name) } - ticketingService = null - } - - fun displayResultSuccess(): Boolean = readerService.displayResultSuccess() - - fun displayResultFailed(): Boolean = readerService.displayResultFailed() -} diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/CalypsoInfo.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/CalypsoInfo.kt deleted file mode 100644 index 1e47d99..0000000 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/CalypsoInfo.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* ************************************************************************************** - * Copyright (c) 2021 Calypso Networks Association https://calypsonet.org/ - * - * See the NOTICE file(s) distributed with this work for additional information - * regarding copyright ownership. - * - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.service.ticketing - -/** - * Helper class to provide specific elements to handle Calypso cards. - * - * * AID application selection (default Calypso AID) - * * SAM_C1_ATR_REGEX regular expression matching the expected C1 SAM ATR - * * Files info (SFI, rec number, etc) - */ -object CalypsoInfo { - - const val AID_1TIC_ICA_1 = "315449432e49434131" - const val AID_1TIC_ICA_3 = "315449432E49434133" - const val AID_NORMALIZED_IDF = "A0000004040125090101" - const val AID_OTHER = "Other" - - const val RECORD_NUMBER_1: Int = 1 - const val RECORD_NUMBER_2: Int = 2 - const val RECORD_NUMBER_3: Int = 3 - const val RECORD_NUMBER_4: Int = 4 - - const val SFI_ENVIRONMENT_AND_HOLDER = 0x07.toByte() - const val SFI_EVENTS_LOG = 0x08.toByte() - const val SFI_CONTRACTS = 0x09.toByte() - const val SFI_COUNTER = 0x19.toByte() - - const val DEFAULT_KIF_PERSONALIZATION = 0x21.toByte() - const val DEFAULT_KIF_LOAD = 0x27.toByte() - const val DEFAULT_KIF_DEBIT = 0x30.toByte() -} diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/TicketingService.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/TicketingService.kt deleted file mode 100644 index 96e806a..0000000 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/TicketingService.kt +++ /dev/null @@ -1,211 +0,0 @@ -/* ************************************************************************************** - * Copyright (c) 2021 Calypso Networks Association https://calypsonet.org/ - * - * See the NOTICE file(s) distributed with this work for additional information - * regarding copyright ownership. - * - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.service.ticketing - -import android.content.Context -import java.util.EnumMap -import javax.inject.Inject -import org.calypsonet.keyple.demo.validation.android.di.scope.AppScoped -import org.calypsonet.keyple.demo.validation.service.reader.ReaderService -import org.calypsonet.keyple.demo.validation.service.ticketing.CalypsoInfo.AID_1TIC_ICA_1 -import org.calypsonet.keyple.demo.validation.service.ticketing.CalypsoInfo.AID_1TIC_ICA_3 -import org.calypsonet.keyple.demo.validation.service.ticketing.CalypsoInfo.AID_NORMALIZED_IDF -import org.calypsonet.keyple.demo.validation.service.ticketing.CalypsoInfo.AID_OTHER -import org.calypsonet.keyple.demo.validation.service.ticketing.CalypsoInfo.DEFAULT_KIF_DEBIT -import org.calypsonet.keyple.demo.validation.service.ticketing.CalypsoInfo.DEFAULT_KIF_LOAD -import org.calypsonet.keyple.demo.validation.service.ticketing.CalypsoInfo.DEFAULT_KIF_PERSONALIZATION -import org.calypsonet.keyple.demo.validation.service.ticketing.exception.ValidationException -import org.calypsonet.keyple.demo.validation.service.ticketing.model.CardReaderResponse -import org.calypsonet.keyple.demo.validation.service.ticketing.model.FileStructureEnum -import org.calypsonet.keyple.demo.validation.service.ticketing.model.Location -import org.calypsonet.keyple.demo.validation.service.ticketing.procedure.ValidationProcedure -import org.calypsonet.terminal.calypso.WriteAccessLevel -import org.calypsonet.terminal.calypso.card.CalypsoCard -import org.calypsonet.terminal.calypso.sam.CalypsoSam -import org.calypsonet.terminal.calypso.transaction.CardSecuritySetting -import org.calypsonet.terminal.reader.CardReader -import org.calypsonet.terminal.reader.ObservableCardReader -import org.calypsonet.terminal.reader.selection.CardSelectionManager -import org.calypsonet.terminal.reader.selection.CardSelectionResult -import org.calypsonet.terminal.reader.selection.ScheduledCardSelectionsResponse -import org.eclipse.keyple.card.calypso.CalypsoExtensionService -import org.eclipse.keyple.core.service.SmartCardServiceProvider -import org.joda.time.DateTime -import timber.log.Timber - -@AppScoped -class TicketingService @Inject constructor(private val readerService: ReaderService) { - - private val calypsoExtensionService: CalypsoExtensionService = - CalypsoExtensionService.getInstance() - - private lateinit var calypsoSam: CalypsoSam - private lateinit var calypsoCard: CalypsoCard - private lateinit var cardSelectionManager: CardSelectionManager - - var cardAid: String? = null - private set - - private var indexOfCardSelectionAid1TicIca1 = 0 - private var indexOfCardSelectionAid1TicIca3 = 0 - private var indexOfCardSelectionAidIdf = 0 - - private var fileStructure: FileStructureEnum? = null - - private val allowedFileStructures: EnumMap> = - EnumMap(FileStructureEnum::class.java) - - init { - allowedFileStructures[FileStructureEnum.FILE_STRUCTURE_02H] = listOf(AID_1TIC_ICA_1) - allowedFileStructures[FileStructureEnum.FILE_STRUCTURE_05H] = - listOf(AID_1TIC_ICA_1, AID_NORMALIZED_IDF) - allowedFileStructures[FileStructureEnum.FILE_STRUCTURE_32H] = listOf(AID_1TIC_ICA_3) - - // attempts to select a SAM if any, sets the isSecureSessionMode flag accordingly - val samReader = readerService.getSamReader() - if (samReader == null || !selectSam(samReader)) { - throw IllegalStateException("SAM reader or SAM not available") - } - } - - fun prepareAndScheduleCardSelectionScenario() { - - // Get the Keyple main service - val smartCardService = SmartCardServiceProvider.getService() - - // Check the Calypso card extension - smartCardService.checkCardExtension(calypsoExtensionService) - - // Get a new card selection manager - cardSelectionManager = smartCardService.createCardSelectionManager() - - // Prepare card selection case #1: 1 TIC ICA 1 - indexOfCardSelectionAid1TicIca1 = - cardSelectionManager.prepareSelection( - calypsoExtensionService - .createCardSelection() - .filterByDfName(AID_1TIC_ICA_1) - .filterByCardProtocol(readerService.getCardReaderProtocolLogicalName())) - - // Prepare card selection case #2: 1 TIC ICA 3 - indexOfCardSelectionAid1TicIca3 = - cardSelectionManager.prepareSelection( - calypsoExtensionService - .createCardSelection() - .filterByDfName(AID_1TIC_ICA_3) - .filterByCardProtocol(readerService.getCardReaderProtocolLogicalName())) - - // Prepare card selection case #3: Navigo - indexOfCardSelectionAidIdf = - cardSelectionManager.prepareSelection( - calypsoExtensionService - .createCardSelection() - .filterByDfName(AID_NORMALIZED_IDF) - .filterByCardProtocol(readerService.getCardReaderProtocolLogicalName())) - - // Schedule the execution of the prepared card selection scenario as soon as a card is presented - cardSelectionManager.scheduleCardSelectionScenario( - readerService.getCardReader() as ObservableCardReader, - ObservableCardReader.DetectionMode.REPEATING, - ObservableCardReader.NotificationMode.ALWAYS) - } - - fun parseScheduledCardSelectionsResponse( - selectionResponse: ScheduledCardSelectionsResponse? - ): CardSelectionResult { - Timber.i("selectionResponse = $selectionResponse") - val cardSelectionResult: CardSelectionResult = - cardSelectionManager.parseScheduledCardSelectionsResponse(selectionResponse) - if (cardSelectionResult.activeSelectionIndex != -1) { - when (cardSelectionResult.smartCards.keys.first()) { - indexOfCardSelectionAid1TicIca1 -> { - calypsoCard = cardSelectionResult.activeSmartCard as CalypsoCard - fileStructure = FileStructureEnum.findEnumByKey(calypsoCard.applicationSubtype.toInt()) - cardAid = AID_1TIC_ICA_1 - } - indexOfCardSelectionAid1TicIca3 -> { - calypsoCard = cardSelectionResult.activeSmartCard as CalypsoCard - cardAid = AID_1TIC_ICA_3 - fileStructure = FileStructureEnum.findEnumByKey(calypsoCard.applicationSubtype.toInt()) - } - indexOfCardSelectionAidIdf -> { - calypsoCard = cardSelectionResult.activeSmartCard as CalypsoCard - cardAid = AID_NORMALIZED_IDF - fileStructure = FileStructureEnum.findEnumByKey(calypsoCard.applicationSubtype.toInt()) - } - else -> cardAid = AID_OTHER - } - } - Timber.i("Card AID = $cardAid") - return cardSelectionResult - } - - fun checkStructure(): Boolean { - if (!allowedFileStructures.containsKey(fileStructure)) { - return false - } - if (!allowedFileStructures[fileStructure]!!.contains(cardAid)) { - return false - } - return true - } - - @Throws(ValidationException::class) - fun launchValidationProcedure(context: Context, locations: List): CardReaderResponse { - return ValidationProcedure() - .launch( - context = context, - validationAmount = 1, - cardReader = readerService.getCardReader()!!, - calypsoCard = calypsoCard, - cardSecuritySettings = getSecuritySettings()!!, - locations = locations, - now = DateTime.now()) - } - - private fun getSecuritySettings(): CardSecuritySetting? { - return calypsoExtensionService - .createCardSecuritySetting() - .setControlSamResource(readerService.getSamReader(), calypsoSam) - .assignDefaultKif(WriteAccessLevel.PERSONALIZATION, DEFAULT_KIF_PERSONALIZATION) - .assignDefaultKif(WriteAccessLevel.LOAD, DEFAULT_KIF_LOAD) - .assignDefaultKif(WriteAccessLevel.DEBIT, DEFAULT_KIF_DEBIT) - .enableRatificationMechanism() - .enableMultipleSession() - } - - private fun selectSam(samReader: CardReader): Boolean { - // Get the Keyple main service - val smartCardService = SmartCardServiceProvider.getService() - - // Create a SAM selection manager. - val samSelectionManager: CardSelectionManager = smartCardService.createCardSelectionManager() - - // Create a SAM selection using the Calypso card extension. - samSelectionManager.prepareSelection( - calypsoExtensionService - .createSamSelection() - .filterByProductType(CalypsoSam.ProductType.SAM_C1)) - try { - // SAM communication: run the selection scenario. - val samSelectionResult = samSelectionManager.processCardSelectionScenario(samReader) - - // Get the Calypso SAM SmartCard resulting of the selection. - calypsoSam = samSelectionResult.activeSmartCard!! as CalypsoSam - return true - } catch (e: Exception) { - Timber.e(e) - Timber.e("An exception occurred while selecting the SAM. ${e.message}") - } - return false - } -} diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/exception/ContractVersionNumberErrorException.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/exception/ContractVersionNumberErrorException.kt deleted file mode 100644 index b90543a..0000000 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/exception/ContractVersionNumberErrorException.kt +++ /dev/null @@ -1,19 +0,0 @@ -/* ************************************************************************************** - * Copyright (c) 2021 Calypso Networks Association https://calypsonet.org/ - * - * See the NOTICE file(s) distributed with this work for additional information - * regarding copyright ownership. - * - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.service.ticketing.exception - -class ContractVersionNumberErrorException() : ValidationException(MESSAGE) { - - companion object { - const val MESSAGE = "Contract Version Number error (!= CURRENT_VERSION)" - } -} diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/exception/EnvironmentException.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/exception/EnvironmentException.kt deleted file mode 100644 index fb7b5a4..0000000 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/exception/EnvironmentException.kt +++ /dev/null @@ -1,19 +0,0 @@ -/* ************************************************************************************** - * Copyright (c) 2021 Calypso Networks Association https://calypsonet.org/ - * - * See the NOTICE file(s) distributed with this work for additional information - * regarding copyright ownership. - * - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.service.ticketing.exception - -class EnvironmentException(key: EnvironmentExceptionKey) : ValidationException(key.value) - -enum class EnvironmentExceptionKey constructor(val key: Int, val value: String) { - WRONG_VERSION_NUMBER(0, "Environment Error: wrong version number"), - EXPIRED(1, "Environment Error: end date expired") -} diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/exception/EventException.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/exception/EventException.kt deleted file mode 100644 index 79cda1e..0000000 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/exception/EventException.kt +++ /dev/null @@ -1,19 +0,0 @@ -/* ************************************************************************************** - * Copyright (c) 2021 Calypso Networks Association https://calypsonet.org/ - * - * See the NOTICE file(s) distributed with this work for additional information - * regarding copyright ownership. - * - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.service.ticketing.exception - -class EventException(key: EventExceptionKey) : ValidationException(key.value) - -enum class EventExceptionKey constructor(val key: Int, val value: String) { - WRONG_VERSION_NUMBER(0, "Event - Wrong version number"), - CLEAN_CARD(1, "No valid title detected") -} diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/exception/NoContractAvailableException.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/exception/NoContractAvailableException.kt deleted file mode 100644 index 7dde91c..0000000 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/exception/NoContractAvailableException.kt +++ /dev/null @@ -1,19 +0,0 @@ -/* ************************************************************************************** - * Copyright (c) 2021 Calypso Networks Association https://calypsonet.org/ - * - * See the NOTICE file(s) distributed with this work for additional information - * regarding copyright ownership. - * - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.service.ticketing.exception - -class NoContractAvailableException : ValidationException(MESSAGE) { - - companion object { - const val MESSAGE = "No valid title detected" - } -} diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/exception/NoSamForValidationException.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/exception/NoSamForValidationException.kt deleted file mode 100644 index 8067118..0000000 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/exception/NoSamForValidationException.kt +++ /dev/null @@ -1,19 +0,0 @@ -/* ************************************************************************************** - * Copyright (c) 2021 Calypso Networks Association https://calypsonet.org/ - * - * See the NOTICE file(s) distributed with this work for additional information - * regarding copyright ownership. - * - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.service.ticketing.exception - -class NoSamForValidationException : ValidationException(MESSAGE) { - - companion object { - const val MESSAGE = "No contact secured element (SAM) is available" - } -} diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/model/Contract.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/model/Contract.kt deleted file mode 100644 index a686490..0000000 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/model/Contract.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* ************************************************************************************** - * Copyright (c) 2021 Calypso Networks Association https://calypsonet.org/ - * - * See the NOTICE file(s) distributed with this work for additional information - * regarding copyright ownership. - * - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.service.ticketing.model - -import android.os.Parcelable -import kotlinx.android.parcel.Parcelize -import org.joda.time.DateTime - -@Parcelize -data class Contract( - val name: String?, - val description: String? = null, - val valid: Boolean, - val validationDate: DateTime?, - val record: Int, - val expired: Boolean -) : Parcelable diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/model/FileStructureEnum.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/model/FileStructureEnum.kt deleted file mode 100644 index 820d3fc..0000000 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/service/ticketing/model/FileStructureEnum.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* ************************************************************************************** - * Copyright (c) 2021 Calypso Networks Association https://calypsonet.org/ - * - * See the NOTICE file(s) distributed with this work for additional information - * regarding copyright ownership. - * - * This program and the accompanying materials are made available under the terms of the - * Eclipse Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.service.ticketing.model - -enum class FileStructureEnum(val key: Int) { - FILE_STRUCTURE_02H(0x2), - FILE_STRUCTURE_05H(0x5), - FILE_STRUCTURE_32H(0x32); - - override fun toString(): String { - return "Structure ${Integer.toHexString(key)}h" - } - - companion object { - fun findEnumByKey(key: Int): FileStructureEnum? { - val values = values() - return values.find { it.key == key } - } - } -} diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/activity/BaseActivity.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/ui/BaseActivity.kt similarity index 73% rename from app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/activity/BaseActivity.kt rename to app/src/main/kotlin/org/calypsonet/keyple/demo/validation/ui/BaseActivity.kt index 5bbd6df..d240f1d 100644 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/activity/BaseActivity.kt +++ b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/ui/BaseActivity.kt @@ -9,18 +9,18 @@ * * SPDX-License-Identifier: EPL-2.0 ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.android.activity +package org.calypsonet.keyple.demo.validation.ui import android.widget.Toast import dagger.android.support.DaggerAppCompatActivity import javax.inject.Inject -import org.calypsonet.keyple.demo.validation.service.MainService -import org.calypsonet.keyple.demo.validation.service.file.LocationFileService +import org.calypsonet.keyple.demo.validation.data.LocationRepository +import org.calypsonet.keyple.demo.validation.domain.TicketingService abstract class BaseActivity : DaggerAppCompatActivity() { - @Inject lateinit var mainService: MainService - @Inject lateinit var locationFileService: LocationFileService + @Inject lateinit var ticketingService: TicketingService + @Inject lateinit var locationRepository: LocationRepository fun showToast(message: String) { runOnUiThread { Toast.makeText(applicationContext, message, Toast.LENGTH_SHORT).show() } diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/activity/CardSummaryActivity.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/ui/CardSummaryActivity.kt similarity index 90% rename from app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/activity/CardSummaryActivity.kt rename to app/src/main/kotlin/org/calypsonet/keyple/demo/validation/ui/CardSummaryActivity.kt index 49a3963..ad932ac 100644 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/activity/CardSummaryActivity.kt +++ b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/ui/CardSummaryActivity.kt @@ -9,7 +9,7 @@ * * SPDX-License-Identifier: EPL-2.0 ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.android.activity +package org.calypsonet.keyple.demo.validation.ui import android.os.Bundle import android.view.View @@ -23,8 +23,8 @@ import kotlinx.android.synthetic.main.activity_card_summary.mainView import kotlinx.android.synthetic.main.activity_card_summary.mediumText import kotlinx.android.synthetic.main.activity_card_summary.smallDesc import org.calypsonet.keyple.demo.validation.R -import org.calypsonet.keyple.demo.validation.service.ticketing.model.CardReaderResponse -import org.calypsonet.keyple.demo.validation.service.ticketing.model.Status +import org.calypsonet.keyple.demo.validation.data.model.CardReaderResponse +import org.calypsonet.keyple.demo.validation.data.model.Status import timber.log.Timber class CardSummaryActivity : BaseActivity() { @@ -39,7 +39,7 @@ class CardSummaryActivity : BaseActivity() { bundle.getParcelable(CardReaderResponse::class.simpleName) when (cardReaderResponse?.status) { Status.SUCCESS -> { - mainService.displayResultSuccess() + ticketingService.displayResultSuccess() animation.setAnimation("tick_white.json") mainView.setBackgroundColor(ContextCompat.getColor(this, R.color.green)) bigText.setText(R.string.valid_main_desc) @@ -67,7 +67,7 @@ class CardSummaryActivity : BaseActivity() { mediumText.setText(R.string.valid_last_desc) } Status.INVALID_CARD -> { - mainService.displayResultFailed() + ticketingService.displayResultFailed() animation.setAnimation("error_white.json") mainView.setBackgroundColor(ContextCompat.getColor(this, R.color.orange)) bigText.setText(R.string.card_invalid_main_desc) @@ -76,7 +76,7 @@ class CardSummaryActivity : BaseActivity() { smallDesc.visibility = View.INVISIBLE } Status.EMPTY_CARD -> { - mainService.displayResultFailed() + ticketingService.displayResultFailed() mainView.setBackgroundColor(ContextCompat.getColor(this, R.color.red)) animation.setAnimation("error_white.json") bigText.text = cardReaderResponse.errorMessage @@ -85,7 +85,7 @@ class CardSummaryActivity : BaseActivity() { smallDesc.visibility = View.INVISIBLE } else -> { - mainService.displayResultFailed() + ticketingService.displayResultFailed() mainView.setBackgroundColor(ContextCompat.getColor(this, R.color.red)) animation.setAnimation("error_white.json") bigText.setText(R.string.error_main_desc) @@ -107,8 +107,8 @@ class CardSummaryActivity : BaseActivity() { override fun onResume() { super.onResume() - if (mainService.readersInitialized) { - mainService.stopNfcDetection() + if (ticketingService.readersInitialized) { + ticketingService.stopNfcDetection() Timber.d("stopNfcDetection") } } diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/activity/HomeActivity.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/ui/HomeActivity.kt similarity index 94% rename from app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/activity/HomeActivity.kt rename to app/src/main/kotlin/org/calypsonet/keyple/demo/validation/ui/HomeActivity.kt index c1ef11a..ed3feb0 100644 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/activity/HomeActivity.kt +++ b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/ui/HomeActivity.kt @@ -9,7 +9,7 @@ * * SPDX-License-Identifier: EPL-2.0 ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.android.activity +package org.calypsonet.keyple.demo.validation.ui import android.content.Intent import android.os.Bundle diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/activity/SplashScreenActivity.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/ui/MainActivity.kt similarity index 85% rename from app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/activity/SplashScreenActivity.kt rename to app/src/main/kotlin/org/calypsonet/keyple/demo/validation/ui/MainActivity.kt index c9f78a3..6f94db9 100644 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/activity/SplashScreenActivity.kt +++ b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/ui/MainActivity.kt @@ -9,20 +9,21 @@ * * SPDX-License-Identifier: EPL-2.0 ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.android.activity +package org.calypsonet.keyple.demo.validation.ui import android.content.Intent import android.os.Bundle import java.util.Timer import java.util.TimerTask import org.calypsonet.keyple.demo.validation.R +import org.calypsonet.keyple.demo.validation.ui.deviceselection.DeviceSelectionActivity -class SplashScreenActivity : BaseActivity() { +class MainActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { // Make sure this is before calling super.onCreate super.onCreate(savedInstanceState) - setContentView(R.layout.activity_splashscreen) + setContentView(R.layout.activity_main) Timer() .schedule( object : TimerTask() { diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/activity/ReaderActivity.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/ui/ReaderActivity.kt similarity index 74% rename from app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/activity/ReaderActivity.kt rename to app/src/main/kotlin/org/calypsonet/keyple/demo/validation/ui/ReaderActivity.kt index c070c42..a587739 100644 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/activity/ReaderActivity.kt +++ b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/ui/ReaderActivity.kt @@ -9,7 +9,7 @@ * * SPDX-License-Identifier: EPL-2.0 ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.android.activity +package org.calypsonet.keyple.demo.validation.ui import android.app.ProgressDialog import android.content.Intent @@ -28,13 +28,11 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import org.calypsonet.keyple.demo.validation.ApplicationSettings import org.calypsonet.keyple.demo.validation.R -import org.calypsonet.keyple.demo.validation.android.di.scope.ActivityScoped -import org.calypsonet.keyple.demo.validation.service.ticketing.CalypsoInfo -import org.calypsonet.keyple.demo.validation.service.ticketing.TicketingService -import org.calypsonet.keyple.demo.validation.service.ticketing.model.CardReaderResponse -import org.calypsonet.keyple.demo.validation.service.ticketing.model.Status +import org.calypsonet.keyple.demo.validation.data.model.AppSettings +import org.calypsonet.keyple.demo.validation.data.model.CardReaderResponse +import org.calypsonet.keyple.demo.validation.data.model.Status +import org.calypsonet.keyple.demo.validation.di.scope.ActivityScoped import org.calypsonet.terminal.reader.CardReaderEvent import org.calypsonet.terminal.reader.spi.CardReaderObserverSpi import timber.log.Timber @@ -44,7 +42,6 @@ class ReaderActivity : BaseActivity() { @Suppress("DEPRECATION") private lateinit var progress: ProgressDialog private var cardReaderObserver: CardReaderObserver? = null - private lateinit var ticketingService: TicketingService var currentAppState = AppState.WAIT_SYSTEM_READY private var timer = Timer() @@ -76,18 +73,15 @@ class ReaderActivity : BaseActivity() { override fun onResume() { super.onResume() animation.playAnimation() - if (!mainService.readersInitialized) { + if (!ticketingService.readersInitialized) { GlobalScope.launch { withContext(Dispatchers.Main) { showProgress() } withContext(Dispatchers.IO) { try { cardReaderObserver = CardReaderObserver() - mainService.init( - cardReaderObserver, this@ReaderActivity, ApplicationSettings.readerType) - ticketingService = mainService.ticketingService!! - mainService.readersInitialized = true + ticketingService.init(cardReaderObserver, this@ReaderActivity, AppSettings.readerType) handleAppEvents(AppState.WAIT_CARD, null) - mainService.startNfcDetection() + ticketingService.startNfcDetection() } catch (e: Exception) { Timber.e(e) withContext(Dispatchers.Main) { @@ -96,14 +90,14 @@ class ReaderActivity : BaseActivity() { } } } - if (mainService.readersInitialized) { + if (ticketingService.readersInitialized) { withContext(Dispatchers.Main) { dismissProgress() } } } } else { - mainService.startNfcDetection() + ticketingService.startNfcDetection() } - if (ApplicationSettings.batteryPowered) { + if (AppSettings.batteryPowered) { timer = Timer() // Need to reinit timer after cancel timer.schedule( object : TimerTask() { @@ -119,15 +113,14 @@ class ReaderActivity : BaseActivity() { super.onPause() animation.cancelAnimation() timer.cancel() - if (mainService.readersInitialized) { - mainService.stopNfcDetection() + if (ticketingService.readersInitialized) { + ticketingService.stopNfcDetection() Timber.d("stopNfcDetection") } } override fun onDestroy() { - mainService.readersInitialized = false - mainService.onDestroy(cardReaderObserver) + ticketingService.onDestroy(cardReaderObserver) cardReaderObserver = null super.onDestroy() } @@ -147,38 +140,13 @@ class ReaderActivity : BaseActivity() { if (newAppState == AppState.WAIT_SYSTEM_READY) { return } - Timber.i("Process default selection...") - val seSelectionResult = - ticketingService.parseScheduledCardSelectionsResponse( - readerEvent.scheduledCardSelectionsResponse) - if (seSelectionResult.activeSelectionIndex == -1) { + Timber.i("Process the selection result...") + val error = + ticketingService.analyseSelectionResult(readerEvent.scheduledCardSelectionsResponse) + if (error != null) { Timber.e("Card Not selected") val error = getString(R.string.card_invalid_aid) - mainService.displayResultFailed() - changeDisplay( - CardReaderResponse( - status = Status.INVALID_CARD, - contract = null, - validation = null, - errorMessage = error)) - return - } - Timber.i("Card AID = ${ticketingService.cardAid}") - if (CalypsoInfo.AID_1TIC_ICA_1 != ticketingService.cardAid && - CalypsoInfo.AID_1TIC_ICA_3 != ticketingService.cardAid && - CalypsoInfo.AID_NORMALIZED_IDF != ticketingService.cardAid) { - val error = getString(R.string.card_invalid_aid) - mainService.displayResultFailed() - changeDisplay( - CardReaderResponse( - status = Status.INVALID_CARD, - contract = null, - validation = null, - errorMessage = error)) - return - } - if (!ticketingService.checkStructure()) { - val error = getString(R.string.card_invalid_structure) + ticketingService.displayResultFailed() changeDisplay( CardReaderResponse( status = Status.INVALID_CARD, @@ -210,8 +178,8 @@ class ReaderActivity : BaseActivity() { withContext(Dispatchers.Main) { progress.show() } val validationResult = withContext(Dispatchers.IO) { - ticketingService.launchValidationProcedure( - this@ReaderActivity, locationFileService.getLocations()) + ticketingService.executeValidationProcedure( + this@ReaderActivity, locationRepository.getLocations()) } withContext(Dispatchers.Main) { progress.dismiss() diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/activity/SettingsActivity.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/ui/SettingsActivity.kt similarity index 81% rename from app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/activity/SettingsActivity.kt rename to app/src/main/kotlin/org/calypsonet/keyple/demo/validation/ui/SettingsActivity.kt index f142dd4..2f14d44 100644 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/activity/SettingsActivity.kt +++ b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/ui/SettingsActivity.kt @@ -9,7 +9,7 @@ * * SPDX-License-Identifier: EPL-2.0 ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.android.activity +package org.calypsonet.keyple.demo.validation.ui import android.content.Intent import android.os.Bundle @@ -20,10 +20,10 @@ import kotlinx.android.synthetic.main.activity_settings.batteryPoweredBox import kotlinx.android.synthetic.main.activity_settings.spinnerLocationList import kotlinx.android.synthetic.main.activity_settings.startBtn import kotlinx.android.synthetic.main.activity_settings.timeBtn -import org.calypsonet.keyple.demo.validation.ApplicationSettings import org.calypsonet.keyple.demo.validation.BuildConfig import org.calypsonet.keyple.demo.validation.R -import org.calypsonet.keyple.demo.validation.service.ticketing.model.Location +import org.calypsonet.keyple.demo.validation.data.model.AppSettings +import org.calypsonet.keyple.demo.validation.data.model.Location class SettingsActivity : BaseActivity() { @@ -32,15 +32,15 @@ class SettingsActivity : BaseActivity() { setContentView(R.layout.activity_settings) setSupportActionBar(findViewById(R.id.toolbar)) // Init location spinner - val locations = locationFileService.getLocations() + val locations = locationRepository.getLocations() val locationsAdapter = ArrayAdapter(this, R.layout.spinner_item_location, R.id.spinner_item_text, locations) spinnerLocationList.adapter = locationsAdapter timeBtn.setOnClickListener { startActivityForResult(Intent(Settings.ACTION_DATE_SETTINGS), 0) } startBtn.setOnClickListener { - ApplicationSettings.location = spinnerLocationList.selectedItem as Location - ApplicationSettings.batteryPowered = batteryPoweredBox.isChecked - if (ApplicationSettings.batteryPowered) { + AppSettings.location = spinnerLocationList.selectedItem as Location + AppSettings.batteryPowered = batteryPoweredBox.isChecked + if (AppSettings.batteryPowered) { startActivity(Intent(this, HomeActivity::class.java)) finish() } else { diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/activity/DeviceSelectionActivity.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/ui/deviceselection/DeviceSelectionActivity.kt similarity index 86% rename from app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/activity/DeviceSelectionActivity.kt rename to app/src/main/kotlin/org/calypsonet/keyple/demo/validation/ui/deviceselection/DeviceSelectionActivity.kt index 6dbfe66..f887d35 100644 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/activity/DeviceSelectionActivity.kt +++ b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/ui/deviceselection/DeviceSelectionActivity.kt @@ -9,7 +9,7 @@ * * SPDX-License-Identifier: EPL-2.0 ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.android.activity +package org.calypsonet.keyple.demo.validation.ui.deviceselection import android.Manifest import android.annotation.SuppressLint @@ -20,12 +20,12 @@ import android.os.Bundle import kotlinx.android.synthetic.main.activity_device_selection.* import kotlinx.android.synthetic.main.activity_device_selection.app_version import kotlinx.android.synthetic.main.activity_settings.* -import org.calypsonet.keyple.demo.validation.ApplicationSettings import org.calypsonet.keyple.demo.validation.BuildConfig import org.calypsonet.keyple.demo.validation.R -import org.calypsonet.keyple.demo.validation.android.dialog.PermissionDeniedDialog -import org.calypsonet.keyple.demo.validation.android.util.PermissionHelper -import org.calypsonet.keyple.demo.validation.service.reader.ReaderType +import org.calypsonet.keyple.demo.validation.data.model.AppSettings +import org.calypsonet.keyple.demo.validation.data.model.ReaderType +import org.calypsonet.keyple.demo.validation.ui.BaseActivity +import org.calypsonet.keyple.demo.validation.ui.SettingsActivity import org.calypsonet.keyple.plugin.bluebird.BluebirdPlugin import org.calypsonet.keyple.plugin.flowbird.FlowbirdPlugin @@ -44,7 +44,7 @@ class DeviceSelectionActivity : BaseActivity() { bluebirdBtn.setBackgroundColor(Color.GRAY) } else { bluebirdBtn.setOnClickListener { - ApplicationSettings.readerType = ReaderType.BLUEBIRD + AppSettings.readerType = ReaderType.BLUEBIRD val permissions: MutableList = mutableListOf( Manifest.permission.READ_EXTERNAL_STORAGE, @@ -59,14 +59,14 @@ class DeviceSelectionActivity : BaseActivity() { // Coppernic coppernicBtn.setOnClickListener { - ApplicationSettings.readerType = ReaderType.COPPERNIC + AppSettings.readerType = ReaderType.COPPERNIC startActivity(Intent(this, SettingsActivity::class.java)) finish() } // Famoco famocoBtn.setOnClickListener { - ApplicationSettings.readerType = ReaderType.FAMOCO + AppSettings.readerType = ReaderType.FAMOCO startActivity(Intent(this, SettingsActivity::class.java)) finish() } @@ -76,7 +76,7 @@ class DeviceSelectionActivity : BaseActivity() { flowbirdBtn.setBackgroundColor(Color.GRAY) } else { flowbirdBtn.setOnClickListener { - ApplicationSettings.readerType = ReaderType.FLOWBIRD + AppSettings.readerType = ReaderType.FLOWBIRD startActivity(Intent(this, SettingsActivity::class.java)) finish() } diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/dialog/PermissionDeniedDialog.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/ui/deviceselection/PermissionDeniedDialog.kt similarity index 95% rename from app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/dialog/PermissionDeniedDialog.kt rename to app/src/main/kotlin/org/calypsonet/keyple/demo/validation/ui/deviceselection/PermissionDeniedDialog.kt index 51cc4d7..9c7de2e 100644 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/dialog/PermissionDeniedDialog.kt +++ b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/ui/deviceselection/PermissionDeniedDialog.kt @@ -9,7 +9,7 @@ * * SPDX-License-Identifier: EPL-2.0 ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.android.dialog +package org.calypsonet.keyple.demo.validation.ui.deviceselection import android.app.Dialog import android.os.Bundle diff --git a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/util/PermissionHelper.kt b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/ui/deviceselection/PermissionHelper.kt similarity index 95% rename from app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/util/PermissionHelper.kt rename to app/src/main/kotlin/org/calypsonet/keyple/demo/validation/ui/deviceselection/PermissionHelper.kt index 56353ba..f2b380a 100644 --- a/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/android/util/PermissionHelper.kt +++ b/app/src/main/kotlin/org/calypsonet/keyple/demo/validation/ui/deviceselection/PermissionHelper.kt @@ -9,7 +9,7 @@ * * SPDX-License-Identifier: EPL-2.0 ************************************************************************************** */ -package org.calypsonet.keyple.demo.validation.android.util +package org.calypsonet.keyple.demo.validation.ui.deviceselection import android.app.Activity import android.content.pm.PackageManager diff --git a/app/src/main/res/layout/activity_device_selection.xml b/app/src/main/res/layout/activity_device_selection.xml index 504e7f2..f54b523 100644 --- a/app/src/main/res/layout/activity_device_selection.xml +++ b/app/src/main/res/layout/activity_device_selection.xml @@ -7,7 +7,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/ic_img_bg_valideur" - tools:context="org.calypsonet.keyple.demo.validation.android.activity.DeviceSelectionActivity"> + tools:context="org.calypsonet.keyple.demo.validation.ui.deviceselection.DeviceSelectionActivity"> diff --git a/app/src/main/res/layout/activity_home.xml b/app/src/main/res/layout/activity_home.xml index bf382c9..478f4f7 100644 --- a/app/src/main/res/layout/activity_home.xml +++ b/app/src/main/res/layout/activity_home.xml @@ -17,7 +17,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context="org.calypsonet.keyple.demo.validation.android.activity.HomeActivity" + tools:context="org.calypsonet.keyple.demo.validation.ui.HomeActivity" android:background="@drawable/ic_img_bg_valideur"> + tools:context="org.calypsonet.keyple.demo.validation.ui.SettingsActivity"> diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml index 01b5250..5557809 100644 --- a/app/src/main/res/menu/menu_main.xml +++ b/app/src/main/res/menu/menu_main.xml @@ -1,7 +1,7 @@ + tools:context="org.calypsonet.keyple.demo.validation.ui.demo.ui.activities.MainActivity"> + Keyple Demo - Validation + Settings Card reading