├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── app ├── app-release.apk ├── build.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── org │ │ └── fitchfamily │ │ └── android │ │ └── wifi_backend │ │ ├── Configuration.java │ │ ├── Constants.java │ │ ├── SpiceService.java │ │ ├── backend │ │ ├── BackendService.java │ │ └── gpsMonitor.java │ │ ├── data │ │ ├── ExportSpiceRequest.java │ │ ├── ImportSpiceRequest.java │ │ └── ResetSpiceRequest.java │ │ ├── database │ │ ├── AccessPoint.java │ │ ├── Database.java │ │ └── SamplerDatabase.java │ │ ├── ui │ │ ├── AdvancedSettingsFragment.java │ │ ├── BaseDialogFragment.java │ │ ├── EditTextPreference.java │ │ ├── MainActivity.java │ │ ├── MainSettingsFragment.java │ │ ├── data │ │ │ ├── CursorAdapter.java │ │ │ ├── CursorLoader.java │ │ │ ├── WifiDetailActivity.java │ │ │ ├── WifiDetailFragment.java │ │ │ ├── WifiListActivity.java │ │ │ ├── WifiListAdapter.java │ │ │ ├── reset │ │ │ │ ├── ResetDatabaseDialogFragment.java │ │ │ │ └── ResetProgressDialog.java │ │ │ └── transfer │ │ │ │ ├── ExportProgressDialog.java │ │ │ │ ├── ImportProgressDialog.java │ │ │ │ └── OperationProgressDialog.java │ │ └── statistic │ │ │ ├── DatabaseStatistic.java │ │ │ └── DatabaseStatisticLoader.java │ │ ├── util │ │ ├── AgeValue.java │ │ ├── CountingInputStream.java │ │ ├── LocationUtil.java │ │ ├── SimpleLocation.java │ │ └── distanceCache.java │ │ └── wifi │ │ ├── WifiAccessPoint.java │ │ ├── WifiBlacklist.java │ │ ├── WifiCompat.java │ │ └── WifiReceiver.java │ ├── res │ ├── drawable-hdpi-v11 │ │ └── ic_stat_no_location.png │ ├── drawable-hdpi │ │ └── ic_stat_no_location.png │ ├── drawable-mdpi-v11 │ │ └── ic_stat_no_location.png │ ├── drawable-mdpi │ │ └── ic_stat_no_location.png │ ├── drawable-xhdpi-v11 │ │ └── ic_stat_no_location.png │ ├── drawable-xhdpi │ │ └── ic_stat_no_location.png │ ├── drawable-xxhdpi-v11 │ │ └── ic_stat_no_location.png │ ├── drawable-xxhdpi │ │ └── ic_stat_no_location.png │ ├── drawable-xxxhdpi-v11 │ │ └── ic_stat_no_location.png │ ├── drawable-xxxhdpi │ │ └── ic_stat_no_location.png │ ├── layout-w900dp │ │ └── activity_wifi_list.xml │ ├── layout │ │ ├── activity_main.xml │ │ ├── activity_wifi_detail.xml │ │ ├── activity_wifi_list.xml │ │ ├── toolbar.xml │ │ ├── wifi_detail.xml │ │ └── wifi_list_content.xml │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ ├── values-de │ │ └── strings.xml │ ├── values-fr │ │ └── strings.xml │ ├── values-pl │ │ └── strings.xml │ ├── values-sr │ │ └── strings.xml │ ├── values-uk │ │ └── strings.xml │ ├── values-v19 │ │ └── constants.xml │ ├── values │ │ ├── constants.xml │ │ ├── defaults.xml │ │ ├── dimens.xml │ │ ├── libraries.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── xml │ │ ├── advanced.xml │ │ └── main.xml │ └── web_hi_res_512.png ├── build.gradle ├── fastlane └── metadata │ └── android │ └── en-US │ ├── changelogs │ └── 42.txt │ ├── full_description.txt │ ├── images │ ├── icon.png │ └── phoneScreenshots │ │ ├── Screenshot1.png │ │ └── Screenshot2.png │ ├── short_description.txt │ └── title.txt ├── get_it_on_f-droid.png ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | tags 2 | .DS_Store 3 | local.properties 4 | gen/ 5 | .idea/ 6 | out/ 7 | *.iml 8 | build/ 9 | *.apk 10 | .gradle/ 11 | user.gradle 12 | local.properties 13 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/en) 5 | and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | ### Added 9 | - Not applicable 10 | 11 | ### Changed 12 | - Not applicable 13 | 14 | ### Removed 15 | - Not applicable 16 | 17 | ## [1.1.13] - 2018-06-17 18 | ### Added 19 | - Add Ukrainian translation 20 | 21 | ### Changed 22 | - Update build environment 23 | 24 | ## [1.1.12] - 2018-06-08 25 | ### Added 26 | - Ability to filter or search for specific AP or group of APs by SSID or BSSIS. 27 | 28 | ### Changed 29 | - Correct typographic error in German translation. Thanks to @N3dal 30 | 31 | ## [1.1.11] - 2018-03-13 32 | ### Added 33 | - Reset database control added. Thanks to @l-jonas 34 | 35 | ## [1.1.10] 2017-09-21 36 | ### Added 37 | - Polish translation. Thanks to @verdulo 38 | 39 | ## [1.1.9] 2017-09-19 40 | ### Changed 41 | - Correct XML errors in French translation. 42 | 43 | ## [1.1.8] 2017-09-12 44 | ### Changed 45 | - Build changed to compile dependency rather than import jar file. 46 | 47 | ## [1.1.7] - 2017-09-08 48 | ### Added 49 | - Add French translation. Thanks to @Massedil 50 | 51 | ### Changed 52 | - Move change log into separate file. 53 | - Update build tools and gradle 54 | 55 | ## [1.1.6] - 2016-08-10 56 | ### Added 57 | - Now supports backup/export of new/changed data in addition to full backup/export. 58 | 59 | ### Changed 60 | - New database schema (probably want to backup/export existing data before upgrading just in case). 61 | - Various other internal changes and fixes to work toward goal of supporting other types of RF sources for position estimation. 62 | 63 | ## [1.1.5] - 2016-07-30 64 | ### Changed 65 | - Add permission to write to external storage so export data will work. 66 | 67 | ## [1.1.4] - 2016-06-23 68 | ### Changed 69 | - Fix calculation cache to miss less often. 70 | 71 | ## [1.1.3] - 2016-06-23 72 | ### Changed 73 | - Improve performance on often used distance calculations 74 | 75 | ## [1.1.2] - 2016-06-22 76 | ### Changed 77 | - Refactor some files and logic. Should be no user discernible change in operation. 78 | 79 | ## [1.1.1] - 2016-05-13 80 | ### Changed 81 | - Fix divide by zero on minimum signal strength. 82 | 83 | ## [1.1.0] - 2016-05-05 84 | ### Changed 85 | - Change import/export format to comma separated value (CSV) format. 86 | 87 | ## [1.0.2] - 2016-03-23 88 | ### Changed 89 | - Update for revised UnifiedNlp with aging of reports. 90 | 91 | ## [1.0.0] - 2016-01-06 92 | ### Added 93 | - Thanks to @pejakm, update Serbian translation 94 | 95 | ## [0.9.9] - 2016-01-16 96 | ### Added 97 | - Thanks to @UnknownUntilNow, new UI, refactored code, import and export of WiFi AP location information, support for Marshmallow 98 | 99 | ## [0.17.0] 2015-08-21 100 | ### Changed 101 | - |21Aug2015|Increase location uncertainty if no position found. 102 | 103 | ## [0.6.1] 104 | ### Changed 105 | - Fix up Android Studio/Gradle build environment 106 | 107 | ## [0.6.0] 108 | ### Added 109 | - Configurable settings for data collection and use. 110 | 111 | ### Changed 112 | - Some improvements in performance 113 | 114 | ## [0.1.0] 115 | ### Added 116 | - Initial version 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | NOTICE 2 | ====== 3 | The author of this backend is now primarily focused on the [Déjá Vu backend](https://github.com/n76/DejaVu). Bug fixes and pull requests will be accepted for this backend but it will not be as well supported as Déjá Vu going forward. 4 | 5 | Local WiFi Backend 6 | ================== 7 | [UnifiedNlp](https://github.com/microg/android_packages_apps_UnifiedNlp) backend that uses locally acquired WiFi AP data to resolve user location. 8 | 9 | This backend consists of two parts sharing a common database. One part passively monitors the GPS. If the GPS has acquired and has a good position accuracy, then the WiFi APs detected by the phone are stored. 10 | 11 | The other part is the actual location provider which uses the database to estimate location when the GPS is not available. The use of stored WiFi AP can decrease the GPS time to first fix and allows apps to get an immediate approximate location. 12 | 13 | This backend uses no network data. All data acquired by the phone stays on the phone and no queries are made to a centralized WiFi AP location provider. 14 | 15 | [![Get it on F-Droid](get_it_on_f-droid.png?raw=true)](https://f-droid.org/repository/browse/?fdid=org.fitchfamily.android.wifi_backend) 16 | 17 | Requirement for building 18 | ======================== 19 | 20 | 1. Building requires Android SDK with API 19 or higher. 21 | 22 | Requirements on phone 23 | ===================== 24 | 1. This is a plug-in for [µg UnifiedNlp](http://forum.xda-developers.com/android/apps-games/app-g-unifiednlp-floss-wi-fi-cell-tower-t2991544) which can be [installed from f-droid](https://f-droid.org/repository/browse/?fdfilter=unified&fdpage=1&page_id=0). The [µg GmsCore](http://forum.xda-developers.com/android/apps-games/app-microg-gmscore-floss-play-services-t3217616) can also use this backend. 25 | 26 | How to build and install 27 | ======================== 28 | 29 | Using Android Studio, select Build->"Generate Signed APK..." 30 | 31 | Setup on phone 32 | ============== 33 | In the NLP Controller app (interface for µg UnifiedNlp) select the "WiFi Location Service". If using GmsCore, then the little gear at microG Settings->UnifiedNlp Settings->Configure location backends->WiFi Location Service is used. 34 | 35 | Advanced Settings 36 | -------- 37 | - Required Accuracy: Sets the maximum error that a GPS location report can have for the sampler to trigger the collection of WiFi Access Point (AP) data. For example, if set to 10m then all GPS locations with accuracy worse (greater) than 10m will be ignored. 38 | - Sample Distance: Sets the minimum distance change in a GPS location for the Android OS to give the sampler a new location. For example if set to 20m, then only GPS positions more than 20m apart will be used. 39 | - Sample Interval: Sets the minimum time between GPS location reports from the Android OS. Smaller values may improve AP range detection but will cause higher processing loads. 40 | - GPS Valid Time: How long a position report from the GPS is considered good. WiFi APs detected during this time will use the most recent valid GPS location when updating the database. 41 | - Minimum AP Range: Sets the minimum range (accuracy) value back end will report for an AP. This value should be set to the usual coverage radius of a WiFi AP. For current model APs this is about 100m. 42 | - Moved Threshold: If a new GPS location sample for an AP is too far from our old estimate we assume the AP has been moved. This value sets the distance that will trigger the moved AP logic. 43 | - Move Guard: Once an AP has been detected as moved we block its location from being used until we are sure it is stable. Stable is defined as having received a number of GPS location updates for the AP that are plausible. This value sets the number of samples required to clear the "moved" indication. 44 | 45 | Collecting WiFi AP Data 46 | ----------------------- 47 | To conserve power the collection process does not actually turn on the GPS. If some other app turns on the app, for example a map or navigation app, then the backend will monitor the location and collect WiFi data. 48 | 49 | What is stored in the database 50 | ------------------------------ 51 | For each WiFi AP the [bssid](https://en.wikipedia.org/wiki/Service_set_(802.11_network)#Basic_service_set_identification_.28BSSID.29) and, if set, the [ssid](https://en.wikipedia.org/wiki/Service_set_(802.11_network)#Service_set_identification_.28SSID.29) are stored along with up to three sets of latitude/longitude samples. There is also a "moved" indicator set if it appears the AP may have moved. 52 | 53 | The ssid is stored for display only and is irrelevant to actual function of this software. 54 | 55 | The algorithm attempts to determine the outer edge of the coverage area a WiFi AP by saving the three samples that give the largest reasonable circle within which the AP is detected. The [logic behind this was to reduce identifiable "bread crumb" trails](http://retiredtechie.fitchfamily.org/2014/12/13/bread-crumbs/) for data collected by stumblers and may not be ideal. 56 | 57 | Export and Import of WiFi (WLAN) Access Point (AP) data 58 | ------------------------------------------------------- 59 | - On export each sample for each AP is written as a separate record with up to three records per AP. Excluded from this are points for any AP which has been detected as moved or moving. 60 | - On import each record is treated the same as a data point from the internal background sampling server. That is the data is merged into the database with each position compared against the ones already in the database to see if using it would provide a better AP position estimate. 61 | - It is possible to share export files: Position sample data from a file someone else exported will be merged on import and you will end up with a database containing the "best" set of location sample points from all imports as well as those collected locally. 62 | - The location lookup process requires a minimum of three samples for a AP before it will use that AP. So imports that have a single best position will be entered in the database but will not be used for position estimation until at least two more position samples are available. If you are importing from another project you may wish to pre-process their data to create three points around the best guess location and then import the three estimated points rather than the center location. 63 | - This backend does not support the import or export of data to anyplace other than local storage on the phone. If you wish to back up the data or share it, you will need to do that through other means. 64 | 65 | Clearing the database 66 | --------------------- 67 | To clear or reset database, touch the "Reset Database" in the backend setting. 68 | 69 | Libraries Used 70 | -------------- 71 | - A full list of libraries used is listed in the "External Libraries" area of this app's settings. 72 | 73 | Other IP used 74 | ============= 75 | Icon created with the [Android Asset Studio](https://romannurik.github.io/AndroidAssetStudio/icons-launcher.html#foreground.type=clipart&foreground.space.trim=1&foreground.space.pad=0.15&foreground.clipart=res%2Fclipart%2Ficons%2Fdevice_signal_wifi_3_bar.svg&foreColor=fff%2C0&crop=0&backgroundShape=circle&backColor=4caf50%2C100&effects=none) (Creative Commons Attribution 3.0 Unported License). 76 | 77 | Notification icon created with the [Android Asset Studio](https://romannurik.github.io/AndroidAssetStudio/icons-notification.html#source.type=clipart&source.space.trim=1&source.space.pad=0&source.clipart=res%2Fclipart%2Ficons%2Fcommunication_location_off.svg&name=ic_stat_no_location) (Creative Commons Attribution 3.0 Unported License). 78 | 79 | Changes 80 | ======= 81 | [History is now a separate file](CHANGELOG.md) 82 | 83 | License 84 | ======= 85 | 86 | Copyright (C) 2014, 2015, 2016 Tod Fitch 87 | 88 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 89 | 90 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 91 | 92 | You should have received a copy of the GNU General Public License along with this program. If not, see . 93 | -------------------------------------------------------------------------------- /app/app-release.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n76/wifi_backend/ab5279a72d50ce90a2ead580cd04bbf2d98d0241/app/app-release.apk -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | jcenter() 4 | } 5 | 6 | dependencies { 7 | // replace with the current version of the Android plugin 8 | classpath 'com.android.tools.build:gradle:2.3.3' 9 | // replace with the current version of the android-apt plugin 10 | classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' 11 | } 12 | } 13 | 14 | apply plugin: 'com.android.application' 15 | apply plugin: 'android-apt' 16 | 17 | android { 18 | compileSdkVersion 23 19 | buildToolsVersion '25.0.2' 20 | 21 | defaultConfig { 22 | applicationId "org.fitchfamily.android.wifi_backend" 23 | minSdkVersion 17 24 | targetSdkVersion 23 25 | } 26 | 27 | buildTypes { 28 | release { 29 | minifyEnabled false 30 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' 31 | } 32 | } 33 | 34 | packagingOptions { 35 | exclude 'META-INF/LICENSE.txt' 36 | exclude 'META-INF/NOTICE.txt' 37 | } 38 | } 39 | 40 | def AAVersion = '3.3.2' 41 | 42 | dependencies { 43 | compile "com.android.support:support-v4:23.4.0" 44 | provided 'com.google.auto.value:auto-value:1.5.2' 45 | apt "org.androidannotations:androidannotations:$AAVersion" 46 | compile "org.androidannotations:androidannotations-api:$AAVersion" 47 | compile('com.mikepenz:materialdrawer:4.6.3@aar') { 48 | transitive = true 49 | } 50 | compile('com.mikepenz:aboutlibraries:5.3.7@aar') { 51 | transitive = true 52 | } 53 | compile 'org.microg:unifiednlp-api:1.5.3' 54 | compile 'com.google.guava:guava:19.0' 55 | compile 'com.mikepenz:google-material-typeface:2.1.0.1.original@aar' 56 | compile "com.android.support:support-v4:23.4.0" 57 | compile "com.android.support:recyclerview-v7:23.4.0" 58 | compile 'com.google.code.gson:gson:2.8.0' 59 | compile 'com.octo.android.robospice:robospice:1.4.14' 60 | compile 'com.github.machinarius:preferencefragment:0.1.1' 61 | compile 'com.opencsv:opencsv:3.7' 62 | } 63 | 64 | apt { 65 | arguments { 66 | androidManifestFile variant.outputs[0]?.processResources?.manifestFile 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /app/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n76/wifi_backend/ab5279a72d50ce90a2ead580cd04bbf2d98d0241/app/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/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 | -------------------------------------------------------------------------------- /app/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/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/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 18 | 19 | 20 | 21 | 24 | 27 | 28 | 29 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 43 | 46 | 47 | 51 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /app/src/main/java/org/fitchfamily/android/wifi_backend/Configuration.java: -------------------------------------------------------------------------------- 1 | package org.fitchfamily.android.wifi_backend; 2 | 3 | /* 4 | * WiFi Backend for Unified Network Location 5 | * Copyright (C) 2014,2015 Tod Fitch 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import android.Manifest; 22 | import android.content.Context; 23 | import android.content.SharedPreferences; 24 | import android.content.pm.PackageManager; 25 | import android.content.res.Resources; 26 | import android.preference.PreferenceManager; 27 | import android.support.v4.content.ContextCompat; 28 | 29 | public class Configuration { 30 | // Identifiers for extra fields in Location records 31 | public static final String EXTRA_MAC_ADDRESS = "MAC_ADDRESS"; 32 | public static final String EXTRA_SIGNAL_LEVEL = "SIGNAL_LEVEL"; 33 | 34 | public static final String PREF_MIN_GPS_TIME = "gps_min_time_preference"; 35 | public static final String PREF_MIN_GPS_ACCURACY = "gps_accuracy_preference"; 36 | public static final String PREF_MIN_GPS_DISTANCE = "gps_min_distance_preference"; 37 | 38 | public static final String PREF_AP_ACCURACY = "ap_min_range_preference"; 39 | public static final String PREF_MOVE_GUARD = "ap_moved_guard_preference"; 40 | public static final String PREF_MOVE_RANGE = "ap_moved_range_preference"; 41 | 42 | public static final String PREF_GPS_VALID_TIME = "gps_valid_time"; 43 | 44 | private static final Object lock = new Object(); 45 | private static Configuration instance; 46 | 47 | public static Configuration with(Context context) { 48 | if(context == null) { 49 | throw new NullPointerException(); 50 | } 51 | 52 | if(instance == null) { 53 | synchronized (lock) { 54 | if (instance == null) { 55 | instance = new Configuration(context.getApplicationContext()); 56 | } 57 | } 58 | } 59 | 60 | return instance; 61 | } 62 | 63 | private SharedPreferences preferences; 64 | private Resources resources; 65 | private Context context; 66 | 67 | public static final int LIST_OPTION_ALL = 0; 68 | public static final int LIST_OPTION_CHANGED = 1; 69 | private static int listOptionValue = LIST_OPTION_ALL; 70 | 71 | public static final int EXPORT_OPTION_ALL = 0; 72 | public static final int EXPORT_OPTION_CHANGED = 1; 73 | private static int exportOptionValue = LIST_OPTION_ALL; 74 | 75 | private Configuration(Context context) { 76 | preferences = PreferenceManager.getDefaultSharedPreferences(context); 77 | resources = context.getResources(); 78 | this.context = context; 79 | } 80 | 81 | // How accurate should our GPS position be to bother recording WiFi signals? 82 | public float minimumGpsAccuracyInMeters() { 83 | return parseFloat(PREF_MIN_GPS_ACCURACY, R.string.gps_accuracy_default); 84 | } 85 | 86 | public long minimumGpsTimeInMilliseconds() { 87 | return parseLong(PREF_MIN_GPS_TIME, R.string.gps_min_time_default) * 1000; 88 | } 89 | 90 | public float minimumGpsDistanceInMeters() { 91 | return parseFloat(PREF_MIN_GPS_DISTANCE, R.string.gps_min_distance_default); 92 | } 93 | 94 | // If new report is too far away from our current estimate then 95 | // we assume the AP has moved. apMovedThreshold sets the value for that 96 | // check. 97 | // 98 | // We set a guard against using the moved AP until we get a number 99 | // of samples confirming that it has a stable location. We get a new 100 | // GPS sample every gpsMinTime and we decrease the move guard count 101 | // by one for each good sample for the specific AP. Set this value 102 | // so big enough so that if we are near a parked WiFi AP equipped bus 103 | // it is likely to move before we count down to zero. 104 | 105 | public float accessPointMoveThresholdInMeters() { 106 | return parseFloat(PREF_MOVE_RANGE, R.string.ap_moved_range_default); 107 | } 108 | 109 | public int accessPointMoveGuardSampleCount() { 110 | return parseInt(PREF_MOVE_GUARD, R.string.ap_moved_guard_default); 111 | } 112 | 113 | // For reporting our results to the network backend we will 114 | // guess about the minimum accuracy for an individual AP 115 | public float accessPointAssumedAccuracy() { 116 | return parseFloat(PREF_AP_ACCURACY, R.string.ap_min_range_default); 117 | } 118 | 119 | public long validGpsTimeInMilliseconds() { 120 | return parseLong(PREF_GPS_VALID_TIME, R.string.gps_valid_time_default) * 1000; 121 | } 122 | 123 | public Configuration register(SharedPreferences.OnSharedPreferenceChangeListener listener) { 124 | preferences.registerOnSharedPreferenceChangeListener(listener); 125 | 126 | return this; 127 | } 128 | 129 | public Configuration unregister(SharedPreferences.OnSharedPreferenceChangeListener listener) { 130 | preferences.unregisterOnSharedPreferenceChangeListener(listener); 131 | 132 | return this; 133 | } 134 | 135 | public boolean hasLocationAccess() { 136 | return ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED; 137 | } 138 | 139 | public static int listOption() { 140 | return listOptionValue; 141 | } 142 | 143 | public static void listOption(int newVal ) { 144 | listOptionValue = newVal; 145 | } 146 | 147 | public static int exportOption() { return exportOptionValue; } 148 | public static void exportOption(int newVal) { exportOptionValue = newVal; } 149 | 150 | private float parseFloat(String preferenceKey, int defaultResource) { 151 | String defaultValue = resources.getString(defaultResource); 152 | 153 | try { 154 | return Float.parseFloat(preferences.getString(preferenceKey, defaultValue)); 155 | } catch (NumberFormatException ex) { 156 | return Float.parseFloat(defaultValue); 157 | } 158 | } 159 | 160 | private long parseLong(String preferenceKey, int defaultResource) { 161 | String defaultValue = resources.getString(defaultResource); 162 | 163 | try { 164 | return Long.parseLong(preferences.getString(preferenceKey, defaultValue)); 165 | } catch (NumberFormatException ex) { 166 | return Long.parseLong(defaultValue); 167 | } 168 | } 169 | 170 | private int parseInt(String preferenceKey, int defaultResource) { 171 | String defaultValue = resources.getString(defaultResource); 172 | 173 | try { 174 | return Integer.parseInt(preferences.getString(preferenceKey, defaultValue)); 175 | } catch (NumberFormatException ex) { 176 | return Integer.parseInt(defaultValue); 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /app/src/main/java/org/fitchfamily/android/wifi_backend/Constants.java: -------------------------------------------------------------------------------- 1 | package org.fitchfamily.android.wifi_backend; 2 | 3 | /* 4 | * WiFi Backend for Unified Network Location 5 | * Copyright (C) 2014,2015 Tod Fitch 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | public abstract class Constants { 22 | private Constants() { 23 | 24 | } 25 | 26 | public static final String WEBSITE = "https://github.com/n76/wifi_backend/blob/master/README.md"; 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/org/fitchfamily/android/wifi_backend/SpiceService.java: -------------------------------------------------------------------------------- 1 | package org.fitchfamily.android.wifi_backend; 2 | 3 | import android.content.Context; 4 | 5 | import com.octo.android.robospice.UncachedSpiceService; 6 | import com.octo.android.robospice.networkstate.NetworkStateChecker; 7 | 8 | public class SpiceService extends UncachedSpiceService { 9 | @Override 10 | protected NetworkStateChecker getNetworkStateChecker() { 11 | return new NetworkStateChecker() { 12 | @Override 13 | public boolean isNetworkAvailable(Context context) { 14 | return true; 15 | } 16 | 17 | @Override 18 | public void checkPermissions(Context context) { 19 | 20 | } 21 | }; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/org/fitchfamily/android/wifi_backend/backend/gpsMonitor.java: -------------------------------------------------------------------------------- 1 | package org.fitchfamily.android.wifi_backend.backend; 2 | 3 | /* 4 | * WiFi Backend for Unified Network Location 5 | * Copyright (C) 2014,2015 Tod Fitch 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import android.app.Service; 22 | import android.content.Intent; 23 | import android.content.SharedPreferences; 24 | import android.location.LocationListener; 25 | import android.location.LocationManager; 26 | import android.net.wifi.WifiManager; 27 | import android.os.Binder; 28 | import android.os.Bundle; 29 | import android.os.IBinder; 30 | import android.support.annotation.NonNull; 31 | import android.text.TextUtils; 32 | import android.util.Log; 33 | 34 | import org.androidannotations.annotations.AfterInject; 35 | import org.androidannotations.annotations.EService; 36 | import org.androidannotations.annotations.SystemService; 37 | import org.fitchfamily.android.wifi_backend.BuildConfig; 38 | import org.fitchfamily.android.wifi_backend.Configuration; 39 | 40 | import java.util.concurrent.ExecutorService; 41 | import java.util.concurrent.Executors; 42 | 43 | @EService 44 | public class gpsMonitor extends Service implements LocationListener, 45 | SharedPreferences.OnSharedPreferenceChangeListener { 46 | 47 | private final static String TAG = "WiFiBackendGpsMon"; 48 | private static final boolean DEBUG = BuildConfig.DEBUG; 49 | 50 | private final ExecutorService executor = Executors.newSingleThreadExecutor(); 51 | 52 | @SystemService 53 | protected LocationManager locationManager; 54 | 55 | @SystemService 56 | protected WifiManager wifi; 57 | 58 | private long sampleTime; 59 | private float sampleDistance; 60 | 61 | @Override 62 | public IBinder onBind(Intent intent) { 63 | return new Binder(); 64 | } 65 | 66 | @AfterInject 67 | protected void init() { 68 | if (DEBUG) { 69 | Log.i(TAG, "service started"); 70 | } 71 | 72 | sampleTime = Configuration.with(this).minimumGpsTimeInMilliseconds(); 73 | sampleDistance = Configuration.with(this).minimumGpsDistanceInMeters(); 74 | 75 | try { 76 | locationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER, 77 | sampleTime, 78 | sampleDistance, 79 | gpsMonitor.this); 80 | } catch (SecurityException ex) { 81 | if(DEBUG) { 82 | Log.w(TAG, "init()", ex); 83 | } 84 | } 85 | 86 | Configuration.with(this).register(this); 87 | } 88 | 89 | @Override 90 | public void onDestroy() { 91 | super.onDestroy(); 92 | 93 | Configuration.with(this).unregister(this); 94 | 95 | try { 96 | locationManager.removeUpdates(gpsMonitor.this); 97 | } catch (SecurityException ex) { 98 | // ignore 99 | } 100 | 101 | if (DEBUG) { 102 | Log.i(TAG, "service destroyed"); 103 | } 104 | } 105 | 106 | @Override 107 | public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { 108 | if(TextUtils.equals(key, Configuration.PREF_MIN_GPS_TIME) || 109 | TextUtils.equals(key, Configuration.PREF_MIN_GPS_DISTANCE)) { 110 | 111 | updateSamplingConf( 112 | Configuration.with(gpsMonitor.this).minimumGpsTimeInMilliseconds(), 113 | Configuration.with(gpsMonitor.this).minimumGpsDistanceInMeters() 114 | ); 115 | } 116 | } 117 | 118 | private void updateSamplingConf(final long sampleTime, final float sampleDistance) { 119 | if (DEBUG) { 120 | Log.i(TAG, "updateSamplingConf(" + sampleTime + ", " + sampleDistance + ")"); 121 | } 122 | 123 | // We are in a call back so we can't change the sampling configuration 124 | // in the caller's thread context. Send a message to the processing thread 125 | // for it to deal with the issue. 126 | executor.submit(new Runnable() { 127 | @Override 128 | public void run() { 129 | if ((gpsMonitor.this.sampleTime != sampleTime) || 130 | (gpsMonitor.this.sampleDistance != sampleDistance)) { 131 | 132 | gpsMonitor.this.sampleTime = sampleTime; 133 | gpsMonitor.this.sampleDistance = sampleDistance; 134 | 135 | if (DEBUG) { 136 | Log.i(TAG, "Changing GPS sampling configuration: " + 137 | gpsMonitor.this.sampleTime + " ms, " + gpsMonitor.this.sampleDistance + " meters"); 138 | } 139 | 140 | try { 141 | locationManager.removeUpdates(gpsMonitor.this); 142 | locationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER, 143 | gpsMonitor.this.sampleTime, 144 | gpsMonitor.this.sampleDistance, 145 | gpsMonitor.this); 146 | } catch (SecurityException ex) { 147 | if(DEBUG) { 148 | Log.w(TAG, "updateSamplingConf()", ex); 149 | } 150 | } 151 | } 152 | } 153 | }); 154 | } 155 | 156 | @Override 157 | public int onStartCommand(Intent intent, int flags, int startId) { 158 | return Service.START_STICKY; 159 | } 160 | 161 | @Override 162 | public void onLocationChanged(final android.location.Location location) { 163 | 164 | if (location.getProvider().equals("gps")) { 165 | if (location.getAccuracy() <= Configuration.with(this).minimumGpsAccuracyInMeters()) { 166 | BackendService.instanceGpsLocationUpdated(location); 167 | } else { 168 | if (DEBUG) { 169 | Log.i(TAG, "Ignoring inaccurate GPS location ("+location.getAccuracy()+" meters)."); 170 | } 171 | } 172 | } else { 173 | if (DEBUG) { 174 | Log.i(TAG, "Ignoring position from \""+location.getProvider()+"\""); 175 | } 176 | } 177 | } 178 | 179 | @Override 180 | public void onProviderDisabled(String arg0) { 181 | if (DEBUG) { 182 | Log.i(TAG, "Provider Disabled."); 183 | } 184 | } 185 | 186 | @Override 187 | public void onProviderEnabled(String arg0) { 188 | if (DEBUG) { 189 | Log.i(TAG, "Provider Enabled."); 190 | } 191 | } 192 | 193 | @Override 194 | public void onStatusChanged(String arg0, int arg1, Bundle arg2) { 195 | if (DEBUG) { 196 | Log.i(TAG, "Status Changed."); 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /app/src/main/java/org/fitchfamily/android/wifi_backend/data/ExportSpiceRequest.java: -------------------------------------------------------------------------------- 1 | package org.fitchfamily.android.wifi_backend.data; 2 | 3 | /* 4 | * WiFi Backend for Unified Network Location 5 | * Copyright (C) 2014,2015 Tod Fitch 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import android.content.Context; 22 | import android.database.Cursor; 23 | import android.net.Uri; 24 | import android.text.TextUtils; 25 | 26 | import com.octo.android.robospice.request.SpiceRequest; 27 | import com.opencsv.CSVWriter; 28 | 29 | import org.fitchfamily.android.wifi_backend.BuildConfig; 30 | import org.fitchfamily.android.wifi_backend.Configuration; 31 | import org.fitchfamily.android.wifi_backend.database.AccessPoint; 32 | import org.fitchfamily.android.wifi_backend.database.Database; 33 | import org.fitchfamily.android.wifi_backend.database.SamplerDatabase; 34 | import org.fitchfamily.android.wifi_backend.util.SimpleLocation; 35 | 36 | import java.io.IOException; 37 | import java.io.OutputStream; 38 | import java.io.OutputStreamWriter; 39 | 40 | public class ExportSpiceRequest extends SpiceRequest { 41 | public static final String TAG = "WiFiBackendExport"; 42 | private static final boolean DEBUG = BuildConfig.DEBUG; 43 | 44 | public static final int MAX_PROGRESS = 1000; 45 | 46 | private final Context context; 47 | private final Uri uri; 48 | 49 | public abstract static class Result { 50 | 51 | } 52 | 53 | public ExportSpiceRequest(Context context, Uri uri) { 54 | super(Result.class); 55 | this.context = context.getApplicationContext(); 56 | this.uri = uri; 57 | } 58 | 59 | @Override 60 | public Result loadDataFromNetwork() throws Exception { 61 | OutputStream outputStream = context.getContentResolver().openOutputStream(uri); 62 | 63 | if(outputStream == null) { 64 | throw new IOException(); 65 | } 66 | 67 | try { 68 | CSVWriter writer = new CSVWriter(new OutputStreamWriter(outputStream, "UTF-8")); 69 | writer.writeNext(new String[]{"bssid","lat","lon","ssid"}); 70 | String selection; 71 | boolean exportAll = true; 72 | switch (Configuration.exportOption()) { 73 | case Configuration.EXPORT_OPTION_CHANGED: 74 | selection = Database.COL_CHANGED + "<> 0"; 75 | break; 76 | 77 | default: 78 | selection = null; 79 | exportAll = true; 80 | } 81 | 82 | Cursor cursor = SamplerDatabase.getInstance(context).getReadableDatabase().query( 83 | Database.TABLE_SAMPLES, 84 | new String[]{Database.COL_RFID}, 85 | selection, 86 | null, null, null, null 87 | ); 88 | 89 | try { 90 | if (cursor != null && cursor.moveToFirst()) { 91 | do { 92 | AccessPoint accessPoint = SamplerDatabase.getInstance(context).query(cursor.getString(0)); 93 | 94 | if(accessPoint != null) { 95 | writeCSV(writer, 96 | accessPoint, 97 | exportAll 98 | ); 99 | } 100 | 101 | publishProgress(cursor.getPosition() * MAX_PROGRESS / cursor.getCount()); 102 | } while (cursor.moveToNext()); 103 | } 104 | 105 | } finally { 106 | if (cursor != null) { 107 | cursor.close(); 108 | } 109 | } 110 | writer.close(); 111 | } finally { 112 | outputStream.close(); 113 | } 114 | SamplerDatabase.getInstance(context).exportComplete(); 115 | return null; 116 | } 117 | 118 | private void writeCSV(CSVWriter out, AccessPoint value, boolean exportAll) throws IOException { 119 | 120 | if(value.moveGuard() != 0) { // Don't export suspect (moved/moving) APs 121 | return; 122 | } 123 | 124 | final String rfId = value.rfId(); 125 | String ssid = ""; 126 | 127 | if(!TextUtils.isEmpty(value.ssid())) { 128 | ssid = value.ssid(); 129 | } 130 | 131 | for(SimpleLocation sample : value.samples()) { 132 | if (exportAll || sample.changed()) { 133 | out.writeNext(new String[]{rfId, 134 | Double.toString(sample.latitude()), 135 | Double.toString(sample.longitude()), 136 | ssid}); 137 | } 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /app/src/main/java/org/fitchfamily/android/wifi_backend/data/ImportSpiceRequest.java: -------------------------------------------------------------------------------- 1 | package org.fitchfamily.android.wifi_backend.data; 2 | 3 | /* 4 | * WiFi Backend for Unified Network Location 5 | * Copyright (C) 2014,2015 Tod Fitch 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import android.content.Context; 22 | import android.database.Cursor; 23 | import android.net.Uri; 24 | import android.provider.OpenableColumns; 25 | import android.util.Log; 26 | 27 | import com.octo.android.robospice.request.SpiceRequest; 28 | import com.opencsv.CSVReader; 29 | 30 | import org.fitchfamily.android.wifi_backend.BuildConfig; 31 | import org.fitchfamily.android.wifi_backend.data.util.CountingInputStream; 32 | import org.fitchfamily.android.wifi_backend.database.Database; 33 | import org.fitchfamily.android.wifi_backend.database.SamplerDatabase; 34 | import org.fitchfamily.android.wifi_backend.util.SimpleLocation; 35 | 36 | import java.io.IOException; 37 | import java.io.InputStream; 38 | import java.io.InputStreamReader; 39 | 40 | public class ImportSpiceRequest extends SpiceRequest { 41 | public static final String TAG = "WiFiBackendImport"; 42 | private static final boolean DEBUG = BuildConfig.DEBUG; 43 | public static final int MAX_PROGRESS = 1000; 44 | 45 | private final Context context; 46 | private final Uri uri; 47 | 48 | public abstract static class Result { 49 | 50 | } 51 | 52 | public ImportSpiceRequest(Context context, Uri uri) { 53 | super(Result.class); 54 | this.context = context.getApplicationContext(); 55 | this.uri = uri; 56 | } 57 | 58 | @Override 59 | public Result loadDataFromNetwork() throws Exception { 60 | long entryTime = System.currentTimeMillis(); 61 | int recCount = 0; 62 | 63 | final long size = getFileSize(uri, context); 64 | 65 | InputStream inputStream = context.getContentResolver().openInputStream(uri); 66 | 67 | if(inputStream == null) { 68 | throw new IOException(); 69 | } 70 | 71 | CountingInputStream countingInputStream = new CountingInputStream(inputStream); 72 | SamplerDatabase database = SamplerDatabase.getInstance(context); 73 | 74 | try { 75 | CSVReader reader = new CSVReader(new InputStreamReader(countingInputStream, "UTF-8")); 76 | final String[] headerLine = reader.readNext(); 77 | if (headerLine == null) { 78 | throw new IOException(); 79 | } 80 | 81 | int bssidIndex = -1; 82 | int latIndex = -1; 83 | int lonIndex = -1; 84 | int ssidIndex = -1; 85 | int idx = 0; 86 | for (String s : headerLine) { 87 | if (s.equals("bssid")) 88 | bssidIndex = idx; 89 | else if (s.equals("lat")) 90 | latIndex = idx; 91 | else if (s.equals("lon")) 92 | lonIndex = idx; 93 | else if (s.equals("ssid")) 94 | ssidIndex = idx; 95 | idx++; 96 | } 97 | Log.i(TAG, "bssidIndex=" + bssidIndex + 98 | ", latIndex=" + latIndex + 99 | ", lonIndex=" + lonIndex + 100 | ", ssidIndex=" + ssidIndex); 101 | if ((bssidIndex < 0) || (latIndex < 0) || (lonIndex < 0)) { 102 | throw new IOException(); 103 | } 104 | String[] nextLine; 105 | 106 | database.beginTransaction(); 107 | while ((nextLine = reader.readNext()) != null) { 108 | String rfId = nextLine[bssidIndex]; 109 | String latString = nextLine[latIndex]; 110 | String lonString = nextLine[lonIndex]; 111 | String ssid = ""; 112 | if (ssidIndex >= 0) 113 | ssid = nextLine[ssidIndex]; 114 | 115 | database.addSample(Database.TYPE_WIFI, ssid, rfId, SimpleLocation.fromLatLon(latString,lonString,false)); 116 | recCount++; 117 | if ((recCount % 100) == 0) { 118 | // Log.i(TAG, "recCount="+recCount+", committing transaction."); 119 | database.commitTransaction(); 120 | database.beginTransaction(); 121 | } 122 | 123 | if (size != 0) { 124 | publishProgress(countingInputStream.getBytesRead() * MAX_PROGRESS / size); 125 | } 126 | } 127 | } catch (Exception e) { 128 | Log.i(TAG, e.toString()); 129 | e.printStackTrace(); 130 | } finally { 131 | inputStream.close(); 132 | database.commitTransaction(); 133 | } 134 | Log.i(TAG, "Total Records processed: " + recCount); 135 | Log.i(TAG, "Import data elapsed time: " + (System.currentTimeMillis() - entryTime) + " ms"); 136 | 137 | return null; 138 | } 139 | 140 | private static int getFileSize(Uri uri, Context context) { 141 | Cursor cursor = context.getContentResolver().query(uri, new String[]{OpenableColumns.SIZE}, null, null, null); 142 | 143 | try { 144 | if(cursor != null && cursor.moveToFirst() && !cursor.isNull(0)) { 145 | return cursor.getInt(0); 146 | } 147 | } finally { 148 | if(cursor != null) { 149 | cursor.close(); 150 | } 151 | } 152 | 153 | return 0; 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /app/src/main/java/org/fitchfamily/android/wifi_backend/data/ResetSpiceRequest.java: -------------------------------------------------------------------------------- 1 | package org.fitchfamily.android.wifi_backend.data; 2 | 3 | /* 4 | * WiFi Backend for Unified Network Location 5 | * Copyright (C) 2014,2015 Tod Fitch 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import android.content.Context; 22 | 23 | import com.octo.android.robospice.request.SpiceRequest; 24 | 25 | import org.fitchfamily.android.wifi_backend.database.SamplerDatabase; 26 | 27 | public class ResetSpiceRequest extends SpiceRequest { 28 | public static final String TAG = "WiFiBackendReset"; 29 | 30 | private final Context context; 31 | 32 | public abstract static class Result { 33 | // nothing 34 | } 35 | 36 | public ResetSpiceRequest(Context context) { 37 | super(Result.class); 38 | this.context = context.getApplicationContext(); 39 | } 40 | 41 | @Override 42 | public Result loadDataFromNetwork() throws Exception { 43 | SamplerDatabase.getInstance(context).dropAll(); 44 | 45 | return null; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/java/org/fitchfamily/android/wifi_backend/database/AccessPoint.java: -------------------------------------------------------------------------------- 1 | package org.fitchfamily.android.wifi_backend.database; 2 | 3 | /* 4 | * WiFi Backend for Unified Network Location 5 | * Copyright (C) 2014,2015 Tod Fitch 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import android.support.annotation.Nullable; 22 | import android.util.Log; 23 | 24 | import com.google.auto.value.AutoValue; 25 | import com.google.common.collect.ImmutableList; 26 | 27 | import org.fitchfamily.android.wifi_backend.BuildConfig; 28 | import org.fitchfamily.android.wifi_backend.util.SimpleLocation; 29 | 30 | import java.util.ArrayList; 31 | import java.util.List; 32 | 33 | @AutoValue 34 | public abstract class AccessPoint { 35 | private static final int MIN_SAMPLES = 3; 36 | private static final String TAG = "WiFiBackendAP"; 37 | private static final boolean DEBUG = BuildConfig.DEBUG; 38 | 39 | AccessPoint() { 40 | 41 | } 42 | 43 | public static String bssid(String bssid) { 44 | return bssid.replace(":", ""); 45 | } 46 | 47 | public static String readableBssid(String bssid) { 48 | return bssid(bssid).replaceAll(".(?!$).(?!$)", "$0:"); 49 | } 50 | 51 | public static Builder builder() { 52 | return new AutoValue_AccessPoint.Builder(); 53 | } 54 | 55 | public abstract String rfId(); 56 | @Nullable 57 | public abstract String ssid(); 58 | public abstract ImmutableList samples(); 59 | public abstract int moveGuard(); 60 | public abstract int rfType(); 61 | 62 | public SimpleLocation sample(int index) { 63 | if(index < samples().size()) { 64 | return samples().get(index); 65 | } else { 66 | return null; 67 | } 68 | } 69 | 70 | /** 71 | * Use this function to get the estimate location 72 | * @return the estimate location or null if no samples are available 73 | */ 74 | public SimpleLocation estimateLocation() { 75 | if(samples().size() == 0) { 76 | return null; 77 | } 78 | 79 | // get center of points 80 | 81 | double latitude = 0.0; 82 | double longitude = 0.0; 83 | 84 | for(SimpleLocation sample : samples()) { 85 | latitude += sample.latitude(); 86 | longitude += sample.longitude(); 87 | } 88 | 89 | latitude /= (double) samples().size(); 90 | longitude /= (double) samples().size(); 91 | 92 | SimpleLocation center = SimpleLocation.builder() 93 | .latitude(latitude) 94 | .longitude(longitude) 95 | .radius(-1.0f) 96 | .changed(false) 97 | .build(); 98 | 99 | // get biggest distance 100 | 101 | float radius = 0.0f; 102 | 103 | for(SimpleLocation sample : samples()) { 104 | radius = Math.max(radius, center.distanceTo(sample)); 105 | } 106 | 107 | 108 | return SimpleLocation.builder() 109 | .latitude(latitude) 110 | .longitude(longitude) 111 | .radius(radius) 112 | .changed(false) 113 | .build(); 114 | } 115 | 116 | public abstract Builder buildUpon(); 117 | 118 | private static float perimeter(List samples) { 119 | float result = 0.0f; 120 | 121 | for (SimpleLocation sample1 : samples) { 122 | for (SimpleLocation sample2 : samples) { 123 | result += sample1.distanceTo(sample2); 124 | } 125 | } 126 | 127 | return result; 128 | } 129 | 130 | public float perimeter() { 131 | return perimeter(samples()); 132 | } 133 | 134 | @AutoValue.Builder 135 | public abstract static class Builder { 136 | public abstract Builder rfId(String rfId); 137 | public abstract Builder ssid(String ssid); 138 | public abstract Builder samples(List samples); 139 | public abstract Builder moveGuard(int moveGuard); 140 | public abstract Builder rfType(int rfType); 141 | public abstract AccessPoint build(); 142 | 143 | protected abstract String rfId(); 144 | protected abstract int moveGuard(); 145 | protected abstract ImmutableList samples(); 146 | protected abstract int rfType(); 147 | 148 | protected int samplesCount() { 149 | try { 150 | return samples().size(); 151 | } catch (Exception ex) { 152 | return 0; 153 | } 154 | } 155 | 156 | public Builder moved(int movedGuardCount) { 157 | return moveGuard(movedGuardCount); 158 | } 159 | 160 | public Builder decMoved() { 161 | return moveGuard(Math.max(0, moveGuard())); 162 | } 163 | 164 | public Builder addSample(SimpleLocation location) { 165 | return addSample(location, MIN_SAMPLES); 166 | } 167 | 168 | public Builder addSample(SimpleLocation location, int maxSamples) { 169 | maxSamples = Math.max(maxSamples, MIN_SAMPLES); 170 | 171 | if(samplesCount() < maxSamples) { 172 | List samples = new ArrayList<>(); 173 | 174 | if(samplesCount() != 0) { 175 | samples.addAll(samples()); 176 | } 177 | 178 | samples.add(location); 179 | if (DEBUG) { 180 | Log.i(TAG, "Simple add to " + rfId() + ", add " + location + ", result="+samples); 181 | } 182 | 183 | return samples(samples); 184 | } else { 185 | // We will take the new sample an see if we can make a triangle with 186 | // a larger perimeter by replacing one of our current samples with 187 | // the new one. 188 | 189 | List bestSamples = samples(); 190 | float bestPerimeter = perimeter(bestSamples); 191 | 192 | for (int i = 0; i < samples().size(); i++) { 193 | List samples = new ArrayList(samples()); 194 | samples.set(i, location); 195 | 196 | float guessPerimeter = perimeter(samples); 197 | 198 | if (guessPerimeter > bestPerimeter) { 199 | bestSamples = samples; 200 | bestPerimeter = guessPerimeter; 201 | 202 | if (DEBUG) { 203 | Log.i(TAG, "Better perimeter point found on " + rfId() + ", i=" + i); 204 | } 205 | } 206 | } 207 | 208 | return samples(bestSamples); 209 | } 210 | } 211 | 212 | public Builder clearSamples() { 213 | return samples(new ArrayList()); 214 | } 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /app/src/main/java/org/fitchfamily/android/wifi_backend/database/SamplerDatabase.java: -------------------------------------------------------------------------------- 1 | package org.fitchfamily.android.wifi_backend.database; 2 | 3 | /* 4 | * WiFi Backend for Unified Network Location 5 | * Copyright (C) 2014,2015 Tod Fitch 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import android.content.Context; 22 | import android.util.Log; 23 | 24 | import org.fitchfamily.android.wifi_backend.BuildConfig; 25 | import org.fitchfamily.android.wifi_backend.Configuration; 26 | import org.fitchfamily.android.wifi_backend.util.SimpleLocation; 27 | 28 | import java.io.File; 29 | 30 | /* 31 | * We estimate the AP location by keeping three samples that form a triangle. 32 | * Our best guess for the AP location is then the average of the lat/lon values 33 | * for each triangle vertice. 34 | * 35 | * We select the samples that form the triangle by trying to maximize the 36 | * perimeter distance. 37 | * 38 | * Field naming conventions are: 39 | * rfid - ID for RF source. For WiFi this is 40 | * the MAC address of the AP 41 | * type - Type of RF source: 42 | * 0 - WiFi AP 43 | * 1 - Mobile/Cell Tower 44 | * latitude - Latitude estimate for the RF source 45 | * longitude - Longitude estimate for the RF source 46 | * move_guard - Count down of times to ignore moved wifi AP 47 | * radius - Estimated coverage radius of RF source 48 | * lat1 - Latitude measure for sample 1 49 | * lon1 - Longitude measure for sample 1 50 | * lat2 - Latitude measure for sample 2 51 | * lon2 - Longitude measure for sample 2 52 | * lat3 - Latitude measure for sample 3 53 | * lon3 - Longitude measure for sample 3 54 | */ 55 | public class SamplerDatabase extends Database { 56 | private final static String TAG = "WiFiBackendSamplerDB"; 57 | private static final boolean DEBUG = BuildConfig.DEBUG; 58 | 59 | private static SamplerDatabase mInstance; 60 | private final Context context; 61 | 62 | private SamplerDatabase(Context context) { 63 | super(context); 64 | 65 | this.context = context; 66 | 67 | if (DEBUG) { 68 | Log.i(TAG, "samplerDatabase.samplerDatabase()"); 69 | } 70 | } 71 | 72 | public synchronized static SamplerDatabase getInstance(Context context) { 73 | if (mInstance == null) { 74 | final File oldFile = new File(context.getFilesDir(), "wifi.db"); 75 | final File newFile = context.getDatabasePath("wifi.db"); 76 | 77 | if(oldFile.exists()) { 78 | newFile.delete(); 79 | oldFile.renameTo(newFile); 80 | } 81 | 82 | mInstance = new SamplerDatabase(context); 83 | } 84 | 85 | return mInstance; 86 | } 87 | 88 | public void addSample(int rfType, String ssid, String rfId, SimpleLocation sampleLocation) { 89 | final long entryTime = System.currentTimeMillis(); 90 | 91 | if (DEBUG) { 92 | Log.i(TAG,"ID="+rfId+": Adding location="+sampleLocation.toString()); 93 | } 94 | AccessPoint accessPoint = query(rfId); 95 | 96 | if (accessPoint != null) { 97 | // We attempt to estimate the position of the AP by making as 98 | // large a triangle around it as possible. 99 | // At this point we have the specified amount of points already 100 | // in the database describing a triangle. 101 | 102 | float diff = accessPoint.estimateLocation().distanceTo(sampleLocation); 103 | 104 | if (diff >= Configuration.with(context).accessPointMoveThresholdInMeters()) { 105 | accessPoint = accessPoint.buildUpon() 106 | .ssid(ssid) 107 | .clearSamples() 108 | .addSample(sampleLocation) 109 | .moved(Configuration.with(context).accessPointMoveGuardSampleCount()) 110 | .build(); 111 | 112 | if (DEBUG) { 113 | Log.i(TAG, "Sample is " + diff + " from AP, assume AP " + accessPoint.rfId() + " has moved."); 114 | } 115 | } else { 116 | accessPoint = accessPoint.buildUpon() 117 | .ssid(ssid) 118 | .decMoved() 119 | .addSample(sampleLocation) 120 | .build(); 121 | } 122 | 123 | if (DEBUG) { 124 | Log.i(TAG, "Sample: " + accessPoint.toString()); 125 | } 126 | 127 | update(accessPoint); 128 | } else { 129 | insert( 130 | AccessPoint.builder() 131 | .ssid(ssid) 132 | .rfId(AccessPoint.bssid(rfId)) 133 | .moveGuard(0) 134 | .addSample(sampleLocation) 135 | .rfType(rfType) 136 | .build() 137 | ); 138 | } 139 | 140 | if (DEBUG) { 141 | Log.i(TAG,"addSample time: "+ (System.currentTimeMillis() - entryTime) + " ms"); 142 | } 143 | } 144 | 145 | public SamplerDatabase dropAccessPoint(String rfId) { 146 | dropAP(rfId); 147 | 148 | return this; 149 | } 150 | 151 | public SamplerDatabase dropAll() { 152 | dropAllAPs(); 153 | 154 | return this; 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /app/src/main/java/org/fitchfamily/android/wifi_backend/ui/AdvancedSettingsFragment.java: -------------------------------------------------------------------------------- 1 | package org.fitchfamily.android.wifi_backend.ui; 2 | 3 | /* 4 | * WiFi Backend for Unified Network Location 5 | * Copyright (C) 2014,2015 Tod Fitch 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | import com.github.machinarius.preferencefragment.PreferenceFragment; 21 | 22 | import org.androidannotations.annotations.EFragment; 23 | import org.androidannotations.annotations.PreferenceScreen; 24 | import org.fitchfamily.android.wifi_backend.R; 25 | 26 | @EFragment 27 | @PreferenceScreen(R.xml.advanced) 28 | public class AdvancedSettingsFragment extends PreferenceFragment { 29 | } -------------------------------------------------------------------------------- /app/src/main/java/org/fitchfamily/android/wifi_backend/ui/BaseDialogFragment.java: -------------------------------------------------------------------------------- 1 | package org.fitchfamily.android.wifi_backend.ui; 2 | 3 | /* 4 | * WiFi Backend for Unified Network Location 5 | * Copyright (C) 2014,2015 Tod Fitch 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import android.support.v4.app.DialogFragment; 22 | 23 | import com.octo.android.robospice.SpiceManager; 24 | 25 | import org.fitchfamily.android.wifi_backend.SpiceService; 26 | 27 | public abstract class BaseDialogFragment extends DialogFragment { 28 | private SpiceManager spiceManager = new SpiceManager(SpiceService.class); 29 | 30 | @Override 31 | public void onStart() { 32 | super.onStart(); 33 | spiceManager.start(getActivity()); 34 | } 35 | 36 | @Override 37 | public void onStop() { 38 | super.onStop(); 39 | spiceManager.shouldStop(); 40 | } 41 | 42 | public SpiceManager getSpiceManager() { 43 | return spiceManager; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/org/fitchfamily/android/wifi_backend/ui/EditTextPreference.java: -------------------------------------------------------------------------------- 1 | package org.fitchfamily.android.wifi_backend.ui; 2 | 3 | /* 4 | * WiFi Backend for Unified Network Location 5 | * Copyright (C) 2014,2015 Tod Fitch 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import android.content.Context; 22 | import android.util.AttributeSet; 23 | 24 | public class EditTextPreference extends android.preference.EditTextPreference { 25 | public EditTextPreference(Context context) { 26 | super(context); 27 | } 28 | 29 | public EditTextPreference(Context context, AttributeSet attributeSet) { 30 | super(context, attributeSet); 31 | } 32 | 33 | @Override 34 | public void setText(String text) { 35 | super.setText(text); 36 | notifyChanged(); 37 | } 38 | 39 | @Override 40 | public CharSequence getSummary() { 41 | final CharSequence summary = super.getSummary(); 42 | 43 | if (summary == null) { 44 | return null; 45 | } else { 46 | return String.format(summary.toString(), getText()); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/org/fitchfamily/android/wifi_backend/ui/MainActivity.java: -------------------------------------------------------------------------------- 1 | package org.fitchfamily.android.wifi_backend.ui; 2 | 3 | /* 4 | * WiFi Backend for Unified Network Location 5 | * Copyright (C) 2014,2015 Tod Fitch 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import android.content.Intent; 22 | import android.net.Uri; 23 | import android.os.Bundle; 24 | import android.support.v4.app.Fragment; 25 | import android.support.v7.app.AppCompatActivity; 26 | import android.support.v7.widget.Toolbar; 27 | import android.view.View; 28 | 29 | import com.mikepenz.aboutlibraries.Libs; 30 | import com.mikepenz.aboutlibraries.LibsBuilder; 31 | import com.mikepenz.google_material_typeface_library.GoogleMaterial; 32 | import com.mikepenz.materialdrawer.Drawer; 33 | import com.mikepenz.materialdrawer.DrawerBuilder; 34 | import com.mikepenz.materialdrawer.model.PrimaryDrawerItem; 35 | import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem; 36 | 37 | import org.androidannotations.annotations.AfterViews; 38 | import org.androidannotations.annotations.EActivity; 39 | import org.androidannotations.annotations.Extra; 40 | import org.androidannotations.annotations.InstanceState; 41 | import org.androidannotations.annotations.ViewById; 42 | import org.fitchfamily.android.wifi_backend.Constants; 43 | import org.fitchfamily.android.wifi_backend.R; 44 | 45 | @EActivity(R.layout.activity_main) 46 | public class MainActivity extends AppCompatActivity { 47 | private static final int SETTINGS = 1; 48 | private static final int ADVANCED = 2; 49 | private static final int LIBRARIES = 3; 50 | private static final int WEBSITE = 4; 51 | 52 | @Extra 53 | protected Action action; 54 | 55 | @ViewById 56 | protected Toolbar toolbar; 57 | 58 | @InstanceState 59 | protected Bundle drawerState; 60 | 61 | private Drawer drawer; 62 | 63 | @AfterViews 64 | protected void init() { 65 | toolbar.setTitle(R.string.app_title); 66 | 67 | drawer = new DrawerBuilder() 68 | .withActivity(this) 69 | .withToolbar(toolbar) 70 | .withFireOnInitialOnClick(drawerState == null) 71 | .withSavedInstance(drawerState) 72 | .addDrawerItems( 73 | new PrimaryDrawerItem() 74 | .withName(R.string.drawer_settings) 75 | .withIcon(GoogleMaterial.Icon.gmd_settings) 76 | .withIdentifier(SETTINGS), 77 | 78 | new PrimaryDrawerItem() 79 | .withName(R.string.drawer_advanced) 80 | .withIcon(GoogleMaterial.Icon.gmd_settings_applications) 81 | .withIdentifier(ADVANCED) 82 | ) 83 | .addStickyDrawerItems( 84 | new PrimaryDrawerItem() 85 | .withName(R.string.drawer_libraries) 86 | .withIcon(GoogleMaterial.Icon.gmd_info_outline) 87 | .withSelectable(false) 88 | .withIdentifier(LIBRARIES), 89 | 90 | new PrimaryDrawerItem() 91 | .withName(R.string.drawer_website) 92 | .withIcon(GoogleMaterial.Icon.gmd_info) 93 | .withSelectable(false) 94 | .withIdentifier(WEBSITE) 95 | ) 96 | .withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() { 97 | @Override 98 | public boolean onItemClick(View view, int position, IDrawerItem drawerItem) { 99 | if (drawerItem != null) { 100 | final int id = drawerItem.getIdentifier(); 101 | 102 | if(id == SETTINGS) { 103 | setFragment(new MainSettingsFragment_()); 104 | } else if (id == ADVANCED) { 105 | setFragment(new AdvancedSettingsFragment_()); 106 | } else if (id == LIBRARIES) { 107 | new LibsBuilder() 108 | .withFields(R.string.class.getFields()) 109 | .withActivityStyle(Libs.ActivityStyle.LIGHT_DARK_TOOLBAR) 110 | .start(MainActivity.this); 111 | } else if (id == WEBSITE) { 112 | startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(Constants.WEBSITE))); 113 | } 114 | } 115 | 116 | return false; 117 | } 118 | }) 119 | .build(); 120 | 121 | updateTitle(); 122 | 123 | if(action == Action.request_permission) { 124 | drawer.setSelection(SETTINGS); 125 | } 126 | } 127 | 128 | @Override 129 | public void onSaveInstanceState(Bundle outState) { 130 | drawer.saveInstanceState(drawerState = new Bundle()); 131 | super.onSaveInstanceState(outState); 132 | } 133 | 134 | private void setFragment(Fragment fragment) { 135 | getSupportFragmentManager().beginTransaction() 136 | .replace(R.id.container, fragment) 137 | .commit(); 138 | 139 | updateTitle(); 140 | } 141 | 142 | private void updateTitle() { 143 | IDrawerItem item = drawer == null ? null : drawer.getDrawerItem(drawer.getCurrentSelection()); 144 | 145 | if (item != null && item instanceof PrimaryDrawerItem) { 146 | toolbar.setSubtitle(((PrimaryDrawerItem) item).getName().getText(this)); 147 | } else { 148 | toolbar.setSubtitle(null); 149 | } 150 | } 151 | 152 | @Override 153 | public void onBackPressed() { 154 | if(drawer != null && drawer.isDrawerOpen()) { 155 | drawer.closeDrawer(); 156 | } else { 157 | super.onBackPressed(); 158 | } 159 | } 160 | 161 | public enum Action { 162 | request_permission 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /app/src/main/java/org/fitchfamily/android/wifi_backend/ui/data/CursorAdapter.java: -------------------------------------------------------------------------------- 1 | package org.fitchfamily.android.wifi_backend.ui.data; 2 | 3 | /* 4 | * WiFi Backend for Unified Network Location 5 | * Copyright (C) 2014,2015 Tod Fitch 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import android.database.Cursor; 22 | import android.support.annotation.NonNull; 23 | import android.support.v7.widget.RecyclerView; 24 | 25 | public abstract class CursorAdapter extends RecyclerView.Adapter { 26 | private Cursor cursor; 27 | 28 | protected void onCursorChanged(@NonNull Cursor cursor) { 29 | } 30 | 31 | @Override 32 | public int getItemCount() { 33 | return cursor == null ? 0 : cursor.getCount(); 34 | } 35 | 36 | public void swap(Cursor cursor) { 37 | this.cursor = cursor; 38 | 39 | if(cursor != null) { 40 | onCursorChanged(cursor); 41 | } 42 | 43 | notifyDataSetChanged(); 44 | } 45 | 46 | @Override 47 | public void onBindViewHolder(VH holder, int position) { 48 | bind(holder, getItem(position)); 49 | } 50 | 51 | public abstract void bind(VH holder, Cursor cursor); 52 | 53 | public Cursor getItem(int position) { 54 | cursor.moveToPosition(position); 55 | return cursor; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/org/fitchfamily/android/wifi_backend/ui/data/CursorLoader.java: -------------------------------------------------------------------------------- 1 | package org.fitchfamily.android.wifi_backend.ui.data; 2 | 3 | /* 4 | * WiFi Backend for Unified Network Location 5 | * Copyright (C) 2014,2015 Tod Fitch 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import android.content.BroadcastReceiver; 22 | import android.content.Context; 23 | import android.content.Intent; 24 | import android.content.IntentFilter; 25 | import android.database.Cursor; 26 | import android.support.v4.content.AsyncTaskLoader; 27 | import android.support.v4.content.LocalBroadcastManager; 28 | 29 | import org.fitchfamily.android.wifi_backend.database.SamplerDatabase; 30 | 31 | public class CursorLoader extends AsyncTaskLoader { 32 | private String table; 33 | private String[] columns; 34 | private String selection; 35 | private String[] selectionArgs; 36 | private String sortOrder; 37 | private Cursor cursor; 38 | private BroadcastReceiver changeReceiver; 39 | 40 | public CursorLoader(Context context) { 41 | super(context); 42 | } 43 | 44 | @Override 45 | public Cursor loadInBackground() { 46 | return SamplerDatabase.getInstance(getContext()).getReadableDatabase().query(table, columns, selection, selectionArgs, null, null, sortOrder); 47 | } 48 | 49 | @Override 50 | protected void onStartLoading() { 51 | super.onStartLoading(); 52 | 53 | if(changeReceiver == null) { 54 | changeReceiver = new BroadcastReceiver() { 55 | @Override 56 | public void onReceive(Context context, Intent intent) { 57 | onContentChanged(); 58 | } 59 | }; 60 | 61 | LocalBroadcastManager.getInstance(getContext()).registerReceiver(changeReceiver, new IntentFilter(SamplerDatabase.ACTION_DATA_CHANGED)); 62 | } 63 | 64 | if (cursor != null) { 65 | // deliver old data (if available) 66 | deliverResult(cursor); 67 | } 68 | 69 | forceLoad(); 70 | } 71 | 72 | @Override 73 | protected void onStopLoading() { 74 | super.deliverResult(null); 75 | super.onStopLoading(); 76 | cancelLoad(); 77 | } 78 | 79 | @Override 80 | protected void onReset() { 81 | super.onReset(); 82 | 83 | LocalBroadcastManager.getInstance(getContext()).unregisterReceiver(changeReceiver); 84 | changeReceiver = null; 85 | 86 | // stop loader 87 | onStopLoading(); 88 | 89 | if (cursor != null && !cursor.isClosed()) { 90 | cursor.close(); 91 | } 92 | 93 | cursor = null; 94 | } 95 | 96 | @Override 97 | public void deliverResult(Cursor cursor) { 98 | if (isReset()) { 99 | if (cursor != null) { 100 | cursor.close(); 101 | } 102 | } else { 103 | Cursor oldCursor = this.cursor; 104 | this.cursor = cursor; 105 | 106 | if (isStarted()) { 107 | super.deliverResult(cursor); 108 | } 109 | 110 | if (oldCursor != null && !oldCursor.isClosed()) { 111 | oldCursor.close(); 112 | } 113 | } 114 | } 115 | 116 | @Override 117 | public void onCanceled(Cursor cursor) { 118 | super.onCanceled(cursor); 119 | 120 | if (cursor != null && !cursor.isClosed()) { 121 | cursor.close(); 122 | } 123 | } 124 | 125 | public String table() { 126 | return table; 127 | } 128 | 129 | public CursorLoader table(String table) { 130 | this.table = table; 131 | return this; 132 | } 133 | 134 | public String[] columns() { 135 | return columns; 136 | } 137 | 138 | public CursorLoader columns(String[] columns) { 139 | this.columns = columns; 140 | return this; 141 | } 142 | 143 | public String selection() { 144 | return selection; 145 | } 146 | 147 | public CursorLoader selection(String selection) { 148 | this.selection = selection; 149 | return this; 150 | } 151 | 152 | public String[] selectionArgs() { 153 | return selectionArgs; 154 | } 155 | 156 | public CursorLoader selectionArgs(String[] selectionArgs) { 157 | this.selectionArgs = selectionArgs; 158 | return this; 159 | } 160 | 161 | public String sortOrder() { 162 | return sortOrder; 163 | } 164 | 165 | public CursorLoader sortOrder(String sortOrder) { 166 | this.sortOrder = sortOrder; 167 | return this; 168 | } 169 | 170 | public CursorLoader load() { 171 | forceLoad(); 172 | return this; 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /app/src/main/java/org/fitchfamily/android/wifi_backend/ui/data/WifiDetailActivity.java: -------------------------------------------------------------------------------- 1 | package org.fitchfamily.android.wifi_backend.ui.data; 2 | 3 | /* 4 | * WiFi Backend for Unified Network Location 5 | * Copyright (C) 2014,2015 Tod Fitch 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import android.content.Intent; 22 | import android.database.Cursor; 23 | import android.os.Bundle; 24 | import android.support.v4.app.LoaderManager; 25 | import android.support.v4.content.Loader; 26 | import android.support.v7.app.ActionBar; 27 | import android.support.v7.app.AppCompatActivity; 28 | import android.support.v7.widget.Toolbar; 29 | import android.view.MenuItem; 30 | 31 | import org.androidannotations.annotations.AfterViews; 32 | import org.androidannotations.annotations.EActivity; 33 | import org.androidannotations.annotations.Extra; 34 | import org.androidannotations.annotations.InstanceState; 35 | import org.androidannotations.annotations.ViewById; 36 | import org.fitchfamily.android.wifi_backend.R; 37 | import org.fitchfamily.android.wifi_backend.database.AccessPoint; 38 | import org.fitchfamily.android.wifi_backend.database.Database; 39 | 40 | /** 41 | * An activity representing a single WiFi detail screen. This 42 | * activity is only used narrow width devices. On tablet-size devices, 43 | * item details are presented side-by-side with a list of items 44 | * in a {@link WifiListActivity}. 45 | */ 46 | @EActivity(R.layout.activity_wifi_detail) 47 | public class WifiDetailActivity extends AppCompatActivity { 48 | @ViewById 49 | protected Toolbar toolbar; 50 | 51 | @Extra 52 | protected String rfId; 53 | 54 | @InstanceState 55 | protected boolean initialized; 56 | 57 | @AfterViews 58 | protected void init() { 59 | setSupportActionBar(toolbar); 60 | 61 | // Show the Up button in the action bar. 62 | ActionBar actionBar = getSupportActionBar(); 63 | if (actionBar != null) { 64 | actionBar.setDisplayHomeAsUpEnabled(true); 65 | } 66 | 67 | // savedInstanceState is non-null when there is fragment state 68 | // saved from previous configurations of this activity 69 | // (e.g. when rotating the screen from portrait to landscape). 70 | // In this case, the fragment will automatically be re-added 71 | // to its container so we don't need to manually add it. 72 | // For more information, see the Fragments API guide at: 73 | // 74 | // http://developer.android.com/guide/components/fragments.html 75 | // 76 | if (!initialized) { 77 | // Create the detail fragment and add it to the activity 78 | // using a fragment transaction. 79 | getSupportFragmentManager().beginTransaction() 80 | .replace( 81 | R.id.container, 82 | WifiDetailFragment_.builder() 83 | .rfId(rfId) 84 | .build() 85 | ) 86 | .commit(); 87 | 88 | initialized = true; 89 | } 90 | 91 | getSupportLoaderManager().initLoader(0, null, new LoaderManager.LoaderCallbacks() { 92 | @Override 93 | public Loader onCreateLoader(int id, Bundle args) { 94 | return new CursorLoader(WifiDetailActivity.this) 95 | .table(Database.TABLE_SAMPLES) 96 | .columns(new String[]{Database.COL_SSID}) 97 | .selection(Database.COL_RFID + " = ?") 98 | .selectionArgs(new String[]{rfId}); 99 | } 100 | 101 | @Override 102 | public void onLoadFinished(Loader loader, Cursor data) { 103 | if(data != null && data.moveToFirst()) { 104 | getSupportActionBar().setTitle(data.getString(0)); 105 | getSupportActionBar().setSubtitle(AccessPoint.readableBssid(rfId)); 106 | } else { 107 | getSupportActionBar().setTitle(AccessPoint.readableBssid(rfId)); 108 | getSupportActionBar().setSubtitle(null); 109 | } 110 | } 111 | 112 | @Override 113 | public void onLoaderReset(Loader loader) { 114 | getSupportActionBar().setTitle(rfId); 115 | getSupportActionBar().setSubtitle(null); 116 | } 117 | }); 118 | } 119 | 120 | @Override 121 | public boolean onOptionsItemSelected(MenuItem item) { 122 | final int id = item.getItemId(); 123 | 124 | if (id == android.R.id.home) { 125 | // This ID represents the Home or Up button. In the case of this 126 | // activity, the Up button is shown. For 127 | // more details, see the Navigation pattern on Android Design: 128 | // 129 | // http://developer.android.com/design/patterns/navigation.html#up-vs-back 130 | // 131 | navigateUpTo(new Intent(this, WifiListActivity_.class)); 132 | return true; 133 | } 134 | 135 | return super.onOptionsItemSelected(item); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /app/src/main/java/org/fitchfamily/android/wifi_backend/ui/data/WifiDetailFragment.java: -------------------------------------------------------------------------------- 1 | package org.fitchfamily.android.wifi_backend.ui.data; 2 | 3 | /* 4 | * WiFi Backend for Unified Network Location 5 | * Copyright (C) 2014,2015 Tod Fitch 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import android.content.Intent; 22 | import android.database.Cursor; 23 | import android.net.Uri; 24 | import android.os.Bundle; 25 | import android.support.v4.app.Fragment; 26 | import android.support.v4.app.LoaderManager; 27 | import android.support.v4.content.Loader; 28 | import android.view.View; 29 | import android.widget.TextView; 30 | 31 | import org.androidannotations.annotations.AfterViews; 32 | import org.androidannotations.annotations.Click; 33 | import org.androidannotations.annotations.EFragment; 34 | import org.androidannotations.annotations.FragmentArg; 35 | import org.androidannotations.annotations.ViewById; 36 | import org.fitchfamily.android.wifi_backend.Configuration; 37 | import org.fitchfamily.android.wifi_backend.R; 38 | import org.fitchfamily.android.wifi_backend.database.Database; 39 | 40 | /** 41 | * A fragment representing a single WiFi detail screen. 42 | * This fragment is either contained in a {@link WifiListActivity} 43 | * in two-pane mode (on tablets) or a {@link WifiDetailActivity} 44 | * on handsets. 45 | */ 46 | @EFragment(R.layout.wifi_detail) 47 | public class WifiDetailFragment extends Fragment { 48 | @FragmentArg 49 | protected String rfId; 50 | 51 | @ViewById 52 | protected TextView accuracy; 53 | 54 | @ViewById 55 | protected View container; 56 | 57 | @ViewById 58 | protected TextView samples; 59 | 60 | private double lat, lon, acc; 61 | private int smp; 62 | private String ssid; 63 | 64 | @AfterViews 65 | protected void init() { 66 | container.setVisibility(View.INVISIBLE); 67 | 68 | getLoaderManager().initLoader(0, null, new LoaderManager.LoaderCallbacks() { 69 | @Override 70 | public Loader onCreateLoader(int id, Bundle args) { 71 | return new CursorLoader(getContext()) 72 | .table(Database.TABLE_SAMPLES) 73 | .selection(Database.COL_RFID + " = ?") 74 | .selectionArgs(new String[]{rfId}) 75 | .columns(new String[]{ 76 | Database.COL_LATITUDE, 77 | Database.COL_LONGITUDE, 78 | Database.COL_RADIUS, 79 | Database.COL_SSID, 80 | Database.COL_LAT1, 81 | Database.COL_LON1, 82 | Database.COL_LAT2, 83 | Database.COL_LON2, 84 | Database.COL_LAT3, 85 | Database.COL_LON3 86 | }); 87 | } 88 | 89 | @Override 90 | public void onLoadFinished(Loader loader, Cursor cursor) { 91 | if (cursor != null && cursor.moveToFirst()) { 92 | container.setVisibility(View.VISIBLE); 93 | 94 | lat = cursor.getDouble(0); 95 | lon = cursor.getDouble(1); 96 | acc = Math.max(Configuration.with(getContext()).accessPointAssumedAccuracy(), cursor.getFloat(2)); 97 | ssid = cursor.getString(3); 98 | smp = countSamples(cursor, 4); 99 | 100 | WifiDetailFragment.this.accuracy.setText(accuracy()); 101 | WifiDetailFragment.this.samples.setText(samples()); 102 | } else { 103 | container.setVisibility(View.INVISIBLE); 104 | } 105 | } 106 | 107 | @Override 108 | public void onLoaderReset(Loader loader) { 109 | container.setVisibility(View.INVISIBLE); 110 | } 111 | }); 112 | } 113 | 114 | private static boolean hasSample(Cursor cursor, int index) { 115 | return cursor.getDouble(index) != 0.d || cursor.getDouble(index + 1) != 0.d; 116 | } 117 | 118 | private static int countSamples(Cursor cursor, int index) { 119 | int result = 0; 120 | 121 | for(int i = 0; i < 3; i++) { 122 | if(hasSample(cursor, index + (i * 2))) { 123 | result++; 124 | } 125 | } 126 | 127 | return result; 128 | } 129 | 130 | private String accuracy() { 131 | return getString(R.string.wifi_detail_accuracy, getString(R.string.pref_meters, String.valueOf((int) acc))); 132 | } 133 | 134 | private String samples() { 135 | return getString(R.string.pref_samples, String.valueOf(smp)); 136 | } 137 | 138 | @Click 139 | protected void map() { 140 | startActivity(new Intent(android.content.Intent.ACTION_VIEW, 141 | Uri.parse("geo:" + lat + "," + lon + "?q=" + lat + "," + lon) 142 | )); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /app/src/main/java/org/fitchfamily/android/wifi_backend/ui/data/WifiListActivity.java: -------------------------------------------------------------------------------- 1 | package org.fitchfamily.android.wifi_backend.ui.data; 2 | 3 | /* 4 | * WiFi Backend for Unified Network Location 5 | * Copyright (C) 2014,2015 Tod Fitch 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import android.database.Cursor; 22 | import android.os.Bundle; 23 | import android.support.v4.app.LoaderManager; 24 | import android.support.v4.app.NavUtils; 25 | import android.support.v4.content.Loader; 26 | import android.support.v7.app.ActionBar; 27 | import android.support.v7.app.AppCompatActivity; 28 | import android.support.v7.widget.RecyclerView; 29 | import android.support.v7.widget.Toolbar; 30 | import android.text.TextUtils; 31 | import android.view.MenuItem; 32 | import android.widget.EditText; 33 | 34 | import org.androidannotations.annotations.AfterViews; 35 | import org.androidannotations.annotations.EActivity; 36 | import org.androidannotations.annotations.TextChange; 37 | import org.androidannotations.annotations.ViewById; 38 | import org.fitchfamily.android.wifi_backend.Configuration; 39 | import org.fitchfamily.android.wifi_backend.R; 40 | import org.fitchfamily.android.wifi_backend.database.Database; 41 | 42 | import java.util.ArrayList; 43 | import java.util.List; 44 | 45 | /** 46 | * An activity representing a list of WiFis. This activity 47 | * has different presentations for handset and tablet-size devices. On 48 | * handsets, the activity presents a list of items, which when touched, 49 | * lead to a {@link WifiDetailActivity} representing 50 | * item details. On tablets, the activity presents the list of items and 51 | * item details side-by-side using two vertical panes. 52 | */ 53 | @EActivity(R.layout.activity_wifi_list) 54 | public class WifiListActivity extends AppCompatActivity { 55 | private static final int LOADER_ID = 0; 56 | 57 | /** 58 | * Whether or not the activity is in two-pane mode, i.e. running on a tablet 59 | * device. 60 | */ 61 | private boolean twoPane; 62 | 63 | @ViewById(R.id.wifi_list) 64 | protected RecyclerView recyclerView; 65 | 66 | @ViewById 67 | protected Toolbar toolbar; 68 | 69 | @ViewById 70 | EditText searchTerm; 71 | 72 | private WifiListAdapter adapter = new WifiListAdapter().listener(new WifiListAdapter.Listener() { 73 | @Override 74 | public void onWifiClicked(String rfId) { 75 | if (twoPane) { 76 | getSupportFragmentManager().beginTransaction() 77 | .replace( 78 | R.id.wifi_detail_container, 79 | WifiDetailFragment_.builder() 80 | .rfId(rfId) 81 | .build() 82 | ) 83 | .commit(); 84 | } else { 85 | WifiDetailActivity_.intent(WifiListActivity.this) 86 | .rfId(rfId) 87 | .start(); 88 | } 89 | } 90 | }); 91 | 92 | @AfterViews 93 | protected void init() { 94 | setSupportActionBar(toolbar); 95 | 96 | // Show the Up button in the action bar. 97 | ActionBar actionBar = getSupportActionBar(); 98 | if (actionBar != null) { 99 | actionBar.setDisplayHomeAsUpEnabled(true); 100 | } 101 | 102 | recyclerView.setAdapter(adapter); 103 | getSupportLoaderManager().initLoader(LOADER_ID, null, new LoaderManager.LoaderCallbacks() { 104 | @Override 105 | public Loader onCreateLoader(int id, Bundle args) { 106 | CursorLoader loader = new CursorLoader(WifiListActivity.this) 107 | .table(Database.TABLE_SAMPLES) 108 | .columns(new String[]{Database.COL_SSID, Database.COL_RFID}) 109 | .sortOrder(Database.COL_SSID + " ASC"); 110 | 111 | updateLoaderSelection(loader); 112 | 113 | return loader; 114 | } 115 | 116 | @Override 117 | public void onLoadFinished(Loader loader, Cursor data) { 118 | adapter.swap(data); 119 | } 120 | 121 | @Override 122 | public void onLoaderReset(Loader loader) { 123 | adapter.swap(null); 124 | } 125 | }); 126 | 127 | if (findViewById(R.id.wifi_detail_container) != null) { 128 | // The detail container view will be present only in the 129 | // large-screen layouts (res/values-w900dp). 130 | // If this view is present, then the 131 | // activity should be in two-pane mode. 132 | twoPane = true; 133 | } 134 | } 135 | 136 | @Override 137 | public boolean onOptionsItemSelected(MenuItem item) { 138 | final int id = item.getItemId(); 139 | 140 | if (id == android.R.id.home) { 141 | // This ID represents the Home or Up button. In the case of this 142 | // activity, the Up button is shown. Use NavUtils to allow users 143 | // to navigate up one level in the application structure. For 144 | // more details, see the Navigation pattern on Android Design: 145 | // 146 | // http://developer.android.com/design/patterns/navigation.html#up-vs-back 147 | // 148 | NavUtils.navigateUpFromSameTask(this); 149 | return true; 150 | } 151 | 152 | return super.onOptionsItemSelected(item); 153 | } 154 | 155 | @TextChange(R.id.search_term) 156 | protected void searchTermChanged() { 157 | Loader loader = getSupportLoaderManager().getLoader(LOADER_ID); 158 | 159 | if (loader != null) { 160 | updateLoaderSelection((CursorLoader) loader); 161 | loader.forceLoad(); 162 | } 163 | } 164 | 165 | private void updateLoaderSelection(CursorLoader loader) { 166 | boolean onlyChanged = Configuration.listOption() == 1; 167 | final String search = searchTerm.getText().toString(); 168 | 169 | String selection = ""; 170 | List selectionArgs = new ArrayList<>(); 171 | 172 | if (!TextUtils.isEmpty(search)) { 173 | selection = Database.COL_RFID + " LIKE ? OR " + Database.COL_SSID + " LIKE ?"; 174 | 175 | // two times because there are two placeholders 176 | selectionArgs.add("%" + /* remove ":" because they are not saved at the database */ search.replaceAll(":", "") + "%"); 177 | selectionArgs.add("%" + search + "%"); 178 | } 179 | 180 | if (onlyChanged) { 181 | if (TextUtils.isEmpty(selection)) { 182 | selection = Database.COL_CHANGED + "<> 0"; 183 | } else { 184 | selection = Database.COL_CHANGED + "<> 0 AND (" + selection + ")"; 185 | } 186 | } 187 | 188 | loader 189 | .selection(selection) 190 | .selectionArgs(selectionArgs.toArray(new String[selectionArgs.size()])); 191 | } 192 | 193 | @Override 194 | public void onBackPressed() { 195 | // clear search field on first back press 196 | 197 | if (TextUtils.isEmpty(searchTerm.getText().toString())) { 198 | super.onBackPressed(); 199 | } else { 200 | searchTerm.setText(""); 201 | } 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /app/src/main/java/org/fitchfamily/android/wifi_backend/ui/data/WifiListAdapter.java: -------------------------------------------------------------------------------- 1 | package org.fitchfamily.android.wifi_backend.ui.data; 2 | 3 | /* 4 | * WiFi Backend for Unified Network Location 5 | * Copyright (C) 2014,2015 Tod Fitch 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import android.database.Cursor; 22 | import android.support.annotation.NonNull; 23 | import android.support.v7.widget.RecyclerView; 24 | import android.view.LayoutInflater; 25 | import android.view.View; 26 | import android.view.ViewGroup; 27 | import android.widget.TextView; 28 | 29 | import org.fitchfamily.android.wifi_backend.R; 30 | import org.fitchfamily.android.wifi_backend.database.AccessPoint; 31 | import org.fitchfamily.android.wifi_backend.database.Database; 32 | 33 | public class WifiListAdapter extends CursorAdapter { 34 | private final View.OnClickListener clickListener = new View.OnClickListener() { 35 | @Override 36 | public void onClick(View v) { 37 | if(listener != null) { 38 | String rfId = (String) v.getTag(); 39 | 40 | listener.onWifiClicked(rfId); 41 | } 42 | } 43 | }; 44 | 45 | private Listener listener; 46 | private int columnSsid; 47 | private int columnRfId; 48 | 49 | public WifiListAdapter() { 50 | setHasStableIds(true); 51 | } 52 | 53 | @Override 54 | public long getItemId(int position) { 55 | return getItem(position).getString(columnRfId).hashCode(); 56 | } 57 | 58 | @Override 59 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 60 | View view = LayoutInflater.from(parent.getContext()) 61 | .inflate(R.layout.wifi_list_content, parent, false); 62 | view.setOnClickListener(clickListener); 63 | return new ViewHolder(view); 64 | } 65 | 66 | @Override 67 | protected void onCursorChanged(@NonNull Cursor cursor) { 68 | super.onCursorChanged(cursor); 69 | columnSsid = cursor.getColumnIndexOrThrow(Database.COL_SSID); 70 | columnRfId = cursor.getColumnIndexOrThrow(Database.COL_RFID); 71 | } 72 | 73 | @Override 74 | public void bind(ViewHolder holder, Cursor cursor) { 75 | holder.bind(cursor.getString(columnSsid), cursor.getString(columnRfId)); 76 | } 77 | 78 | public class ViewHolder extends RecyclerView.ViewHolder { 79 | private final TextView title, id; 80 | private final View view; 81 | 82 | public ViewHolder(View view) { 83 | super(view); 84 | this.view = view; 85 | 86 | id = (TextView) view.findViewById(R.id.id); 87 | title = (TextView) view.findViewById(R.id.title); 88 | } 89 | 90 | public ViewHolder bind(String ssid, String rfId) { 91 | view.setTag(rfId); 92 | 93 | title.setText(ssid); 94 | id.setText(AccessPoint.readableBssid(rfId)); 95 | 96 | return this; 97 | } 98 | } 99 | 100 | public WifiListAdapter listener(Listener listener) { 101 | this.listener = listener; 102 | return this; 103 | } 104 | 105 | public Listener listener() { 106 | return listener; 107 | } 108 | 109 | public interface Listener { 110 | void onWifiClicked(String rfId); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /app/src/main/java/org/fitchfamily/android/wifi_backend/ui/data/reset/ResetDatabaseDialogFragment.java: -------------------------------------------------------------------------------- 1 | package org.fitchfamily.android.wifi_backend.ui.data.reset; 2 | 3 | import android.app.Dialog; 4 | import android.content.DialogInterface; 5 | import android.os.Bundle; 6 | import android.support.annotation.NonNull; 7 | import android.support.v4.app.DialogFragment; 8 | import android.support.v4.app.FragmentManager; 9 | import android.support.v7.app.AlertDialog; 10 | 11 | import org.fitchfamily.android.wifi_backend.R; 12 | 13 | public class ResetDatabaseDialogFragment extends DialogFragment { 14 | private static final String TAG = "ResetDatabaseDialogFragment"; 15 | 16 | @NonNull 17 | @Override 18 | public Dialog onCreateDialog(Bundle savedInstanceState) { 19 | return new AlertDialog.Builder(getContext()) 20 | .setTitle(R.string.data_reset) 21 | .setMessage(R.string.data_reset_warning) 22 | .setNegativeButton(R.string.data_reset_no, null) 23 | .setPositiveButton(R.string.data_reset_yes, new DialogInterface.OnClickListener() { 24 | @Override 25 | public void onClick(DialogInterface dialogInterface, int i) { 26 | ResetProgressDialog_.builder().build().show(getFragmentManager()); 27 | } 28 | }) 29 | .create(); 30 | } 31 | 32 | public void show(FragmentManager fragmentManager) { 33 | show(fragmentManager, TAG); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/org/fitchfamily/android/wifi_backend/ui/data/reset/ResetProgressDialog.java: -------------------------------------------------------------------------------- 1 | package org.fitchfamily.android.wifi_backend.ui.data.reset; 2 | 3 | /* 4 | * WiFi Backend for Unified Network Location 5 | * Copyright (C) 2014,2015 Tod Fitch 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import android.app.Dialog; 22 | import android.app.ProgressDialog; 23 | import android.os.Bundle; 24 | import android.support.annotation.NonNull; 25 | import android.support.v4.app.FragmentManager; 26 | import android.util.Log; 27 | import android.widget.Toast; 28 | 29 | import com.octo.android.robospice.persistence.DurationInMillis; 30 | import com.octo.android.robospice.persistence.exception.SpiceException; 31 | import com.octo.android.robospice.request.listener.RequestListener; 32 | 33 | import org.androidannotations.annotations.EFragment; 34 | import org.fitchfamily.android.wifi_backend.BuildConfig; 35 | import org.fitchfamily.android.wifi_backend.R; 36 | import org.fitchfamily.android.wifi_backend.data.ResetSpiceRequest; 37 | import org.fitchfamily.android.wifi_backend.ui.BaseDialogFragment; 38 | 39 | @EFragment 40 | public class ResetProgressDialog extends BaseDialogFragment implements RequestListener { 41 | private static final String TAG = "WiFiBackendResetDlg"; 42 | 43 | @Override 44 | public void onStart() { 45 | super.onStart(); 46 | 47 | getSpiceManager().execute( 48 | new ResetSpiceRequest(getContext().getApplicationContext()), 49 | TAG, 50 | DurationInMillis.ALWAYS_EXPIRED, 51 | this 52 | ); 53 | } 54 | 55 | @NonNull 56 | @Override 57 | public Dialog onCreateDialog(Bundle savedInstanceState) { 58 | ProgressDialog progressDialog = new ProgressDialog(getActivity()); 59 | progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); 60 | progressDialog.setIndeterminate(true); 61 | progressDialog.setMessage(getString(R.string.data_reset)); 62 | progressDialog.setProgressNumberFormat(null); 63 | progressDialog.setCancelable(false); 64 | progressDialog.setCanceledOnTouchOutside(false); 65 | 66 | return progressDialog; 67 | } 68 | 69 | public void show(FragmentManager fragmentManager) { 70 | show(fragmentManager, TAG); 71 | } 72 | 73 | @Override 74 | public void onRequestSuccess(ResetSpiceRequest.Result result) { 75 | dismissAllowingStateLoss(); 76 | } 77 | 78 | @Override 79 | public void onRequestFailure(SpiceException spiceException) { 80 | // should not happen 81 | Toast.makeText(getContext(), "reset failed", Toast.LENGTH_SHORT).show(); 82 | 83 | if (BuildConfig.DEBUG) { 84 | Log.w(TAG, "error cleaning database", spiceException); 85 | } 86 | 87 | dismissAllowingStateLoss(); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /app/src/main/java/org/fitchfamily/android/wifi_backend/ui/data/transfer/ExportProgressDialog.java: -------------------------------------------------------------------------------- 1 | package org.fitchfamily.android.wifi_backend.ui.data.transfer; 2 | 3 | /* 4 | * WiFi Backend for Unified Network Location 5 | * Copyright (C) 2014,2015 Tod Fitch 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import android.net.Uri; 22 | import android.support.v4.app.FragmentManager; 23 | 24 | import com.octo.android.robospice.request.SpiceRequest; 25 | 26 | import org.androidannotations.annotations.EFragment; 27 | import org.androidannotations.annotations.FragmentArg; 28 | import org.fitchfamily.android.wifi_backend.Configuration; 29 | import org.fitchfamily.android.wifi_backend.R; 30 | import org.fitchfamily.android.wifi_backend.data.ExportSpiceRequest; 31 | 32 | @EFragment 33 | public class ExportProgressDialog extends OperationProgressDialog { 34 | 35 | private static final String TAG = "WiFiBackendExportDlg"; 36 | 37 | @FragmentArg 38 | protected Uri uri; 39 | 40 | @Override 41 | protected String getMessage() { 42 | String message = getString(R.string.data_export_all); 43 | switch (Configuration.exportOption()) { 44 | case Configuration.EXPORT_OPTION_CHANGED: 45 | message = getString(R.string.data_export_changed);; 46 | break; 47 | 48 | default: 49 | } 50 | 51 | return message; 52 | } 53 | 54 | @Override 55 | protected String getFailureMessage() { 56 | return getString(R.string.data_export_error); 57 | } 58 | 59 | @Override 60 | protected int getMaxProgress() { 61 | return ExportSpiceRequest.MAX_PROGRESS; 62 | } 63 | 64 | @Override 65 | protected SpiceRequest getRequest() { 66 | return new ExportSpiceRequest(getContext(), uri); 67 | } 68 | 69 | @Override 70 | protected Object getCacheKey() { 71 | return uri.toString(); 72 | } 73 | 74 | public void show(FragmentManager fragmentManager) { 75 | show(fragmentManager, TAG); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /app/src/main/java/org/fitchfamily/android/wifi_backend/ui/data/transfer/ImportProgressDialog.java: -------------------------------------------------------------------------------- 1 | package org.fitchfamily.android.wifi_backend.ui.data.transfer; 2 | 3 | /* 4 | * WiFi Backend for Unified Network Location 5 | * Copyright (C) 2014,2015 Tod Fitch 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import android.net.Uri; 22 | import android.support.v4.app.FragmentManager; 23 | 24 | import com.octo.android.robospice.request.SpiceRequest; 25 | 26 | import org.androidannotations.annotations.EFragment; 27 | import org.androidannotations.annotations.FragmentArg; 28 | import org.fitchfamily.android.wifi_backend.R; 29 | import org.fitchfamily.android.wifi_backend.data.ImportSpiceRequest; 30 | 31 | @EFragment 32 | public class ImportProgressDialog extends OperationProgressDialog { 33 | 34 | private static final String TAG = "WiFiBackendImportDlg"; 35 | 36 | @FragmentArg 37 | protected Uri uri; 38 | 39 | public void show(FragmentManager fragmentManager) { 40 | show(fragmentManager, TAG); 41 | } 42 | 43 | @Override 44 | protected String getMessage() { 45 | return getString(R.string.data_import); 46 | } 47 | 48 | @Override 49 | protected String getFailureMessage() { 50 | return getString(R.string.data_import_error); 51 | } 52 | 53 | @Override 54 | protected int getMaxProgress() { 55 | return ImportSpiceRequest.MAX_PROGRESS; 56 | } 57 | 58 | @Override 59 | protected SpiceRequest getRequest() { 60 | return new ImportSpiceRequest(getActivity(), uri); 61 | } 62 | 63 | @Override 64 | protected Object getCacheKey() { 65 | return uri.toString(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /app/src/main/java/org/fitchfamily/android/wifi_backend/ui/data/transfer/OperationProgressDialog.java: -------------------------------------------------------------------------------- 1 | package org.fitchfamily.android.wifi_backend.ui.data.transfer; 2 | 3 | /* 4 | * WiFi Backend for Unified Network Location 5 | * Copyright (C) 2014,2015 Tod Fitch 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import android.app.Dialog; 22 | import android.app.ProgressDialog; 23 | import android.os.Bundle; 24 | import android.support.annotation.NonNull; 25 | import android.widget.Toast; 26 | 27 | import com.octo.android.robospice.persistence.DurationInMillis; 28 | import com.octo.android.robospice.persistence.exception.SpiceException; 29 | import com.octo.android.robospice.request.SpiceRequest; 30 | import com.octo.android.robospice.request.listener.PendingRequestListener; 31 | import com.octo.android.robospice.request.listener.RequestProgress; 32 | import com.octo.android.robospice.request.listener.RequestProgressListener; 33 | 34 | import org.androidannotations.annotations.EFragment; 35 | import org.androidannotations.annotations.InstanceState; 36 | import org.fitchfamily.android.wifi_backend.ui.BaseDialogFragment; 37 | 38 | @EFragment 39 | public abstract class OperationProgressDialog extends BaseDialogFragment implements RequestProgressListener, PendingRequestListener { 40 | private ProgressDialog progressDialog; 41 | 42 | @InstanceState 43 | protected boolean hasStartedRequest; 44 | 45 | @Override 46 | public void onStart() { 47 | super.onStart(); 48 | 49 | if(!hasStartedRequest) { 50 | getSpiceManager().execute(getRequest(), getCacheKey(), DurationInMillis.ALWAYS_EXPIRED, this); 51 | 52 | hasStartedRequest = true; 53 | } else { 54 | getSpiceManager().addListenerIfPending(getRequest().getResultType(), getCacheKey(), this); 55 | } 56 | } 57 | 58 | @NonNull 59 | @Override 60 | public Dialog onCreateDialog(Bundle savedInstanceState) { 61 | final int maxProgress = getMaxProgress(); 62 | 63 | progressDialog = new ProgressDialog(getActivity()); 64 | progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); 65 | progressDialog.setIndeterminate(maxProgress == 0); 66 | progressDialog.setTitle(getMessage()); 67 | progressDialog.setMax(getMaxProgress()); 68 | progressDialog.setProgressNumberFormat(null); 69 | progressDialog.setCancelable(false); 70 | progressDialog.setCanceledOnTouchOutside(false); 71 | 72 | return progressDialog; 73 | } 74 | 75 | protected abstract String getMessage(); 76 | protected abstract String getFailureMessage(); 77 | protected abstract SpiceRequest getRequest(); 78 | protected abstract Object getCacheKey(); 79 | protected int getMaxProgress() { 80 | return 0; 81 | }; 82 | 83 | @Override 84 | public void onRequestProgressUpdate(RequestProgress progress) { 85 | if(progressDialog != null) { 86 | progressDialog.setProgress((int) progress.getProgress()); 87 | } 88 | } 89 | 90 | @Override 91 | public void onRequestNotFound() { 92 | dismissAllowingStateLoss(); 93 | } 94 | 95 | @Override 96 | public void onRequestSuccess(R result) { 97 | dismissAllowingStateLoss(); 98 | } 99 | 100 | @Override 101 | public void onRequestFailure(SpiceException exception) { 102 | Toast.makeText(getActivity(), getFailureMessage(), Toast.LENGTH_LONG).show(); 103 | dismissAllowingStateLoss(); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /app/src/main/java/org/fitchfamily/android/wifi_backend/ui/statistic/DatabaseStatistic.java: -------------------------------------------------------------------------------- 1 | package org.fitchfamily.android.wifi_backend.ui.statistic; 2 | 3 | /* 4 | * WiFi Backend for Unified Network Location 5 | * Copyright (C) 2014,2015 Tod Fitch 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import com.google.auto.value.AutoValue; 22 | 23 | @AutoValue 24 | public abstract class DatabaseStatistic { 25 | public static Builder builder() { 26 | return new AutoValue_DatabaseStatistic.Builder(); 27 | } 28 | 29 | DatabaseStatistic() { 30 | 31 | } 32 | 33 | public abstract int accessPointCount(); 34 | public abstract int accessPointChangeCount(); 35 | 36 | @AutoValue.Builder 37 | public abstract static class Builder { 38 | Builder() { 39 | 40 | } 41 | 42 | public abstract Builder accessPointCount(int count); 43 | public abstract Builder accessPointChangeCount(int count); 44 | public abstract DatabaseStatistic build(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/org/fitchfamily/android/wifi_backend/ui/statistic/DatabaseStatisticLoader.java: -------------------------------------------------------------------------------- 1 | package org.fitchfamily.android.wifi_backend.ui.statistic; 2 | 3 | /* 4 | * WiFi Backend for Unified Network Location 5 | * Copyright (C) 2014,2015 Tod Fitch 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import android.content.BroadcastReceiver; 22 | import android.content.Context; 23 | import android.content.Intent; 24 | import android.content.IntentFilter; 25 | import android.support.v4.content.AsyncTaskLoader; 26 | import android.support.v4.content.LocalBroadcastManager; 27 | 28 | import org.fitchfamily.android.wifi_backend.database.SamplerDatabase; 29 | 30 | public class DatabaseStatisticLoader extends AsyncTaskLoader { 31 | private BroadcastReceiver changeReceiver; 32 | 33 | public DatabaseStatisticLoader(Context context) { 34 | super(context); 35 | } 36 | 37 | @Override 38 | public DatabaseStatistic loadInBackground() { 39 | return DatabaseStatistic.builder() 40 | .accessPointCount(SamplerDatabase.getInstance(getContext()).getAccessPointCount(false)) 41 | .accessPointChangeCount(SamplerDatabase.getInstance(getContext()).getAccessPointCount(true)) 42 | .build(); 43 | } 44 | 45 | @Override 46 | protected void onStartLoading() { 47 | super.onStartLoading(); 48 | 49 | if(changeReceiver == null) { 50 | changeReceiver = new BroadcastReceiver() { 51 | @Override 52 | public void onReceive(Context context, Intent intent) { 53 | onContentChanged(); 54 | } 55 | }; 56 | 57 | LocalBroadcastManager.getInstance(getContext()).registerReceiver(changeReceiver, new IntentFilter(SamplerDatabase.ACTION_DATA_CHANGED)); 58 | } 59 | 60 | forceLoad(); 61 | } 62 | 63 | @Override 64 | protected void onReset() { 65 | super.onReset(); 66 | 67 | LocalBroadcastManager.getInstance(getContext()).unregisterReceiver(changeReceiver); 68 | changeReceiver = null; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /app/src/main/java/org/fitchfamily/android/wifi_backend/util/AgeValue.java: -------------------------------------------------------------------------------- 1 | package org.fitchfamily.android.wifi_backend.util; 2 | 3 | /* 4 | * WiFi Backend for Unified Network Location 5 | * Copyright (C) 2014,2015 Tod Fitch 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import android.os.SystemClock; 22 | 23 | public class AgeValue { 24 | public static AgeValue create() { 25 | return new AgeValue(); 26 | } 27 | 28 | private T value; 29 | private long time; 30 | 31 | private AgeValue() { 32 | 33 | } 34 | 35 | /** 36 | * Use this function to set the value 37 | * @param value the new value 38 | * @return this object 39 | */ 40 | public AgeValue value(T value) { 41 | this.value = value; 42 | this.time = now(); 43 | 44 | return this; 45 | } 46 | 47 | /** 48 | * Use this function to get the last value 49 | * @return the last value 50 | */ 51 | public T value() { 52 | return value; 53 | } 54 | 55 | /** 56 | * Use this function to get the age of the last value 57 | * @return the age of the last value in milliseconds 58 | */ 59 | public long age() { 60 | if(value == null) { 61 | return Long.MAX_VALUE; 62 | } else { 63 | return now() - time; 64 | } 65 | } 66 | 67 | /** 68 | * Use this function to get the time which is never decreasing 69 | * @return the time in milliseconds 70 | */ 71 | private static long now() { 72 | return SystemClock.elapsedRealtime(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /app/src/main/java/org/fitchfamily/android/wifi_backend/util/CountingInputStream.java: -------------------------------------------------------------------------------- 1 | package org.fitchfamily.android.wifi_backend.data.util; 2 | 3 | /* 4 | * WiFi Backend for Unified Network Location 5 | * Copyright (C) 2014,2015 Tod Fitch 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import android.support.annotation.NonNull; 22 | 23 | import java.io.FilterInputStream; 24 | import java.io.IOException; 25 | import java.io.InputStream; 26 | 27 | public class CountingInputStream extends FilterInputStream { 28 | private long bytesRead = 0; 29 | 30 | public CountingInputStream(InputStream inputStream) { 31 | super(inputStream); 32 | } 33 | 34 | @Override 35 | public int read() throws IOException { 36 | final int result = super.read(); 37 | 38 | if(result != -1) { 39 | bytesRead++; 40 | } 41 | 42 | return result; 43 | } 44 | 45 | @Override 46 | public int read(@NonNull byte[] buffer) throws IOException { 47 | final int result = super.read(buffer); 48 | 49 | if(result != -1) { 50 | bytesRead += result; 51 | } 52 | 53 | return result; 54 | } 55 | 56 | @Override 57 | public int read(@NonNull byte[] buffer, int byteOffset, int byteCount) throws IOException { 58 | final int result = super.read(buffer, byteOffset, byteCount); 59 | 60 | if(result != -1) { 61 | bytesRead += result; 62 | } 63 | 64 | return result; 65 | } 66 | 67 | public long getBytesRead() { 68 | return bytesRead; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /app/src/main/java/org/fitchfamily/android/wifi_backend/util/SimpleLocation.java: -------------------------------------------------------------------------------- 1 | package org.fitchfamily.android.wifi_backend.util; 2 | 3 | /* 4 | * WiFi Backend for Unified Network Location 5 | * Copyright (C) 2014,2015 Tod Fitch 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import android.support.annotation.Nullable; 22 | 23 | import com.google.auto.value.AutoValue; 24 | import org.fitchfamily.android.wifi_backend.util.distanceCache; 25 | 26 | @AutoValue 27 | public abstract class SimpleLocation { 28 | 29 | public static Builder builder() { 30 | return new AutoValue_SimpleLocation.Builder(); 31 | } 32 | 33 | public abstract double latitude(); 34 | public abstract double longitude(); 35 | public abstract float radius(); 36 | public abstract boolean changed(); 37 | 38 | private static distanceCache distance = new distanceCache(); 39 | 40 | public static SimpleLocation fromAndroidLocation(android.location.Location location) { 41 | return builder() 42 | .latitude(location.getLatitude()) 43 | .longitude(location.getLongitude()) 44 | .radius(location.getAccuracy()) 45 | .changed(true) 46 | .build(); 47 | } 48 | 49 | public android.location.Location toAndroidLocation() { 50 | android.location.Location location = new android.location.Location("wifi"); 51 | location.setLatitude(latitude()); 52 | location.setLongitude(longitude()); 53 | location.setAccuracy(radius()); 54 | 55 | return location; 56 | } 57 | 58 | public static SimpleLocation fromLatLon(double lat, double lon, boolean changed) { 59 | return builder().latitude(lat).longitude(lon).radius(150f).changed(changed).build(); 60 | } 61 | 62 | public static SimpleLocation fromLatLon(String lat, String lon, boolean changed) { 63 | return fromLatLon(Double.parseDouble(lat),Double.parseDouble(lon),changed); 64 | } 65 | 66 | public float distanceTo(SimpleLocation location) { 67 | return distanceTo(location.toAndroidLocation()); 68 | } 69 | 70 | public float distanceTo(android.location.Location location) { 71 | return distance.distanceBetween(this.toAndroidLocation(), location); 72 | } 73 | 74 | @AutoValue.Builder 75 | public abstract static class Builder { 76 | Builder() { 77 | } 78 | 79 | public abstract Builder latitude(double latitude); 80 | public abstract Builder longitude(double longitude); 81 | public abstract Builder radius(float radius); 82 | public abstract Builder changed(boolean changed); 83 | public abstract SimpleLocation build(); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /app/src/main/java/org/fitchfamily/android/wifi_backend/util/distanceCache.java: -------------------------------------------------------------------------------- 1 | package org.fitchfamily.android.wifi_backend.util; 2 | 3 | /* 4 | * WiFi Backend for Unified Network Location 5 | * Copyright (C) 2014,2015 Tod Fitch 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | import android.location.Location; 21 | import android.support.v4.util.LruCache; 22 | import android.util.Log; 23 | 24 | import org.fitchfamily.android.wifi_backend.BuildConfig; 25 | 26 | public class distanceCache { 27 | private static final String TAG = "WiFiBackendDistCache"; 28 | private static final boolean DEBUG = BuildConfig.DEBUG; 29 | 30 | private class cacheKey { 31 | double lat1; 32 | double lon1; 33 | double lat2; 34 | double lon2; 35 | 36 | cacheKey(Location first, Location second) { 37 | lat1 = first.getLatitude(); 38 | lon1 = first.getLongitude(); 39 | lat2 = second.getLatitude(); 40 | lon2 = second.getLongitude(); 41 | } 42 | 43 | private int longHash(long f) { 44 | return (int)(f ^ (f >>> 32)); 45 | } 46 | 47 | public int hashCode() { 48 | int result = 101; 49 | result = 37 * result + longHash(Double.doubleToLongBits(lat1)); 50 | result = 37 * result + longHash(Double.doubleToLongBits(lon1)); 51 | result = 37 * result + longHash(Double.doubleToLongBits(lat2)); 52 | result = 37 * result + longHash(Double.doubleToLongBits(lon2)); 53 | return result; 54 | } 55 | 56 | public boolean equals(Object obj) { 57 | cacheKey other = (cacheKey)obj; 58 | 59 | return (lat1 == other.lat1) && (lon1 == other.lon1) && 60 | (lat2 == other.lat2) && (lon2 == other.lon2); 61 | } 62 | } 63 | protected class distanceRec { 64 | public float distance; 65 | } 66 | 67 | private static final LruCache distanceCache = new LruCache<>(1000); 68 | private static long myHits = 0; 69 | private static long myMisses = 0; 70 | 71 | public synchronized float distanceBetween(Location loc1, Location loc2) { 72 | cacheKey key = new cacheKey(loc1, loc2); 73 | cacheKey key1 = new cacheKey(loc2, loc1); 74 | distanceRec cachedValue = distanceCache.get(key); 75 | if (cachedValue == null) { 76 | cachedValue = distanceCache.get(key1); 77 | } 78 | if (cachedValue == null) { 79 | myMisses++; 80 | cachedValue = new distanceRec(); 81 | cachedValue.distance = loc1.distanceTo(loc2); 82 | distanceCache.put(key,cachedValue); 83 | } else 84 | myHits++; 85 | return cachedValue.distance; 86 | } 87 | 88 | public void logCacheStats() { 89 | Log.i(TAG, "LRU stats: Hits=" + distanceCache.hitCount() + 90 | ", Misses=" + distanceCache.missCount() + 91 | ", Entries=" + distanceCache.size() + 92 | ", MyHits=" + myHits + 93 | ", MyMisses=" + myMisses); 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /app/src/main/java/org/fitchfamily/android/wifi_backend/wifi/WifiAccessPoint.java: -------------------------------------------------------------------------------- 1 | package org.fitchfamily.android.wifi_backend.wifi; 2 | 3 | /* 4 | * WiFi Backend for Unified Network Location 5 | * Copyright (C) 2014,2015 Tod Fitch 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import com.google.auto.value.AutoValue; 22 | 23 | @AutoValue 24 | public abstract class WifiAccessPoint { 25 | WifiAccessPoint() { 26 | 27 | } 28 | 29 | public static Builder builder() { 30 | return new AutoValue_WifiAccessPoint.Builder(); 31 | } 32 | 33 | public abstract String ssid(); 34 | public abstract String rfId(); 35 | public abstract int level(); 36 | public abstract int rfType(); 37 | 38 | @AutoValue.Builder 39 | public abstract static class Builder { 40 | Builder() { 41 | 42 | } 43 | 44 | public abstract Builder ssid(String ssid); 45 | public abstract Builder rfId(String rfId); 46 | public abstract Builder level(int level); 47 | public abstract WifiAccessPoint build(); 48 | public abstract Builder rfType(int type); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/org/fitchfamily/android/wifi_backend/wifi/WifiBlacklist.java: -------------------------------------------------------------------------------- 1 | package org.fitchfamily.android.wifi_backend.wifi; 2 | 3 | /* 4 | * WiFi Backend for Unified Network Location 5 | * Copyright (C) 2014,2015 Tod Fitch 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import java.util.Locale; 22 | 23 | public abstract class WifiBlacklist { 24 | private WifiBlacklist() { 25 | 26 | } 27 | 28 | public static boolean ignore(String SSID) { 29 | String SSIDLower = SSID.toLowerCase(Locale.US); 30 | 31 | return (SSIDLower.endsWith("_nomap") || // Google unsubscibe option 32 | 33 | SSID.startsWith("Audi") || // some cars seem to have this AP on-board 34 | SSID.startsWith("BusWiFi") || // Some transit buses in LA Calif metro area 35 | SSID.startsWith("CellSpot") || // T-Mobile US portable cell based WiFi 36 | SSID.startsWith("CoachAmerica") || // Charter bus service with on board WiFi 37 | SSID.startsWith("DisneyLandResortExpress") || // Bus with on board WiFi 38 | SSID.startsWith("HUAWEI-") || 39 | SSID.startsWith("Samsung Galaxy") || // mobile AP 40 | SSID.startsWith("TaxiLinQ") || // Mobile AP, see http://www.mobile-knowledge.com/products/driver-solutions/taxilinq/ 41 | 42 | SSIDLower.contains("admin@ms ") || // WLAN network on Hurtigruten ships 43 | SSIDLower.contains("android") || // mobile AP 44 | SSIDLower.contains("contiki-wifi") || // WLAN network on board of bus 45 | SSIDLower.contains("db ic bus") || // WLAN network on board of German bus 46 | SSIDLower.contains("deinbus.de") || // WLAN network on board of German bus 47 | SSIDLower.contains("ecolines") || // WLAN network on board of German bus 48 | SSIDLower.contains("eurolines_wifi") || // WLAN network on board of German bus 49 | SSIDLower.contains("fernbus") || // WLAN network on board of German bus 50 | SSIDLower.contains("flixbus") || // WLAN network on board of German bus 51 | SSIDLower.contains("guest@ms ") || // WLAN network on Hurtigruten ships 52 | SSIDLower.contains("ipad") || // mobile AP 53 | SSIDLower.contains("iphone") || // mobile AP 54 | SSIDLower.contains("mobile hotspot") || // e.g "MetroPCS Portable Mobile Hotspot" 55 | SSIDLower.contains("motorola") || // mobile AP 56 | SSIDLower.contains("muenchenlinie") || // WLAN network on board of bus 57 | SSIDLower.contains("nsb_interakti") || 58 | SSIDLower.contains("postbus") || // WLAN network on board of bus line 59 | SSIDLower.contains("telekom_ice") || // WLAN network on DB trains 60 | 61 | SSIDLower.contentEquals("amtrakconnect") || // WLAN network on USA Amtrak trains 62 | SSIDLower.contentEquals("amtrak") || // WLAN network on USA Amtrak trains 63 | SSIDLower.contentEquals("megabus") // WLAN network on MegaBus US bus 64 | ); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /app/src/main/java/org/fitchfamily/android/wifi_backend/wifi/WifiCompat.java: -------------------------------------------------------------------------------- 1 | package org.fitchfamily.android.wifi_backend.wifi; 2 | 3 | /* 4 | * WiFi Backend for Unified Network Location 5 | * Copyright (C) 2014,2015 Tod Fitch 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import android.net.wifi.WifiManager; 22 | 23 | public abstract class WifiCompat { 24 | private WifiCompat() { 25 | 26 | } 27 | 28 | public static boolean isScanAlwaysAvailable(WifiManager wifiManager) { 29 | try { 30 | return wifiManager.isScanAlwaysAvailable(); 31 | } catch (NoSuchMethodError e) { 32 | return false; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/org/fitchfamily/android/wifi_backend/wifi/WifiReceiver.java: -------------------------------------------------------------------------------- 1 | package org.fitchfamily.android.wifi_backend.wifi; 2 | 3 | /* 4 | * WiFi Backend for Unified Network Location 5 | * Copyright (C) 2014,2015 Tod Fitch 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import java.util.ArrayList; 22 | import java.util.Collections; 23 | import java.util.List; 24 | import java.util.Locale; 25 | 26 | import android.content.BroadcastReceiver; 27 | import android.content.Context; 28 | import android.content.Intent; 29 | import android.net.wifi.ScanResult; 30 | import android.net.wifi.WifiManager; 31 | import android.os.Bundle; 32 | import android.support.annotation.NonNull; 33 | import android.util.Log; 34 | 35 | import org.fitchfamily.android.wifi_backend.BuildConfig; 36 | import org.fitchfamily.android.wifi_backend.database.Database; 37 | 38 | public class WifiReceiver extends BroadcastReceiver { 39 | private static final String TAG = "WiFiReceiver"; 40 | private static final boolean DEBUG = BuildConfig.DEBUG; 41 | 42 | private boolean scanStarted = false; 43 | private static long scanStartTime; 44 | private final WifiManager wifiManager; 45 | private final WifiReceivedCallback callback; 46 | 47 | public WifiReceiver(WifiManager wifiManager, WifiReceivedCallback callback) { 48 | if (DEBUG) { 49 | Log.i(TAG, "WifiReceiver() constructor"); 50 | } 51 | 52 | this.callback = callback; 53 | this.wifiManager = wifiManager; 54 | } 55 | 56 | public void onReceive(Context c, Intent intent) { 57 | if (!scanStarted()) { 58 | if(DEBUG) { 59 | Log.i(TAG, "Scan received but no scan started"); 60 | } 61 | } else { 62 | scanStarted(false); 63 | } 64 | 65 | List configs = wifiManager.getScanResults(); 66 | 67 | if(configs == null) { 68 | if(DEBUG) { 69 | Log.d(TAG, "wifi.getScanResults() == null"); 70 | } 71 | 72 | return; 73 | } 74 | 75 | if (DEBUG) { 76 | Log.i(TAG, "Got " + configs.size() + " wifi access points"); 77 | } 78 | 79 | List accessPoints = new ArrayList<>(configs.size()); 80 | 81 | for (ScanResult config : configs) { 82 | accessPoints.add( 83 | WifiAccessPoint.builder() 84 | .ssid(config.SSID) 85 | // some strange devices use a dot instead of : 86 | .rfId(config.BSSID.toUpperCase(Locale.US).replace(".", ":")) 87 | .level(config.level) 88 | .rfType(Database.TYPE_WIFI) 89 | .build() 90 | ); 91 | } 92 | 93 | callback.processWiFiScanResults(Collections.unmodifiableList(accessPoints)); 94 | } 95 | 96 | public boolean scanStarted() { 97 | return scanStarted; 98 | } 99 | 100 | private void scanStarted(boolean scanStarted) { 101 | if (scanStarted) 102 | this.scanStartTime = System.currentTimeMillis(); 103 | else { 104 | if (DEBUG) 105 | Log.i(TAG, "WiFi Scan time = " + (System.currentTimeMillis()-this.scanStartTime)); 106 | } 107 | this.scanStarted = scanStarted; 108 | } 109 | 110 | public void startScan() { 111 | if (!wifiManager.isWifiEnabled() && !WifiCompat.isScanAlwaysAvailable(wifiManager)) { 112 | if(DEBUG) { 113 | Log.i(TAG, "Wifi is disabled and we can't scan either. Not doing anything."); 114 | } 115 | scanStarted(false); 116 | } else { 117 | if (scanStarted()) { 118 | if (DEBUG) { 119 | Log.i(TAG, "startScan(): Scan already in progress."); 120 | } 121 | } else { 122 | if (DEBUG) { 123 | Log.i(TAG, "startScan(): Starting scan."); 124 | } 125 | scanStarted(true); 126 | wifiManager.startScan(); 127 | } 128 | } 129 | } 130 | 131 | public interface WifiReceivedCallback { 132 | void processWiFiScanResults(@NonNull List accessPoints); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi-v11/ic_stat_no_location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n76/wifi_backend/ab5279a72d50ce90a2ead580cd04bbf2d98d0241/app/src/main/res/drawable-hdpi-v11/ic_stat_no_location.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_stat_no_location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n76/wifi_backend/ab5279a72d50ce90a2ead580cd04bbf2d98d0241/app/src/main/res/drawable-hdpi/ic_stat_no_location.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi-v11/ic_stat_no_location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n76/wifi_backend/ab5279a72d50ce90a2ead580cd04bbf2d98d0241/app/src/main/res/drawable-mdpi-v11/ic_stat_no_location.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_stat_no_location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n76/wifi_backend/ab5279a72d50ce90a2ead580cd04bbf2d98d0241/app/src/main/res/drawable-mdpi/ic_stat_no_location.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi-v11/ic_stat_no_location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n76/wifi_backend/ab5279a72d50ce90a2ead580cd04bbf2d98d0241/app/src/main/res/drawable-xhdpi-v11/ic_stat_no_location.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_stat_no_location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n76/wifi_backend/ab5279a72d50ce90a2ead580cd04bbf2d98d0241/app/src/main/res/drawable-xhdpi/ic_stat_no_location.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi-v11/ic_stat_no_location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n76/wifi_backend/ab5279a72d50ce90a2ead580cd04bbf2d98d0241/app/src/main/res/drawable-xxhdpi-v11/ic_stat_no_location.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_stat_no_location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n76/wifi_backend/ab5279a72d50ce90a2ead580cd04bbf2d98d0241/app/src/main/res/drawable-xxhdpi/ic_stat_no_location.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi-v11/ic_stat_no_location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n76/wifi_backend/ab5279a72d50ce90a2ead580cd04bbf2d98d0241/app/src/main/res/drawable-xxxhdpi-v11/ic_stat_no_location.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_stat_no_location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n76/wifi_backend/ab5279a72d50ce90a2ead580cd04bbf2d98d0241/app/src/main/res/drawable-xxxhdpi/ic_stat_no_location.png -------------------------------------------------------------------------------- /app/src/main/res/layout-w900dp/activity_wifi_list.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 21 | 22 | 28 | 29 | 35 | 36 | 42 | 43 | 50 | 51 | 52 | 53 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_wifi_detail.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_wifi_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 20 | 21 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/res/layout/toolbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/wifi_detail.xml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 17 | 18 | 24 | 25 |