+
+
+
diff --git a/assets/html/passive_data_kit/generator_location_disclosure.html b/assets/html/passive_data_kit/generator_location_disclosure.html
new file mode 100755
index 0000000..583b4d7
--- /dev/null
+++ b/assets/html/passive_data_kit/generator_location_disclosure.html
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+ TODO: Write disclosure for use of location data...
+
+
+
+ Place completed file in assets/html/passive_data_kit/generator_location_disclosure.html
in your project to override this placeholder.
+
+
+
\ No newline at end of file
diff --git a/res/drawable-hdpi/ic_button_disclosure_setting.png b/res/drawable-hdpi/ic_button_disclosure_setting.png
new file mode 100755
index 0000000..9628f97
Binary files /dev/null and b/res/drawable-hdpi/ic_button_disclosure_setting.png differ
diff --git a/res/drawable-mdpi/ic_button_disclosure_setting.png b/res/drawable-mdpi/ic_button_disclosure_setting.png
new file mode 100755
index 0000000..a252c98
Binary files /dev/null and b/res/drawable-mdpi/ic_button_disclosure_setting.png differ
diff --git a/res/drawable-xhdpi/ic_button_disclosure_setting.png b/res/drawable-xhdpi/ic_button_disclosure_setting.png
new file mode 100755
index 0000000..c006523
Binary files /dev/null and b/res/drawable-xhdpi/ic_button_disclosure_setting.png differ
diff --git a/res/drawable-xxhdpi/ic_button_disclosure_setting.png b/res/drawable-xxhdpi/ic_button_disclosure_setting.png
new file mode 100755
index 0000000..8310d1e
Binary files /dev/null and b/res/drawable-xxhdpi/ic_button_disclosure_setting.png differ
diff --git a/res/drawable-xxxhdpi/ic_button_disclosure_setting.png b/res/drawable-xxxhdpi/ic_button_disclosure_setting.png
new file mode 100755
index 0000000..b25919e
Binary files /dev/null and b/res/drawable-xxxhdpi/ic_button_disclosure_setting.png differ
diff --git a/res/layout/dialog_location_randomized.xml b/res/layout/dialog_location_randomized.xml
new file mode 100755
index 0000000..58965d7
--- /dev/null
+++ b/res/layout/dialog_location_randomized.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
diff --git a/res/layout/dialog_location_user.xml b/res/layout/dialog_location_user.xml
new file mode 100755
index 0000000..17bbf31
--- /dev/null
+++ b/res/layout/dialog_location_user.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
diff --git a/res/layout/layout_data_disclosure_detail_pdk.xml b/res/layout/layout_data_disclosure_detail_pdk.xml
new file mode 100755
index 0000000..9cd2e4e
--- /dev/null
+++ b/res/layout/layout_data_disclosure_detail_pdk.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
diff --git a/res/layout/layout_data_disclosure_pdk.xml b/res/layout/layout_data_disclosure_pdk.xml
new file mode 100755
index 0000000..cc763d0
--- /dev/null
+++ b/res/layout/layout_data_disclosure_pdk.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/row_disclosure_action_pdk.xml b/res/layout/row_disclosure_action_pdk.xml
new file mode 100755
index 0000000..a5250e3
--- /dev/null
+++ b/res/layout/row_disclosure_action_pdk.xml
@@ -0,0 +1,19 @@
+
+
+
+
diff --git a/res/layout/row_disclosure_location_accuracy_pdk.xml b/res/layout/row_disclosure_location_accuracy_pdk.xml
new file mode 100755
index 0000000..78cd4cc
--- /dev/null
+++ b/res/layout/row_disclosure_location_accuracy_pdk.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
diff --git a/res/layout/row_generator_disclosure_generic.xml b/res/layout/row_generator_disclosure_generic.xml
new file mode 100755
index 0000000..200f44e
--- /dev/null
+++ b/res/layout/row_generator_disclosure_generic.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
diff --git a/res/values/generators.xml b/res/values/generators.xml
index 2fbcf3b..cd52c21 100755
--- a/res/values/generators.xml
+++ b/res/values/generators.xml
@@ -43,6 +43,33 @@
Device Location
Coordinates: %1$.4f, %2$.4f
+ Location Accuracy
+ Tap here to update the accuracy of your data.
+
+ Best Accuracy
+ Best Available Data From Location Hardware
+
+ Locally Randomized
+ Location Combined With Random Noise
+
+ User Provided
+ Static Location Provided By User
+
+ Disabled
+ App Does Not Use Location Data
+
+ Best Accuracy
+ This app will use the location hardware on this device to obtain the most accurate location readings.
+
+ Location Disabled
+ This app will not use your location, but use an placeholder instead.
+
+ Locally Randomized
+ Please enter the random distance (miles/kilometers)to use to obfuscate your exact location:
+
+ User Provided
+ Please enter a postal code or city and province to use as your location:
+ Unable to find your location. Please enter your location another way and try again.
Screen State
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a784812..73b220f 100755
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -4,6 +4,7 @@
0.0.1
Unknown Version
Data Stream
+ Continue
- %d generator
@@ -20,5 +21,11 @@
Diagnostics
The app is set up correctly.\n\nNo further actions are needed.
+
+ Passive Data Disclosure
+ Select a disclosure item for more information…
+
+ Data Collection Description
+ Tap here to learn how this app uses your data.
diff --git a/src/com/audacious_software/passive_data_kit/activities/DataDisclosureActivity.java b/src/com/audacious_software/passive_data_kit/activities/DataDisclosureActivity.java
new file mode 100755
index 0000000..70d41f3
--- /dev/null
+++ b/src/com/audacious_software/passive_data_kit/activities/DataDisclosureActivity.java
@@ -0,0 +1,44 @@
+package com.audacious_software.passive_data_kit.activities;
+
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.MenuItem;
+import android.widget.FrameLayout;
+
+import com.audacious_software.passive_data_kit.activities.generators.GeneratorsAdapter;
+import com.audacious_software.pdk.passivedatakit.R;
+
+public class DataDisclosureActivity extends AppCompatActivity {
+ private GeneratorsAdapter mAdapter = null;
+
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ this.setContentView(R.layout.layout_data_disclosure_pdk);
+ this.getSupportActionBar().setTitle(R.string.title_data_disclosure);
+ this.getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+ FrameLayout dataView = (FrameLayout) this.findViewById(R.id.data_view);
+
+ this.mAdapter = new GeneratorsAdapter();
+ this.mAdapter.setContext(this.getApplicationContext());
+ this.mAdapter.setDataView(dataView);
+
+ RecyclerView listView = (RecyclerView) this.findViewById(R.id.list_view);
+
+ listView.setLayoutManager(new LinearLayoutManager(this));
+
+ listView.setAdapter(this.mAdapter);
+ }
+
+ public boolean onOptionsItemSelected(MenuItem item)
+ {
+ if (item.getItemId() == android.R.id.home)
+ {
+ this.finish();
+ }
+
+ return true;
+ }
+}
diff --git a/src/com/audacious_software/passive_data_kit/activities/DataDisclosureDetailActivity.java b/src/com/audacious_software/passive_data_kit/activities/DataDisclosureDetailActivity.java
new file mode 100755
index 0000000..aaf79bb
--- /dev/null
+++ b/src/com/audacious_software/passive_data_kit/activities/DataDisclosureDetailActivity.java
@@ -0,0 +1,123 @@
+package com.audacious_software.passive_data_kit.activities;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.FrameLayout;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import com.audacious_software.passive_data_kit.Logger;
+import com.audacious_software.passive_data_kit.generators.Generator;
+import com.audacious_software.pdk.passivedatakit.R;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.List;
+
+public class DataDisclosureDetailActivity extends AppCompatActivity {
+ public static class Action {
+ public String title;
+ public String subtitle;
+
+ public View view;
+ }
+
+ public static final String GENERATOR_CLASS_NAME = "com.audacious_software.passive_data_kit.activities.DataDisclosureDetailActivity.GENERATOR_CLASS_NAME";
+
+ private Class extends Generator> mGeneratorClass = null;
+
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final DataDisclosureDetailActivity me = this;
+
+ this.setContentView(R.layout.layout_data_disclosure_detail_pdk);
+ this.getSupportActionBar().setSubtitle(R.string.title_data_disclosure);
+ this.getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+ try {
+ this.mGeneratorClass = (Class extends Generator>) Class.forName(this.getIntent().getStringExtra(DataDisclosureDetailActivity.GENERATOR_CLASS_NAME));
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ }
+
+ if (this.mGeneratorClass != null) {
+ try {
+ Method getGeneratorTitle = this.mGeneratorClass.getDeclaredMethod("getGeneratorTitle", Context.class);
+
+ String title = (String) getGeneratorTitle.invoke(null, this);
+ this.getSupportActionBar().setTitle(title);
+
+ Method getDisclosureActions = this.mGeneratorClass.getDeclaredMethod("getDisclosureActions", Context.class);
+
+ final List actions = (List) getDisclosureActions.invoke(null, this);
+
+ ListView actionsList = (ListView) this.findViewById(R.id.disclosure_actions);
+ ArrayAdapter adapter = new ArrayAdapter(this, R.layout.row_disclosure_action_pdk, actions) {
+ public View getView (int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ convertView = LayoutInflater.from(me).inflate(R.layout.row_disclosure_action_pdk, null);
+ }
+
+ Action action = actions.get(position);
+
+ TextView title = (TextView) convertView.findViewById(R.id.action_title);
+ title.setText(action.title);
+
+ TextView description = (TextView) convertView.findViewById(R.id.action_description);
+ description.setText(action.subtitle);
+
+ return convertView;
+ }
+ };
+
+ actionsList.setAdapter(adapter);
+
+ actionsList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView> adapterView, View view, int position, long l) {
+ Log.e("PDK", "TAPPED: " + position);
+
+ Action action = actions.get(position);
+
+ FrameLayout dataView = (FrameLayout) me.findViewById(R.id.data_view);
+ dataView.removeAllViews();
+
+ if (action.view != null) {
+ FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+
+ action.view.setLayoutParams(params);
+
+ dataView.addView(action.view);
+ }
+ }
+ });
+
+ actionsList.performItemClick(null, 0, 0);
+ } catch (NoSuchMethodException e1) {
+ Logger.getInstance(this).logThrowable(e1);
+ } catch (InvocationTargetException e1) {
+ Logger.getInstance(this).logThrowable(e1);
+ } catch (IllegalAccessException e1) {
+ Logger.getInstance(this).logThrowable(e1);
+ }
+ }
+ }
+
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ this.finish();
+ }
+
+ return true;
+ }
+
+}
diff --git a/src/com/audacious_software/passive_data_kit/activities/DataStreamActivity.java b/src/com/audacious_software/passive_data_kit/activities/DataStreamActivity.java
index 04e416c..8815505 100755
--- a/src/com/audacious_software/passive_data_kit/activities/DataStreamActivity.java
+++ b/src/com/audacious_software/passive_data_kit/activities/DataStreamActivity.java
@@ -6,15 +6,8 @@
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
-import com.audacious_software.passive_data_kit.PassiveDataKit;
import com.audacious_software.passive_data_kit.activities.generators.DataPointsAdapter;
-import com.audacious_software.passive_data_kit.diagnostics.DiagnosticAction;
import com.audacious_software.passive_data_kit.generators.Generators;
import com.audacious_software.pdk.passivedatakit.R;
diff --git a/src/com/audacious_software/passive_data_kit/activities/generators/GeneratorViewHolder.java b/src/com/audacious_software/passive_data_kit/activities/generators/GeneratorViewHolder.java
new file mode 100755
index 0000000..5726eb2
--- /dev/null
+++ b/src/com/audacious_software/passive_data_kit/activities/generators/GeneratorViewHolder.java
@@ -0,0 +1,10 @@
+package com.audacious_software.passive_data_kit.activities.generators;
+
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+
+public class GeneratorViewHolder extends RecyclerView.ViewHolder {
+ public GeneratorViewHolder(View itemView) {
+ super(itemView);
+ }
+}
diff --git a/src/com/audacious_software/passive_data_kit/activities/generators/GeneratorsAdapter.java b/src/com/audacious_software/passive_data_kit/activities/generators/GeneratorsAdapter.java
new file mode 100755
index 0000000..adf3618
--- /dev/null
+++ b/src/com/audacious_software/passive_data_kit/activities/generators/GeneratorsAdapter.java
@@ -0,0 +1,129 @@
+package com.audacious_software.passive_data_kit.activities.generators;
+
+import android.content.Context;
+import android.content.Intent;
+import android.support.v7.widget.RecyclerView;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.Toast;
+
+import com.audacious_software.passive_data_kit.Logger;
+import com.audacious_software.passive_data_kit.activities.DataDisclosureDetailActivity;
+import com.audacious_software.passive_data_kit.generators.Generator;
+import com.audacious_software.passive_data_kit.generators.Generators;
+import com.audacious_software.pdk.passivedatakit.R;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+public class GeneratorsAdapter extends RecyclerView.Adapter {
+ private Context mContext = null;
+ private FrameLayout mDataView = null;
+
+ @Override
+ public GeneratorViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View view = Generator.fetchDisclosureView(parent);
+
+ return new GeneratorViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(final GeneratorViewHolder holder, int position) {
+ final GeneratorsAdapter me = this;
+
+ List> activeGenerators = Generators.getInstance(holder.itemView.getContext()).activeGenerators();
+
+ this.sortGenerators(this.mContext, activeGenerators);
+
+ Class extends Generator> generatorClass = activeGenerators.get(position);
+
+ Log.e("PDK", "GENERATOR CLASS: " + generatorClass);
+
+ try {
+ Method bindViewHolder = generatorClass.getDeclaredMethod("bindDisclosureViewHolder", GeneratorViewHolder.class);
+ bindViewHolder.invoke(null, holder);
+ } catch (Exception e) {
+// e.printStackTrace();
+ try {
+ generatorClass = Generator.class;
+
+ Method bindViewHolder = generatorClass.getDeclaredMethod("bindDisclosureViewHolder", GeneratorViewHolder.class);
+
+ bindViewHolder.invoke(null, holder);
+ } catch (NoSuchMethodException e1) {
+ Logger.getInstance(holder.itemView.getContext()).logThrowable(e1);
+ } catch (InvocationTargetException e1) {
+ Logger.getInstance(holder.itemView.getContext()).logThrowable(e1);
+ } catch (IllegalAccessException e1) {
+ Logger.getInstance(holder.itemView.getContext()).logThrowable(e1);
+ }
+ }
+
+ final Class extends Generator> finalClass = generatorClass;
+
+ holder.itemView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ me.mDataView.removeAllViews();
+
+ try {
+ Method bindViewHolder = finalClass.getDeclaredMethod("getDisclosureDataView", GeneratorViewHolder.class);
+
+ View dataView = (View) bindViewHolder.invoke(null, holder);
+ me.mDataView.addView(dataView);
+ } catch (NoSuchMethodException e1) {
+ Logger.getInstance(holder.itemView.getContext()).logThrowable(e1);
+ } catch (InvocationTargetException e1) {
+ Logger.getInstance(holder.itemView.getContext()).logThrowable(e1);
+ } catch (IllegalAccessException e1) {
+ Logger.getInstance(holder.itemView.getContext()).logThrowable(e1);
+ }
+ }
+ });
+
+ ImageView settingsButton = (ImageView) holder.itemView.findViewById(R.id.button_disclosure_item);
+
+ settingsButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Intent intent = new Intent(holder.itemView.getContext(), DataDisclosureDetailActivity.class);
+ intent.putExtra(DataDisclosureDetailActivity.GENERATOR_CLASS_NAME, finalClass.getCanonicalName());
+
+ holder.itemView.getContext().startActivity(intent);
+ }
+ });
+
+ }
+
+ @Override
+ public int getItemCount() {
+ return Generators.getInstance(null).activeGenerators().size();
+ }
+
+ private void sortGenerators(final Context context, List> generators) {
+ Collections.sort(generators, new Comparator>() {
+ @Override
+ public int compare(Class extends Generator> one, Class extends Generator> two) {
+ return one.getName().compareTo(two.getName());
+ }
+ });
+ }
+
+ public int getItemViewType (int position) {
+ return 0;
+ }
+
+ public void setContext(Context context) {
+ this.mContext = context;
+ }
+
+ public void setDataView(FrameLayout dataView) {
+ this.mDataView = dataView;
+ }
+}
diff --git a/src/com/audacious_software/passive_data_kit/generators/Generator.java b/src/com/audacious_software/passive_data_kit/generators/Generator.java
index abb0938..4adc72e 100755
--- a/src/com/audacious_software/passive_data_kit/generators/Generator.java
+++ b/src/com/audacious_software/passive_data_kit/generators/Generator.java
@@ -11,6 +11,7 @@
import android.widget.TextView;
import com.audacious_software.passive_data_kit.activities.generators.DataPointViewHolder;
+import com.audacious_software.passive_data_kit.activities.generators.GeneratorViewHolder;
import com.audacious_software.pdk.passivedatakit.R;
import java.text.DateFormat;
@@ -82,6 +83,20 @@ public static void bindViewHolder(DataPointViewHolder holder) {
generatorLabel.setText(identifier);
}
+ public static View fetchDisclosureView(ViewGroup parent) {
+ return LayoutInflater.from(parent.getContext()).inflate(R.layout.row_generator_disclosure_generic, parent, false);
+ }
+
+ public static void bindDisclosureViewHolder(GeneratorViewHolder holder) {
+ Class currentClass = new Object() { }.getClass().getEnclosingClass();
+
+ String identifier = currentClass.getCanonicalName();
+
+ TextView generatorLabel = (TextView) holder.itemView.findViewById(R.id.label_generator);
+
+ generatorLabel.setText(identifier);
+ }
+
public static String formatTimestamp(Context context, double timestamp) {
timestamp *= 1000;
diff --git a/src/com/audacious_software/passive_data_kit/generators/Generators.java b/src/com/audacious_software/passive_data_kit/generators/Generators.java
index 42b4cd9..ddac98b 100755
--- a/src/com/audacious_software/passive_data_kit/generators/Generators.java
+++ b/src/com/audacious_software/passive_data_kit/generators/Generators.java
@@ -24,6 +24,7 @@
public class Generators {
private Context mContext = null;
private boolean mStarted = false;
+
private ArrayList mGenerators = new ArrayList<>();
private HashSet mActiveGenerators = new HashSet<>();
private SharedPreferences mSharedPreferences = null;
diff --git a/src/com/audacious_software/passive_data_kit/generators/device/Location.java b/src/com/audacious_software/passive_data_kit/generators/device/Location.java
index f00204c..64c74b0 100755
--- a/src/com/audacious_software/passive_data_kit/generators/device/Location.java
+++ b/src/com/audacious_software/passive_data_kit/generators/device/Location.java
@@ -4,6 +4,7 @@
import android.app.Activity;
import android.content.ContentValues;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
@@ -12,6 +13,8 @@
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
+import android.location.Address;
+import android.location.Geocoder;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
@@ -20,18 +23,28 @@
import android.support.v4.content.ContextCompat;
import android.support.v4.content.res.ResourcesCompat;
import android.support.v4.graphics.drawable.DrawableCompat;
+import android.support.v7.app.AlertDialog;
import android.support.v7.widget.SwitchCompat;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.webkit.WebView;
+import android.widget.ArrayAdapter;
+import android.widget.CheckBox;
import android.widget.CompoundButton;
+import android.widget.EditText;
+import android.widget.FrameLayout;
+import android.widget.ListView;
import android.widget.TextView;
+import android.widget.Toast;
import com.audacious_software.passive_data_kit.DeviceInformation;
import com.audacious_software.passive_data_kit.PassiveDataKit;
+import com.audacious_software.passive_data_kit.activities.DataDisclosureDetailActivity;
import com.audacious_software.passive_data_kit.activities.generators.DataPointViewHolder;
+import com.audacious_software.passive_data_kit.activities.generators.GeneratorViewHolder;
import com.audacious_software.passive_data_kit.activities.generators.RequestPermissionActivity;
import com.audacious_software.passive_data_kit.diagnostics.DiagnosticAction;
import com.audacious_software.passive_data_kit.generators.Generator;
@@ -53,6 +66,7 @@
import com.google.maps.android.ui.IconGenerator;
import java.io.File;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@@ -79,6 +93,22 @@ public class Location extends Generator implements GoogleApiClient.ConnectionCal
private static final String SETTING_DISPLAY_HYBRID_MAP = "com.audacious_software.passive_data_kit.generators.device.Location.SETTING_DISPLAY_HYBRID_MAP";
private static final boolean SETTING_DISPLAY_HYBRID_MAP_DEFAULT = true;
+ private static String ACCURACY_MODE = "com.audacious_software.passive_data_kit.generators.device.Location.ACCURACY_MODE";
+
+ private static int ACCURACY_BEST = 0;
+ private static int ACCURACY_RANDOMIZED = 1;
+ private static int ACCURACY_USER = 2;
+ private static int ACCURACY_DISABLED = 3;
+
+ private static final String ACCURACY_MODE_RANDOMIZED_RANGE = "com.audacious_software.passive_data_kit.generators.device.Location.ACCURACY_MODE_RANDOMIZED_RANGE";
+ private static final long ACCURACY_MODE_RANDOMIZED_RANGE_DEFAULT = 100;
+
+ private static final String ACCURACY_MODE_USER_LOCATION = "com.audacious_software.passive_data_kit.generators.device.Location.ACCURACY_MODE_USER_LOCATION";
+ private static final String ACCURACY_MODE_USER_LOCATION_DEFAULT = "Chicago, Illinois";
+
+ private static final String ACCURACY_MODE_USER_LOCATION_LATITUDE = "com.audacious_software.passive_data_kit.generators.device.Location.ACCURACY_MODE_USER_LOCATION_LATITUDE";
+ private static final String ACCURACY_MODE_USER_LOCATION_LONGITUDE = "com.audacious_software.passive_data_kit.generators.device.Location.ACCURACY_MODE_USER_LOCATION_LONGITUDE";
+
private static Location sInstance = null;
private GoogleApiClient mGoogleApiClient = null;
private android.location.Location mLastLocation = null;
@@ -524,7 +554,347 @@ else if (Location.useGoogleLocationServices(parent.getContext()))
}
}
+ public static String getGeneratorTitle(Context context) {
+ return context.getString(R.string.generator_location);
+ }
+
+ public static List getDisclosureActions(final Context context) {
+ List actions = new ArrayList<>();
+
+ DataDisclosureDetailActivity.Action disclosure = new DataDisclosureDetailActivity.Action();
+
+ disclosure.title = context.getString(R.string.label_data_collection_description);
+ disclosure.subtitle = context.getString(R.string.label_data_collection_description_more);
+
+ WebView disclosureView = new WebView(context);
+ disclosureView.loadUrl("file:///android_asset/html/passive_data_kit/generator_location_disclosure.html");
+
+ disclosure.view = disclosureView;
+
+ actions.add(disclosure);
+
+ DataDisclosureDetailActivity.Action accuracy = new DataDisclosureDetailActivity.Action();
+ accuracy.title = context.getString(R.string.label_data_collection_location_accuracy);
+ accuracy.subtitle = context.getString(R.string.label_data_collection_location_accuracy_more);
+
+ final Integer[] options = { Location.ACCURACY_BEST, Location.ACCURACY_RANDOMIZED, Location.ACCURACY_USER, Location.ACCURACY_DISABLED };
+
+ final ListView listView = new ListView(context);
+
+ final ArrayAdapter accuracyAdapter = new ArrayAdapter(context, R.layout.row_disclosure_location_accuracy_pdk, options) {
+ public View getView (final int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ convertView = LayoutInflater.from(context).inflate(R.layout.row_disclosure_location_accuracy_pdk, null);
+ }
+
+ final Integer option = options[position];
+
+ final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ int selected = prefs.getInt(Location.ACCURACY_MODE, Location.ACCURACY_BEST);
+
+ CheckBox checked = (CheckBox) convertView.findViewById(R.id.action_checked);
+
+ checked.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
+
+ }
+ });
+
+ checked.setChecked(selected == option);
+
+ TextView title = (TextView) convertView.findViewById(R.id.action_title);
+ TextView description = (TextView) convertView.findViewById(R.id.action_description);
+
+ if (option == Location.ACCURACY_BEST) {
+ title.setText(R.string.label_data_collection_location_accuracy_best);
+ description.setText(R.string.label_data_collection_location_accuracy_best_more);
+ } else if (option == Location.ACCURACY_RANDOMIZED) {
+ title.setText(R.string.label_data_collection_location_accuracy_randomized);
+ description.setText(R.string.label_data_collection_location_accuracy_randomized_more);
+ } else if (option == Location.ACCURACY_USER) {
+ title.setText(R.string.label_data_collection_location_accuracy_user);
+ description.setText(R.string.label_data_collection_location_accuracy_user_more);
+ } else if (option == Location.ACCURACY_DISABLED) {
+ title.setText(R.string.label_data_collection_location_accuracy_disabled);
+ description.setText(R.string.label_data_collection_location_accuracy_disabled_more);
+ }
+
+ final ArrayAdapter meAdapter = this;
+
+ checked.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(final CompoundButton compoundButton, final boolean checked) {
+ Log.e("PDK", "CHECK CHANGE!");
+
+ final CompoundButton.OnCheckedChangeListener me = this;
+
+ if (option == Location.ACCURACY_BEST) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.title_location_accuracy_best);
+ builder.setMessage(R.string.message_location_accuracy_best);
+
+ builder.setPositiveButton(R.string.action_continue, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialogInterface, int i) {
+ SharedPreferences.Editor e = prefs.edit();
+ e.putInt(Location.ACCURACY_MODE, option);
+ e.apply();
+
+ meAdapter.notifyDataSetChanged();
+ }
+ });
+
+ builder.create().show();
+ } else if (option == Location.ACCURACY_RANDOMIZED) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.title_location_accuracy_randomized);
+
+ View body = LayoutInflater.from(context).inflate(R.layout.dialog_location_randomized, null);
+ builder.setView(body);
+
+ final EditText rangeField = (EditText) body.findViewById(R.id.random_range);
+
+ long existingRange = prefs.getLong(Location.ACCURACY_MODE_RANDOMIZED_RANGE, Location.ACCURACY_MODE_RANDOMIZED_RANGE_DEFAULT);
+
+ rangeField.setText("" + existingRange);
+
+ builder.setPositiveButton(R.string.action_continue, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialogInterface, int i) {
+ SharedPreferences.Editor e = prefs.edit();
+
+ long randomRange = Long.parseLong(rangeField.getText().toString());
+
+ e.putLong(Location.ACCURACY_MODE_RANDOMIZED_RANGE, randomRange);
+
+ e.putInt(Location.ACCURACY_MODE, option);
+ e.apply();
+
+ meAdapter.notifyDataSetChanged();
+ }
+ });
+
+ builder.create().show();
+ } else if (option == Location.ACCURACY_USER) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.title_location_accuracy_user);
+
+ View body = LayoutInflater.from(context).inflate(R.layout.dialog_location_user, null);
+ builder.setView(body);
+
+ final EditText locationField = (EditText) body.findViewById(R.id.user_location);
+
+ String existingLocation = prefs.getString(Location.ACCURACY_MODE_USER_LOCATION, Location.ACCURACY_MODE_USER_LOCATION_DEFAULT);
+
+ locationField.setText(existingLocation);
+
+ builder.setPositiveButton(R.string.action_continue, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialogInterface, int i) {
+ SharedPreferences.Editor e = prefs.edit();
+
+ String location = locationField.getText().toString();
+
+ e.putString(Location.ACCURACY_MODE_USER_LOCATION, location);
+
+ try {
+ List results = (new Geocoder(context)).getFromLocationName(location, 1);
+
+ if (results.size() > 0) {
+ Address match = results.get(0);
+
+ e.putFloat(Location.ACCURACY_MODE_USER_LOCATION_LATITUDE, (float) match.getLatitude());
+ e.putFloat(Location.ACCURACY_MODE_USER_LOCATION_LONGITUDE, (float) match.getLongitude());
+ } else {
+ Toast.makeText(context, R.string.toast_location_lookup_failed, Toast.LENGTH_LONG).show();
+
+ me.onCheckedChanged(compoundButton, checked);
+ }
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ }
+
+ e.putInt(Location.ACCURACY_MODE, option);
+ e.apply();
+
+ meAdapter.notifyDataSetChanged();
+ }
+ });
+
+ builder.create().show();
+ } else if (option == Location.ACCURACY_DISABLED) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.title_location_accuracy_disabled);
+ builder.setMessage(R.string.message_location_accuracy_disabled);
+
+ builder.setPositiveButton(R.string.action_continue, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialogInterface, int i) {
+ SharedPreferences.Editor e = prefs.edit();
+ e.putInt(Location.ACCURACY_MODE, option);
+ e.apply();
+
+ meAdapter.notifyDataSetChanged();
+ }
+ });
+
+ builder.create().show();
+ }
+
+ }
+ });
+
+ return convertView;
+ }
+ };
+
+ listView.setAdapter(accuracyAdapter);
+
+ accuracy.view = listView;
+
+ actions.add(accuracy);
+
+ return actions;
+ }
+
+ public static View getDisclosureDataView(final GeneratorViewHolder holder) {
+ final Context context = holder.itemView.getContext();
+
+ if (Location.useKindleLocationServices())
+ {
+ // TODO
+ throw new RuntimeException("Throw rocks at developer to implement Kindle support.");
+ }
+ else if (Location.useGoogleLocationServices(holder.itemView.getContext()))
+ {
+ final MapView mapView = new MapView(context);
+
+ FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+ mapView.setLayoutParams(params);
+
+ mapView.onCreate(null);
+
+ final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+
+ final boolean useHybrid = prefs.getBoolean(Location.SETTING_DISPLAY_HYBRID_MAP, Location.SETTING_DISPLAY_HYBRID_MAP_DEFAULT);
+
+ IconGenerator iconGen = new IconGenerator(context);
+
+ Drawable shapeDrawable = ResourcesCompat.getDrawable(context.getResources(), R.drawable.ic_location_heatmap_marker, null);
+ iconGen.setBackground(shapeDrawable);
+
+ View view = new View(context);
+ view.setLayoutParams(new ViewGroup.LayoutParams(8, 8));
+ iconGen.setContentView(view);
+
+ final Bitmap bitmap = iconGen.makeIcon();
+
+ mapView.getMapAsync(new OnMapReadyCallback() {
+ public void onMapReady(GoogleMap googleMap) {
+ if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED ||
+ ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
+ googleMap.setMyLocationEnabled(true);
+ }
+
+ if (useHybrid) {
+ googleMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
+ } else {
+ googleMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
+ }
+
+ googleMap.getUiSettings().setZoomControlsEnabled(true);
+ googleMap.getUiSettings().setMyLocationButtonEnabled(false);
+ googleMap.getUiSettings().setMapToolbarEnabled(false);
+ googleMap.getUiSettings().setAllGesturesEnabled(false);
+
+ Location me = Location.getInstance(context);
+
+ double lastLatitude = 0.0;
+ double lastLongitude = 0.0;
+ long timestamp = 0;
+
+ final List locations = new ArrayList<>();
+
+ String where = Location.HISTORY_OBSERVED + " > ?";
+ String[] args = { "" + (System.currentTimeMillis() - (1000 * 60 * 60 * 24)) };
+
+ Cursor c = me.mDatabase.query(Location.TABLE_HISTORY, null, where, args, null, null, Location.HISTORY_OBSERVED);
+
+ while (c.moveToNext()) {
+ lastLatitude = c.getDouble(c.getColumnIndex(Location.HISTORY_LATITUDE));
+ lastLongitude = c.getDouble(c.getColumnIndex(Location.HISTORY_LONGITUDE));
+
+ LatLng location = new LatLng(lastLatitude, lastLongitude);
+
+ locations.add(location);
+ }
+
+ c.close();
+
+ LatLngBounds.Builder builder = new LatLngBounds.Builder();
+
+ for (LatLng latlng : locations) {
+ builder.include(latlng);
+ }
+
+ final DisplayMetrics metrics = new DisplayMetrics();
+ ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(metrics);
+
+ if (locations.size() > 0) {
+ googleMap.moveCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), (int) (16 * metrics.density)));
+ }
+
+ for (LatLng latLng : locations) {
+ googleMap.addMarker(new MarkerOptions()
+ .position(latLng)
+ .icon(BitmapDescriptorFactory.fromBitmap(bitmap)));
+ }
+
+ mapView.onResume();
+ }
+ });
+
+ return mapView;
+ }
+ else
+ {
+ // TODO
+ throw new RuntimeException("Throw rocks at developer to implement generic location support.");
+ }
+ }
+
+ public static void bindDisclosureViewHolder(final GeneratorViewHolder holder) {
+ Class currentClass = new Object() { }.getClass().getEnclosingClass();
+
+ String identifier = currentClass.getCanonicalName();
+
+ TextView generatorLabel = (TextView) holder.itemView.findViewById(R.id.label_generator);
+
+ generatorLabel.setText(Location.getGeneratorTitle(holder.itemView.getContext()));
+ }
+
public android.location.Location getLastKnownLocation() {
+ final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this.mContext);
+ int selected = prefs.getInt(Location.ACCURACY_MODE, Location.ACCURACY_BEST);
+
+ if (selected == Location.ACCURACY_USER) {
+ double latitude = prefs.getFloat(Location.ACCURACY_MODE_USER_LOCATION_LATITUDE, 0);
+ double longitude = prefs.getFloat(Location.ACCURACY_MODE_USER_LOCATION_LONGITUDE, 0);
+
+ android.location.Location location = new android.location.Location("");
+ location.setLatitude(latitude);
+ location.setLongitude(longitude);
+
+ return location;
+ } else if (selected == Location.ACCURACY_DISABLED) {
+ android.location.Location location = new android.location.Location("");
+ location.setLatitude(41.8781);
+ location.setLongitude(-87.6298);
+
+ return location;
+ }
+
if (this.mLastLocation != null) {
return this.mLastLocation;
}