Panagiotis Triantafyllou

android target api 35, changed sdk version in constants

...@@ -3,13 +3,14 @@ apply plugin: 'com.google.gms.google-services' ...@@ -3,13 +3,14 @@ apply plugin: 'com.google.gms.google-services'
3 apply plugin: 'com.huawei.agconnect' 3 apply plugin: 'com.huawei.agconnect'
4 4
5 android { 5 android {
6 - compileSdkVersion 34 6 + compileSdkVersion 35
7 - buildToolsVersion "34.0.0" 7 + buildToolsVersion "35.0.0"
8 8
9 + namespace "warp.ly.android_sdk"
9 defaultConfig { 10 defaultConfig {
10 applicationId "warp.ly.android_sdk" 11 applicationId "warp.ly.android_sdk"
11 minSdkVersion 31 12 minSdkVersion 31
12 - targetSdkVersion 34 13 + targetSdkVersion 35
13 versionCode 100 14 versionCode 100
14 versionName "1.0.0" 15 versionName "1.0.0"
15 } 16 }
......
...@@ -8,9 +8,9 @@ buildscript { ...@@ -8,9 +8,9 @@ buildscript {
8 maven { url 'https://plugins.gradle.org/m2/' } 8 maven { url 'https://plugins.gradle.org/m2/' }
9 } 9 }
10 dependencies { 10 dependencies {
11 - classpath 'com.android.tools.build:gradle:8.8.0' 11 + classpath 'com.android.tools.build:gradle:8.7.3'
12 - classpath 'com.google.gms:google-services:4.4.2' 12 + classpath 'com.google.gms:google-services:4.4.3'
13 - classpath 'com.huawei.agconnect:agcp:1.9.1.300' 13 + classpath 'com.huawei.agconnect:agcp:1.9.1.301'
14 classpath 'io.github.gradle-nexus:publish-plugin:1.1.0' 14 classpath 'io.github.gradle-nexus:publish-plugin:1.1.0'
15 15
16 // NOTE: Do not place your application dependencies here; they belong 16 // NOTE: Do not place your application dependencies here; they belong
...@@ -18,6 +18,10 @@ buildscript { ...@@ -18,6 +18,10 @@ buildscript {
18 } 18 }
19 } 19 }
20 20
21 +plugins {
22 + id 'maven-publish'
23 +}
24 +
21 allprojects { 25 allprojects {
22 repositories { 26 repositories {
23 mavenCentral() 27 mavenCentral()
...@@ -27,5 +31,4 @@ allprojects { ...@@ -27,5 +31,4 @@ allprojects {
27 } 31 }
28 } 32 }
29 33
30 -apply plugin: 'io.github.gradle-nexus.publish-plugin'
31 apply from: "${rootDir}/scripts/publish-root.gradle" 34 apply from: "${rootDir}/scripts/publish-root.gradle"
...\ No newline at end of file ...\ No newline at end of file
......
1 #Fri Jul 26 17:08:44 EEST 2024 1 #Fri Jul 26 17:08:44 EEST 2024
2 distributionBase=GRADLE_USER_HOME 2 distributionBase=GRADLE_USER_HOME
3 distributionPath=wrapper/dists 3 distributionPath=wrapper/dists
4 -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip 4 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip
5 zipStoreBase=GRADLE_USER_HOME 5 zipStoreBase=GRADLE_USER_HOME
6 zipStorePath=wrapper/dists 6 zipStorePath=wrapper/dists
......
...@@ -40,7 +40,7 @@ afterEvaluate { ...@@ -40,7 +40,7 @@ afterEvaluate {
40 // Sources are now handled by the android block's singleVariant 40 // Sources are now handled by the android block's singleVariant
41 // artifact javadocJar 41 // artifact javadocJar
42 42
43 - // Mostly self-explanatory metadata 43 + // POM metadata for Maven Central
44 pom { 44 pom {
45 name = PUBLISH_ARTIFACT_ID 45 name = PUBLISH_ARTIFACT_ID
46 description = 'Warply Android SDK Maven Plugin' 46 description = 'Warply Android SDK Maven Plugin'
...@@ -60,8 +60,7 @@ afterEvaluate { ...@@ -60,8 +60,7 @@ afterEvaluate {
60 // Add all other devs here... 60 // Add all other devs here...
61 } 61 }
62 62
63 - // Version control info - if you're using GitHub, follow the 63 + // Version control info
64 - // format as seen here
65 scm { 64 scm {
66 connection = 'scm:git:git.warp.ly/open-source/warply_android_sdk_maven_plugin.git' 65 connection = 'scm:git:git.warp.ly/open-source/warply_android_sdk_maven_plugin.git'
67 developerConnection = 'scm:git:ssh://git.warp.ly/open-source/warply_android_sdk_maven_plugin.git' 66 developerConnection = 'scm:git:ssh://git.warp.ly/open-source/warply_android_sdk_maven_plugin.git'
...@@ -81,3 +80,365 @@ signing { ...@@ -81,3 +80,365 @@ signing {
81 ) 80 )
82 sign publishing.publications 81 sign publishing.publications
83 } 82 }
83 +
84 +// Configuration for Central Publishing (similar to Maven plugin configuration)
85 +ext.centralPublishing = [
86 + autoPublish : false, // Manual publishing for safety
87 + // waitUntil: "published", // Commented out - don't wait for publishing
88 + deploymentName: "Warply Android SDK ${PUBLISH_VERSION}",
89 + centralBaseUrl: "https://central.sonatype.com"
90 +]
91 +
92 +// Custom task that implements the same functionality as org.sonatype.central:central-publishing-maven-plugin:0.8.0
93 +// This uses the Central Portal API directly to achieve the same result
94 +task publishToCentralPortal {
95 + dependsOn 'publishReleasePublicationToMavenLocal'
96 +
97 + description = 'Publishes to Maven Central Portal using the same API as central-publishing-maven-plugin:0.8.0'
98 + group = 'publishing'
99 +
100 + doLast {
101 + def username = rootProject.ext["centralPortalUsername"]
102 + def password = rootProject.ext["centralPortalPassword"]
103 + def config = project.ext.centralPublishing
104 +
105 + if (!username || !password) {
106 + throw new GradleException("Central Portal credentials not configured. Please set centralPortalUsername and centralPortalPassword in local.properties or environment variables.")
107 + }
108 +
109 + println "=== Central Portal Publishing ==="
110 + println "Deployment: ${config.deploymentName}"
111 + println "Auto-publish: ${config.autoPublish}"
112 + println "Portal URL: ${config.centralBaseUrl}"
113 + println ""
114 +
115 + // Step 1: Create deployment bundle
116 + println "Step 1: Creating deployment bundle..."
117 + def bundleFile = createDeploymentBundle()
118 + println "✓ Bundle created: ${bundleFile.name} (${bundleFile.length()} bytes)"
119 +
120 + // Step 2: Upload bundle to Central Portal
121 + println "\nStep 2: Uploading to Central Portal..."
122 + def deploymentId = uploadBundle(bundleFile, username, password, config)
123 + println "✓ Upload successful. Deployment ID: ${deploymentId}"
124 +
125 + // Step 3: Wait for validation
126 + println "\nStep 3: Waiting for validation..."
127 + def validationResult = waitForValidation(deploymentId, username, password, config)
128 +
129 + if (validationResult.success) {
130 + def state = validationResult.state
131 + println "✓ Validation successful! State: ${state}"
132 +
133 + if (config.autoPublish && state == "VALIDATED") {
134 + println "\nStep 4: Auto-publishing..."
135 + def publishResult = publishDeployment(deploymentId, username, password, config)
136 + if (publishResult.success) {
137 + println "✓ Published successfully to Maven Central!"
138 + } else {
139 + throw new GradleException("Auto-publishing failed: ${publishResult.error}")
140 + }
141 + } else if (state == "PUBLISHED") {
142 + println "✓ Already published to Maven Central!"
143 + def response = validationResult.response
144 + def purls = response.purls ?: []
145 + if (purls) {
146 + println " Published artifacts:"
147 + purls.each { purl -> println " - ${purl}" }
148 + }
149 + } else {
150 + println "\n✓ Deployment uploaded and validated successfully!"
151 + println "📋 Manual action required:"
152 + println " Visit: ${config.centralBaseUrl}/publishing/deployments"
153 + println " Find deployment: ${config.deploymentName}"
154 + println " Click 'Publish' to complete the process"
155 + }
156 + } else {
157 + throw new GradleException("Validation failed: ${validationResult.error}")
158 + }
159 +
160 + println "\n=== Publishing Complete ==="
161 + }
162 +}
163 +
164 +def createDeploymentBundle() {
165 + def bundleDir = file("${buildDir}/central-publishing")
166 + def stagingDir = file("${bundleDir}/staging")
167 +
168 + // Clean and create directories
169 + bundleDir.deleteDir()
170 + stagingDir.mkdirs()
171 +
172 + // Create Maven repository structure
173 + def groupPath = PUBLISH_GROUP_ID.replace('.', '/')
174 + def artifactDir = file("${stagingDir}/${groupPath}/${PUBLISH_ARTIFACT_ID}/${PUBLISH_VERSION}")
175 + artifactDir.mkdirs()
176 +
177 + // Copy artifacts to staging area
178 + def artifacts = [:]
179 +
180 + // AAR file
181 + def aarFile = file("${buildDir}/outputs/aar/warply_android_sdk-release.aar")
182 + if (aarFile.exists()) {
183 + def targetAar = file("${artifactDir}/${PUBLISH_ARTIFACT_ID}-${PUBLISH_VERSION}.aar")
184 + copy {
185 + from aarFile
186 + into artifactDir
187 + rename { targetAar.name }
188 + }
189 + artifacts['aar'] = targetAar
190 +
191 + // Copy AAR signature if exists
192 + def aarSigFile = file("${aarFile.path}.asc")
193 + if (aarSigFile.exists()) {
194 + def targetAarSig = file("${targetAar.path}.asc")
195 + copy {
196 + from aarSigFile
197 + into artifactDir
198 + rename { targetAarSig.name }
199 + }
200 + artifacts['aar-sig'] = targetAarSig
201 + }
202 + }
203 +
204 + // Sources JAR
205 + def sourcesFile = file("${buildDir}/libs/warply_android_sdk-${PUBLISH_VERSION}-sources.jar")
206 + if (sourcesFile.exists()) {
207 + def targetSources = file("${artifactDir}/${PUBLISH_ARTIFACT_ID}-${PUBLISH_VERSION}-sources.jar")
208 + copy {
209 + from sourcesFile
210 + into artifactDir
211 + rename { targetSources.name }
212 + }
213 + artifacts['sources'] = targetSources
214 +
215 + // Copy sources signature if exists
216 + def sourcesSigFile = file("${sourcesFile.path}.asc")
217 + if (sourcesSigFile.exists()) {
218 + def targetSourcesSig = file("${targetSources.path}.asc")
219 + copy {
220 + from sourcesSigFile
221 + into artifactDir
222 + rename { targetSourcesSig.name }
223 + }
224 + artifacts['sources-sig'] = targetSourcesSig
225 + }
226 + }
227 +
228 + // POM file
229 + def pomFile = file("${buildDir}/publications/release/pom-default.xml")
230 + if (pomFile.exists()) {
231 + def targetPom = file("${artifactDir}/${PUBLISH_ARTIFACT_ID}-${PUBLISH_VERSION}.pom")
232 + copy {
233 + from pomFile
234 + into artifactDir
235 + rename { targetPom.name }
236 + }
237 + artifacts['pom'] = targetPom
238 +
239 + // Copy POM signature if exists
240 + def pomSigFile = file("${pomFile.path}.asc")
241 + if (pomSigFile.exists()) {
242 + def targetPomSig = file("${targetPom.path}.asc")
243 + copy {
244 + from pomSigFile
245 + into artifactDir
246 + rename { targetPomSig.name }
247 + }
248 + artifacts['pom-sig'] = targetPomSig
249 + }
250 + }
251 +
252 + // Generate checksums for all files
253 + artifacts.each { type, artifactFile ->
254 + if (artifactFile.exists()) {
255 + generateChecksums(artifactFile)
256 + }
257 + }
258 +
259 + // Create bundle ZIP
260 + def bundleFile = file("${bundleDir}/central-bundle.zip")
261 + ant.zip(destfile: bundleFile) {
262 + fileset(dir: stagingDir)
263 + }
264 +
265 + return bundleFile
266 +}
267 +
268 +def generateChecksums(File file) {
269 + ['md5', 'sha1', 'sha256', 'sha512'].each { algorithm ->
270 + def checksum = file.withInputStream { stream ->
271 + java.security.MessageDigest.getInstance(algorithm.toUpperCase()).digest(stream.bytes).encodeHex().toString()
272 + }
273 + new File("${file.path}.${algorithm}").text = checksum
274 + }
275 +}
276 +
277 +def uploadBundle(File bundleFile, String username, String password, Map config) {
278 + def url = "${config.centralBaseUrl}/api/v1/publisher/upload"
279 + def credentials = "${username}:${password}".bytes.encodeBase64().toString()
280 +
281 + // Add query parameters
282 + def publishingType = config.autoPublish ? "AUTOMATIC" : "USER_MANAGED"
283 + def urlWithParams = "${url}?publishingType=${publishingType}&name=${URLEncoder.encode(config.deploymentName, 'UTF-8')}"
284 +
285 + println " Uploading to: ${urlWithParams}"
286 + println " Publishing type: ${publishingType}"
287 +
288 + def connection = new URL(urlWithParams).openConnection() as HttpURLConnection
289 + connection.setRequestMethod("POST")
290 + connection.setRequestProperty("Authorization", "Bearer ${credentials}")
291 + connection.setDoOutput(true)
292 + connection.setDoInput(true)
293 +
294 + // Create multipart/form-data boundary
295 + def boundary = "----WebKitFormBoundary" + System.currentTimeMillis()
296 + connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=${boundary}")
297 +
298 + // Write multipart data
299 + connection.outputStream.withWriter("UTF-8") { writer ->
300 + writer.write("--${boundary}\r\n")
301 + writer.write("Content-Disposition: form-data; name=\"bundle\"; filename=\"${bundleFile.name}\"\r\n")
302 + writer.write("Content-Type: application/octet-stream\r\n")
303 + writer.write("\r\n")
304 + writer.flush()
305 +
306 + // Write file content
307 + bundleFile.withInputStream { input ->
308 + connection.outputStream << input
309 + }
310 +
311 + writer.write("\r\n--${boundary}--\r\n")
312 + writer.flush()
313 + }
314 +
315 + // Get response
316 + def responseCode = connection.responseCode
317 + if (responseCode == 201) {
318 + def deploymentId = connection.inputStream.text.trim()
319 + println " ✓ Upload successful (HTTP ${responseCode})"
320 + return deploymentId
321 + } else {
322 + def errorMessage = connection.errorStream?.text ?: "Unknown error"
323 + throw new GradleException("Upload failed (HTTP ${responseCode}): ${errorMessage}")
324 + }
325 +}
326 +
327 +def waitForValidation(String deploymentId, String username, String password, Map config) {
328 + def credentials = "${username}:${password}".bytes.encodeBase64().toString()
329 + def maxAttempts = 60 // 5 minutes with 5-second intervals
330 + def attempt = 0
331 +
332 + while (attempt < maxAttempts) {
333 + attempt++
334 +
335 + def url = "${config.centralBaseUrl}/api/v1/publisher/status?id=${deploymentId}"
336 + def connection = new URL(url).openConnection() as HttpURLConnection
337 + connection.setRequestMethod("POST")
338 + connection.setRequestProperty("Authorization", "Bearer ${credentials}")
339 + connection.setRequestProperty("Content-Type", "application/json")
340 +
341 + def responseCode = connection.responseCode
342 + if (responseCode == 200) {
343 + def response = new groovy.json.JsonSlurper().parseText(connection.inputStream.text)
344 + def state = response.deploymentState
345 +
346 + println " Status check ${attempt}: ${state}"
347 +
348 + switch (state) {
349 + case "PENDING":
350 + case "VALIDATING":
351 + // Continue waiting
352 + Thread.sleep(5000)
353 + break
354 + case "VALIDATED":
355 + return [success: true, state: state, response: response]
356 + case "PUBLISHED":
357 + return [success: true, state: state, response: response]
358 + case "FAILED":
359 + def errors = response.errors ?: ["Unknown validation error"]
360 + return [success: false, error: "Validation failed: ${errors.join(', ')}", response: response]
361 + default:
362 + return [success: false, error: "Unknown deployment state: ${state}", response: response]
363 + }
364 + } else {
365 + def errorMessage = connection.errorStream?.text ?: "Unknown error"
366 + throw new GradleException("Status check failed (HTTP ${responseCode}): ${errorMessage}")
367 + }
368 + }
369 +
370 + return [success: false, error: "Timeout waiting for validation (${maxAttempts * 5} seconds)"]
371 +}
372 +
373 +def publishDeployment(String deploymentId, String username, String password, Map config) {
374 + def credentials = "${username}:${password}".bytes.encodeBase64().toString()
375 + def url = "${config.centralBaseUrl}/api/v1/publisher/deployment/${deploymentId}"
376 +
377 + println " Calling publish API..."
378 +
379 + def connection = new URL(url).openConnection() as HttpURLConnection
380 + connection.setRequestMethod("POST")
381 + connection.setRequestProperty("Authorization", "Bearer ${credentials}")
382 +
383 + def responseCode = connection.responseCode
384 + if (responseCode == 204) {
385 + println " ✓ Publish request successful (HTTP ${responseCode})"
386 +
387 + // Wait for publishing to complete
388 + println " Waiting for publishing to complete..."
389 + def result = waitForPublishing(deploymentId, username, password, config)
390 + return result
391 + } else {
392 + def errorMessage = connection.errorStream?.text ?: "Unknown error"
393 + throw new GradleException("Publish failed (HTTP ${responseCode}): ${errorMessage}")
394 + }
395 +}
396 +
397 +def waitForPublishing(String deploymentId, String username, String password, Map config) {
398 + def credentials = "${username}:${password}".bytes.encodeBase64().toString()
399 + def maxAttempts = 120 // 10 minutes with 5-second intervals
400 + def attempt = 0
401 +
402 + while (attempt < maxAttempts) {
403 + attempt++
404 +
405 + def url = "${config.centralBaseUrl}/api/v1/publisher/status?id=${deploymentId}"
406 + def connection = new URL(url).openConnection() as HttpURLConnection
407 + connection.setRequestMethod("POST")
408 + connection.setRequestProperty("Authorization", "Bearer ${credentials}")
409 + connection.setRequestProperty("Content-Type", "application/json")
410 +
411 + def responseCode = connection.responseCode
412 + if (responseCode == 200) {
413 + def response = new groovy.json.JsonSlurper().parseText(connection.inputStream.text)
414 + def state = response.deploymentState
415 +
416 + println " Publishing status ${attempt}: ${state}"
417 +
418 + switch (state) {
419 + case "PUBLISHING":
420 + // Continue waiting
421 + Thread.sleep(5000)
422 + break
423 + case "PUBLISHED":
424 + def purls = response.purls ?: []
425 + println " ✓ Successfully published to Maven Central!"
426 + if (purls) {
427 + println " Published artifacts:"
428 + purls.each { purl -> println " - ${purl}" }
429 + }
430 + return [success: true, state: state, response: response]
431 + case "FAILED":
432 + def errors = response.errors ?: ["Unknown publishing error"]
433 + return [success: false, error: "Publishing failed: ${errors.join(', ')}", response: response]
434 + default:
435 + return [success: false, error: "Unexpected state during publishing: ${state}", response: response]
436 + }
437 + } else {
438 + def errorMessage = connection.errorStream?.text ?: "Unknown error"
439 + throw new GradleException("Publishing status check failed (HTTP ${responseCode}): ${errorMessage}")
440 + }
441 + }
442 +
443 + return [success: false, error: "Timeout waiting for publishing (${maxAttempts * 5} seconds)"]
444 +}
......
1 // Create variables with empty default values 1 // Create variables with empty default values
2 2
3 +// Central Portal credentials
4 +ext["centralPortalUsername"] = ''
5 +ext["centralPortalPassword"] = ''
6 +
3 // keyId is the last 8 characters of the GPG key 7 // keyId is the last 8 characters of the GPG key
4 ext["signing.keyId"] = '' 8 ext["signing.keyId"] = ''
5 // password is the passphrase of the GPG key 9 // password is the passphrase of the GPG key
6 ext["signing.password"] = '' 10 ext["signing.password"] = ''
7 // key is the base64 private GPG key 11 // key is the base64 private GPG key
8 ext["signing.key"] = '' 12 ext["signing.key"] = ''
9 -// osshrUsername and ossrhPassword are the account details for MavenCentral
10 -// which we’ve chosen at the Jira registration step (Sonatype site))
11 -ext["ossrhUsername"] = ''
12 -ext["ossrhPassword"] = ''
13 -ext["sonatypeStagingProfileId"] = ''
14 13
15 File secretPropsFile = project.rootProject.file('local.properties') 14 File secretPropsFile = project.rootProject.file('local.properties')
16 if (secretPropsFile.exists()) { 15 if (secretPropsFile.exists()) {
...@@ -19,24 +18,11 @@ if (secretPropsFile.exists()) { ...@@ -19,24 +18,11 @@ if (secretPropsFile.exists()) {
19 new FileInputStream(secretPropsFile).withCloseable { is -> p.load(is) } 18 new FileInputStream(secretPropsFile).withCloseable { is -> p.load(is) }
20 p.each { name, value -> ext[name] = value } 19 p.each { name, value -> ext[name] = value }
21 } else { 20 } else {
22 - // Use system environment variables 21 + // Use system environment variables for signing
23 - ext["ossrhUsername"] = System.getenv('OSSRH_USERNAME')
24 - ext["ossrhPassword"] = System.getenv('OSSRH_PASSWORD')
25 - ext["sonatypeStagingProfileId"] = System.getenv('SONATYPE_STAGING_PROFILE_ID')
26 ext["signing.keyId"] = System.getenv('SIGNING_KEY_ID') 22 ext["signing.keyId"] = System.getenv('SIGNING_KEY_ID')
27 ext["signing.password"] = System.getenv('SIGNING_PASSWORD') 23 ext["signing.password"] = System.getenv('SIGNING_PASSWORD')
28 ext["signing.key"] = System.getenv('SIGNING_KEY') 24 ext["signing.key"] = System.getenv('SIGNING_KEY')
29 -} 25 + // Central Portal credentials can also come from environment
30 - 26 + ext["centralPortalUsername"] = System.getenv('CENTRAL_PORTAL_USERNAME')
31 -// Set up Sonatype repository 27 + ext["centralPortalPassword"] = System.getenv('CENTRAL_PORTAL_PASSWORD')
32 -nexusPublishing {
33 - repositories {
34 - sonatype {
35 - stagingProfileId = sonatypeStagingProfileId
36 - username = ossrhUsername
37 - password = ossrhPassword
38 - nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/"))
39 - snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/"))
40 - }
41 - }
42 } 28 }
......
...@@ -5,15 +5,15 @@ android.buildFeatures.buildConfig = true ...@@ -5,15 +5,15 @@ android.buildFeatures.buildConfig = true
5 5
6 ext { 6 ext {
7 PUBLISH_GROUP_ID = 'ly.warp' 7 PUBLISH_GROUP_ID = 'ly.warp'
8 - PUBLISH_VERSION = '4.5.5.4deh2' 8 + PUBLISH_VERSION = '4.5.5.4deh3'
9 PUBLISH_ARTIFACT_ID = 'warply-android-sdk' 9 PUBLISH_ARTIFACT_ID = 'warply-android-sdk'
10 } 10 }
11 11
12 apply from: "${rootProject.projectDir}/scripts/publish-module.gradle" 12 apply from: "${rootProject.projectDir}/scripts/publish-module.gradle"
13 13
14 android { 14 android {
15 - compileSdkVersion 34 15 + compileSdkVersion 35
16 - buildToolsVersion "34.0.0" 16 + buildToolsVersion "35.0.0"
17 17
18 useLibrary 'org.apache.http.legacy' 18 useLibrary 'org.apache.http.legacy'
19 19
...@@ -27,7 +27,7 @@ android { ...@@ -27,7 +27,7 @@ android {
27 27
28 defaultConfig { 28 defaultConfig {
29 minSdkVersion 31 29 minSdkVersion 31
30 - targetSdkVersion 34 30 + targetSdkVersion 35
31 consumerProguardFiles 'proguard-rules.pro' 31 consumerProguardFiles 'proguard-rules.pro'
32 vectorDrawables.useSupportLibrary = true 32 vectorDrawables.useSupportLibrary = true
33 } 33 }
...@@ -61,45 +61,42 @@ android { ...@@ -61,45 +61,42 @@ android {
61 61
62 dependencies { 62 dependencies {
63 //------------------------------ Support -----------------------------// 63 //------------------------------ Support -----------------------------//
64 - implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' 64 + implementation 'androidx.appcompat:appcompat:1.7.1'
65 - implementation 'androidx.appcompat:appcompat:1.4.1' 65 + implementation "androidx.security:security-crypto:1.1.0"
66 - api "androidx.security:security-crypto:1.1.0-alpha03"
67 // For minSDK 23 use 1.0.0, for minSDK 21 use 1.1.0 that is currently in alpha 66 // For minSDK 23 use 1.0.0, for minSDK 21 use 1.1.0 that is currently in alpha
68 - api 'org.altbeacon:android-beacon-library:2.19.3' 67 + implementation 'org.altbeacon:android-beacon-library:2.19.3'
69 - api 'org.jbundle.util.osgi.wrapped:org.jbundle.util.osgi.wrapped.org.apache.http.client:4.1.2' 68 + implementation 'org.jbundle.util.osgi.wrapped:org.jbundle.util.osgi.wrapped.org.apache.http.client:4.1.2'
70 - api 'io.reactivex.rxjava3:rxjava:3.1.8' 69 + implementation 'io.reactivex.rxjava3:rxjava:3.1.8'
71 - api 'io.reactivex.rxjava3:rxandroid:3.0.2' 70 + implementation 'io.reactivex.rxjava3:rxandroid:3.0.2'
72 - implementation 'com.google.android.material:material:1.5.0' 71 + implementation 'com.google.android.material:material:1.12.0'
73 implementation 'org.greenrobot:eventbus:3.3.1' 72 implementation 'org.greenrobot:eventbus:3.3.1'
74 - api 'com.google.guava:guava:30.1-android' 73 + implementation 'com.google.guava:guava:33.0.0-android'
75 74
76 //------------------------------ Firebase -----------------------------// 75 //------------------------------ Firebase -----------------------------//
77 - api platform('com.google.firebase:firebase-bom:29.0.3') 76 + implementation platform('com.google.firebase:firebase-bom:34.2.0')
78 implementation('com.google.firebase:firebase-messaging') { 77 implementation('com.google.firebase:firebase-messaging') {
79 exclude group: 'com.google.android.gms', module: 'play-services-location' 78 exclude group: 'com.google.android.gms', module: 'play-services-location'
80 } 79 }
81 80
82 //------------------------------ GMS -----------------------------// 81 //------------------------------ GMS -----------------------------//
83 - api 'com.google.android.gms:play-services-base:18.1.0' 82 + implementation 'com.google.android.gms:play-services-base:18.7.2'
84 - implementation 'com.google.android.gms:play-services-location:19.0.1' 83 + implementation 'com.google.android.gms:play-services-location:21.3.0'
85 84
86 //------------------------------ Work Manager -----------------------------// 85 //------------------------------ Work Manager -----------------------------//
87 - api 'androidx.work:work-runtime:2.7.1' 86 + implementation 'androidx.work:work-runtime:2.10.3'
88 87
89 //------------------------------ Glide -----------------------------// 88 //------------------------------ Glide -----------------------------//
90 - implementation 'com.github.bumptech.glide:glide:4.12.0' 89 + implementation 'com.github.bumptech.glide:glide:4.16.0'
91 - annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0' 90 + annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'
92 91
93 //------------------------------ Huawei -----------------------------// 92 //------------------------------ Huawei -----------------------------//
94 - implementation 'com.huawei.agconnect:agconnect-core:1.7.2.300' 93 + implementation 'com.huawei.agconnect:agconnect-core:1.9.3.301'
95 - implementation 'com.huawei.hms:base:6.6.0.300' 94 + implementation 'com.huawei.hms:base:6.13.0.303'
96 - implementation 'com.huawei.hms:push:6.7.0.300' 95 + implementation 'com.huawei.hms:push:6.10.0.300'
97 - implementation 'com.huawei.hms:ads-identifier:3.4.56.300' 96 + implementation 'com.huawei.hms:ads-identifier:3.4.62.300'
98 - 97 +
99 - //------------------------------ SQLCipher -----------------------------// 98 + //------------------------------ SQLite (Standard Android) -----------------------------//
100 - api "net.zetetic:android-database-sqlcipher:4.5.2" 99 + implementation 'androidx.sqlite:sqlite:2.5.2'
101 - api "androidx.sqlite:sqlite:2.2.0"
102 - api 'com.getkeepsafe.relinker:relinker:1.4.4'
103 100
104 //------------------------------ Retrofit -----------------------------// 101 //------------------------------ Retrofit -----------------------------//
105 implementation 'com.squareup.retrofit2:retrofit:2.9.0' 102 implementation 'com.squareup.retrofit2:retrofit:2.9.0'
......
...@@ -52,7 +52,6 @@ public class BaseFragmentActivity extends FragmentActivity implements Navigation ...@@ -52,7 +52,6 @@ public class BaseFragmentActivity extends FragmentActivity implements Navigation
52 mBottomNavigationView = findViewById(R.id.bt_tabs); 52 mBottomNavigationView = findViewById(R.id.bt_tabs);
53 53
54 if (WarplyDBHelper.getInstance(this).isTableNotEmpty("auth")) { 54 if (WarplyDBHelper.getInstance(this).isTableNotEmpty("auth")) {
55 - WarplyManager.getSupermarketCampaign(mCampaignsCallback);
56 WarplyManager.getRedeemedSMHistory(mSMHistoryReceiver); 55 WarplyManager.getRedeemedSMHistory(mSMHistoryReceiver);
57 } 56 }
58 57
...@@ -138,18 +137,6 @@ public class BaseFragmentActivity extends FragmentActivity implements Navigation ...@@ -138,18 +137,6 @@ public class BaseFragmentActivity extends FragmentActivity implements Navigation
138 // Inner and Anonymous Classes 137 // Inner and Anonymous Classes
139 // =========================================================== 138 // ===========================================================
140 139
141 - private final CallbackReceiver<ArrayList<Campaign>> mCampaignsCallback = new CallbackReceiver<ArrayList<Campaign>>() {
142 - @Override
143 - public void onSuccess(ArrayList<Campaign> result) {
144 - Toast.makeText(BaseFragmentActivity.this, "Campaigns Success " + String.valueOf(result.size()), Toast.LENGTH_SHORT).show();
145 - }
146 -
147 - @Override
148 - public void onFailure(int errorCode) {
149 - Toast.makeText(BaseFragmentActivity.this, "Campaigns Error", Toast.LENGTH_SHORT).show();
150 - }
151 - };
152 -
153 private final CallbackReceiver<RedeemedSMHistoryModel> mSMHistoryReceiver = new CallbackReceiver<RedeemedSMHistoryModel>() { 140 private final CallbackReceiver<RedeemedSMHistoryModel> mSMHistoryReceiver = new CallbackReceiver<RedeemedSMHistoryModel>() {
154 @Override 141 @Override
155 public void onSuccess(RedeemedSMHistoryModel result) { 142 public void onSuccess(RedeemedSMHistoryModel result) {
......
...@@ -7,32 +7,18 @@ import android.content.Context; ...@@ -7,32 +7,18 @@ import android.content.Context;
7 import android.database.Cursor; 7 import android.database.Cursor;
8 import android.database.SQLException; 8 import android.database.SQLException;
9 import android.text.TextUtils; 9 import android.text.TextUtils;
10 -import android.util.Log;
11 10
12 import androidx.annotation.Nullable; 11 import androidx.annotation.Nullable;
13 12
14 -import com.getkeepsafe.relinker.ReLinker; 13 +import android.database.DatabaseUtils;
14 +import android.database.sqlite.SQLiteDatabase;
15 +import android.database.sqlite.SQLiteOpenHelper;
15 16
16 -import net.sqlcipher.DatabaseUtils;
17 -import net.sqlcipher.database.SQLiteDatabase;
18 -import net.sqlcipher.database.SQLiteOpenHelper;
19 -import net.sqlcipher.database.SQLiteStatement;
20 -
21 -import org.json.JSONArray;
22 -import org.json.JSONObject;
23 -
24 -import java.io.File;
25 -import java.io.FileNotFoundException;
26 -import java.io.IOException;
27 import java.util.ArrayList; 17 import java.util.ArrayList;
28 import java.util.List; 18 import java.util.List;
29 -import java.util.concurrent.ExecutorService;
30 -import java.util.concurrent.Executors;
31 -import java.util.concurrent.Future;
32 -import java.util.concurrent.TimeUnit;
33 -import java.util.concurrent.TimeoutException;
34 19
35 import ly.warp.sdk.utils.constants.WarpConstants; 20 import ly.warp.sdk.utils.constants.WarpConstants;
21 +import ly.warp.sdk.utils.CryptoUtils;
36 22
37 public class WarplyDBHelper extends SQLiteOpenHelper { 23 public class WarplyDBHelper extends SQLiteOpenHelper {
38 24
...@@ -40,17 +26,8 @@ public class WarplyDBHelper extends SQLiteOpenHelper { ...@@ -40,17 +26,8 @@ public class WarplyDBHelper extends SQLiteOpenHelper {
40 // Constants 26 // Constants
41 // =========================================================== 27 // ===========================================================
42 28
43 - private enum State { 29 + private static final String DB_NAME = "warply_v2.db";
44 - DOES_NOT_EXIST, UNENCRYPTED, ENCRYPTED 30 + private static final int DB_VERSION = 1;
45 - }
46 -
47 - private static final String DB_NAME = "warply.db";
48 - private static final int DB_VERSION = 14;
49 - private static final String KEY_CIPHER = "tn#mpOl3v3Dy1pr@W";
50 -
51 - // Timeout constants
52 - private static final int CONNECTION_TIMEOUT_MS = 5000; // 5 seconds for database connection
53 - private static final int OPERATION_TIMEOUT_MS = 5000; // 5 seconds for database operations
54 31
55 //------------------------------ Fields -----------------------------// 32 //------------------------------ Fields -----------------------------//
56 private static String TABLE_REQUESTS = "requests"; 33 private static String TABLE_REQUESTS = "requests";
...@@ -64,16 +41,12 @@ public class WarplyDBHelper extends SQLiteOpenHelper { ...@@ -64,16 +41,12 @@ public class WarplyDBHelper extends SQLiteOpenHelper {
64 private static String TABLE_CLIENT = "client"; 41 private static String TABLE_CLIENT = "client";
65 private static String TABLE_AUTH = "auth"; 42 private static String TABLE_AUTH = "auth";
66 private static String TABLE_TAGS = "tags"; 43 private static String TABLE_TAGS = "tags";
67 - private static String TABLE_TELEMATICS = "telematics";
68 public static final String KEY_TAG = "tag"; 44 public static final String KEY_TAG = "tag";
69 public static final String KEY_TAG_LAST_ADD_DATE = "last_add_date"; 45 public static final String KEY_TAG_LAST_ADD_DATE = "last_add_date";
70 public static final String KEY_CLIENT_ID = "client_id"; 46 public static final String KEY_CLIENT_ID = "client_id";
71 public static final String KEY_CLIENT_SECRET = "client_secret"; 47 public static final String KEY_CLIENT_SECRET = "client_secret";
72 public static final String KEY_ACCESS_TOKEN = "access_token"; 48 public static final String KEY_ACCESS_TOKEN = "access_token";
73 public static final String KEY_REFRESH_TOKEN = "refresh_token"; 49 public static final String KEY_REFRESH_TOKEN = "refresh_token";
74 - public static final String KEY_TIMESTAMP = "timestamp";
75 - public static final String KEY_ACCELERATION = "acceleration";
76 - public static final String KEY_SPEED = "speed";
77 50
78 //------------------------------ Tables -----------------------------// 51 //------------------------------ Tables -----------------------------//
79 public static final String CREATE_TABLE_REQUESTS = "create table if not exists " 52 public static final String CREATE_TABLE_REQUESTS = "create table if not exists "
...@@ -115,12 +88,6 @@ public class WarplyDBHelper extends SQLiteOpenHelper { ...@@ -115,12 +88,6 @@ public class WarplyDBHelper extends SQLiteOpenHelper {
115 + KEY_ACCESS_TOKEN + " text, " 88 + KEY_ACCESS_TOKEN + " text, "
116 + KEY_REFRESH_TOKEN + " text)"; 89 + KEY_REFRESH_TOKEN + " text)";
117 90
118 - public static final String CREATE_TABLE_TELEMATICS = "create table if not exists "
119 - + TABLE_TELEMATICS + " ("
120 - + KEY_TIMESTAMP + " text, "
121 - + KEY_ACCELERATION + " real, "
122 - + KEY_SPEED + " real)";
123 -
124 // =========================================================== 91 // ===========================================================
125 // Fields 92 // Fields
126 // =========================================================== 93 // ===========================================================
...@@ -128,21 +95,12 @@ public class WarplyDBHelper extends SQLiteOpenHelper { ...@@ -128,21 +95,12 @@ public class WarplyDBHelper extends SQLiteOpenHelper {
128 private SQLiteDatabase mDb; 95 private SQLiteDatabase mDb;
129 private static WarplyDBHelper mDBHelperInstance; 96 private static WarplyDBHelper mDBHelperInstance;
130 97
131 - // Single-threaded executor for database operations
132 - private final ExecutorService dbExecutor = Executors.newSingleThreadExecutor();
133 -
134 // =========================================================== 98 // ===========================================================
135 // Constructors 99 // Constructors
136 // =========================================================== 100 // ===========================================================
137 101
138 public static synchronized WarplyDBHelper getInstance(Context context) { 102 public static synchronized WarplyDBHelper getInstance(Context context) {
139 if (mDBHelperInstance == null) { 103 if (mDBHelperInstance == null) {
140 -// SQLiteDatabase.loadLibs(context); //old implementation
141 - SQLiteDatabase.loadLibs(context, libraries -> {
142 - for (String library : libraries) {
143 - ReLinker.loadLibrary(context, library);
144 - }
145 - });
146 mDBHelperInstance = new WarplyDBHelper(context); 104 mDBHelperInstance = new WarplyDBHelper(context);
147 } 105 }
148 return mDBHelperInstance; 106 return mDBHelperInstance;
...@@ -150,141 +108,26 @@ public class WarplyDBHelper extends SQLiteOpenHelper { ...@@ -150,141 +108,26 @@ public class WarplyDBHelper extends SQLiteOpenHelper {
150 108
151 private WarplyDBHelper(Context context) { 109 private WarplyDBHelper(Context context) {
152 super(context, DB_NAME, null, DB_VERSION); 110 super(context, DB_NAME, null, DB_VERSION);
153 - State tempDatabaseState = getDatabaseState(context, DB_NAME);
154 - if (tempDatabaseState.equals(State.UNENCRYPTED)) {
155 - encrypt(context, context.getDatabasePath(DB_NAME), KEY_CIPHER.getBytes());
156 - }
157 } 111 }
158 112
159 /** 113 /**
160 - * Gets a writable database connection on a background thread with timeout. 114 + * Get standard SQLite database connection
161 - * This prevents ANR issues by ensuring database operations don't block the main thread.
162 */ 115 */
163 private SQLiteDatabase getDb() { 116 private SQLiteDatabase getDb() {
164 if (mDb == null || !mDb.isOpen()) { 117 if (mDb == null || !mDb.isOpen()) {
165 - try { 118 + mDb = getWritableDatabase();
166 - // Submit task to executor and get future
167 - Future<SQLiteDatabase> future = dbExecutor.submit(() -> getWritableDatabase(KEY_CIPHER));
168 -
169 - // Wait for result with timeout
170 - mDb = future.get(CONNECTION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
171 -
172 - // Set busy timeout on the database connection
173 - if (mDb != null && mDb.isOpen()) {
174 - mDb.rawExecSQL("PRAGMA busy_timeout = " + CONNECTION_TIMEOUT_MS);
175 - }
176 - } catch (TimeoutException e) {
177 - Log.e("WarplyDBHelper", "Timeout getting writable database connection", e);
178 - // Return null or handle timeout as appropriate
179 - } catch (Exception e) {
180 - Log.e("WarplyDBHelper", "Error getting writable database connection", e);
181 - // Handle other exceptions
182 - }
183 - }
184 - return mDb;
185 - }
186 -
187 - /**
188 - * Gets a readable database connection on a background thread with timeout.
189 - * This prevents ANR issues by ensuring database operations don't block the main thread.
190 - */
191 - private SQLiteDatabase getReadableDb() {
192 - if (mDb == null || !mDb.isOpen()) {
193 - try {
194 - // Submit task to executor and get future
195 - Future<SQLiteDatabase> future = dbExecutor.submit(() -> getReadableDatabase(KEY_CIPHER));
196 -
197 - // Wait for result with timeout
198 - mDb = future.get(CONNECTION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
199 -
200 - // Set busy timeout on the database connection
201 - if (mDb != null && mDb.isOpen()) {
202 - mDb.rawExecSQL("PRAGMA busy_timeout = " + CONNECTION_TIMEOUT_MS);
203 - }
204 - } catch (TimeoutException e) {
205 - Log.e("WarplyDBHelper", "Timeout getting readable database connection", e);
206 - // Return null or handle timeout as appropriate
207 - } catch (Exception e) {
208 - Log.e("WarplyDBHelper", "Error getting readable database connection", e);
209 - // Handle other exceptions
210 - }
211 } 119 }
212 return mDb; 120 return mDb;
213 } 121 }
214 122
215 - /**
216 - * Close database connection - should only be called when app is being destroyed
217 - * or when database won't be used for a long time
218 - */
219 - private void closeDb() {
220 - if (mDb != null && mDb.isOpen()) {
221 - mDb.close();
222 - mDb = null;
223 - }
224 - }
225 -
226 - /**
227 - * Initialize database connection on a background thread.
228 - * Call this during app startup to prevent ANR issues.
229 - */
230 - public void initialize() {
231 - if (mDb == null || !mDb.isOpen()) {
232 - try {
233 - // Submit task to executor and get future
234 - Future<SQLiteDatabase> future = dbExecutor.submit(() -> getWritableDatabase(KEY_CIPHER));
235 -
236 - // Wait for result with timeout
237 - mDb = future.get(CONNECTION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
238 -
239 - // Set busy timeout on the database connection
240 - if (mDb != null && mDb.isOpen()) {
241 - mDb.rawExecSQL("PRAGMA busy_timeout = " + CONNECTION_TIMEOUT_MS);
242 - }
243 - } catch (TimeoutException e) {
244 - Log.e("WarplyDBHelper", "Timeout initializing database connection", e);
245 - } catch (Exception e) {
246 - Log.e("WarplyDBHelper", "Error initializing database connection", e);
247 - }
248 - }
249 - }
250 -
251 - /**
252 - * Shutdown database connection and executor service.
253 - * Call this when app is being destroyed.
254 - */
255 - public void shutdown() {
256 - closeDb();
257 -
258 - // Shutdown executor service
259 - dbExecutor.shutdown();
260 - try {
261 - if (!dbExecutor.awaitTermination(5, TimeUnit.SECONDS)) {
262 - dbExecutor.shutdownNow();
263 - }
264 - } catch (InterruptedException e) {
265 - dbExecutor.shutdownNow();
266 - Thread.currentThread().interrupt();
267 - }
268 - }
269 -
270 @Override 123 @Override
271 public void onCreate(SQLiteDatabase db) { 124 public void onCreate(SQLiteDatabase db) {
272 - // Create tables
273 db.execSQL(CREATE_TABLE_REQUESTS); 125 db.execSQL(CREATE_TABLE_REQUESTS);
274 db.execSQL(CREATE_TABLE_TAGS); 126 db.execSQL(CREATE_TABLE_TAGS);
275 db.execSQL(CREATE_TABLE_PUSH_REQUESTS); 127 db.execSQL(CREATE_TABLE_PUSH_REQUESTS);
276 db.execSQL(CREATE_TABLE_PUSH_ACK_REQUESTS); 128 db.execSQL(CREATE_TABLE_PUSH_ACK_REQUESTS);
277 db.execSQL(CREATE_TABLE_CLIENT); 129 db.execSQL(CREATE_TABLE_CLIENT);
278 db.execSQL(CREATE_TABLE_AUTH); 130 db.execSQL(CREATE_TABLE_AUTH);
279 - db.execSQL(CREATE_TABLE_TELEMATICS);
280 -
281 - // Create indexes for frequently queried columns to improve performance
282 - db.execSQL("CREATE INDEX IF NOT EXISTS idx_requests_force ON " + TABLE_REQUESTS + "(" + KEY_REQUESTS_FORCE + ")");
283 - db.execSQL("CREATE INDEX IF NOT EXISTS idx_requests_date ON " + TABLE_REQUESTS + "(" + KEY_REQUESTS_DATE_ADDED + ")");
284 - db.execSQL("CREATE INDEX IF NOT EXISTS idx_push_requests_force ON " + TABLE_PUSH_REQUESTS + "(" + KEY_REQUESTS_FORCE + ")");
285 - db.execSQL("CREATE INDEX IF NOT EXISTS idx_push_requests_date ON " + TABLE_PUSH_REQUESTS + "(" + KEY_REQUESTS_DATE_ADDED + ")");
286 - db.execSQL("CREATE INDEX IF NOT EXISTS idx_push_ack_requests_force ON " + TABLE_PUSH_ACK_REQUESTS + "(" + KEY_REQUESTS_FORCE + ")");
287 - db.execSQL("CREATE INDEX IF NOT EXISTS idx_push_ack_requests_date ON " + TABLE_PUSH_ACK_REQUESTS + "(" + KEY_REQUESTS_DATE_ADDED + ")");
288 } 131 }
289 132
290 @Override 133 @Override
...@@ -297,7 +140,6 @@ public class WarplyDBHelper extends SQLiteOpenHelper { ...@@ -297,7 +140,6 @@ public class WarplyDBHelper extends SQLiteOpenHelper {
297 db.execSQL("drop table if exists " + TABLE_TAGS); 140 db.execSQL("drop table if exists " + TABLE_TAGS);
298 db.execSQL("drop table if exists " + TABLE_CLIENT); 141 db.execSQL("drop table if exists " + TABLE_CLIENT);
299 db.execSQL("drop table if exists " + TABLE_AUTH); 142 db.execSQL("drop table if exists " + TABLE_AUTH);
300 - db.execSQL("drop table if exists " + TABLE_TELEMATICS);
301 onCreate(db); 143 onCreate(db);
302 } 144 }
303 } 145 }
...@@ -310,7 +152,6 @@ public class WarplyDBHelper extends SQLiteOpenHelper { ...@@ -310,7 +152,6 @@ public class WarplyDBHelper extends SQLiteOpenHelper {
310 db.execSQL("drop table if exists " + TABLE_TAGS); 152 db.execSQL("drop table if exists " + TABLE_TAGS);
311 db.execSQL("drop table if exists " + TABLE_CLIENT); 153 db.execSQL("drop table if exists " + TABLE_CLIENT);
312 db.execSQL("drop table if exists " + TABLE_AUTH); 154 db.execSQL("drop table if exists " + TABLE_AUTH);
313 - db.execSQL("drop table if exists " + TABLE_TELEMATICS);
314 onCreate(db); 155 onCreate(db);
315 } 156 }
316 157
...@@ -337,45 +178,36 @@ public class WarplyDBHelper extends SQLiteOpenHelper { ...@@ -337,45 +178,36 @@ public class WarplyDBHelper extends SQLiteOpenHelper {
337 public synchronized void clearTable(String tableName) { 178 public synchronized void clearTable(String tableName) {
338 SQLiteDatabase db = getDb(); 179 SQLiteDatabase db = getDb();
339 db.delete(tableName, null, null); 180 db.delete(tableName, null, null);
340 - // Don't close the database here to improve performance
341 } 181 }
342 182
343 public synchronized void insert(String tableName, ContentValues values) { 183 public synchronized void insert(String tableName, ContentValues values) {
344 SQLiteDatabase db = getDb(); 184 SQLiteDatabase db = getDb();
345 db.insert(tableName, null, values); 185 db.insert(tableName, null, values);
346 - // Don't close the database here to improve performance
347 } 186 }
348 187
349 public synchronized void update(String tableName, ContentValues values) { 188 public synchronized void update(String tableName, ContentValues values) {
350 SQLiteDatabase db = getDb(); 189 SQLiteDatabase db = getDb();
351 db.update(tableName, values, null, null); 190 db.update(tableName, values, null, null);
352 - // Don't close the database here to improve performance
353 } 191 }
354 192
355 public synchronized boolean isTableNotEmpty(String tableName) { 193 public synchronized boolean isTableNotEmpty(String tableName) {
356 - boolean isNotEmpty = false; 194 + Cursor cursor = getDb().rawQuery("SELECT COUNT(*) FROM " + tableName,
357 - Cursor cursor = null; 195 + null);
358 - try {
359 - cursor = getReadableDb().rawQuery("SELECT COUNT(*) FROM " + tableName, null);
360 if (cursor != null && cursor.moveToFirst()) { 196 if (cursor != null && cursor.moveToFirst()) {
361 - isNotEmpty = cursor.getInt(0) > 0; 197 + boolean isNotEmpty = cursor.getInt(0) > 0;
362 - }
363 - } finally {
364 - if (cursor != null) {
365 cursor.close(); 198 cursor.close();
366 - }
367 - // Don't close the database here to improve performance
368 - }
369 return isNotEmpty; 199 return isNotEmpty;
200 + } else
201 + return false;
370 } 202 }
371 203
372 - //------------------------------ Auth -----------------------------// 204 + //------------------------------ Auth (with field-level encryption) -----------------------------//
373 public synchronized void saveClientAccess(String clientId, String clientSecret) { 205 public synchronized void saveClientAccess(String clientId, String clientSecret) {
374 ContentValues values = new ContentValues(); 206 ContentValues values = new ContentValues();
375 if (!TextUtils.isEmpty(clientId)) 207 if (!TextUtils.isEmpty(clientId))
376 - values.put(KEY_CLIENT_ID, clientId); 208 + values.put(KEY_CLIENT_ID, CryptoUtils.encrypt(clientId));
377 if (!TextUtils.isEmpty(clientSecret)) 209 if (!TextUtils.isEmpty(clientSecret))
378 - values.put(KEY_CLIENT_SECRET, clientSecret); 210 + values.put(KEY_CLIENT_SECRET, CryptoUtils.encrypt(clientSecret));
379 if (isTableNotEmpty(TABLE_CLIENT)) 211 if (isTableNotEmpty(TABLE_CLIENT))
380 update(TABLE_CLIENT, values); 212 update(TABLE_CLIENT, values);
381 else 213 else
...@@ -385,9 +217,9 @@ public class WarplyDBHelper extends SQLiteOpenHelper { ...@@ -385,9 +217,9 @@ public class WarplyDBHelper extends SQLiteOpenHelper {
385 public synchronized void saveAuthAccess(String accessToken, String refreshToken) { 217 public synchronized void saveAuthAccess(String accessToken, String refreshToken) {
386 ContentValues values = new ContentValues(); 218 ContentValues values = new ContentValues();
387 if (!TextUtils.isEmpty(accessToken)) 219 if (!TextUtils.isEmpty(accessToken))
388 - values.put(KEY_ACCESS_TOKEN, accessToken); 220 + values.put(KEY_ACCESS_TOKEN, CryptoUtils.encrypt(accessToken));
389 if (!TextUtils.isEmpty(refreshToken)) 221 if (!TextUtils.isEmpty(refreshToken))
390 - values.put(KEY_REFRESH_TOKEN, refreshToken); 222 + values.put(KEY_REFRESH_TOKEN, CryptoUtils.encrypt(refreshToken));
391 if (isTableNotEmpty(TABLE_AUTH)) 223 if (isTableNotEmpty(TABLE_AUTH))
392 update(TABLE_AUTH, values); 224 update(TABLE_AUTH, values);
393 else 225 else
...@@ -397,36 +229,24 @@ public class WarplyDBHelper extends SQLiteOpenHelper { ...@@ -397,36 +229,24 @@ public class WarplyDBHelper extends SQLiteOpenHelper {
397 @Nullable 229 @Nullable
398 public synchronized String getAuthValue(String columnName) { 230 public synchronized String getAuthValue(String columnName) {
399 String columnValue = ""; 231 String columnValue = "";
400 - Cursor cursor = null; 232 + Cursor cursor = getDb().query(TABLE_AUTH, new String[]{columnName}, null, null, null, null, null);
401 - try {
402 - cursor = getReadableDb().query(TABLE_AUTH, new String[]{columnName}, null, null, null, null, null);
403 if (cursor != null && cursor.moveToFirst()) { 233 if (cursor != null && cursor.moveToFirst()) {
404 - columnValue = cursor.getString(cursor.getColumnIndex(columnName)); 234 + String encryptedValue = cursor.getString(cursor.getColumnIndex(columnName));
405 - } 235 + columnValue = CryptoUtils.decrypt(encryptedValue);
406 - } finally {
407 - if (cursor != null) {
408 cursor.close(); 236 cursor.close();
409 } 237 }
410 - // Don't close the database here to improve performance
411 - }
412 return columnValue; 238 return columnValue;
413 } 239 }
414 240
415 @Nullable 241 @Nullable
416 public synchronized String getClientValue(String columnName) { 242 public synchronized String getClientValue(String columnName) {
417 String columnValue = ""; 243 String columnValue = "";
418 - Cursor cursor = null; 244 + Cursor cursor = getDb().query(TABLE_CLIENT, new String[]{columnName}, null, null, null, null, null);
419 - try {
420 - cursor = getReadableDb().query(TABLE_CLIENT, new String[]{columnName}, null, null, null, null, null);
421 if (cursor != null && cursor.moveToFirst()) { 245 if (cursor != null && cursor.moveToFirst()) {
422 - columnValue = cursor.getString(cursor.getColumnIndex(columnName)); 246 + String encryptedValue = cursor.getString(cursor.getColumnIndex(columnName));
423 - } 247 + columnValue = CryptoUtils.decrypt(encryptedValue);
424 - } finally {
425 - if (cursor != null) {
426 cursor.close(); 248 cursor.close();
427 } 249 }
428 - // Don't close the database here to improve performance
429 - }
430 return columnValue; 250 return columnValue;
431 } 251 }
432 252
...@@ -439,37 +259,25 @@ public class WarplyDBHelper extends SQLiteOpenHelper { ...@@ -439,37 +259,25 @@ public class WarplyDBHelper extends SQLiteOpenHelper {
439 } 259 }
440 260
441 //------------------------------ Api requests -----------------------------// 261 //------------------------------ Api requests -----------------------------//
442 - /**
443 - * Gets all requests from the database.
444 - * NOTE: The caller is responsible for closing the returned Cursor when done with it.
445 - */
446 public synchronized Cursor getAllRequests() { 262 public synchronized Cursor getAllRequests() {
447 - // We don't close the database here because the cursor needs it to remain open 263 +
448 - return getReadableDb().query(TABLE_REQUESTS, 264 + return getDb().query(TABLE_REQUESTS,
449 new String[]{KEY_REQUESTS_ID, 265 new String[]{KEY_REQUESTS_ID,
450 KEY_REQUESTS_MICROAPP, KEY_REQUESTS_ENTITY}, null, null, null, null, 266 KEY_REQUESTS_MICROAPP, KEY_REQUESTS_ENTITY}, null, null, null, null,
451 KEY_REQUESTS_DATE_ADDED + " asc", "20"); 267 KEY_REQUESTS_DATE_ADDED + " asc", "20");
452 } 268 }
453 269
454 - /**
455 - * Gets all push requests from the database.
456 - * NOTE: The caller is responsible for closing the returned Cursor when done with it.
457 - */
458 public synchronized Cursor getAllPushRequests() { 270 public synchronized Cursor getAllPushRequests() {
459 - // We don't close the database here because the cursor needs it to remain open 271 +
460 - return getReadableDb().query(TABLE_PUSH_REQUESTS, 272 + return getDb().query(TABLE_PUSH_REQUESTS,
461 new String[]{KEY_REQUESTS_ID, 273 new String[]{KEY_REQUESTS_ID,
462 KEY_REQUESTS_MICROAPP, KEY_REQUESTS_ENTITY}, null, null, null, null, 274 KEY_REQUESTS_MICROAPP, KEY_REQUESTS_ENTITY}, null, null, null, null,
463 KEY_REQUESTS_DATE_ADDED + " asc", "20"); 275 KEY_REQUESTS_DATE_ADDED + " asc", "20");
464 } 276 }
465 277
466 - /**
467 - * Gets all push acknowledgment requests from the database.
468 - * NOTE: The caller is responsible for closing the returned Cursor when done with it.
469 - */
470 public synchronized Cursor getAllPushAckRequests() { 278 public synchronized Cursor getAllPushAckRequests() {
471 - // We don't close the database here because the cursor needs it to remain open 279 +
472 - return getReadableDb().query(TABLE_PUSH_ACK_REQUESTS, 280 + return getDb().query(TABLE_PUSH_ACK_REQUESTS,
473 new String[]{KEY_REQUESTS_ID, 281 new String[]{KEY_REQUESTS_ID,
474 KEY_REQUESTS_MICROAPP, KEY_REQUESTS_ENTITY}, null, null, null, null, 282 KEY_REQUESTS_MICROAPP, KEY_REQUESTS_ENTITY}, null, null, null, null,
475 KEY_REQUESTS_DATE_ADDED + " asc", "20"); 283 KEY_REQUESTS_DATE_ADDED + " asc", "20");
...@@ -488,6 +296,7 @@ public class WarplyDBHelper extends SQLiteOpenHelper { ...@@ -488,6 +296,7 @@ public class WarplyDBHelper extends SQLiteOpenHelper {
488 } 296 }
489 297
490 public synchronized long addRequest(String microapp, String entity, boolean force) { 298 public synchronized long addRequest(String microapp, String entity, boolean force) {
299 +
491 ContentValues values = new ContentValues(); 300 ContentValues values = new ContentValues();
492 values.put(KEY_REQUESTS_MICROAPP, microapp); 301 values.put(KEY_REQUESTS_MICROAPP, microapp);
493 values.put(KEY_REQUESTS_ENTITY, entity); 302 values.put(KEY_REQUESTS_ENTITY, entity);
...@@ -498,6 +307,7 @@ public class WarplyDBHelper extends SQLiteOpenHelper { ...@@ -498,6 +307,7 @@ public class WarplyDBHelper extends SQLiteOpenHelper {
498 } 307 }
499 308
500 public synchronized long addPushRequest(String microapp, String entity, boolean force) { 309 public synchronized long addPushRequest(String microapp, String entity, boolean force) {
310 +
501 ContentValues values = new ContentValues(); 311 ContentValues values = new ContentValues();
502 values.put(KEY_REQUESTS_MICROAPP, microapp); 312 values.put(KEY_REQUESTS_MICROAPP, microapp);
503 values.put(KEY_REQUESTS_ENTITY, entity); 313 values.put(KEY_REQUESTS_ENTITY, entity);
...@@ -508,6 +318,7 @@ public class WarplyDBHelper extends SQLiteOpenHelper { ...@@ -508,6 +318,7 @@ public class WarplyDBHelper extends SQLiteOpenHelper {
508 } 318 }
509 319
510 public synchronized long addPushAckRequest(String microapp, String entity, boolean force) { 320 public synchronized long addPushAckRequest(String microapp, String entity, boolean force) {
321 +
511 ContentValues values = new ContentValues(); 322 ContentValues values = new ContentValues();
512 values.put(KEY_REQUESTS_MICROAPP, microapp); 323 values.put(KEY_REQUESTS_MICROAPP, microapp);
513 values.put(KEY_REQUESTS_ENTITY, entity); 324 values.put(KEY_REQUESTS_ENTITY, entity);
...@@ -518,32 +329,19 @@ public class WarplyDBHelper extends SQLiteOpenHelper { ...@@ -518,32 +329,19 @@ public class WarplyDBHelper extends SQLiteOpenHelper {
518 } 329 }
519 330
520 public synchronized long getRequestsInQueueCount() { 331 public synchronized long getRequestsInQueueCount() {
521 - SQLiteDatabase db = getReadableDb(); 332 + return DatabaseUtils.queryNumEntries(getReadableDatabase(), TABLE_REQUESTS);
522 - long count = DatabaseUtils.queryNumEntries(db, TABLE_REQUESTS);
523 - // Don't close the database here to improve performance
524 - return count;
525 } 333 }
526 334
527 public synchronized long getPushRequestsInQueueCount() { 335 public synchronized long getPushRequestsInQueueCount() {
528 - SQLiteDatabase db = getReadableDb(); 336 + return DatabaseUtils.queryNumEntries(getReadableDatabase(), TABLE_PUSH_REQUESTS);
529 - long count = DatabaseUtils.queryNumEntries(db, TABLE_PUSH_REQUESTS);
530 - // Don't close the database here to improve performance
531 - return count;
532 } 337 }
533 338
534 public synchronized long getPushAckRequestsInQueueCount() { 339 public synchronized long getPushAckRequestsInQueueCount() {
535 - SQLiteDatabase db = getReadableDb(); 340 + return DatabaseUtils.queryNumEntries(getReadableDatabase(), TABLE_PUSH_ACK_REQUESTS);
536 - long count = DatabaseUtils.queryNumEntries(db, TABLE_PUSH_ACK_REQUESTS);
537 - // Don't close the database here to improve performance
538 - return count;
539 } 341 }
540 342
541 public synchronized void deleteRequests(Long... ids) { 343 public synchronized void deleteRequests(Long... ids) {
542 - if (ids.length == 0) return;
543 344
544 - SQLiteDatabase db = getDb();
545 - try {
546 - db.beginTransaction();
547 StringBuilder strFilter = new StringBuilder(); 345 StringBuilder strFilter = new StringBuilder();
548 for (int i = 0; i < ids.length; i++) { 346 for (int i = 0; i < ids.length; i++) {
549 if (i > 0) { 347 if (i > 0) {
...@@ -553,20 +351,11 @@ public class WarplyDBHelper extends SQLiteOpenHelper { ...@@ -553,20 +351,11 @@ public class WarplyDBHelper extends SQLiteOpenHelper {
553 strFilter.append("="); 351 strFilter.append("=");
554 strFilter.append(ids[i]); 352 strFilter.append(ids[i]);
555 } 353 }
556 - db.delete(TABLE_REQUESTS, strFilter.toString(), null); 354 + getDb().delete(TABLE_REQUESTS, strFilter.toString(), null);
557 - db.setTransactionSuccessful();
558 - } finally {
559 - db.endTransaction();
560 - // Don't close the database here to improve performance
561 - }
562 } 355 }
563 356
564 public synchronized void deletePushRequests(Long... ids) { 357 public synchronized void deletePushRequests(Long... ids) {
565 - if (ids.length == 0) return;
566 358
567 - SQLiteDatabase db = getDb();
568 - try {
569 - db.beginTransaction();
570 StringBuilder strFilter = new StringBuilder(); 359 StringBuilder strFilter = new StringBuilder();
571 for (int i = 0; i < ids.length; i++) { 360 for (int i = 0; i < ids.length; i++) {
572 if (i > 0) { 361 if (i > 0) {
...@@ -576,20 +365,11 @@ public class WarplyDBHelper extends SQLiteOpenHelper { ...@@ -576,20 +365,11 @@ public class WarplyDBHelper extends SQLiteOpenHelper {
576 strFilter.append("="); 365 strFilter.append("=");
577 strFilter.append(ids[i]); 366 strFilter.append(ids[i]);
578 } 367 }
579 - db.delete(TABLE_PUSH_REQUESTS, strFilter.toString(), null); 368 + getDb().delete(TABLE_PUSH_REQUESTS, strFilter.toString(), null);
580 - db.setTransactionSuccessful();
581 - } finally {
582 - db.endTransaction();
583 - // Don't close the database here to improve performance
584 - }
585 } 369 }
586 370
587 public synchronized void deletePushAckRequests(Long... ids) { 371 public synchronized void deletePushAckRequests(Long... ids) {
588 - if (ids.length == 0) return;
589 372
590 - SQLiteDatabase db = getDb();
591 - try {
592 - db.beginTransaction();
593 StringBuilder strFilter = new StringBuilder(); 373 StringBuilder strFilter = new StringBuilder();
594 for (int i = 0; i < ids.length; i++) { 374 for (int i = 0; i < ids.length; i++) {
595 if (i > 0) { 375 if (i > 0) {
...@@ -599,127 +379,69 @@ public class WarplyDBHelper extends SQLiteOpenHelper { ...@@ -599,127 +379,69 @@ public class WarplyDBHelper extends SQLiteOpenHelper {
599 strFilter.append("="); 379 strFilter.append("=");
600 strFilter.append(ids[i]); 380 strFilter.append(ids[i]);
601 } 381 }
602 - db.delete(TABLE_PUSH_ACK_REQUESTS, strFilter.toString(), null); 382 + getDb().delete(TABLE_PUSH_ACK_REQUESTS, strFilter.toString(), null);
603 - db.setTransactionSuccessful();
604 - } finally {
605 - db.endTransaction();
606 - // Don't close the database here to improve performance
607 - }
608 } 383 }
609 384
610 public synchronized boolean isForceRequestsExist() { 385 public synchronized boolean isForceRequestsExist() {
611 - boolean result = false; 386 + Cursor cursor = getDb().query(TABLE_REQUESTS, null, KEY_REQUESTS_FORCE + "=1",
612 - Cursor cursor = null;
613 - try {
614 - cursor = getReadableDb().query(TABLE_REQUESTS, null, KEY_REQUESTS_FORCE + "=1",
615 null, null, null, null); 387 null, null, null, null);
388 + boolean result = false;
616 if (cursor != null) { 389 if (cursor != null) {
617 result = cursor.getCount() > 0; 390 result = cursor.getCount() > 0;
618 - }
619 - } finally {
620 - if (cursor != null) {
621 cursor.close(); 391 cursor.close();
622 } 392 }
623 - // Don't close the database here to improve performance
624 - }
625 return result; 393 return result;
626 } 394 }
627 395
628 public synchronized boolean isForcePushRequestsExist() { 396 public synchronized boolean isForcePushRequestsExist() {
629 - boolean result = false; 397 + Cursor cursor = getDb().query(TABLE_PUSH_REQUESTS, null, KEY_REQUESTS_FORCE + "=1",
630 - Cursor cursor = null;
631 - try {
632 - cursor = getReadableDb().query(TABLE_PUSH_REQUESTS, null, KEY_REQUESTS_FORCE + "=1",
633 null, null, null, null); 398 null, null, null, null);
399 + boolean result = false;
634 if (cursor != null) { 400 if (cursor != null) {
635 result = cursor.getCount() > 0; 401 result = cursor.getCount() > 0;
636 - }
637 - } finally {
638 - if (cursor != null) {
639 cursor.close(); 402 cursor.close();
640 } 403 }
641 - // Don't close the database here to improve performance
642 - }
643 return result; 404 return result;
644 } 405 }
645 406
646 public synchronized boolean isForcePushAckRequestsExist() { 407 public synchronized boolean isForcePushAckRequestsExist() {
647 - boolean result = false; 408 + Cursor cursor = getDb().query(TABLE_PUSH_ACK_REQUESTS, null, KEY_REQUESTS_FORCE + "=1",
648 - Cursor cursor = null;
649 - try {
650 - cursor = getReadableDb().query(TABLE_PUSH_ACK_REQUESTS, null, KEY_REQUESTS_FORCE + "=1",
651 null, null, null, null); 409 null, null, null, null);
410 + boolean result = false;
652 if (cursor != null) { 411 if (cursor != null) {
653 result = cursor.getCount() > 0; 412 result = cursor.getCount() > 0;
654 - }
655 - } finally {
656 - if (cursor != null) {
657 cursor.close(); 413 cursor.close();
658 } 414 }
659 - // Don't close the database here to improve performance
660 - }
661 return result; 415 return result;
662 } 416 }
663 417
664 //------------------------------ Tags -----------------------------// 418 //------------------------------ Tags -----------------------------//
665 public synchronized void saveTags(String[] tags) { 419 public synchronized void saveTags(String[] tags) {
420 +
666 if (tags != null && tags.length > 0) { 421 if (tags != null && tags.length > 0) {
667 - SQLiteDatabase db = getDb(); 422 +
668 try { 423 try {
669 - db.beginTransaction(); 424 + getDb().beginTransaction();
670 ContentValues values = new ContentValues(); 425 ContentValues values = new ContentValues();
671 for (String tag : tags) { 426 for (String tag : tags) {
672 - values.clear();
673 values.put(KEY_TAG, tag); 427 values.put(KEY_TAG, tag);
674 values.put(KEY_TAG_LAST_ADD_DATE, System.currentTimeMillis()); 428 values.put(KEY_TAG_LAST_ADD_DATE, System.currentTimeMillis());
675 - db.insert(TABLE_TAGS, null, values); 429 + insert(TABLE_TAGS, values);
676 } 430 }
677 - db.setTransactionSuccessful(); 431 + getDb().setTransactionSuccessful();
432 +
678 } catch (SQLException e) { 433 } catch (SQLException e) {
679 if (WarpConstants.DEBUG) { 434 if (WarpConstants.DEBUG) {
680 e.printStackTrace(); 435 e.printStackTrace();
681 } 436 }
682 } finally { 437 } finally {
683 - db.endTransaction(); 438 + getDb().endTransaction();
684 - // Don't close the database here to improve performance
685 - }
686 - }
687 - }
688 -
689 - public synchronized void saveTelematics(JSONArray jsonArray) {
690 - if (jsonArray != null && jsonArray.length() > 0) {
691 - SQLiteDatabase db = getDb();
692 - try {
693 - db.beginTransaction();
694 - ContentValues values = new ContentValues();
695 - for (int i = 0; i < jsonArray.length(); i++) {
696 - JSONObject jsonobject = jsonArray.optJSONObject(i);
697 - if (jsonobject != null) {
698 - values.clear();
699 - String timestamp = jsonobject.keys().next();
700 - values.put(KEY_TIMESTAMP, timestamp);
701 - JSONObject jobjData = jsonobject.optJSONObject(timestamp);
702 - if (jobjData != null) {
703 - values.put(KEY_ACCELERATION, jobjData.optDouble(KEY_ACCELERATION));
704 - values.put(KEY_SPEED, jobjData.optDouble(KEY_SPEED));
705 - }
706 - db.insert(TABLE_TELEMATICS, null, values);
707 - }
708 - }
709 - db.setTransactionSuccessful();
710 - } finally {
711 - db.endTransaction();
712 - // Don't close the database here to improve performance
713 } 439 }
714 } 440 }
715 } 441 }
716 442
717 public synchronized void removeTags(String[] tags) { 443 public synchronized void removeTags(String[] tags) {
718 - if (tags.length == 0) return;
719 444
720 - SQLiteDatabase db = getDb();
721 - try {
722 - db.beginTransaction();
723 StringBuilder strFilter = new StringBuilder(); 445 StringBuilder strFilter = new StringBuilder();
724 for (int i = 0; i < tags.length; i++) { 446 for (int i = 0; i < tags.length; i++) {
725 if (i > 0) { 447 if (i > 0) {
...@@ -731,12 +453,7 @@ public class WarplyDBHelper extends SQLiteOpenHelper { ...@@ -731,12 +453,7 @@ public class WarplyDBHelper extends SQLiteOpenHelper {
731 strFilter.append(tags[i]); 453 strFilter.append(tags[i]);
732 strFilter.append("'"); 454 strFilter.append("'");
733 } 455 }
734 - db.delete(TABLE_TAGS, strFilter.toString(), null); 456 + getDb().delete(TABLE_TAGS, strFilter.toString(), null);
735 - db.setTransactionSuccessful();
736 - } finally {
737 - db.endTransaction();
738 - // Don't close the database here to improve performance
739 - }
740 } 457 }
741 458
742 public synchronized void removeAllTags() { 459 public synchronized void removeAllTags() {
...@@ -745,84 +462,18 @@ public class WarplyDBHelper extends SQLiteOpenHelper { ...@@ -745,84 +462,18 @@ public class WarplyDBHelper extends SQLiteOpenHelper {
745 462
746 @Nullable 463 @Nullable
747 public synchronized String[] getTags() { 464 public synchronized String[] getTags() {
465 +
748 List<String> tags = null; 466 List<String> tags = null;
749 - Cursor cursor = null; 467 + Cursor cursor = getDb().query(TABLE_TAGS, null, null, null, null, null, null);
750 - try {
751 - cursor = getReadableDb().query(TABLE_TAGS, null, null, null, null, null, null);
752 if (cursor != null) { 468 if (cursor != null) {
469 +
753 tags = new ArrayList<>(cursor.getCount()); 470 tags = new ArrayList<>(cursor.getCount());
754 while (cursor.moveToNext()) { 471 while (cursor.moveToNext()) {
755 tags.add(cursor.getString(cursor.getColumnIndex(KEY_TAG))); 472 tags.add(cursor.getString(cursor.getColumnIndex(KEY_TAG)));
756 } 473 }
757 - }
758 - } finally {
759 - if (cursor != null) {
760 cursor.close(); 474 cursor.close();
761 } 475 }
762 - // Don't close the database here to improve performance
763 - }
764 return tags != null ? tags.toArray(new String[tags.size()]) : null; 476 return tags != null ? tags.toArray(new String[tags.size()]) : null;
765 } 477 }
766 478
767 - private State getDatabaseState(Context context, String dbName) {
768 - SQLiteDatabase.loadLibs(context);
769 -
770 - return (getDatabaseState(context.getDatabasePath(dbName)));
771 - }
772 -
773 - private static State getDatabaseState(File dbPath) {
774 - if (dbPath.exists()) {
775 - SQLiteDatabase db = null;
776 - try {
777 - db = SQLiteDatabase.openDatabase(dbPath.getAbsolutePath(), "", null, SQLiteDatabase.OPEN_READONLY);
778 - db.getVersion();
779 -
780 - return (State.UNENCRYPTED);
781 - } catch (Exception e) {
782 - return (State.ENCRYPTED);
783 - } finally {
784 - if (db != null) {
785 - db.close();
786 - }
787 - }
788 - }
789 -
790 - return (State.DOES_NOT_EXIST);
791 - }
792 -
793 - private void encrypt(Context context, File originalFile, byte[] passphrase) {
794 - SQLiteDatabase.loadLibs(context);
795 -
796 - try {
797 - if (originalFile.exists()) {
798 - File newFile = File.createTempFile("sqlcipherutils", "tmp", context.getCacheDir());
799 - SQLiteDatabase db = SQLiteDatabase.openDatabase(originalFile.getAbsolutePath(),
800 - "", null, SQLiteDatabase.OPEN_READWRITE);
801 - int version = db.getVersion();
802 -
803 - db.close();
804 -
805 - db = SQLiteDatabase.openDatabase(newFile.getAbsolutePath(), passphrase,
806 - null, SQLiteDatabase.OPEN_READWRITE, null, null);
807 -
808 - final SQLiteStatement st = db.compileStatement("ATTACH DATABASE ? AS plaintext KEY ''");
809 -
810 - st.bindString(1, originalFile.getAbsolutePath());
811 - st.execute();
812 -
813 - db.rawExecSQL("SELECT sqlcipher_export('main', 'plaintext')");
814 - db.rawExecSQL("DETACH DATABASE plaintext");
815 - db.setVersion(version);
816 - st.close();
817 - db.close();
818 -
819 - originalFile.delete();
820 - newFile.renameTo(originalFile);
821 - } else {
822 - throw new FileNotFoundException(originalFile.getAbsolutePath() + " not found");
823 - }
824 - } catch (IOException ex) {
825 - Log.v("WarplyDB Exception: ", ex.getMessage());
826 - }
827 - }
828 } 479 }
......
...@@ -14,26 +14,19 @@ import android.widget.Toast; ...@@ -14,26 +14,19 @@ import android.widget.Toast;
14 14
15 import androidx.annotation.NonNull; 15 import androidx.annotation.NonNull;
16 import androidx.fragment.app.Fragment; 16 import androidx.fragment.app.Fragment;
17 -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
18 17
19 import org.json.JSONObject; 18 import org.json.JSONObject;
20 19
21 -import java.util.ArrayList;
22 -
23 import ly.warp.sdk.R; 20 import ly.warp.sdk.R;
24 import ly.warp.sdk.activities.HomeActivity; 21 import ly.warp.sdk.activities.HomeActivity;
25 import ly.warp.sdk.db.WarplyDBHelper; 22 import ly.warp.sdk.db.WarplyDBHelper;
26 import ly.warp.sdk.io.callbacks.CallbackReceiver; 23 import ly.warp.sdk.io.callbacks.CallbackReceiver;
27 -import ly.warp.sdk.io.models.Campaign;
28 -import ly.warp.sdk.io.models.RedeemedSMHistoryModel;
29 import ly.warp.sdk.utils.WarplyManagerHelper; 24 import ly.warp.sdk.utils.WarplyManagerHelper;
30 import ly.warp.sdk.utils.managers.WarplyManager; 25 import ly.warp.sdk.utils.managers.WarplyManager;
31 26
32 -public class HomeFragment extends Fragment implements View.OnClickListener, SwipeRefreshLayout.OnRefreshListener { 27 +public class HomeFragment extends Fragment implements View.OnClickListener {
33 -
34 private RelativeLayout mOptionOne, mOptionTwo, mOptionThree, mPbLoading; 28 private RelativeLayout mOptionOne, mOptionTwo, mOptionThree, mPbLoading;
35 private TextView mTvUsername, mTvUser; 29 private TextView mTvUsername, mTvUser;
36 - private SwipeRefreshLayout mSwipeRefresh;
37 private EditText mEtGuid; 30 private EditText mEtGuid;
38 private LinearLayout mLlAuthLogin, mLlAuthLogout, mRlSmFlow, mRlSmMap; 31 private LinearLayout mLlAuthLogin, mLlAuthLogout, mRlSmFlow, mRlSmMap;
39 32
...@@ -45,8 +38,6 @@ public class HomeFragment extends Fragment implements View.OnClickListener, Swip ...@@ -45,8 +38,6 @@ public class HomeFragment extends Fragment implements View.OnClickListener, Swip
45 public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { 38 public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
46 super.onViewCreated(view, savedInstanceState); 39 super.onViewCreated(view, savedInstanceState);
47 40
48 - mSwipeRefresh = view.findViewById(R.id.sw_refresh);
49 - mSwipeRefresh.setOnRefreshListener(this);
50 mOptionOne = view.findViewById(R.id.info_button); 41 mOptionOne = view.findViewById(R.id.info_button);
51 TextView mOptionOneText = mOptionOne.findViewById(R.id.option_text); 42 TextView mOptionOneText = mOptionOne.findViewById(R.id.option_text);
52 ImageView mOptionOneImage = mOptionOne.findViewById(R.id.option_icon); 43 ImageView mOptionOneImage = mOptionOne.findViewById(R.id.option_icon);
...@@ -100,17 +91,6 @@ public class HomeFragment extends Fragment implements View.OnClickListener, Swip ...@@ -100,17 +91,6 @@ public class HomeFragment extends Fragment implements View.OnClickListener, Swip
100 } 91 }
101 92
102 @Override 93 @Override
103 - public void onRefresh() {
104 - if (WarplyDBHelper.getInstance(getActivity()).isTableNotEmpty("auth")) {
105 - WarplyManager.getSupermarketCampaign(mCampaignsCallback);
106 - WarplyManager.getRedeemedSMHistory(mSMHistoryReceiver);
107 - mSwipeRefresh.setRefreshing(false);
108 - } else {
109 - mSwipeRefresh.setRefreshing(false);
110 - }
111 - }
112 -
113 - @Override
114 public void onClick(View view) { 94 public void onClick(View view) {
115 if (view.getId() == R.id.ll_auth_login) { 95 if (view.getId() == R.id.ll_auth_login) {
116 //6012049321, 6012049322, 6012049323, 7000000831 history, 7000000826, 7000000831 shared coupons 96 //6012049321, 6012049322, 6012049323, 7000000831 history, 7000000826, 7000000831 shared coupons
...@@ -141,18 +121,6 @@ public class HomeFragment extends Fragment implements View.OnClickListener, Swip ...@@ -141,18 +121,6 @@ public class HomeFragment extends Fragment implements View.OnClickListener, Swip
141 return homeFragment; 121 return homeFragment;
142 } 122 }
143 123
144 - private final CallbackReceiver<ArrayList<Campaign>> mCampaignsCallback = new CallbackReceiver<ArrayList<Campaign>>() {
145 - @Override
146 - public void onSuccess(ArrayList<Campaign> result) {
147 - Toast.makeText(getActivity(), "Campaigns Success", Toast.LENGTH_SHORT).show();
148 - }
149 -
150 - @Override
151 - public void onFailure(int errorCode) {
152 - Toast.makeText(getActivity(), "Campaigns Error", Toast.LENGTH_SHORT).show();
153 - }
154 - };
155 -
156 private final CallbackReceiver<JSONObject> mLogoutReceiver = new CallbackReceiver<JSONObject>() { 124 private final CallbackReceiver<JSONObject> mLogoutReceiver = new CallbackReceiver<JSONObject>() {
157 @Override 125 @Override
158 public void onSuccess(JSONObject result) { 126 public void onSuccess(JSONObject result) {
...@@ -180,16 +148,6 @@ public class HomeFragment extends Fragment implements View.OnClickListener, Swip ...@@ -180,16 +148,6 @@ public class HomeFragment extends Fragment implements View.OnClickListener, Swip
180 mLlAuthLogout.setVisibility(View.VISIBLE); 148 mLlAuthLogout.setVisibility(View.VISIBLE);
181 mTvUser.setVisibility(View.VISIBLE); 149 mTvUser.setVisibility(View.VISIBLE);
182 mTvUser.setText(mEtGuid.getText().toString()); 150 mTvUser.setText(mEtGuid.getText().toString());
183 -// if (WarplyManagerHelper.getConsumerInternal() != null) {
184 -// JSONObject profMetadata = WarpJSONParser.getJSONFromString(WarplyManagerHelper.getConsumerInternal().getProfileMetadata());
185 -// if (profMetadata != null && profMetadata.has("guid")) {
186 -// String userGuid = profMetadata.optString("guid", "");
187 -// mTvUser.setText(userGuid);
188 -// }
189 -// }
190 -
191 - WarplyManager.getSupermarketCampaign(mCampaignsCallback);
192 - WarplyManager.getRedeemedSMHistory(mSMHistoryReceiver);
193 } 151 }
194 152
195 @Override 153 @Override
...@@ -198,16 +156,4 @@ public class HomeFragment extends Fragment implements View.OnClickListener, Swip ...@@ -198,16 +156,4 @@ public class HomeFragment extends Fragment implements View.OnClickListener, Swip
198 Toast.makeText(getActivity(), "LOGIN ERROR", Toast.LENGTH_SHORT).show(); 156 Toast.makeText(getActivity(), "LOGIN ERROR", Toast.LENGTH_SHORT).show();
199 } 157 }
200 }; 158 };
201 -
202 - private final CallbackReceiver<RedeemedSMHistoryModel> mSMHistoryReceiver = new CallbackReceiver<RedeemedSMHistoryModel>() {
203 - @Override
204 - public void onSuccess(RedeemedSMHistoryModel result) {
205 - Toast.makeText(getActivity(), "SM HISTORY SUCCESS", Toast.LENGTH_SHORT).show();
206 - }
207 -
208 - @Override
209 - public void onFailure(int errorCode) {
210 - Toast.makeText(getActivity(), "SM HISTORY ERROR", Toast.LENGTH_SHORT).show();
211 - }
212 - };
213 } 159 }
...\ No newline at end of file ...\ No newline at end of file
......
1 -/*
2 - * Copyright 2010-2013 Warply Ltd. All rights reserved.
3 - *
4 - * Redistribution and use in source and binary forms, without modification, are
5 - * permitted provided that the following conditions are met:
6 - *
7 - * 1. Redistributions of source code must retain the above copyright notice,
8 - * this list of conditions and the following disclaimer.
9 - *
10 - * 2. Redistributions in binary form must reproduce the above copyright notice,
11 - * this list of conditions and the following disclaimer in the documentation
12 - * and/or other materials provided with the distribution.
13 - *
14 - * THIS SOFTWARE IS PROVIDED BY THE WARPLY LTD ``AS IS'' AND ANY EXPRESS OR
15 - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
16 - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
17 - * EVENT SHALL WARPLY LTD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
18 - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19 - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
20 - * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21 - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22 - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
23 - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 - */
25 -
26 -package ly.warp.sdk.io.models;
27 -
28 -import org.json.JSONArray;
29 -import org.json.JSONException;
30 -import org.json.JSONObject;
31 -
32 -import java.util.ArrayList;
33 -
34 -import ly.warp.sdk.utils.WarpUtils;
35 -import ly.warp.sdk.utils.constants.WarpConstants;
36 -
37 -/**
38 - * Created by Panagiotis Triantafyllou on 20-Jan-25.
39 - */
40 -
41 -public class MarketPassDetailsModel {
42 - private static final String BARCODE = "barcode";
43 - private static final String SUPERMARKETS = "supermarkets";
44 - private static final String TOTAL_DISCOUNT = "total_available_discount";
45 - private static final String NEW_OFFERS = "new_offers";
46 -
47 - private ArrayList<Supermarkets> supermarkets = new ArrayList<Supermarkets>();
48 - private String barcode = "";
49 - private double totalDiscount = 0.0d;
50 - private int newOffers = 0;
51 -
52 - public MarketPassDetailsModel() {
53 - this.supermarkets = new ArrayList<Supermarkets>();
54 - this.barcode = "";
55 - this.totalDiscount = 0.0d;
56 - this.newOffers = 0;
57 - }
58 -
59 - /**
60 - * Basic constructor used to create an object from a String, representing a
61 - * JSON Object
62 - *
63 - * @param json The String, representing the JSON Object
64 - * @throws JSONException Thrown if the String cannot be converted to JSON
65 - */
66 - public MarketPassDetailsModel(String json) throws JSONException {
67 - this(new JSONObject(json));
68 - }
69 -
70 - /**
71 - * Constructor used to create an Object from a given JSON Object
72 - *
73 - * @param json JSON Object used to create the Campaign
74 - */
75 - public MarketPassDetailsModel(JSONObject json) {
76 - if (json != null) {
77 - if (json.optJSONArray(SUPERMARKETS) != null) {
78 - JSONArray tempSupermarkets = json.optJSONArray(SUPERMARKETS);
79 - if (tempSupermarkets != null && tempSupermarkets.length() > 0) {
80 - for (int i = 0, lim = tempSupermarkets.length(); i < lim; ++i) {
81 - this.supermarkets.add(new Supermarkets(tempSupermarkets.optJSONObject(i)));
82 - }
83 - }
84 - }
85 - this.barcode = json.isNull(BARCODE) ? "" : json.optString(BARCODE);
86 - this.totalDiscount = json.optDouble(TOTAL_DISCOUNT);
87 - this.newOffers = json.optInt(NEW_OFFERS);
88 - }
89 - }
90 -
91 - /**
92 - * Converts the Campaign into a JSON Object
93 - *
94 - * @return The JSON Object created from this campaign
95 - */
96 - public JSONObject toJSONObject() {
97 - JSONObject jObj = new JSONObject();
98 - try {
99 - jObj.putOpt(BARCODE, this.barcode);
100 - jObj.putOpt(TOTAL_DISCOUNT, this.totalDiscount);
101 - jObj.putOpt(SUPERMARKETS, this.supermarkets);
102 - jObj.putOpt(NEW_OFFERS, this.newOffers);
103 - } catch (JSONException e) {
104 - if (WarpConstants.DEBUG) {
105 - e.printStackTrace();
106 - }
107 - }
108 - return jObj;
109 - }
110 -
111 - /**
112 - * String representation of the Campaign, as a JSON object
113 - *
114 - * @return A String representation of JSON object
115 - */
116 - public String toString() {
117 - if (toJSONObject() != null)
118 - return toJSONObject().toString();
119 - return null;
120 - }
121 -
122 - /**
123 - * String representation of the Campaign, as a human readable JSON object
124 - *
125 - * @return A human readable String representation of JSON object
126 - */
127 - public String toHumanReadableString() {
128 - String humanReadableString = null;
129 - try {
130 - humanReadableString = toJSONObject().toString(2);
131 - } catch (JSONException e) {
132 - WarpUtils.warn("Failed converting Campaign JSON object to String",
133 - e);
134 - }
135 - return humanReadableString;
136 - }
137 -
138 - public class Supermarkets {
139 - private static final String LOGO = "logo";
140 - private static final String NAME = "name";
141 - private static final String UUID = "uuid";
142 -
143 -
144 - private String logo = "";
145 - private String name = "";
146 - private String uuid = "";
147 -
148 - public Supermarkets() {
149 - this.logo = "";
150 - this.name = "";
151 - this.uuid = "";
152 - }
153 -
154 - public Supermarkets(JSONObject json) {
155 - if (json != null) {
156 - this.logo = json.isNull(LOGO) ? "" : json.optString(LOGO);
157 - this.name = json.isNull(NAME) ? "" : json.optString(NAME);
158 - this.uuid = json.isNull(UUID) ? "" : json.optString(UUID);
159 - }
160 - }
161 -
162 - public String getLogo() {
163 - return logo;
164 - }
165 -
166 - public void setLogo(String logo) {
167 - this.logo = logo;
168 - }
169 -
170 - public String getName() {
171 - return name;
172 - }
173 -
174 - public void setName(String name) {
175 - this.name = name;
176 - }
177 -
178 - public String getUuid() {
179 - return uuid;
180 - }
181 -
182 - public void setUuid(String uuid) {
183 - this.uuid = uuid;
184 - }
185 - }
186 -
187 - // ================================================================================
188 - // Getters
189 - // ================================================================================
190 -
191 -
192 - public ArrayList<Supermarkets> getSupermarkets() {
193 - return supermarkets;
194 - }
195 -
196 - public void setSupermarkets(ArrayList<Supermarkets> supermarkets) {
197 - this.supermarkets = supermarkets;
198 - }
199 -
200 - public String getBarcode() {
201 - return barcode;
202 - }
203 -
204 - public void setBarcode(String barcode) {
205 - this.barcode = barcode;
206 - }
207 -
208 - public double getTotalDiscount() {
209 - return totalDiscount;
210 - }
211 -
212 - public void setTotalDiscount(double totalDiscount) {
213 - this.totalDiscount = totalDiscount;
214 - }
215 -
216 - public int getNewOffers() {
217 - return newOffers;
218 - }
219 -
220 - public void setNewOffers(int newOffers) {
221 - this.newOffers = newOffers;
222 - }
223 -}
1 +package ly.warp.sdk.utils;
2 +
3 +import android.security.keystore.KeyGenParameterSpec;
4 +import android.security.keystore.KeyProperties;
5 +import android.util.Base64;
6 +import android.util.Log;
7 +
8 +import java.security.KeyStore;
9 +
10 +import javax.crypto.Cipher;
11 +import javax.crypto.KeyGenerator;
12 +import javax.crypto.SecretKey;
13 +import javax.crypto.spec.GCMParameterSpec;
14 +
15 +/**
16 + * Utility class for encrypting and decrypting sensitive data using Android Keystore
17 + */
18 +public class CryptoUtils {
19 +
20 + private static final String TAG = "CryptoUtils";
21 + private static final String TRANSFORMATION = "AES/GCM/NoPadding";
22 + private static final String ANDROID_KEYSTORE = "AndroidKeyStore";
23 + private static final String KEY_ALIAS = "tn#mpOl3v3Dy1pr@W";
24 + private static final int GCM_IV_LENGTH = 12;
25 + private static final int GCM_TAG_LENGTH = 16;
26 +
27 + /**
28 + * Encrypt a plain text string
29 + *
30 + * @param plainText The text to encrypt
31 + * @return Base64 encoded encrypted string, or original text if encryption fails
32 + */
33 + public static String encrypt(String plainText) {
34 + if (plainText == null || plainText.isEmpty()) {
35 + return plainText;
36 + }
37 +
38 + try {
39 + SecretKey secretKey = getOrCreateSecretKey();
40 + Cipher cipher = Cipher.getInstance(TRANSFORMATION);
41 + cipher.init(Cipher.ENCRYPT_MODE, secretKey);
42 +
43 + byte[] iv = cipher.getIV();
44 + byte[] encryptedData = cipher.doFinal(plainText.getBytes("UTF-8"));
45 +
46 + // Combine IV + encrypted data
47 + byte[] combined = new byte[iv.length + encryptedData.length];
48 + System.arraycopy(iv, 0, combined, 0, iv.length);
49 + System.arraycopy(encryptedData, 0, combined, iv.length, encryptedData.length);
50 +
51 + return Base64.encodeToString(combined, Base64.DEFAULT);
52 + } catch (Exception e) {
53 + Log.e(TAG, "Encryption failed, returning original text", e);
54 + // Return original text if encryption fails - graceful degradation
55 + return plainText;
56 + }
57 + }
58 +
59 + /**
60 + * Decrypt an encrypted string
61 + *
62 + * @param encryptedText The Base64 encoded encrypted text
63 + * @return Decrypted plain text, or original text if decryption fails
64 + */
65 + public static String decrypt(String encryptedText) {
66 + if (encryptedText == null || encryptedText.isEmpty()) {
67 + return encryptedText;
68 + }
69 +
70 + try {
71 + SecretKey secretKey = getOrCreateSecretKey();
72 + byte[] combined = Base64.decode(encryptedText, Base64.DEFAULT);
73 +
74 + // Extract IV and encrypted data
75 + byte[] iv = new byte[GCM_IV_LENGTH];
76 + byte[] encryptedData = new byte[combined.length - GCM_IV_LENGTH];
77 + System.arraycopy(combined, 0, iv, 0, GCM_IV_LENGTH);
78 + System.arraycopy(combined, GCM_IV_LENGTH, encryptedData, 0, encryptedData.length);
79 +
80 + Cipher cipher = Cipher.getInstance(TRANSFORMATION);
81 + GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv);
82 + cipher.init(Cipher.DECRYPT_MODE, secretKey, spec);
83 +
84 + byte[] decryptedData = cipher.doFinal(encryptedData);
85 + return new String(decryptedData, "UTF-8");
86 + } catch (Exception e) {
87 + Log.e(TAG, "Decryption failed, returning original text", e);
88 + // Return original text if decryption fails - might be unencrypted legacy data
89 + return encryptedText;
90 + }
91 + }
92 +
93 + /**
94 + * Get or create the secret key in Android Keystore
95 + */
96 + private static SecretKey getOrCreateSecretKey() throws Exception {
97 + KeyStore keyStore = KeyStore.getInstance(ANDROID_KEYSTORE);
98 + keyStore.load(null);
99 +
100 + if (!keyStore.containsAlias(KEY_ALIAS)) {
101 + KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEYSTORE);
102 + KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec.Builder(KEY_ALIAS,
103 + KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
104 + .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
105 + .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
106 + .setRandomizedEncryptionRequired(true)
107 + .build();
108 + keyGenerator.init(keyGenParameterSpec);
109 + keyGenerator.generateKey();
110 + }
111 +
112 + return ((KeyStore.SecretKeyEntry) keyStore.getEntry(KEY_ALIAS, null)).getSecretKey();
113 + }
114 +
115 + /**
116 + * Check if a string appears to be encrypted (Base64 format)
117 + *
118 + * @param text The text to check
119 + * @return true if text appears to be encrypted
120 + */
121 + public static boolean isEncrypted(String text) {
122 + if (text == null || text.isEmpty()) {
123 + return false;
124 + }
125 +
126 + try {
127 + byte[] decoded = Base64.decode(text, Base64.DEFAULT);
128 + // Check if decoded length is reasonable for encrypted data (IV + data + tag)
129 + return decoded.length > GCM_IV_LENGTH + GCM_TAG_LENGTH;
130 + } catch (Exception e) {
131 + return false;
132 + }
133 + }
134 +}
...@@ -30,7 +30,7 @@ public class WarpConstants { ...@@ -30,7 +30,7 @@ public class WarpConstants {
30 /** 30 /**
31 * The version of the SDK installed in the device 31 * The version of the SDK installed in the device
32 */ 32 */
33 - public static final String SDK_VERSION = "4.5.5.4"; 33 + public static final String SDK_VERSION = "4.5.5.5";
34 34
35 /** 35 /**
36 * The URL of the server where it should ping 36 * The URL of the server where it should ping
......
...@@ -63,7 +63,6 @@ import ly.warp.sdk.io.models.Campaign; ...@@ -63,7 +63,6 @@ import ly.warp.sdk.io.models.Campaign;
63 import ly.warp.sdk.io.models.Coupon; 63 import ly.warp.sdk.io.models.Coupon;
64 import ly.warp.sdk.io.models.CouponList; 64 import ly.warp.sdk.io.models.CouponList;
65 import ly.warp.sdk.io.models.LoyaltySDKDynatraceEventModel; 65 import ly.warp.sdk.io.models.LoyaltySDKDynatraceEventModel;
66 -import ly.warp.sdk.io.models.MarketPassDetailsModel;
67 import ly.warp.sdk.io.models.NewCampaign; 66 import ly.warp.sdk.io.models.NewCampaign;
68 import ly.warp.sdk.io.models.RedeemedSMHistoryModel; 67 import ly.warp.sdk.io.models.RedeemedSMHistoryModel;
69 import ly.warp.sdk.io.request.WarplyRefreshTokenRequest; 68 import ly.warp.sdk.io.request.WarplyRefreshTokenRequest;
...@@ -227,7 +226,7 @@ public class WarplyManager { ...@@ -227,7 +226,7 @@ public class WarplyManager {
227 }); 226 });
228 } 227 }
229 228
230 - public static void getCosmoteUser(String guid, final CallbackReceiver<JSONObject> receiver) { 229 + public static void getDehUser(String guid, final CallbackReceiver<JSONObject> receiver) {
231 WarpUtils.log("************* WARPLY Cosmote User Request ********************"); 230 WarpUtils.log("************* WARPLY Cosmote User Request ********************");
232 WarpUtils.log("[WARP Trace] WARPLY Cosmote User Request is active"); 231 WarpUtils.log("[WARP Trace] WARPLY Cosmote User Request is active");
233 WarpUtils.log("**************************************************"); 232 WarpUtils.log("**************************************************");
...@@ -930,88 +929,6 @@ public class WarplyManager { ...@@ -930,88 +929,6 @@ public class WarplyManager {
930 return future; 929 return future;
931 } 930 }
932 931
933 - private static ListenableFuture<MarketPassDetailsModel> getMarketPassDetails(ApiService service) {
934 - SettableFuture<MarketPassDetailsModel> future = SettableFuture.create();
935 -
936 - String timeStamp = DateFormat.format("yyyy-MM-dd hh:mm:ss", System.currentTimeMillis()).toString();
937 - String apiKey = WarpUtils.getApiKey(Warply.getWarplyContext());
938 - String webId = WarpUtils.getWebId(Warply.getWarplyContext());
939 -
940 - Map<String, Object> jsonParamsMarketPassDetails = new ArrayMap<>();
941 - Map<String, Object> jsonParams = new ArrayMap<>();
942 - jsonParams.put("action", "integration");
943 - jsonParams.put("method", "supermarket_profile");
944 -
945 - jsonParamsMarketPassDetails.put("consumer_data", jsonParams);
946 - RequestBody marketPassDetailsRequest = RequestBody.create(MediaType.get("application/json; charset=utf-8"), (new JSONObject(jsonParamsMarketPassDetails)).toString());
947 -
948 - Call<ResponseBody> marketPassDetailsCall = service.getMarketPassDetails(WarplyProperty.getAppUuid(Warply.getWarplyContext()), marketPassDetailsRequest, timeStamp, "android:" + Warply.getWarplyContext().getPackageName(), new WarplyDeviceInfoCollector(Warply.getWarplyContext()).getUniqueDeviceId(), "mobile", webId, WarpUtils.produceSignature(apiKey + timeStamp), "Bearer " + WarplyDBHelper.getInstance(Warply.getWarplyContext()).getAuthValue("access_token"));
949 -
950 - marketPassDetailsCall.enqueue(new Callback<ResponseBody>() {
951 - @Override
952 - public void onResponse(@NonNull Call<ResponseBody> call, @NonNull Response<ResponseBody> response) {
953 - if (response.code() == 200 && response.body() != null) {
954 - JSONObject marketPassDetailsResponse = null;
955 - try {
956 - marketPassDetailsResponse = new JSONObject(response.body().string());
957 - } catch (Exception e) {
958 - e.printStackTrace();
959 - }
960 -
961 - if (marketPassDetailsResponse != null && marketPassDetailsResponse.has("status") && marketPassDetailsResponse.optString("status", "2").equals("1")) {
962 - LoyaltySDKDynatraceEventModel dynatraceEvent = new LoyaltySDKDynatraceEventModel();
963 - dynatraceEvent.setEventName("custom_success_market_pass_details");
964 - EventBus.getDefault().post(new WarplyEventBusManager(dynatraceEvent));
965 -
966 - final ExecutorService executorMarketPassDetails = Executors.newFixedThreadPool(1);
967 - final JSONObject finalMarketPassDetailsResponse = marketPassDetailsResponse;
968 - executorMarketPassDetails.submit(() -> {
969 - JSONObject marketPassDetailsBody = null;
970 - try {
971 - marketPassDetailsBody = finalMarketPassDetailsResponse.optJSONObject("result");
972 - } catch (Exception e) {
973 - e.printStackTrace();
974 - }
975 -
976 - if (marketPassDetailsBody != null) {
977 - MarketPassDetailsModel marketPassDetailsModel = new MarketPassDetailsModel(marketPassDetailsBody);
978 - executorMarketPassDetails.shutdownNow();
979 - future.set(marketPassDetailsModel);
980 - } else {
981 - LoyaltySDKDynatraceEventModel dynatraceEvent2 = new LoyaltySDKDynatraceEventModel();
982 - dynatraceEvent2.setEventName("custom_error_market_pass_details");
983 - EventBus.getDefault().post(new WarplyEventBusManager(dynatraceEvent2));
984 - future.set(new MarketPassDetailsModel());
985 - }
986 - });
987 - } else {
988 - LoyaltySDKDynatraceEventModel dynatraceEvent = new LoyaltySDKDynatraceEventModel();
989 - dynatraceEvent.setEventName("custom_error_market_pass_details");
990 - EventBus.getDefault().post(new WarplyEventBusManager(dynatraceEvent));
991 - future.set(new MarketPassDetailsModel());
992 - }
993 - } else {
994 - LoyaltySDKDynatraceEventModel dynatraceEvent = new LoyaltySDKDynatraceEventModel();
995 - dynatraceEvent.setEventName("custom_error_market_pass_details");
996 - EventBus.getDefault().post(new WarplyEventBusManager(dynatraceEvent));
997 -// future.set(new JSONObject());
998 - future.setException(new Throwable());
999 - }
1000 - }
1001 -
1002 - @Override
1003 - public void onFailure(@NonNull Call<ResponseBody> call, @NonNull Throwable t) {
1004 - LoyaltySDKDynatraceEventModel dynatraceEvent = new LoyaltySDKDynatraceEventModel();
1005 - dynatraceEvent.setEventName("custom_error_market_pass_details");
1006 - EventBus.getDefault().post(new WarplyEventBusManager(dynatraceEvent));
1007 -// future.set(new JSONObject());
1008 - future.setException(new Throwable());
1009 - }
1010 - });
1011 -
1012 - return future;
1013 - }
1014 -
1015 private static ListenableFuture<RedeemedSMHistoryModel> getSMCouponsUniversalRetro(ApiService service, int tries, SettableFuture<RedeemedSMHistoryModel> future) { 932 private static ListenableFuture<RedeemedSMHistoryModel> getSMCouponsUniversalRetro(ApiService service, int tries, SettableFuture<RedeemedSMHistoryModel> future) {
1016 String timeStamp = DateFormat.format("yyyy-MM-dd hh:mm:ss", System.currentTimeMillis()).toString(); 933 String timeStamp = DateFormat.format("yyyy-MM-dd hh:mm:ss", System.currentTimeMillis()).toString();
1017 String apiKey = WarpUtils.getApiKey(Warply.getWarplyContext()); 934 String apiKey = WarpUtils.getApiKey(Warply.getWarplyContext());
...@@ -1301,220 +1218,6 @@ public class WarplyManager { ...@@ -1301,220 +1218,6 @@ public class WarplyManager {
1301 }, null); 1218 }, null);
1302 } 1219 }
1303 1220
1304 - public static void getMarketPassDetails(final CallbackReceiver<MarketPassDetailsModel> receiver) {
1305 - WarpUtils.log("************* WARPLY Market Pass Details Request ********************");
1306 - WarpUtils.log("[WARP Trace] WARPLY Market Pass Details is active");
1307 - WarpUtils.log("**************************************************");
1308 -
1309 - ApiService service = ApiClient.getRetrofitInstance().create(ApiService.class);
1310 - ListeningExecutorService executorService = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(1));
1311 -
1312 - ListenableFuture<MarketPassDetailsModel> futureMarketPassDetails = getMarketPassDetails(service);
1313 -
1314 - ListenableFuture<List<Object>> allResultsFuture = Futures.allAsList(futureMarketPassDetails);
1315 - ListenableFuture<MarketPassDetailsModel> mergedResultFuture = Futures.transformAsync(allResultsFuture, results -> {
1316 - MarketPassDetailsModel resultMarketPassDetails = (MarketPassDetailsModel) results.get(0);
1317 - return executorService.submit(() -> resultMarketPassDetails);
1318 - }, executorService);
1319 -
1320 - Futures.addCallback(mergedResultFuture, new FutureCallback<MarketPassDetailsModel>() {
1321 - @Override
1322 - public void onSuccess(MarketPassDetailsModel mergedResult) {
1323 - executorService.shutdownNow();
1324 - new Handler(Looper.getMainLooper()).post(() -> receiver.onSuccess(mergedResult));
1325 - }
1326 -
1327 - @Override
1328 - public void onFailure(Throwable throwable) {
1329 - executorService.shutdownNow();
1330 - new Handler(Looper.getMainLooper()).post(() -> receiver.onFailure(2));
1331 - }
1332 - }, executorService);
1333 - }
1334 -
1335 - private static /*void*/ ListenableFuture<ArrayList<Campaign>> getSupermarketCampaignRetro(ApiService service/*, final CallbackReceiver<ArrayList<Campaign>> receiver*/) {
1336 - SettableFuture<ArrayList<Campaign>> future = SettableFuture.create();
1337 -
1338 - String timeStamp = DateFormat.format("yyyy-MM-dd hh:mm:ss", System.currentTimeMillis()).toString();
1339 - String apiKey = WarpUtils.getApiKey(Warply.getWarplyContext());
1340 - String webId = WarpUtils.getWebId(Warply.getWarplyContext());
1341 -
1342 - Map<String, Object> jsonParamsCampaigns = new ArrayMap<>();
1343 - Map<String, Object> jsonParams = new ArrayMap<>();
1344 - JSONObject jFilters = new JSONObject();
1345 - JSONObject jExtra = new JSONObject();
1346 - try {
1347 - jFilters.putOpt("communication_category", "more_for_you");
1348 - jExtra.putOpt("filter", "supermarket");
1349 - jExtra.putOpt("version", "magenta");
1350 - jFilters.putOpt("extra_fields", jExtra);
1351 - } catch (JSONException e) {
1352 - throw new RuntimeException(e);
1353 - }
1354 - jsonParams.put("action", "retrieve");
1355 - jsonParams.put("filters", jFilters);
1356 - jsonParams.put("language", WarpUtils.getApplicationLocale(Warply.getWarplyContext()));
1357 -
1358 - jsonParamsCampaigns.put("campaigns", jsonParams);
1359 - RequestBody campaignsRequest = RequestBody.create(MediaType.get("application/json; charset=utf-8"), (new JSONObject(jsonParamsCampaigns)).toString());
1360 -
1361 - Call<ResponseBody> campaignsCall = service.getCampaigns(WarplyProperty.getAppUuid(Warply.getWarplyContext()), campaignsRequest, timeStamp, "android:" + Warply.getWarplyContext().getPackageName(), new WarplyDeviceInfoCollector(Warply.getWarplyContext()).getUniqueDeviceId(), "mobile", webId, WarpUtils.produceSignature(apiKey + timeStamp));
1362 -
1363 - campaignsCall.enqueue(new Callback<ResponseBody>() {
1364 - @Override
1365 - public void onResponse(@NonNull Call<ResponseBody> call, @NonNull Response<ResponseBody> response) {
1366 - if (response.code() == 200 && response.body() != null) {
1367 - JSONObject jobjCampaignsResponse = null;
1368 - try {
1369 - jobjCampaignsResponse = new JSONObject(response.body().string());
1370 - } catch (Exception e) {
1371 - e.printStackTrace();
1372 - }
1373 -
1374 - if (jobjCampaignsResponse != null && jobjCampaignsResponse.has("status") && jobjCampaignsResponse.optString("status", "2").equals("1")) {
1375 - LoyaltySDKDynatraceEventModel dynatraceEvent = new LoyaltySDKDynatraceEventModel();
1376 - dynatraceEvent.setEventName("custom_success_supermarket_campaign_loyalty");
1377 - EventBus.getDefault().post(new WarplyEventBusManager(dynatraceEvent));
1378 -
1379 - JSONArray jCampaignsBody = null;
1380 - try {
1381 - jCampaignsBody = jobjCampaignsResponse.optJSONObject("context").optJSONObject("MAPP_CAMPAIGNING").optJSONArray("campaigns");
1382 - } catch (Exception e) {
1383 - e.printStackTrace();
1384 - }
1385 -
1386 - if (jCampaignsBody != null) {
1387 - ArrayList<NewCampaign> tempCampaigns = new ArrayList<>();
1388 - ArrayList<Campaign> mCampaignsList = new ArrayList<>();
1389 -
1390 - final ExecutorService executorCampaigns = Executors.newFixedThreadPool(1);
1391 - JSONArray finalCampaignsJBody = jCampaignsBody;
1392 - executorCampaigns.submit(() -> {
1393 - for (int i = 0; i < finalCampaignsJBody.length(); ++i) {
1394 - tempCampaigns.add(new NewCampaign(finalCampaignsJBody.optJSONObject(i)));
1395 - }
1396 - for (NewCampaign newCamp : tempCampaigns) {
1397 - Campaign camp = new Campaign();
1398 - camp.setIndexUrl(newCamp.getIndexUrl());
1399 - camp.setLogoUrl(newCamp.getLogoUrl());
1400 - camp.setMessage(newCamp.getMessage());
1401 - camp.setOfferCategory(newCamp.getCommunicationCategory());
1402 - camp.setSessionUUID(newCamp.getCommunicationUUID());
1403 - camp.setTitle(newCamp.getTitle());
1404 - camp.setSubtitle(newCamp.getSubtitle());
1405 - camp.setSorting(newCamp.getSorting());
1406 - camp.setNew(newCamp.getIsNew());
1407 - camp.setType(newCamp.getCampaignType());
1408 - camp.setEndDate(newCamp.getEndDate());
1409 - camp.setStartDate(newCamp.getStartDate());
1410 - camp.setShowExpiration(newCamp.isShowExpiration());
1411 - camp.setCouponImg(newCamp.getCouponImg());
1412 - camp.setFilter(newCamp.getFilter());
1413 - try {
1414 - camp.setExtraFields(newCamp.getExtraFields().toString());
1415 - if (!TextUtils.isEmpty(newCamp.getExtraFields().toString())) {
1416 - JSONObject extraFieldsResp = WarpJSONParser.getJSONFromString(newCamp.getExtraFields().toString());
1417 - if (extraFieldsResp != null) {
1418 - if (extraFieldsResp.has("Banner_title")) {
1419 - camp.setBannerTitle(extraFieldsResp.optString("Banner_title", ""));
1420 - }
1421 - if (extraFieldsResp.has("Banner_img")) {
1422 - camp.setBannerImage(extraFieldsResp.optString("Banner_img", ""));
1423 - }
1424 - if (extraFieldsResp.has("category_id")) {
1425 - camp.setCategoryId(extraFieldsResp.optString("category_id", ""));
1426 - }
1427 - }
1428 - } else {
1429 - camp.setBannerImage("");
1430 - camp.setBannerTitle("");
1431 - camp.setCategoryId("");
1432 - }
1433 - } catch (NullPointerException e) {
1434 - camp.setExtraFields("");
1435 - camp.setBannerImage("");
1436 - camp.setBannerTitle("");
1437 - camp.setCategoryId("");
1438 - e.printStackTrace();
1439 - }
1440 -
1441 - try {
1442 - camp.setCampaignTypeSettings(newCamp.getSettings().toString());
1443 - } catch (NullPointerException e) {
1444 - camp.setCampaignTypeSettings("");
1445 - e.printStackTrace();
1446 - }
1447 - mCampaignsList.add(camp);
1448 - }
1449 -
1450 - executorCampaigns.shutdownNow();
1451 -// receiver.onSuccess(mCampaignsList);
1452 - future.set(mCampaignsList);
1453 - });
1454 - }
1455 - } else {
1456 - LoyaltySDKDynatraceEventModel dynatraceEvent = new LoyaltySDKDynatraceEventModel();
1457 - dynatraceEvent.setEventName("custom_error_supermarket_campaign_loyalty");
1458 - EventBus.getDefault().post(new WarplyEventBusManager(dynatraceEvent));
1459 -// receiver.onFailure(2);
1460 - future.set(new ArrayList<Campaign>());
1461 - }
1462 - } else {
1463 - LoyaltySDKDynatraceEventModel dynatraceEvent = new LoyaltySDKDynatraceEventModel();
1464 - dynatraceEvent.setEventName("custom_error_supermarket_campaign_loyalty");
1465 - EventBus.getDefault().post(new WarplyEventBusManager(dynatraceEvent));
1466 -// receiver.onFailure(response.code());
1467 -// future.set(new ArrayList<Campaign>());
1468 - future.setException(new Throwable());
1469 - }
1470 - }
1471 -
1472 - @Override
1473 - public void onFailure(@NonNull Call<ResponseBody> call, @NonNull Throwable t) {
1474 - LoyaltySDKDynatraceEventModel dynatraceEvent = new LoyaltySDKDynatraceEventModel();
1475 - dynatraceEvent.setEventName("custom_error_supermarket_campaign_loyalty");
1476 - EventBus.getDefault().post(new WarplyEventBusManager(dynatraceEvent));
1477 -// receiver.onFailure(2);
1478 -// future.set(new ArrayList<Campaign>());
1479 - future.setException(new Throwable());
1480 - }
1481 - });
1482 -
1483 - return future;
1484 - }
1485 -
1486 - public static void getSupermarketCampaign(final CallbackReceiver<ArrayList<Campaign>> receiver) {
1487 - WarpUtils.log("************* WARPLY Get Supermarket Campaigns Request ********************");
1488 - WarpUtils.log("[WARP Trace] WARPLY Get Supermarket Campaigns Request is active");
1489 - WarpUtils.log("**************************************************");
1490 -
1491 - ApiService service = ApiClient.getRetrofitInstance().create(ApiService.class);
1492 - ListeningExecutorService executorService = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(1));
1493 -
1494 - ListenableFuture<ArrayList<Campaign>> futureCampaigns = getSupermarketCampaignRetro(service);
1495 -
1496 - ListenableFuture<List<Object>> allResultsFuture = Futures.allAsList(futureCampaigns);
1497 - ListenableFuture<ArrayList<Campaign>> mergedResultFuture = Futures.transformAsync(allResultsFuture, results -> {
1498 - ArrayList<Campaign> resultCampaigns = (ArrayList<Campaign>) results.get(0);
1499 - return executorService.submit(() -> resultCampaigns);
1500 - }, executorService);
1501 -
1502 - Futures.addCallback(mergedResultFuture, new FutureCallback<ArrayList<Campaign>>() {
1503 - @Override
1504 - public void onSuccess(ArrayList<Campaign> mergedResult) {
1505 - WarplyManagerHelper.setSupermarketCampaigns(mergedResult);
1506 - executorService.shutdownNow();
1507 - new Handler(Looper.getMainLooper()).post(() -> receiver.onSuccess(mergedResult));
1508 - }
1509 -
1510 - @Override
1511 - public void onFailure(Throwable throwable) {
1512 - executorService.shutdownNow();
1513 - new Handler(Looper.getMainLooper()).post(() -> receiver.onFailure(2));
1514 - }
1515 - }, executorService);
1516 - }
1517 -
1518 public static void getRedeemedSMHistory(final CallbackReceiver<RedeemedSMHistoryModel> receiver) { 1221 public static void getRedeemedSMHistory(final CallbackReceiver<RedeemedSMHistoryModel> receiver) {
1519 WarpUtils.log("************* WARPLY User Redeemed History Request ********************"); 1222 WarpUtils.log("************* WARPLY User Redeemed History Request ********************");
1520 WarpUtils.log("[WARP Trace] WARPLY User Redeemed History Request is active"); 1223 WarpUtils.log("[WARP Trace] WARPLY User Redeemed History Request is active");
......
...@@ -568,8 +568,8 @@ public class WarpView extends WebView implements DefaultLifecycleObserver { ...@@ -568,8 +568,8 @@ public class WarpView extends WebView implements DefaultLifecycleObserver {
568 public void onGeolocationPermissionsShowPrompt(String origin, Callback callback) { 568 public void onGeolocationPermissionsShowPrompt(String origin, Callback callback) {
569 if (WarpActivity != null && !WarpActivity.isFinishing()) { 569 if (WarpActivity != null && !WarpActivity.isFinishing()) {
570 AlertDialog.Builder builder = new AlertDialog.Builder(WarpActivity); 570 AlertDialog.Builder builder = new AlertDialog.Builder(WarpActivity);
571 - builder.setTitle(getContext().getString(R.string.lbl_cosmote_webview_permission_title)); 571 + builder.setTitle(getContext().getString(R.string.webview_permission_title));
572 - builder.setMessage(getContext().getString(R.string.lbl_cosmote_webview_permission_message)) 572 + builder.setMessage(getContext().getString(R.string.webview_permission_message))
573 .setCancelable(false) 573 .setCancelable(false)
574 .setPositiveButton(getContext().getString(R.string.lbl_take_photo_accept), (dialog, id) -> checkForPermissions(origin, callback)) 574 .setPositiveButton(getContext().getString(R.string.lbl_take_photo_accept), (dialog, id) -> checkForPermissions(origin, callback))
575 .setNegativeButton(getContext().getString(R.string.lbl_take_photo_decline), (dialog, id) -> callback.invoke(origin, false, false)); 575 .setNegativeButton(getContext().getString(R.string.lbl_take_photo_decline), (dialog, id) -> callback.invoke(origin, false, false));
......
1 <?xml version="1.0" encoding="utf-8"?> 1 <?xml version="1.0" encoding="utf-8"?>
2 - 2 +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
3 -<androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
4 xmlns:app="http://schemas.android.com/apk/res-auto" 3 xmlns:app="http://schemas.android.com/apk/res-auto"
5 xmlns:tools="http://schemas.android.com/tools" 4 xmlns:tools="http://schemas.android.com/tools"
6 - android:id="@+id/sw_refresh"
7 - android:layout_width="match_parent"
8 - android:layout_height="match_parent"
9 - android:background="@color/cos_skyblue2">
10 -
11 - <ScrollView
12 android:layout_width="match_parent" 5 android:layout_width="match_parent"
13 android:layout_height="match_parent" 6 android:layout_height="match_parent"
14 android:background="@color/cos_skyblue2"> 7 android:background="@color/cos_skyblue2">
...@@ -261,5 +254,4 @@ ...@@ -261,5 +254,4 @@
261 </RelativeLayout> 254 </RelativeLayout>
262 255
263 </RelativeLayout> 256 </RelativeLayout>
264 - </ScrollView> 257 +</ScrollView>
265 -</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
......
1 <resources> 1 <resources>
2 - <string name="lbl_cosmote_webview_permission_title">Demo App</string> 2 + <string name="webview_permission_title">Demo App</string>
3 - <string name="lbl_cosmote_webview_permission_message">Το Demo App ζητάει πρόσβαση στην τοποθεσία σας.</string> 3 + <string name="webview_permission_message">Το Demo App ζητάει πρόσβαση στην τοποθεσία σας.</string>
4 <string name="lbl_take_photo_accept">Οκ</string> 4 <string name="lbl_take_photo_accept">Οκ</string>
5 <string name="lbl_take_photo_decline">Άκυρο</string> 5 <string name="lbl_take_photo_decline">Άκυρο</string>
6 <string name="welcome_user">Γεια σου %1$s !</string> 6 <string name="welcome_user">Γεια σου %1$s !</string>
......