From ffb52e0bce4ae6d79fc5eb01f4cbdf66dadde14e Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 23 May 2018 10:01:00 -0700 Subject: [PATCH] Release v4.1.0 (#12) --- README.md | 74 +++++------ SampleApp/app/build.gradle | 11 +- .../android/sampleapp/ApplicationTest.java | 13 -- .../android/sampleapp/MainActivity.java | 60 +++++---- .../android/sampleapp/SampleDialog.java | 65 ++++++++++ .../src/main/res/drawable-hdpi/ic_new_tab.png | Bin 0 -> 232 bytes .../drawable-hdpi/rounded_corners_shape.xml | 14 +++ .../src/main/res/drawable-mdpi/ic_new_tab.png | Bin 0 -> 349 bytes .../main/res/drawable-xhdpi/ic_new_tab.png | Bin 0 -> 448 bytes .../app/src/main/res/layout/activity_main.xml | 2 +- .../app/src/main/res/layout/sample_dialog.xml | 117 ++++++++++++++++++ SampleApp/app/src/main/res/values/colors.xml | 5 + SampleApp/app/src/main/res/values/strings.xml | 9 ++ SampleApp/app/src/main/res/values/styles.xml | 6 + .../android/sampleapp/ExampleUnitTest.java | 15 --- SampleApp/build.gradle | 4 +- .../gradle/wrapper/gradle-wrapper.properties | 4 +- gallery/aff-android-optin-dialogue1.jpg | Bin 0 -> 434131 bytes gallery/aff-android-optin-dialogue2.jpg | Bin 0 -> 771308 bytes gallery/aff-android-optin-full1.jpg | Bin 0 -> 518895 bytes gallery/aff-android-optin-full2.jpg | Bin 0 -> 1051484 bytes gallery/aff-android-optin-full3.jpg | Bin 0 -> 568296 bytes .../4.1.0/android-persistent-sdk-4.1.0.aar | Bin 0 -> 161919 bytes .../android-persistent-sdk-4.1.0.aar.md5 | 1 + .../android-persistent-sdk-4.1.0.aar.sha1 | 1 + .../4.1.0/android-persistent-sdk-4.1.0.pom | 50 ++++++++ .../android-persistent-sdk-4.1.0.pom.md5 | 1 + .../android-persistent-sdk-4.1.0.pom.sha1 | 1 + .../android-persistent-sdk/maven-metadata.xml | 5 +- .../maven-metadata.xml.md5 | 2 +- .../maven-metadata.xml.sha1 | 2 +- 31 files changed, 361 insertions(+), 101 deletions(-) delete mode 100644 SampleApp/app/src/androidTest/java/com/placed/android/sampleapp/ApplicationTest.java create mode 100644 SampleApp/app/src/main/java/com/placed/android/sampleapp/SampleDialog.java create mode 100644 SampleApp/app/src/main/res/drawable-hdpi/ic_new_tab.png create mode 100644 SampleApp/app/src/main/res/drawable-hdpi/rounded_corners_shape.xml create mode 100644 SampleApp/app/src/main/res/drawable-mdpi/ic_new_tab.png create mode 100644 SampleApp/app/src/main/res/drawable-xhdpi/ic_new_tab.png create mode 100644 SampleApp/app/src/main/res/layout/sample_dialog.xml delete mode 100644 SampleApp/app/src/test/java/com/placed/android/sampleapp/ExampleUnitTest.java create mode 100755 gallery/aff-android-optin-dialogue1.jpg create mode 100755 gallery/aff-android-optin-dialogue2.jpg create mode 100755 gallery/aff-android-optin-full1.jpg create mode 100755 gallery/aff-android-optin-full2.jpg create mode 100755 gallery/aff-android-optin-full3.jpg create mode 100644 repository/com/placed/client/android-persistent-sdk/4.1.0/android-persistent-sdk-4.1.0.aar create mode 100644 repository/com/placed/client/android-persistent-sdk/4.1.0/android-persistent-sdk-4.1.0.aar.md5 create mode 100644 repository/com/placed/client/android-persistent-sdk/4.1.0/android-persistent-sdk-4.1.0.aar.sha1 create mode 100644 repository/com/placed/client/android-persistent-sdk/4.1.0/android-persistent-sdk-4.1.0.pom create mode 100644 repository/com/placed/client/android-persistent-sdk/4.1.0/android-persistent-sdk-4.1.0.pom.md5 create mode 100644 repository/com/placed/client/android-persistent-sdk/4.1.0/android-persistent-sdk-4.1.0.pom.sha1 diff --git a/README.md b/README.md index 04514f1..a7d88fd 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ## Introduction -The Placed SDK for Android is designed to help you add Placed location gathering to your app. It exposes simple public API calls that can be used to turn location gathering on. +The Placed SDK for Android is designed to help you add Placed location gathering to your app. It exposes simple public API calls that can be used to turn on and off location gathering. The SDK has been designed for easy setup and integration with both new and existing mobile applications. @@ -16,10 +16,10 @@ Before you can integrate the Placed SDK into your app, you need to add the relev 1. Add the following to your **root** `build.gradle` file: - ``` + ```gradle allprojects { repositories { - ... + // other repositories maven { url "https://raw.githubusercontent.com/placed/android-placed-sdk/master/repository" } } @@ -28,7 +28,7 @@ Before you can integrate the Placed SDK into your app, you need to add the relev 2. Add the following to your **app** `build.gradle` file: - ``` + ```gradle dependencies { // other dependencies go here... @@ -36,7 +36,7 @@ Before you can integrate the Placed SDK into your app, you need to add the relev // noinspection AndroidLintGradleDynamicVersion compile 'com.placed.client:android-persistent-sdk:4.0.+' - // NOTE: If you use any Google Play services APIs above version + // NOTE: If you use any Google Play services APIs above version // 10.0.1, add the following to avoid crashes caused by inconsistent // API versions. compile('com.google.android.gms:play-services-ads:') { @@ -51,7 +51,7 @@ Before you can integrate the Placed SDK into your app, you need to add the relev 3. You may encounter Lint error: 'InvalidPackage: Package not included in Android' related to Okio and Retrofit. (This is a known issue with Okio that you can read about [here](https://github.com/square/okio/issues/58).) If so, create a `lint.xml` with the following contents: - ``` + ```xml @@ -61,9 +61,10 @@ Before you can integrate the Placed SDK into your app, you need to add the relev ``` And reference it from your **app** `build.gradle` file: - ``` + ```gradle android { - ... + // ... + lintOptions { lintConfig file("lint.xml") } @@ -74,50 +75,53 @@ At this point your app should build, although the Placed SDK will not start yet. ### Second, integrate the Placed SDK -Once you've added the Placed SDK as an app dependency, adding the Placed SDK to your app involves several more steps: +Once you've added the Placed SDK as an app dependency, completing installation of the Placed SDK to your app involves a couple more steps: -1. Add the Placed provided app key to AndroidManifest.xml -2. Prompt for location permission -3. Register the user for location collection by the Placed SDK. +1. Prompt for location permission. +2. Register the user with Placed SDK. -#### AndroidManifest.xml changes +#### Location Permission -Add the application key provided to you by Placed in the application tag of your **AndroidManifest.xml**. +As outlined in Section 5 of the [Placed Affiliate Agreement](https://affiliate.placed.com/placed-affiliate-agreement/), you must satisfy two requirements prior to registering a user with the Placed SDK: +1. *Gather Express Consent for User Data Collection via Opt-in Dialog* +In addition having a legally compliant privacy policy describing Placed’s collection of location and device information, you must include a discrete opt-in dialog which gathers express consent for data collection. This dialog appears prior to the fine location permission prompt, and includes: + - The language: “*Aggregated device data, including location and apps, is measured for the purposes of market research by Placed, Inc.*” + - Links to the Placed [Terms of Service](https://www.placed.com/terms-of-service) and [Privacy Policy](https://www.placed.com/privacy-policy) + - Buttons to “Accept” or “Cancel” -``` - -``` +2. *Prompt for Fine Location Permission* +Eligible users must allow fine location permission, which subsequently triggers app registration as described in the [Register a User](#register-a-user) section. -#### Prompting for location permission +For an example of the opt-in dialog and location permission prompt, please refer to the [main activity of the sample app](./SampleApp/app/src/main/java/com/placed/android/sampleapp/MainActivity.java). We have also provided a [gallery for inspiration](./gallery) on how you can better integrate the opt-in experience into your app. -If you don't already prompt for fine location permission, you should do so before registering the user with the Placed SDK. +#### Register a user -For an example, see the [main activity in the sample app](https://github.com/placed/android-placed-sdk/blob/master/SampleApp/app/src/main/java/com/placed/android/sampleapp/MainActivity.java). +Once you've prompted for location permission and gathered express consent to collect user data, you need to register your user with the Placed SDK. There are __two options__ when registering a user with the Placed SDK. -Notice in this case the fine location permission is requested before registering the user and acceptance triggers app registion as described below. +##### Option 1: Add your Placed app key to your AndroidManifest.xml -### Registering a user - -Once you've prompted for fine location permission you need to register your user with the Placed SDK. +```xml + +``` -The following code snippet registers the user: +And then call: ```java PlacedAgent.registerUser(this); ``` -For an example, see the [main activity in the sample app](https://github.com/placed/android-placed-sdk/blob/master/SampleApp/app/src/main/java/com/placed/android/sampleapp/MainActivity.java). +For an example, see the [main activity in the sample app](./SampleApp/app/src/main/java/com/placed/android/sampleapp/MainActivity.java). -## How to join -Please contact your Placed representative to find out how to register your account. If you do not have a representative yet, please email [affiliate@placed.com](mailto:affiliate@placed.com). +##### Option 2: Add your Placed app key at runtime -## Support -For further guidance contact [affiliate@placed.com](mailto:affliate@placed.com). +All you have to do is call: -## Reference +```java +PlacedAgent.registerUser(this, "YOUR_APP_KEY"); +``` -`static void registerUser(final Context context)` +## How to join +Please contact your Placed representative to find out how to register your account. If you do not have a representative yet, please email [affiliate@placed.com](mailto:affiliate@placed.com). -This is the main method to register the user with the Placed SDK and begin -location collection by the Placed SDK. If you have EULA or terms of service that the user is required to accept before tracking, -call this method after the user accepts those terms. +## Support +For further guidance, please contact [affiliate@placed.com](mailto:affliate@placed.com). diff --git a/SampleApp/app/build.gradle b/SampleApp/app/build.gradle index a7f6c86..deda770 100644 --- a/SampleApp/app/build.gradle +++ b/SampleApp/app/build.gradle @@ -2,11 +2,10 @@ apply plugin: 'com.android.application' android { compileSdkVersion 25 - buildToolsVersion "25.0.3" defaultConfig { applicationId "com.placed.android.sampleapp" - minSdkVersion 9 + minSdkVersion 14 targetSdkVersion 25 versionCode 1 versionName "1.0" @@ -23,11 +22,11 @@ android { } dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - testCompile 'junit:junit:4.12' - compile 'com.android.support:appcompat-v7:25.3.1' + implementation fileTree(dir: 'libs', include: ['*.jar']) + testImplementation 'junit:junit:4.12' + implementation 'com.android.support:appcompat-v7:25.4.0' // Use the latest patch version of the Placed SDK // noinspection AndroidLintGradleDynamicVersion - compile 'com.placed.client:android-persistent-sdk:4.0.+' + implementation 'com.placed.client:android-persistent-sdk:4.1.+' } diff --git a/SampleApp/app/src/androidTest/java/com/placed/android/sampleapp/ApplicationTest.java b/SampleApp/app/src/androidTest/java/com/placed/android/sampleapp/ApplicationTest.java deleted file mode 100644 index d15755a..0000000 --- a/SampleApp/app/src/androidTest/java/com/placed/android/sampleapp/ApplicationTest.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.placed.android.sampleapp; - -import android.app.Application; -import android.test.ApplicationTestCase; - -/** - * Testing Fundamentals - */ -public class ApplicationTest extends ApplicationTestCase { - public ApplicationTest() { - super(Application.class); - } -} \ No newline at end of file diff --git a/SampleApp/app/src/main/java/com/placed/android/sampleapp/MainActivity.java b/SampleApp/app/src/main/java/com/placed/android/sampleapp/MainActivity.java index abf6d27..f50eaf1 100644 --- a/SampleApp/app/src/main/java/com/placed/android/sampleapp/MainActivity.java +++ b/SampleApp/app/src/main/java/com/placed/android/sampleapp/MainActivity.java @@ -1,26 +1,24 @@ package com.placed.android.sampleapp; import android.Manifest; -import android.content.DialogInterface; +import android.content.Intent; import android.content.pm.PackageManager; +import android.net.Uri; import android.os.Bundle; -import android.preference.PreferenceManager; import android.support.annotation.NonNull; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; -import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.util.Log; +import android.widget.Toast; import com.placed.client.android.persistent.PlacedAgent; -public class MainActivity extends AppCompatActivity { +public class MainActivity extends AppCompatActivity implements SampleDialog.SampleDialogListener { private static final String TAG = MainActivity.class.getSimpleName(); private static final int REQUEST_CODE_PERMISSION = 1; - private static final String PREF_KEY_PLACED_DIALOG_SHOWN = "placed_dialog_shown"; - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -28,7 +26,7 @@ protected void onCreate(Bundle savedInstanceState) { if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { Log.d(TAG, "Requesting for ACCESS_FINE_LOCATION permission."); - ActivityCompat.requestPermissions(this, new String[] { Manifest.permission.ACCESS_FINE_LOCATION }, REQUEST_CODE_PERMISSION); + ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_CODE_PERMISSION); } else { Log.d(TAG, "Already have ACCESS_FINE_LOCATION permission. Proceeding to register app with Placed."); registerUser(); @@ -55,24 +53,38 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permis } private void registerUser() { - if (!PreferenceManager.getDefaultSharedPreferences(this).getBoolean(PREF_KEY_PLACED_DIALOG_SHOWN, false)) { - PreferenceManager.getDefaultSharedPreferences(this) - .edit() - .putBoolean(PREF_KEY_PLACED_DIALOG_SHOWN, true) - .apply(); + if (!PlacedAgent.isUserRegistered(this)) { + SampleDialog sampleDialog = new SampleDialog(this); + sampleDialog.show(); + sampleDialog.onActionListener = this; + } else { + Toast.makeText(this, "User already registered!", Toast.LENGTH_SHORT).show(); + } + } + + @Override + public void onActionReceived(SampleDialogAction action) { + switch (action) { + case TERMS: + Intent termsIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.placed.com/terms-of-service")); + startActivity(termsIntent); + break; + + case PRIVACY: + Intent privacyIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.placed.com/privacy-policy")); + startActivity(privacyIntent); + break; + + case ACCEPT: + PlacedAgent.registerUser(this); + break; + + case CANCEL: + PlacedAgent.deregisterUser(this); + break; - new AlertDialog.Builder(this) - .setTitle("Asking your users for consent") - .setMessage("Implement your own user experience to ask your users to collect Placed data.") - .setPositiveButton("OK", new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - PlacedAgent.registerUser(MainActivity.this); - } - }) - .setNegativeButton("Cancel", null) - .create() - .show(); + default: + break; } } } diff --git a/SampleApp/app/src/main/java/com/placed/android/sampleapp/SampleDialog.java b/SampleApp/app/src/main/java/com/placed/android/sampleapp/SampleDialog.java new file mode 100644 index 0000000..e1f44b2 --- /dev/null +++ b/SampleApp/app/src/main/java/com/placed/android/sampleapp/SampleDialog.java @@ -0,0 +1,65 @@ +package com.placed.android.sampleapp; + +import android.app.Dialog; +import android.content.Context; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.view.View; +import android.view.Window; + +enum SampleDialogAction { + TERMS, PRIVACY, ACCEPT, CANCEL +} + +public class SampleDialog extends Dialog { + public interface SampleDialogListener { + void onActionReceived(SampleDialogAction action); + } + + public SampleDialogListener onActionListener; + + SampleDialog(@NonNull Context context) { + super(context); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); + setContentView(R.layout.sample_dialog); + + setupButtons(); + } + + private void setupButtons() { + findViewById(R.id.btn_terms).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + onActionListener.onActionReceived(SampleDialogAction.TERMS); + } + }); + + findViewById(R.id.btn_privacy).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + onActionListener.onActionReceived(SampleDialogAction.PRIVACY); + } + }); + + findViewById(R.id.btn_accept).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + onActionListener.onActionReceived(SampleDialogAction.ACCEPT); + dismiss(); + } + }); + + findViewById(R.id.btn_cancel).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + onActionListener.onActionReceived(SampleDialogAction.CANCEL); + dismiss(); + } + }); + } +} diff --git a/SampleApp/app/src/main/res/drawable-hdpi/ic_new_tab.png b/SampleApp/app/src/main/res/drawable-hdpi/ic_new_tab.png new file mode 100644 index 0000000000000000000000000000000000000000..a64260f631bdd830db9999f152c42967a201de99 GIT binary patch literal 232 zcmeAS@N?(olHy`uVBq!ia0vp^oFL4>1|%O$WD@{Vk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx;$MRLn>}1{dugqe`;gn!EQ#TU5yn+0{5Ad#8yZ!3x_ze z3$QbNR8gp7IR)**)T$mxb3 z{B24K6PO<*EjX#5{)Qz + + + + + + + + + diff --git a/SampleApp/app/src/main/res/drawable-mdpi/ic_new_tab.png b/SampleApp/app/src/main/res/drawable-mdpi/ic_new_tab.png new file mode 100644 index 0000000000000000000000000000000000000000..3f5e83e172ebac832156f685843ca2285bebe722 GIT binary patch literal 349 zcmV-j0iyniP)Kr98sCxKXo0S90L;$>J20Rc^@0U)4_&kzt`0b)HU`vMRjM3TD%#J8aAy+B+H z#Bxx+1`xXeF(a};FaR>70EdPDfVdCJjt1gfs3HGhhA}aa20(@^g6abqz6&{w$TcJm zh;NW;5H>@WqQ@E`;EBhOM^O3+3lRp{;&I6YAifO5SBWvG7fK%n;_c|M$-MQ!ZuIEK z1>OR26)wrY-U?hm>N}~?j}8KW_!B4%QfiO`5Ho|qky3*g$Vre4gfcA**h8a}6kv~R v&?RUIU;xGBfJ-2+ML`WC7l8Ce0R$KT5wSD;f}VMI00000NkvXXu0mjfvWkHf literal 0 HcmV?d00001 diff --git a/SampleApp/app/src/main/res/drawable-xhdpi/ic_new_tab.png b/SampleApp/app/src/main/res/drawable-xhdpi/ic_new_tab.png new file mode 100644 index 0000000000000000000000000000000000000000..51ae0df7c95917a20853eaa01f3b1437e248bd54 GIT binary patch literal 448 zcmV;x0YCnUP)u+y=x34A{XV zAPxiKlSpDbK)eo!U4WR8fe63^wP*_-i$FjQh?~*GqJh|zSc_nmwLxh@;3EM(LBN9M zqrX7>1WnC5Fq|eP0Mgv1dPi~)QmHdETY6RVlDcK zoLI=QObLir5$U6CNbEf28{mIHDGUO;N}fF$H;4t3koi?0 q%eDit57dV=@e#;;U>Uj{Aiw}_G+-e2 + android:text="@string/hello_from_placed" /> diff --git a/SampleApp/app/src/main/res/layout/sample_dialog.xml b/SampleApp/app/src/main/res/layout/sample_dialog.xml new file mode 100644 index 0000000..b50987d --- /dev/null +++ b/SampleApp/app/src/main/res/layout/sample_dialog.xml @@ -0,0 +1,117 @@ + + + + + + + +