- Afegeix aquesta dependència en el teu projecte:
implementation 'com.github.AjuntamentdeBarcelona.modul_comu_osam:common-android:3.1.0'- Afegir aquest codi al teu build.gradle
allprojects {
repositories {
maven { url "https://jitpack.io" }
}
}- Per utilitzar el mòdul de control de versions, cal afegir l'arxiu Podfile la ubicació del repositori:
pod 'OSAMCommon', :git => 'https://github.com/AjuntamentdeBarcelona/modul_comu_osam.git', :tag => '3.1.0'
- Actualitzar mitjançant el comandament
pod updateles dependències.
Aquest mòdul uneix el control de versions i el control de valoracions.
En el control de versions es mostrarà un avís quan el servei avisi que hi ha una nova versió de l'app. Aquesta alerta la podem mostrar amb un missatge amb botons de confirmació d'accions.
Tindrem tres diferents tipus d'alerta:
- Informativa: Alerta amb un missatge i / o un títol informatiu, amb un botó d ' "ok" per confirmar que s'ha llegit. No obre cap enllaç.
- Restrictiva: Alerta amb un missatge i / o un títol, amb botó d ' "ok" que un cop fet clic redirigirà l'usuari a una url.
- Permisiva: Alerta amb un missatge i / o un títol, amb botons de "ok" i "cancel". Si fem clic al botó de cancel·lar l'alerta desapareixerà, i si ho fem al de confirmar s'obrirà una url.
Pel que respecta al control de valoracions, la seva funcionalitat és mostrar periòdicament una popup que convida a l’usuari a deixar un comentari sobre l'app al market place corresponent (Google Play o AppStore).
A Android s'utilitza la llibreria de Google Play Core
A iOS s'utilitza la llibreria nativa:
SKStoreReviewController.requestReview()Des de la OSAM es proporcionen mòduls per realitzar un conjunt de tasques comunes a totes les apps publicades per l'Ajuntament de Barcelona.
El mòdul comú (iOS / Android) està disponible com a repositori a: https://github.com/AjuntamentdeBarcelona/modul_comu_osam
Per tal de poder utilitzar el mòdul, és necessari especificar en temps de inicialització els següents paràmetres:
- backendEndpoint: url del backend del mòdul comú
- crashlyticsWrapper: implementació del wrapper de crashlytics que hem de implementar (o de qualsevol altre llibreria)
- analyticsWrapper: implementació del wrapper de analytics que hem de implementar (o de qualsevol altre llibreria)
- performanceWrapper: implementació del wrapper de performance que hem de implementar (o de qualsevol altre llibreria)
- messagingWrapper: implementació del wrapper de messaging que hem de implementar (o de qualsevol altre llibreria)
A continuació, es detalla per cada plataforma, com es realitza aquesta inicialització. Per a més detalls de com integrar el mòdul comú amb la CI de la OSAM, consultar el manual de la CI.
Inicialitzarem el mòdul comú de la següent manera:
private val osamCommons by lazy {
OSAMCommons(
activity = this,
context = this,
backendEndpoint = getString(R.string.common_module_endpoint),
crashlyticsWrapper = CrashlyticsWrapperAndroid(),
performanceWrapper = PerformanceWrapperAndroid(),
analyticsWrapper = AnalyticsWrapperAndroid(this),
platformUtil = PlatformUtilAndroid(this),
messagingWrapper = MessagingWrapperAndroid()
)
}Com que OSAMCommons pot mostrar diàlegs (per al control de versions o la valoració de l'app), necessita una referència a l'Activity actual. Si la vostra aplicació té múltiples activitats o es recreen (per exemple, canvis de configuració), heu d'actualitzar la referència de l'activitat a OSAMCommons per assegurar-vos que els diàlegs es mostrin correctament.
Utilitzeu el mètode setActivity en el onResume de la vostra activitat:
override fun onResume() {
super.onResume()
osamCommons.setActivity(this)
}La URL del backend s'ha de declarar en el config_keys.xml amb el nom "common_module_endpoint". El fixer quedaria de la següent manera:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="common_module_endpoint" translatable="false">https://dev-osam-modul-comu.dtibcn.cat/</string>
</resources>Per accedir a aquesta url durant el desenvolupament es requereix indicar usuari i contrasenya, informació que es pot obtenir consultant amb el Cap de Projecte de l'OSAM asignat al projecte.
A continuació s'indiquen les implementacions del wrapper de crashlytics, analytics, performance i platform util:
class CrashlyticsWrapperAndroid : CrashlyticsWrapper {
override fun recordException(exception: Exception) {
FirebaseCrashlytics.getInstance().recordException(exception)
}
}
class AnalyticsWrapperAndroid(context: Context) : AnalyticsWrapper {
private val analytics = FirebaseAnalytics.getInstance(context)
override fun logEvent(name: String, parameters: Map<String, String>) {
analytics.logEvent(name, parameters.toBundle())
}
private fun Map<String, String>.toBundle(): Bundle =
Bundle().apply {
this@toBundle.forEach {
putString(it.key, it.value)
}
}
}
class PerformanceWrapperAndroid : PerformanceWrapper {
override fun createMetric(url: String, httpMethod: String): PerformanceMetric {
return PerformanceMetricAndroid(FirebasePerformance.getInstance().newHttpMetric(url, httpMethod))
}
}
class PerformanceMetricAndroid(val metric: HttpMetric?) : PerformanceMetric {
override fun start() {
metric?.start()
}
override fun setRequestPayloadSize(bytes: Long) {
metric?.setRequestPayloadSize(bytes)
}
override fun markRequestComplete() {
metric?.markRequestComplete()
}
override fun markResponseStart() {
metric?.markResponseStart()
}
override fun setResponseContentType(contentType: String) {
metric?.setResponseContentType(contentType)
}
override fun setHttpResponseCode(responseCode: Int) {
metric?.setHttpResponseCode(responseCode)
}
override fun setResponsePayloadSize(bytes: Long) {
metric?.setResponsePayloadSize(bytes)
}
override fun putAttribute(attribute: String, value: String) {
metric?.putAttribute(attribute, value)
}
override fun stop() {
metric?.stop()
}
}
class PlatformUtilAndroid(private val context: Context) : PlatformUtil {
override fun encodeUrl(url: String): String? {
return url
}
override fun openUrl(url: String): Boolean {
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse(url)
ContextCompat.startActivity(context, intent, null)
return true
}
override fun getDeviceModelIdentifier(): String {
return ""
}
}
class MessagingWrapperAndroid : MessagingWrapper {
private val firebaseMessaging = FirebaseMessaging.getInstance()
override suspend fun subscribeToTopic(topic: String) {
firebaseMessaging.subscribeToTopic(topic)
}
override suspend fun unsubscribeFromTopic(topic: String) {
firebaseMessaging.unsubscribeFromTopic(topic)
}
override suspend fun getToken(): String {
return firebaseMessaging.token.await()
}
}Inicialitzarem el mòdul comú de la següent manera:
lazy var osamCommons = OSAMCommons(
vc: self,
backendEndpoint: <url_endpoint_modul_comu>,
crashlyticsWrapper: CrashlyticsWrapperIOS(),
performanceWrapper: PerformanceWrapperIOS(),
analyticsWrapper: AnalyticsWrapperIOS(),
platformUtil: PlatformUtilIOS(),
messagingWrapper: MessagingWrapperIOS()
)La URL del backend s'ha de declarar en el config_keys.plist amb el nom "common_module_endpoint". El fixer quedaria de la següent manera:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>common_module_endpoint</key>
<string>https://dev-osam-modul-comu.dtibcn.cat/</string>
</dict>
</plist>A continuació s'indiquen les implementacions del wrapper de Crashlytics, Performance i Analytics:
class CrashlyticsWrapperIOS: CrashlyticsWrapper {
func recordException(className: String, stackTrace: String) {
let exception = ExceptionModel(name: className, reason: stackTrace)
Crashlytics.crashlytics().record(exceptionModel: exception)
}
}
class PerformanceWrapperIOS: PerformanceWrapper {
func createMetric(url: String, httpMethod: String) -> PerformanceMetric {
let httpMethodType: HTTPMethod
switch httpMethod.lowercased() {
case "put":
httpMethodType = HTTPMethod.put
case "post":
httpMethodType = HTTPMethod.post
case "delete":
httpMethodType = HTTPMethod.delete
case "head":
httpMethodType = HTTPMethod.head
case "patch":
httpMethodType = HTTPMethod.patch
case "options":
httpMethodType = HTTPMethod.options
case "trace":
httpMethodType = HTTPMethod.trace
case "connect":
httpMethodType = HTTPMethod.connect
default:
httpMethodType = HTTPMethod.get
}
return PerformanceMetricIOS(
metric: HTTPMetric.init(url: URL(string: url)!, httpMethod: httpMethodType)!
)
}
}
class PerformanceMetricIOS: PerformanceMetric {
let metric: HTTPMetric?
init(metric: HTTPMetric?) {
self.metric = metric
}
func start() {
metric?.start()
}
func setRequestPayloadSize(bytes: Int64) {
metric?.requestPayloadSize = Int(bytes)
}
func markRequestComplete() {
// not used for iOs
}
func markResponseStart() {
// not used for iOs
}
func setResponseContentType(contentType: String) {
metric?.responseContentType = contentType
}
func setHttpResponseCode(responseCode: Int32) {
metric?.responseCode = Int(responseCode)
}
func setResponsePayloadSize(bytes: Int64) {
metric?.responsePayloadSize = Int(bytes)
}
func putAttribute(attribute: String, value: String) {
metric?.setValue(value, forAttribute: attribute)
}
func stop() {
metric?.stop()
}
}
class AnalyticsWrapperIOS: AnalyticsWrapper {
func logEvent(name: String, parameters: [String : String]) {
Analytics.logEvent(name, parameters: parameters)
}
}
class PlatformUtilIOS : PlatformUtil {
func encodeUrl(url: String) -> String? {
let urlString: String? = url.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed)
return urlString
}
func openUrl(url: String) -> Bool {
if let urlObj = URL(string: url) {
UIApplication.shared.open(urlObj)
return true
} else {
return false
}
}
func getDeviceModelIdentifier() -> String {
var modelName: String {
var systemInfo = utsname()
uname(&systemInfo)
let machineMirror = Mirror(reflecting: systemInfo.machine)
let identifier = machineMirror.children.reduce("") { identifier, element in
guard let value = element.value as? Int8, value != 0 else { return identifier }
return identifier + String(UnicodeScalar(UInt8(value)))
}
switch identifier {
/*case "iPhone11,6": return "iPhone XR"
case "iPhone11,4", "iPhone11,2": return "iPhone XS Max"
case "iPhone11,8": return "iPhone XS"
case "iPhone12,1": return "iPhone 11"
case "iPhone12,3": return "iPhone 11 Pro"
case "iPhone12,5": return "iPhone 11 Pro Max"*/
//Add more cases for other devices as needed
default: return identifier
}
}
return modelName
}
}
class MessagingWrapperIOS: MessagingWrapper {
func subscribeToTopic(topic: String) async throws {
// The Firebase iOS SDK provides modern async/await functions.
try await Messaging.messaging().subscribe(toTopic: topic)
}
func unsubscribeFromTopic(topic: String) async throws {
try await Messaging.messaging().unsubscribe(fromTopic: topic)
}
func getToken() async throws -> String {
// The async version of token() can be awaited and will throw on failure.
return try await Messaging.messaging().token()
}
}Per crear el missatge d'alerta, únicament hem de cridar a la funció que descarregarà el json amb les variables ja definides i mostrarà l'alerta segons els valors rebuts:
osamCommons.versionControl(
language = Language.CA,
isDarkMode = false, // Opcional, per defecte false
applyComModStyles = true // Opcional, per defecte true
) {
// Do something...
}A la inicialització se li ha de passar el context de l'app. Per cridar al control de versions només
cal executar la funció versionControl() i facilitar-li l'idioma en què es vol mostrar el popup.
Per facilitar l'idioma, la llibreria inclou la classe Language que conté Català (CA),
Castellà (ES) i Anglès (EN), que són els idiomes suportats. Com a extra, se li ha afegit un
callback perquè la pantalla principal pugui reaccionar en cas que hi hagi hagut un error o si, a
part de la funcionalitat que ofereix la llibreria, es vol afegir alguna funcionalitat més pròpia de
l'aplicació. Lo que revem en el callback es l'objecte VersionControlResponse. Aquest objecte pot
arrivar amb quatre valors possibles:
- ACCEPTED: si l'usuari ha escollit el botó d'acceptar/ok
- DISMISSED: si l'usuari ha tret el popup
- CANCELLED: si l'usuari ha escollit el botó de cancel·lar
- ERROR: si hi ha hagut cap error al procés d'obtenir la informació necessaria o al mostrar el popup
Per exemple: Si l'usuari cancel·la el popup, al callback rebriem
l'objecte VersionControlResponse.CANCELLED. Si en el cas de que volgués fer alguna acció diferent
si l'usuari cancel·la el popup, es podria definir en aquest punt la casuistica.
Per crear el missatge d'alerta, únicament hem de cridar a la funció que descarregarà el json amb les variables ja definides i mostrarà l'alerta segons els valors rebuts:
osamCommons.versionControl(
language: Language.es,
isDarkMode: false, // Opcional, per defecte false
applyComModStyles: true, // Opcional, per defecte true
f: {_ in }
)A la inicialització se li ha de passar el UIViewController de la pantalla que crida al mòdul. Per
cridar al control de versions només cal executar la funció versionControl(), facilitar-li l'idioma
en què es vol mostrar el popup i la funció que volem que executi el callback que retorna el mòdul.
Per facilitar l'idioma, la llibreria inclou la classe Language que conté Català (CA),
Castellà (ES) i Anglès (EN), que són els idiomes suportats. Pel que respecta al callback,
s'ha afegit perquè la pantalla pugui reaccionar en cas que hi hagi hagut un error o si, a part de la
funcionalitat que ofereix la llibreria, es vol afegir alguna funcionalitat més pròpia de
l'aplicació. Lo que revem en el callback es l'objecte VersionControlResponse. Aquest objecte pot
arrivar amb quatre valors possibles:
- ACCEPTED: si l'usuari ha escollit el botó d'acceptar/ok
- DISMISSED: si l'usuari ha tret el popup o no compleix les condicions per mostrar-li a l'usuari
- CANCELLED: si l'usuari ha escollit el botó de cancel·lar
- ERROR: si hi ha hagut cap error al procés d'obtenir la informació necessaria o al mostrar el popup
Per exemple: Si l'usuari cancel·la el popup, al callback rebriem
l'objecte VersionControlResponse.CANCELLED. Si en el cas de que volgués fer alguna acció diferent
si l'usuari cancel·la el popup, es podria definir en aquest punt la casuistica.
Per crear el missatge d'alerta, únicament hem de cridar a la funció que descarregarà el json amb les variables ja definides i mostrarà l'alerta segons els valors rebuts:
osamCommons.rating(
language = Language.CA,
isDarkMode = false, // Opcional, per defecte false
applyComModStyles = true // Opcional, per defecte true
) {
// Do something...
}A la inicialització se li ha de passar el context de l'app. Per cridar al control de valoracions
només cal executar la funció rating() i facilitar-li l'idioma en què es vol mostrar el popup. Per
facilitar l'idioma, la llibreria inclou la classe Language que conté Català (CA),
Castellà (ES) i Anglès (EN), que són els idiomes suportats. Com a extra, se li ha afegit un
callback perquè la pantalla principal pugui reaccionar en cas que hi hagi hagut un error o si, a
part de la funcionalitat que ofereix la llibreria, es vol afegir alguna funcionalitat més pròpia de
l'aplicació. Lo que revem en el callback es l'objecte RatingControlResponse. Aquest objecte pot
arrivar amb quatre valors possibles:
- ACCEPTED: s'ha sol·licitat que surti el popup natiu de valoració de Android: Google In-App Review
- DISMISSED: el popup no compleix les condicions per mostrar-li a l'usuari
- ERROR: si hi ha hagut cap error al procés d'obtenir la informació necessaria o al mostrar el popup
Per exemple: Si l'usuari treu el popup, al callback rebriem
l'objecte RatingControlResponse.DISMISSED. Si en el cas de que volgués fer alguna acció diferent
si l'usuari ha tret el popup o aquest no compleix les condicions per sortir, es podria definir en
aquest punt la casuistica.
Per crear el missatge d'alerta, únicament hem de cridar a la funció que descarregarà el json amb les variables ja definides i mostrarà l'alerta segons els valors rebuts:
osamCommons.rating(
language: Language.es,
isDarkMode: false, // Opcional, per defecte false
applyComModStyles: true, // Opcional, per defecte true
f: {_ in }
)A la inicialització se li ha de passar el UIViewController de la pantalla que crida al mòdul. Per
cridar al control de valoracions només cal executar la funció rating(), facilitar-li l'idioma en
què es vol mostrar el popup i la funció que volem que executi el callback que retorna el mòdul. Per
facilitar l'idioma, la llibreria inclou la classe Language que conté Català (CA),
Castellà (ES) i Anglès (EN), que són els idiomes suportats. Pel que respecta al callback,
s'ha afegit perquè la pantalla pugui reaccionar en cas que hi hagi hagut un error o si, a part de la
funcionalitat que ofereix la llibreria, es vol afegir alguna funcionalitat més pròpia de
l'aplicació. Lo que revem en el callback es l'objecte RatingControlResponse. Aquest objecte pot
arrivar amb tres valors possibles:
- ACCEPTED: s'ha sol·licitat que surti el popup natiu de valoració d'iOS: SKStoreReviewController
- DISMISSED: el popup no compleix les condicions per mostrar-li a l'usuari
- ERROR: si hi ha hagut cap error al procés d'obtenir la informació necessaria o al mostrar el popup
Per exemple: Si l'usuari treu el popup, al callback rebriem
l'objecte RatingControlResponse.DISMISSED. Si en el cas de que volgués fer alguna acció diferent
si l'usuari ha tret el popup o aquest no compleix les condicions per sortir, es podria definir en
aquest punt la casuistica.
Entre la informació a extreure hi ha: el sistema operatiu, la versió del sistema operatiu i el model del dispositiu.
osamCommons.deviceInformation { deviceInformationResponse, deviceInformation ->
// Do something...
}Per obtenir l'informació només cal executar la funció deviceInformation(). Com a extra, se li ha afegit un
callback perquè la pantalla principal pugui reaccionar en cas que hi hagi hagut un error o si, a
part de la funcionalitat que ofereix la llibreria, es vol afegir alguna funcionalitat més pròpia de
l'aplicació. Lo que revem en el callback es l'objecte DeviceInformationResponse. Aquest objecte pot
arrivar amb 2 valors possibles:
- ACCEPTED: s'ha pogut obtenir les dades i s'han retornat correctament
- ERROR: si hi ha hagut cap error al procés d'obtenir la informació necessaria
Entre la informació a extreure hi ha: el sistema operatiu, la versió del sistema operatiu i el model del dispositiu.
osamCommons.deviceInformation(
f: {deviceInformationResponse, deviceInformation in }
)Per obtenir l'informació només cal executar la funció deviceInformation(). Com a extra, se li ha afegit un
callback perquè la pantalla principal pugui reaccionar en cas que hi hagi hagut un error o si, a
part de la funcionalitat que ofereix la llibreria, es vol afegir alguna funcionalitat més pròpia de
l'aplicació. Lo que revem en el callback es l'objecte DeviceInformationResponse. Aquest objecte pot
arrivar amb 2 valors possibles:
- ACCEPTED: s'ha pogut obtenir les dades i s'han retornat correctament
- ERROR: si hi ha hagut cap error al procés d'obtenir la informació necessaria
Entre la informació a extreure està: informació del model , sistema operatiu, versió de SO, nom app, i versió de l'app.
osamCommons.appInformation { appInformationResponse, appInformation ->
// Do something...
}Per obtenir l'informació només cal executar la funció appInformation(). Com a extra, se li ha afegit un
callback perquè la pantalla principal pugui reaccionar en cas que hi hagi hagut un error o si, a
part de la funcionalitat que ofereix la llibreria, es vol afegir alguna funcionalitat més pròpia de
l'aplicació. Lo que revem en el callback es l'objecte AppInformationResponse. Aquest objecte pot
arrivar amb 2 valors possibles:
- ACCEPTED: s'ha pogut obtenir les dades i s'han retornat correctament
- ERROR: si hi ha hagut cap error al procés d'obtenir la informació necessaria
Entre la informació a extreure està: informació del model , sistema operatiu, versió de SO, nom app, i versió de l'app.
osamCommons.appInformation(
f: { appInformationResponse, appInformation in }
)Per obtenir l'informació només cal executar la funció appInformation(). Com a extra, se li ha afegit un
callback perquè la pantalla principal pugui reaccionar en cas que hi hagi hagut un error o si, a
part de la funcionalitat que ofereix la llibreria, es vol afegir alguna funcionalitat més pròpia de
l'aplicació. Lo que revem en el callback es l'objecte AppInformationResponse. Aquest objecte pot
arrivar amb 2 valors possibles:
- ACCEPTED: s'ha pogut obtenir les dades i s'han retornat correctament
- ERROR: si hi ha hagut cap error al procés d'obtenir la informació necessaria
Per executar l'esdeveniment, cal cridar la funció changeLanguageEvent, passant el nou Language i un callback per gestionar la resposta.
osamCommons.changeLanguageEvent(
language = Language.CA
) { response ->
// Do something
}Per obtenir la informació només cal executar la funció changeLanguageEvent(). Com a extra, se li ha afegit un
callback perquè la pantalla principal pugui reaccionar en cas que hi hagi hagut un error o si, a
part de la funcionalitat que ofereix la llibreria, es vol afegir alguna funcionalitat més pròpia de
l'aplicació. El que reviem callback és l'objecte AppLanguageResponse. Aquest objecte pot
arribar amb 3 valors possibles:
- SUCCESS: La part síncrona de l'esdeveniment
- (actualització de preferències i registre d'analítiques)
- s'ha completat correctament i s'ha enviat la sol·licitud asíncrona per actualitzar la subscripció al topic.
- UNCHANGED: No s'ha realitzat cap acció perquè l'idioma seleccionat era el mateix que l'actual.
- ERROR: S'ha produït un error en intentar actualitzar les preferències locals o registrar l'esdeveniment d'analítica.
Aquest esdeveniment recull la informació de l'idioma anterior al canvi, l'idioma recent seleccionat i l'idioma del dispositiu mòbil
osamCommons.changeLanguageEvent(
language: Language.es,
f: {_ in }
)Per obtenir la informació només cal executar la funció changeLanguageEvent(). Com a extra, se li ha afegit un
callback perquè la pantalla principal pugui reaccionar en cas que hi hagi hagut un error o si, a
part de la funcionalitat que ofereix la llibreria, es vol afegir alguna funcionalitat més pròpia de
l'aplicació. El que reviem callback és l'objecte AppLanguageResponse. Aquest objecte pot
arribar amb 3 valors possibles:
- SENT: l'analítica s'ha enviat correctament
- NOT_SENT: l'analitica no s'ha enviat perqu l'idioma no ha canviat
- ERROR: hi ha hagut un error enviant l'analitica
Per executar l'esdeveniment, cal cridar la funció firstTimeOrUpdateEvent, passant l'idioma actual i un callback per gestionar la resposta.
osamCommons.firstTimeOrUpdateEvent(language = Language.CA){ response ->
// Do something
}Aquesta funció s'encarrega de tota la lògica de fons. S'ha afegit un callback perquè l'aplicació pugui reaccionar al resultat de l'operació. L'objecte AppLanguageResponse pot arribar amb 3 valors possibles:
- SUCCESS: La subscripció al topic s'ha realitzat o actualitzat correctament.
- UNCHANGED: No s'ha realitzat cap acció perquè el topic no ha canviat (mateixa versió i idioma).
- ERROR: S'ha produït un error durant el procés de subscripció.
Per executar l'esdeveniment, cal cridar la funció firstTimeOrUpdateEvent,
passant l'idioma actual i un completion handler per processar la resposta.
osamCommons.firstTimeOrUpdateEvent(
language: Language.es,
f: {_ in }
)La funció inclou un callback que retorna un objecte AppLanguageResponse,
permetent a l'aplicació reaccionar al resultat de l'operació.
Aquest objecte pot tenir un dels tres valors possibles:
- SUCCESS: La subscripció al topic s'ha realitzat o actualitzat correctament.
- UNCHANGED: No s'ha realitzat cap acció perquè el topic no ha canviat (mateixa versió i idioma).
- ERROR: S'ha produït un error durant el procés de subscripció.
Per executar l'esdeveniment, cal cridar la funció subscribeToCustomTopic, passant el nom del topic i un callback per gestionar la resposta.
osamCommons.subscribeToCustomTopic(topic = "NOM_DEL_TOPIC") { response ->
{
// Do something
}
}Aquesta funció s'encarrega de tota la lògica de fons. S'ha afegit un callback perquè l'aplicació pugui reaccionar al resultat de l'operació. L'objecte AppLanguageResponse pot arribar amb 3 valors possibles:
- SUCCESS: La subscripció al topic s'ha realitzat o actualitzat correctament.
- UNCHANGED: No s'ha realitzat cap acció perquè el topic no ha canviat (mateixa versió i idioma).
- ERROR: S'ha produït un error durant el procés de subscripció.
Per executar l'esdeveniment, cal cridar la funció subscribeToCustomTopic,
passant el nom del topic i un completion handler per processar la resposta.
osamCommons.subscribeToCustomTopic(topic: "NOM_DEL_TOPIC")La funció inclou un callback que retorna un objecte SubscriptionResponse, permetent a l'aplicació reaccionar
al resultat de l'operació. Aquest objecte pot tenir un dels dos valors possibles:
- SUCCESS: La subscripció al topic s'ha realitzat correctament
- ERROR: S'ha produït un error durant el procés de subscripció.
Per executar l'esdeveniment, cal cridar la funció unsubscribeToCustomTopic, passant el nom del topic i un callback per gestionar la resposta.
osamCommons.unsubscribeToCustomTopic(topic = "NOM_DEL_TOPIC") { response ->
{
// Do something
}
}Aquesta funció s'encarrega de tota la lògica de fons. S'ha afegit un callback perquè l'aplicació pugui reaccionar al resultat de l'operació. L'objecte AppLanguageResponse pot arribar amb 3 valors possibles:
- SUCCESS: La unsubscripció al topic s'ha realitzat o actualitzat correctament.
- UNCHANGED: No s'ha realitzat cap acció perquè el topic no ha canviat (mateixa versió i idioma).
- ERROR: S'ha produït un error durant el procés de unsubscripció.
Per executar l'esdeveniment, cal cridar la funció unsubscribeToCustomTopic,
passant el nom del topic i un completion handler per processar la resposta.
osamCommons.unsubscribeToCustomTopic(topic: "NOM_DEL_TOPIC")La funció inclou un callback que retorna un objecte SubscriptionResponse, permetent a l'aplicació reaccionar
al resultat de l'operació. Aquest objecte pot tenir un dels dos valors possibles:
- SUCCESS: La unsubscripció al topic s'ha realitzat correctament
- ERROR: S'ha produït un error durant el procés de unsubscripció.
Per obtenir el token, cal cridar la funció getFCMToken i gestionar el resultat mitjançant un callback.
osamCommons.getFCMToken { response ->
when (response) {
is TokenResponse.Success -> {
val token = response.token
// El token s'ha obtingut correctament.
// Pots utilitzar el 'token' aquí.
Log.d("MyApp", "Token FCM: $token")
}
is TokenResponse.Error -> {
// S'ha produït un error en obtenir el token.
val exception = response.error.exception
Log.e("MyApp", "Error en obtenir el token", exception)
}
}
}La funció inclou un callback que retorna un objecte TokenResponse, permetent a l'aplicació reaccionar al resultat de l'operació. Aquest objecte pot tenir un dels dos estats possibles:
- SUCCESS: L'operació s'ha completat correctament. Aquest objecte conté una propietat token amb el String del token FCM.
- ERROR: S'ha produït un error durant el procés d'obtenció del token. Aquest objecte conté una propietat error amb els detalls de l'excepció.
Per obtenir el token, cal cridar la funció getFCMToken i gestionar el resultat mitjançant un completion handler.
osamCommons.getFCMToken { response in
// Aquest callback s'executa sempre en el fil principal (main thread).
switch response {
case let success as TokenResponse.Success:
let token = success.token
print("Token FCM obtingut: \(token)")
case let error as TokenResponse.Error:
let errorMessage = error.error.exception.message ?? "Error desconegut"
print("S'ha produït un error en obtenir el token: \(errorMessage)")
default:
print("Resposta desconeguda")
}
}La funció inclou un callback que retorna un objecte TokenResponse, permetent a l'aplicació reaccionar al resultat de l'operació.
Aquest objecte pot tenir un dels dos estats possibles:
- SUCCESS: L'operació s'ha completat correctament. Aquest objecte conté una propietat token amb el String del token FCM.
- ERROR: S'ha produït un error durant el procés d'obtenció del token. Aquest objecte conté una propietat error amb els detalls de l'excepció.
Aquesta funció permet comprovar de manera asíncrona si el dispositiu té connexió a internet i pot arribar al backend.
osamCommons.isOnline { online ->
if (online) {
// El dispositiu està en línia
} else {
// El dispositiu està fora de línia
}
}osamCommons.isOnline(f: { online in
if (online) {
// El dispositiu està en línia
} else {
// El dispositiu està fora de línia
}
}){
"data": {
"id": 109,
"appId": 400,
"packageName": "cat.bcn.commonmodule",
"versionCode": 2021050000,
"versionName": "1.0.0",
"startDate": 1645311600000,
"endDate": 1645311600000,
"serverDate": 1645788600000,
"platform": "IOS",
"comparisonMode": "NONE",
"title": {
"es": "TITLE_ES",
"en": "TITLE_EN",
"ca": "TITLE_CA"
},
"message": {
"es": "MESSAGE_ES",
"en": "MESSAGE_EN",
"ca": "MESSAGE_CA"
},
"ok": {
"es": "OK",
"en": "OK",
"ca": "OK"
},
"cancel": {
"es": "Cancelar",
"en": "Cancel",
"ca": "Cancel.lar"
},
"url": "https://apps.apple.com/es/app/barcelona-a-la-butxaca/id1465234509?l=ca",
"checkBoxDontShowAgain": true,
"dialogDisplayDuration": 3600,
"osVersionComparisonMode": 0,
"osVersion": "2.0.0",
"modelComparisonMode": 1,
"models": ["SM-A536B"]
}
}- packageName
- Obligatori
- Especifica el ApplicationID o BundleID de l'app que afecta
- versionCode
- Obligatori
- Especifica la versió a la que afecta el control de versions
- startDate
- Opcional
- Data des de quan s'ha de començar a mostrar el pop-up del control de versions, expressada amb timestamp (milisegons des del 01/01/1970). Si no arriba informada, es considerarà com si fos el 0.
- endDate
- Opcional
- Data fins quan s'ha de mostrar el pop-up del control de versions, expressada amb timestamp ( milisegons des del 01/01/1970). Si no arriba informada, es considerara com si fos 9223372036854775807 (el valor màxim possible del Long).
- serverDate
- Obligatori
- Data actual proporcionada per el servidor. Serà la que s'utilitzi per comparar amb
startDateyendDate.
- platform
- Obligatori
- Especifica per a quina plataforma (ANDROID o IOS) afecta
- comparisonMode
- Obligatori
- Especifica la manera de comparació de la versió de l'app amb el mòdul
- title
- Obligatori
- Títol de l'alerta en el cas que s'hagi de mostrar.
- message
- Obligatori
- Missatge de l'alerta en cas que s'hagi de mostrar.
- ok
- Opcional
- Títol del botó d'acceptar.
- Si es rep aquest paràmetre juntament amb el paràmetre okButtonActionURL, es mostrarà en l'alerta un botó d'acceptar que obrirà el link que s'ha especificat en el paràmetre okButtonActionURL.
- cancel
- Opcional
- Títol del botó de cancel·lar
- url
- Opcional
- Link que s'obrirà quan l'usuari seleccioni el botó d'acceptar. Per exemple: link de la nova
versió de l'aplicació a l'App Store / Google Play. Si el
comparisonModeés del tipus INFO, el botó no redirigirà a aquesta URL.
- checkBoxDontShowAgain
- Opcional (default_value=True)
- Als modes INFO i LAZY hi ha una casella de selecció "No ho mostris més" si l'usuari no vol actualitzar l'app i no vol tornar a veure el pop-up.
- dialogDisplayDuration
- Opcional (default_value=3600seconds)
- Per als modes INFO i LAZY, quan l’usuari obre el control de versions i accepta, ara existeix un camp que defineix el temps perquè torni a aparèixer aquest popup.
- osVersionComparisonMode
- Opcional (default_value=-1)
- Especifica la regla de comparació de la versió del sistema operatiu del dispositiu respecte al paràmetre osVersion.
- Valors:
- -1: Totes les versions (no filtra per SO).
- 0: Menor o igual que la versió especificada.
- 1: Exactament la versió especificada.
- 2: Major o igual que la versió especificada.
- osVersion
- Opcional
- Especifica la versió del sistema operatiu objectiu per a la comparació (ex: "13", "14.2").
- modelComparisonMode
- Opcional (default_value=0)
- Especifica la regla de comparació del model del dispositiu respecte al paràmetre
models. - Valors:
- 0: Tots els models (
ALL_MODELS), no aplica cap filtre per model. - 1: Només aquests models (
ONLY_THESE_MODELS), només mostra el pop-up si el model actual és dins demodels. - 2: Excloure aquests models (
NOT_THESE_MODELS), mostra el pop-up si el model actual NO és dins demodels.
- 0: Tots els models (
- models
- Opcional
- Llista de models de dispositiu a utilitzar en el filtre de
modelComparisonMode. - Format esperat: array JSON de strings, per exemple
["SM-A536B", "iPhone14,2"]. - Aquest camp només té efecte quan
modelComparisonModeés 1 o 2.
{
"data": {
"id": 74,
"appId": 401,
"appStoreIdentifier": "1234567890",
"packageName": "cat.bcn.commonmodule",
"platform": "ANDROID",
"minutes": 2880,
"numAperture": 5,
"message": {
"es": "MESSAGE_ES",
"en": "MESSAGE_EN",
"ca": "MESSAGE_CA"
}
}
}- appStoreIdentifier
- Obligatori
- Especifica el id de l'app al AppStore per poder valorar-la
- packageName
- Obligatori
- Especifica el ApplicationID o BundleID de l'app que afecta
- platform
- Obligatori
- Especifica per a quina plataforma (ANDROID o IOS) afecta
- minutes
- Obligatori
- Especifica el temps (en minuts) que ha de passar perquè surti el popup
- numAperture
- Obligatori
- Especifica la quantitat de vegades que s'ha d'obrir l'app perquè surti el popup
- message
- Obsolet
- A partir de la versió 2.0.0, aquest paràmetre ja no es fa servir
En primer lloc, ha de cumplir-se que el valor del paràmetre serverDate estigui entre el valor
de startDate i el de endDate. Si això no es compleix, no es mostrarà l'alerta. Si es compleix,
en funció del valor del paràmetre comparisonMode es mostrarà o no l'alerta. Aquest paràmetre
compararà la versió instal·lada amb la qual rebem del json, en funció de tres valors:
- FORCE: Mostra l'alerta i no es pot treure. Actualització obligatoria
- LAZY: Mostra l'alerta amb l'opció d'actualitzar l'app o seguir utilitzant l'actual. Actualització voluntaria
- INFO: Mostra l'alerta amb un missatge informatiu. El botó no obre cap URL, deixa seguir utilitzant l'app amb normalitat.
- NONE: no es mostra el popup
- L’app compta cada vegada que s’obre (s'ha de cridar el mètode "rating" de la llibreria)
- L’app espera a que passin un nº de minuts determinats (p.ex. 90) des de l’últim cop que ha mostrat la pop up (per tal de l’usuari no la consideri intrusiva o abusiva).
- Un cop passats aquests dies i quan el comptador superi un valor determinat (p.ex. 20), mostra el popup i el comptador es reinicia independentment de la resposta de l’usuari.*
- La operativa no es veu modificada si hi ha un canvi de versió (és a dir, es mantenen els valors de comptatge de dies i de nº de apertures).
- En cas de què s'hagi de mostrar el popup, a Android es crida a la llibreria de Google Play Core i a iOS es crida al SKStoreReviewController.
Idioma per "default", s'ha d'utilitzar Language.parse(...) en comptes de valueOf(...)
- S'ha d'utilitzar: Language.parse(...) si es vol obtenir un idioma "default". Així no genera l'error a l'utilitzar valueOf(...) de l'enum de Language.
Gestiona tota la lògica associada al canvi d'idioma de l'aplicació. Aquesta funció és el punt d'entrada principal per processar un canvi d'idioma. Orquestra diverses accions clau per assegurar que l'estat de l'aplicació s'actualitzi correctament:
- Actualitza les preferències locals: Desa a l'emmagatzematge local del dispositiu tant l'idioma nou seleccionat com l'idioma anterior.
- Envia un esdeveniment d'analítica: Registra un esdeveniment language_change a Firebase Analytics, capturant l'idioma previ, el nou idioma i l'idioma de visualització del dispositiu.
- Actualitza la subscripció al topic de notificacions: Actualitza de manera asíncrona la subscripció de l'app als topics de Firebase Cloud Messaging. Això garanteix que l'usuari rebi les notificacions push dirigides al seu nou idioma seleccionat.
Gestiona la subscripció inicial o l'actualització del topic de notificacions de l'aplicació. Aquesta funció s'ha de cridar un cop l'aplicació s'inicia per assegurar que el dispositiu estigui subscrit al topic correcte de Firebase Cloud Messaging.
A més, durant la primera instal·lació també s’executa l’enviament de dades d’analítica inicial, de manera que el sistema pot registrar l’estat de l’usuari i la configuració de l’app des del primer moment.
Orquestra les següents accions:
- Recupera la informació del darrer topic al qual l'app estava subscrita (si existeix).
- Construeix el nom del nou topic basant-se en la versió actual de l'app i l'idioma del dispositiu.
- Actualitza la subscripció a Firebase, donant de baixa l'antic topic i subscrivint-se al nou.
- Desa localment la informació de la nova versió per a la propera execució de l'app.
Aquesta funció permet subscriure l'aplicació a un topic de notificacions de Firebase amb un nom específic i personalitzat.
És útil per a campanyes de màrqueting o per segmentar usuaris en grups que no depenen de la versió de l'app o de l'idioma. La funció s'encarrega de gestionar la comunicació amb Firebase per realitzar la subscripció de manera asíncrona.
Aquesta funció permet desubscriure l'aplicació d'un topic de notificacions de Firebase amb un nom específic i personalitzat.
És l'operació inversa a subscribeToCustomTopic i és útil per aturar la recepció de notificacions d'una campanya concreta o per netejar subscripcions quan ja no són necessàries. La funció s'encarrega de gestionar la comunicació amb Firebase per realitzar la desubscripció de manera asíncrona.
Aquesta funció permet obtenir el token de registre de Firebase Cloud Messaging (FCM) del dispositiu. Aquest token és un identificador únic que s'utilitza per enviar notificacions push directament a un dispositiu específic. L'operació es realitza de manera asíncrona.
Aquesta funció realitza una petició ràpida al backend per verificar la connectivitat. S'executa de manera asíncrona per no bloquejar el fil principal i retorna un booleà mitjançant un callback.
Si voleu compilar el mòdul manualment o realitzar canvis en la part comuna, podeu utilitzar l'script build.sh situat a l'arrel del projecte. Aquest script neteja els binaris antics, compila la part d'Android i genera l'XCFramework per a iOS.
./build.sh