├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── drawable-hdpi
│ │ │ │ ├── ic_drawer.png
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── drawer_shadow.9.png
│ │ │ ├── drawable-mdpi
│ │ │ │ ├── ic_drawer.png
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── drawer_shadow.9.png
│ │ │ ├── drawable-xhdpi
│ │ │ │ ├── ic_drawer.png
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── drawer_shadow.9.png
│ │ │ ├── drawable-xxhdpi
│ │ │ │ ├── ic_drawer.png
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── drawer_shadow.9.png
│ │ │ ├── menu
│ │ │ │ ├── global.xml
│ │ │ │ └── main.xml
│ │ │ ├── xml
│ │ │ │ └── searchable.xml
│ │ │ ├── values
│ │ │ │ ├── dimens.xml
│ │ │ │ ├── strings.xml
│ │ │ │ ├── styles.xml
│ │ │ │ └── colors.xml
│ │ │ └── layout
│ │ │ │ ├── fragment_navigation_drawer.xml
│ │ │ │ ├── country_info_row.xml
│ │ │ │ └── activity_main.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── moac
│ │ │ │ └── android
│ │ │ │ └── refuge
│ │ │ │ ├── util
│ │ │ │ ├── ColorMaker.java
│ │ │ │ ├── DoOnce.java
│ │ │ │ └── Visualizer.java
│ │ │ │ ├── importer
│ │ │ │ ├── FileImportException.java
│ │ │ │ ├── LoadDataRunnable.java
│ │ │ │ ├── ImportService.java
│ │ │ │ └── DataFileImporter.java
│ │ │ │ ├── model
│ │ │ │ ├── DisplayedCountry.java
│ │ │ │ ├── persistent
│ │ │ │ │ ├── PersistableObject.java
│ │ │ │ │ ├── Demography.java
│ │ │ │ │ ├── Country.java
│ │ │ │ │ └── RefugeeFlow.java
│ │ │ │ └── CountriesModel.java
│ │ │ │ ├── adapter
│ │ │ │ ├── CountryAdapter.java
│ │ │ │ ├── CountryViewBinder.java
│ │ │ │ └── CountryViewModel.java
│ │ │ │ ├── RefugeApplication.java
│ │ │ │ ├── inject
│ │ │ │ ├── AppModule.java
│ │ │ │ └── DebugAppModule.java
│ │ │ │ ├── database
│ │ │ │ ├── RefugeeDataStore.java
│ │ │ │ ├── MockRefugeeDataStore.java
│ │ │ │ ├── DatabaseHelper.java
│ │ │ │ └── PersistentRefugeeDataStore.java
│ │ │ │ ├── search
│ │ │ │ └── CountrySuggestionProvider.java
│ │ │ │ ├── fragment
│ │ │ │ └── NavigationDrawerFragment.java
│ │ │ │ └── activity
│ │ │ │ └── MainActivity.java
│ │ ├── assets
│ │ │ ├── smalltestsample.xml
│ │ │ └── CountriesLatLong.csv
│ │ └── AndroidManifest.xml
│ └── androidTest
│ │ ├── assets
│ │ ├── smalltestsample.xml
│ │ └── CountriesLatLong.csv
│ │ └── java
│ │ └── com
│ │ └── moac
│ │ └── android
│ │ └── refuge
│ │ └── importer
│ │ ├── CountriesLatLongImporterTest.java
│ │ └── XMLFileImporterTest.java
├── proguard-rules.txt
└── build.gradle
├── settings.gradle
├── gradle
├── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew.bat
└── gradlew
├── .gitignore
├── gradle.properties
├── README.md
├── gradlew.bat
├── gradlew
└── LICENSE
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peter-tackage/refuge/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peter-tackage/refuge/HEAD/gradle/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_drawer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peter-tackage/refuge/HEAD/app/src/main/res/drawable-hdpi/ic_drawer.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peter-tackage/refuge/HEAD/app/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_drawer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peter-tackage/refuge/HEAD/app/src/main/res/drawable-mdpi/ic_drawer.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peter-tackage/refuge/HEAD/app/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_drawer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peter-tackage/refuge/HEAD/app/src/main/res/drawable-xhdpi/ic_drawer.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_drawer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peter-tackage/refuge/HEAD/app/src/main/res/drawable-xxhdpi/ic_drawer.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | local.properties
2 |
3 | # Intellij artifacts
4 | .idea/
5 | *.iml
6 |
7 | # Gradle artifacts
8 | .gradle/
9 | build/
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peter-tackage/refuge/HEAD/app/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peter-tackage/refuge/HEAD/app/src/main/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/drawer_shadow.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peter-tackage/refuge/HEAD/app/src/main/res/drawable-hdpi/drawer_shadow.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/drawer_shadow.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peter-tackage/refuge/HEAD/app/src/main/res/drawable-mdpi/drawer_shadow.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/drawer_shadow.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peter-tackage/refuge/HEAD/app/src/main/res/drawable-xhdpi/drawer_shadow.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/drawer_shadow.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peter-tackage/refuge/HEAD/app/src/main/res/drawable-xxhdpi/drawer_shadow.9.png
--------------------------------------------------------------------------------
/app/src/main/java/com/moac/android/refuge/util/ColorMaker.java:
--------------------------------------------------------------------------------
1 | package com.moac.android.refuge.util;
2 |
3 | /**
4 | * @author Peter Tackage
5 | * @since 03/05/15
6 | */
7 | public class ColorMaker {
8 |
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/moac/android/refuge/importer/FileImportException.java:
--------------------------------------------------------------------------------
1 | package com.moac.android.refuge.importer;
2 |
3 | public class FileImportException extends Exception {
4 |
5 | public FileImportException(Exception e) {
6 | super(e);
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Apr 10 15:27:10 PDT 2013
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradle/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 20 13:36:32 CEST 2014
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-1.12-bin.zip
7 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/global.xml:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/searchable.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
8 | 240dp
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/java/com/moac/android/refuge/model/DisplayedCountry.java:
--------------------------------------------------------------------------------
1 | package com.moac.android.refuge.model;
2 |
3 | /**
4 | * @author Peter Tackage
5 | * @since 03/05/15
6 | */
7 | public class DisplayedCountry {
8 | long id;
9 | int color;
10 |
11 | public DisplayedCountry(long id, int color) {
12 | this.id = id;
13 | this.color = color;
14 | }
15 |
16 | public long getId() {
17 | return id;
18 | }
19 |
20 | public int getColor() {
21 | return color;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_navigation_drawer.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Refuge
5 | Open navigation drawer
6 | Close navigation drawer
7 | About
8 | Clear
9 | Search
10 | Search countries
11 | Open Drawer
12 | Close Drawer
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
11 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/androidTest/assets/smalltestsample.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Albania
4 | Algeria
5 | 2012
6 | 1
7 | 1
8 |
9 | 1
10 |
11 | 1
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/assets/smalltestsample.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Albania
4 | Algeria
5 | 2012
6 | 1
7 | 1
8 |
9 | 1
10 |
11 | 1
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/moac/android/refuge/importer/CountriesLatLongImporterTest.java:
--------------------------------------------------------------------------------
1 | package com.moac.android.refuge.importer;
2 |
3 | import android.test.InstrumentationTestCase;
4 |
5 | /**
6 | * Created by amelysh on 09/02/14.
7 | */
8 | public class CountriesLatLongImporterTest extends InstrumentationTestCase {
9 |
10 | private static String testFile = "CountriesLatLong.csv";
11 |
12 | // public void testLatLongImporter () throws IOException, FileImportException {
13 | // CountriesLatLongImporter parser = new CountriesLatLongImporter();
14 | // InputStream is = getInstrumentation().getContext().getResources().getAssets().open(testFile);
15 | // parser.parse(is);
16 | // }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/app/proguard-rules.txt:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/peter.tackage/Development/android-sdk-macosx/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the ProGuard
5 | # include property in project.properties.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
--------------------------------------------------------------------------------
/app/src/main/res/menu/main.xml:
--------------------------------------------------------------------------------
1 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | #2196f3
7 | #1976d2
8 | #FFFFFF
9 |
10 |
11 |
12 | #66D6B331
13 | #FFD6B331
14 |
15 | #66663399
16 | #DD663399
17 |
18 | #55FF6600
19 | #FFFF6600
20 |
21 | #66669900
22 | #DD669900
23 |
24 | #660066cc
25 | #DD0066cc
26 |
27 | #CDDB2B
28 |
29 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Settings specified in this file will override any Gradle settings
5 | # configured through the IDE.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/app/src/main/java/com/moac/android/refuge/model/persistent/PersistableObject.java:
--------------------------------------------------------------------------------
1 | package com.moac.android.refuge.model.persistent;
2 |
3 | import com.j256.ormlite.field.DatabaseField;
4 |
5 | /**
6 | * In OrmLite, the foreign id references are done using an instance of
7 | * the class that corresponds to the foreign table.
8 | *
9 | * This can be confusing if getters are provided for these fields as
10 | * the returned object will not have all its fields set; only the _id
11 | * field is populated.
12 | * *
13 | * http://ormlite.com/javadoc/ormlite-core/doc-files/ormlite_2.html#Foreign-Objects
14 | */
15 | public abstract class PersistableObject {
16 |
17 | public static final long UNSET_ID = -1;
18 |
19 | @DatabaseField(columnName = Columns._ID, generatedId = true, unique = true, canBeNull = false)
20 | private long id = UNSET_ID;
21 |
22 | public interface Columns {
23 | public static final String _ID = "_id";
24 | }
25 |
26 | public long getId() {
27 | return id;
28 | }
29 |
30 | public void setId(long id) {
31 | this.id = id;
32 | }
33 | }
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 22
5 | buildToolsVersion "22.0.1"
6 |
7 | defaultConfig {
8 | minSdkVersion 14
9 | targetSdkVersion 22
10 | }
11 | buildTypes {
12 | release {
13 | minifyEnabled false
14 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
15 | }
16 | }
17 | }
18 |
19 | dependencies {
20 | compile 'com.android.support:support-v13:22.1.1'
21 | compile 'com.android.support:appcompat-v7:22.1.1'
22 | compile 'com.google.android.gms:play-services:7.0.0'
23 | compile 'com.j256.ormlite:ormlite-android:4.48'
24 | compile 'com.j256.ormlite:ormlite-core:4.48'
25 | compile 'com.squareup.dagger:dagger:1.2.0'
26 | compile 'com.squareup.dagger:dagger-compiler:1.2.0'
27 | compile 'io.reactivex:rxjava:1.0.10'
28 | compile 'io.reactivex:rxandroid:0.24.0'
29 |
30 | debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3'
31 | releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3'
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/java/com/moac/android/refuge/model/CountriesModel.java:
--------------------------------------------------------------------------------
1 | package com.moac.android.refuge.model;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Collections;
5 | import java.util.List;
6 |
7 | import rx.Observable;
8 | import rx.subjects.BehaviorSubject;
9 |
10 | public class CountriesModel {
11 |
12 | private static final int[] FILL_COLORS = {0x660066cc, 0x66D6B331, 0x66663399, 0x55FF6600,
13 | 0x66669900};
14 |
15 | private static final int[] STROKE_COLORS = {0xDD0066cc, 0xFFD6B331, 0xDD663399, 0xFFFF6600,
16 | 0xDD669900};
17 |
18 | private List ids = new ArrayList<>();
19 | private BehaviorSubject> subject = BehaviorSubject.create();
20 |
21 | public Observable> getDisplayedCountries() {
22 | return subject;
23 | }
24 |
25 | public void add(Long id) {
26 | DisplayedCountry country = new DisplayedCountry(id, STROKE_COLORS[ids.size()]);
27 | ids.add(country);
28 | subject.onNext(ids);
29 | }
30 |
31 | public void clear() {
32 | ids.clear();
33 | subject.onNext(Collections.emptyList());
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/main/java/com/moac/android/refuge/util/DoOnce.java:
--------------------------------------------------------------------------------
1 | package com.moac.android.refuge.util;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 | import android.preference.PreferenceManager;
6 |
7 | public class DoOnce {
8 |
9 | public static final String TAG = DoOnce.class.getSimpleName();
10 |
11 | private static final String DO_ONCE_TAG = "do_once";
12 |
13 | public static boolean doOnce(Context context, String taskTag, Runnable task) {
14 | final String prefTag = toIsDoneTag(taskTag);
15 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
16 | boolean isDone = prefs.getBoolean(prefTag, false);
17 | if (!isDone) {
18 | task.run();
19 | prefs.edit().putBoolean(prefTag, true).apply();
20 | return true;
21 | }
22 | return false;
23 | }
24 |
25 | public static boolean isDone(Context context, String taskTag) {
26 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
27 | return prefs.getBoolean(toIsDoneTag(taskTag), false);
28 | }
29 |
30 | private static String toIsDoneTag(String taskTag) {
31 | return DO_ONCE_TAG + taskTag;
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/country_info_row.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
18 |
19 |
25 |
26 |
32 |
33 |
--------------------------------------------------------------------------------
/app/src/main/java/com/moac/android/refuge/adapter/CountryAdapter.java:
--------------------------------------------------------------------------------
1 | package com.moac.android.refuge.adapter;
2 |
3 | import android.content.Context;
4 | import android.view.View;
5 | import android.view.ViewGroup;
6 | import android.widget.BaseAdapter;
7 |
8 | import java.util.List;
9 |
10 | public class CountryAdapter extends BaseAdapter {
11 |
12 | private final Context context;
13 | private List items;
14 | private final ViewModelBinder binder;
15 |
16 | public CountryAdapter(Context context, List items, ViewModelBinder binder) {
17 | this.context = context;
18 | this.items = items;
19 | this.binder = binder;
20 | }
21 |
22 | @Override
23 | public int getCount() {
24 | return items.size();
25 | }
26 |
27 | @Override
28 | public CountryViewModel getItem(int position) {
29 | return items.get(position);
30 | }
31 |
32 | @Override
33 | public long getItemId(int position) {
34 | return getItem(position).getCountryId();
35 | }
36 |
37 | @Override
38 | public View getView(int position, View convertView, ViewGroup parent) {
39 | return binder.getView(context, convertView, parent, items.get(position));
40 | }
41 |
42 | public interface ViewModelBinder {
43 | View getView(Context context, View convertView, ViewGroup parent, CountryViewModel item);
44 | }
45 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/moac/android/refuge/importer/LoadDataRunnable.java:
--------------------------------------------------------------------------------
1 | package com.moac.android.refuge.importer;
2 |
3 |
4 | import android.util.Log;
5 |
6 | import java.io.IOException;
7 | import java.io.InputStream;
8 |
9 | public class LoadDataRunnable implements Runnable {
10 |
11 | private static final String TAG = LoadDataRunnable.class.getSimpleName();
12 |
13 | private final DataFileImporter importer;
14 | private final InputStream unDataInputStream;
15 | private final InputStream countriesLatLongInputStream;
16 |
17 | public LoadDataRunnable(DataFileImporter importer, InputStream unDataInputStream, InputStream countriesLatLongInputStream) {
18 | this.importer = importer;
19 | this.unDataInputStream = unDataInputStream;
20 | this.countriesLatLongInputStream = countriesLatLongInputStream;
21 | }
22 |
23 | @Override
24 | public void run() {
25 | try {
26 | importer.parse(unDataInputStream, countriesLatLongInputStream);
27 | } catch (FileImportException e) {
28 | Log.e(TAG, "Failed to import the dataset", e);
29 | } finally {
30 | try {
31 | unDataInputStream.close();
32 | countriesLatLongInputStream.close();
33 | } catch (IOException ioe) {
34 | Log.w(TAG, "Failed to close the input stream", ioe);
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/main/java/com/moac/android/refuge/RefugeApplication.java:
--------------------------------------------------------------------------------
1 | package com.moac.android.refuge;
2 |
3 | import android.app.Activity;
4 | import android.app.Application;
5 | import android.app.Service;
6 | import android.util.Log;
7 |
8 | import com.moac.android.refuge.inject.AppModule;
9 | import com.squareup.leakcanary.LeakCanary;
10 |
11 | import dagger.ObjectGraph;
12 |
13 | public class RefugeApplication extends Application {
14 | private static final String TAG = RefugeApplication.class.getSimpleName();
15 |
16 | private ObjectGraph objectGraph;
17 |
18 | public static RefugeApplication from(Activity activity) {
19 | return (RefugeApplication) activity.getApplication();
20 | }
21 |
22 | public static RefugeApplication from(android.support.v4.app.Fragment fragment) {
23 | return (RefugeApplication) fragment.getActivity().getApplication();
24 | }
25 |
26 | public static RefugeApplication from(Service service) {
27 | return (RefugeApplication) service.getApplication();
28 | }
29 |
30 | @Override
31 | public void onCreate() {
32 | Log.d(TAG, "onCreate() - start");
33 | Object prodModule = new AppModule(this);
34 | //Object debugAppModule = new DebugAppModule(this);
35 | objectGraph = ObjectGraph.create(prodModule);
36 | LeakCanary.install(this);
37 | }
38 |
39 | public void inject(Object object) {
40 | objectGraph.inject(object);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/java/com/moac/android/refuge/inject/AppModule.java:
--------------------------------------------------------------------------------
1 | package com.moac.android.refuge.inject;
2 |
3 | import android.util.Log;
4 |
5 | import com.moac.android.refuge.RefugeApplication;
6 | import com.moac.android.refuge.activity.MainActivity;
7 | import com.moac.android.refuge.database.DatabaseHelper;
8 | import com.moac.android.refuge.database.PersistentRefugeeDataStore;
9 | import com.moac.android.refuge.database.RefugeeDataStore;
10 | import com.moac.android.refuge.fragment.NavigationDrawerFragment;
11 | import com.moac.android.refuge.importer.ImportService;
12 | import com.moac.android.refuge.model.CountriesModel;
13 |
14 | import javax.inject.Singleton;
15 |
16 | import dagger.Module;
17 | import dagger.Provides;
18 |
19 | @Module(injects = {
20 | RefugeApplication.class,
21 | MainActivity.class,
22 | NavigationDrawerFragment.class,
23 | ImportService.class})
24 | public class AppModule {
25 | private static final String TAG = AppModule.class.getSimpleName();
26 |
27 | private final RefugeApplication application;
28 |
29 | public AppModule(RefugeApplication application) {
30 | this.application = application;
31 | }
32 |
33 | @Provides @Singleton RefugeeDataStore provideDatabase() {
34 | Log.i(TAG, "Providing database");
35 | DatabaseHelper databaseHelper = new DatabaseHelper(application);
36 | return new PersistentRefugeeDataStore(databaseHelper);
37 | }
38 |
39 | @Provides @Singleton CountriesModel provideCountriesModel() {
40 | return new CountriesModel();
41 | }
42 |
43 |
44 | }
45 |
46 |
--------------------------------------------------------------------------------
/app/src/main/java/com/moac/android/refuge/database/RefugeeDataStore.java:
--------------------------------------------------------------------------------
1 | package com.moac.android.refuge.database;
2 |
3 | import com.moac.android.refuge.model.persistent.Country;
4 | import com.moac.android.refuge.model.persistent.Demography;
5 | import com.moac.android.refuge.model.persistent.RefugeeFlow;
6 |
7 | import java.util.List;
8 |
9 | public interface RefugeeDataStore {
10 |
11 | /*
12 | * Country
13 | */
14 |
15 | List getAllCountries();
16 |
17 | Country getCountry(long id);
18 |
19 | long createCountry(Country country);
20 |
21 | void updateCountry(Country country);
22 |
23 | void deleteCountry(long id);
24 |
25 | /*
26 | * Demography
27 | */
28 |
29 | List getAllDemographics();
30 |
31 | Demography getDemography(long id);
32 |
33 | long createDemography(Demography demography);
34 |
35 | void updateDemography(Demography demography);
36 |
37 | void deleteDemography(long id);
38 |
39 | /*
40 | * Refugee Flow
41 | */
42 |
43 | List getAllRefugeeFlows();
44 |
45 | RefugeeFlow getRefugeeFlow(long id);
46 |
47 | long createRefugeeFlow(RefugeeFlow refugeeFlow);
48 |
49 | void updateRefugeeFlow(RefugeeFlow refugeeFlow);
50 |
51 | void deleteRefugeeFlow(long id);
52 |
53 | long getTotalRefugeeFlowTo(long countryId);
54 |
55 | long getTotalRefugeeFlowFrom(long countryId);
56 |
57 | List getRefugeeFlowsFrom(long countryId);
58 |
59 | List getRefugeeFlowsTo(long countryId);
60 |
61 | Country getCountry(String countryName);
62 | }
63 |
--------------------------------------------------------------------------------
/app/src/main/java/com/moac/android/refuge/model/persistent/Demography.java:
--------------------------------------------------------------------------------
1 | package com.moac.android.refuge.model.persistent;
2 |
3 | import com.j256.ormlite.field.DatabaseField;
4 | import com.j256.ormlite.table.DatabaseTable;
5 |
6 | @DatabaseTable(tableName = Demography.TABLE_NAME)
7 | public class Demography extends PersistableObject {
8 |
9 | public static final String TABLE_NAME = "demographies";
10 |
11 | public static interface Columns extends PersistableObject.Columns {
12 | public static final String COUNTRY_COLUMN = "COUNTRY";
13 | public static final String YEAR_COLUMN = "YEAR";
14 | public static final String POPULATION_COLUMN = "POPULATION";
15 | public static final String MIGRATION_COLUMN = "MIGRATION";
16 | }
17 |
18 | @DatabaseField(columnName = Columns.COUNTRY_COLUMN, foreign = true, canBeNull = false)
19 | private Country country;
20 |
21 | @DatabaseField(columnName = Columns.YEAR_COLUMN, canBeNull = false)
22 | private int year;
23 |
24 | @DatabaseField(columnName = Columns.POPULATION_COLUMN, canBeNull = false)
25 | private long population;
26 |
27 | @DatabaseField(columnName = Columns.MIGRATION_COLUMN, canBeNull = false)
28 | private long migration;
29 |
30 | public Demography(Country country) {
31 | this.country = country;
32 | }
33 |
34 | // Required by ORMLite
35 | public Demography() {
36 | }
37 |
38 | public Country getCountry() {
39 | return country;
40 | }
41 |
42 | public void setCountry(Country country) {
43 | this.country = country;
44 | }
45 |
46 | public int getYear() {
47 | return year;
48 | }
49 |
50 | public double getPopulation() {
51 | return population;
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
12 |
17 |
18 |
23 |
24 |
29 |
30 |
31 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/app/src/main/java/com/moac/android/refuge/model/persistent/Country.java:
--------------------------------------------------------------------------------
1 | package com.moac.android.refuge.model.persistent;
2 |
3 | import com.google.android.gms.maps.model.LatLng;
4 | import com.j256.ormlite.field.DatabaseField;
5 | import com.j256.ormlite.field.ForeignCollectionField;
6 | import com.j256.ormlite.table.DatabaseTable;
7 |
8 | @DatabaseTable(tableName = Country.TABLE_NAME)
9 | public class Country extends PersistableObject {
10 |
11 | public static final String TABLE_NAME = "countries";
12 |
13 | public static interface Columns extends PersistableObject.Columns {
14 | public static final String NAME_COLUMN = "NAME";
15 | public static final String LONGITUDE_COLUMN = "LONGITUDE";
16 | public static final String LATITUDE_COLUMN = "LATITUDE";
17 | }
18 |
19 | @DatabaseField(columnName = Columns.NAME_COLUMN, unique = true, canBeNull = false)
20 | private String name;
21 |
22 | @DatabaseField(columnName = Columns.LONGITUDE_COLUMN, canBeNull = false)
23 | private double longitude;
24 |
25 | @DatabaseField(columnName = Columns.LATITUDE_COLUMN, canBeNull = false)
26 | private double latitude;
27 |
28 | @ForeignCollectionField(eager = false)
29 | private java.util.Collection mRefugeeFlows;
30 |
31 | public Country(String name, Double latitude, Double longitude) {
32 | this.name = name;
33 | this.latitude = latitude;
34 | this.longitude = longitude;
35 | }
36 |
37 | // Required by ORMLite
38 | public Country() {
39 | }
40 |
41 | public String getName() {
42 | return name;
43 | }
44 |
45 | public void setName(String name) {
46 | this.name = name;
47 | }
48 |
49 | public double getLongitude() {
50 | return longitude;
51 | }
52 |
53 | public double getLatitude() {
54 | return latitude;
55 | }
56 |
57 | public LatLng getLatLng() {
58 | return new LatLng(latitude, longitude);
59 | }
60 |
61 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | refuge
2 | ======
3 | Originally created during the 2014 Hack4good Berlin hackathon, this is an Android Google Maps visualization of world refugee flows. It's currently very raw, unpolished and unfinished - but work will continue with hopes of making it a more useful tool.
4 |
5 | The app presents data obtained from the UNHCR (United Nations High Commissioner for Refugees) about the refugee flows worldwide in a way that would add some perspective for this sometimes emotive global and political issue.
6 |
7 | It allows you to search for particular countries and displays where the refugees have come from.
8 |
9 | It thus shows a comparison in terms of how many refugees are received by countries in relation to each other as well as compared to the total refugee number worldwide.
10 |
11 | Dataset from http://data.un.org/
12 |
13 | **Visually comparing France and Germany**
14 | 
15 |
16 | **Refugee flow in the Middle East during 2012**
17 | 
18 |
19 | **Comparing refugees intake across multiple countries**
20 | 
21 |
22 | License
23 | =======
24 |
25 | Copyright 2015 Peter Tackage
26 |
27 | Licensed under the Apache License, Version 2.0 (the "License");
28 | you may not use this file except in compliance with the License.
29 | You may obtain a copy of the License at
30 |
31 | http://www.apache.org/licenses/LICENSE-2.0
32 |
33 | Unless required by applicable law or agreed to in writing, software
34 | distributed under the License is distributed on an "AS IS" BASIS,
35 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
36 | See the License for the specific language governing permissions and
37 | limitations under the License.
--------------------------------------------------------------------------------
/app/src/main/java/com/moac/android/refuge/adapter/CountryViewBinder.java:
--------------------------------------------------------------------------------
1 | package com.moac.android.refuge.adapter;
2 |
3 | import android.content.Context;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.TextView;
8 |
9 | import com.moac.android.refuge.R;
10 |
11 | public class CountryViewBinder implements CountryAdapter.ViewModelBinder {
12 |
13 | private final int resourceId;
14 |
15 | public CountryViewBinder(int resourceId) {
16 | this.resourceId = resourceId;
17 | }
18 |
19 | @Override
20 | public View getView(Context context, View convertView, ViewGroup parent, final CountryViewModel item) {
21 | View view = convertView;
22 | TextView countryNameTextView;
23 | TextView totalIntakeTextView;
24 | final View colorIndicatorView;
25 |
26 | if (view == null) {
27 | view = LayoutInflater.from(context).inflate(resourceId, parent, false);
28 | countryNameTextView = (TextView) view.findViewById(R.id.country_name_textView);
29 | totalIntakeTextView = (TextView) view.findViewById(R.id.total_intake_textView);
30 | colorIndicatorView = view.findViewById(R.id.country_item_check_indicator);
31 | view.setTag(R.id.country_name_textView, countryNameTextView);
32 | view.setTag(R.id.total_intake_textView, totalIntakeTextView);
33 | view.setTag(R.id.country_item_check_indicator, colorIndicatorView);
34 | } else {
35 | countryNameTextView = (TextView) view.getTag(R.id.country_name_textView);
36 | totalIntakeTextView = (TextView) view.getTag(R.id.total_intake_textView);
37 | colorIndicatorView = (View) view.getTag(R.id.country_item_check_indicator);
38 | }
39 |
40 | countryNameTextView.setText(item.getCountryName());
41 | totalIntakeTextView.setText(String.valueOf(item.getTotalIntake()));
42 | colorIndicatorView.setBackgroundColor(item.getColor());
43 | return view;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/app/src/main/java/com/moac/android/refuge/model/persistent/RefugeeFlow.java:
--------------------------------------------------------------------------------
1 | package com.moac.android.refuge.model.persistent;
2 |
3 | import com.j256.ormlite.field.DatabaseField;
4 | import com.j256.ormlite.table.DatabaseTable;
5 |
6 | @DatabaseTable(tableName = RefugeeFlow.TABLE_NAME)
7 | public class RefugeeFlow extends PersistableObject {
8 |
9 | public static final String TABLE_NAME = "refugeeFlows";
10 |
11 | public static interface Columns extends PersistableObject.Columns {
12 | public static final String FROM_COUNTRY_COLUMN = "FROM_COUNTRY";
13 | public static final String TO_COUNTRY_COLUMN = "TO_COUNTRY";
14 | public static final String YEAR_COLUMN = "YEAR";
15 | public static final String REFUGEE_COUNT_COLUMN = "REFUGEE_COUNT";
16 | }
17 |
18 | public RefugeeFlow(Country fromCountry, Country toCountry) {
19 | mFromCountry = fromCountry;
20 | mToCountry = toCountry;
21 | }
22 |
23 | // Required by ORMLite
24 | public RefugeeFlow() {
25 | }
26 |
27 | @DatabaseField(columnName = Columns.FROM_COUNTRY_COLUMN, foreign = true, canBeNull = false)
28 | private Country mFromCountry;
29 |
30 | @DatabaseField(columnName = Columns.TO_COUNTRY_COLUMN, foreign = true, canBeNull = false)
31 | private Country mToCountry;
32 |
33 | @DatabaseField(columnName = Columns.YEAR_COLUMN, canBeNull = false)
34 | private int mYear;
35 |
36 | @DatabaseField(columnName = Columns.REFUGEE_COUNT_COLUMN, canBeNull = false)
37 | private long mRefugeeCount;
38 |
39 | public Country getFromCountry() {
40 | return mFromCountry;
41 | }
42 |
43 | public Country getToCountry() {
44 | return mToCountry;
45 | }
46 |
47 | public int getYear() {
48 | return mYear;
49 | }
50 |
51 | public void setYear(int _year) {
52 | mYear = _year;
53 | }
54 |
55 | public long getRefugeeCount() {
56 | return mRefugeeCount;
57 | }
58 |
59 | public void setRefugeeCount(long _refugeCount) {
60 | mRefugeeCount = _refugeCount;
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
14 |
15 |
22 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
39 |
40 |
41 |
44 |
45 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/gradle/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/app/src/main/java/com/moac/android/refuge/importer/ImportService.java:
--------------------------------------------------------------------------------
1 | package com.moac.android.refuge.importer;
2 |
3 | import android.app.IntentService;
4 | import android.content.Intent;
5 | import android.os.Binder;
6 | import android.os.IBinder;
7 | import android.util.Log;
8 |
9 | import com.moac.android.refuge.RefugeApplication;
10 | import com.moac.android.refuge.database.RefugeeDataStore;
11 | import com.moac.android.refuge.util.DoOnce;
12 |
13 | import javax.inject.Inject;
14 |
15 | import rx.Notification;
16 | import rx.Observable;
17 | import rx.subjects.BehaviorSubject;
18 |
19 | /**
20 | * @author Peter Tackage
21 | * @since 02/05/15
22 | */
23 | public class ImportService extends IntentService {
24 |
25 | public static final String LOAD_DATA_TASK_TAG = "LOAD_DATA_TASK";
26 |
27 | private static final String TAG = ImportService.class.getSimpleName();
28 | private static final String ASSET_FILE = "UNDataExport2012.xml";
29 | private static final String COUNTRIES_LAT_LONG_CSV = "CountriesLatLong.csv";
30 |
31 | @Inject
32 | RefugeeDataStore refugeeDataStore;
33 |
34 | private BehaviorSubject status = BehaviorSubject.create(Status.IDLE);
35 |
36 | public ImportService() {
37 | super(ImportService.class.getSimpleName());
38 | }
39 |
40 | @Override
41 | public void onCreate() {
42 | super.onCreate();
43 | RefugeApplication app = ((RefugeApplication) getApplication());
44 | app.inject(this);
45 | }
46 |
47 | @Override
48 | protected void onHandleIntent(Intent intent) {
49 | Log.d(TAG, "onHandleIntent() started - " + intent);
50 | status.onNext(Status.RUNNING);
51 | try {
52 | boolean attemptedToLoad = DoOnce.doOnce(this, LOAD_DATA_TASK_TAG,
53 | new LoadDataRunnable(new DataFileImporter(refugeeDataStore),
54 | getAssets().open(ASSET_FILE),
55 | getAssets().open(COUNTRIES_LAT_LONG_CSV)));
56 | Log.i(TAG, "Attempted to load data: " + attemptedToLoad);
57 | status.onCompleted();
58 | } catch (Exception e) {
59 | Log.e(TAG, "Failed to open the data file: " + ASSET_FILE, e);
60 | status.onError(e);
61 | }
62 | Log.d(TAG, "onHandleIntent() finished");
63 | }
64 |
65 | @Override
66 | public IBinder onBind(Intent intent) {
67 | return new ImportClient();
68 | }
69 |
70 | public class ImportClient extends Binder {
71 | public Observable> getStatus() {
72 | return status.materialize();
73 | }
74 | }
75 |
76 | public enum Status {
77 | IDLE,
78 | RUNNING,
79 | COMPLETED
80 | }
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/app/src/main/java/com/moac/android/refuge/adapter/CountryViewModel.java:
--------------------------------------------------------------------------------
1 | package com.moac.android.refuge.adapter;
2 |
3 | import com.moac.android.refuge.database.RefugeeDataStore;
4 | import com.moac.android.refuge.model.persistent.Country;
5 |
6 | import rx.Observable;
7 | import rx.functions.Action1;
8 | import rx.functions.Func1;
9 | import rx.subscriptions.CompositeSubscription;
10 |
11 | public class CountryViewModel {
12 |
13 | private final CompositeSubscription compositeSubscription = new CompositeSubscription();
14 |
15 | private final RefugeeDataStore dataStore;
16 | private final long countryId;
17 |
18 | private String countryName;
19 | private long totalIntake;
20 | private int color;
21 |
22 | public CountryViewModel(final RefugeeDataStore dataStore, final long countryId, int color) {
23 | this.dataStore = dataStore;
24 | this.countryId = countryId;
25 | this.color = color;
26 | }
27 |
28 | public long getCountryId() {
29 | return countryId;
30 | }
31 |
32 | public String getCountryName() {
33 | return countryName;
34 | }
35 |
36 | public int getColor() {
37 | return color;
38 | }
39 |
40 | public Long getTotalIntake() {
41 | return totalIntake;
42 | }
43 |
44 | public void subscribeToDataStore() {
45 | compositeSubscription.add(createCountryNameObservable().subscribe(new Action1() {
46 | @Override public void call(String countryName) {
47 | CountryViewModel.this.countryName = countryName;
48 | }
49 | }));
50 | compositeSubscription.add(createTotalIntakeObservable().subscribe(new Action1() {
51 | @Override public void call(Long totalIntake) {
52 | CountryViewModel.this.totalIntake = totalIntake;
53 | }
54 | }));
55 | compositeSubscription.add(createCountryColorObservable().subscribe(new Action1() {
56 | @Override public void call(Integer color) {
57 | CountryViewModel.this.color = color;
58 | }
59 | }));
60 | }
61 |
62 | public void unsubscribeFromDataStore() {
63 | compositeSubscription.clear();
64 | }
65 |
66 | private Observable createCountryNameObservable() {
67 | return Observable.just(dataStore.getCountry(countryId))
68 | .map(new Func1() {
69 | @Override
70 | public String call(Country country) {
71 | return country.getName();
72 | }
73 | });
74 | }
75 |
76 | private Observable createTotalIntakeObservable() {
77 | return Observable.just(dataStore.getTotalRefugeeFlowTo(countryId));
78 | }
79 |
80 | private Observable createCountryColorObservable() {
81 | return Observable.just(color);
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/moac/android/refuge/importer/XMLFileImporterTest.java:
--------------------------------------------------------------------------------
1 | package com.moac.android.refuge.importer;
2 |
3 | import android.test.InstrumentationTestCase;
4 |
5 | import com.moac.android.refuge.database.DatabaseHelper;
6 | import com.moac.android.refuge.database.PersistentRefugeeDataStore;
7 | import com.moac.android.refuge.model.persistent.RefugeeFlow;
8 |
9 | import java.io.IOException;
10 |
11 | /**
12 | * Created by amelysh on 08/02/14.
13 | */
14 | public class XMLFileImporterTest extends InstrumentationTestCase {
15 |
16 | private static String countriesLatLongFile = "CountriesLatLong.csv";
17 | private static String testDataXMLFile = "smalltestsample.xml";
18 |
19 | @Override
20 | protected void setUp() throws Exception {
21 | super.setUp();
22 |
23 | getInstrumentation().getContext().deleteDatabase("/data/data/com.moac.android.refuge/databases/refuge.db");
24 | }
25 |
26 | @Override
27 | protected void tearDown() throws Exception {
28 | super.tearDown();
29 |
30 | }
31 |
32 | // public void testDOMHandler () throws IOException, FileImportException {
33 | // ModelService mockModelService = new MockModelService();
34 | // DataFileImporter domParser = new DataFileImporter(mockModelService);
35 | //
36 | // InputStream countriesLatLongIs = getInstrumentation().getContext().getResources().getAssets().open(countriesLatLongFile);
37 | // InputStream is = getInstrumentation().getContext().getResources().getAssets().open(testDataXMLFile);
38 | // domParser.parse(is, countriesLatLongIs);
39 | //
40 | // Country fromCountry = new Country();
41 | // fromCountry.setName("Algeria");
42 | // Country toCountry = new Country();
43 | // toCountry.setName("Albania");
44 | //
45 | // RefugeeFlow expectedResult = new RefugeeFlow(fromCountry, toCountry);
46 | // expectedResult.setRefugeeCount(1);
47 | // expectedResult.setYear(2012);
48 | //
49 | // assertTrue(areRefugeeFlowsEqual(expectedResult, domParser.refugeeFlow));
50 | // }
51 |
52 | public void testDB() {
53 | String assetFile = "UNDataExport2012.xml";
54 | String countriesLongLat = "CountriesLatLong.csv";
55 | DatabaseHelper databaseHelper = new DatabaseHelper(getInstrumentation().getTargetContext());
56 | PersistentRefugeeDataStore persistentModelService = new PersistentRefugeeDataStore(databaseHelper);
57 | try {
58 | LoadDataRunnable loadDataRunnable = new LoadDataRunnable(new DataFileImporter(persistentModelService), getInstrumentation().getTargetContext().getAssets().open(assetFile), getInstrumentation().getTargetContext().getAssets().open(countriesLongLat));
59 | loadDataRunnable.run();
60 | } catch (IOException e) {
61 | e.printStackTrace();
62 | }
63 |
64 | persistentModelService.getTotalRefugeeFlowTo(2);
65 |
66 | assertEquals(149765, persistentModelService.getTotalRefugeeFlowTo(72));
67 |
68 | }
69 |
70 | String printRefugeeFlow(RefugeeFlow refugeeFlow) {
71 | return "flow from: " + refugeeFlow.getFromCountry().getName() + " to:" + refugeeFlow.getToCountry().getName()
72 | + " year: " + refugeeFlow.getYear() + " num:" + refugeeFlow.getRefugeeCount();
73 | }
74 |
75 | private Boolean areRefugeeFlowsEqual(RefugeeFlow a, RefugeeFlow b) {
76 | return (a.getFromCountry().getName().equals(b.getFromCountry().getName())) &&
77 | (a.getToCountry().getName().equals(b.getToCountry().getName())) &&
78 | (a.getRefugeeCount() == b.getRefugeeCount()) &&
79 | (a.getYear() == b.getYear());
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/app/src/main/java/com/moac/android/refuge/search/CountrySuggestionProvider.java:
--------------------------------------------------------------------------------
1 | package com.moac.android.refuge.search;
2 |
3 | import android.app.SearchManager;
4 | import android.content.ContentProvider;
5 | import android.content.ContentValues;
6 | import android.content.UriMatcher;
7 | import android.database.Cursor;
8 | import android.net.Uri;
9 | import android.provider.ContactsContract;
10 | import android.util.Log;
11 |
12 | import java.util.Arrays;
13 |
14 | public class CountrySuggestionProvider extends ContentProvider {
15 |
16 | public static String TAG = CountrySuggestionProvider.class.getSimpleName();
17 |
18 | /*
19 | * Authority must match searchable.xml and Provider in AndroidManifest.xml
20 | */
21 | public static final String AUTHORITY = "com.moac.android.refuge.country";
22 |
23 | public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/search");
24 |
25 | // UriMatcher constant for search suggestions
26 | private static final int SEARCH_SUGGEST = 1;
27 |
28 | private static final UriMatcher uriMatcher;
29 |
30 | private static final String[] SEARCH_SUGGESTIONS_COLUMNS = {
31 | "_id",
32 | SearchManager.SUGGEST_COLUMN_TEXT_1,
33 | };
34 |
35 | static {
36 | uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
37 | uriMatcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, SEARCH_SUGGEST);
38 | uriMatcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", SEARCH_SUGGEST);
39 | }
40 |
41 | private static final String KEY_SEARCH_COLUMN = ContactsContract.Contacts.DISPLAY_NAME;
42 |
43 | @Override
44 | public boolean onCreate() {
45 | return true;
46 | }
47 |
48 | @Override
49 | public String getType(Uri uri) {
50 | switch (uriMatcher.match(uri)) {
51 | case SEARCH_SUGGEST:
52 | return SearchManager.SUGGEST_MIME_TYPE;
53 | default:
54 | throw new IllegalArgumentException("Unknown URL " + uri);
55 | }
56 | }
57 |
58 | @Override
59 | public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
60 | String sortOrder) {
61 |
62 | Log.i(TAG, "query() - uri:" + uri);
63 |
64 | // Use the UriMatcher to see what kind of query we have
65 | switch (uriMatcher.match(uri)) {
66 | case SEARCH_SUGGEST:
67 | Log.i(TAG, "query() - matched SEARCH_SUGGEST");
68 |
69 | // The search term (perhaps partial) of interest
70 | // See com.android.globalsearch.SuggestionProvider
71 | String query;
72 | if (uri.getPathSegments().size() > 1) {
73 | query = uri.getLastPathSegment().toLowerCase();
74 | } else {
75 | query = "";
76 | }
77 |
78 | // Get a suggestions cursor from the Contacts Content Provider
79 | // These calls effectively replicate the calls made by the SearchManager, without the baggage of the framework.
80 | Uri suggestionsBaseUri = Uri.withAppendedPath(ContactsContract.AUTHORITY_URI, SearchManager.SUGGEST_URI_PATH_QUERY);
81 | Uri suggestionsQueryUri = Uri.withAppendedPath(suggestionsBaseUri, query);
82 | Cursor countryCursor = getContext().getContentResolver().query(suggestionsQueryUri, null, null, null, null);
83 | Log.i(TAG, "query() - cursor length:" + countryCursor.getCount());
84 | Log.i(TAG, "query() - cursor column names:" + Arrays.toString(countryCursor.getColumnNames()));
85 | return countryCursor;
86 | default:
87 | throw new IllegalArgumentException("Unknown Uri: " + uri);
88 | }
89 | }
90 |
91 | @Override
92 | public int delete(Uri uri, String arg1, String[] arg2) {
93 | throw new UnsupportedOperationException();
94 | }
95 |
96 | @Override
97 | public int update(Uri uri, ContentValues arg1, String arg2, String[] arg3) {
98 | throw new UnsupportedOperationException();
99 | }
100 |
101 | @Override
102 | public Uri insert(Uri uri, ContentValues arg1) {
103 | throw new UnsupportedOperationException();
104 | }
105 |
106 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/moac/android/refuge/database/MockRefugeeDataStore.java:
--------------------------------------------------------------------------------
1 | package com.moac.android.refuge.database;
2 |
3 | import com.moac.android.refuge.model.persistent.Country;
4 | import com.moac.android.refuge.model.persistent.Demography;
5 | import com.moac.android.refuge.model.persistent.RefugeeFlow;
6 |
7 | import java.util.ArrayList;
8 | import java.util.List;
9 |
10 | public class MockRefugeeDataStore implements RefugeeDataStore {
11 |
12 | List countries;
13 | List flows;
14 |
15 | public MockRefugeeDataStore(List countrys,
16 | List fLows) {
17 | countries = countrys;
18 | flows = fLows;
19 | }
20 |
21 | /*
22 | * Country
23 | */
24 |
25 | @Override
26 | public List getAllCountries() {
27 | throw new UnsupportedOperationException();
28 | }
29 |
30 | @Override
31 | public Country getCountry(long id) {
32 | for (Country c : countries) {
33 | if (c.getId() == id) {
34 | return c;
35 | }
36 | }
37 | return null;
38 | }
39 |
40 | @Override
41 | public long createCountry(Country country) {
42 | throw new UnsupportedOperationException();
43 | }
44 |
45 | @Override
46 | public void updateCountry(Country country) {
47 | throw new UnsupportedOperationException();
48 | }
49 |
50 | @Override
51 | public void deleteCountry(long id) {
52 | throw new UnsupportedOperationException();
53 | }
54 |
55 | /*
56 | * Demography
57 | */
58 |
59 | @Override
60 | public List getAllDemographics() {
61 | throw new UnsupportedOperationException();
62 | }
63 |
64 | @Override
65 | public Demography getDemography(long id) {
66 | throw new UnsupportedOperationException();
67 | }
68 |
69 | @Override
70 | public long createDemography(Demography demography) {
71 | throw new UnsupportedOperationException();
72 | }
73 |
74 | @Override
75 | public void updateDemography(Demography demography) {
76 | throw new UnsupportedOperationException();
77 | }
78 |
79 | @Override
80 | public void deleteDemography(long id) {
81 | throw new UnsupportedOperationException();
82 | }
83 |
84 | /*
85 | * Refugee Flow
86 | */
87 |
88 | @Override
89 | public List getAllRefugeeFlows() {
90 | throw new UnsupportedOperationException();
91 | }
92 |
93 | @Override
94 | public RefugeeFlow getRefugeeFlow(long id) {
95 | throw new UnsupportedOperationException();
96 | }
97 |
98 | @Override
99 | public long createRefugeeFlow(RefugeeFlow refugeeFlow) {
100 | throw new UnsupportedOperationException();
101 | }
102 |
103 | @Override
104 | public void updateRefugeeFlow(RefugeeFlow refugeeFlow) {
105 | throw new UnsupportedOperationException();
106 | }
107 |
108 | @Override
109 | public void deleteRefugeeFlow(long id) {
110 | throw new UnsupportedOperationException();
111 | }
112 |
113 | @Override
114 | public long getTotalRefugeeFlowTo(long toCountryId) {
115 | long totalCount = 0;
116 | for (RefugeeFlow flow : flows) {
117 | if (flow.getToCountry().getId() == toCountryId) {
118 | totalCount += flow.getRefugeeCount();
119 | }
120 | }
121 | return totalCount;
122 | }
123 |
124 | @Override
125 | public long getTotalRefugeeFlowFrom(long countryId) {
126 | throw new UnsupportedOperationException();
127 | }
128 |
129 | @Override
130 | public List getRefugeeFlowsFrom(long countryId) {
131 | throw new UnsupportedOperationException();
132 | }
133 |
134 | @Override
135 | public List getRefugeeFlowsTo(long toCountryId) {
136 | List result = new ArrayList();
137 | for (RefugeeFlow flow : flows) {
138 | if (flow.getToCountry().getId() == toCountryId) {
139 | result.add(flow);
140 | }
141 | }
142 | return result;
143 | }
144 |
145 | @Override
146 | public Country getCountry(String countryName) {
147 | for (Country country : countries) {
148 | if (country.getName().equalsIgnoreCase(countryName)) {
149 | return country;
150 | }
151 | }
152 | return null;
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/app/src/main/java/com/moac/android/refuge/inject/DebugAppModule.java:
--------------------------------------------------------------------------------
1 | package com.moac.android.refuge.inject;
2 |
3 | import android.util.Log;
4 |
5 | import com.moac.android.refuge.RefugeApplication;
6 | import com.moac.android.refuge.activity.MainActivity;
7 | import com.moac.android.refuge.database.MockRefugeeDataStore;
8 | import com.moac.android.refuge.database.RefugeeDataStore;
9 | import com.moac.android.refuge.fragment.NavigationDrawerFragment;
10 | import com.moac.android.refuge.importer.ImportService;
11 | import com.moac.android.refuge.model.CountriesModel;
12 | import com.moac.android.refuge.model.persistent.Country;
13 | import com.moac.android.refuge.model.persistent.RefugeeFlow;
14 |
15 | import java.util.ArrayList;
16 | import java.util.List;
17 |
18 | import javax.inject.Singleton;
19 |
20 | import dagger.Module;
21 | import dagger.Provides;
22 |
23 | @Module(injects = {RefugeApplication.class,
24 | MainActivity.class,
25 | NavigationDrawerFragment.class,
26 | ImportService.class})
27 | public class DebugAppModule {
28 | private static final String TAG = DebugAppModule.class.getSimpleName();
29 |
30 | private final RefugeApplication application;
31 |
32 | public DebugAppModule(RefugeApplication application) {
33 | this.application = application;
34 | }
35 |
36 | @Provides @Singleton RefugeeDataStore provideDatabase() {
37 | Log.i(TAG, "Providing debug database");
38 | return makeMock();
39 | }
40 |
41 | @Provides @Singleton CountriesModel provideCountriesModel() {
42 | return new CountriesModel();
43 | }
44 |
45 | private MockRefugeeDataStore makeMock() {
46 | List countryList = new ArrayList();
47 | List refugeeFlowList = new ArrayList();
48 |
49 | Country au = new Country("Australia", -27d, 133d);
50 | au.setId(0);
51 | countryList.add(au);
52 |
53 | Country af = new Country("Afghanistan", 33d, 65d);
54 | af.setId(1);
55 | countryList.add(af);
56 |
57 | Country iq = new Country("Iraq", 33d, 45d);
58 | iq.setId(2);
59 | countryList.add(iq);
60 |
61 | Country sy = new Country("Syria", 30d, 40d);
62 | sy.setId(3);
63 | countryList.add(sy);
64 |
65 | Country sl = new Country("Sri Lanka", 15d, 80d);
66 | sl.setId(4);
67 | countryList.add(sl);
68 |
69 | Country sw = new Country("Sweden", 59.3294, 18.0686);
70 | sw.setId(5);
71 | countryList.add(sw);
72 |
73 | Country es = new Country("Spain", 40d, -5d);
74 | es.setId(6);
75 | countryList.add(es);
76 |
77 | Country mx = new Country("Mexico", 15d, -80d);
78 | mx.setId(7);
79 | countryList.add(mx);
80 |
81 | Country cn = new Country("China", 35d, 110d);
82 | cn.setId(8);
83 | countryList.add(cn);
84 |
85 | // AU
86 | {
87 | RefugeeFlow af2au = new RefugeeFlow(af, au);
88 | af2au.setRefugeeCount(1000);
89 | af2au.setYear(2012);
90 | refugeeFlowList.add(af2au);
91 |
92 | RefugeeFlow iq2au = new RefugeeFlow(iq, au);
93 | iq2au.setRefugeeCount(750);
94 | iq2au.setYear(2012);
95 | refugeeFlowList.add(iq2au);
96 |
97 | RefugeeFlow sy2au = new RefugeeFlow(sy, au);
98 | sy2au.setRefugeeCount(550);
99 | sy2au.setYear(2012);
100 | refugeeFlowList.add(sy2au);
101 |
102 | RefugeeFlow sl2au = new RefugeeFlow(sl, au);
103 | sl2au.setRefugeeCount(250);
104 | sl2au.setYear(2012);
105 | refugeeFlowList.add(sl2au);
106 |
107 | RefugeeFlow s22au = new RefugeeFlow(sw, au);
108 | s22au.setRefugeeCount(1500);
109 | s22au.setYear(2012);
110 | refugeeFlowList.add(s22au);
111 | }
112 |
113 | // ES
114 | {
115 | RefugeeFlow af2es = new RefugeeFlow(af, es);
116 | af2es.setRefugeeCount(1000);
117 | af2es.setYear(2012);
118 | refugeeFlowList.add(af2es);
119 |
120 | RefugeeFlow iqes = new RefugeeFlow(iq, es);
121 | iqes.setRefugeeCount(2000);
122 | iqes.setYear(2012);
123 | refugeeFlowList.add(iqes);
124 |
125 | RefugeeFlow sl2es = new RefugeeFlow(sl, es);
126 | sl2es.setRefugeeCount(250);
127 | sl2es.setYear(2012);
128 | refugeeFlowList.add(sl2es);
129 |
130 | RefugeeFlow cn2es = new RefugeeFlow(cn, es);
131 | cn2es.setRefugeeCount(800);
132 | cn2es.setYear(2012);
133 | refugeeFlowList.add(cn2es);
134 |
135 | RefugeeFlow mx2es = new RefugeeFlow(mx, es);
136 | mx2es.setRefugeeCount(1200);
137 | mx2es.setYear(2012);
138 | refugeeFlowList.add(mx2es);
139 | }
140 |
141 | return new MockRefugeeDataStore(countryList, refugeeFlowList);
142 | }
143 |
144 | }
145 |
146 |
--------------------------------------------------------------------------------
/app/src/main/java/com/moac/android/refuge/util/Visualizer.java:
--------------------------------------------------------------------------------
1 | package com.moac.android.refuge.util;
2 |
3 | import android.graphics.Color;
4 | import android.util.Log;
5 |
6 | import com.google.android.gms.maps.GoogleMap;
7 | import com.google.android.gms.maps.model.Circle;
8 | import com.google.android.gms.maps.model.CircleOptions;
9 | import com.google.android.gms.maps.model.LatLng;
10 | import com.moac.android.refuge.database.RefugeeDataStore;
11 | import com.moac.android.refuge.model.DisplayedCountry;
12 | import com.moac.android.refuge.model.persistent.Country;
13 | import com.moac.android.refuge.model.persistent.RefugeeFlow;
14 |
15 | import java.util.List;
16 |
17 | public class Visualizer {
18 |
19 | private static final String TAG = Visualizer.class.getSimpleName();
20 |
21 | private static final long MAX_RADIUS = 1500000; // 1500 kms
22 | private static final double MAX_AREA = Math.PI * MAX_RADIUS * MAX_RADIUS;
23 | private static final int MAGICAL_ALPHA_OFFSET = 120;
24 | private static final int NO_FILL_COLOR = 0;
25 |
26 | /*
27 | * Draw all circles flow in/out for a specific country
28 | */
29 | public static void drawCountries(RefugeeDataStore refugeeDataStore,
30 | GoogleMap map,
31 | List countries,
32 | long maxFlow) {
33 | Log.d(TAG, "drawCountries() - Draw flows for into countries: " + countries);
34 |
35 | for (DisplayedCountry country : countries) {
36 | int strokeColor = country.getColor();
37 | int fillColor = toFillColor(strokeColor);
38 |
39 | // Draw all the circle for outgoing refugees
40 | drawFromCircles(refugeeDataStore, map, country.getId(), strokeColor, maxFlow);
41 |
42 | // Draw single circle for intake
43 | drawToCircle(refugeeDataStore,
44 | map,
45 | country.getId(),
46 | strokeColor,
47 | fillColor,
48 | (refugeeDataStore.getTotalRefugeeFlowTo(country.getId()) / (double)maxFlow));
49 | }
50 | Log.d(TAG, "drawCountries() - Using: " + maxFlow);
51 | }
52 |
53 | /*
54 | * Draws all circles for outgoing refugees to a specific country
55 | */
56 | private static void drawFromCircles(RefugeeDataStore refugeeDataStore, GoogleMap map, long toCountryId, int strokeColor, long maxCount) {
57 | Log.d(TAG, "drawFromCircles() - toCountryId: " + toCountryId + " toCountryColor: " + strokeColor + " maxFlow: " + maxCount);
58 | List flows = refugeeDataStore.getRefugeeFlowsTo(toCountryId);
59 | for (RefugeeFlow flow : flows) {
60 | Country fromCountry = refugeeDataStore.getCountry(flow.getFromCountry().getId());
61 | Log.d(TAG, "drawFromCircles() - Drawing flow from: " + fromCountry.getName() + " count: " + flow.getRefugeeCount() + " / " + maxCount);
62 | drawScaledCircle(map, fromCountry.getLatLng(), (flow.getRefugeeCount() / (double)maxCount), strokeColor, NO_FILL_COLOR); // no fill
63 | }
64 | }
65 |
66 | /*
67 | * Draws a single circles from the intake to a specific country
68 | */
69 | private static void drawToCircle(RefugeeDataStore refugeeDataStore, GoogleMap map, long toCountryId, int strokeColor, int fillColor, double percent) {
70 | Country toCountry = refugeeDataStore.getCountry(toCountryId);
71 | drawScaledCircle(map, toCountry.getLatLng(), percent, strokeColor, fillColor);
72 | }
73 |
74 | /*
75 | * Draws a general circle shape with provided stroke & fill colors
76 | */
77 | private static Circle drawScaledCircle(GoogleMap map,
78 | LatLng coordinates,
79 | double percent,
80 | int strokeColor,
81 | int fillColor) {
82 | Log.d(TAG, "drawScaledCircle() - percent: " + percent);
83 | double circleArea = percent * MAX_AREA;
84 | double radius = Math.sqrt(circleArea / Math.PI);
85 | Log.d(TAG, "drawScaledCircle() - radius (m): " + radius + " circleArea: " + circleArea + " percent: " + percent + " max Area: " + MAX_AREA);
86 | CircleOptions circleOptions = new CircleOptions()
87 | .center(coordinates)
88 | .radius(radius)
89 | .fillColor(fillColor)
90 | .strokeColor(strokeColor)
91 | .strokeWidth(5);
92 | return map.addCircle(circleOptions);
93 | }
94 |
95 | /*
96 | * Generate an appropriate fill color for a given stroke color
97 | */
98 | private static int toFillColor(int strokeColor) {
99 | return Color.argb(Color.alpha(strokeColor) + MAGICAL_ALPHA_OFFSET,
100 | Color.red(strokeColor),
101 | Color.green(strokeColor),
102 | Color.blue(strokeColor));
103 | }
104 | }
105 |
106 |
--------------------------------------------------------------------------------
/app/src/main/java/com/moac/android/refuge/database/DatabaseHelper.java:
--------------------------------------------------------------------------------
1 | package com.moac.android.refuge.database;
2 |
3 | import android.content.Context;
4 | import android.database.SQLException;
5 | import android.database.sqlite.SQLiteDatabase;
6 |
7 | import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper;
8 | import com.j256.ormlite.dao.Dao;
9 | import com.j256.ormlite.support.ConnectionSource;
10 | import com.j256.ormlite.table.DatabaseTableConfig;
11 | import com.j256.ormlite.table.TableUtils;
12 | import com.moac.android.refuge.model.persistent.Country;
13 | import com.moac.android.refuge.model.persistent.Demography;
14 | import com.moac.android.refuge.model.persistent.PersistableObject;
15 | import com.moac.android.refuge.model.persistent.RefugeeFlow;
16 |
17 | import java.util.HashMap;
18 | import java.util.List;
19 | import java.util.Map;
20 |
21 | public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
22 |
23 | private static final String TAG = DatabaseHelper.class.getSimpleName();
24 |
25 | private static final String DATABASE_NAME = "refuge.db";
26 | private static final int DATABASE_VERSION = 1;
27 |
28 | protected Class[] PERSISTABLE_OBJECTS;
29 |
30 | private final Map, Dao extends PersistableObject, Long>> daos =
31 | new HashMap, Dao extends PersistableObject, Long>>();
32 |
33 | private final Map, DatabaseTableConfig extends PersistableObject>> tableConfigs =
34 | new HashMap, DatabaseTableConfig extends PersistableObject>>();
35 |
36 | public DatabaseHelper(Context ctx) {
37 | super(ctx, DATABASE_NAME, null, DATABASE_VERSION);
38 | PERSISTABLE_OBJECTS = new Class[]{Country.class, Demography.class, RefugeeFlow.class};
39 | }
40 |
41 | protected DatabaseHelper(Context context, String databaseName) {
42 | super(context, databaseName, null, DATABASE_VERSION);
43 | }
44 |
45 | @Override
46 | public void onCreate(SQLiteDatabase db, ConnectionSource cs) {
47 | createTables(db, cs);
48 | }
49 |
50 | @Override
51 | public void onUpgrade(SQLiteDatabase sqLiteDatabase, ConnectionSource connectionSource, int i, int i2) {
52 | // Do nothing
53 | }
54 |
55 | @Override
56 | public void onOpen(SQLiteDatabase db) {
57 | super.onOpen(db);
58 | if (!db.isReadOnly()) {
59 | // Enable foreign key constraints
60 | db.execSQL("PRAGMA foreign_keys=ON;");
61 | }
62 | }
63 |
64 | @Override
65 | public void close() {
66 | super.close();
67 | daos.clear();
68 | tableConfigs.clear();
69 | }
70 |
71 | @SuppressWarnings("unchecked")
72 | public Dao getDaoEx(Class objClass) {
73 | Dao result;
74 | if (daos.containsKey(objClass)) {
75 | result = (Dao) daos.get(objClass);
76 | } else {
77 | try {
78 | result = getDao(objClass);
79 | } catch (java.sql.SQLException e) {
80 | throw new SQLException(e.getMessage());
81 | }
82 | daos.put(objClass, result);
83 | }
84 | return result;
85 | }
86 |
87 | @SuppressWarnings("unchecked")
88 | private void createTables(SQLiteDatabase db, ConnectionSource cs) {
89 | for (Class extends PersistableObject> objClass : PERSISTABLE_OBJECTS) {
90 | createTable(objClass, cs);
91 | }
92 | }
93 |
94 | private void createTable(Class extends PersistableObject> objClass, ConnectionSource cs) {
95 | try {
96 | TableUtils.createTable(cs, objClass);
97 | } catch (java.sql.SQLException e) {
98 | throw new SQLException(e.getMessage());
99 | }
100 | }
101 |
102 | public List queryAll(Class objClass) {
103 | List entity;
104 | try {
105 | entity = getDaoEx(objClass).queryForAll();
106 | } catch (java.sql.SQLException e) {
107 | throw new SQLException(e.getMessage());
108 | }
109 | return entity;
110 | }
111 |
112 | public T queryById(long id, Class objClass) {
113 | T entity;
114 | try {
115 | entity = getDaoEx(objClass).queryForId(id);
116 | } catch (java.sql.SQLException e) {
117 | throw new SQLException(e.getMessage());
118 | }
119 | return entity;
120 | }
121 |
122 | public long create(PersistableObject entity, Class objClass) {
123 | long id = PersistableObject.UNSET_ID;
124 | try {
125 | if (getDaoEx(objClass).create(objClass.cast(entity)) == 1) {
126 | id = entity.getId();
127 | }
128 | } catch (java.sql.SQLException e) {
129 | throw new SQLException(e.getMessage());
130 | }
131 | return id;
132 | }
133 |
134 | public void update(PersistableObject entity, Class objClass) {
135 | try {
136 | int count = getDaoEx(objClass).update(objClass.cast(entity));
137 | } catch (java.sql.SQLException e) {
138 | throw new SQLException(e.getMessage());
139 | }
140 | }
141 |
142 | public void deleteById(long id, Class objClass) {
143 | try {
144 | int count = getDaoEx(objClass).deleteById(id);
145 | } catch (java.sql.SQLException e) {
146 | throw new SQLException(e.getMessage());
147 | }
148 | }
149 | }
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/gradle/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/app/src/main/assets/CountriesLatLong.csv:
--------------------------------------------------------------------------------
1 | ANDORRA,42.5,1.5
2 | AFGHANISTAN,33,65
3 | ANTIGUA AND BARBUDA,17.05,-61.8
4 | ANGUILLA,18.25,-63.1667
5 | ALBANIA,41,20
6 | ARMENIA,40,45
7 | ANGOLA,-12.5,18.5
8 | ANTARTICA,-90,0
9 | ARGENTINA,-34,-64
10 | AMERICAN SAMOA,-14.3333,-170
11 | AUSTRIA,47.3333,13.3333
12 | AUSTRALIA,-27,133
13 | ARUBA,12.5,-69.9667
14 | AZERBAIJAN,40.5,47.5
15 | BOSNIA AND HERZEGOVINA,44,18
16 | BARBADOS,13.1667,-59.5333
17 | BANGLADESH,24,90
18 | BELGIUM,50.8333,4
19 | BURKINA FASO,13,-2
20 | BULGARIA,43,25
21 | BAHRAIN,26,50.55
22 | BURUNDI,-3.5,30
23 | BENIN,9.5,2.25
24 | BERMUDA,32.3333,-64.75
25 | BRUNEI DARUSSALAM,4.5,114.6667
26 | BOLIVIA,-17,-65
27 | BRAZIL,-10,-55
28 | BAHAMAS,24.25,-76
29 | BHUTAN,27.5,90.5
30 | BOUVET ISLAND,-54.4333,3.4
31 | BOTSWANA,-22,24
32 | BELARUS,53,28
33 | BELIZE,17.25,-88.75
34 | CANADA,60,-95
35 | COCOS (KEELING) ISLANDS,-12.5,96.8333
36 | THE DEMOCRATIC REPUBLIC OF CONGO,0,25
37 | CENTRAL AFRICA,7,21
38 | CONGO,-1,15
39 | SWITZERLAND,47,8
40 | CÔTE D'IVOIRE,8,-5
41 | COOK ISLANDS,-21.2333,-159.7667
42 | CHILE,-30,-71
43 | CAMEROON,6,12
44 | CHINA,35,105
45 | COLOMBIA,4,-72
46 | COSTA RICA,10,-84
47 | CUBA,21.5,-80
48 | CAPE VERDE,16,-24
49 | CHRISTMAS ISLAND,-10.5,105.6667
50 | CYPRUS,35,33
51 | CZECH REPUBLIC,49.75,15.5
52 | GERMANY,51,9
53 | DJIBOUTI,11.5,43
54 | DENMARK,56,10
55 | DOMINICA,15.4167,-61.3333
56 | DOMINICAN REPUBLIC,19,-70.6667
57 | ALGERIA,28,3
58 | ECUADOR,-2,-77.5
59 | ESTONIA,59,26
60 | EGYPT,27,30
61 | WESTERN SAHARA,24.5,-13
62 | ERITREA,15,39
63 | SPAIN,40,-4
64 | ETHIOPIA,8,38
65 | FINLAND,64,26
66 | FIJI,-18,175
67 | FALKLAND ISLANDS,-51.75,-59
68 | MICRONESIA,6.9167,158.25
69 | FAROE ISLANDS,62,-7
70 | FRANCE,46,2
71 | GABON,-1,11.75
72 | UNITED KINGDOM,54,-2
73 | GRENADA,12.1167,-61.6667
74 | GEORGIA,42,43.5
75 | FRENCH GUIANA,4,-53
76 | GHANA,8,-2
77 | GIBRALTAR,36.1833,-5.3667
78 | GREENLAND,72,-40
79 | GAMBIA,13.4667,-16.5667
80 | GUINEA,11,-10
81 | GUADELOUPE,16.25,-61.5833
82 | EQUATORIAL GUINEA,2,10
83 | GREECE,39,22
84 | SOUTH GEORGIA AND THE SOUTH SANDWICH ISLANDS,-54.5,-37
85 | GUATEMALA,15.5,-90.25
86 | GUAM,13.4667,144.7833
87 | GUINEA-BISSAU,12,-15
88 | GUYANA,5,-59
89 | HONG KONG,22.25,114.1667
90 | HEARD ISLAND AND MCDONALD ISLANDS,-53.1,72.5167
91 | HONDURAS,15,-86.5
92 | CROATIA,45.1667,15.5
93 | HAITI,19,-72.4167
94 | HUNGARY,47,20
95 | INDONESIA,-5,120
96 | IRELAND,53,-8
97 | ISRAEL,31.5,34.75
98 | INDIA,20,77
99 | IRAQ,33,44
100 | IRAN,32,53
101 | ICELAND,65,-18
102 | ITALY,42.8333,12.8333
103 | JAMAICA,18.25,-77.5
104 | JORDAN,31,36
105 | JAPAN,36,138
106 | KENYA,1,38
107 | KYRGYZSTAN,41,75
108 | CAMBODIA,13,105
109 | KIRIBATI,1.4167,173
110 | COMOROS,-12.1667,44.25
111 | SAINT KITTS AND NEVIS,17.3333,-62.75
112 | SAINT MARTIN, 18.06, -63.05
113 | SOUTH KOREA,40,127
114 | NORTH KOREA,37,127.5
115 | KUWAIT,29.3375,47.6581
116 | CAYMAN ISLANDS,19.5,-80.5
117 | KAZAKHSTAN,48,68
118 | LAOS,18,105
119 | LEBANON,33.8333,35.8333
120 | SAINT LUCIA,13.8833,-61.1333
121 | LIECHTENSTEIN,47.1667,9.5333
122 | SRI LANKA,7,81
123 | LIBERIA,6.5,-9.5
124 | LESOTHO,-29.5,28.5
125 | LITHUANIA,56,24
126 | LUXEMBOURG,49.75,6.1667
127 | LATVIA,57,25
128 | LIBYA,25,17
129 | MOROCCO,32,-5
130 | MONACO,43.7333,7.4
131 | MOLDOVA,47,29
132 | MONTENEGRO,42,19
133 | MADAGASCAR,-20,47
134 | MARSHALL ISLANDS,9,168
135 | MACEDONIA,41.8333,22
136 | MALI,17,-4
137 | MYANMAR,22,98
138 | MONGOLIA,46,105
139 | MACAO,22.1667,113.55
140 | NORTHERN MARIANA ISLANDS,15.2,145.75
141 | MARTINIQUE,14.6667,-61
142 | MAURITANIA,20,-12
143 | MONTSERRAT,16.75,-62.2
144 | MALTA,35.8333,14.5833
145 | MAURITIUS,-20.2833,57.55
146 | MALDIVES,3.25,73
147 | MALAWI,-13.5,34
148 | MEXICO,23,-102
149 | MALAYSIA,2.5,112.5
150 | MOZAMBIQUE,-18.25,35
151 | NAMIBIA,-22,17
152 | NEW CALEDONIA,-21.5,165.5
153 | NIGER,16,8
154 | NORFOLK ISLAND,-29.0333,167.95
155 | NIGERIA,10,8
156 | NICARAGUA,13,-85
157 | NETHERLANDS,52.5,5.75
158 | NORWAY,62,10
159 | NEPAL,28,84
160 | NAURU,-0.5333,166.9167
161 | NIUE,-19.0333,-169.8667
162 | NEW ZEALAND,-41,174
163 | OMAN,21,57
164 | PANAMA,9,-80
165 | PERU,-10,-76
166 | FRENCH POLYNESIA,-15,-140
167 | PAPUA NEW GUINEA,-6,147
168 | PHILIPPINES,13,122
169 | PAKISTAN,30,70
170 | POLAND,52,20
171 | SAINT PIERRE AND MIQUELON,46.8333,-56.3333
172 | PUERTO RICO,18.25,-66.5
173 | PALESTINE,32,35.25
174 | PORTUGAL,39.5,-8
175 | PALAU,7.5,134.5
176 | PARAGUAY,-23,-58
177 | QATAR,25.5,51.25
178 | REUNION,-21.1,55.6
179 | ROMANIA,46,25
180 | SERBIA,44,21
181 | RUSSIAN FEDERATION,60,100
182 | RWANDA,-2,30
183 | SAUDI ARABIA,25,45
184 | SOLOMON ISLANDS,-8,159
185 | SEYCHELLES,-4.5833,55.6667
186 | SUDAN,15,30
187 | SOUTH SUDAN,6.98,30.45
188 | SWEDEN,62,15
189 | SINGAPORE,1.3667,103.8
190 | SLOVENIA,46,15
191 | SVALBARD AND JAN MAYEN,78,20
192 | SLOVAKIA,48.6667,19.5
193 | SIERRA LEONE,8.5,-11.5
194 | SAN MARINO,43.7667,12.4167
195 | SENEGAL,14,-14
196 | SOMALIA,10,49
197 | SURINAME,4,-56
198 | SAO TOME AND PRINCIPE,1,7
199 | SYRIA,35,38
200 | SWAZILAND,-26.5,31.5
201 | TURKS AND CAICOS ISLANDS,21.75,-71.5833
202 | CHAD,15,19
203 | TOGO,8,1.1667
204 | THAILAND,15,100
205 | TAJIKISTAN,39,71
206 | TOKELAU,-9,-172
207 | TURKMENISTAN,40,60
208 | TUNISIA,34,9
209 | TONGA,-20,-175
210 | TURKEY,39,35
211 | TRINIDAD AND TOBAGO,11,-61
212 | TUVALU,-8,178
213 | TAIWAN,23.5,121
214 | TANZANIA,-6,35
215 | UKRAINE,49,32
216 | UGANDA,1,32
217 | UNITED STATES MINOR OUTLYING ISLANDS,19.2833,166.6
218 | UNITED STATES,38,-97
219 | URUGUAY,-33,-56
220 | UZBEKISTAN,41,64
221 | VATICAN,41.9,12.45
222 | SAINT VINCENT AND THE GRENADINES,13.25,-61.2
223 | VENEZUELA,8,-66
224 | BRITISH VIRGIN ISLANDS,18.5,-64.5
225 | VIRGIN ISLANDS US,18.3333,-64.8333
226 | VIETNAM,16,106
227 | VANUATU,-16,167
228 | WALLIS AND FUTUNA,-13.3,-176.2
229 | SAMOA,-13.5833,-172.3333
230 | YEMEN,15,48
231 | MAYOTTE,-12.8333,45.1667
232 | SOUTH AFRICA,-29,24
233 | ZAMBIA,-15,30
234 | ZIMBABWE,-20,30
235 | STATELESS,0,0
236 | VARIOUS,0,0
237 | UNITED ARAB EMIRATES, 23.7, 54
238 | EL SALVADOR, 13.6, 88.8
239 | EAST TIMOR, -8.5, 125.5
240 | CURACAO, 12.18, -69
241 | TIBET, 31.7, 86.94
--------------------------------------------------------------------------------
/app/src/androidTest/assets/CountriesLatLong.csv:
--------------------------------------------------------------------------------
1 | ANDORRA,42.5,1.5
2 | AFGHANISTAN,33,65
3 | ANTIGUA AND BARBUDA,17.05,-61.8
4 | ANGUILLA,18.25,-63.1667
5 | ALBANIA,41,20
6 | ARMENIA,40,45
7 | ANGOLA,-12.5,18.5
8 | ANTARTICA,-90,0
9 | ARGENTINA,-34,-64
10 | AMERICAN SAMOA,-14.3333,-170
11 | AUSTRIA,47.3333,13.3333
12 | AUSTRALIA,-27,133
13 | ARUBA,12.5,-69.9667
14 | AZERBAIJAN,40.5,47.5
15 | BOSNIA AND HERZEGOVINA,44,18
16 | BARBADOS,13.1667,-59.5333
17 | BANGLADESH,24,90
18 | BELGIUM,50.8333,4
19 | BURKINA FASO,13,-2
20 | BULGARIA,43,25
21 | BAHRAIN,26,50.55
22 | BURUNDI,-3.5,30
23 | BENIN,9.5,2.25
24 | BERMUDA,32.3333,-64.75
25 | BRUNEI DARUSSALAM,4.5,114.6667
26 | BOLIVIA,-17,-65
27 | BRAZIL,-10,-55
28 | BAHAMAS,24.25,-76
29 | BHUTAN,27.5,90.5
30 | BOUVET ISLAND,-54.4333,3.4
31 | BOTSWANA,-22,24
32 | BELARUS,53,28
33 | BELIZE,17.25,-88.75
34 | CANADA,60,-95
35 | COCOS (KEELING) ISLANDS,-12.5,96.8333
36 | THE DEMOCRATIC REPUBLIC OF CONGO,0,25
37 | CENTRAL AFRICA,7,21
38 | CONGO,-1,15
39 | SWITZERLAND,47,8
40 | CÔTE D'IVOIRE,8,-5
41 | COOK ISLANDS,-21.2333,-159.7667
42 | CHILE,-30,-71
43 | CAMEROON,6,12
44 | CHINA,35,105
45 | COLOMBIA,4,-72
46 | COSTA RICA,10,-84
47 | CUBA,21.5,-80
48 | CAPE VERDE,16,-24
49 | CHRISTMAS ISLAND,-10.5,105.6667
50 | CYPRUS,35,33
51 | CZECH REPUBLIC,49.75,15.5
52 | GERMANY,51,9
53 | DJIBOUTI,11.5,43
54 | DENMARK,56,10
55 | DOMINICA,15.4167,-61.3333
56 | DOMINICAN REPUBLIC,19,-70.6667
57 | ALGERIA,28,3
58 | ECUADOR,-2,-77.5
59 | ESTONIA,59,26
60 | EGYPT,27,30
61 | WESTERN SAHARA,24.5,-13
62 | ERITREA,15,39
63 | SPAIN,40,-4
64 | ETHIOPIA,8,38
65 | FINLAND,64,26
66 | FIJI,-18,175
67 | FALKLAND ISLANDS,-51.75,-59
68 | MICRONESIA,6.9167,158.25
69 | FAROE ISLANDS,62,-7
70 | FRANCE,46,2
71 | GABON,-1,11.75
72 | UNITED KINGDOM,54,-2
73 | GRENADA,12.1167,-61.6667
74 | GEORGIA,42,43.5
75 | FRENCH GUIANA,4,-53
76 | GHANA,8,-2
77 | GIBRALTAR,36.1833,-5.3667
78 | GREENLAND,72,-40
79 | GAMBIA,13.4667,-16.5667
80 | GUINEA,11,-10
81 | GUADELOUPE,16.25,-61.5833
82 | EQUATORIAL GUINEA,2,10
83 | GREECE,39,22
84 | SOUTH GEORGIA AND THE SOUTH SANDWICH ISLANDS,-54.5,-37
85 | GUATEMALA,15.5,-90.25
86 | GUAM,13.4667,144.7833
87 | GUINEA-BISSAU,12,-15
88 | GUYANA,5,-59
89 | HONG KONG,22.25,114.1667
90 | HEARD ISLAND AND MCDONALD ISLANDS,-53.1,72.5167
91 | HONDURAS,15,-86.5
92 | CROATIA,45.1667,15.5
93 | HAITI,19,-72.4167
94 | HUNGARY,47,20
95 | INDONESIA,-5,120
96 | IRELAND,53,-8
97 | ISRAEL,31.5,34.75
98 | INDIA,20,77
99 | IRAQ,33,44
100 | IRAN,32,53
101 | ICELAND,65,-18
102 | ITALY,42.8333,12.8333
103 | JAMAICA,18.25,-77.5
104 | JORDAN,31,36
105 | JAPAN,36,138
106 | KENYA,1,38
107 | KYRGYZSTAN,41,75
108 | CAMBODIA,13,105
109 | KIRIBATI,1.4167,173
110 | COMOROS,-12.1667,44.25
111 | SAINT KITTS AND NEVIS,17.3333,-62.75
112 | SAINT MARTIN, 18.06, -63.05
113 | SOUTH KOREA,40,127
114 | NORTH KOREA,37,127.5
115 | KUWAIT,29.3375,47.6581
116 | CAYMAN ISLANDS,19.5,-80.5
117 | KAZAKHSTAN,48,68
118 | LAOS,18,105
119 | LEBANON,33.8333,35.8333
120 | SAINT LUCIA,13.8833,-61.1333
121 | LIECHTENSTEIN,47.1667,9.5333
122 | SRI LANKA,7,81
123 | LIBERIA,6.5,-9.5
124 | LESOTHO,-29.5,28.5
125 | LITHUANIA,56,24
126 | LUXEMBOURG,49.75,6.1667
127 | LATVIA,57,25
128 | LIBYA,25,17
129 | MOROCCO,32,-5
130 | MONACO,43.7333,7.4
131 | MOLDOVA,47,29
132 | MONTENEGRO,42,19
133 | MADAGASCAR,-20,47
134 | MARSHALL ISLANDS,9,168
135 | MACEDONIA,41.8333,22
136 | MALI,17,-4
137 | MYANMAR,22,98
138 | MONGOLIA,46,105
139 | MACAO,22.1667,113.55
140 | NORTHERN MARIANA ISLANDS,15.2,145.75
141 | MARTINIQUE,14.6667,-61
142 | MAURITANIA,20,-12
143 | MONTSERRAT,16.75,-62.2
144 | MALTA,35.8333,14.5833
145 | MAURITIUS,-20.2833,57.55
146 | MALDIVES,3.25,73
147 | MALAWI,-13.5,34
148 | MEXICO,23,-102
149 | MALAYSIA,2.5,112.5
150 | MOZAMBIQUE,-18.25,35
151 | NAMIBIA,-22,17
152 | NEW CALEDONIA,-21.5,165.5
153 | NIGER,16,8
154 | NORFOLK ISLAND,-29.0333,167.95
155 | NIGERIA,10,8
156 | NICARAGUA,13,-85
157 | NETHERLANDS,52.5,5.75
158 | NORWAY,62,10
159 | NEPAL,28,84
160 | NAURU,-0.5333,166.9167
161 | NIUE,-19.0333,-169.8667
162 | NEW ZEALAND,-41,174
163 | OMAN,21,57
164 | PANAMA,9,-80
165 | PERU,-10,-76
166 | FRENCH POLYNESIA,-15,-140
167 | PAPUA NEW GUINEA,-6,147
168 | PHILIPPINES,13,122
169 | PAKISTAN,30,70
170 | POLAND,52,20
171 | SAINT PIERRE AND MIQUELON,46.8333,-56.3333
172 | PUERTO RICO,18.25,-66.5
173 | PALESTINE,32,35.25
174 | PORTUGAL,39.5,-8
175 | PALAU,7.5,134.5
176 | PARAGUAY,-23,-58
177 | QATAR,25.5,51.25
178 | REUNION,-21.1,55.6
179 | ROMANIA,46,25
180 | SERBIA,44,21
181 | RUSSIAN FEDERATION,60,100
182 | RWANDA,-2,30
183 | SAUDI ARABIA,25,45
184 | SOLOMON ISLANDS,-8,159
185 | SEYCHELLES,-4.5833,55.6667
186 | SUDAN,15,30
187 | SOUTH SUDAN,6.98,30.45
188 | SWEDEN,62,15
189 | SINGAPORE,1.3667,103.8
190 | SLOVENIA,46,15
191 | SVALBARD AND JAN MAYEN,78,20
192 | SLOVAKIA,48.6667,19.5
193 | SIERRA LEONE,8.5,-11.5
194 | SAN MARINO,43.7667,12.4167
195 | SENEGAL,14,-14
196 | SOMALIA,10,49
197 | SURINAME,4,-56
198 | SAO TOME AND PRINCIPE,1,7
199 | SYRIA,35,38
200 | SWAZILAND,-26.5,31.5
201 | TURKS AND CAICOS ISLANDS,21.75,-71.5833
202 | CHAD,15,19
203 | TOGO,8,1.1667
204 | THAILAND,15,100
205 | TAJIKISTAN,39,71
206 | TOKELAU,-9,-172
207 | TURKMENISTAN,40,60
208 | TUNISIA,34,9
209 | TONGA,-20,-175
210 | TURKEY,39,35
211 | TRINIDAD AND TOBAGO,11,-61
212 | TUVALU,-8,178
213 | TAIWAN,23.5,121
214 | TANZANIA,-6,35
215 | UKRAINE,49,32
216 | UGANDA,1,32
217 | UNITED STATES MINOR OUTLYING ISLANDS,19.2833,166.6
218 | UNITED STATES,38,-97
219 | URUGUAY,-33,-56
220 | UZBEKISTAN,41,64
221 | VATICAN,41.9,12.45
222 | SAINT VINCENT AND THE GRENADINES,13.25,-61.2
223 | VENEZUELA,8,-66
224 | BRITISH VIRGIN ISLANDS,18.5,-64.5
225 | VIRGIN ISLANDS US,18.3333,-64.8333
226 | VIETNAM,16,106
227 | VANUATU,-16,167
228 | WALLIS AND FUTUNA,-13.3,-176.2
229 | SAMOA,-13.5833,-172.3333
230 | YEMEN,15,48
231 | MAYOTTE,-12.8333,45.1667
232 | SOUTH AFRICA,-29,24
233 | ZAMBIA,-15,30
234 | ZIMBABWE,-20,30
235 | STATELESS,0,0
236 | VARIOUS,0,0
237 | UNITED ARAB EMIRATES, 23.7, 54
238 | EL SALVADOR, 13.6, 88.8
239 | EAST TIMOR, -8.5, 125.5
240 | CURACAO, 12.18, -69
241 | TIBET, 31.7, 86.94
--------------------------------------------------------------------------------
/app/src/main/java/com/moac/android/refuge/importer/DataFileImporter.java:
--------------------------------------------------------------------------------
1 | package com.moac.android.refuge.importer;
2 |
3 | import com.moac.android.refuge.database.RefugeeDataStore;
4 | import com.moac.android.refuge.model.persistent.Country;
5 | import com.moac.android.refuge.model.persistent.RefugeeFlow;
6 |
7 | import org.w3c.dom.Document;
8 | import org.w3c.dom.Element;
9 | import org.w3c.dom.Node;
10 | import org.w3c.dom.NodeList;
11 | import org.xml.sax.SAXException;
12 |
13 | import java.io.BufferedReader;
14 | import java.io.IOException;
15 | import java.io.InputStream;
16 | import java.io.InputStreamReader;
17 | import java.util.HashMap;
18 |
19 | import javax.xml.parsers.DocumentBuilder;
20 | import javax.xml.parsers.DocumentBuilderFactory;
21 | import javax.xml.parsers.ParserConfigurationException;
22 |
23 | public class DataFileImporter {
24 |
25 | static final String TAG = DataFileImporter.class.getSimpleName();
26 |
27 | static final String NAME_ATTRIBUTE_TAG = "name";
28 | static final String FROM_COUNTRY_TAG = "Country or territory of origin";
29 | static final String TO_COUNTRY_TAG = "Country or territory of asylum or residence";
30 | static final String YEAR_TAG = "Year";
31 | static final String REFUGEE_NUM_TAG = "Refugees*";
32 |
33 | public RefugeeFlow refugeeFlow;
34 | private RefugeeDataStore refugeeDataStore;
35 | private HashMap countriesMap;
36 |
37 | public DataFileImporter(RefugeeDataStore refugeeDataStore) {
38 | this.refugeeDataStore = refugeeDataStore;
39 | countriesMap = new HashMap<>();
40 | }
41 |
42 | public void parse(InputStream UNDataInputStream, InputStream countriesLatLongInputStream) throws FileImportException {
43 |
44 | try {
45 |
46 | parseCountriesLatLong(countriesLatLongInputStream);
47 | DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
48 | DocumentBuilder builder = factory.newDocumentBuilder();
49 | Document document = builder.parse(UNDataInputStream);
50 |
51 | Country fromCountry;
52 | Country toCountry;
53 | int year;
54 | long refugeeNum;
55 |
56 | NodeList nodeList = document.getDocumentElement().getChildNodes();
57 |
58 | for (int i = 0; i < nodeList.getLength(); i++) {
59 |
60 | //We have encountered an tag, let's reset everything to make sure
61 | fromCountry = toCountry = null;
62 | refugeeNum = -1;
63 | year = -1;
64 |
65 | Node recordNode = nodeList.item(i);
66 | if (recordNode instanceof Element) {
67 | NodeList childNodes = recordNode.getChildNodes();
68 | for (int j = 0; j < childNodes.getLength(); j++) {
69 | Node fieldNode = childNodes.item(j);
70 | if (fieldNode instanceof Element) {
71 | String fieldNameValue = fieldNode.getAttributes().getNamedItem(NAME_ATTRIBUTE_TAG).getNodeValue();
72 | switch (fieldNameValue) {
73 | case FROM_COUNTRY_TAG: {
74 | String content = fieldNode.getLastChild().getTextContent().trim();
75 | fromCountry = getLatLong(content);
76 | break;
77 | }
78 | case TO_COUNTRY_TAG: {
79 | String content = fieldNode.getLastChild().getTextContent().trim();
80 | toCountry = getLatLong(content);
81 | break;
82 | }
83 | case YEAR_TAG: {
84 | String content = fieldNode.getLastChild().getTextContent().trim();
85 | year = Integer.parseInt(content);
86 | break;
87 | }
88 | case REFUGEE_NUM_TAG: {
89 | String content = fieldNode.getLastChild().getTextContent().trim();
90 | refugeeNum = Long.parseLong(content);
91 | break;
92 | }
93 | }
94 | }
95 | }
96 | // we have all our data for the Record node, let's build our RefugeeFlow and insert in DB
97 | refugeeFlow = new RefugeeFlow(fromCountry, toCountry);
98 | refugeeFlow.setYear(year);
99 | refugeeFlow.setRefugeeCount(refugeeNum);
100 | refugeeDataStore.createRefugeeFlow(refugeeFlow);
101 | }
102 | }
103 | } catch (ParserConfigurationException | SAXException | IOException e) {
104 | throw new FileImportException(e);
105 | }
106 | }
107 |
108 | private void parseCountriesLatLong(InputStream is) throws FileImportException, IOException {
109 | BufferedReader reader = new BufferedReader(new InputStreamReader(is));
110 |
111 | String line;
112 | while ((line = reader.readLine()) != null) {
113 | String[] rowData = line.split(",");
114 | Country country = new Country(rowData[0], Double.parseDouble(rowData[1]), Double.parseDouble(rowData[2]));
115 | countriesMap.put(rowData[0], country);
116 | refugeeDataStore.createCountry(country);
117 | }
118 | }
119 |
120 | private Country getLatLong(String country) throws FileImportException {
121 | country = country.toUpperCase();
122 | if (countriesMap.containsKey(country)) {
123 | return countriesMap.get(country);
124 | } else {
125 | throw new FileImportException(new Exception("Country not found: " + country));
126 | }
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/app/src/main/java/com/moac/android/refuge/database/PersistentRefugeeDataStore.java:
--------------------------------------------------------------------------------
1 | package com.moac.android.refuge.database;
2 |
3 | import android.util.Log;
4 |
5 | import com.j256.ormlite.dao.GenericRawResults;
6 | import com.j256.ormlite.stmt.SelectArg;
7 | import com.moac.android.refuge.model.persistent.Country;
8 | import com.moac.android.refuge.model.persistent.Demography;
9 | import com.moac.android.refuge.model.persistent.PersistableObject;
10 | import com.moac.android.refuge.model.persistent.RefugeeFlow;
11 |
12 | import java.sql.SQLException;
13 | import java.util.List;
14 |
15 | public class PersistentRefugeeDataStore implements RefugeeDataStore {
16 |
17 | static final String TAG = PersistentRefugeeDataStore.class.getSimpleName();
18 |
19 | private final DatabaseHelper dbHelper;
20 |
21 | public PersistentRefugeeDataStore(DatabaseHelper helper) {
22 | dbHelper = helper;
23 | }
24 |
25 | private List queryAll(Class objClass) {
26 | return dbHelper.queryAll(objClass);
27 | }
28 |
29 | private T queryById(long id, Class objClass) {
30 | return dbHelper.queryById(id, objClass);
31 | }
32 |
33 | @SuppressWarnings("unchecked")
34 | private T queryById(T obj) {
35 | return (T) dbHelper.queryById(obj.getId(), obj.getClass());
36 | }
37 |
38 | private long create(T obj) {
39 | return dbHelper.create(obj, obj.getClass());
40 | }
41 |
42 | private void update(T obj) {
43 | dbHelper.update(obj, obj.getClass());
44 | }
45 |
46 | private void delete(T obj) {
47 | dbHelper.deleteById(obj.getId(), obj.getClass());
48 | }
49 |
50 | private void delete(long id, Class objClass) {
51 | dbHelper.deleteById(id, objClass);
52 | }
53 |
54 | @Override
55 | public List getAllCountries() {
56 | return queryAll(Country.class);
57 | }
58 |
59 | @Override
60 | public Country getCountry(long id) {
61 | return queryById(id, Country.class);
62 | }
63 |
64 | @Override
65 | public long createCountry(Country country) {
66 | return create(country);
67 | }
68 |
69 | @Override
70 | public void updateCountry(Country country) {
71 | update(country);
72 | }
73 |
74 | @Override
75 | public void deleteCountry(long id) {
76 | delete(id, Country.class);
77 | }
78 |
79 | @Override
80 | public List getAllDemographics() {
81 | return queryAll(Demography.class);
82 | }
83 |
84 | @Override
85 | public Demography getDemography(long id) {
86 | return queryById(id, Demography.class);
87 | }
88 |
89 | @Override
90 | public long createDemography(Demography demography) {
91 | return create(demography);
92 | }
93 |
94 | @Override
95 | public void updateDemography(Demography demography) {
96 | update(demography);
97 | }
98 |
99 | @Override
100 | public void deleteDemography(long id) {
101 | delete(id, Demography.class);
102 | }
103 |
104 | @Override
105 | public List getAllRefugeeFlows() {
106 | return queryAll(RefugeeFlow.class);
107 | }
108 |
109 | @Override
110 | public RefugeeFlow getRefugeeFlow(long id) {
111 | return queryById(id, RefugeeFlow.class);
112 | }
113 |
114 | @Override
115 | public long createRefugeeFlow(RefugeeFlow refugeeFlow) {
116 | return create(refugeeFlow);
117 | }
118 |
119 | @Override
120 | public void updateRefugeeFlow(RefugeeFlow refugeeFlow) {
121 | update(refugeeFlow);
122 | }
123 |
124 | @Override
125 | public void deleteRefugeeFlow(long id) {
126 | delete(id, RefugeeFlow.class);
127 | }
128 |
129 | @Override
130 | public long getTotalRefugeeFlowTo(long countryId) {
131 | return queryTotalRefugeeFlowTo(countryId);
132 | }
133 |
134 | @Override
135 | public long getTotalRefugeeFlowFrom(long countryId) {
136 | return queryTotalRefugeeFlowFrom(countryId);
137 | }
138 |
139 | @Override
140 | public List getRefugeeFlowsFrom(long countryId) {
141 | return queryAllRefugeeFlowsFrom(countryId);
142 | }
143 |
144 | @Override
145 | public List getRefugeeFlowsTo(long countryId) {
146 | return queryAllRefugeeFlowsTo(countryId);
147 | }
148 |
149 | @Override
150 | public Country getCountry(String countryName) {
151 | try {
152 | // Use SelectArg to ensure values are properly escaped
153 | // Refer - http://ormlite.com/javadoc/ormlite-core/doc-files/ormlite_3.html#index-select-arguments
154 | SelectArg selectArg = new SelectArg();
155 | selectArg.setValue(countryName);
156 | return dbHelper.getDaoEx(Country.class).queryBuilder().where()
157 | .like(Country.Columns.NAME_COLUMN, selectArg)
158 | .queryForFirst();
159 | } catch (java.sql.SQLException e) {
160 | throw new android.database.SQLException(e.getMessage());
161 | }
162 | }
163 |
164 | /**
165 | * Bespoke queries - examples
166 | */
167 | private long queryTotalRefugeeFlowTo(long countryId) {
168 | String query = "select sum(" + RefugeeFlow.Columns.REFUGEE_COUNT_COLUMN + ") from " + RefugeeFlow.TABLE_NAME + " where " + RefugeeFlow.Columns.TO_COUNTRY_COLUMN + " = " + countryId;
169 | Log.d(TAG, query);
170 | GenericRawResults rawResults;
171 | try {
172 | rawResults = dbHelper.getDaoEx(RefugeeFlow.class).queryRaw(query);
173 | return getLongResult(rawResults.getResults());
174 | } catch (java.sql.SQLException e) {
175 | throw new android.database.SQLException(e.getMessage());
176 | }
177 | }
178 |
179 | private long queryTotalRefugeeFlowFrom(long countryId) {
180 | String query = "select sum(" + RefugeeFlow.Columns.REFUGEE_COUNT_COLUMN + ") from " + RefugeeFlow.TABLE_NAME + " where " + RefugeeFlow.Columns.FROM_COUNTRY_COLUMN + " = " + countryId;
181 | Log.d(TAG, query);
182 |
183 | GenericRawResults rawResults;
184 | try {
185 | rawResults = dbHelper.getDaoEx(RefugeeFlow.class).queryRaw(query);
186 | return getLongResult(rawResults.getResults());
187 | } catch (java.sql.SQLException e) {
188 | throw new android.database.SQLException(e.getMessage());
189 | }
190 | }
191 |
192 | private static long getLongResult(List results) {
193 | // the results array should have 1 value
194 | String[] resultArray = results.get(0);
195 | String result = resultArray[0];
196 | return result != null ? Long.parseLong(result) : 0;
197 | }
198 |
199 | private List queryAllRefugeeFlowsFrom(long countryId) {
200 | try {
201 | return dbHelper.getDaoEx(RefugeeFlow.class).queryBuilder()
202 | .where().eq(RefugeeFlow.Columns.FROM_COUNTRY_COLUMN, countryId)
203 | .query();
204 | } catch (SQLException e) {
205 | throw new android.database.SQLException(e.getMessage());
206 | }
207 | }
208 |
209 | private List queryAllRefugeeFlowsTo(long countryId) {
210 | try {
211 | return dbHelper.getDaoEx(RefugeeFlow.class).queryBuilder()
212 | .where().eq(RefugeeFlow.Columns.TO_COUNTRY_COLUMN, countryId)
213 | .query();
214 | } catch (SQLException e) {
215 | throw new android.database.SQLException(e.getMessage());
216 | }
217 | }
218 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/moac/android/refuge/fragment/NavigationDrawerFragment.java:
--------------------------------------------------------------------------------
1 | package com.moac.android.refuge.fragment;
2 |
3 | import android.app.Activity;
4 | import android.content.res.Configuration;
5 | import android.os.Bundle;
6 | import android.support.annotation.Nullable;
7 | import android.support.v4.view.GravityCompat;
8 | import android.support.v4.widget.DrawerLayout;
9 | import android.support.v7.app.ActionBarDrawerToggle;
10 | import android.support.v7.widget.Toolbar;
11 | import android.view.LayoutInflater;
12 | import android.view.View;
13 | import android.view.ViewGroup;
14 | import android.widget.AdapterView;
15 | import android.widget.ListView;
16 |
17 | import com.moac.android.refuge.R;
18 | import com.moac.android.refuge.RefugeApplication;
19 | import com.moac.android.refuge.adapter.CountryAdapter;
20 | import com.moac.android.refuge.adapter.CountryViewBinder;
21 | import com.moac.android.refuge.adapter.CountryViewModel;
22 | import com.moac.android.refuge.database.RefugeeDataStore;
23 | import com.moac.android.refuge.model.DisplayedCountry;
24 |
25 | import java.util.ArrayList;
26 | import java.util.Collections;
27 | import java.util.List;
28 |
29 | import rx.Observable;
30 | import rx.Subscription;
31 | import rx.android.schedulers.AndroidSchedulers;
32 | import rx.functions.Action1;
33 | import rx.functions.Func1;
34 | import rx.schedulers.Schedulers;
35 |
36 | public class NavigationDrawerFragment extends android.support.v4.app.Fragment {
37 |
38 | private static final String TAG = NavigationDrawerFragment.class.getSimpleName();
39 | private NavigationDrawerCallbacks callbacks;
40 |
41 | private ActionBarDrawerToggle drawerToggle;
42 | private View fragmentContainerView;
43 | private ListView drawerListView;
44 | private List viewModels = Collections.emptyList();
45 |
46 | private FragmentContainer fragmentContainer;
47 | private Subscription displayCountrySubscription;
48 |
49 | @Override
50 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
51 | Bundle savedInstanceState) {
52 |
53 | drawerListView = (ListView) inflater.inflate(
54 | R.layout.fragment_navigation_drawer, container, false);
55 | drawerListView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
56 | @Override
57 | public void onItemSelected(AdapterView> parent, View view, int position, long id) {
58 | callbacks.onCountryItemSelected(id, view.isSelected());
59 | }
60 |
61 | @Override
62 | public void onNothingSelected(AdapterView> parent) {
63 | // Nothing selected
64 | }
65 | });
66 |
67 | return drawerListView;
68 | }
69 |
70 | @Override
71 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
72 | super.onViewCreated(view, savedInstanceState);
73 | RefugeApplication.from(this).inject(this);
74 | }
75 |
76 | @Override
77 | public void onResume() {
78 | super.onResume();
79 | displayCountrySubscription = fragmentContainer.getDisplayedCountries()
80 | .map(new Func1, List>() {
81 | @Override
82 | public List call(List countries) {
83 | return createDataModel(countries,
84 | fragmentContainer.getRefugeeDataStore());
85 | }
86 | })
87 | .subscribeOn(Schedulers.computation())
88 | .observeOn(AndroidSchedulers.mainThread())
89 | .subscribe(new Action1>() {
90 | @Override
91 | public void call(List viewModels) {
92 | NavigationDrawerFragment.this.viewModels = viewModels;
93 | subscribeViewModels();
94 | drawerListView.setAdapter(new CountryAdapter(getActivity(),
95 | NavigationDrawerFragment.this.viewModels,
96 | new CountryViewBinder(R.layout.country_info_row)));
97 | }
98 | });
99 | }
100 |
101 | @Override
102 | public void onPause() {
103 | displayCountrySubscription.unsubscribe();
104 | unsubscribeViewModels();
105 | super.onPause();
106 | }
107 |
108 | /**
109 | * Users of this fragment must call this method to set up the navigation drawer interactions.
110 | *
111 | * @param fragmentId The android:id of this fragment in its activity's layout.
112 | * @param drawerLayout The DrawerLayout containing this fragment's UI.
113 | */
114 | public void setUp(int fragmentId, DrawerLayout drawerLayout, Toolbar toolbar) {
115 | fragmentContainerView = getActivity().findViewById(fragmentId);
116 |
117 | // set a custom shadow that overlays the main content when the drawer opens
118 | drawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
119 |
120 | drawerToggle = new ActionBarDrawerToggle(
121 | getActivity(),
122 | drawerLayout,
123 | toolbar,
124 | R.string.navigation_drawer_open,
125 | R.string.navigation_drawer_close) {
126 | @Override
127 | public void onDrawerClosed(View drawerView) {
128 | super.onDrawerClosed(drawerView);
129 | if (!isAdded()) {
130 | return;
131 | }
132 | getActivity().invalidateOptionsMenu(); // calls onPrepareOptionsMenu()
133 | }
134 |
135 | @Override
136 | public void onDrawerOpened(View drawerView) {
137 | super.onDrawerOpened(drawerView);
138 | if (!isAdded()) {
139 | return;
140 | }
141 | getActivity().invalidateOptionsMenu(); // calls onPrepareOptionsMenu()
142 | }
143 | };
144 |
145 | // Defer code dependent on restoration of previous instance state.
146 | drawerLayout.post(new Runnable() {
147 | @Override
148 | public void run() {
149 | drawerToggle.syncState();
150 | }
151 | });
152 | drawerLayout.setDrawerListener(drawerToggle);
153 | }
154 |
155 | @Override
156 | public void onAttach(Activity activity) {
157 | super.onAttach(activity);
158 | try {
159 | callbacks = (NavigationDrawerCallbacks) activity;
160 | fragmentContainer = (FragmentContainer) activity;
161 | } catch (ClassCastException e) {
162 | throw new ClassCastException("Activity must implement NavigationDrawerCallbacks.");
163 | }
164 | }
165 |
166 | @Override
167 | public void onDetach() {
168 | super.onDetach();
169 | callbacks = null;
170 | fragmentContainer = null;
171 | }
172 |
173 | @Override
174 | public void onConfigurationChanged(Configuration newConfig) {
175 | super.onConfigurationChanged(newConfig);
176 | drawerToggle.onConfigurationChanged(newConfig);
177 | }
178 |
179 | public static interface NavigationDrawerCallbacks {
180 | /**
181 | * Called when an item in the navigation drawer is selected.
182 | */
183 | void onCountryItemSelected(long countryId, boolean isSelected);
184 | }
185 |
186 | public static interface FragmentContainer {
187 | Observable> getDisplayedCountries();
188 |
189 | RefugeeDataStore getRefugeeDataStore();
190 | }
191 |
192 | private static List createDataModel(List countries,
193 | RefugeeDataStore refugeeDataStore) {
194 | List models = new ArrayList<>();
195 | for (DisplayedCountry country : countries) {
196 | CountryViewModel viewModel = new CountryViewModel(refugeeDataStore, country.getId(), country.getColor());
197 | models.add(viewModel);
198 | }
199 | return models;
200 | }
201 |
202 | private void subscribeViewModels() {
203 | for (CountryViewModel vm : viewModels) {
204 | vm.subscribeToDataStore();
205 | }
206 | }
207 |
208 | private void unsubscribeViewModels() {
209 | for (CountryViewModel vm : viewModels) {
210 | vm.unsubscribeFromDataStore();
211 | }
212 | }
213 |
214 | }
215 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
--------------------------------------------------------------------------------
/app/src/main/java/com/moac/android/refuge/activity/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.moac.android.refuge.activity;
2 |
3 | import android.app.SearchManager;
4 | import android.app.Service;
5 | import android.content.ComponentName;
6 | import android.content.Context;
7 | import android.content.Intent;
8 | import android.content.ServiceConnection;
9 | import android.content.res.Configuration;
10 | import android.os.Bundle;
11 | import android.os.IBinder;
12 | import android.support.v4.view.MenuItemCompat;
13 | import android.support.v4.widget.DrawerLayout;
14 | import android.support.v7.app.ActionBarDrawerToggle;
15 | import android.support.v7.app.AlertDialog;
16 | import android.support.v7.app.AppCompatActivity;
17 | import android.support.v7.widget.SearchView;
18 | import android.support.v7.widget.Toolbar;
19 | import android.util.Log;
20 | import android.view.Menu;
21 | import android.view.MenuInflater;
22 | import android.view.MenuItem;
23 | import android.view.View;
24 | import android.widget.Toast;
25 |
26 | import com.google.android.gms.maps.GoogleMap;
27 | import com.google.android.gms.maps.MapFragment;
28 | import com.moac.android.refuge.R;
29 | import com.moac.android.refuge.RefugeApplication;
30 | import com.moac.android.refuge.database.RefugeeDataStore;
31 | import com.moac.android.refuge.fragment.NavigationDrawerFragment;
32 | import com.moac.android.refuge.importer.ImportService;
33 | import com.moac.android.refuge.model.CountriesModel;
34 | import com.moac.android.refuge.model.DisplayedCountry;
35 | import com.moac.android.refuge.model.persistent.Country;
36 | import com.moac.android.refuge.util.DoOnce;
37 | import com.moac.android.refuge.util.Visualizer;
38 |
39 | import java.util.List;
40 |
41 | import javax.inject.Inject;
42 |
43 | import rx.Notification;
44 | import rx.Observable;
45 | import rx.Subscription;
46 | import rx.android.schedulers.AndroidSchedulers;
47 | import rx.functions.Action1;
48 | import rx.functions.Func1;
49 | import rx.schedulers.Schedulers;
50 |
51 | public class MainActivity extends AppCompatActivity
52 | implements NavigationDrawerFragment.NavigationDrawerCallbacks,
53 | NavigationDrawerFragment.FragmentContainer {
54 |
55 | private static final String TAG = MainActivity.class.getSimpleName();
56 |
57 | @Inject
58 | RefugeeDataStore refugeeDataStore;
59 |
60 | @Inject
61 | CountriesModel countriesModel;
62 |
63 | private NavigationDrawerFragment navigationDrawerFragment;
64 | private GoogleMap mapFragment;
65 | private SearchView searchView;
66 | private DrawerLayout drawerLayout;
67 | private ActionBarDrawerToggle drawerToggle;
68 | private Subscription importSubscription;
69 | private Subscription countriesSubscription;
70 | private boolean isBound;
71 | private AlertDialog dialog;
72 | private ServiceConnection connection = new ServiceConnection() {
73 | @Override
74 | public void onServiceConnected(ComponentName name, IBinder service) {
75 | ImportService.ImportClient importClient = (ImportService.ImportClient) service;
76 | Log.d(TAG, "Service is bound");
77 | isBound = true;
78 | importSubscription = importClient.getStatus()
79 | .observeOn(AndroidSchedulers.mainThread())
80 | .subscribe(new Action1>() {
81 | @Override
82 | public void call(Notification statusNotification) {
83 | Log.d(TAG, "Got status event: " + statusNotification);
84 | if (statusNotification.hasValue() && statusNotification.getValue() == ImportService.Status.RUNNING) {
85 | AlertDialog.Builder builder =
86 | new AlertDialog.Builder(MainActivity.this);
87 | builder.setTitle("Importing data");
88 | builder.setMessage("This can take a while...");
89 | builder.setCancelable(false);
90 | dialog = builder.show();
91 | } else if (dialog != null) {
92 | dialog.cancel();
93 | }
94 | }
95 | });
96 | }
97 |
98 | @Override
99 | public void onServiceDisconnected(ComponentName name) {
100 | Log.d(TAG, "Service is now unbound");
101 | importSubscription.unsubscribe();
102 | connection = null;
103 | isBound = false;
104 | if (dialog != null) {
105 | dialog.cancel();
106 | }
107 | dialog = null;
108 | }
109 | };
110 |
111 | @Override
112 | protected void onCreate(Bundle savedInstanceState) {
113 | super.onCreate(savedInstanceState);
114 | RefugeApplication.from(this).inject(this);
115 | setContentView(R.layout.activity_main);
116 |
117 | initDataStore();
118 |
119 | navigationDrawerFragment = (NavigationDrawerFragment)
120 | getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);
121 |
122 | // Set up the drawer.
123 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
124 | setSupportActionBar(toolbar);
125 | navigationDrawerFragment.setUp(
126 | R.id.navigation_drawer,
127 | (DrawerLayout) findViewById(R.id.drawer_layout),
128 | toolbar);
129 | drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
130 | drawerToggle = new ActionBarDrawerToggle(this, drawerLayout,
131 | toolbar, R.string.open_drawer_hint, R.string.close_drawer_hint) {
132 |
133 | public void onDrawerOpened(View drawerView) {
134 | super.onDrawerOpened(drawerView);
135 | searchView.setIconified(true);
136 | }
137 | };
138 |
139 | // Set the drawer toggle as the DrawerListener
140 | drawerLayout.setDrawerListener(drawerToggle);
141 |
142 | // Initialise map
143 | mapFragment = ((MapFragment) getFragmentManager()
144 | .findFragmentById(R.id.map)).getMap();
145 | mapFragment.getUiSettings().setZoomControlsEnabled(true);
146 |
147 | handleIntent(getIntent());
148 |
149 | }
150 |
151 | @Override
152 | protected void onPostCreate(Bundle savedInstanceState) {
153 | super.onPostCreate(savedInstanceState);
154 | drawerToggle.syncState();
155 | }
156 |
157 | @Override
158 | public void onConfigurationChanged(Configuration newConfig) {
159 | super.onConfigurationChanged(newConfig);
160 | drawerToggle.onConfigurationChanged(newConfig);
161 | }
162 |
163 | @Override
164 | public void onPause() {
165 | super.onPause();
166 | countriesSubscription.unsubscribe();
167 | }
168 |
169 | @Override
170 | public void onResume() {
171 | super.onResume();
172 | countriesSubscription = countriesModel.getDisplayedCountries()
173 | .map(new Func1, CirclesViewModel>() {
174 | @Override public CirclesViewModel call(List countries) {
175 | long maxFlow = 0;
176 | for (DisplayedCountry country : countries) {
177 | maxFlow = Math.max(maxFlow, refugeeDataStore.getTotalRefugeeFlowTo(country.getId()));
178 | }
179 | return new CirclesViewModel(countries, maxFlow);
180 | }
181 | })
182 | .subscribeOn(Schedulers.computation())
183 | .observeOn(AndroidSchedulers.mainThread())
184 | .subscribe(new Action1() {
185 | @Override
186 | public void call(CirclesViewModel circlesViewModel) {
187 | mapFragment.clear();
188 | if (circlesViewModel.maxFlow > 0) {
189 | Visualizer.drawCountries(refugeeDataStore, mapFragment, circlesViewModel.countries, circlesViewModel.maxFlow);
190 | } else if (!circlesViewModel.countries.isEmpty()) {
191 | Toast.makeText(MainActivity.this, "No refugee flows found", Toast.LENGTH_LONG).show();
192 | }
193 | }
194 | });
195 | }
196 |
197 | @Override
198 | protected void onDestroy() {
199 | super.onDestroy();
200 | if (isBound) {
201 | unbindService(connection);
202 | isBound = false;
203 | }
204 | }
205 |
206 | @Override
207 | public void onCountryItemSelected(long countryId, boolean isSelected) {
208 | // TODO Display some further info about country
209 | }
210 |
211 | @Override
212 | public boolean onCreateOptionsMenu(Menu menu) {
213 |
214 | MenuInflater inflater = getMenuInflater();
215 | inflater.inflate(R.menu.main, menu);
216 |
217 | // Get the SearchView and set the searchable configuration
218 | SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
219 | MenuItem searchItem = menu.findItem(R.id.action_search);
220 | searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
221 | searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
222 | searchView.setIconifiedByDefault(true);
223 | searchView.setOnQueryTextFocusChangeListener(new View.OnFocusChangeListener() {
224 | @Override
225 | public void onFocusChange(View v, boolean hasFocus) {
226 | if (v == searchView && !hasFocus) searchView.setIconified(true);
227 | }
228 | });
229 |
230 | return true;
231 | }
232 |
233 | @Override
234 | public boolean onOptionsItemSelected(MenuItem item) {
235 | int id = item.getItemId();
236 | switch (id) {
237 | case R.id.action_about:
238 | // TODO Show info
239 | return false;
240 | case R.id.action_clear:
241 | countriesModel.clear();
242 | return true;
243 | default:
244 | return super.onOptionsItemSelected(item);
245 | }
246 | }
247 |
248 | @Override
249 | protected void onNewIntent(Intent intent) {
250 | Log.i(TAG, "onNewIntent - received intent");
251 | handleIntent(intent);
252 | }
253 |
254 | @Override
255 | public Observable> getDisplayedCountries() {
256 | return countriesModel.getDisplayedCountries();
257 | }
258 |
259 | @Override
260 | public RefugeeDataStore getRefugeeDataStore() {
261 | return refugeeDataStore;
262 | }
263 |
264 | private void handleIntent(Intent intent) {
265 | if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
266 | String query = intent.getStringExtra(SearchManager.QUERY).trim();
267 | // TODO Enforce limits some how
268 | // if (displayedCountriesStore. == 5) {
269 | // Toast.makeText(this, "Displaying max number of countries on map.", Toast.LENGTH_SHORT).show();
270 | // // add more colors!
271 | // return;
272 | // }
273 | Country country = refugeeDataStore.getCountry(query);
274 | if (country != null) {
275 | addCountry(country);
276 | } else {
277 | Toast.makeText(this, "Country not found", Toast.LENGTH_SHORT).show();
278 | }
279 | }
280 | }
281 |
282 | private void addCountry(Country country) {
283 | countriesModel.add(country.getId());
284 | }
285 |
286 | private void initDataStore() {
287 | Intent intent = new Intent(this, ImportService.class);
288 | if (!DoOnce.isDone(this, ImportService.LOAD_DATA_TASK_TAG)) {
289 | startService(intent);
290 | bindService(intent, connection, Service.BIND_AUTO_CREATE);
291 | }
292 | }
293 |
294 | private class CirclesViewModel {
295 | List countries;
296 | long maxFlow;
297 |
298 | public CirclesViewModel(List countries, long maxFlow) {
299 | this.countries = countries;
300 | this.maxFlow = maxFlow;
301 | }
302 | }
303 | }
304 |
--------------------------------------------------------------------------------