diff --git a/app/build.gradle b/app/build.gradle index f7fe002..f996e50 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,6 +8,8 @@ apply plugin: 'kotlin-kapt' apply plugin: "androidx.navigation.safeargs.kotlin" +apply plugin: 'com.google.gms.google-services' + buildscript { repositories { google() @@ -33,15 +35,14 @@ android { } defaultConfig { applicationId "au.gov.health.covidsafe" - resValue "string", "build_config_package", "au.gov.health.covidsafe" minSdkVersion 21 /* TargetSdk is currently set to 28 because we are using a greylisted api in SDK 29, in order to fix a BLE vulnerability Before you increase the targetSdkVersion make sure that all its usage are still working */ targetSdkVersion 28 - versionCode 33 - versionName "1.0.33" + versionCode 39 + versionName "1.0.39" buildConfigField "String", "GITHASH", "\"${getGitHash()}\"" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -64,7 +65,7 @@ android { buildConfigField "String", "SERVICE_FOREGROUND_CHANNEL_NAME", SERVICE_FOREGROUND_CHANNEL_NAME buildConfigField "int", "PUSH_NOTIFICATION_ID", PUSH_NOTIFICATION_ID - buildConfigField "String", "PUSH_NOTIFICATION_CHANNEL_NAME", PUSH_NOTIFICATION_CHANNEL_NAME + buildConfigField "String", "PUSH_NOTIFICATION_CHANNEL_ID", PUSH_NOTIFICATION_CHANNEL_ID buildConfigField "long", "SCAN_DURATION", SCAN_DURATION buildConfigField "long", "MIN_SCAN_INTERVAL", MIN_SCAN_INTERVAL @@ -203,6 +204,7 @@ dependencies { //androidx implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.recyclerview:recyclerview:1.1.0' + implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0" implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.1.0-alpha01' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' @@ -228,8 +230,8 @@ dependencies { implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' //bottom navigation - implementation 'androidx.navigation:navigation-fragment-ktx:2.2.2' - implementation 'androidx.navigation:navigation-ui-ktx:2.2.2' + implementation 'androidx.navigation:navigation-fragment-ktx:2.3.0' + implementation 'androidx.navigation:navigation-ui-ktx:2.3.0' //cardview implementation 'androidx.cardview:cardview:1.0.0' @@ -246,6 +248,9 @@ dependencies { // flags implementation 'com.michaelfotiadis:android-country-flags:1.0.3' + // Firebase Cloud Messaging + implementation 'com.google.firebase:firebase-messaging:20.2.3' + androidTestImplementation "androidx.room:room-testing:2.2.5" diff --git a/app/google-services.json b/app/google-services.json new file mode 100644 index 0000000..3610e2f --- /dev/null +++ b/app/google-services.json @@ -0,0 +1,40 @@ +{ + "project_info": { + "project_number": "382814507764", + "firebase_url": "https://covidsafe-prod.firebaseio.com", + "project_id": "covidsafe-prod", + "storage_bucket": "covidsafe-prod.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:382814507764:android:d3eaa52d82fc647f8dc4a1", + "android_client_info": { + "package_name": "au.gov.health.covidsafe" + } + }, + "oauth_client": [ + { + "client_id": "382814507764-bsndgpff7lano9u44sqgftu6q5besbg0.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyAo_cVd_eNYyx_ckLnC7dYgS_Po9-DsTXU" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "382814507764-bsndgpff7lano9u44sqgftu6q5besbg0.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/app/src/debug/google-services.json b/app/src/debug/google-services.json new file mode 100644 index 0000000..9b90052 --- /dev/null +++ b/app/src/debug/google-services.json @@ -0,0 +1,69 @@ +{ + "project_info": { + "project_number": "768227664885", + "firebase_url": "https://covidsafe-beta.firebaseio.com", + "project_id": "covidsafe-beta", + "storage_bucket": "covidsafe-beta.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:768227664885:android:842074835553a7613136bc", + "android_client_info": { + "package_name": "au.gov.health.covidsafe.beta" + } + }, + "oauth_client": [ + { + "client_id": "768227664885-86vucs5sakhvks1umnfk8lv3l8b14hs8.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyDlTaBwuswUzG_Cr2gA_AwgjVEmjwk05Zc" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "768227664885-86vucs5sakhvks1umnfk8lv3l8b14hs8.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:768227664885:android:c9dbe1353d465e543136bc", + "android_client_info": { + "package_name": "au.gov.health.covidsafe.debug" + } + }, + "oauth_client": [ + { + "client_id": "768227664885-86vucs5sakhvks1umnfk8lv3l8b14hs8.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyDlTaBwuswUzG_Cr2gA_AwgjVEmjwk05Zc" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "768227664885-86vucs5sakhvks1umnfk8lv3l8b14hs8.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4da99dd..0a04319 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -11,7 +11,7 @@ - + @@ -31,8 +31,7 @@ + android:configChanges="keyboardHidden"> @@ -40,21 +39,19 @@ - + @@ -78,6 +75,19 @@ + + + + + + + + diff --git a/app/src/main/java/au/gov/health/covidsafe/HomeActivity.kt b/app/src/main/java/au/gov/health/covidsafe/HomeActivity.kt index 04e0f08..49d8212 100644 --- a/app/src/main/java/au/gov/health/covidsafe/HomeActivity.kt +++ b/app/src/main/java/au/gov/health/covidsafe/HomeActivity.kt @@ -2,14 +2,49 @@ package au.gov.health.covidsafe import android.os.Bundle import androidx.fragment.app.FragmentActivity +import au.gov.health.covidsafe.logging.CentralLog +import au.gov.health.covidsafe.scheduler.GetMessagesScheduler +import com.google.android.gms.tasks.OnCompleteListener +import com.google.firebase.iid.FirebaseInstanceId + +private const val TAG = "HomeActivity" class HomeActivity : FragmentActivity() { + /** + * Provides notification support to inform users to update to the latest version of the app. + * This feature will also allow for troubleshooting of the app in the future and allow for + * targeted notifications + */ + private fun getInstanceID() { + FirebaseInstanceId.getInstance().instanceId + .addOnCompleteListener(OnCompleteListener { task -> + if (!task.isSuccessful) { + CentralLog.e(TAG, "getInstanceId failed", task.exception) + return@OnCompleteListener + } + + // Get new Instance ID token + val token = task.result?.token + + // Log and toast + CentralLog.d(TAG, "getInstance() InstanceID = $token") + + token?.let { + Preference.putFirebaseInstanceID(TracerApp.AppContext, it) + } + }) + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_home) Utils.startBluetoothMonitoringService(this) + // messages API related + getInstanceID() +// GetMessagesScheduler.getMessages() + GetMessagesScheduler.scheduleGetMessagesJob() } -} +} \ No newline at end of file diff --git a/app/src/main/java/au/gov/health/covidsafe/Preference.kt b/app/src/main/java/au/gov/health/covidsafe/Preference.kt index 4060271..272949c 100644 --- a/app/src/main/java/au/gov/health/covidsafe/Preference.kt +++ b/app/src/main/java/au/gov/health/covidsafe/Preference.kt @@ -12,7 +12,7 @@ object Preference { private const val CALLING_CODE = "CALLING_CODE" private const val AUSTRALIA_CALLING_CODE = 61 private const val COUNTRY_NAME_RES_ID = "COUNTRY_NAME" - private const val AUSTRALIA_COUNTRY_NAME_RES_ID = R.string.country_au + private const val AUSTRALIA_COUNTRY_NAME_RES_ID = R.string.country_region_name_au private const val NATIONAL_FLAG_RES_ID = "NATIONAL_FLAG_RES_ID" private const val AUSTRALIA_NATIONAL_FLAG_RES_ID = R.drawable.ic_list_country_au private const val PHONE_NUMBER = "PHONE_NUMBER" @@ -24,7 +24,7 @@ object Preference { private const val IS_DATA_UPLOADED = "IS_DATA_UPLOADED" private const val DATA_UPLOADED_DATE_MS = "DATA_UPLOADED_DATE_MS" private const val UPLOADED_MORE_THAN_24_HRS = "UPLOADED_MORE_THAN_24_HRS" - + private const val FIREBASE_INSTANCE_ID = "FIREBASE_INSTANCE_ID" private const val NEXT_FETCH_TIME = "NEXT_FETCH_TIME" private const val EXPIRY_TIME = "EXPIRY_TIME" private const val NAME = "NAME" @@ -32,6 +32,8 @@ object Preference { private const val POST_CODE = "POST_CODE" private const val AGE = "AGE" + private const val APP_UPDATE_REMINDER_DISMISSED_TIME = "APP_UPDATE_REMINDER_DISMISSED_TIME" + fun putDeviceID(context: Context, value: String) { context.getSharedPreferences(PREF_ID, Context.MODE_PRIVATE) .edit().putString(DEVICE_ID, value)?.apply() @@ -130,6 +132,16 @@ object Preference { ?.getString(PHONE_NUMBER, "") ?: "" } + fun putFirebaseInstanceID(context: Context, value: String) { + context.getSharedPreferences(PREF_ID, Context.MODE_PRIVATE) + .edit().putString(FIREBASE_INSTANCE_ID, value).apply() + } + + fun getFirebaseInstanceID(context: Context): String { + return context.getSharedPreferences(PREF_ID, Context.MODE_PRIVATE) + ?.getString(FIREBASE_INSTANCE_ID, "") ?: "" + } + fun putCallingCode(context: Context, value: Int) { context.getSharedPreferences(PREF_ID, Context.MODE_PRIVATE) .edit().putInt(CALLING_CODE, value).apply() @@ -243,4 +255,14 @@ object Preference { .getString(AGE, null) } + fun putAppUpdateReminderDismissedTime(context: Context): Boolean { + return context.getSharedPreferences(PREF_ID, Context.MODE_PRIVATE) + .edit().putLong(APP_UPDATE_REMINDER_DISMISSED_TIME, System.currentTimeMillis()).commit() + } + + fun getAppUpdateReminderDismissedTime(context: Context): Long { + return context.getSharedPreferences(PREF_ID, Context.MODE_PRIVATE) + .getLong(APP_UPDATE_REMINDER_DISMISSED_TIME, 0L) + } + } diff --git a/app/src/main/java/au/gov/health/covidsafe/TracerApp.kt b/app/src/main/java/au/gov/health/covidsafe/TracerApp.kt index e7e1cb1..fb6a71e 100644 --- a/app/src/main/java/au/gov/health/covidsafe/TracerApp.kt +++ b/app/src/main/java/au/gov/health/covidsafe/TracerApp.kt @@ -6,6 +6,7 @@ import android.os.Build import com.atlassian.mobilekit.module.feedback.FeedbackModule import au.gov.health.covidsafe.logging.CentralLog +import au.gov.health.covidsafe.scheduler.GetMessagesScheduler import au.gov.health.covidsafe.services.BluetoothMonitoringService import au.gov.health.covidsafe.streetpass.CentralDevice import au.gov.health.covidsafe.streetpass.PeripheralDevice @@ -16,6 +17,8 @@ class TracerApp : Application() { super.onCreate() AppContext = applicationContext FeedbackModule.init(this) + +// GetMessagesScheduler.scheduleGetMessagesJob() } companion object { diff --git a/app/src/main/java/au/gov/health/covidsafe/Utils.kt b/app/src/main/java/au/gov/health/covidsafe/Utils.kt index 19a033f..fbe0bf4 100644 --- a/app/src/main/java/au/gov/health/covidsafe/Utils.kt +++ b/app/src/main/java/au/gov/health/covidsafe/Utils.kt @@ -33,7 +33,7 @@ object Utils { private const val TAG = "Utils" fun getRequiredPermissions(): Array { - return arrayOf(Manifest.permission.ACCESS_FINE_LOCATION) + return arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION) } fun getBatteryOptimizerExemptionIntent(packageName: String): Intent { diff --git a/app/src/main/java/au/gov/health/covidsafe/bluetooth/gatt/GattServer.kt b/app/src/main/java/au/gov/health/covidsafe/bluetooth/gatt/GattServer.kt index 34106d6..19ec4eb 100644 --- a/app/src/main/java/au/gov/health/covidsafe/bluetooth/gatt/GattServer.kt +++ b/app/src/main/java/au/gov/health/covidsafe/bluetooth/gatt/GattServer.kt @@ -42,7 +42,7 @@ class GattServer constructor(val context: Context, serviceUUIDString: String) { CentralLog.i(TAG, "${device?.address} Connected to local GATT server") device?.let { val b = bluetoothManager.getConnectedDevices(BluetoothProfile.GATT) - .contains(device) + .contains(device) } } @@ -62,10 +62,10 @@ class GattServer constructor(val context: Context, serviceUUIDString: String) { } override fun onCharacteristicReadRequest( - device: BluetoothDevice?, - requestId: Int, - offset: Int, - characteristic: BluetoothGattCharacteristic? + device: BluetoothDevice?, + requestId: Int, + offset: Int, + characteristic: BluetoothGattCharacteristic? ) { device?.let { @@ -76,9 +76,15 @@ class GattServer constructor(val context: Context, serviceUUIDString: String) { if (Utils.bmValid(context)) { val peripheral = TracerApp.asPeripheralDevice() - val readRequest = ReadRequestEncryptedPayload(peripheral.modelP, - TracerApp.thisDeviceMsg()) + val readRequest = ReadRequestEncryptedPayload( + System.currentTimeMillis() / 1000L, + peripheral.modelP, + TracerApp.thisDeviceMsg() + ) val plainRecord = gson.toJson(readRequest) + + CentralLog.d(TAG, "onCharacteristicReadRequest plainRecord = $plainRecord") + val plainRecordByteArray = plainRecord.toByteArray(Charsets.UTF_8) val remoteBlob = Encryption.encryptPayload(plainRecordByteArray) val base = readPayloadMap.getOrPut(device.address, { @@ -93,25 +99,25 @@ class GattServer constructor(val context: Context, serviceUUIDString: String) { val value = base.copyOfRange(offset, base.size) CentralLog.i( - TAG, - "onCharacteristicReadRequest from ${device.address} - $requestId- $offset - ${String( - value, - Charsets.UTF_8 - )}" + TAG, + "onCharacteristicReadRequest from ${device.address} - $requestId- $offset - ${String( + value, + Charsets.UTF_8 + )}" ) bluetoothGattServer?.sendResponse(device, requestId, GATT_SUCCESS, 0, value) } else { CentralLog.i( - TAG, - "onCharacteristicReadRequest from ${device.address} - $requestId- $offset - BM Expired" + TAG, + "onCharacteristicReadRequest from ${device.address} - $requestId- $offset - BM Expired" ) bluetoothGattServer?.sendResponse( - device, - requestId, - GATT_FAILURE, - 0, - ByteArray(0) + device, + requestId, + GATT_FAILURE, + 0, + ByteArray(0) ) } } else { @@ -126,29 +132,29 @@ class GattServer constructor(val context: Context, serviceUUIDString: String) { } - inner class ReadRequestEncryptedPayload (val modelP : String, val msg: String) + inner class ReadRequestEncryptedPayload(val timestamp: Long, val modelP: String, val msg: String) override fun onCharacteristicWriteRequest( - device: BluetoothDevice?, - requestId: Int, - characteristic: BluetoothGattCharacteristic, - preparedWrite: Boolean, - responseNeeded: Boolean, - offset: Int, - value: ByteArray? + device: BluetoothDevice?, + requestId: Int, + characteristic: BluetoothGattCharacteristic, + preparedWrite: Boolean, + responseNeeded: Boolean, + offset: Int, + value: ByteArray? ) { device?.let { CentralLog.i( - TAG, - "onCharacteristicWriteRequest - ${device.address} - preparedWrite: $preparedWrite" + TAG, + "onCharacteristicWriteRequest - ${device.address} - preparedWrite: $preparedWrite" ) CentralLog.i( - TAG, - "onCharacteristicWriteRequest from ${device.address} - $requestId - $offset" + TAG, + "onCharacteristicWriteRequest from ${device.address} - $requestId - $offset" ) if (serviceUUID == characteristic.uuid) { @@ -157,8 +163,8 @@ class GattServer constructor(val context: Context, serviceUUIDString: String) { valuePassed = String(value, Charsets.UTF_8) } CentralLog.i( - TAG, - "onCharacteristicWriteRequest from ${device.address} - $valuePassed" + TAG, + "onCharacteristicWriteRequest from ${device.address} - $valuePassed" ) if (value != null) { var dataBuffer = writeDataPayload[device.address] @@ -171,21 +177,21 @@ class GattServer constructor(val context: Context, serviceUUIDString: String) { writeDataPayload[device.address] = dataBuffer CentralLog.i( - TAG, - "Accumulated characteristic: ${String( - dataBuffer, - Charsets.UTF_8 - )}" + TAG, + "Accumulated characteristic: ${String( + dataBuffer, + Charsets.UTF_8 + )}" ) if (responseNeeded) { CentralLog.i(TAG, "Sending response offset: ${dataBuffer.size}") bluetoothGattServer?.sendResponse( - device, - requestId, - GATT_SUCCESS, - dataBuffer.size, - value + device, + requestId, + GATT_SUCCESS, + dataBuffer.size, + value ) } } @@ -196,8 +202,8 @@ class GattServer constructor(val context: Context, serviceUUIDString: String) { if (!preparedWrite) { CentralLog.i( - TAG, - "onCharacteristicWriteRequest - ${device.address} - preparedWrite: $preparedWrite" + TAG, + "onCharacteristicWriteRequest - ${device.address} - preparedWrite: $preparedWrite" ) saveDataSaved(device) @@ -218,11 +224,11 @@ class GattServer constructor(val context: Context, serviceUUIDString: String) { if (dataBuffer != null) { CentralLog.i( - TAG, - "onExecuteWrite - $requestId- ${device.address} - ${String( - dataBuffer, - Charsets.UTF_8 - )}" + TAG, + "onExecuteWrite - $requestId- ${device.address} - ${String( + dataBuffer, + Charsets.UTF_8 + )}" ) saveDataSaved(device) bluetoothGattServer?.sendResponse(device, requestId, GATT_SUCCESS, 0, null) @@ -245,18 +251,18 @@ class GattServer constructor(val context: Context, serviceUUIDString: String) { try { centralDevice = CentralDevice(dataWritten.modelC, device.address) val connectionRecord = ConnectionRecord( - version = dataWritten.v, - msg = dataWritten.msg, - org = dataWritten.org, - peripheral = TracerApp.asPeripheralDevice(), - central = centralDevice, - rssi = dataWritten.rssi, - txPower = dataWritten.txPower + version = dataWritten.v, + msg = dataWritten.msg, + org = dataWritten.org, + peripheral = TracerApp.asPeripheralDevice(), + central = centralDevice, + rssi = dataWritten.rssi, + txPower = dataWritten.txPower ) Utils.broadcastStreetPassReceived( - context, - connectionRecord + context, + connectionRecord ) } catch (e: Throwable) { CentralLog.e(TAG, "caught error here ${e.message}") diff --git a/app/src/main/java/au/gov/health/covidsafe/extensions/PermissionExtensions.kt b/app/src/main/java/au/gov/health/covidsafe/extensions/PermissionExtensions.kt index 841b489..da059db 100644 --- a/app/src/main/java/au/gov/health/covidsafe/extensions/PermissionExtensions.kt +++ b/app/src/main/java/au/gov/health/covidsafe/extensions/PermissionExtensions.kt @@ -1,22 +1,24 @@ package au.gov.health.covidsafe.extensions -import android.Manifest.permission.ACCESS_FINE_LOCATION +import android.Manifest.permission.ACCESS_COARSE_LOCATION import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothManager import android.content.Context import android.content.Intent +import android.location.LocationManager import android.os.Build import android.os.PowerManager import android.provider.Settings +import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.core.app.NotificationManagerCompat import androidx.fragment.app.Fragment import au.gov.health.covidsafe.R import au.gov.health.covidsafe.Utils -import pub.devrel.easypermissions.AppSettingsDialog import pub.devrel.easypermissions.EasyPermissions import pub.devrel.easypermissions.PermissionRequest + const val REQUEST_ENABLE_BT = 123 const val LOCATION = 345 const val BATTERY_OPTIMISER = 789 @@ -46,13 +48,13 @@ private fun Fragment.requestFineLocationAndCheckBleSupportThenNextPermission(onE if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { activity?.let { when { - EasyPermissions.hasPermissions(it, ACCESS_FINE_LOCATION) -> { + EasyPermissions.hasPermissions(it, ACCESS_COARSE_LOCATION) -> { checkBLESupport() excludeFromBatteryOptimization(onEndCallback) } else -> { EasyPermissions.requestPermissions( - PermissionRequest.Builder(this, LOCATION, ACCESS_FINE_LOCATION) + PermissionRequest.Builder(this, LOCATION, ACCESS_COARSE_LOCATION) .setRationale(R.string.permission_location_rationale) .build()) } @@ -113,12 +115,20 @@ fun Fragment.isPushNotificationEnabled(): Boolean? { } } -fun Fragment.isFineLocationEnabled(): Boolean? { +fun Fragment.isLocationPermissionAllowed(): Boolean? { return activity?.let { activity -> - EasyPermissions.hasPermissions(activity, ACCESS_FINE_LOCATION) + EasyPermissions.hasPermissions(activity, ACCESS_COARSE_LOCATION) } } +fun Fragment.isLocationEnabledOnDevice(): Boolean { + val locationManager = context?.getSystemService(Context.LOCATION_SERVICE) as LocationManager? + return locationManager?.let { + it.isProviderEnabled(LocationManager.GPS_PROVIDER) || + it.isProviderEnabled(LocationManager.NETWORK_PROVIDER) + } ?: false +} + fun Fragment.isBatteryOptimizationDisabled(): Boolean? { return activity?.let { activity -> val powerManager = activity.getSystemService(AppCompatActivity.POWER_SERVICE) as PowerManager? @@ -137,18 +147,24 @@ fun Fragment.isBatteryOptimizationDisabled(): Boolean? { fun Fragment.askForLocationPermission() { activity?.let { when { - EasyPermissions.hasPermissions(it, ACCESS_FINE_LOCATION) -> { - - } - EasyPermissions.somePermissionPermanentlyDenied(this, listOf(ACCESS_FINE_LOCATION)) -> { - AppSettingsDialog.Builder(this).build().show() - } - else -> { + !EasyPermissions.hasPermissions(it, ACCESS_COARSE_LOCATION) -> { EasyPermissions.requestPermissions( - PermissionRequest.Builder(this, LOCATION, ACCESS_FINE_LOCATION) + PermissionRequest.Builder(this, LOCATION, ACCESS_COARSE_LOCATION) .setRationale(R.string.permission_location_rationale) .build()) } + + !isLocationEnabledOnDevice() -> { + AlertDialog.Builder(it).apply { + setMessage(R.string.need_location_service) + setPositiveButton(android.R.string.ok) { _, _ -> + it.startActivity(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)) + } + }.create().show() + } + + else -> { + }// do nothing } } } diff --git a/app/src/main/java/au/gov/health/covidsafe/links/LinkBuilder.kt b/app/src/main/java/au/gov/health/covidsafe/links/LinkBuilder.kt new file mode 100644 index 0000000..70de689 --- /dev/null +++ b/app/src/main/java/au/gov/health/covidsafe/links/LinkBuilder.kt @@ -0,0 +1,165 @@ +package au.gov.health.covidsafe.links + +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.text.Html +import android.text.SpannableString +import android.text.Spanned +import android.text.TextPaint +import android.text.style.ClickableSpan +import android.view.View +import au.gov.health.covidsafe.R +import au.gov.health.covidsafe.TracerApp +import au.gov.health.covidsafe.logging.CentralLog +import java.lang.StringBuilder +import java.util.* + + +const val TAG = "LinkBuilder" + +private const val PRIVACY_URL = "https://www.health.gov.au/using-our-websites/privacy/privacy-notice-for-covidsafe-app" +private const val DEPARTMENT_OF_HEALTH_URL = "https://www.health.gov.au/" + +private const val HELP_TOPICS_BASE = "https://www.covidsafe.gov.au/help-topics" +private const val HELP_TOPICS_SUFFIX = ".html" +private const val HELP_TOPICS_ENGLISH_PAGE = "" +private const val HELP_TOPICS_S_CHINESE_PAGE = "/zh-hans" +private const val HELP_TOPICS_T_CHINESE_PAGE = "/zh-hant" +private const val HELP_TOPICS_ARABIC_PAGE = "/ar" +private const val HELP_TOPICS_VIETNAMESE_PAGE = "/vi" +private const val HELP_TOPICS_KOREAN_PAGE = "/ko" +private const val HELP_TOPICS_GREEK_PAGE = "/el" +private const val HELP_TOPICS_ITALIAN_PAGE = "/it" + +private const val HELP_TOPICS_ANCHOR_VERIFY_MOBILE_NUMBER_PIN = "#verify-mobile-number-pin" +private const val HELP_TOPICS_ANCHOR_BLUETOOTH_PAIRING_REQUEST = "#bluetooth-pairing-request" + +object LinkBuilder { + + private fun buildHtmlText(text: String) = + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { + Html.fromHtml(text, Html.FROM_HTML_MODE_COMPACT) + } else { + Html.fromHtml(text) + } + + fun getHelpTopicsUrl(): String { + val localeLanguageTag = Locale.getDefault().toLanguageTag() + + val url = HELP_TOPICS_BASE + when { + localeLanguageTag.startsWith("zh-Hans") -> HELP_TOPICS_S_CHINESE_PAGE + localeLanguageTag.startsWith("zh-Hant") -> HELP_TOPICS_T_CHINESE_PAGE + localeLanguageTag.startsWith("ar") -> HELP_TOPICS_ARABIC_PAGE + localeLanguageTag.startsWith("vi") -> HELP_TOPICS_VIETNAMESE_PAGE + localeLanguageTag.startsWith("ko") -> HELP_TOPICS_KOREAN_PAGE + localeLanguageTag.startsWith("el") -> HELP_TOPICS_GREEK_PAGE + localeLanguageTag.startsWith("it") -> HELP_TOPICS_ITALIAN_PAGE + else -> HELP_TOPICS_ENGLISH_PAGE + } + HELP_TOPICS_SUFFIX + + + CentralLog.d(TAG, "getHelpTopicsUrl() " + + "localeLanguageTag = $localeLanguageTag " + + "url = $url") + + return url + } + + private fun getBluetoothPairingRequestUrl() = + getHelpTopicsUrl() + HELP_TOPICS_ANCHOR_BLUETOOTH_PAIRING_REQUEST + + private fun getVerifyMobileNumberPinLink(linkText: String) = buildHtmlText( + "$linkText") + + class LinkSpan(private val context: Context, private val linkURL: String) : ClickableSpan() { + override fun onClick(widget: View) { + val intent = Intent(Intent.ACTION_VIEW, Uri.parse(linkURL)) + context.startActivity(intent) + } + + override fun updateDrawState(ds: TextPaint) { + super.updateDrawState(ds) + + ds.isUnderlineText = true +// ds.typeface = Typeface.create(Typeface.DEFAULT, Typeface.ITALIC) + } + } + + private fun buildSpannableStringContent(context: Context, originalString: String, links: List): SpannableString { + val stringBuilder = StringBuilder() + val spanStartEndIndex = mutableListOf>() + + val split = (" $originalString").split("*") + + split.forEachIndexed { index, s -> + if (index % 2 == 1) { + val start = stringBuilder.length - 1 + spanStartEndIndex.add(Pair(start, start + s.length)) + } + + stringBuilder.append(s) + } + + val retVal = SpannableString(stringBuilder.toString().trim()) + + spanStartEndIndex.forEachIndexed { index, pair -> + retVal.setSpan(LinkSpan(context, links[index]), pair.first, pair.second, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) + } + + return retVal + } + + fun getHowCOVIdSafeWorksContent(context: Context): SpannableString { + return buildSpannableStringContent( + context, + TracerApp.AppContext.getString(R.string.how_it_works_content), + listOf(getHelpTopicsUrl()) + ) + } + + fun getRegistrationAndPrivacyContent(context: Context): SpannableString { + return buildSpannableStringContent( + context, + TracerApp.AppContext.getString(R.string.data_privacy_content), + listOf( + PRIVACY_URL, + PRIVACY_URL, + getHelpTopicsUrl(), + DEPARTMENT_OF_HEALTH_URL, + PRIVACY_URL + ) + ) + } + + fun getHowPermissionSuccessContent(context: Context): SpannableString { + return buildSpannableStringContent( + context, + TracerApp.AppContext.getString(R.string.permission_success_content), + listOf(getBluetoothPairingRequestUrl()) + ) + } + + fun getIssuesReceivingPINContent(): Spanned { + val linkText = TracerApp.AppContext.getString(R.string.ReceivePinIssue) + return getVerifyMobileNumberPinLink(linkText).also { + CentralLog.d(TAG, "getIssuesReceivingPINContent() returns $it") + } + } + + fun getNoBluetoothPairingContent(context: Context): SpannableString { + return buildSpannableStringContent( + context, + TracerApp.AppContext.getString(R.string.home_header_no_pairing), + listOf(getBluetoothPairingRequestUrl()) + ) + } + + fun getUploadConsentContent(context: Context): SpannableString { + return buildSpannableStringContent( + context, + TracerApp.AppContext.getString(R.string.upload_step_4_sub_header), + listOf(PRIVACY_URL) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/au/gov/health/covidsafe/logging/CentralLog.kt b/app/src/main/java/au/gov/health/covidsafe/logging/CentralLog.kt index be27312..3a2ffe1 100644 --- a/app/src/main/java/au/gov/health/covidsafe/logging/CentralLog.kt +++ b/app/src/main/java/au/gov/health/covidsafe/logging/CentralLog.kt @@ -72,7 +72,7 @@ class CentralLog { Log.e(tag, getIdleStatus() + message) } - fun e(tag: String, message: String, exception: Exception) { + fun e(tag: String, message: String, exception: Exception?) { if (!shouldLog()) { return } diff --git a/app/src/main/java/au/gov/health/covidsafe/networking/response/MessagesResponse.kt b/app/src/main/java/au/gov/health/covidsafe/networking/response/MessagesResponse.kt new file mode 100644 index 0000000..13416cf --- /dev/null +++ b/app/src/main/java/au/gov/health/covidsafe/networking/response/MessagesResponse.kt @@ -0,0 +1,6 @@ +package au.gov.health.covidsafe.networking.response + +import androidx.annotation.Keep + +@Keep +data class MessagesResponse(val message: String, val forceappupgrade: Boolean) \ No newline at end of file diff --git a/app/src/main/java/au/gov/health/covidsafe/networking/service/AwsClient.kt b/app/src/main/java/au/gov/health/covidsafe/networking/service/AwsClient.kt index 1514347..2dfb6cb 100644 --- a/app/src/main/java/au/gov/health/covidsafe/networking/service/AwsClient.kt +++ b/app/src/main/java/au/gov/health/covidsafe/networking/service/AwsClient.kt @@ -5,29 +5,37 @@ import au.gov.health.covidsafe.networking.request.AuthChallengeRequest import au.gov.health.covidsafe.networking.request.OTPChallengeRequest import au.gov.health.covidsafe.networking.response.* import retrofit2.Call -import retrofit2.http.Body -import retrofit2.http.GET -import retrofit2.http.Header -import retrofit2.http.POST +import retrofit2.http.* interface AwsClient { @POST(BuildConfig.END_POINT_PREFIX + "/initiateAuth") - fun initiateAuth(@Body body : OTPChallengeRequest) : Call + fun initiateAuth(@Body body: OTPChallengeRequest): Call @POST(BuildConfig.END_POINT_PREFIX + "/respondToAuthChallenge") - fun respondToAuthChallenge(@Body body : AuthChallengeRequest) : Call + fun respondToAuthChallenge(@Body body: AuthChallengeRequest): Call @GET(BuildConfig.END_POINT_PREFIX + "/getTempId") - fun getTempId(@Header("Authorization") jwtToken: String?) : Call + fun getTempId(@Header("Authorization") jwtToken: String?): Call @GET(BuildConfig.END_POINT_PREFIX + "/initiateDataUpload") - fun initiateUpload(@Header("Authorization") jwtToken: String?,@Header("pin") pin : String) : Call + fun initiateUpload( + @Header("Authorization") jwtToken: String?, + @Header("pin") pin: String + ): Call @GET(BuildConfig.END_POINT_PREFIX + "/initiateDataUpload") fun initiateReUpload(@Header("Authorization") jwtToken: String?): Call @GET(BuildConfig.END_POINT_PREFIX + "/requestUploadOtp") - fun requestUploadOtp(@Header("Authorization") jwtToken : String?) : Call + fun requestUploadOtp(@Header("Authorization") jwtToken: String?): Call + + @GET(BuildConfig.END_POINT_PREFIX + "/messages") + fun getMessages( + @Header("Authorization") jwtToken: String?, + @Query("os") os: String, + @Query("appversion") appversion: String, + @Query("token") token: String + ): Call } \ No newline at end of file diff --git a/app/src/main/java/au/gov/health/covidsafe/notifications/NotificationTemplates.kt b/app/src/main/java/au/gov/health/covidsafe/notifications/NotificationTemplates.kt index 4003c50..7df8b20 100644 --- a/app/src/main/java/au/gov/health/covidsafe/notifications/NotificationTemplates.kt +++ b/app/src/main/java/au/gov/health/covidsafe/notifications/NotificationTemplates.kt @@ -20,14 +20,14 @@ class NotificationTemplates { fun getRunningNotification(context: Context, channel: String): Notification { val intent = Intent(context, HomeActivity::class.java) + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) val activityPendingIntent = PendingIntent.getActivity( - context, PENDING_ACTIVITY, + context, + PENDING_ACTIVITY, intent, 0 ) - val zeroHeightView = RemoteViews(context.packageName, R.layout.zero_height_view) - val builder = NotificationCompat.Builder(context, channel) .setContentTitle(context.getText(R.string.service_ok_title)) .setContentText(context.getText(R.string.service_ok_body)) @@ -41,15 +41,13 @@ class NotificationTemplates { .setSound(null) .setVibrate(null) .setColor(ContextCompat.getColor(context, R.color.notification_tint)) - .setCustomContentView(zeroHeightView) return builder.build() } fun lackingThingsNotification(context: Context, channel: String): Notification { - - val intent = Intent(context, HomeActivity::class.java) + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) intent.putExtra("page", 3) val activityPendingIntent = PendingIntent.getActivity( @@ -79,40 +77,6 @@ class NotificationTemplates { return builder.build() } - - fun getUploadReminder(context: Context, channel: String): Notification { - val intent = Intent(context, HomeActivity::class.java) - intent.putExtra("uploadNotification", true) - - val activityPendingIntent = PendingIntent.getActivity( - context, DAILY_UPLOAD_NOTIFICATION_CODE, - intent, PendingIntent.FLAG_UPDATE_CURRENT - ) - - val builder = NotificationCompat.Builder(context, channel) - .setContentTitle(context.getText(R.string.upload_your_data_title)) - .setContentText(context.getText(R.string.upload_your_data_description)) - .setOngoing(false) - .setPriority(NotificationCompat.PRIORITY_DEFAULT) - .setSmallIcon(R.drawable.ic_notification_icon) - .setTicker(context.getText(R.string.upload_your_data_description)) - .setStyle(NotificationCompat.BigTextStyle().bigText(context.getText(R.string.upload_your_data_description))) - .setAutoCancel(true) - - .addAction( - R.drawable.ic_notification_setting, - context.getText(R.string.upload_data_action), - activityPendingIntent - ) - .setContentIntent(activityPendingIntent) - .setWhen(System.currentTimeMillis()) - .setSound(null) - .setVibrate(null) - .setColor(ContextCompat.getColor(context, R.color.notification_tint)) - - return builder.build() - } - } } diff --git a/app/src/main/java/au/gov/health/covidsafe/scheduler/GetMessagesScheduler.kt b/app/src/main/java/au/gov/health/covidsafe/scheduler/GetMessagesScheduler.kt new file mode 100644 index 0000000..a83960d --- /dev/null +++ b/app/src/main/java/au/gov/health/covidsafe/scheduler/GetMessagesScheduler.kt @@ -0,0 +1,101 @@ +package au.gov.health.covidsafe.scheduler + +import android.app.job.JobInfo +import android.app.job.JobParameters +import android.app.job.JobScheduler +import android.app.job.JobService +import android.content.ComponentName +import android.content.Context +import au.gov.health.covidsafe.BuildConfig +import au.gov.health.covidsafe.Preference +import au.gov.health.covidsafe.TracerApp +import au.gov.health.covidsafe.factory.NetworkFactory.Companion.awsClient +import au.gov.health.covidsafe.logging.CentralLog +import au.gov.health.covidsafe.networking.response.MessagesResponse +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response + + +private const val TAG = "GetMessagesScheduler" +private const val GET_MESSAGES_JOB_ID = 1 +private const val TWENTY_FOUR_HOURS_IN_MILLIS = 24 * 60 * 60 * 1000L + +// for testing only +//private const val TWENTY_FOUR_HOURS_IN_MILLIS = 16 * 60 * 1000L + +class GetMessagesJobSchedulerService : JobService() { + override fun onStartJob(params: JobParameters): Boolean { + CentralLog.d(TAG, "onStartJob()") + GetMessagesScheduler.getMessages(this, params) + return true + } + + override fun onStopJob(params: JobParameters): Boolean { + CentralLog.d(TAG, "onStopJob()") + return true + } +} + +object GetMessagesScheduler { + fun scheduleGetMessagesJob() { + CentralLog.d(TAG, "scheduleGetMessagesJob()") + + val context = TracerApp.AppContext + + (context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler?) + ?.let { + CentralLog.d(TAG, "JobScheduler available") + + it.schedule( + JobInfo.Builder(GET_MESSAGES_JOB_ID, + ComponentName(context, GetMessagesJobSchedulerService::class.java)) + .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) + .setPersisted(true) + .setPeriodic(TWENTY_FOUR_HOURS_IN_MILLIS) + .build() + ) + } + } + + fun getMessages(jobService: JobService? = null, params: JobParameters? = null) { + val context = TracerApp.AppContext + + val jwtToken = Preference.getEncrypterJWTToken(context) + val os = "android-${android.os.Build.VERSION.SDK_INT}" + val appVersion = "${BuildConfig.VERSION_CODE}" + val token = Preference.getFirebaseInstanceID(context) + + val messagesCall: Call = awsClient.getMessages( + "Bearer $jwtToken", + os, + appVersion, + token + ) + + CentralLog.d(TAG, "getMessages() to be called with InstanceID = $token") + + messagesCall.enqueue(object : Callback { + override fun onResponse(call: Call, response: Response) { + val responseCode = response.code() + if (responseCode == 200) { + CentralLog.d(TAG, "onResponse() got 200 response.") + + response.body()?.let { + CentralLog.d(TAG, "onResponse() MessagesResponse = $it") + } + } else { + CentralLog.w(TAG, "onResponse() got error response code = $responseCode.") + } + + jobService?.jobFinished(params, false) + } + + override fun onFailure(call: Call, t: Throwable) { + CentralLog.e(TAG, "onResponse() failed $t") + + jobService?.jobFinished(params, false) + } + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/au/gov/health/covidsafe/services/CovidFirebaseMessagingService.kt b/app/src/main/java/au/gov/health/covidsafe/services/CovidFirebaseMessagingService.kt new file mode 100644 index 0000000..dfb830d --- /dev/null +++ b/app/src/main/java/au/gov/health/covidsafe/services/CovidFirebaseMessagingService.kt @@ -0,0 +1,49 @@ +package au.gov.health.covidsafe.services + +import au.gov.health.covidsafe.* +import au.gov.health.covidsafe.logging.CentralLog +import com.google.firebase.messaging.FirebaseMessagingService +import com.google.firebase.messaging.RemoteMessage + +private const val TAG = "CovidFirebaseMessagingService" + +class CovidFirebaseMessagingService : FirebaseMessagingService() { + + /** + * Called when message is received. + * + * @param remoteMessage Object representing the message received from Firebase Cloud Messaging. + */ + override fun onMessageReceived(remoteMessage: RemoteMessage) { + // There are two types of messages data messages and notification messages. Data messages are handled + // here in onMessageReceived whether the app is in the foreground or background. Data messages are the type + // traditionally used with GCM. Notification messages are only received here in onMessageReceived when the app + // is in the foreground. When the app is in the background an automatically generated notification is displayed. + // When the user taps on the notification they are returned to the app. Messages containing both notification + // and data payloads are treated as notification messages. The Firebase console always sends notification + // messages. For more see: https://firebase.google.com/docs/cloud-messaging/concept-options + + // Not getting messages here? See why this may be: https://goo.gl/39bRNJ + CentralLog.d(TAG, "onMessageReceived() received message from ${remoteMessage.from}") + + // log notification payload. + remoteMessage.notification?.let { + CentralLog.d(TAG, "onMessageReceived() notification = ${it.title} ${it.body}") + + } + + // log data payload. + remoteMessage.data.isNotEmpty().let { + CentralLog.d(TAG, "onMessageReceived() data = " + remoteMessage.data) + } + } + + /** + * Called when InstanceID token is updated. + */ + override fun onNewToken(token: String) { + CentralLog.d(TAG, "onNewToken() InstanceID = $token") + + Preference.putFirebaseInstanceID(TracerApp.AppContext, token) + } +} \ No newline at end of file diff --git a/app/src/main/java/au/gov/health/covidsafe/streetpass/StreetPassWorker.kt b/app/src/main/java/au/gov/health/covidsafe/streetpass/StreetPassWorker.kt index f0f62b9..2000c43 100644 --- a/app/src/main/java/au/gov/health/covidsafe/streetpass/StreetPassWorker.kt +++ b/app/src/main/java/au/gov/health/covidsafe/streetpass/StreetPassWorker.kt @@ -604,12 +604,17 @@ class StreetPassWorker(val context: Context) { //we are writing as the central device val thisCentralDevice = TracerApp.asCentralDevice() + val plainRecord = gson.toJson(EncryptedWriteRequestPayload( + System.currentTimeMillis() / 1000L, thisCentralDevice.modelC, work.connectable.rssi, work.connectable.transmissionPower, - TracerApp.thisDeviceMsg())).toByteArray(Charsets.UTF_8) - val remoteBlob = Encryption.encryptPayload(plainRecord) + TracerApp.thisDeviceMsg())) + + CentralLog.d(TAG, "onCharacteristicRead plainRecord = $plainRecord") + + val remoteBlob = Encryption.encryptPayload(plainRecord.toByteArray(Charsets.UTF_8)) val writedata = WriteRequestPayload( v = TracerApp.protocolVersion, @@ -641,7 +646,7 @@ class StreetPassWorker(val context: Context) { } } - inner class EncryptedWriteRequestPayload(val modelC: String, val rssi: Int, val txPower: Int?, val msg : String) + inner class EncryptedWriteRequestPayload(val timestamp: Long, val modelC: String, val rssi: Int, val txPower: Int?, val msg: String) override fun onCharacteristicWrite( gatt: BluetoothGatt, diff --git a/app/src/main/java/au/gov/health/covidsafe/talkback/TalkBack.kt b/app/src/main/java/au/gov/health/covidsafe/talkback/TalkBack.kt new file mode 100644 index 0000000..e6ab614 --- /dev/null +++ b/app/src/main/java/au/gov/health/covidsafe/talkback/TalkBack.kt @@ -0,0 +1,46 @@ +package au.gov.health.covidsafe.talkback + +import android.view.View +import android.widget.TextView +import au.gov.health.covidsafe.R +import au.gov.health.covidsafe.TracerApp +import au.gov.health.covidsafe.logging.CentralLog +import java.lang.StringBuilder + +private const val TAG = "TalkBack.kt" + +private fun getHeadingLabel(): String { + return TracerApp.AppContext.getString(R.string.heading) +} + +private fun convertNumberToDigits(originalText: String): String { + val stringBuilder = StringBuilder() + + originalText.forEach { + if (it.isDigit()) { + stringBuilder.append(", ") + } + + stringBuilder.append(it) + } + + return stringBuilder.toString() +} + +fun setHeading(view: View, shouldConvertNumberToDigits: Boolean = false) { + if (view is TextView) { + val content = if (shouldConvertNumberToDigits) { + convertNumberToDigits(view.text.toString()) + } else { + view.text.toString() + } + + view.contentDescription = "${content}, ${getHeadingLabel()}".also { + CentralLog.d(TAG, it) + } + } +} + +fun TextView.setHeading(shouldConvertNumberToDigits: Boolean = false) { + setHeading(this, shouldConvertNumberToDigits) +} \ No newline at end of file diff --git a/app/src/main/java/au/gov/health/covidsafe/ui/BaseFragment.kt b/app/src/main/java/au/gov/health/covidsafe/ui/BaseFragment.kt index b9b45f6..6686fb7 100644 --- a/app/src/main/java/au/gov/health/covidsafe/ui/BaseFragment.kt +++ b/app/src/main/java/au/gov/health/covidsafe/ui/BaseFragment.kt @@ -1,10 +1,13 @@ package au.gov.health.covidsafe.ui +import android.content.res.Configuration import android.os.Bundle +import android.view.View import androidx.fragment.app.Fragment import androidx.navigation.Navigator import androidx.navigation.fragment.NavHostFragment import au.gov.health.covidsafe.HasBlockingState +import kotlinx.android.synthetic.main.fragment_intro.* open class BaseFragment : Fragment() { @@ -31,4 +34,11 @@ open class BaseFragment : Fragment() { } NavHostFragment.findNavController(this).popBackStack() } + + protected fun removeViewInLandscapeMode(viewToRemove: View){ + if (requireContext().resources.configuration.orientation == + Configuration.ORIENTATION_LANDSCAPE){ + viewToRemove.visibility = View.GONE + } + } } \ No newline at end of file diff --git a/app/src/main/java/au/gov/health/covidsafe/ui/home/HelpFragment.kt b/app/src/main/java/au/gov/health/covidsafe/ui/home/HelpFragment.kt index 530a779..4e8e4c2 100644 --- a/app/src/main/java/au/gov/health/covidsafe/ui/home/HelpFragment.kt +++ b/app/src/main/java/au/gov/health/covidsafe/ui/home/HelpFragment.kt @@ -16,19 +16,12 @@ import com.atlassian.mobilekit.module.feedback.FeedbackModule import kotlinx.android.synthetic.main.fragment_help.* import kotlinx.android.synthetic.main.fragment_help.view.* import au.gov.health.covidsafe.R +import au.gov.health.covidsafe.links.LinkBuilder import au.gov.health.covidsafe.logging.CentralLog import au.gov.health.covidsafe.ui.BaseFragment import kotlinx.android.synthetic.main.activity_country_code_selection.* import java.util.* -private const val HELP_URL_BASE = "https://www.covidsafe.gov.au/help-topics" -private const val HELP_URL_ENGLISH_PAGE = ".html" -private const val HELP_URL_ARABIC_PAGE = "/ar.html" -private const val HELP_URL_VIETNAMESE_PAGE = "/vi.html" -private const val HELP_URL_KOREAN_PAGE = "/ko.html" -private const val HELP_URL_SIMPLIFIED_CHINESE_PAGE = "/zh-hans.html" -private const val HELP_URL_TRADITIONAL_CHINESE_PAGE = "/zh-hant.html" - private const val TAG = "HelpFragment" class HelpFragment : BaseFragment() { @@ -44,7 +37,7 @@ class HelpFragment : BaseFragment() { val webView = view.helpWebView webView.settings.javaScriptEnabled = true webView.webViewClient = createWebVieClient(view) - webView.loadUrl(getHelpUrlBasedOnLocaleLanguage()) + webView.loadUrl(LinkBuilder.getHelpTopicsUrl()) reportAnIssue.setOnClickListener { FeedbackModule.showFeedbackScreen() } @@ -65,7 +58,7 @@ class HelpFragment : BaseFragment() { if (!loadFinished) isRedirecting = true loadFinished = false val urlString = request.url.toString() - if (urlString.startsWith(HELP_URL_BASE)) { + if (urlString.startsWith(LinkBuilder.getHelpTopicsUrl())) { webView.loadUrl(request.url.toString()) } else { val intent = Intent(Intent.ACTION_VIEW, Uri.parse(urlString)) @@ -94,25 +87,7 @@ class HelpFragment : BaseFragment() { } - private fun getHelpUrlBasedOnLocaleLanguage(): String { - val localeLanguageTag = Locale.getDefault().toLanguageTag() - val url = HELP_URL_BASE + when { - localeLanguageTag.startsWith("zh-Hans") -> HELP_URL_SIMPLIFIED_CHINESE_PAGE - localeLanguageTag.startsWith("zh-Hant") -> HELP_URL_TRADITIONAL_CHINESE_PAGE - localeLanguageTag.startsWith("ar") -> HELP_URL_ARABIC_PAGE - localeLanguageTag.startsWith("vi") -> HELP_URL_VIETNAMESE_PAGE - localeLanguageTag.startsWith("ko") -> HELP_URL_KOREAN_PAGE - else -> HELP_URL_ENGLISH_PAGE - } - - - CentralLog.d(TAG, "getHelpUrlBasedOnLocaleLanguage() " + - "localeLanguageTag = $localeLanguageTag " + - "url = $url") - - return url - } } diff --git a/app/src/main/java/au/gov/health/covidsafe/ui/home/HomeFragment.kt b/app/src/main/java/au/gov/health/covidsafe/ui/home/HomeFragment.kt index 9abd8a9..55251e0 100644 --- a/app/src/main/java/au/gov/health/covidsafe/ui/home/HomeFragment.kt +++ b/app/src/main/java/au/gov/health/covidsafe/ui/home/HomeFragment.kt @@ -13,6 +13,7 @@ import android.view.View import android.view.View.GONE import android.view.View.VISIBLE import android.view.ViewGroup +import android.view.accessibility.AccessibilityEvent import androidx.core.content.ContextCompat import androidx.navigation.fragment.findNavController import au.gov.health.covidsafe.BuildConfig @@ -20,6 +21,8 @@ import au.gov.health.covidsafe.Preference import au.gov.health.covidsafe.R import au.gov.health.covidsafe.WebViewActivity import au.gov.health.covidsafe.extensions.* +import au.gov.health.covidsafe.links.LinkBuilder +import au.gov.health.covidsafe.talkback.setHeading import au.gov.health.covidsafe.ui.BaseFragment import kotlinx.android.synthetic.main.fragment_home.* import kotlinx.android.synthetic.main.fragment_home.view.* @@ -31,7 +34,8 @@ import pub.devrel.easypermissions.EasyPermissions import java.text.SimpleDateFormat import java.util.* -private const val FOURTEEN_DAYS_IN_MILLIS = 14 * 24 * 60 * 60 * 1000L +private const val ONE_DAY_IN_MILLIS = 24 * 60 * 60 * 1000L +private const val FOURTEEN_DAYS_IN_MILLIS = 14 * ONE_DAY_IN_MILLIS class HomeFragment : BaseFragment(), EasyPermissions.PermissionCallbacks { @@ -91,6 +95,19 @@ class HomeFragment : BaseFragment(), EasyPermissions.PermissionCallbacks { override fun onResume() { super.onResume() + + // display app update reminder +// if (System.currentTimeMillis() +// - +// Preference.getAppUpdateReminderDismissedTime(requireContext()) +// > ONE_DAY_IN_MILLIS +// ) { +// app_update_reminder.visibility = VISIBLE +// } + + // disable the app update reminder for now + app_update_reminder.visibility = GONE + bluetooth_card_view.setOnClickListener { requestBlueToothPermissionThenNextPermission() } location_card_view.setOnClickListener { askForLocationPermission() } battery_card_view.setOnClickListener { excludeFromBatteryOptimization() } @@ -112,11 +129,21 @@ class HomeFragment : BaseFragment(), EasyPermissions.PermissionCallbacks { findNavController().navigate(HomeFragmentDirections.actionHomeFragmentToHelpFragment()) } + go_to_play_store.setOnClickListener { + app_update_reminder.visibility = GONE + } + + remind_me_later.setOnClickListener { + Preference.putAppUpdateReminderDismissedTime(requireContext()) + app_update_reminder.visibility = GONE + } + if (!mIsBroadcastListenerRegistered) { registerBroadcast() } refreshSetupCompleteOrIncompleteUi() + home_header_no_bluetooth_pairing.text = LinkBuilder.getNoBluetoothPairingContent(requireContext()) home_header_no_bluetooth_pairing.movementMethod = LinkMovementMethod.getInstance() } @@ -176,12 +203,12 @@ class HomeFragment : BaseFragment(), EasyPermissions.PermissionCallbacks { ) val line2 = if (isDataUploadedInPast14Days) { - "

" + it.getString(R.string.home_header_uploaded_on_date, getDataUploadDateHtmlString(it)) + it.getString(R.string.home_header_uploaded_on_date, getDataUploadDateHtmlString(it)) + "
" } else { "" } - val line3 = "
" + it.getString( + val line3 = it.getString( if (isAllPermissionsEnabled) { R.string.home_header_active_no_action_required } else { @@ -189,25 +216,44 @@ class HomeFragment : BaseFragment(), EasyPermissions.PermissionCallbacks { } ) - val headerHtmlText = "$line1$line2$line3" + home_header_setup_complete_header_line_1.text = line1 - home_header_setup_complete_header.text = + home_header_setup_complete_header_line_1.setHeading() + home_header_setup_complete_header_line_1.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) + + val line2and3 = "$line2$line3" + + home_header_setup_complete_header_line_2.text = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { - Html.fromHtml(headerHtmlText, Html.FROM_HTML_MODE_COMPACT) + Html.fromHtml(line2and3, Html.FROM_HTML_MODE_COMPACT) } else { - Html.fromHtml(headerHtmlText) + Html.fromHtml(line2and3) } if (isAllPermissionsEnabled) { + home_header_top_left_icon.visibility = GONE + home_header_picture_setup_complete.layoutParams.let { layoutParams -> + val size = resources.getDimensionPixelSize(R.dimen.covidsafe_lottie_size) + layoutParams.height = size + layoutParams.width = size + } home_header_picture_setup_complete.setAnimation("spinner_home.json") home_header_picture_setup_complete.resumeAnimation() + content_setup_incomplete_group.visibility = GONE ContextCompat.getColor(it, R.color.lighter_green).let { bgColor -> header_background.setBackgroundColor(bgColor) header_background_overlap.setBackgroundColor(bgColor) } } else { - home_header_picture_setup_complete.setImageResource(R.drawable.ic_logo_home_inactive) + home_header_top_left_icon.visibility = VISIBLE + home_header_picture_setup_complete.layoutParams.let { layoutParams -> + val size = resources.getDimensionPixelSize(R.dimen.keyline_8) + layoutParams.height = size + layoutParams.width = size + } + home_header_picture_setup_complete.setImageResource(R.drawable.ic_red_cross) + content_setup_incomplete_group.visibility = VISIBLE updateBlueToothStatus() updatePushNotificationStatus() @@ -227,12 +273,13 @@ class HomeFragment : BaseFragment(), EasyPermissions.PermissionCallbacks { val bluetoothEnabled = isBlueToothEnabled() ?: false val pushNotificationEnabled = isPushNotificationEnabled() ?: true val nonBatteryOptimizationAllowed = isBatteryOptimizationDisabled() ?: true - val locationStatusAllowed = isFineLocationEnabled() ?: true + val locationStatusAllowed = isLocationPermissionAllowed() ?: true return bluetoothEnabled && pushNotificationEnabled && nonBatteryOptimizationAllowed && - locationStatusAllowed + locationStatusAllowed && + isLocationEnabledOnDevice() } private fun registerBroadcast() { @@ -251,7 +298,6 @@ class HomeFragment : BaseFragment(), EasyPermissions.PermissionCallbacks { val newIntent = Intent(Intent.ACTION_SEND) newIntent.type = "text/plain" newIntent.putExtra(Intent.EXTRA_TEXT, getString(R.string.share_this_app_content)) - newIntent.putExtra(Intent.EXTRA_HTML_TEXT, getString(R.string.share_this_app_content_html)) startActivity(Intent.createChooser(newIntent, null)) } @@ -291,11 +337,13 @@ class HomeFragment : BaseFragment(), EasyPermissions.PermissionCallbacks { } private fun updateLocationStatus() { - isFineLocationEnabled()?.let { + isLocationPermissionAllowed()?.let { + val locationWorking = it && isLocationEnabledOnDevice() + location_card_view.visibility = VISIBLE - location_card_view.render(formatLocationTitle(it), it) + location_card_view.render(formatLocationTitle(locationWorking), locationWorking) } ?: run { - location_card_view.visibility = VISIBLE + location_card_view.visibility = GONE } } @@ -352,7 +400,7 @@ class HomeFragment : BaseFragment(), EasyPermissions.PermissionCallbacks { } override fun onPermissionsDenied(requestCode: Int, perms: MutableList) { - if (requestCode == LOCATION && EasyPermissions.somePermissionPermanentlyDenied(this, listOf(Manifest.permission.ACCESS_FINE_LOCATION))) { + if (requestCode == LOCATION && EasyPermissions.somePermissionPermanentlyDenied(this, listOf(Manifest.permission.ACCESS_COARSE_LOCATION))) { AppSettingsDialog.Builder(this).build().show() } } diff --git a/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/CountryList.kt b/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/CountryList.kt index c1ab807..238a69d 100644 --- a/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/CountryList.kt +++ b/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/CountryList.kt @@ -20,224 +20,224 @@ object CountryList { private fun getCountries(): List { return listOf( - CountryListItem(R.string.country_al, 355, R.drawable.ic_list_country_al), - CountryListItem(R.string.country_dz, 213, R.drawable.ic_list_country_dz), -// CountryListItem(R.string.country_ad, 376, R.drawable.ic_list_country_ad), - CountryListItem(R.string.country_ao, 244, R.drawable.ic_list_country_ao), - CountryListItem(R.string.country_ai, 1, R.drawable.ic_list_country_ai), - CountryListItem(R.string.country_ag, 1, R.drawable.ic_list_country_ag), - CountryListItem(R.string.country_ar, 54, R.drawable.ic_list_country_ar), - CountryListItem(R.string.country_am, 374, R.drawable.ic_list_country_am), - CountryListItem(R.string.country_aw, 297, R.drawable.ic_list_country_aw), - CountryListItem(R.string.country_au, 61, R.drawable.ic_list_country_au), - CountryListItem(R.string.country_at, 43, R.drawable.ic_list_country_at), - CountryListItem(R.string.country_az, 994, R.drawable.ic_list_country_az), - CountryListItem(R.string.country_bs, 1, R.drawable.ic_list_country_bs), - CountryListItem(R.string.country_bh, 973, R.drawable.ic_list_country_bh), - CountryListItem(R.string.country_bd, 880, R.drawable.ic_list_country_bd), - CountryListItem(R.string.country_bb, 1, R.drawable.ic_list_country_bb), - CountryListItem(R.string.country_by, 375, R.drawable.ic_list_country_by), - CountryListItem(R.string.country_be, 32, R.drawable.ic_list_country_be), - CountryListItem(R.string.country_bz, 501, R.drawable.ic_list_country_bz), - CountryListItem(R.string.country_bj, 229, R.drawable.ic_list_country_bj), - CountryListItem(R.string.country_bm, 1, R.drawable.ic_list_country_bm), -// CountryListItem(R.string.country_bt, 975, R.drawable.ic_list_country_bt), - CountryListItem(R.string.country_bo, 591, R.drawable.ic_list_country_bo), - CountryListItem(R.string.country_ba, 387, R.drawable.ic_list_country_ba), - CountryListItem(R.string.country_bw, 267, R.drawable.ic_list_country_bw), - CountryListItem(R.string.country_br, 55, R.drawable.ic_list_country_br), -// CountryListItem(R.string.country_bn, 673, R.drawable.ic_list_country_bn), - CountryListItem(R.string.country_vg, 1, R.drawable.ic_list_country_vg), - CountryListItem(R.string.country_bg, 359, R.drawable.ic_list_country_bg), - CountryListItem(R.string.country_bf, 226, R.drawable.ic_list_country_bf), -// CountryListItem(R.string.country_bi, 257, R.drawable.ic_list_country_bi), - CountryListItem(R.string.country_kh, 855, R.drawable.ic_list_country_kh), - CountryListItem(R.string.country_cm, 237, R.drawable.ic_list_country_cm), - CountryListItem(R.string.country_ca, 1, R.drawable.ic_list_country_ca), - CountryListItem(R.string.country_cv, 238, R.drawable.ic_list_country_cv), - CountryListItem(R.string.country_ky, 1, R.drawable.ic_list_country_ky), -// CountryListItem(R.string.country_cf, 236, R.drawable.ic_list_country_cf), -// CountryListItem(R.string.country_td, 235, R.drawable.ic_list_country_td), - CountryListItem(R.string.country_cl, 56, R.drawable.ic_list_country_cl), - CountryListItem(R.string.country_cn, 86, R.drawable.ic_list_country_cn), - CountryListItem(R.string.country_co, 57, R.drawable.ic_list_country_co), -// CountryListItem(R.string.country_km, 269, R.drawable.ic_list_country_km), -// CountryListItem(R.string.country_ck, 682, R.drawable.ic_list_country_ck), - CountryListItem(R.string.country_cr, 506, R.drawable.ic_list_country_cr), - CountryListItem(R.string.country_hr, 385, R.drawable.ic_list_country_hr), - CountryListItem(R.string.country_cu, 53, R.drawable.ic_list_country_cu), - CountryListItem(R.string.country_cw, 599, R.drawable.ic_list_country_cw), - CountryListItem(R.string.country_cy, 357, R.drawable.ic_list_country_cy), - CountryListItem(R.string.country_cz, 420, R.drawable.ic_list_country_cz), -// CountryListItem(R.string.country_cd, 243, R.drawable.ic_list_country_cd), - CountryListItem(R.string.country_dk, 45, R.drawable.ic_list_country_dk), -// CountryListItem(R.string.country_dj, 253, R.drawable.ic_list_country_dj), - CountryListItem(R.string.country_dm, 1, R.drawable.ic_list_country_dm), - CountryListItem(R.string.country_do, 1, R.drawable.ic_list_country_do), - CountryListItem(R.string.country_ec, 593, R.drawable.ic_list_country_ec), -// CountryListItem(R.string.country_eg, 20, R.drawable.ic_list_country_eg), - CountryListItem(R.string.country_sv, 503, R.drawable.ic_list_country_sv), -// CountryListItem(R.string.country_gq, 240, R.drawable.ic_list_country_gq), - CountryListItem(R.string.country_ee, 372, R.drawable.ic_list_country_ee), -// CountryListItem(R.string.country_et, 251, R.drawable.ic_list_country_et), -// CountryListItem(R.string.country_fo, 298, R.drawable.ic_list_country_fo), - CountryListItem(R.string.country_fj, 679, R.drawable.ic_list_country_fj), - CountryListItem(R.string.country_fi, 358, R.drawable.ic_list_country_fi), - CountryListItem(R.string.country_fr, 33, R.drawable.ic_list_country_fr), -// CountryListItem(R.string.country_gf, 995, R.drawable.ic_list_country_fr), - CountryListItem(R.string.country_ga, 241, R.drawable.ic_list_country_ga), -// CountryListItem(R.string.country_gm, 220, R.drawable.ic_list_country_gm), - CountryListItem(R.string.country_ge, 995, R.drawable.ic_list_country_ge), - CountryListItem(R.string.country_de, 49, R.drawable.ic_list_country_de), - CountryListItem(R.string.country_gh, 233, R.drawable.ic_list_country_gh), -// CountryListItem(R.string.country_gi, 350, R.drawable.ic_list_country_gi), - CountryListItem(R.string.country_gr, 30, R.drawable.ic_list_country_gr), -// CountryListItem(R.string.country_gl, 299, R.drawable.ic_list_country_gl), - CountryListItem(R.string.country_gd, 1, R.drawable.ic_list_country_gd), -// CountryListItem(R.string.country_gp, 224, R.drawable.ic_list_country_fr), - CountryListItem(R.string.country_gu, 1, R.drawable.ic_list_country_gu), - CountryListItem(R.string.country_gt, 502, R.drawable.ic_list_country_gt), -// CountryListItem(R.string.country_gn, 224, R.drawable.ic_list_country_gn), - CountryListItem(R.string.country_gw, 245, R.drawable.ic_list_country_gw), -// CountryListItem(R.string.country_gy, 592, R.drawable.ic_list_country_gy), - CountryListItem(R.string.country_ht, 509, R.drawable.ic_list_country_ht), - CountryListItem(R.string.country_hn, 504, R.drawable.ic_list_country_hn), - CountryListItem(R.string.country_hk, 852, R.drawable.ic_list_country_hk), - CountryListItem(R.string.country_hu, 36, R.drawable.ic_list_country_hu), - CountryListItem(R.string.country_is, 354, R.drawable.ic_list_country_is), - CountryListItem(R.string.country_in, 91, R.drawable.ic_list_country_in), - CountryListItem(R.string.country_id, 62, R.drawable.ic_list_country_id), - CountryListItem(R.string.country_ir, 964, R.drawable.ic_list_country_ir), - CountryListItem(R.string.country_iq, 964, R.drawable.ic_list_country_iq), - CountryListItem(R.string.country_ie, 353, R.drawable.ic_list_country_ie), - CountryListItem(R.string.country_il, 972, R.drawable.ic_list_country_il), -// CountryListItem(R.string.country_it, 39, R.drawable.ic_list_country_it), - CountryListItem(R.string.country_ci, 225, R.drawable.ic_list_country_ci), - CountryListItem(R.string.country_jm, 1, R.drawable.ic_list_country_jm), - CountryListItem(R.string.country_jp, 81, R.drawable.ic_list_country_jp), - CountryListItem(R.string.country_jo, 962, R.drawable.ic_list_country_jo), - CountryListItem(R.string.country_kz, 7, R.drawable.ic_list_country_kz), - CountryListItem(R.string.country_ke, 254, R.drawable.ic_list_country_ke), -// CountryListItem(R.string.country_ki, 686, R.drawable.ic_list_country_ki), - CountryListItem(R.string.country_kw, 965, R.drawable.ic_list_country_kw), - CountryListItem(R.string.country_kg, 996, R.drawable.ic_list_country_kg), - CountryListItem(R.string.country_la, 856, R.drawable.ic_list_country_la), - CountryListItem(R.string.country_lv, 371, R.drawable.ic_list_country_lv), - CountryListItem(R.string.country_lb, 961, R.drawable.ic_list_country_lb), -// CountryListItem(R.string.country_ls, 266, R.drawable.ic_list_country_ls), -// CountryListItem(R.string.country_lr, 231, R.drawable.ic_list_country_lr), -// CountryListItem(R.string.country_ly, 218, R.drawable.ic_list_country_ly), - CountryListItem(R.string.country_li, 423, R.drawable.ic_list_country_li), - CountryListItem(R.string.country_lt, 370, R.drawable.ic_list_country_lt), - CountryListItem(R.string.country_lu, 352, R.drawable.ic_list_country_lu), - CountryListItem(R.string.country_mo, 853, R.drawable.ic_list_country_mo), -// CountryListItem(R.string.country_mg, 261, R.drawable.ic_list_country_mg), -// CountryListItem(R.string.country_mw, 265, R.drawable.ic_list_country_mw), - CountryListItem(R.string.country_my, 60, R.drawable.ic_list_country_my), -// CountryListItem(R.string.country_mv, 960, R.drawable.ic_list_country_mv), - CountryListItem(R.string.country_ml, 223, R.drawable.ic_list_country_ml), - CountryListItem(R.string.country_mt, 356, R.drawable.ic_list_country_mt), - CountryListItem(R.string.country_mq, 1, R.drawable.ic_list_country_mq), -// CountryListItem(R.string.country_mr, 222, R.drawable.ic_list_country_mr), - CountryListItem(R.string.country_mu, 230, R.drawable.ic_list_country_mu), - CountryListItem(R.string.country_mx, 52, R.drawable.ic_list_country_mx), - CountryListItem(R.string.country_md, 373, R.drawable.ic_list_country_md), -// CountryListItem(R.string.country_mc, 377, R.drawable.ic_list_country_mc), -// CountryListItem(R.string.country_mn, 976, R.drawable.ic_list_country_mn), -// CountryListItem(R.string.country_me, 382, R.drawable.ic_list_country_me), - CountryListItem(R.string.country_ms, 1, R.drawable.ic_list_country_ms), - CountryListItem(R.string.country_ma, 212, R.drawable.ic_list_country_ma), - CountryListItem(R.string.country_mz, 258, R.drawable.ic_list_country_mz), - CountryListItem(R.string.country_mm, 95, R.drawable.ic_list_country_mm), - CountryListItem(R.string.country_na, 264, R.drawable.ic_list_country_na), - CountryListItem(R.string.country_np, 977, R.drawable.ic_list_country_np), - CountryListItem(R.string.country_nl, 31, R.drawable.ic_list_country_nl), -// CountryListItem(R.string.country_an, 599, R.drawable.ic_list_country_an), -// CountryListItem(R.string.country_nc, 687, R.drawable.ic_list_country_nc), - CountryListItem(R.string.country_nz, 64, R.drawable.ic_list_country_nz), - CountryListItem(R.string.country_ni, 505, R.drawable.ic_list_country_ni), - CountryListItem(R.string.country_ne, 227, R.drawable.ic_list_country_ne), - CountryListItem(R.string.country_ng, 234, R.drawable.ic_list_country_ng), - CountryListItem(R.string.country_nf, 672, R.drawable.ic_list_country_nf), - CountryListItem(R.string.country_mk, 389, R.drawable.ic_list_country_mk), - CountryListItem(R.string.country_no, 47, R.drawable.ic_list_country_no), - CountryListItem(R.string.country_om, 968, R.drawable.ic_list_country_om), - CountryListItem(R.string.country_pk, 92, R.drawable.ic_list_country_pk), -// CountryListItem(R.string.country_pw, 680, R.drawable.ic_list_country_pw), -// CountryListItem(R.string.country_ps, 970, R.drawable.ic_list_country_ps), - CountryListItem(R.string.country_pa, 507, R.drawable.ic_list_country_pa), - CountryListItem(R.string.country_pg, 675, R.drawable.ic_list_country_pg), - CountryListItem(R.string.country_py, 595, R.drawable.ic_list_country_py), - CountryListItem(R.string.country_pe, 51, R.drawable.ic_list_country_pe), - CountryListItem(R.string.country_ph, 63, R.drawable.ic_list_country_ph), - CountryListItem(R.string.country_pl, 48, R.drawable.ic_list_country_pl), - CountryListItem(R.string.country_pt, 351, R.drawable.ic_list_country_pt), - CountryListItem(R.string.country_pr, 1, R.drawable.ic_list_country_pr), - CountryListItem(R.string.country_qa, 974, R.drawable.ic_list_country_qa), -// CountryListItem(R.string.country_cg, 242, R.drawable.ic_list_country_cg), -// CountryListItem(R.string.country_re, 262, R.drawable.ic_list_country_fr), -// CountryListItem(R.string.country_ro, 40, R.drawable.ic_list_country_ro), - CountryListItem(R.string.country_ru, 7, R.drawable.ic_list_country_ru), - CountryListItem(R.string.country_rw, 250, R.drawable.ic_list_country_rw), - CountryListItem(R.string.country_kn, 1, R.drawable.ic_list_country_kn), - CountryListItem(R.string.country_lc, 1, R.drawable.ic_list_country_lc), - CountryListItem(R.string.country_vc, 1, R.drawable.ic_list_country_vc), -// CountryListItem(R.string.country_ws, 685, R.drawable.ic_list_country_ws), -// CountryListItem(R.string.country_st, 239, R.drawable.ic_list_country_st), -// CountryListItem(R.string.country_sa, 966, R.drawable.ic_list_country_sa), - CountryListItem(R.string.country_sn, 221, R.drawable.ic_list_country_sn), - CountryListItem(R.string.country_rs, 381, R.drawable.ic_list_country_rs), -// CountryListItem(R.string.country_sc, 248, R.drawable.ic_list_country_sc), -// CountryListItem(R.string.country_sl, 232, R.drawable.ic_list_country_sl), - CountryListItem(R.string.country_sg, 65, R.drawable.ic_list_country_sg), - CountryListItem(R.string.country_sk, 421, R.drawable.ic_list_country_sk), - CountryListItem(R.string.country_si, 386, R.drawable.ic_list_country_si), - CountryListItem(R.string.country_sb, 677, R.drawable.ic_list_country_sb), -// CountryListItem(R.string.country_so, 252, R.drawable.ic_list_country_so), - CountryListItem(R.string.country_za, 27, R.drawable.ic_list_country_za), - CountryListItem(R.string.country_kr, 82, R.drawable.ic_list_country_kr), -// CountryListItem(R.string.country_ss, 211, R.drawable.ic_list_country_ss), - CountryListItem(R.string.country_es, 34, R.drawable.ic_list_country_es), - CountryListItem(R.string.country_lk, 94, R.drawable.ic_list_country_lk), - CountryListItem(R.string.country_sd, 249, R.drawable.ic_list_country_sd), -// CountryListItem(R.string.country_sr, 597, R.drawable.ic_list_country_sr), -// CountryListItem(R.string.country_sz, 268, R.drawable.ic_list_country_sz), - CountryListItem(R.string.country_se, 46, R.drawable.ic_list_country_se), - CountryListItem(R.string.country_ch, 41, R.drawable.ic_list_country_ch), - CountryListItem(R.string.country_tw, 886, R.drawable.ic_list_country_tw), - CountryListItem(R.string.country_tj, 992, R.drawable.ic_list_country_tj), - CountryListItem(R.string.country_tz, 255, R.drawable.ic_list_country_tz), - CountryListItem(R.string.country_th, 66, R.drawable.ic_list_country_th), -// CountryListItem(R.string.country_tl, 670, R.drawable.ic_list_country_tl), - CountryListItem(R.string.country_tg, 228, R.drawable.ic_list_country_tg), -// CountryListItem(R.string.country_to, 676, R.drawable.ic_list_country_to), - CountryListItem(R.string.country_tt, 1, R.drawable.ic_list_country_tt), - CountryListItem(R.string.country_tn, 216, R.drawable.ic_list_country_tn), - CountryListItem(R.string.country_tr, 90, R.drawable.ic_list_country_tr), - CountryListItem(R.string.country_tm, 993, R.drawable.ic_list_country_tm), - CountryListItem(R.string.country_tc, 1, R.drawable.ic_list_country_tc), - CountryListItem(R.string.country_ug, 256, R.drawable.ic_list_country_ug), - CountryListItem(R.string.country_ua, 380, R.drawable.ic_list_country_ua), - CountryListItem(R.string.country_ae, 971, R.drawable.ic_list_country_ae), - CountryListItem(R.string.country_gb, 44, R.drawable.ic_list_country_gb), - CountryListItem(R.string.country_us, 1, R.drawable.ic_list_country_us), - CountryListItem(R.string.country_uy, 598, R.drawable.ic_list_country_uy), - CountryListItem(R.string.country_uz, 998, R.drawable.ic_list_country_uz), -// CountryListItem(R.string.country_vu, 678, R.drawable.ic_list_country_vu), - CountryListItem(R.string.country_ve, 58, R.drawable.ic_list_country_ve), - CountryListItem(R.string.country_vn, 84, R.drawable.ic_list_country_vn), - CountryListItem(R.string.country_vi, 1, R.drawable.ic_list_country_vi), - CountryListItem(R.string.country_ye, 967, R.drawable.ic_list_country_ye), - CountryListItem(R.string.country_zm, 260, R.drawable.ic_list_country_zm), - CountryListItem(R.string.country_zw, 263, R.drawable.ic_list_country_zw) + CountryListItem(R.string.country_region_name_al, 355, R.drawable.ic_list_country_al), + CountryListItem(R.string.country_region_name_dz, 213, R.drawable.ic_list_country_dz), +// CountryListItem(R.string.country_region_name_ad, 376, R.drawable.ic_list_country_ad), + CountryListItem(R.string.country_region_name_ao, 244, R.drawable.ic_list_country_ao), + CountryListItem(R.string.country_region_name_ai, 1, R.drawable.ic_list_country_ai), + CountryListItem(R.string.country_region_name_ag, 1, R.drawable.ic_list_country_ag), + CountryListItem(R.string.country_region_name_ar, 54, R.drawable.ic_list_country_ar), + CountryListItem(R.string.country_region_name_am, 374, R.drawable.ic_list_country_am), + CountryListItem(R.string.country_region_name_aw, 297, R.drawable.ic_list_country_aw), + CountryListItem(R.string.country_region_name_au, 61, R.drawable.ic_list_country_au), + CountryListItem(R.string.country_region_name_at, 43, R.drawable.ic_list_country_at), + CountryListItem(R.string.country_region_name_az, 994, R.drawable.ic_list_country_az), + CountryListItem(R.string.country_region_name_bs, 1, R.drawable.ic_list_country_bs), + CountryListItem(R.string.country_region_name_bh, 973, R.drawable.ic_list_country_bh), + CountryListItem(R.string.country_region_name_bd, 880, R.drawable.ic_list_country_bd), + CountryListItem(R.string.country_region_name_bb, 1, R.drawable.ic_list_country_bb), + CountryListItem(R.string.country_region_name_by, 375, R.drawable.ic_list_country_by), + CountryListItem(R.string.country_region_name_be, 32, R.drawable.ic_list_country_be), + CountryListItem(R.string.country_region_name_bz, 501, R.drawable.ic_list_country_bz), + CountryListItem(R.string.country_region_name_bj, 229, R.drawable.ic_list_country_bj), + CountryListItem(R.string.country_region_name_bm, 1, R.drawable.ic_list_country_bm), +// CountryListItem(R.string.country_region_name_bt, 975, R.drawable.ic_list_country_bt), + CountryListItem(R.string.country_region_name_bo, 591, R.drawable.ic_list_country_bo), + CountryListItem(R.string.country_region_name_ba, 387, R.drawable.ic_list_country_ba), + CountryListItem(R.string.country_region_name_bw, 267, R.drawable.ic_list_country_bw), + CountryListItem(R.string.country_region_name_br, 55, R.drawable.ic_list_country_br), +// CountryListItem(R.string.country_region_name_bn, 673, R.drawable.ic_list_country_bn), + CountryListItem(R.string.country_region_name_vg, 1, R.drawable.ic_list_country_vg), + CountryListItem(R.string.country_region_name_bg, 359, R.drawable.ic_list_country_bg), + CountryListItem(R.string.country_region_name_bf, 226, R.drawable.ic_list_country_bf), +// CountryListItem(R.string.country_region_name_bi, 257, R.drawable.ic_list_country_bi), + CountryListItem(R.string.country_region_name_kh, 855, R.drawable.ic_list_country_kh), + CountryListItem(R.string.country_region_name_cm, 237, R.drawable.ic_list_country_cm), + CountryListItem(R.string.country_region_name_ca, 1, R.drawable.ic_list_country_ca), + CountryListItem(R.string.country_region_name_cv, 238, R.drawable.ic_list_country_cv), + CountryListItem(R.string.country_region_name_ky, 1, R.drawable.ic_list_country_ky), +// CountryListItem(R.string.country_region_name_cf, 236, R.drawable.ic_list_country_cf), +// CountryListItem(R.string.country_region_name_td, 235, R.drawable.ic_list_country_td), + CountryListItem(R.string.country_region_name_cl, 56, R.drawable.ic_list_country_cl), + CountryListItem(R.string.country_region_name_cn, 86, R.drawable.ic_list_country_cn), + CountryListItem(R.string.country_region_name_co, 57, R.drawable.ic_list_country_co), +// CountryListItem(R.string.country_region_name_km, 269, R.drawable.ic_list_country_km), +// CountryListItem(R.string.country_region_name_ck, 682, R.drawable.ic_list_country_ck), + CountryListItem(R.string.country_region_name_cr, 506, R.drawable.ic_list_country_cr), + CountryListItem(R.string.country_region_name_hr, 385, R.drawable.ic_list_country_hr), + CountryListItem(R.string.country_region_name_cu, 53, R.drawable.ic_list_country_cu), + CountryListItem(R.string.country_region_name_cw, 599, R.drawable.ic_list_country_cw), + CountryListItem(R.string.country_region_name_cy, 357, R.drawable.ic_list_country_cy), + CountryListItem(R.string.country_region_name_cz, 420, R.drawable.ic_list_country_cz), +// CountryListItem(R.string.country_region_name_cd, 243, R.drawable.ic_list_country_cd), + CountryListItem(R.string.country_region_name_dk, 45, R.drawable.ic_list_country_dk), +// CountryListItem(R.string.country_region_name_dj, 253, R.drawable.ic_list_country_dj), + CountryListItem(R.string.country_region_name_dm, 1, R.drawable.ic_list_country_dm), + CountryListItem(R.string.country_region_name_do, 1, R.drawable.ic_list_country_do), + CountryListItem(R.string.country_region_name_ec, 593, R.drawable.ic_list_country_ec), +// CountryListItem(R.string.country_region_name_eg, 20, R.drawable.ic_list_country_eg), + CountryListItem(R.string.country_region_name_sv, 503, R.drawable.ic_list_country_sv), +// CountryListItem(R.string.country_region_name_gq, 240, R.drawable.ic_list_country_gq), + CountryListItem(R.string.country_region_name_ee, 372, R.drawable.ic_list_country_ee), +// CountryListItem(R.string.country_region_name_et, 251, R.drawable.ic_list_country_et), +// CountryListItem(R.string.country_region_name_fo, 298, R.drawable.ic_list_country_fo), + CountryListItem(R.string.country_region_name_fj, 679, R.drawable.ic_list_country_fj), + CountryListItem(R.string.country_region_name_fi, 358, R.drawable.ic_list_country_fi), + CountryListItem(R.string.country_region_name_fr, 33, R.drawable.ic_list_country_fr), +// CountryListItem(R.string.country_region_name_gf, 995, R.drawable.ic_list_country_fr), + CountryListItem(R.string.country_region_name_ga, 241, R.drawable.ic_list_country_ga), +// CountryListItem(R.string.country_region_name_gm, 220, R.drawable.ic_list_country_gm), + CountryListItem(R.string.country_region_name_ge, 995, R.drawable.ic_list_country_ge), + CountryListItem(R.string.country_region_name_de, 49, R.drawable.ic_list_country_de), + CountryListItem(R.string.country_region_name_gh, 233, R.drawable.ic_list_country_gh), +// CountryListItem(R.string.country_region_name_gi, 350, R.drawable.ic_list_country_gi), + CountryListItem(R.string.country_region_name_gr, 30, R.drawable.ic_list_country_gr), +// CountryListItem(R.string.country_region_name_gl, 299, R.drawable.ic_list_country_gl), + CountryListItem(R.string.country_region_name_gd, 1, R.drawable.ic_list_country_gd), +// CountryListItem(R.string.country_region_name_gp, 224, R.drawable.ic_list_country_fr), + CountryListItem(R.string.country_region_name_gu, 1, R.drawable.ic_list_country_gu), + CountryListItem(R.string.country_region_name_gt, 502, R.drawable.ic_list_country_gt), +// CountryListItem(R.string.country_region_name_gn, 224, R.drawable.ic_list_country_gn), + CountryListItem(R.string.country_region_name_gw, 245, R.drawable.ic_list_country_gw), +// CountryListItem(R.string.country_region_name_gy, 592, R.drawable.ic_list_country_gy), + CountryListItem(R.string.country_region_name_ht, 509, R.drawable.ic_list_country_ht), + CountryListItem(R.string.country_region_name_hn, 504, R.drawable.ic_list_country_hn), + CountryListItem(R.string.country_region_name_hk, 852, R.drawable.ic_list_country_hk), + CountryListItem(R.string.country_region_name_hu, 36, R.drawable.ic_list_country_hu), + CountryListItem(R.string.country_region_name_is, 354, R.drawable.ic_list_country_is), + CountryListItem(R.string.country_region_name_in, 91, R.drawable.ic_list_country_in), + CountryListItem(R.string.country_region_name_id, 62, R.drawable.ic_list_country_id), + CountryListItem(R.string.country_region_name_ir, 98, R.drawable.ic_list_country_ir), + CountryListItem(R.string.country_region_name_iq, 964, R.drawable.ic_list_country_iq), + CountryListItem(R.string.country_region_name_ie, 353, R.drawable.ic_list_country_ie), + CountryListItem(R.string.country_region_name_il, 972, R.drawable.ic_list_country_il), +// CountryListItem(R.string.country_region_name_it, 39, R.drawable.ic_list_country_it), + CountryListItem(R.string.country_region_name_ci, 225, R.drawable.ic_list_country_ci), + CountryListItem(R.string.country_region_name_jm, 1, R.drawable.ic_list_country_jm), + CountryListItem(R.string.country_region_name_jp, 81, R.drawable.ic_list_country_jp), + CountryListItem(R.string.country_region_name_jo, 962, R.drawable.ic_list_country_jo), + CountryListItem(R.string.country_region_name_kz, 7, R.drawable.ic_list_country_kz), + CountryListItem(R.string.country_region_name_ke, 254, R.drawable.ic_list_country_ke), +// CountryListItem(R.string.country_region_name_ki, 686, R.drawable.ic_list_country_ki), + CountryListItem(R.string.country_region_name_kw, 965, R.drawable.ic_list_country_kw), + CountryListItem(R.string.country_region_name_kg, 996, R.drawable.ic_list_country_kg), + CountryListItem(R.string.country_region_name_la, 856, R.drawable.ic_list_country_la), + CountryListItem(R.string.country_region_name_lv, 371, R.drawable.ic_list_country_lv), + CountryListItem(R.string.country_region_name_lb, 961, R.drawable.ic_list_country_lb), +// CountryListItem(R.string.country_region_name_ls, 266, R.drawable.ic_list_country_ls), +// CountryListItem(R.string.country_region_name_lr, 231, R.drawable.ic_list_country_lr), +// CountryListItem(R.string.country_region_name_ly, 218, R.drawable.ic_list_country_ly), + CountryListItem(R.string.country_region_name_li, 423, R.drawable.ic_list_country_li), + CountryListItem(R.string.country_region_name_lt, 370, R.drawable.ic_list_country_lt), + CountryListItem(R.string.country_region_name_lu, 352, R.drawable.ic_list_country_lu), + CountryListItem(R.string.country_region_name_mo, 853, R.drawable.ic_list_country_mo), +// CountryListItem(R.string.country_region_name_mg, 261, R.drawable.ic_list_country_mg), +// CountryListItem(R.string.country_region_name_mw, 265, R.drawable.ic_list_country_mw), + CountryListItem(R.string.country_region_name_my, 60, R.drawable.ic_list_country_my), +// CountryListItem(R.string.country_region_name_mv, 960, R.drawable.ic_list_country_mv), + CountryListItem(R.string.country_region_name_ml, 223, R.drawable.ic_list_country_ml), + CountryListItem(R.string.country_region_name_mt, 356, R.drawable.ic_list_country_mt), + CountryListItem(R.string.country_region_name_mq, 1, R.drawable.ic_list_country_mq), +// CountryListItem(R.string.country_region_name_mr, 222, R.drawable.ic_list_country_mr), + CountryListItem(R.string.country_region_name_mu, 230, R.drawable.ic_list_country_mu), + CountryListItem(R.string.country_region_name_mx, 52, R.drawable.ic_list_country_mx), + CountryListItem(R.string.country_region_name_md, 373, R.drawable.ic_list_country_md), +// CountryListItem(R.string.country_region_name_mc, 377, R.drawable.ic_list_country_mc), +// CountryListItem(R.string.country_region_name_mn, 976, R.drawable.ic_list_country_mn), +// CountryListItem(R.string.country_region_name_me, 382, R.drawable.ic_list_country_me), + CountryListItem(R.string.country_region_name_ms, 1, R.drawable.ic_list_country_ms), + CountryListItem(R.string.country_region_name_ma, 212, R.drawable.ic_list_country_ma), + CountryListItem(R.string.country_region_name_mz, 258, R.drawable.ic_list_country_mz), + CountryListItem(R.string.country_region_name_mm, 95, R.drawable.ic_list_country_mm), + CountryListItem(R.string.country_region_name_na, 264, R.drawable.ic_list_country_na), + CountryListItem(R.string.country_region_name_np, 977, R.drawable.ic_list_country_np), + CountryListItem(R.string.country_region_name_nl, 31, R.drawable.ic_list_country_nl), +// CountryListItem(R.string.country_region_name_an, 599, R.drawable.ic_list_country_an), +// CountryListItem(R.string.country_region_name_nc, 687, R.drawable.ic_list_country_nc), + CountryListItem(R.string.country_region_name_nz, 64, R.drawable.ic_list_country_nz), + CountryListItem(R.string.country_region_name_ni, 505, R.drawable.ic_list_country_ni), + CountryListItem(R.string.country_region_name_ne, 227, R.drawable.ic_list_country_ne), + CountryListItem(R.string.country_region_name_ng, 234, R.drawable.ic_list_country_ng), + CountryListItem(R.string.country_region_name_au2, 672, R.drawable.ic_list_country_nf), + CountryListItem(R.string.country_region_name_mk, 389, R.drawable.ic_list_country_mk), + CountryListItem(R.string.country_region_name_no, 47, R.drawable.ic_list_country_no), + CountryListItem(R.string.country_region_name_om, 968, R.drawable.ic_list_country_om), + CountryListItem(R.string.country_region_name_pk, 92, R.drawable.ic_list_country_pk), +// CountryListItem(R.string.country_region_name_pw, 680, R.drawable.ic_list_country_pw), +// CountryListItem(R.string.country_region_name_ps, 970, R.drawable.ic_list_country_ps), + CountryListItem(R.string.country_region_name_pa, 507, R.drawable.ic_list_country_pa), + CountryListItem(R.string.country_region_name_pg, 675, R.drawable.ic_list_country_pg), + CountryListItem(R.string.country_region_name_py, 595, R.drawable.ic_list_country_py), + CountryListItem(R.string.country_region_name_pe, 51, R.drawable.ic_list_country_pe), + CountryListItem(R.string.country_region_name_ph, 63, R.drawable.ic_list_country_ph), + CountryListItem(R.string.country_region_name_pl, 48, R.drawable.ic_list_country_pl), + CountryListItem(R.string.country_region_name_pt, 351, R.drawable.ic_list_country_pt), + CountryListItem(R.string.country_region_name_pr, 1, R.drawable.ic_list_country_pr), + CountryListItem(R.string.country_region_name_qa, 974, R.drawable.ic_list_country_qa), +// CountryListItem(R.string.country_region_name_cg, 242, R.drawable.ic_list_country_cg), +// CountryListItem(R.string.country_region_name_re, 262, R.drawable.ic_list_country_fr), +// CountryListItem(R.string.country_region_name_ro, 40, R.drawable.ic_list_country_ro), + CountryListItem(R.string.country_region_name_ru, 7, R.drawable.ic_list_country_ru), + CountryListItem(R.string.country_region_name_rw, 250, R.drawable.ic_list_country_rw), + CountryListItem(R.string.country_region_name_kn, 1, R.drawable.ic_list_country_kn), + CountryListItem(R.string.country_region_name_lc, 1, R.drawable.ic_list_country_lc), + CountryListItem(R.string.country_region_name_vc, 1, R.drawable.ic_list_country_vc), +// CountryListItem(R.string.country_region_name_ws, 685, R.drawable.ic_list_country_ws), +// CountryListItem(R.string.country_region_name_st, 239, R.drawable.ic_list_country_st), +// CountryListItem(R.string.country_region_name_sa, 966, R.drawable.ic_list_country_sa), + CountryListItem(R.string.country_region_name_sn, 221, R.drawable.ic_list_country_sn), + CountryListItem(R.string.country_region_name_rs, 381, R.drawable.ic_list_country_rs), +// CountryListItem(R.string.country_region_name_sc, 248, R.drawable.ic_list_country_sc), +// CountryListItem(R.string.country_region_name_sl, 232, R.drawable.ic_list_country_sl), + CountryListItem(R.string.country_region_name_sg, 65, R.drawable.ic_list_country_sg), + CountryListItem(R.string.country_region_name_sk, 421, R.drawable.ic_list_country_sk), + CountryListItem(R.string.country_region_name_si, 386, R.drawable.ic_list_country_si), + CountryListItem(R.string.country_region_name_sb, 677, R.drawable.ic_list_country_sb), +// CountryListItem(R.string.country_region_name_so, 252, R.drawable.ic_list_country_so), + CountryListItem(R.string.country_region_name_za, 27, R.drawable.ic_list_country_za), + CountryListItem(R.string.country_region_name_kr, 82, R.drawable.ic_list_country_kr), +// CountryListItem(R.string.country_region_name_ss, 211, R.drawable.ic_list_country_ss), + CountryListItem(R.string.country_region_name_es, 34, R.drawable.ic_list_country_es), + CountryListItem(R.string.country_region_name_lk, 94, R.drawable.ic_list_country_lk), + CountryListItem(R.string.country_region_name_sd, 249, R.drawable.ic_list_country_sd), +// CountryListItem(R.string.country_region_name_sr, 597, R.drawable.ic_list_country_sr), +// CountryListItem(R.string.country_region_name_sz, 268, R.drawable.ic_list_country_sz), + CountryListItem(R.string.country_region_name_se, 46, R.drawable.ic_list_country_se), + CountryListItem(R.string.country_region_name_ch, 41, R.drawable.ic_list_country_ch), + CountryListItem(R.string.country_region_name_tw, 886, R.drawable.ic_list_country_tw), + CountryListItem(R.string.country_region_name_tj, 992, R.drawable.ic_list_country_tj), + CountryListItem(R.string.country_region_name_tz, 255, R.drawable.ic_list_country_tz), + CountryListItem(R.string.country_region_name_th, 66, R.drawable.ic_list_country_th), +// CountryListItem(R.string.country_region_name_tl, 670, R.drawable.ic_list_country_tl), + CountryListItem(R.string.country_region_name_tg, 228, R.drawable.ic_list_country_tg), +// CountryListItem(R.string.country_region_name_to, 676, R.drawable.ic_list_country_to), + CountryListItem(R.string.country_region_name_tt, 1, R.drawable.ic_list_country_tt), + CountryListItem(R.string.country_region_name_tn, 216, R.drawable.ic_list_country_tn), + CountryListItem(R.string.country_region_name_tr, 90, R.drawable.ic_list_country_tr), + CountryListItem(R.string.country_region_name_tm, 993, R.drawable.ic_list_country_tm), + CountryListItem(R.string.country_region_name_tc, 1, R.drawable.ic_list_country_tc), + CountryListItem(R.string.country_region_name_ug, 256, R.drawable.ic_list_country_ug), + CountryListItem(R.string.country_region_name_ua, 380, R.drawable.ic_list_country_ua), + CountryListItem(R.string.country_region_name_ae, 971, R.drawable.ic_list_country_ae), + CountryListItem(R.string.country_region_name_gb, 44, R.drawable.ic_list_country_gb), + CountryListItem(R.string.country_region_name_us, 1, R.drawable.ic_list_country_us), + CountryListItem(R.string.country_region_name_uy, 598, R.drawable.ic_list_country_uy), + CountryListItem(R.string.country_region_name_uz, 998, R.drawable.ic_list_country_uz), +// CountryListItem(R.string.country_region_name_vu, 678, R.drawable.ic_list_country_vu), + CountryListItem(R.string.country_region_name_ve, 58, R.drawable.ic_list_country_ve), + CountryListItem(R.string.country_region_name_vn, 84, R.drawable.ic_list_country_vn), + CountryListItem(R.string.country_region_name_vi, 1, R.drawable.ic_list_country_vi), + CountryListItem(R.string.country_region_name_ye, 967, R.drawable.ic_list_country_ye), + CountryListItem(R.string.country_region_name_zm, 260, R.drawable.ic_list_country_zm), + CountryListItem(R.string.country_region_name_zw, 263, R.drawable.ic_list_country_zw) ) } private fun getCountryListInitialisedWithOptionsForAustralia(): MutableList { return mutableListOf( CountryGroupTitle(R.string.options_for_australia), - CountryListItem(R.string.country_au, 61, R.drawable.ic_list_country_au), - CountryListItem(R.string.country_nf, 672, R.drawable.ic_list_country_nf) + CountryListItem(R.string.country_region_name_au, 61, R.drawable.ic_list_country_au), + CountryListItem(R.string.country_region_name_au2, 672, R.drawable.ic_list_country_nf) ) } diff --git a/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/dataprivacy/DataPrivacyFragment.kt b/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/dataprivacy/DataPrivacyFragment.kt index 843830c..57ecaf6 100644 --- a/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/dataprivacy/DataPrivacyFragment.kt +++ b/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/dataprivacy/DataPrivacyFragment.kt @@ -7,9 +7,12 @@ import android.view.View import android.view.ViewGroup import android.view.accessibility.AccessibilityEvent import au.gov.health.covidsafe.R +import au.gov.health.covidsafe.links.LinkBuilder +import au.gov.health.covidsafe.talkback.setHeading import au.gov.health.covidsafe.ui.PagerChildFragment import au.gov.health.covidsafe.ui.UploadButtonLayout import kotlinx.android.synthetic.main.fragment_data_privacy.* +import kotlinx.android.synthetic.main.fragment_data_privacy.root import kotlinx.android.synthetic.main.fragment_data_privacy.view.* class DataPrivacyFragment : PagerChildFragment() { @@ -21,13 +24,17 @@ class DataPrivacyFragment : PagerChildFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + + view.data_privacy_content.text = LinkBuilder.getRegistrationAndPrivacyContent(requireContext()) view.data_privacy_content.movementMethod = LinkMovementMethod.getInstance() } override fun onResume() { super.onResume() - // set accessibility focus to the title "Registration and privacy" + removeViewInLandscapeMode(data_privacy_picture) + + data_privacy_headline.setHeading() data_privacy_headline.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) } diff --git a/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/enternumber/EnterNumberFragment.kt b/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/enternumber/EnterNumberFragment.kt index 1c51f90..9065da1 100644 --- a/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/enternumber/EnterNumberFragment.kt +++ b/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/enternumber/EnterNumberFragment.kt @@ -10,9 +10,8 @@ import android.view.View import android.view.View.GONE import android.view.View.VISIBLE import android.view.ViewGroup +import android.view.accessibility.AccessibilityEvent import android.view.inputmethod.EditorInfo -import android.widget.TextView -import android.widget.TextView.OnEditorActionListener import androidx.annotation.NavigationRes import androidx.core.content.ContextCompat import androidx.core.os.bundleOf @@ -20,6 +19,7 @@ import androidx.core.widget.addTextChangedListener import au.gov.health.covidsafe.Preference import au.gov.health.covidsafe.R import au.gov.health.covidsafe.TracerApp +import au.gov.health.covidsafe.talkback.setHeading import au.gov.health.covidsafe.ui.PagerChildFragment import au.gov.health.covidsafe.ui.UploadButtonLayout import au.gov.health.covidsafe.ui.onboarding.CountryCodeSelectionActivity @@ -98,6 +98,9 @@ class EnterNumberFragment : PagerChildFragment() { updateButtonState() displaySelectedCountryOrRegion() + + enter_number_page_headline.setHeading() + enter_number_page_headline.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) } @SuppressLint("SetTextI18n") diff --git a/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/enterpin/EnterPinFragment.kt b/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/enterpin/EnterPinFragment.kt index 40d1e29..ac4f45c 100644 --- a/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/enterpin/EnterPinFragment.kt +++ b/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/enterpin/EnterPinFragment.kt @@ -7,11 +7,15 @@ import android.text.method.LinkMovementMethod import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.view.accessibility.AccessibilityEvent import androidx.annotation.NavigationRes import androidx.core.content.ContextCompat +import androidx.core.widget.doOnTextChanged import au.gov.health.covidsafe.R import au.gov.health.covidsafe.Utils.announceForAccessibility import au.gov.health.covidsafe.extensions.toHyperlink +import au.gov.health.covidsafe.links.LinkBuilder +import au.gov.health.covidsafe.talkback.setHeading import au.gov.health.covidsafe.ui.PagerChildFragment import au.gov.health.covidsafe.ui.UploadButtonLayout import com.atlassian.mobilekit.module.core.utils.SystemUtils @@ -19,7 +23,6 @@ import kotlinx.android.synthetic.main.fragment_enter_pin.* import kotlinx.android.synthetic.main.fragment_enter_pin.view.* import kotlin.math.floor - class EnterPinFragment : PagerChildFragment() { companion object { @@ -58,6 +61,9 @@ class EnterPinFragment : PagerChildFragment() { enter_pin_headline.text = resources.getString(R.string.enter_pin_headline, "+$callingCode", phoneNumber) + enter_pin_headline.setHeading(shouldConvertNumberToDigits = true) + enter_pin_headline.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) + presenter = EnterPinPresenter(this@EnterPinFragment, session, challengeName, @@ -73,6 +79,7 @@ class EnterPinFragment : PagerChildFragment() { presenter.resendCode() } + view.pin_issue.text = LinkBuilder.getIssuesReceivingPINContent() view.pin_issue.movementMethod = LinkMovementMethod.getInstance() startTimer() @@ -81,17 +88,13 @@ class EnterPinFragment : PagerChildFragment() { override fun onResume() { super.onResume() updateButtonState() - pin.onPinChanged = { + + pin.doOnTextChanged { _, _, _, _ -> updateButtonState() hideInvalidOtp() } } - override fun onPause() { - super.onPause() - pin.onPinChanged = null - } - private fun startTimer() { stopWatch = object : CountDownTimer(COUNTDOWN_DURATION * 1000, 1000) { override fun onTick(millisUntilFinished: Long) { @@ -146,7 +149,7 @@ class EnterPinFragment : PagerChildFragment() { fun showInvalidOtp() { enter_pin_error_label.visibility = View.VISIBLE // make the announcement in voice if talkback is turned on - announceForAccessibility(requireContext().getString(R.string.wrong_pin_number)) + announceForAccessibility(requireContext().getString(R.string.wrong_ping_number)) } private fun hideInvalidOtp() { @@ -162,7 +165,7 @@ class EnterPinFragment : PagerChildFragment() { } private fun isIncorrectPinFormat(): Boolean { - return requireView().pin.isIncomplete + return requireView().pin.text.toString().length != 6 } override fun updateButtonState() { @@ -174,7 +177,7 @@ class EnterPinFragment : PagerChildFragment() { } private fun validateOtp() { - presenter.validateOTP(requireView().pin.value) + presenter.validateOTP(requireView().pin.text.toString()) } fun showErrorOtpMustBeSixDigits() { diff --git a/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/howitworks/HowItWorksFragment.kt b/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/howitworks/HowItWorksFragment.kt index 1b61acc..b93f197 100644 --- a/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/howitworks/HowItWorksFragment.kt +++ b/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/howitworks/HowItWorksFragment.kt @@ -7,6 +7,8 @@ import android.view.View import android.view.ViewGroup import android.view.accessibility.AccessibilityEvent import au.gov.health.covidsafe.R +import au.gov.health.covidsafe.links.LinkBuilder +import au.gov.health.covidsafe.talkback.setHeading import au.gov.health.covidsafe.ui.PagerChildFragment import au.gov.health.covidsafe.ui.UploadButtonLayout import kotlinx.android.synthetic.main.fragment_how_it_works.* @@ -21,13 +23,17 @@ class HowItWorksFragment : PagerChildFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + + view.how_it_works_content.text = LinkBuilder.getHowCOVIdSafeWorksContent(requireContext()) view.how_it_works_content.movementMethod = LinkMovementMethod.getInstance() } override fun onResume() { super.onResume() - // set accessibility focus to the title "How COVIDSafe works" + removeViewInLandscapeMode(how_it_works_picture) + + how_it_works_headline.setHeading() how_it_works_headline.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) } diff --git a/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/introduction/IntroductionFragment.kt b/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/introduction/IntroductionFragment.kt index 1817111..1f462fd 100644 --- a/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/introduction/IntroductionFragment.kt +++ b/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/introduction/IntroductionFragment.kt @@ -6,6 +6,7 @@ import android.view.View import android.view.ViewGroup import android.view.accessibility.AccessibilityEvent import au.gov.health.covidsafe.R +import au.gov.health.covidsafe.talkback.setHeading import au.gov.health.covidsafe.ui.PagerChildFragment import au.gov.health.covidsafe.ui.UploadButtonLayout import kotlinx.android.synthetic.main.fragment_intro.* @@ -25,7 +26,9 @@ class IntroductionFragment : PagerChildFragment() { override fun onResume() { super.onResume() - // set accessibility focus to the title "Together we can stop ..." + removeViewInLandscapeMode(intro_picture) + + intro_headline.setHeading() intro_headline.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) } diff --git a/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/permission/PermissionDeviceNameFragment.kt b/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/permission/PermissionDeviceNameFragment.kt index 7ee9757..2acd67b 100644 --- a/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/permission/PermissionDeviceNameFragment.kt +++ b/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/permission/PermissionDeviceNameFragment.kt @@ -6,7 +6,9 @@ import android.text.Html import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.view.accessibility.AccessibilityEvent import au.gov.health.covidsafe.R +import au.gov.health.covidsafe.talkback.setHeading import au.gov.health.covidsafe.ui.PagerChildFragment import au.gov.health.covidsafe.ui.UploadButtonLayout import kotlinx.android.synthetic.main.fragment_permission.root @@ -38,6 +40,9 @@ class PermissionDeviceNameFragment : PagerChildFragment() { } } + + change_device_name_headline.setHeading() + change_device_name_headline.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) } private fun navigateToNextPage() { diff --git a/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/permission/PermissionFragment.kt b/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/permission/PermissionFragment.kt index 2295531..908c041 100644 --- a/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/permission/PermissionFragment.kt +++ b/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/permission/PermissionFragment.kt @@ -10,12 +10,14 @@ import android.os.PowerManager import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.view.accessibility.AccessibilityEvent import androidx.core.content.ContextCompat import au.gov.health.covidsafe.HomeActivity import au.gov.health.covidsafe.Preference import au.gov.health.covidsafe.R import au.gov.health.covidsafe.TracerApp import au.gov.health.covidsafe.extensions.* +import au.gov.health.covidsafe.talkback.setHeading import au.gov.health.covidsafe.ui.PagerChildFragment import au.gov.health.covidsafe.ui.UploadButtonLayout import kotlinx.android.synthetic.main.fragment_permission.* @@ -28,7 +30,7 @@ class PermissionFragment : PagerChildFragment(), EasyPermissions.PermissionCallb val requiredPermissions = arrayOf( Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN, - Manifest.permission.ACCESS_FINE_LOCATION + Manifest.permission.ACCESS_COARSE_LOCATION ) } @@ -39,6 +41,15 @@ class PermissionFragment : PagerChildFragment(), EasyPermissions.PermissionCallb override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?) : View? = inflater.inflate(R.layout.fragment_permission, container, false) + override fun onResume() { + super.onResume() + + removeViewInLandscapeMode(permission_picture) + + permission_headline.setHeading() + permission_headline.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) + } + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { if (requestCode == REQUEST_ENABLE_BT) { if (resultCode == Activity.RESULT_CANCELED) { diff --git a/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/permissionsuccess/PermissionSuccessFragment.kt b/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/permissionsuccess/PermissionSuccessFragment.kt index a67eb9a..77ba38f 100644 --- a/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/permissionsuccess/PermissionSuccessFragment.kt +++ b/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/permissionsuccess/PermissionSuccessFragment.kt @@ -6,8 +6,11 @@ import android.text.method.LinkMovementMethod import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.view.accessibility.AccessibilityEvent import au.gov.health.covidsafe.HomeActivity import au.gov.health.covidsafe.R +import au.gov.health.covidsafe.links.LinkBuilder +import au.gov.health.covidsafe.talkback.setHeading import au.gov.health.covidsafe.ui.PagerChildFragment import au.gov.health.covidsafe.ui.UploadButtonLayout import kotlinx.android.synthetic.main.fragment_permission_success.* @@ -22,6 +25,10 @@ class PermissionSuccessFragment : PagerChildFragment() { override fun onResume() { super.onResume() + permission_success_headline.setHeading() + permission_success_headline.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) + + permission_success_content.text = LinkBuilder.getHowPermissionSuccessContent(requireContext()) permission_success_content.movementMethod = LinkMovementMethod.getInstance() } diff --git a/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/personal/PersonalDetailsFragment.kt b/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/personal/PersonalDetailsFragment.kt index 704d045..610b5e4 100644 --- a/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/personal/PersonalDetailsFragment.kt +++ b/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/personal/PersonalDetailsFragment.kt @@ -1,40 +1,39 @@ package au.gov.health.covidsafe.ui.onboarding.fragment.personal +import android.app.Activity import android.os.Bundle -import android.text.Editable -import android.text.TextWatcher import android.view.KeyEvent import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.accessibility.AccessibilityEvent import android.view.inputmethod.EditorInfo -import android.widget.NumberPicker +import android.view.inputmethod.InputMethodManager +import android.widget.ArrayAdapter +import android.widget.TextView.OnEditorActionListener import androidx.appcompat.app.AlertDialog import androidx.core.os.bundleOf -import androidx.core.widget.addTextChangedListener import au.gov.health.covidsafe.Preference import au.gov.health.covidsafe.R +import au.gov.health.covidsafe.logging.CentralLog +import au.gov.health.covidsafe.talkback.setHeading import au.gov.health.covidsafe.ui.PagerChildFragment import au.gov.health.covidsafe.ui.UploadButtonLayout import au.gov.health.covidsafe.ui.onboarding.fragment.enternumber.EnterNumberFragment -import kotlinx.android.synthetic.main.fragment_enter_number.* import kotlinx.android.synthetic.main.fragment_personal_details.* import java.util.regex.Pattern +private const val TAG = "PersonalDetailsFragment" + private val POST_CODE_REGEX = Pattern.compile("^(?:(?:[2-8]\\d|9[0-7]|0?[28]|0?9(?=09))(?:\\d{2}))$") -private val NAME_REGEX = Pattern.compile("^[A-Za-z0-9][A-Za-z'0-9\\\\-\\\\u00C0-\\\\u017F ]{0,80}\$") +private val NAME_REGEX = Pattern.compile("^[A-Za-z0-9\\u00C0-\\u017F][A-Za-z'0-9\\-\\u00C0-\\u017F ]{0,80}\$") class PersonalDetailsFragment : PagerChildFragment() { - private var picker: NumberPicker? = null - private var alertDialog: AlertDialog? = null override var stepProgress: Int? = 1 - private var ageSelected: Pair = Pair(-1, "") - private lateinit var name: String private lateinit var postcode: String private var age: Int = -1 @@ -42,7 +41,6 @@ class PersonalDetailsFragment : PagerChildFragment() { private fun updatePersonalDetailsDataField() { name = personal_details_name.text.toString() postcode = personal_details_post_code.text.toString() - age = ageSelected.first } private fun isValidName() = NAME_REGEX.matcher(name).matches() @@ -53,57 +51,65 @@ class PersonalDetailsFragment : PagerChildFragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?) : View? = inflater.inflate(R.layout.fragment_personal_details, container, false) + private fun checkAgeAndDisplayAgeError(): Boolean { + val isValidAge = isValidAge() + + if (isValidAge) { + personal_details_age_error.visibility = View.GONE + } else { + personal_details_age_error.visibility = View.VISIBLE + } + + return isValidAge + } + + private fun hideKeyboard() { + val imm = requireContext().getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager + imm.hideSoftInputFromWindow(personal_details_name.windowToken, 0) + } + override fun onResume() { super.onResume() - personal_details_age.setText(ageSelected.second) + ArrayAdapter.createFromResource( + requireContext(), + R.array.age_range_array, + android.R.layout.simple_spinner_item + ).also { adapter -> + // Specify the layout to use when the list of choices appears + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) - fun showAgePicker() { - activity?.let { activity -> - val ages = resources.getStringArray(R.array.personal_details_age_array).map { - it.split(":").let { split -> - (split[0]).toInt() to split[1] - } - } - - var selected = ages.firstOrNull { it == ageSelected }?.let { - ages.indexOf(it) - } ?: 0 - - picker = NumberPicker(activity) - picker?.minValue = 0 - picker?.maxValue = ages.size - 1 - picker?.displayedValues = ages.map { it.second }.toTypedArray() - picker?.setOnValueChangedListener { _, _, newVal -> - selected = newVal - } - picker?.value = selected - alertDialog?.dismiss() - alertDialog = AlertDialog.Builder(activity) - .setTitle(R.string.personal_details_age_dialog_title) - .setView(picker) - .setPositiveButton(R.string.personal_details_dialog_ok) { _, _ -> - ageSelected = ages[selected] - - personal_details_age.setText(ageSelected.second) - - updatePersonalDetailsDataField() - updateButtonState() - - personal_details_age_error.visibility = - if (isValidAge()) { - View.GONE - } else { - View.VISIBLE - } - - personal_details_post_code.requestFocus() - } - .setNegativeButton(android.R.string.no, null) - .show() - } + // Apply the adapter to the spinner + personal_details_age.adapter = adapter } + personal_details_age.setSelectionCallback(object : + SelectionCallbackSpinner.CustomSpinnerSelectionCallback { + override fun onItemSelected(position: Int) { + age = when (position) { + 0 -> -1 + 1 -> 8 + 2 -> 22 + 3 -> 35 + 4 -> 45 + 5 -> 55 + 6 -> 65 + 7 -> 75 + 8 -> 85 + 9 -> 95 + else -> -1 + } + + CentralLog.d(TAG, "age = $age") + + updateButtonState() + + if (checkAgeAndDisplayAgeError()) { + personal_details_post_code.requestFocus() + } + } + }) + personal_details_name.setOnFocusChangeListener { _, hasFocus -> updatePersonalDetailsDataField() updateButtonState() @@ -115,17 +121,21 @@ class PersonalDetailsFragment : PagerChildFragment() { } } + personal_details_name.setOnEditorActionListener(OnEditorActionListener { textView, actionId, event -> + if (actionId == EditorInfo.IME_ACTION_NEXT) { + hideKeyboard() + textView.clearFocus() + personal_details_age.requestFocus() + personal_details_age.performClick() + } + + true + }) + personal_details_post_code.setOnFocusChangeListener { _, hasFocus -> - if(hasFocus) { + if (hasFocus) { updatePersonalDetailsDataField() updateButtonState() - - personal_details_age_error.visibility = - if (isValidAge()) { - View.GONE - } else { - View.VISIBLE - } } } @@ -141,13 +151,6 @@ class PersonalDetailsFragment : PagerChildFragment() { updatePersonalDetailsDataField() updateButtonState() - personal_details_age_error.visibility = - if (isValidAge()) { - View.GONE - } else { - View.VISIBLE - } - personal_details_post_code_error.visibility = if (isValidPostcode()) { View.GONE } else { @@ -159,17 +162,7 @@ class PersonalDetailsFragment : PagerChildFragment() { false // pass on to other listeners. } - personal_details_age.setOnFocusChangeListener { _, hasFocus -> - if(hasFocus){ - showAgePicker() - } - } - - personal_details_age.setOnClickListener { - showAgePicker() - } - - // set accessibility focus to the title "Enter your details" + personal_details_headline.setHeading() personal_details_headline.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) } diff --git a/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/personal/SelectionCallbackSpinner.kt b/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/personal/SelectionCallbackSpinner.kt new file mode 100644 index 0000000..8f1380c --- /dev/null +++ b/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/personal/SelectionCallbackSpinner.kt @@ -0,0 +1,32 @@ +package au.gov.health.covidsafe.ui.onboarding.fragment.personal + +import android.content.Context +import android.util.AttributeSet +import androidx.appcompat.widget.AppCompatSpinner + +class SelectionCallbackSpinner : AppCompatSpinner { + + constructor(context: Context) : super(context) + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) + + interface CustomSpinnerSelectionCallback { + fun onItemSelected(position: Int) + } + + private var selectionCallback: CustomSpinnerSelectionCallback? = null + + fun setSelectionCallback(selectionCallback: CustomSpinnerSelectionCallback) { + this.selectionCallback = selectionCallback + } + + fun removeSelectionCallback() { + selectionCallback = null + } + + override fun setSelection(position: Int) { + super.setSelection(position) + + if (position == selectedItemPosition) selectionCallback?.onItemSelected(position) + } +} \ No newline at end of file diff --git a/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/registrationconsent/RegistrationConsentFragment.kt b/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/registrationconsent/RegistrationConsentFragment.kt index 2dcfc03..939eef9 100644 --- a/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/registrationconsent/RegistrationConsentFragment.kt +++ b/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/registrationconsent/RegistrationConsentFragment.kt @@ -6,6 +6,7 @@ import android.view.View import android.view.ViewGroup import android.view.accessibility.AccessibilityEvent import au.gov.health.covidsafe.R +import au.gov.health.covidsafe.talkback.setHeading import au.gov.health.covidsafe.ui.PagerChildFragment import au.gov.health.covidsafe.ui.PagerContainer import au.gov.health.covidsafe.ui.UploadButtonLayout @@ -21,8 +22,8 @@ class RegistrationConsentFragment : PagerChildFragment() { override fun onResume() { super.onResume() - // set accessibility focus to the title "I consent to the Australian ..." - registration_consent_text.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) + registration_consent_headline.setHeading() + registration_consent_headline.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) updateButtonState() } diff --git a/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/undersixteen/UnderSixteenFragment.kt b/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/undersixteen/UnderSixteenFragment.kt index 5a2d305..fed5567 100644 --- a/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/undersixteen/UnderSixteenFragment.kt +++ b/app/src/main/java/au/gov/health/covidsafe/ui/onboarding/fragment/undersixteen/UnderSixteenFragment.kt @@ -1,12 +1,13 @@ package au.gov.health.covidsafe.ui.onboarding.fragment.undersixteen import android.os.Bundle -import android.text.method.LinkMovementMethod import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.view.accessibility.AccessibilityEvent import androidx.core.os.bundleOf import au.gov.health.covidsafe.R +import au.gov.health.covidsafe.talkback.setHeading import au.gov.health.covidsafe.ui.PagerChildFragment import au.gov.health.covidsafe.ui.UploadButtonLayout import au.gov.health.covidsafe.ui.onboarding.fragment.enternumber.EnterNumberFragment @@ -22,6 +23,9 @@ class UnderSixteenFragment : PagerChildFragment() { override fun onResume() { super.onResume() updateButtonState() + + under_sixteen_headline.setHeading() + under_sixteen_headline.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) } override fun getUploadButtonLayout(): UploadButtonLayout = UploadButtonLayout.ContinueLayout(R.string.consent_button) { diff --git a/app/src/main/java/au/gov/health/covidsafe/ui/upload/presentation/UploadFinishedFragment.kt b/app/src/main/java/au/gov/health/covidsafe/ui/upload/presentation/UploadFinishedFragment.kt index 0e208c4..15428c3 100644 --- a/app/src/main/java/au/gov/health/covidsafe/ui/upload/presentation/UploadFinishedFragment.kt +++ b/app/src/main/java/au/gov/health/covidsafe/ui/upload/presentation/UploadFinishedFragment.kt @@ -6,6 +6,7 @@ import android.view.View import android.view.ViewGroup import android.view.accessibility.AccessibilityEvent import au.gov.health.covidsafe.R +import au.gov.health.covidsafe.talkback.setHeading import au.gov.health.covidsafe.ui.PagerChildFragment import au.gov.health.covidsafe.ui.UploadButtonLayout import kotlinx.android.synthetic.main.fragment_upload_finished.* @@ -21,7 +22,7 @@ class UploadFinishedFragment : PagerChildFragment() { override fun onResume() { super.onResume() - // set accessibility focus to the title + header.setHeading() header.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) } diff --git a/app/src/main/java/au/gov/health/covidsafe/ui/upload/presentation/UploadInitialFragment.kt b/app/src/main/java/au/gov/health/covidsafe/ui/upload/presentation/UploadInitialFragment.kt index 01a70b6..b055784 100644 --- a/app/src/main/java/au/gov/health/covidsafe/ui/upload/presentation/UploadInitialFragment.kt +++ b/app/src/main/java/au/gov/health/covidsafe/ui/upload/presentation/UploadInitialFragment.kt @@ -6,6 +6,7 @@ import android.view.View import android.view.ViewGroup import android.view.accessibility.AccessibilityEvent import au.gov.health.covidsafe.R +import au.gov.health.covidsafe.talkback.setHeading import au.gov.health.covidsafe.ui.PagerChildFragment import au.gov.health.covidsafe.ui.UploadButtonLayout import kotlinx.android.synthetic.main.fragment_upload_initial.* @@ -22,7 +23,9 @@ class UploadInitialFragment : PagerChildFragment() { override fun onResume() { super.onResume() - // set accessibility focus to the title + removeViewInLandscapeMode(upload_initial_picture) + + upload_initial_headline.setHeading() upload_initial_headline.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) } diff --git a/app/src/main/java/au/gov/health/covidsafe/ui/upload/presentation/UploadStepFourFragment.kt b/app/src/main/java/au/gov/health/covidsafe/ui/upload/presentation/UploadStepFourFragment.kt index 7a6ed7f..4b7cf21 100644 --- a/app/src/main/java/au/gov/health/covidsafe/ui/upload/presentation/UploadStepFourFragment.kt +++ b/app/src/main/java/au/gov/health/covidsafe/ui/upload/presentation/UploadStepFourFragment.kt @@ -8,6 +8,8 @@ import android.view.View import android.view.ViewGroup import android.view.accessibility.AccessibilityEvent import au.gov.health.covidsafe.R +import au.gov.health.covidsafe.links.LinkBuilder +import au.gov.health.covidsafe.talkback.setHeading import au.gov.health.covidsafe.ui.PagerChildFragment import au.gov.health.covidsafe.ui.UploadButtonLayout import kotlinx.android.synthetic.main.fragment_upload_page_4.* @@ -22,6 +24,8 @@ class UploadStepFourFragment : PagerChildFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + + subHeader.text = LinkBuilder.getUploadConsentContent(requireContext()) subHeader.movementMethod = LinkMovementMethod.getInstance() } @@ -30,7 +34,7 @@ class UploadStepFourFragment : PagerChildFragment() { super.onResume() updateButtonState() - // set accessibility focus to the title + header.setHeading() header.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) } diff --git a/app/src/main/java/au/gov/health/covidsafe/ui/upload/presentation/VerifyUploadPinFragment.kt b/app/src/main/java/au/gov/health/covidsafe/ui/upload/presentation/VerifyUploadPinFragment.kt index 30103dc..c180fb3 100644 --- a/app/src/main/java/au/gov/health/covidsafe/ui/upload/presentation/VerifyUploadPinFragment.kt +++ b/app/src/main/java/au/gov/health/covidsafe/ui/upload/presentation/VerifyUploadPinFragment.kt @@ -8,7 +8,9 @@ import android.view.View import android.view.ViewGroup import android.view.accessibility.AccessibilityEvent import androidx.core.os.bundleOf +import androidx.core.widget.doOnTextChanged import au.gov.health.covidsafe.R +import au.gov.health.covidsafe.talkback.setHeading import au.gov.health.covidsafe.ui.PagerChildFragment import au.gov.health.covidsafe.ui.UploadButtonLayout import au.gov.health.covidsafe.ui.onboarding.fragment.enternumber.EnterNumberFragment @@ -16,7 +18,10 @@ import au.gov.health.covidsafe.ui.view.UploadingDialog import au.gov.health.covidsafe.ui.view.UploadingErrorDialog import com.atlassian.mobilekit.module.core.utils.SystemUtils import kotlinx.android.synthetic.main.fragment_verify_upload_pin.* -import kotlinx.android.synthetic.main.fragment_verify_upload_pin.view.* +import kotlinx.android.synthetic.main.fragment_verify_upload_pin.enter_pin_error_label +import kotlinx.android.synthetic.main.fragment_verify_upload_pin.pin +import kotlinx.android.synthetic.main.fragment_verify_upload_pin.root +import kotlinx.android.synthetic.main.fragment_verify_upload_pin.view.pin class VerifyUploadPinFragment : PagerChildFragment() { @@ -28,7 +33,7 @@ class VerifyUploadPinFragment : PagerChildFragment() { private var dialog: Dialog? = null - private lateinit var presenter : VerifyUploadPinPresenter + private lateinit var presenter: VerifyUploadPinPresenter override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? = inflater.inflate(R.layout.fragment_verify_upload_pin, container, false) @@ -42,22 +47,18 @@ class VerifyUploadPinFragment : PagerChildFragment() { override fun onResume() { super.onResume() - pin.onPinChanged = { + + pin.doOnTextChanged { _, _, _, _ -> updateButtonState() hideInvalidOtp() } - // set accessibility focus to the title + header.setHeading() header.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) } - override fun onPause() { - super.onPause() - pin.onPinChanged = null - } - override fun getUploadButtonLayout() = UploadButtonLayout.ContinueLayout(R.string.action_verify_upload_pin) { - presenter.uploadData(requireView().pin.value) + presenter.uploadData(requireView().pin.text.toString()) } override fun updateButtonState() { @@ -69,7 +70,7 @@ class VerifyUploadPinFragment : PagerChildFragment() { } private fun isIncorrectPinFormat(): Boolean { - return requireView().pin.isIncomplete + return requireView().pin.text.toString().length != 6 } fun hideKeyboard() { @@ -92,7 +93,7 @@ class VerifyUploadPinFragment : PagerChildFragment() { activity?.let { dialog = UploadingErrorDialog(it, object : OnUploadErrorInterface { override fun onPositiveClicked() { - presenter.uploadData(requireView().pin.value) + presenter.uploadData(requireView().pin.text.toString()) } override fun onNegativeClicked() { diff --git a/app/src/main/java/au/gov/health/covidsafe/ui/view/PinInputView.kt b/app/src/main/java/au/gov/health/covidsafe/ui/view/PinInputView.kt deleted file mode 100644 index 565ea4a..0000000 --- a/app/src/main/java/au/gov/health/covidsafe/ui/view/PinInputView.kt +++ /dev/null @@ -1,80 +0,0 @@ -package au.gov.health.covidsafe.ui.view - -import android.content.Context -import android.util.AttributeSet -import android.view.KeyEvent -import android.view.LayoutInflater -import android.widget.EditText -import androidx.constraintlayout.widget.ConstraintLayout -import androidx.core.widget.doAfterTextChanged -import kotlinx.android.synthetic.main.view_pin.view.* -import au.gov.health.covidsafe.R - -class PinInputView @JvmOverloads constructor( - context: Context, - attrs: AttributeSet? = null, - defStyle: Int = -1) : - ConstraintLayout(context, attrs, defStyle) { - - private val pinOne: EditText? by lazy { pin_1 } - private val pinTwo: EditText? by lazy { pin_2 } - private val pinThree: EditText? by lazy { pin_3 } - private val pinFour: EditText? by lazy { pin_4 } - private val pinFive: EditText? by lazy { pin_5 } - private val pinSix: EditText? by lazy { pin_6 } - var onPinChanged: (() -> Unit)? = null - - private val allInputs by lazy { - listOf(pinOne, pinTwo, pinThree, pinFour, pinFive, pinSix) - } - - val value: String - get() = allInputs.mapNotNull { it?.text }.joinToString("") - - val isIncomplete: Boolean - get() = allInputs.any { it?.text.isNullOrEmpty() } - - init { - LayoutInflater.from(context).inflate(R.layout.view_pin, this, true) - pinOne?.onDigitChanged(pinTwo) - pinOne?.onDeletePressed(null) - - pinTwo?.onDigitChanged(pinThree) - pinTwo?.onDeletePressed(pinOne) - - pinThree?.onDigitChanged(pinFour) - pinThree?.onDeletePressed(pinTwo) - - pinFour?.onDigitChanged(pinFive) - pinFour?.onDeletePressed(pinThree) - - pinFive?.onDigitChanged(pinSix) - pinFive?.onDeletePressed(pinFour) - - pinSix?.onDigitChanged(null) - pinSix?.onDeletePressed(pinFive) - } - - private fun EditText.onDigitChanged(next: EditText? = null) { - doAfterTextChanged { - if (it?.length == 1) { - next?.requestFocus() - onPinChanged?.invoke() - } else if (it.isNullOrBlank()) { - onPinChanged?.invoke() - } - } - } - - private fun EditText.onDeletePressed(prev: EditText? = null) { - setOnKeyListener { view, keyCode, keyEvent -> - if (keyCode == KeyEvent.KEYCODE_DEL && text.isNullOrEmpty()) { - prev?.requestFocus() - onPinChanged?.invoke() - true - } else { - false - } - } - } -} diff --git a/app/src/main/res/drawable/ic_arrow_drop_down.xml b/app/src/main/res/drawable/ic_arrow_drop_down.xml deleted file mode 100644 index b571455..0000000 --- a/app/src/main/res/drawable/ic_arrow_drop_down.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_check.xml b/app/src/main/res/drawable/ic_check.xml deleted file mode 100644 index 2505846..0000000 --- a/app/src/main/res/drawable/ic_check.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ic_close.xml b/app/src/main/res/drawable/ic_close.xml deleted file mode 100644 index 55c4d27..0000000 --- a/app/src/main/res/drawable/ic_close.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_doctor_circle.xml b/app/src/main/res/drawable/ic_doctor_circle.xml deleted file mode 100644 index 8b78a18..0000000 --- a/app/src/main/res/drawable/ic_doctor_circle.xml +++ /dev/null @@ -1,108 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/ic_home_logo.xml b/app/src/main/res/drawable/ic_home_logo.xml deleted file mode 100644 index 1fae9cb..0000000 --- a/app/src/main/res/drawable/ic_home_logo.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - diff --git a/app/src/main/res/drawable/ic_home_unprotected.xml b/app/src/main/res/drawable/ic_home_unprotected.xml deleted file mode 100644 index 25c0e0a..0000000 --- a/app/src/main/res/drawable/ic_home_unprotected.xml +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/ic_illustration_upload_step_4.xml b/app/src/main/res/drawable/ic_illustration_upload_step_4.xml deleted file mode 100644 index feeca9e..0000000 --- a/app/src/main/res/drawable/ic_illustration_upload_step_4.xml +++ /dev/null @@ -1,130 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index 3700fd0..0000000 --- a/app/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - diff --git a/app/src/main/res/drawable/ic_launcher_round.xml b/app/src/main/res/drawable/ic_launcher_round.xml deleted file mode 100644 index 9aa50ee..0000000 --- a/app/src/main/res/drawable/ic_launcher_round.xml +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/ic_logo_home_uploaded.xml b/app/src/main/res/drawable/ic_logo_home_uploaded.xml deleted file mode 100644 index 59a3c6c..0000000 --- a/app/src/main/res/drawable/ic_logo_home_uploaded.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/drawable/ic_personal_details.xml b/app/src/main/res/drawable/ic_personal_details.xml deleted file mode 100644 index 5b28ddf..0000000 --- a/app/src/main/res/drawable/ic_personal_details.xml +++ /dev/null @@ -1,165 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/ic_phone_checked_circle.xml b/app/src/main/res/drawable/ic_phone_checked_circle.xml deleted file mode 100644 index bb41493..0000000 --- a/app/src/main/res/drawable/ic_phone_checked_circle.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/ic_red_cross.xml b/app/src/main/res/drawable/ic_red_cross.xml new file mode 100644 index 0000000..a189255 --- /dev/null +++ b/app/src/main/res/drawable/ic_red_cross.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_smartphone.xml b/app/src/main/res/drawable/ic_smartphone.xml deleted file mode 100644 index 51ad6a1..0000000 --- a/app/src/main/res/drawable/ic_smartphone.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ic_smile.xml b/app/src/main/res/drawable/ic_smile.xml deleted file mode 100644 index f7c1170..0000000 --- a/app/src/main/res/drawable/ic_smile.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - diff --git a/app/src/main/res/drawable/ic_splash_screen_title.xml b/app/src/main/res/drawable/ic_splash_screen_title.xml deleted file mode 100644 index e1ede1b..0000000 --- a/app/src/main/res/drawable/ic_splash_screen_title.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - diff --git a/app/src/main/res/drawable/ic_under_sixteen.xml b/app/src/main/res/drawable/ic_under_sixteen.xml deleted file mode 100644 index 695680a..0000000 --- a/app/src/main/res/drawable/ic_under_sixteen.xml +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/ic_unfold_less_black_24dp.xml b/app/src/main/res/drawable/ic_unfold_less_black_24dp.xml deleted file mode 100644 index 7282a97..0000000 --- a/app/src/main/res/drawable/ic_unfold_less_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_unfold_more_black_24dp.xml b/app/src/main/res/drawable/ic_unfold_more_black_24dp.xml deleted file mode 100644 index e9ba754..0000000 --- a/app/src/main/res/drawable/ic_unfold_more_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_upload_error.xml b/app/src/main/res/drawable/ic_upload_error.xml deleted file mode 100644 index 5d6d3fd..0000000 --- a/app/src/main/res/drawable/ic_upload_error.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - diff --git a/app/src/main/res/drawable/illustration_data_uploaded.xml b/app/src/main/res/drawable/illustration_data_uploaded.xml deleted file mode 100644 index f0e0ec5..0000000 --- a/app/src/main/res/drawable/illustration_data_uploaded.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/splash_screen_white_wave.xml b/app/src/main/res/drawable/splash_screen_white_wave.xml deleted file mode 100644 index 0b8abe5..0000000 --- a/app/src/main/res/drawable/splash_screen_white_wave.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - diff --git a/app/src/main/res/layout/activity_plot.xml b/app/src/main/res/layout/activity_plot.xml deleted file mode 100644 index b414ffc..0000000 --- a/app/src/main/res/layout/activity_plot.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - diff --git a/app/src/main/res/layout/activity_self_isolation.xml b/app/src/main/res/layout/activity_self_isolation.xml deleted file mode 100644 index 3bfdd05..0000000 --- a/app/src/main/res/layout/activity_self_isolation.xml +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - - - - - - - - - - - -