├── .gitignore ├── .idea ├── assetWizardSettings.xml ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── misc.xml ├── runConfigurations.xml └── vcs.xml ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── smartlook │ │ └── consentsdksample │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── smartlook │ │ │ └── consentsdksample │ │ │ ├── App.kt │ │ │ └── ui │ │ │ └── main │ │ │ ├── ConsentResultsAdapter.kt │ │ │ └── MainActivity.kt │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ └── consent_result.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── smartlook │ └── consentsdksample │ └── ExampleUnitTest.kt ├── build.gradle ├── consentsdk ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── smartlook │ │ └── consentsdk │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── smartlook │ │ │ └── consentsdk │ │ │ ├── ConsentSDK.kt │ │ │ ├── data │ │ │ ├── ConsentFormData.kt │ │ │ └── ConsentFormItem.kt │ │ │ ├── helpers │ │ │ ├── ConsentHelper.kt │ │ │ ├── SharedPreferencesHelper.kt │ │ │ └── UtilsHelper.kt │ │ │ ├── listeners │ │ │ ├── ConsentItemListener.kt │ │ │ └── ConsentResultsListener.kt │ │ │ └── ui │ │ │ ├── consent │ │ │ ├── ConsentFormBase.kt │ │ │ ├── ConsentFormItemView.kt │ │ │ ├── activity │ │ │ │ └── ConsentFormActivity.kt │ │ │ ├── dialog │ │ │ │ ├── ConsentFormDialog.kt │ │ │ │ └── ConsentFormDialogFragment.kt │ │ │ └── fragment │ │ │ │ └── ConsentFormFragment.kt │ │ │ └── customViews │ │ │ └── LeftSideSwitch.kt │ └── res │ │ ├── drawable │ │ └── ic_info_link.xml │ │ ├── layout │ │ ├── consent_activity.xml │ │ ├── consent_dialog.xml │ │ ├── consent_item.xml │ │ └── left_side_switch.xml │ │ └── values │ │ ├── attrs.xml │ │ ├── colors.xml │ │ └── dimens.xml │ └── test │ └── java │ └── com │ └── smartlook │ └── consentsdk │ └── ExampleUnitTest.java ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── screenshots ├── consent_form_activity.png ├── consent_form_activity_styled.png ├── consent_form_dialog.png └── consent_form_dialog_styled.png └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | *.aab 5 | 6 | # Files for the ART/Dalvik VM 7 | *.dex 8 | 9 | # Java class files 10 | *.class 11 | 12 | # Generated files 13 | bin/ 14 | gen/ 15 | out/ 16 | 17 | # Gradle files 18 | .gradle/ 19 | build/ 20 | 21 | # Local configuration file (sdk path, etc) 22 | local.properties 23 | 24 | # Proguard folder generated by Eclipse 25 | proguard/ 26 | 27 | # Log Files 28 | *.log 29 | 30 | # Android Studio Navigation editor temp files 31 | .navigation/ 32 | 33 | # Android Studio captures folder 34 | captures/ 35 | 36 | # User-specific stuff 37 | .idea/**/workspace.xml 38 | .idea/**/tasks.xml 39 | .idea/**/usage.statistics.xml 40 | .idea/**/dictionaries 41 | .idea/**/shelf 42 | *.iml 43 | 44 | # Generated files 45 | .idea/**/contentModel.xml 46 | 47 | # Sensitive or high-churn files 48 | .idea/**/dataSources/ 49 | .idea/**/dataSources.ids 50 | .idea/**/dataSources.local.xml 51 | .idea/**/sqlDataSources.xml 52 | .idea/**/dynamic.xml 53 | .idea/**/uiDesigner.xml 54 | .idea/**/dbnavigator.xml 55 | 56 | # Gradle 57 | .idea/**/gradle.xml 58 | .idea/**/libraries 59 | 60 | # Gradle and Maven with auto-import 61 | .idea/modules.xml 62 | .idea/*.iml 63 | .idea/modules 64 | 65 | # CMake 66 | cmake-build-*/ 67 | 68 | # Mongo Explorer plugin 69 | .idea/**/mongoSettings.xml 70 | 71 | # File-based project format 72 | *.iws 73 | 74 | # IntelliJ 75 | out/ 76 | 77 | # mpeltonen/sbt-idea plugin 78 | .idea_modules/ 79 | 80 | # JIRA plugin 81 | atlassian-ide-plugin.xml 82 | 83 | # Cursive Clojure plugin 84 | .idea/replstate.xml 85 | 86 | # Crashlytics plugin (for Android Studio and IntelliJ) 87 | com_crashlytics_export_strings.xml 88 | crashlytics.properties 89 | crashlytics-build.properties 90 | fabric.properties 91 | 92 | # Editor-based Rest Client 93 | .idea/httpRequests 94 | 95 | # Android studio 3.1+ serialized cache file 96 | .idea/caches/build_file_checksums.ser 97 | 98 | # Sonarlint plugin 99 | .idea/sonarlint 100 | 101 | # Keystore files 102 | *.jks 103 | *.keystore 104 | 105 | # External native build folder generated in Android Studio 2.2 and later 106 | .externalNativeBuild 107 | 108 | # Google Services (e.g. APIs or Firebase) 109 | google-services.json 110 | 111 | # Freeline 112 | freeline.py 113 | freeline/ 114 | freeline_project_description.json 115 | 116 | # fastlane 117 | fastlane/report.xml 118 | fastlane/Preview.html 119 | fastlane/screenshots 120 | fastlane/test_output 121 | fastlane/readme.md 122 | -------------------------------------------------------------------------------- /.idea/assetWizardSettings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 45 | 46 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 9 | 10 | 11 | 21 | 22 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 38 | 39 | 40 | 42 | 43 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Smartlook 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Consent SDK for Android 2 | 3 | Obtaining explicit user consent regarding the gathering analytics data in an app, or with processing user’s personal data is an important part of establishing user trust and seamless user experience. 4 | 5 | Although implementing some form to obtain user consents and store them for further reference seems pretty straightforward, digging into it reveals (as usual with “simple tasks”) many programming and design details that must be implemented, which are not the core functionality of your app. 6 | 7 | 8 | 9 | ## Consent SDK main functionality 10 | 11 | - Provides configurable __consent form__ that can be displayed as: 12 | - __Dialog__ 13 | - __FragmentDialog__(persists orientation changes) 14 | - __Activity__ 15 | - __Fragment__ 16 | - Stores consent results and provides access methods. 17 | 18 | ## Installation 19 | Add the following dependency in your app's build.gradle: 20 | 21 | ``` 22 | implementation 'com.smartlook:consent:1.0' 23 | ``` 24 | 25 | And add the following in your project's build.gradle: 26 | 27 | ``` 28 | allprojects { 29 | repositories { 30 | maven { 31 | url "https://sdk.smartlook.com/android/release" 32 | } 33 | } 34 | } 35 | ``` 36 | 37 | ## How to use 38 | 39 | Firstly you need to instantiate `ConsentSDK` with `applicationContext`. 40 | 41 | ``` 42 | val consentSDK = ConsentSDK(applicationContext) 43 | ``` 44 | 45 | This object is going to be used for all interactions with ConsentSDK. 46 | 47 | ### Consent form data 48 | 49 | Before you can display consent form you need to prepare consent form data. 50 | 51 | ``` 52 | companion object { 53 | const val CONSENT_1_KEY = "consent_1_key" 54 | const val CONSENT_2_KEY = "consent_2_key" 55 | } 56 | 57 | ... 58 | 59 | val consentFormItems = arrayOf( 60 | ConsentFormItem( 61 | consentKey = CONSENT_1_KEY, 62 | required = true, 63 | description = getString(R.string.consent_1_description), 64 | link = null 65 | ), 66 | ConsentFormItem( 67 | consentKey = CONSENT_2_KEY, 68 | required = false, 69 | description = getString(R.string.consent_2_description), 70 | link = getString(R.string.consent_2_link) 71 | ) 72 | ) 73 | 74 | val consentFormData = ConsentFormData( 75 | titleText = getString(R.string.consent_form_title), 76 | descriptionText = getString(R.string.consent_form_description), 77 | confirmButtonText = getString(R.string.consent_form_confirm_button_text), 78 | consentFormItems = consentFormItems) 79 | 80 | ``` 81 | 82 | Array `consentFormItems` represents consents we want the user to grant us. Every item needs to have: 83 | - unique `consentKey` that represents it and can be used to obtain grant result for this consent. 84 | - `required` flag. If this flag is set to `true` user cannot successfully finish the consent form without granting this consent. 85 | - `descriptionText` informing the user about the consent. 86 | - `link` (optional) that lets the user open a web page (URL) with more info. 87 | 88 | Object `consentFormData` provides all needed data for displaying consent form. 89 | 90 | ### Showing consent form on `Dialog` 91 | A most simple and straight-forward way of displaying consent form is on `Dialog`. It has one __drawback__, this way we __cannot__ properly persist user data on orientation change. Use this if you have locked screen orientation. 92 | 93 | ``` 94 | consentSDK.showConsentFormDialog(consentFormData, object : ConsentResultsListener { 95 | override fun onConsentResults(consentResults: HashMap) { 96 | // consent form result here 97 | } 98 | }) 99 | ``` 100 | 101 | ### Showing consent form on `DialogFragment` 102 | By using `DialogFragment` SDK can properly handle orientation changes. 103 | 104 | ``` 105 | consentSDK.showConsentFormDialogFragment(/, consentFormData) 106 | ``` 107 | 108 | The first parameter of `showConsentFormDialogFragment` accepts `Activity` or `Fragment` reference so you can call it from both. 109 | Your calling `Activity` or `Fragment` __must__ implement ConsentResultsListener. 110 | 111 | ``` 112 | class SampleActivity : AppCompatActivity(), ConsentResultsListener { 113 | 114 | ... 115 | 116 | override fun onConsentResults(consentResults: HashMap) { 117 | // consent form result here 118 | } 119 | } 120 | ``` 121 | 122 | ### Starting consent form `Activity` 123 | 124 | ``` 125 | class SampleActivity : AppCompatActivity() { 126 | 127 | companion object { 128 | const val CONSENT_REQUEST_CODE = 10001 129 | } 130 | 131 | ... 132 | 133 | consentSDK.startConsentFormActivity(this, consentFormData, CONSENT_REQUEST_CODE) 134 | 135 | ... 136 | 137 | override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { 138 | if (requestCode == CONSENT_REQUEST_CODE) { 139 | if (resultCode == Activity.RESULT_OK) { 140 | val consentResults = consentSDK.parseOutConsentResults(data) 141 | } else { 142 | // user didnt confirm the form (back press) 143 | } 144 | } 145 | } 146 | } 147 | ``` 148 | 149 | Consent form `Activity` is started "for a result" so to get a result you need to implement `onActivityResult` method in your `Activity`. 150 | 151 | ### Creating conset form `Fragment` 152 | Method `createConsentFormFragment` lets you create `Fragment` with consent form. Example usage might look something like this: 153 | 154 | ``` 155 | const val TAG = "unique_fragment_tag" 156 | 157 | ... 158 | 159 | with(supportFragmentManager) { 160 | beginTransaction() 161 | .replace(R.id.fragment_placeholder, consentSDK.createConsentFormFragment(consentFormData), TAG) 162 | .commit() 163 | executePendingTransactions() 164 | } 165 | ``` 166 | 167 | `ConsentResultsListener` can be registered like this: 168 | 169 | ``` 170 | val consentFormFragment = supportFragmentManager.findFragmentByTag(TAG) as ConsentFormFragment 171 | consentFormFragment.registerConsentResultsListener(object : ConsentResultsListener { 172 | override fun onConsentResults(consentResults: HashMap) { 173 | // Consent form result here 174 | } 175 | }) 176 | ``` 177 | 178 | ### Consent results 179 | When user sucessfully finishes consent form you gonna get `consentResult`. It is a `HashMap` in which: 180 | - `key` == `consentKey` 181 | - `value` represents `consentResult`: 182 | - `true` consent was granted. 183 | - `false` consent was rejected. 184 | 185 | ### Are consent results stored? 186 | SDK method `areConsentResultsStored()` can be used to determine if the user has already successfully filled consent form and results were stored. 187 | 188 | ### Obtaining consent 189 | 190 | If you want to obtain a grant result for given conset (identified by unique `consentKey`) you can do it like this: 191 | 192 | ``` 193 | val consentResult = consentSDK.loadConsetResult(consentKey) 194 | ``` 195 | 196 | If `consentResult` is: 197 | - `true` consent was granted. 198 | - `false` consent was rejected. 199 | - `null` not defined. 200 | 201 | ## Styling 202 | 203 | 204 | 205 | You can define custom `style` for the consent form. All configurable attributes are listed in the table below. 206 | 207 | | Attribute | Description | 208 | |:-------------------------:|:------------------------------------------------:| 209 | | colorAccent | Confirm button, link icons and `Switches` color. | 210 | | cf_textColor | Description text and form item texts color. | 211 | | cf_titleTextColor | Title text color. | 212 | | cf_confirmButtonTextColor | Confirm button text color. | 213 | | cf_backgroundColor | Form background color. | 214 | | cf_dividerColor | Form item list divider color. | 215 | 216 | ### `Dialog`/`FragmentDialog` 217 | 218 | In `styles.xml` define custom Dialog style: 219 | 220 | ``` 221 | 229 | ``` 230 | 231 | Then add the style reference to `showConsentFormDialog`/`showConsentFormDialogFragment` method like this: 232 | 233 | ``` 234 | // Dialog 235 | consentSDK.showConsentFormDialog(this, consentFormData, R.style.DialogStyle, listener) 236 | 237 | // DialogFragment 238 | consentSDK.showConsentFormDialogFragment(this, consentFormData, R.style.DialogStyle) 239 | ``` 240 | 241 | ### `Activity` 242 | 243 | In `styles.xml` define custom Activity style: 244 | 245 | ``` 246 | 254 | ``` 255 | 256 | Then add the style reference to `startConsentFormActivity` method like this: 257 | 258 | ``` 259 | consentSDK.startConsentFormActivity(this, consentFormData, CONSENT_REQUEST_CODE, R.style.ActivityStyle) 260 | ``` 261 | 262 | ### Fragment 263 | 264 | In `styles.xml` define custom Fragment style: 265 | 266 | ``` 267 | 275 | ``` 276 | 277 | Then add the style reference to `startConsentFormActivity` method like this: 278 | 279 | ``` 280 | consentSDK.createConsentFormFragment(consentFormData, R.style.FragmentStyle) 281 | ``` 282 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-android-extensions' 4 | 5 | androidExtensions { 6 | experimental = true 7 | } 8 | 9 | android { 10 | compileSdkVersion 28 11 | defaultConfig { 12 | applicationId "com.smartlook.consentsdksample" 13 | minSdkVersion 16 14 | targetSdkVersion 28 15 | versionCode 1 16 | versionName "1.0" 17 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 18 | } 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | } 26 | 27 | dependencies { 28 | implementation fileTree(dir: 'libs', include: ['*.jar']) 29 | implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 30 | implementation 'com.android.support:appcompat-v7:28.0.0' 31 | implementation "com.android.support:design:28.0.0" 32 | testImplementation 'junit:junit:4.12' 33 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 34 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 35 | 36 | compile project(':consentsdk') 37 | } 38 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/smartlook/consentsdksample/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.smartlook.consentsdksample 2 | 3 | import android.support.test.InstrumentationRegistry 4 | import android.support.test.runner.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getTargetContext() 22 | assertEquals("com.smartlook.consentsdksample", appContext.packageName) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/smartlook/consentsdksample/App.kt: -------------------------------------------------------------------------------- 1 | package com.smartlook.consentsdksample 2 | 3 | import android.app.Application 4 | import com.smartlook.consentsdk.ConsentSDK 5 | 6 | class App: Application() { 7 | 8 | companion object { 9 | lateinit var consentSDK: ConsentSDK 10 | } 11 | 12 | override fun onCreate() { 13 | super.onCreate() 14 | 15 | consentSDK = ConsentSDK(applicationContext) 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/java/com/smartlook/consentsdksample/ui/main/ConsentResultsAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.smartlook.consentsdksample.ui.main 2 | 3 | import android.content.Context 4 | import android.graphics.Color 5 | import android.support.v7.widget.RecyclerView 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import com.smartlook.consentsdksample.R 10 | import kotlinx.android.extensions.LayoutContainer 11 | import kotlinx.android.synthetic.main.consent_result.view.* 12 | 13 | class ConsentResultsAdapter(val context: Context, 14 | private var consentResults: HashMap) : RecyclerView.Adapter() { 15 | 16 | private var consentKeys = consentResults.keys.toList() 17 | 18 | /* Public methods *****************************************************************************/ 19 | 20 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ConsentResultVH { 21 | return ConsentResultVH(LayoutInflater.from(context).inflate(R.layout.consent_result, parent, false)) 22 | } 23 | 24 | override fun onBindViewHolder(holder: ConsentResultVH, position: Int) { 25 | holder.bindConsentResult(consentKeys[position], consentResults[consentKeys[position]]) 26 | } 27 | 28 | override fun getItemCount() = consentResults.size 29 | 30 | 31 | /* View holders *******************************************************************************/ 32 | 33 | inner class ConsentResultVH(override val containerView: View) : RecyclerView.ViewHolder(containerView), LayoutContainer { 34 | fun bindConsentResult(consentKey: String, grantResult: Boolean?) { 35 | 36 | containerView.consent_key.text = consentKey 37 | containerView.grand_result.text = grantResultText(grantResult) 38 | containerView.grand_result.setTextColor(grantResultColor(grantResult)) 39 | } 40 | 41 | private fun grantResultText(grantResult: Boolean?): String { 42 | return when(grantResult) { 43 | true -> "GRANTED" 44 | false -> "REJECTED" 45 | null -> "UNDEFINED" 46 | } 47 | } 48 | 49 | private fun grantResultColor(grantResult: Boolean?): Int { 50 | return when(grantResult) { 51 | true -> Color.GREEN 52 | false -> Color.RED 53 | null -> Color.GRAY 54 | } 55 | } 56 | } 57 | 58 | } -------------------------------------------------------------------------------- /app/src/main/java/com/smartlook/consentsdksample/ui/main/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.smartlook.consentsdksample.ui.main 2 | 3 | import android.app.Activity 4 | import android.content.Intent 5 | import android.os.Bundle 6 | import android.support.v7.app.AppCompatActivity 7 | import android.support.v7.widget.LinearLayoutManager 8 | import com.smartlook.consentsdk.data.ConsentFormData 9 | import com.smartlook.consentsdk.data.ConsentFormItem 10 | import com.smartlook.consentsdk.listeners.ConsentResultsListener 11 | import com.smartlook.consentsdk.ui.consent.fragment.ConsentFormFragment 12 | import com.smartlook.consentsdksample.App 13 | import com.smartlook.consentsdksample.R 14 | import kotlinx.android.synthetic.main.activity_main.* 15 | 16 | class MainActivity : AppCompatActivity(), ConsentResultsListener { 17 | 18 | companion object { 19 | const val CONSENT_1_KEY = "consent_1_key" 20 | const val CONSENT_2_KEY = "consent_2_key" 21 | 22 | const val CONSENT_REQUEST_CODE = 10001 23 | 24 | const val CONSENT_FORM_FRAGMENT_TAG = "consent_form_fragment" 25 | } 26 | 27 | override fun onCreate(savedInstanceState: Bundle?) { 28 | super.onCreate(savedInstanceState) 29 | setContentView(R.layout.activity_main) 30 | 31 | prepareConsentFormData().let { 32 | displayConsentResults(loadConsentResults(it.consentFormItems)) 33 | handleShowDialog(it) 34 | handleShowFragmentDialog(it) 35 | handleStartConsentActivity(it) 36 | handleShowFragment(it) 37 | } 38 | } 39 | 40 | private fun loadConsentResults(consentFormItems: Array): HashMap { 41 | return hashMapOf().apply { 42 | consentFormItems.forEach { 43 | put(it.consentKey, App.consentSDK.loadConsentResult(it.consentKey)) 44 | } 45 | } 46 | } 47 | 48 | // Getting result from DialogFragment 49 | override fun onConsentResults(consentResults: HashMap) { 50 | displayConsentResults(consentResults as HashMap) 51 | } 52 | 53 | // Getting result from activity 54 | override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { 55 | if (requestCode == CONSENT_REQUEST_CODE) { 56 | if (resultCode == Activity.RESULT_OK) { 57 | displayConsentResults(App.consentSDK.parseOutConsentResults(data) as HashMap) 58 | } else { 59 | // consent form not filled successfully 60 | } 61 | } 62 | } 63 | 64 | private fun prepareConsentFormData(): ConsentFormData { 65 | return ConsentFormData( 66 | titleText = getString(R.string.consent_form_title), 67 | descriptionText = getString(R.string.consent_form_description), 68 | confirmButtonText = getString(R.string.consent_form_confirm_button_text), 69 | consentFormItems = prepareConsentFormItems() 70 | ) 71 | } 72 | 73 | private fun prepareConsentFormItems(): Array { 74 | return arrayOf( 75 | ConsentFormItem( 76 | consentKey = CONSENT_1_KEY, 77 | required = true, 78 | description = getString(R.string.consent_1_description), 79 | link = null 80 | ), 81 | ConsentFormItem( 82 | consentKey = CONSENT_2_KEY, 83 | required = false, 84 | description = getString(R.string.consent_2_description), 85 | link = getString(R.string.consent_2_link) 86 | ) 87 | ) 88 | } 89 | 90 | private fun handleStartConsentActivity(consentFormData: ConsentFormData) { 91 | start_activity.setOnClickListener { 92 | App.consentSDK.startConsentFormActivity(this, consentFormData, CONSENT_REQUEST_CODE, R.style.ActivityStyle) 93 | } 94 | } 95 | 96 | private fun handleShowFragmentDialog(consentFormData: ConsentFormData) { 97 | show_dialog_fragment.setOnClickListener { 98 | App.consentSDK.showConsentFormDialogFragment(this, consentFormData, R.style.DialogStyle) 99 | } 100 | } 101 | 102 | private fun handleShowDialog(consentFormData: ConsentFormData) { 103 | show_dialog.setOnClickListener { 104 | App.consentSDK.showConsentFormDialog(this, consentFormData, object : ConsentResultsListener { 105 | override fun onConsentResults(consentResults: HashMap) { 106 | displayConsentResults(consentResults as HashMap) 107 | } 108 | }) 109 | } 110 | } 111 | 112 | private fun handleShowFragment(consentFormData: ConsentFormData) { 113 | show_fragment.setOnClickListener { 114 | with(supportFragmentManager) { 115 | beginTransaction() 116 | .replace(R.id.fragment_placeholder, App.consentSDK.createConsentFormFragment(consentFormData, R.style.ActivityStyle), CONSENT_FORM_FRAGMENT_TAG) 117 | .commit() 118 | executePendingTransactions() 119 | } 120 | 121 | show_fragment.isEnabled = false 122 | 123 | with(supportFragmentManager.findFragmentByTag(CONSENT_FORM_FRAGMENT_TAG) as ConsentFormFragment) { 124 | registerConsentResultsListener(createConsentFormFragmentResultsListener()) 125 | } 126 | } 127 | 128 | } 129 | 130 | private fun createConsentFormFragmentResultsListener(): ConsentResultsListener { 131 | return object : ConsentResultsListener { 132 | override fun onConsentResults(consentResults: HashMap) { 133 | displayConsentResults(consentResults as HashMap) 134 | 135 | with(supportFragmentManager.beginTransaction()) { 136 | remove(supportFragmentManager.findFragmentByTag(CONSENT_FORM_FRAGMENT_TAG) as ConsentFormFragment) 137 | commit() 138 | } 139 | 140 | show_fragment.isEnabled = true 141 | } 142 | } 143 | } 144 | 145 | private fun displayConsentResults(consentResults: HashMap) { 146 | with(consent_results) { 147 | hasFixedSize() 148 | layoutManager = LinearLayoutManager(this@MainActivity) 149 | adapter = ConsentResultsAdapter(context!!, consentResults) 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 10 | 12 | 14 | 16 | 18 | 20 | 22 | 24 | 26 | 28 | 30 | 32 | 34 | 36 | 38 | 40 | 42 | 44 | 46 | 48 | 50 | 52 | 54 | 56 | 58 | 60 | 62 | 64 | 66 | 68 | 70 | 72 | 74 | 75 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 15 | 16 | 20 | 21 |