├── .github
└── ISSUE_TEMPLATE
│ ├── bug_report.md
│ ├── feature_request.md
│ └── refactoring.md
├── .gitignore
├── .idea
├── codeStyles
│ └── Project.xml
├── gradle.xml
├── misc.xml
├── modules.xml
├── runConfigurations.xml
└── vcs.xml
├── LICENSE.md
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── assets
│ ├── icons
│ │ ├── arrow.svg
│ │ └── compass.svg
│ └── themes
│ │ ├── classic
│ │ ├── patterns
│ │ │ └── kraft.png
│ │ ├── shields
│ │ │ ├── motorway
│ │ │ │ ├── 1.svg
│ │ │ │ ├── 10.svg
│ │ │ │ ├── 2.svg
│ │ │ │ ├── 3.svg
│ │ │ │ ├── 4.svg
│ │ │ │ ├── 5.svg
│ │ │ │ ├── 6.svg
│ │ │ │ ├── 8.svg
│ │ │ │ └── branches
│ │ │ │ │ └── 3A.svg
│ │ │ ├── primary
│ │ │ │ ├── 1.svg
│ │ │ │ ├── 10.svg
│ │ │ │ ├── 11.svg
│ │ │ │ ├── 12.svg
│ │ │ │ ├── 13.svg
│ │ │ │ ├── 14.svg
│ │ │ │ ├── 15.svg
│ │ │ │ ├── 16.svg
│ │ │ │ ├── 17.svg
│ │ │ │ ├── 18.svg
│ │ │ │ ├── 19.svg
│ │ │ │ ├── 2.svg
│ │ │ │ ├── 20.svg
│ │ │ │ ├── 21.svg
│ │ │ │ ├── 22.svg
│ │ │ │ ├── 23.svg
│ │ │ │ ├── 24.svg
│ │ │ │ ├── 25.svg
│ │ │ │ ├── 26.svg
│ │ │ │ ├── 27.svg
│ │ │ │ ├── 28.svg
│ │ │ │ ├── 29.svg
│ │ │ │ ├── 3.svg
│ │ │ │ ├── 30.svg
│ │ │ │ ├── 31.svg
│ │ │ │ ├── 35.svg
│ │ │ │ ├── 37.svg
│ │ │ │ ├── 39.svg
│ │ │ │ ├── 4.svg
│ │ │ │ ├── 5.svg
│ │ │ │ ├── 6.svg
│ │ │ │ ├── 63.svg
│ │ │ │ ├── 7.svg
│ │ │ │ ├── 8.svg
│ │ │ │ ├── 9.svg
│ │ │ │ └── branches
│ │ │ │ │ ├── 10B.svg
│ │ │ │ │ ├── 11A.svg
│ │ │ │ │ ├── 11B.svg
│ │ │ │ │ ├── 11C.svg
│ │ │ │ │ ├── 13A.svg
│ │ │ │ │ ├── 14A.svg
│ │ │ │ │ ├── 14B.svg
│ │ │ │ │ ├── 14C.svg
│ │ │ │ │ ├── 14D.svg
│ │ │ │ │ ├── 15A.svg
│ │ │ │ │ ├── 17A.svg
│ │ │ │ │ ├── 17B.svg
│ │ │ │ │ ├── 19A.svg
│ │ │ │ │ ├── 1A.svg
│ │ │ │ │ ├── 1B.svg
│ │ │ │ │ ├── 1C.svg
│ │ │ │ │ ├── 1D.svg
│ │ │ │ │ ├── 1E.svg
│ │ │ │ │ ├── 1F.svg
│ │ │ │ │ ├── 20A.svg
│ │ │ │ │ ├── 20B.svg
│ │ │ │ │ ├── 21A.svg
│ │ │ │ │ ├── 27A.svg
│ │ │ │ │ ├── 2A.svg
│ │ │ │ │ ├── 2B.svg
│ │ │ │ │ ├── 2C.svg
│ │ │ │ │ ├── 2D.svg
│ │ │ │ │ ├── 2E.svg
│ │ │ │ │ ├── 2F.svg
│ │ │ │ │ ├── 2G.svg
│ │ │ │ │ ├── 3A.svg
│ │ │ │ │ ├── 3B.svg
│ │ │ │ │ ├── 3C.svg
│ │ │ │ │ ├── 5A.svg
│ │ │ │ │ ├── 5B.svg
│ │ │ │ │ ├── 63A.svg
│ │ │ │ │ ├── 68A.svg
│ │ │ │ │ ├── 74A.svg
│ │ │ │ │ ├── 7A.svg
│ │ │ │ │ ├── 7B.svg
│ │ │ │ │ ├── 7C.svg
│ │ │ │ │ ├── 7D.svg
│ │ │ │ │ ├── 8A.svg
│ │ │ │ │ ├── 9A.svg
│ │ │ │ │ ├── 9B.svg
│ │ │ │ │ └── 9C.svg
│ │ │ └── trunk
│ │ │ │ ├── 61.svg
│ │ │ │ ├── 62.svg
│ │ │ │ ├── 64.svg
│ │ │ │ ├── 65.svg
│ │ │ │ ├── 66.svg
│ │ │ │ ├── 68.svg
│ │ │ │ ├── 72.svg
│ │ │ │ ├── 74.svg
│ │ │ │ ├── 76.svg
│ │ │ │ ├── 78.svg
│ │ │ │ ├── 82.svg
│ │ │ │ ├── 84.svg
│ │ │ │ ├── 86.svg
│ │ │ │ ├── 88.svg
│ │ │ │ └── branches
│ │ │ │ ├── 61A.svg
│ │ │ │ └── 61B.svg
│ │ ├── symbols
│ │ │ ├── hsr.svg
│ │ │ ├── metro_ks.svg
│ │ │ ├── metro_tp.svg
│ │ │ ├── metro_ty.svg
│ │ │ └── tra.svg
│ │ └── theme.xml
│ │ └── default
│ │ ├── shields
│ │ ├── motorway
│ │ │ ├── 1.svg
│ │ │ ├── 10.svg
│ │ │ ├── 2.svg
│ │ │ ├── 3.svg
│ │ │ ├── 4.svg
│ │ │ ├── 5.svg
│ │ │ ├── 6.svg
│ │ │ ├── 8.svg
│ │ │ └── branches
│ │ │ │ ├── 2A.svg
│ │ │ │ └── 3A.svg
│ │ ├── primary
│ │ │ ├── 1.svg
│ │ │ ├── 10.svg
│ │ │ ├── 11.svg
│ │ │ ├── 12.svg
│ │ │ ├── 13.svg
│ │ │ ├── 14.svg
│ │ │ ├── 15.svg
│ │ │ ├── 16.svg
│ │ │ ├── 17.svg
│ │ │ ├── 18.svg
│ │ │ ├── 19.svg
│ │ │ ├── 2.svg
│ │ │ ├── 20.svg
│ │ │ ├── 21.svg
│ │ │ ├── 22.svg
│ │ │ ├── 23.svg
│ │ │ ├── 24.svg
│ │ │ ├── 25.svg
│ │ │ ├── 26.svg
│ │ │ ├── 27.svg
│ │ │ ├── 28.svg
│ │ │ ├── 29.svg
│ │ │ ├── 3.svg
│ │ │ ├── 30.svg
│ │ │ ├── 31.svg
│ │ │ ├── 35.svg
│ │ │ ├── 37.svg
│ │ │ ├── 39.svg
│ │ │ ├── 4.svg
│ │ │ ├── 5.svg
│ │ │ ├── 6.svg
│ │ │ ├── 63.svg
│ │ │ ├── 7.svg
│ │ │ ├── 8.svg
│ │ │ ├── 9.svg
│ │ │ └── branches
│ │ │ │ ├── 10B.svg
│ │ │ │ ├── 11A.svg
│ │ │ │ ├── 11B.svg
│ │ │ │ ├── 11C.svg
│ │ │ │ ├── 13A.svg
│ │ │ │ ├── 14A.svg
│ │ │ │ ├── 14B.svg
│ │ │ │ ├── 14C.svg
│ │ │ │ ├── 14D.svg
│ │ │ │ ├── 15A.svg
│ │ │ │ ├── 17A.svg
│ │ │ │ ├── 17B.svg
│ │ │ │ ├── 19A.svg
│ │ │ │ ├── 1A.svg
│ │ │ │ ├── 1B.svg
│ │ │ │ ├── 1C.svg
│ │ │ │ ├── 1D.svg
│ │ │ │ ├── 1E.svg
│ │ │ │ ├── 1F.svg
│ │ │ │ ├── 20A.svg
│ │ │ │ ├── 20B.svg
│ │ │ │ ├── 21A.svg
│ │ │ │ ├── 27A.svg
│ │ │ │ ├── 2A.svg
│ │ │ │ ├── 2B.svg
│ │ │ │ ├── 2C.svg
│ │ │ │ ├── 2D.svg
│ │ │ │ ├── 2E.svg
│ │ │ │ ├── 2F.svg
│ │ │ │ ├── 2G.svg
│ │ │ │ ├── 3A.svg
│ │ │ │ ├── 3B.svg
│ │ │ │ ├── 3C.svg
│ │ │ │ ├── 5A.svg
│ │ │ │ ├── 5B.svg
│ │ │ │ ├── 63A.svg
│ │ │ │ ├── 68A.svg
│ │ │ │ ├── 74A.svg
│ │ │ │ ├── 7A.svg
│ │ │ │ ├── 7B.svg
│ │ │ │ ├── 7C.svg
│ │ │ │ ├── 7D.svg
│ │ │ │ ├── 8A.svg
│ │ │ │ ├── 9A.svg
│ │ │ │ ├── 9B.svg
│ │ │ │ └── 9C.svg
│ │ └── trunk
│ │ │ ├── 61.svg
│ │ │ ├── 62.svg
│ │ │ ├── 64.svg
│ │ │ ├── 65.svg
│ │ │ ├── 66.svg
│ │ │ ├── 68.svg
│ │ │ ├── 72.svg
│ │ │ ├── 74.svg
│ │ │ ├── 76.svg
│ │ │ ├── 78.svg
│ │ │ ├── 82.svg
│ │ │ ├── 84.svg
│ │ │ ├── 86.svg
│ │ │ ├── 88.svg
│ │ │ └── branches
│ │ │ ├── 61A.svg
│ │ │ └── 61B.svg
│ │ ├── symbols
│ │ ├── hsr.svg
│ │ ├── metro_ks.svg
│ │ ├── metro_tp.svg
│ │ ├── metro_ty.svg
│ │ └── tra.svg
│ │ └── theme.xml
│ ├── java
│ └── tacoball
│ │ └── com
│ │ └── geomancer
│ │ ├── MainActivity.java
│ │ ├── MainUtils.java
│ │ ├── MapViewFragment.java
│ │ ├── PermissionUtils.java
│ │ ├── SettingsFragment.java
│ │ ├── SimpleFragment.java
│ │ ├── UpdateToolFragment.java
│ │ ├── checkupdate
│ │ ├── AutoUpdateAdapter.java
│ │ ├── AutoUpdateManager.java
│ │ └── CheckUpdateAdapter.java
│ │ ├── map
│ │ ├── FixedFpsSurfaceView.java
│ │ ├── Pin.java
│ │ ├── PinGroup.java
│ │ ├── SurfaceMapView.java
│ │ └── TaiwanMapView.java
│ │ └── view
│ │ ├── CircleButton.java
│ │ └── LinkView.java
│ └── res
│ ├── drawable
│ ├── contrib96.png
│ ├── debug_border.xml
│ ├── g0v_dark.png
│ ├── geomancer_info_border.xml
│ ├── license96.png
│ ├── measure_result_background.xml
│ ├── more96.png
│ └── settings96.png
│ ├── layout
│ ├── activity_main.xml
│ ├── fragment_contributors.xml
│ ├── fragment_license.xml
│ ├── fragment_main.xml
│ └── fragment_updater.xml
│ ├── mipmap-xhdpi
│ └── geomancer.png
│ ├── values-w820dp
│ └── dimens.xml
│ ├── values
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
│ └── xml
│ └── settings.xml
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: 蟲
6 | assignees: virus-warnning
7 |
8 | ---
9 |
10 | **問題描述**
11 | A clear and concise description of what the bug is.
12 |
13 | **重現步驟**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **預期行為**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **線索**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **環境資訊**
27 | - 機型: [e.g. ZenFone 3]
28 | - Android 版本: [e.g. 6.0]
29 | - App 版本: [e.g. 0.1.6]
30 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: 新功能
6 | assignees: virus-warnning
7 |
8 | ---
9 |
10 | **願望**
11 | A clear and concise description of what you want to happen.
12 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/refactoring.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Refactoring
3 | about: 重構應用程式
4 | title: ''
5 | labels: 重構
6 | assignees: virus-warnning
7 |
8 | ---
9 |
10 | **重構原因**
11 |
12 | **套件異動**
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the ART/Dalvik VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # Generated files
12 | bin/
13 | gen/
14 | out/
15 |
16 | # Gradle files
17 | .gradle/
18 | build/
19 |
20 | # Local configuration file (sdk path, etc)
21 | local.properties
22 |
23 | # Proguard folder generated by Eclipse
24 | proguard/
25 |
26 | # Log Files
27 | *.log
28 |
29 | # Android Studio Navigation editor temp files
30 | .navigation/
31 |
32 | # Android Studio captures folder
33 | captures/
34 |
35 | # IntelliJ
36 | *.iml
37 | .idea/workspace.xml
38 | .idea/tasks.xml
39 | .idea/gradle.xml
40 | .idea/assetWizardSettings.xml
41 | .idea/dictionaries
42 | .idea/libraries
43 | .idea/caches
44 |
45 | # Keystore files
46 | # Uncomment the following line if you do not want to check your keystore files in.
47 | #*.jks
48 |
49 | # External native build folder generated in Android Studio 2.2 and later
50 | .externalNativeBuild
51 |
52 | # Google Services (e.g. APIs or Firebase)
53 | google-services.json
54 |
55 | # Freeline
56 | freeline.py
57 | freeline/
58 | freeline_project_description.json
59 |
60 | # fastlane
61 | fastlane/report.xml
62 | fastlane/Preview.html
63 | fastlane/screenshots
64 | fastlane/test_output
65 | fastlane/readme.md
66 |
67 | # OSX Thumbnails
68 | .DS_Store
69 |
70 | # Bundle
71 | app/release/
72 |
73 | # Crashlytics
74 | google-services.json
75 |
76 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
18 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### 鄉民風水師
2 | [Google Play 下載](https://play.google.com/store/apps/details?id=tacoball.com.geomancer)
3 |
4 | 這是一個利用資料視覺化的方式設計的人文地理工具,目前可查詢:
5 | * 全台灣凶宅地點
6 | * 台北市勞工局違反勞基法事業地點
7 |
8 | ### 閃退紀錄
9 | [Crashlytics 統計](https://fabric.io/taco-studio/android/apps/tacoball.com.geomancer/issues)
10 |
11 | ### 更新檔
12 | 包含離線地圖、凶宅資料庫、違反勞基法資料庫:
13 | [OSSPlanet](http://mirror.ossplanet.net/geomancer/0.1.0)
14 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'com.google.gms.google-services'
3 |
4 | android {
5 | // 配合 appcompat-v7
6 | compileSdkVersion 28
7 | buildToolsVersion '28.0.3'
8 | defaultConfig {
9 | // 名稱與版本
10 | applicationId "tacoball.com.geomancer"
11 | versionCode 106
12 | versionName "0.1.6"
13 |
14 | // Android 6.0+
15 | minSdkVersion 23
16 | targetSdkVersion 28
17 | }
18 | }
19 |
20 | project.ext {
21 | supportVersion = "28.0.0"
22 | mapsforgeVersion = "0.8.0"
23 | }
24 |
25 | dependencies {
26 | // mapsforge
27 | implementation "org.mapsforge:mapsforge-map-android:${project.mapsforgeVersion}"
28 | implementation "org.mapsforge:mapsforge-themes:${project.mapsforgeVersion}"
29 | /*
30 | compile("org.mapsforge:mapsforge-map-android-extras:${project.mapsforgeVersion}") {
31 | transitive = false
32 | }
33 | */
34 |
35 | // Android support library
36 | // See: https://developer.android.com/topic/libraries/support-library/packages.html
37 | implementation "com.android.support:support-v4:${project.supportVersion}"
38 | implementation "com.android.support:appcompat-v7:${project.supportVersion}"
39 | implementation "com.android.support:preference-v7:${project.supportVersion}"
40 |
41 | // Others
42 | implementation 'commons-io:commons-io:2.5'
43 | implementation 'com.google.code.gson:gson:2.8.2'
44 |
45 | // Crashlytics
46 | implementation 'com.google.firebase:firebase-core:16.0.6'
47 |
48 | // Unit test
49 | testImplementation 'junit:junit:4.12'
50 | }
51 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By bright, the flags in this file are appended to flags specified
3 | # in /Users/raymond/android-sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
23 |
24 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/app/src/main/assets/icons/arrow.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/app/src/main/assets/icons/compass.svg:
--------------------------------------------------------------------------------
1 |
2 |
8 |
118 |
--------------------------------------------------------------------------------
/app/src/main/assets/themes/classic/patterns/kraft.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OsmHackTW/GeomancerAndroid/f134ee8751a0abe85325c3fd9707cd0f84f7a9c7/app/src/main/assets/themes/classic/patterns/kraft.png
--------------------------------------------------------------------------------
/app/src/main/assets/themes/classic/shields/motorway/1.svg:
--------------------------------------------------------------------------------
1 |
2 |
9 |
37 |
--------------------------------------------------------------------------------
/app/src/main/assets/themes/classic/shields/motorway/10.svg:
--------------------------------------------------------------------------------
1 |
2 |
9 |
37 |
--------------------------------------------------------------------------------
/app/src/main/assets/themes/classic/shields/motorway/2.svg:
--------------------------------------------------------------------------------
1 |
2 |
9 |
37 |
--------------------------------------------------------------------------------
/app/src/main/assets/themes/classic/shields/motorway/3.svg:
--------------------------------------------------------------------------------
1 |
2 |
9 |
37 |
--------------------------------------------------------------------------------
/app/src/main/assets/themes/classic/shields/motorway/4.svg:
--------------------------------------------------------------------------------
1 |
2 |
9 |
37 |
--------------------------------------------------------------------------------
/app/src/main/assets/themes/classic/shields/motorway/5.svg:
--------------------------------------------------------------------------------
1 |
2 |
9 |
37 |
--------------------------------------------------------------------------------
/app/src/main/assets/themes/classic/shields/motorway/6.svg:
--------------------------------------------------------------------------------
1 |
2 |
9 |
37 |
--------------------------------------------------------------------------------
/app/src/main/assets/themes/classic/shields/motorway/8.svg:
--------------------------------------------------------------------------------
1 |
2 |
9 |
37 |
--------------------------------------------------------------------------------
/app/src/main/assets/themes/classic/shields/motorway/branches/3A.svg:
--------------------------------------------------------------------------------
1 |
2 |
9 |
37 |
--------------------------------------------------------------------------------
/app/src/main/assets/themes/classic/shields/primary/1.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/app/src/main/assets/themes/classic/shields/primary/35.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
96 |
--------------------------------------------------------------------------------
/app/src/main/assets/themes/classic/shields/primary/7.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/app/src/main/assets/themes/classic/shields/primary/branches/20B.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
--------------------------------------------------------------------------------
/app/src/main/assets/themes/classic/shields/primary/branches/7D.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/app/src/main/assets/themes/classic/symbols/hsr.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
28 |
--------------------------------------------------------------------------------
/app/src/main/assets/themes/classic/symbols/metro_ks.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
73 |
--------------------------------------------------------------------------------
/app/src/main/assets/themes/classic/symbols/metro_tp.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
35 |
--------------------------------------------------------------------------------
/app/src/main/assets/themes/classic/symbols/metro_ty.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
90 |
--------------------------------------------------------------------------------
/app/src/main/assets/themes/classic/symbols/tra.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
24 |
--------------------------------------------------------------------------------
/app/src/main/assets/themes/default/shields/primary/1.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/app/src/main/assets/themes/default/shields/primary/7.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/app/src/main/assets/themes/default/shields/primary/branches/20B.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
--------------------------------------------------------------------------------
/app/src/main/assets/themes/default/shields/primary/branches/7D.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/app/src/main/assets/themes/default/symbols/hsr.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
101 |
--------------------------------------------------------------------------------
/app/src/main/assets/themes/default/symbols/metro_ks.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
74 |
--------------------------------------------------------------------------------
/app/src/main/assets/themes/default/symbols/metro_tp.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
34 |
--------------------------------------------------------------------------------
/app/src/main/assets/themes/default/symbols/metro_ty.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
90 |
--------------------------------------------------------------------------------
/app/src/main/assets/themes/default/symbols/tra.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
25 |
--------------------------------------------------------------------------------
/app/src/main/java/tacoball/com/geomancer/MainActivity.java:
--------------------------------------------------------------------------------
1 | package tacoball.com.geomancer;
2 |
3 |
4 | import android.content.BroadcastReceiver;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.content.pm.PackageManager;
8 | import android.os.Bundle;
9 | import android.support.annotation.NonNull;
10 | import android.support.annotation.Nullable;
11 | import android.support.v4.app.Fragment;
12 | import android.support.v4.app.FragmentManager;
13 | import android.support.v7.app.AppCompatActivity;
14 | import android.util.Log;
15 | import android.widget.Toast;
16 |
17 | import org.mapsforge.map.android.graphics.AndroidGraphicFactory;
18 |
19 | import java.util.Locale;
20 |
21 | import tacoball.com.geomancer.map.TaiwanMapView;
22 |
23 | /**
24 | * 前端程式進入點
25 | */
26 | public class MainActivity extends AppCompatActivity {
27 |
28 | private static final String TAG = "MainActivity";
29 |
30 | private static final boolean SIMULATE_OLD_MTIME = false;
31 |
32 | private MapViewFragment mMapFragment = new MapViewFragment();
33 | private Fragment mUpdateFragment = new UpdateToolFragment();
34 |
35 | @Override
36 | protected void onCreate(Bundle savedInstanceState) {
37 | super.onCreate(savedInstanceState);
38 |
39 | // 配置 Android 繪圖資源,必須在 inflate 之前完成
40 | AndroidGraphicFactory.createInstance(getApplication());
41 | setContentView(R.layout.activity_main);
42 |
43 | // 配置廣播接收器
44 | this.registerReceiver(receiver, MainUtils.buildFragmentSwitchIntentFilter());
45 |
46 | // 清理儲存空間
47 | MainUtils.cleanStorage(this);
48 |
49 | // 檢查是否殘留除錯設定,釋出前使用
50 | checkDebugParameters();
51 |
52 | // 先進入更新介面
53 | changeFragment(mUpdateFragment);
54 | }
55 |
56 | @Override
57 | protected void onDestroy() {
58 | super.onDestroy();
59 | unregisterReceiver(receiver);
60 | }
61 |
62 | @Override
63 | public void onBackPressed() {
64 | // 從設定頁返回主畫面要重新載入設定值
65 | // TODO: 目前的寫法會導致貢獻者頁面和授權頁面返回也重新載入
66 | FragmentManager fm = getSupportFragmentManager();
67 | if (fm.getBackStackEntryCount() > 0) {
68 | mMapFragment.reloadSettings();
69 | }
70 | super.onBackPressed();
71 | }
72 |
73 | @Override
74 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
75 | // 位置權限被允許
76 | if (requestCode == PermissionUtils.RC_GOTO_POSITION) {
77 | if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
78 | // TODO: 繼續定位動作
79 | }
80 | }
81 | }
82 |
83 | // 地毯式檢查用到的除錯參數
84 | private void checkDebugParameters() {
85 | int cnt = 0;
86 |
87 | if (MainUtils.MIRROR_NUM != 0) {
88 | Log.w(TAG, getString(R.string.log_use_debugging_mirror));
89 | cnt++;
90 | }
91 |
92 | if (TaiwanMapView.SEE_DEBUGGING_POINT) {
93 | Log.w(TAG, getString(R.string.log_see_debugging_point));
94 | cnt++;
95 | }
96 |
97 | if (SIMULATE_OLD_MTIME) {
98 | Log.w(TAG, getString(R.string.log_simulate_old_mtime));
99 | cnt++;
100 | }
101 |
102 | if (cnt > 0) {
103 | String pat = getString(R.string.pattern_enable_debugging);
104 | String msg = String.format(Locale.getDefault(), pat, cnt);
105 | Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
106 | }
107 | }
108 |
109 | // 切換 Fragment
110 | private void changeFragment(Fragment nextFrag) {
111 | // Issue #65 處理方式
112 | if (isFinishing() || isDestroyed()) {
113 | Log.w(TAG, "應用程式即將關閉,取消畫面切換");
114 | return;
115 | }
116 |
117 | FragmentManager fm = getSupportFragmentManager();
118 | if (nextFrag == mMapFragment || nextFrag == mUpdateFragment) {
119 | // 放在堆疊底層
120 | fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
121 | fm.beginTransaction()
122 | .replace(R.id.frag_container, nextFrag)
123 | .commit();
124 | } else {
125 | // 疊在 mMapFragment 上面
126 | fm.beginTransaction()
127 | .add(R.id.frag_container, nextFrag)
128 | .attach(nextFrag)
129 | .addToBackStack("detail")
130 | .commit();
131 | }
132 | }
133 |
134 | // 廣播接收器,處理使用者更新要求用
135 | private BroadcastReceiver receiver = new BroadcastReceiver() {
136 |
137 | @Override
138 | public void onReceive(Context context, Intent intent) {
139 | String msg = String.format(Locale.getDefault(), "Got broadcast intent action=%s", intent.getAction());
140 | Log.d(TAG, msg);
141 |
142 | String action = intent.getAction();
143 | if (action == null) {
144 | return;
145 | }
146 |
147 | if (action.equals("MAIN")) {
148 | changeFragment(mMapFragment);
149 | }
150 |
151 | if (action.equals("UPDATE")) {
152 | changeFragment(mUpdateFragment);
153 | }
154 |
155 | if (action.equals("SETTINGS")) {
156 | Fragment f = new SettingsFragment();
157 | changeFragment(f);
158 | }
159 |
160 | if (action.equals("CONTRIBUTORS")) {
161 | Fragment f = new SimpleFragment();
162 | Bundle args = new Bundle();
163 | args.putInt("LAYOUT_ID", R.layout.fragment_contributors);
164 | f.setArguments(args);
165 | changeFragment(f);
166 | }
167 |
168 | if (action.equals("LICENSE")) {
169 | Fragment f = new SimpleFragment();
170 | Bundle args = new Bundle();
171 | args.putInt("LAYOUT_ID", R.layout.fragment_license);
172 | f.setArguments(args);
173 | changeFragment(f);
174 | }
175 | }
176 |
177 | };
178 |
179 | }
180 |
--------------------------------------------------------------------------------
/app/src/main/java/tacoball/com/geomancer/MainUtils.java:
--------------------------------------------------------------------------------
1 | package tacoball.com.geomancer;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.content.IntentFilter;
6 | import android.content.SharedPreferences;
7 | import android.database.sqlite.SQLiteDatabase;
8 | import android.net.ConnectivityManager;
9 | import android.net.NetworkInfo;
10 | import android.preference.PreferenceManager;
11 | import android.util.Log;
12 |
13 | import org.apache.commons.io.FileUtils;
14 | import org.mapsforge.map.datastore.MapDataStore;
15 | import org.mapsforge.map.reader.MapFile;
16 |
17 | import java.io.File;
18 | import java.io.IOException;
19 | import java.util.Locale;
20 |
21 | /**
22 | * 共用程式
23 | */
24 | public class MainUtils {
25 |
26 | // 除錯標籤
27 | private static final String TAG = "MainUtils";
28 |
29 | // 各偏好設定 KEY 值
30 | private static final String PREFKEY_UPDATE_BY_MOBILE = "UPDATE_FROM_MOBILE"; // 允許行動網路更新
31 |
32 | // 地圖檔名
33 | public static final String MAP_NAME = "taiwan-taco.map";
34 |
35 | // 資料庫檔名
36 | public static final String UNLUCKY_HOUSE = "unluckyhouse.sqlite";
37 |
38 | // 前端狀態事件的分類名稱
39 | private static final String INTENT_CATEGORY = "tacoball.com.geomancer.FrontEndState";
40 |
41 | // 更新伺服器
42 | public static final int MIRROR_NUM = 0;
43 | private static final String[] MIRROR_SITES = {
44 | "tacosync.com"
45 | };
46 |
47 | /**
48 | * 取得更新鏡像站的網址
49 | *
50 | * @return 網址
51 | */
52 | public static String getUpdateSource() {
53 | return String.format(Locale.getDefault(), "http://%s/geomancer/0.1.0", MIRROR_SITES[MIRROR_NUM]);
54 | }
55 |
56 | /**
57 | * 取得 DB 路徑
58 | *
59 | * @param context Activity 或 Service
60 | * @return DB 路徑
61 | * @throws IOException ...
62 | */
63 | public static File getDbPath(Context context) throws IOException {
64 | File[] dirs = context.getExternalFilesDirs("db");
65 | for (int i=dirs.length-1;i>=0;i--) {
66 | if (dirs[i]!=null) return dirs[i];
67 | }
68 | throw new IOException("");
69 | }
70 |
71 | /**
72 | * 取得紀錄檔路徑
73 | *
74 | * @param context Activity 或 Service
75 | * @return 紀錄檔路徑
76 | * @throws IOException ...
77 | */
78 | public static File getLogPath(Context context) throws IOException {
79 | File[] dirs = context.getExternalFilesDirs("log");
80 | for (int i=dirs.length-1;i>=0;i--) {
81 | if (dirs[i]!=null) return dirs[i];
82 | }
83 | throw new IOException("");
84 | }
85 |
86 | /**
87 | * 取得地圖路徑
88 | *
89 | * @param context Activity 或 Service
90 | * @return 地圖路徑
91 | * @throws IOException
92 | */
93 | public static File getMapPath(Context context) throws IOException {
94 | File[] dirs = context.getExternalFilesDirs("map");
95 | for (int i=dirs.length-1;i>=0;i--) {
96 | if (dirs[i]!=null) return dirs[i];
97 | }
98 | throw new IOException("");
99 | }
100 |
101 | /**
102 | * 開啟地圖
103 | *
104 | * @param context Activity 或 Service
105 | * @return 圖資存取介面
106 | * @throws IOException
107 | */
108 | public static MapDataStore openMapData(Context context) throws IOException {
109 | File path = new File(getMapPath(context), MAP_NAME);
110 | return new MapFile(path);
111 | }
112 |
113 | /**
114 | * 唯讀模式開啟 SQLite 資料庫
115 | *
116 | * @param context Activity 或 Service
117 | * @param filename 資料庫檔名
118 | * @return 資料庫連線
119 | * @throws IOException
120 | */
121 | public static SQLiteDatabase openReadOnlyDB(Context context, String filename) throws IOException {
122 | String path = getDbPath(context).getAbsolutePath() + "/" + filename;
123 | return SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READONLY);
124 | }
125 |
126 | /**
127 | * 檢查是否可以傳輸資料
128 | *
129 | * @param context Activity 或 Service
130 | * @return 是否可以傳輸
131 | */
132 | public static boolean isNetworkConnected(Context context) {
133 | ConnectivityManager cm = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
134 | NetworkInfo ni = cm.getActiveNetworkInfo();
135 |
136 | if (ni != null) {
137 | // TODO: 檢查是否限用 WiFi
138 | return ni.isConnected();
139 | } else {
140 | return false;
141 | }
142 | }
143 |
144 | /**
145 | * 清理儲存空間
146 | */
147 | public static void cleanStorage(Context context) {
148 | File[] dirs = context.getExternalFilesDirs("database");
149 | for (int i=dirs.length-1;i>=0;i--) {
150 | if (dirs[i]!=null) {
151 | try {
152 | FileUtils.deleteDirectory(dirs[i]);
153 | } catch(IOException ex) {
154 | Log.e(TAG, getReason(ex));
155 | }
156 | }
157 | }
158 |
159 | // TODO: 移除故障的殘留檔案
160 | }
161 |
162 | /**
163 | * 例外訊息改進程式,避免捕捉例外時還發生例外
164 | */
165 | public static String getReason(final Exception ex) {
166 | String msg = ex.getMessage();
167 |
168 | if (msg==null) {
169 | StackTraceElement ste = ex.getStackTrace()[0];
170 | msg = String.format(
171 | Locale.getDefault(),
172 | "%s with null message (%s.%s() Line:%d)",
173 | ex.getClass().getSimpleName(),
174 | ste.getClassName(),
175 | ste.getMethodName(),
176 | ste.getLineNumber()
177 | );
178 | }
179 |
180 | return msg;
181 | }
182 |
183 | /**
184 | * 是否允許透過行動網路更新
185 | */
186 | public static boolean canUpdateByMobile(Context context) {
187 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
188 | return prefs.getBoolean(PREFKEY_UPDATE_BY_MOBILE, true);
189 | }
190 |
191 | /**
192 | * 產生 Fragment 切換事件
193 | *
194 | * @param action 動作名稱
195 | * @return Fragment 切換事件
196 | */
197 | public static Intent buildFragmentSwitchIntent(String action) {
198 | Intent i = new Intent();
199 | i.addCategory(INTENT_CATEGORY);
200 | i.setAction(action);
201 | return i;
202 | }
203 |
204 | /**
205 | * 產生 Fragment 切換事件過濾器
206 | *
207 | * @return Fragment 切換事件過濾器
208 | */
209 | public static IntentFilter buildFragmentSwitchIntentFilter() {
210 | IntentFilter filter = new IntentFilter();
211 | filter.addCategory(INTENT_CATEGORY);
212 | filter.addAction("MAIN");
213 | filter.addAction("UPDATE");
214 | filter.addAction("SETTINGS");
215 | filter.addAction("CONTRIBUTORS");
216 | filter.addAction("LICENSE");
217 | return filter;
218 | }
219 |
220 | }
221 |
--------------------------------------------------------------------------------
/app/src/main/java/tacoball/com/geomancer/PermissionUtils.java:
--------------------------------------------------------------------------------
1 | package tacoball.com.geomancer;
2 |
3 | import android.Manifest;
4 | import android.content.DialogInterface;
5 | import android.content.Intent;
6 | import android.net.Uri;
7 | import android.provider.Settings;
8 | import android.support.annotation.NonNull;
9 | import android.support.v7.app.AlertDialog;
10 | import android.support.v7.app.AppCompatActivity;
11 |
12 | public class PermissionUtils {
13 |
14 | public static int RC_GOTO_POSITION = 32769;
15 |
16 | /**
17 | * 請求位置權限
18 | *
19 | * @param activity 應用程式
20 | * @param requestCode 請求代碼,完成請求後接續處理用
21 | */
22 | public static void requestLocationPermission(@NonNull final AppCompatActivity activity, final int requestCode) {
23 | // 沒有位置資訊權限
24 | if (activity.shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)) {
25 | // 權限已拒絕狀態,提示用戶開啟
26 | DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
27 | @Override
28 | public void onClick(DialogInterface dialog, int which) {
29 | Intent intent = new Intent();
30 | intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
31 | Uri uri = Uri.fromParts("package", activity.getPackageName(), null);
32 | intent.setData(uri);
33 | activity.startActivityForResult(intent, requestCode);
34 | }
35 | };
36 |
37 | new AlertDialog.Builder(activity)
38 | .setMessage(R.string.prompt_loc_permission_rejected)
39 | .setPositiveButton(R.string.prompt_loc_permission_enable, listener)
40 | .setNegativeButton(R.string.prompt_loc_permission_cancel, null)
41 | .create()
42 | .show();
43 | } else {
44 | // 權限未設定狀態,請求權限
45 | activity.requestPermissions(new String[] { Manifest.permission.ACCESS_FINE_LOCATION }, requestCode);
46 | }
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/app/src/main/java/tacoball/com/geomancer/SettingsFragment.java:
--------------------------------------------------------------------------------
1 | package tacoball.com.geomancer;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.preference.PreferenceFragmentCompat;
5 |
6 | /**
7 | * 設定畫面
8 | */
9 | public class SettingsFragment extends PreferenceFragmentCompat {
10 |
11 | // POI 項目 (可擴充)
12 | // private final String[] POI_KEYS = {"search_unlucky_house"};
13 |
14 | @Override
15 | public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
16 | setPreferencesFromResource(R.xml.settings, rootKey);
17 |
18 | // 驗證設定
19 | /*
20 | PreferenceScreen ps = getPreferenceScreen();
21 | for (String k : POI_KEYS) {
22 | ps.findPreference(k).setOnPreferenceChangeListener(mPoiValidator);
23 | }
24 | */
25 | }
26 |
27 | // POI 至少要選一項的檢查程式
28 | /*
29 | Preference.OnPreferenceChangeListener mPoiValidator = new Preference.OnPreferenceChangeListener() {
30 |
31 | @Override
32 | public boolean onPreferenceChange(Preference preference, Object newValue) {
33 | PreferenceScreen ps = getPreferenceScreen();
34 | Boolean newChecked = (Boolean)newValue;
35 |
36 | if (newChecked.booleanValue() == false) {
37 | int checkedCount = 0;
38 |
39 | for (String k : POI_KEYS) {
40 | if (preference.getKey().equals(k)) continue;
41 | CheckBoxPreference cbp = (CheckBoxPreference)ps.findPreference(k);
42 | if (cbp.isChecked()) {
43 | checkedCount++;
44 | }
45 | }
46 |
47 | if (checkedCount == 0) {
48 | Toast.makeText(getActivity(), R.string.prompt_at_least_one_poi, Toast.LENGTH_SHORT).show();
49 | return false;
50 | }
51 | }
52 |
53 | return true;
54 | }
55 |
56 | };
57 | */
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/app/src/main/java/tacoball/com/geomancer/SimpleFragment.java:
--------------------------------------------------------------------------------
1 | package tacoball.com.geomancer;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.NonNull;
5 | import android.support.v4.app.Fragment;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 |
10 | /**
11 | * 簡易頁面用的 Fragment,目前用於授權和貢獻者頁面
12 | */
13 | public class SimpleFragment extends Fragment {
14 |
15 | @Override
16 | public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
17 | Bundle args = this.getArguments();
18 | if (args != null) {
19 | int layoutId = args.getInt("LAYOUT_ID");
20 | return inflater.inflate(layoutId, null);
21 | }
22 | return null;
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/java/tacoball/com/geomancer/checkupdate/AutoUpdateAdapter.java:
--------------------------------------------------------------------------------
1 | package tacoball.com.geomancer.checkupdate;
2 |
3 | /**
4 | * 自動更新進度接收程式
5 | */
6 | public class AutoUpdateAdapter {
7 |
8 | /**
9 | * 回報檔案需要更新
10 | *
11 | * @param filename 更新中檔案
12 | * @param reason 需要更新的原因
13 | * @param gzLength 更新檔壓縮長度
14 | * @param exLength 更新檔原始長度
15 | */
16 | public void onFileExpired(String filename, String reason, long gzLength, long exLength) {}
17 |
18 | /**
19 | * 回報開始更新檔案
20 | *
21 | * @param filename 更新中檔案
22 | */
23 | public void onFileBegin(String filename) {}
24 |
25 | /**
26 | * 回報傳輸進度,每填滿一次緩衝區觸發一次
27 | *
28 | * @param filename 更新中檔案
29 | * @param transfered 已傳輸長度
30 | */
31 | public void onFileTransferLength(String filename, long transfered) {}
32 |
33 | /**
34 | * 回報傳輸進度,進度百分比異動時觸發一次
35 | *
36 | * @param filename 更新中檔案
37 | * @param percent 進度百分比
38 | */
39 | public void onFileTransfer(String filename, int percent) {}
40 |
41 | /**
42 | * 回報解壓縮進度
43 | *
44 | * @param filename 更新中檔案
45 | * @param percent 進度百分比
46 | */
47 | public void onFileExtract(String filename, int percent) {}
48 |
49 | /**
50 | * 回報完成一個檔案
51 | *
52 | * @param filename 更新中檔案
53 | * @param isNew 是否有更新
54 | */
55 | public void onFileComplete(String filename, boolean isNew) {}
56 |
57 | /**
58 | * 回報一項警告,此時更新程序會自動修復
59 | *
60 | * @param filename 更新中檔案
61 | * @param reason 原因
62 | */
63 | public void onFileWarning(String filename, String reason) {}
64 |
65 | /**
66 | * 回報更新完成
67 | */
68 | public void onComplete() {}
69 |
70 | /**
71 | * 回報更新失敗,發生後會結束更新作業
72 | *
73 | * @param reason 原因
74 | */
75 | public void onError(String reason) {}
76 |
77 | /**
78 | * 回報更新取消,發生後會結束更新作業
79 | */
80 | public void onUserCancel(String progress) {}
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/app/src/main/java/tacoball/com/geomancer/checkupdate/CheckUpdateAdapter.java:
--------------------------------------------------------------------------------
1 | package tacoball.com.geomancer.checkupdate;
2 |
3 | public class CheckUpdateAdapter {
4 |
5 | /**
6 | * 檢查完成
7 | *
8 | * @param totalLength 需要更新的總長度
9 | * @param lastModified 相關檔案最後更新時間
10 | */
11 | public void onCheck(long totalLength, String lastModified) {
12 | System.out.printf("需要更新 %d bytes, 異動日期: %s\n", totalLength, lastModified);
13 | }
14 |
15 | /**
16 | * 檢查更新時發生錯誤
17 | *
18 | * @param reason 錯誤原因
19 | */
20 | public void onError(String reason) {
21 | System.err.println(reason);
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/java/tacoball/com/geomancer/map/FixedFpsSurfaceView.java:
--------------------------------------------------------------------------------
1 | package tacoball.com.geomancer.map;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.util.AttributeSet;
6 | import android.util.Log;
7 | import android.view.SurfaceHolder;
8 | import android.view.SurfaceView;
9 | import android.view.ViewParent;
10 |
11 | import org.mapsforge.map.android.graphics.AndroidGraphicFactory;
12 |
13 | import java.util.Locale;
14 | import java.util.concurrent.Executors;
15 | import java.util.concurrent.RejectedExecutionException;
16 | import java.util.concurrent.ScheduledExecutorService;
17 | import java.util.concurrent.TimeUnit;
18 |
19 | public class FixedFpsSurfaceView extends SurfaceView {
20 |
21 | private static final String TAG = "FixedFpsSurfaceView";
22 | private static final int FPS = 24;
23 | private static final boolean TRACE = true;
24 |
25 | private SurfaceMapView parallelMapView;
26 |
27 | public FixedFpsSurfaceView(Context context) {
28 | this(context, null);
29 | }
30 |
31 | public FixedFpsSurfaceView(Context context, AttributeSet attrs) {
32 | super(context, attrs);
33 |
34 | this.setWillNotDraw(false);
35 | this.getHolder().addCallback(callback);
36 |
37 | if (TRACE) {
38 | long thId = Thread.currentThread().getId();
39 | String msg = String.format(Locale.getDefault(), "Init MySurface at thread #%d", thId);
40 | Log.d(TAG, msg);
41 | }
42 | }
43 |
44 | public SurfaceMapView getParentViewGroup() {
45 | if (parallelMapView == null) {
46 | ViewParent parent = getParent();
47 | if (parent instanceof SurfaceMapView) {
48 | parallelMapView = (SurfaceMapView)parent;
49 | }
50 | }
51 |
52 | return parallelMapView;
53 | }
54 |
55 | private void onDrawSurface(Canvas androidCanvas) {
56 | if (getParentViewGroup()!=null) {
57 | org.mapsforge.core.graphics.Canvas graphicContext = AndroidGraphicFactory.createGraphicContext(androidCanvas);
58 |
59 | parallelMapView.getFrameBuffer().draw(graphicContext);
60 | parallelMapView.getMapScaleBar().draw(graphicContext);
61 | parallelMapView.getFpsCounter().draw(graphicContext);
62 |
63 | graphicContext.destroy();
64 | }
65 |
66 | if (TRACE) {
67 | long thId = Thread.currentThread().getId();
68 | String msg = String.format(Locale.getDefault(), "Draw MySurface at thread #%d", thId);
69 | Log.d(TAG, msg);
70 | }
71 | }
72 |
73 | private SurfaceHolder.Callback callback = new SurfaceHolder.Callback() {
74 |
75 | ScheduledExecutorService exesvc;
76 |
77 | @Override
78 | public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
79 | if (TRACE) {
80 | Log.d(TAG, "surfaceChanged()");
81 | }
82 | try {
83 | long period = 1000000 / FPS;
84 | exesvc.scheduleAtFixedRate(drawTask, 0, period, TimeUnit.MICROSECONDS);
85 | exesvc.schedule(new Runnable() {
86 | @Override
87 | public void run() {
88 | exesvc.shutdown();
89 | }
90 | }, 1000, TimeUnit.SECONDS);
91 | } catch(RejectedExecutionException ex) {
92 | Log.e(TAG, "Cannot run drawTask.");
93 | }
94 | }
95 |
96 | @Override
97 | public void surfaceCreated(SurfaceHolder surfaceHolder) {
98 | if (TRACE) {
99 | Log.d(TAG, "surfaceCreated()");
100 | }
101 | exesvc = Executors.newSingleThreadScheduledExecutor();
102 | }
103 |
104 | @Override
105 | public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
106 | if (TRACE) {
107 | Log.d(TAG, "surfaceDestroyed()");
108 | }
109 | exesvc.shutdown();
110 | exesvc = null;
111 | }
112 |
113 | };
114 |
115 | private Runnable drawTask = new Runnable() {
116 |
117 | @Override
118 | public void run() {
119 | Canvas canvas = getHolder().lockCanvas();
120 | if (canvas!=null) {
121 | onDrawSurface(canvas);
122 | getHolder().unlockCanvasAndPost(canvas);
123 | postInvalidate();
124 | } else {
125 | Log.w(TAG, "canvas is null.");
126 | }
127 | }
128 | };
129 |
130 | }
131 |
--------------------------------------------------------------------------------
/app/src/main/java/tacoball/com/geomancer/map/Pin.java:
--------------------------------------------------------------------------------
1 | package tacoball.com.geomancer.map;
2 |
3 | import org.mapsforge.core.graphics.Bitmap;
4 | import org.mapsforge.core.graphics.Canvas;
5 | import org.mapsforge.core.graphics.FontFamily;
6 | import org.mapsforge.core.graphics.FontStyle;
7 | import org.mapsforge.core.graphics.GraphicFactory;
8 | import org.mapsforge.core.graphics.Matrix;
9 | import org.mapsforge.core.graphics.Paint;
10 | import org.mapsforge.core.graphics.Path;
11 | import org.mapsforge.core.model.BoundingBox;
12 | import org.mapsforge.core.model.LatLong;
13 | import org.mapsforge.core.model.Point;
14 | import org.mapsforge.core.util.MercatorProjection;
15 | import org.mapsforge.map.layer.Layer;
16 |
17 | /**
18 | * Pin of POI
19 | *
20 | * This Layer draw a pin and some text on the map directly.
21 | * The pin has selected and unselected state, using two different colors to represent.
22 | * Under selected state, a label string appears below the pin to explain what it is.
23 | *
24 | * @author 小璋丸
25 | */
26 | public class Pin extends Layer {
27 |
28 | private static final int PIN_WIDTH = 100;
29 | private static final int PIN_HEIGHT = 200;
30 |
31 | private LatLong latLong;
32 | private String category;
33 | private String label;
34 | private GraphicFactory gf;
35 | private boolean selected = false;
36 | private float angle = 0f;
37 | private float scale = 0.4f;
38 |
39 | private int darkColor = 0xff900000;
40 | private int brightColor = 0xffff0000;
41 |
42 | /**
43 | * Create a pin without category or label.
44 | *
45 | * @param latLong position
46 | * @param gf GraphicFactory
47 | */
48 | public Pin(LatLong latLong, GraphicFactory gf) {
49 | this(latLong, "", "", gf);
50 | }
51 |
52 | /**
53 | * Create a pin with category and label.
54 | *
55 | * @param latLong position
56 | * @param category text in the pin
57 | * @param label text below the pin
58 | * @param gf GraphicFactory
59 | */
60 | public Pin(LatLong latLong, String category, String label, GraphicFactory gf) {
61 | super();
62 |
63 | this.latLong = latLong;
64 | this.category = category;
65 | this.label = label;
66 | this.gf = gf;
67 |
68 | if (gf.getClass().getSimpleName().equals("AndroidGraphicFactory")) {
69 | scale = 1.0f;
70 | }
71 | }
72 |
73 | @Override
74 | public boolean onTap(LatLong tapLatLong, Point layerXY, Point tapXY) {
75 | double dx1 = tapXY.x - layerXY.x;
76 | double dy1 = tapXY.y - layerXY.y;
77 |
78 | double rad = Math.toRadians(-angle);
79 | double dx = (dx1 * Math.cos(rad) - dy1 * Math.sin(rad)) / scale;
80 | double dy = (dx1 * Math.sin(rad) + dy1 * Math.cos(rad)) / scale;
81 |
82 | if (Math.abs(dx) < PIN_WIDTH/2 && Math.abs(dy + PIN_HEIGHT/2) < PIN_HEIGHT/2) {
83 | selected = !selected;
84 | requestRedraw();
85 | return true;
86 | }
87 |
88 | return false;
89 | }
90 |
91 | @Override
92 | public synchronized void draw(BoundingBox boundingBox, byte zoomLevel, Canvas canvas, Point topLeftPoint) {
93 | long mapSize = MercatorProjection.getMapSize(zoomLevel, displayModel.getTileSize());
94 | int tx = (int)(MercatorProjection.longitudeToPixelX(latLong.longitude, mapSize) - topLeftPoint.x);
95 | int ty = (int)(MercatorProjection.latitudeToPixelY(latLong.latitude, mapSize) - topLeftPoint.y);
96 |
97 | Paint paint = gf.createPaint();
98 | if (selected) {
99 | paint.setColor(brightColor);
100 | } else {
101 | paint.setColor(darkColor);
102 | }
103 |
104 | Bitmap tempBitmap = gf.createBitmap(PIN_WIDTH, PIN_HEIGHT);
105 | Canvas tempCanvas = gf.createCanvas();
106 | tempCanvas.setBitmap(tempBitmap);
107 |
108 | // Draw triangle
109 | Path path = gf.createPath();
110 | path.moveTo(PIN_WIDTH/2, PIN_HEIGHT-1);
111 | path.lineTo((int)(PIN_WIDTH*0.2), PIN_WIDTH/2);
112 | path.lineTo((int)(PIN_WIDTH*0.8), PIN_WIDTH/2);
113 | path.close();
114 | tempCanvas.drawPath(path, paint);
115 |
116 | // Draw circle
117 | tempCanvas.drawCircle(PIN_WIDTH/2, PIN_WIDTH/2, PIN_WIDTH/2, paint);
118 |
119 | // Draw category
120 | paint.setColor(0xffffffff);
121 | paint.setTextSize(PIN_WIDTH * 0.55f);
122 | paint.setTypeface(FontFamily.SANS_SERIF, FontStyle.BOLD);
123 | int cx = (PIN_WIDTH - paint.getTextWidth(category))/2;
124 | int cy = (int)(PIN_WIDTH/2 + paint.getTextHeight(category)*0.35);
125 | tempCanvas.drawText(category, cx, cy, paint);
126 |
127 | // Paste pin
128 | Matrix matrix = gf.createMatrix();
129 | matrix.translate(tx, ty);
130 | matrix.scale(scale, scale);
131 | matrix.rotate((float)Math.toRadians(angle));
132 | matrix.translate(-PIN_WIDTH/2, -PIN_HEIGHT);
133 | canvas.drawBitmap(tempBitmap, matrix);
134 |
135 | // Draw label
136 | if (selected) {
137 | paint.setColor(0xff000000);
138 | paint.setTextSize(PIN_WIDTH * 0.4f);
139 | int margin = 10;
140 | int lw = paint.getTextWidth(label);
141 | int lh = paint.getTextHeight(label);
142 | int fw = lw + margin * 2;
143 | int fh = lh + margin * 2;
144 | tempBitmap = gf.createBitmap(fw, fh);
145 | tempCanvas.setBitmap(tempBitmap);
146 | tempCanvas.fillColor(0xe0ffd070);
147 | tempCanvas.drawText(label, margin, lh + margin - 3, paint);
148 |
149 | matrix.reset();
150 | matrix.translate(tx, ty);
151 | matrix.scale(scale, scale);
152 | matrix.rotate((float)Math.toRadians(angle));
153 | matrix.translate(-fw/2, margin);
154 |
155 | canvas.drawBitmap(tempBitmap, matrix);
156 | }
157 | }
158 |
159 | /**
160 | * Return label of the pin.
161 | *
162 | * @return label
163 | */
164 | public synchronized String getLabel() {
165 | return label;
166 | }
167 |
168 | @Override
169 | public synchronized LatLong getPosition() {
170 | return this.latLong;
171 | }
172 |
173 | /**
174 | * Set colors of the pin.
175 | *
176 | * @param darkColor unselected state color
177 | * @param brightColor selected state color
178 | */
179 | public synchronized void setPinColors(int darkColor, int brightColor) {
180 | this.darkColor = darkColor;
181 | this.brightColor = brightColor;
182 | }
183 |
184 | /**
185 | * Set position of the pin.
186 | *
187 | * @param latLong position
188 | */
189 | public synchronized void setPosition(LatLong latLong) {
190 | this.latLong = latLong;
191 | }
192 |
193 | /**
194 | * Set selection state of the pin.
195 | *
196 | * @param selected
197 | */
198 | public synchronized void setSelected(boolean selected) {
199 | this.selected = selected;
200 | }
201 |
202 | /**
203 | * Set angle of pin.
204 | *
205 | * @param angle
206 | */
207 | public synchronized void setAngle(float angle) {
208 | this.angle = angle;
209 | }
210 |
211 | }
212 |
213 |
--------------------------------------------------------------------------------
/app/src/main/java/tacoball/com/geomancer/map/PinGroup.java:
--------------------------------------------------------------------------------
1 | package tacoball.com.geomancer.map;
2 |
3 | import org.mapsforge.core.graphics.Canvas;
4 | import org.mapsforge.core.graphics.GraphicFactory;
5 | import org.mapsforge.core.model.BoundingBox;
6 | import org.mapsforge.core.model.LatLong;
7 | import org.mapsforge.core.model.Point;
8 | import org.mapsforge.map.layer.Layer;
9 | import org.mapsforge.map.util.MapViewProjection;
10 |
11 | import java.util.ArrayList;
12 | import java.util.HashMap;
13 | import java.util.List;
14 | import java.util.Map;
15 |
16 | /**
17 | * Maintain a set of pins and make all pins trigger onTap() event in single selection mode.
18 | *
19 | * @author 小璋丸
20 | */
21 | public class PinGroup extends Layer {
22 |
23 | private String category;
24 | private GraphicFactory gf;
25 | private MapViewProjection proj;
26 | private Map idOfPin = new HashMap<>();
27 |
28 | private int darkColor = 0xff900000;
29 | private int brightColor = 0xffff0000;
30 | private float angle = 0;
31 |
32 | private List pinPool = new ArrayList<>();
33 | private OnSelectListener listener = null;
34 |
35 | public interface OnSelectListener {
36 | void OnSelectPin(String category, String id);
37 | }
38 |
39 | /**
40 | * Create a PinGroup
41 | *
42 | * @param category text in the pin
43 | * @param gf GraphicFactory
44 | * @param proj Projection
45 | */
46 | public PinGroup(String category, GraphicFactory gf, MapViewProjection proj) {
47 | this.category = category;
48 | this.gf = gf;
49 | this.proj = proj;
50 | }
51 |
52 | /**
53 | * Add a pin into group.
54 | *
55 | * @param latLong position
56 | * @param label text below the pin
57 | * @param id if of the pin
58 | */
59 | public synchronized void add(LatLong latLong, String label, String id) {
60 | Pin p = new Pin(latLong, category, label, gf);
61 | p.setPinColors(darkColor, brightColor);
62 | p.setAngle(angle);
63 | pinPool.add(p);
64 | idOfPin.put(p, id);
65 | }
66 |
67 | /**
68 | * Remove all pins in the group.
69 | */
70 | public synchronized void clear() {
71 | pinPool.clear();
72 | requestRedraw();
73 | }
74 |
75 | /**
76 | * Set colors of all pins in the group.
77 | *
78 | * @param darkColor unselected state color
79 | * @param brightColor selected state color
80 | */
81 | public synchronized void setPinColors(int darkColor, int brightColor) {
82 | this.darkColor = darkColor;
83 | this.brightColor = brightColor;
84 |
85 | if (pinPool.size() > 0) {
86 | for (Pin p : pinPool) {
87 | p.setPinColors(darkColor, brightColor);
88 | }
89 | requestRedraw();
90 | }
91 | }
92 |
93 | public synchronized void setAngle(float angle) {
94 | this.angle = angle;
95 |
96 | if (pinPool.size() > 0) {
97 | for (Pin p : pinPool) {
98 | p.setAngle(angle);
99 | }
100 | requestRedraw();
101 | }
102 | }
103 |
104 | public synchronized void setOnSelectListener(OnSelectListener listener) {
105 | this.listener = listener;
106 | }
107 |
108 | public synchronized int size() {
109 | return pinPool.size();
110 | }
111 |
112 | @Override
113 | public boolean onTap(LatLong tapLatLong, Point layerXY, Point tapXY) {
114 | Pin bingo = null;
115 |
116 | // trigger bottom first
117 | for (int i = pinPool.size() - 1; i >= 0; i--) {
118 | Pin p = pinPool.get(i);
119 | Point pinXY = proj.toPixels(p.getPosition());
120 | if (p.onTap(tapLatLong, pinXY, tapXY)) {
121 | bingo = p;
122 | break;
123 | }
124 | }
125 |
126 | // deselect others & move bingo to the top.
127 | if (bingo != null) {
128 | pinPool.remove(bingo);
129 | for (Pin p : pinPool) {
130 | p.setSelected(false);
131 | }
132 | pinPool.add(0, bingo);
133 | requestRedraw();
134 |
135 | if (listener != null) {
136 | String id = idOfPin.get(bingo);
137 | listener.OnSelectPin(category, id);
138 | }
139 | }
140 |
141 | return false;
142 | }
143 |
144 | @Override
145 | public synchronized void draw(BoundingBox boundingBox, byte zoomLevel, Canvas canvas, Point topLeftPoint) {
146 | // draw bottom first
147 | for (int i = pinPool.size() - 1; i >= 0; i--) {
148 | Pin p = pinPool.get(i);
149 | p.setDisplayModel(getDisplayModel());
150 | p.draw(boundingBox, zoomLevel, canvas, topLeftPoint);
151 | }
152 | }
153 |
154 | }
155 |
--------------------------------------------------------------------------------
/app/src/main/java/tacoball/com/geomancer/map/SurfaceMapView.java:
--------------------------------------------------------------------------------
1 | package tacoball.com.geomancer.map;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.util.AttributeSet;
6 | import android.view.ViewGroup;
7 |
8 | import org.mapsforge.map.android.view.MapView;
9 |
10 | /**
11 | * Android MapView using SurfaceView
12 | */
13 | public class SurfaceMapView extends MapView {
14 |
15 | private FixedFpsSurfaceView mapSurface;
16 |
17 | public SurfaceMapView(Context context) {
18 | this(context, null);
19 | }
20 |
21 | public SurfaceMapView(Context context, AttributeSet attrs) {
22 | super(context, attrs);
23 |
24 | // Create SurfaceView to draw tiles.
25 | mapSurface = new FixedFpsSurfaceView(context);
26 | ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
27 | ViewGroup.LayoutParams.MATCH_PARENT,
28 | ViewGroup.LayoutParams.MATCH_PARENT
29 | );
30 | addViewInLayout(mapSurface, -1, params, true);
31 |
32 | // Move MapZoomControls to top of MapView.
33 | removeViewInLayout(getMapZoomControls());
34 | params = getMapZoomControls().getLayoutParams();
35 | addViewInLayout(getMapZoomControls(), -1, params, true);
36 | }
37 |
38 | @Override
39 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
40 | super.onLayout(changed, left, top, right, bottom);
41 | int w = mapSurface.getMeasuredWidth();
42 | int h = mapSurface.getMeasuredHeight();
43 | mapSurface.layout(0, 0, w, h);
44 | }
45 |
46 | @Override
47 | protected void onDraw(Canvas androidCanvas) {
48 | // Do nothing, draw everything by SurfaceView.
49 | }
50 |
51 | @Override
52 | public void destroy() {
53 | this.removeViewInLayout(mapSurface);
54 | // mapSurface.release();
55 | mapSurface = null;
56 | super.destroy();
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/app/src/main/java/tacoball/com/geomancer/view/CircleButton.java:
--------------------------------------------------------------------------------
1 | package tacoball.com.geomancer.view;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.Canvas;
6 | import android.graphics.Matrix;
7 | import android.graphics.Paint;
8 | import android.graphics.PorterDuff;
9 | import android.graphics.PorterDuffXfermode;
10 | import android.graphics.drawable.Drawable;
11 | import android.support.v7.widget.AppCompatImageView;
12 | import android.util.AttributeSet;
13 |
14 | /**
15 | * 覆蓋在地圖介面上的圓形按鈕,用來切換 Fragment
16 | */
17 | public class CircleButton extends AppCompatImageView {
18 |
19 | /**
20 | * 建構方法
21 | */
22 | public CircleButton(Context context, AttributeSet attrs) {
23 | super(context, attrs);
24 | }
25 |
26 | /**
27 | * 畫圓形按鈕的動作細節
28 | *
29 | * @param canvas 畫布
30 | */
31 | @Override
32 | protected void onDraw(Canvas canvas) {
33 | Canvas tempCv = new Canvas();
34 | Drawable d = getDrawable();
35 | Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
36 |
37 | int w = getMeasuredWidth();
38 | int h = getMeasuredHeight();
39 | int dw = d.getIntrinsicWidth();
40 | int dh = d.getIntrinsicHeight();
41 | float sw = (float)w/dw;
42 | float sh = (float)h/dh;
43 | float s = Math.max(sw, sh);
44 | float strokeWidth = 6.0f;
45 |
46 | // Create circle mask
47 | Bitmap mask = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
48 | tempCv.setBitmap(mask);
49 | p.setColor(0xffffffff);
50 | tempCv.drawCircle(w/2, h/2, w/2-1, p);
51 |
52 | // Paste Image
53 | Bitmap result = Bitmap.createBitmap(w, h , Bitmap.Config.ARGB_8888);
54 | tempCv.setBitmap(result);
55 | tempCv.scale(s, s);
56 | d.draw(tempCv);
57 | tempCv.setMatrix(new Matrix());
58 |
59 | // Apply circle mask
60 | p.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
61 | tempCv.drawBitmap(mask, 0, 0, p);
62 | p.setXfermode(null);
63 |
64 | // Draw circle border
65 | p.setColor(0xff7b5d1d);
66 | p.setStyle(Paint.Style.STROKE);
67 | p.setStrokeWidth(strokeWidth);
68 | tempCv.drawCircle(w/2, h/2, w/2-(float)Math.ceil(strokeWidth/2), p);
69 |
70 | // Display
71 | canvas.drawBitmap(result, 0, 0, p);
72 | }
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/app/src/main/java/tacoball/com/geomancer/view/LinkView.java:
--------------------------------------------------------------------------------
1 | package tacoball.com.geomancer.view;
2 |
3 | import android.content.Context;
4 | import android.support.v7.widget.AppCompatTextView;
5 | import android.text.method.LinkMovementMethod;
6 | import android.util.AttributeSet;
7 |
8 | /**
9 | * 用於 License 頁面,讓文字自動變成超連結
10 | */
11 | public class LinkView extends AppCompatTextView {
12 |
13 | /**
14 | * 自動變超連結的設定
15 | */
16 | @Override
17 | protected void onAttachedToWindow() {
18 | super.onAttachedToWindow();
19 | setMovementMethod(LinkMovementMethod.getInstance());
20 | }
21 |
22 | /**
23 | * 建構方法
24 | */
25 | public LinkView(Context context, AttributeSet attrs) {
26 | super(context, attrs);
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/contrib96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OsmHackTW/GeomancerAndroid/f134ee8751a0abe85325c3fd9707cd0f84f7a9c7/app/src/main/res/drawable/contrib96.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/debug_border.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/g0v_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OsmHackTW/GeomancerAndroid/f134ee8751a0abe85325c3fd9707cd0f84f7a9c7/app/src/main/res/drawable/g0v_dark.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/geomancer_info_border.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/license96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OsmHackTW/GeomancerAndroid/f134ee8751a0abe85325c3fd9707cd0f84f7a9c7/app/src/main/res/drawable/license96.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/measure_result_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/more96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OsmHackTW/GeomancerAndroid/f134ee8751a0abe85325c3fd9707cd0f84f7a9c7/app/src/main/res/drawable/more96.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/settings96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OsmHackTW/GeomancerAndroid/f134ee8751a0abe85325c3fd9707cd0f84f7a9c7/app/src/main/res/drawable/settings96.png
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_license.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
12 |
13 |
17 |
18 |
27 |
28 |
37 |
38 |
44 |
45 |
50 |
51 |
57 |
58 |
63 |
64 |
70 |
71 |
76 |
77 |
83 |
84 |
89 |
90 |
99 |
100 |
106 |
107 |
112 |
113 |
119 |
120 |
125 |
126 |
127 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_updater.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
14 |
15 |
22 |
23 |
30 |
31 |
38 |
39 |
48 |
49 |
55 |
56 |
57 |
58 |
66 |
67 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/geomancer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OsmHackTW/GeomancerAndroid/f134ee8751a0abe85325c3fd9707cd0f84f7a9c7/app/src/main/res/mipmap-xhdpi/geomancer.png
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 鄉民風水師
5 |
6 |
7 | 至少要選一項
8 | 定位功能沒開啟,請先打開吧
9 | 需要網路連線更新地圖,請打開網路後重試
10 | 無法存取檔案,是否儲存空間已用盡?
11 | 想用什麼風格呈現地圖呢?
12 | 讓地圖與眼前景物一致,也就是指向目前的方位角,停用時則會將地圖指向北方。
13 | 啟用凶宅資料庫
14 | 啟用違反勞基法事業資料庫
15 | 檔案確認完成,稍候進入鄉民風水師
16 | 位置權限請求已被您拒絕,如果想使用定位功能,請依下列步驟啟用位置權限:\n\n1. 按 [馬上設定]\n2. 按 [權限]\n3. 啟用 [位置]
17 | 馬上設定
18 | 不想定位
19 |
20 |
21 | 有 %s 風水資料可更新,想現在更新嗎?
22 | 有 %.2f MB 資料可更新,想現在更新嗎?
23 | 有 %.2f MB 資料可更新。
24 | 啟用了 %d 個除錯參數
25 | 凶宅 %d 間、血汗工廠 %d 間
26 | %s階段發生錯誤
27 | 凶宅 (%s)
28 | 屎缺 (%s)
29 |
30 |
31 | 方位:
32 | 關心什麼?
33 | 檢查網路連線
34 | 檢查更新檔
35 | 清除
36 | 海岸線圖資
37 | 貢獻者/貢獻單位
38 | 資料來源
39 | 軟體開發
40 | 協助測試
41 | 正在下載
42 | 正在解壓縮
43 | 零時政府
44 | 插圖設計
45 | 高雄市
46 | 緯:
47 | 授權資訊
48 | Apache 2.0
49 | LGPL 3.0
50 | ODbL 1.0
51 | 經:
52 | 地圖
53 | 地圖風格
54 | 神算
55 | 資料主機
56 | 30.0
57 | 25.0000
58 | 121.0000
59 | Something
60 | 15
61 | 定位
62 | 新北市
63 | 咩頌
64 | 開放資料授權
65 | 開放原始碼授權
66 | 開放街圖圖資
67 | 其他縣市
68 | 平安無事
69 | androidsvg 1.2.2
70 | commons-io 2.5
71 | gson 2.8.2
72 | mapsforge 0.8.0
73 | 準備下載
74 | 專案促成
75 | 正在修復
76 | 自動旋轉地圖
77 | 摘要
78 | 台中市
79 | 台南市
80 | 臺北市
81 | 桃園市
82 | 凶宅
83 | 凶宅資料庫
84 | 屎缺
85 | 違反勞基法事業\n格式化資料
86 | 血汗勞工資料庫
87 | 違反勞基法事業\n法定資料
88 | 資料更新
89 | 賀啊
90 | 地圖放大到 Z=13 才能算喔
91 | 縮放:
92 |
93 |
94 |
95 | - classic
96 | - default
97 |
98 |
99 |
100 |
101 | - 假掰文青風格
102 | - 沒FU普通風格
103 |
104 |
105 |
106 | 已取消更新
107 | 停用檢查更新的時間間隔限制
108 | 停用檢查更新的機率分流限制
109 | 啟用地圖元件除錯點功能
110 | 啟用一次性下載錯誤模擬
111 | 模擬檔案太舊強制更新
112 | 啟用一次性修復錯誤模擬
113 | 線上更新資料源是測試主機
114 |
115 |
116 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
11 |
12 |
18 |
19 |
20 |
21 |
22 |
33 |
34 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | google()
6 | jcenter()
7 | mavenCentral()
8 | }
9 | dependencies {
10 | // Gradle build tools
11 | classpath 'com.android.tools.build:gradle:3.2.1'
12 |
13 | // Firebase
14 | classpath 'com.google.gms:google-services:4.2.0'
15 | }
16 | }
17 |
18 | allprojects {
19 | repositories {
20 | google()
21 | jcenter()
22 | mavenCentral()
23 | }
24 | }
25 |
26 | task clean(type: Delete) {
27 | delete rootProject.buildDir
28 | }
29 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OsmHackTW/GeomancerAndroid/f134ee8751a0abe85325c3fd9707cd0f84f7a9c7/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Oct 11 09:51:40 GMT+07:00 2018
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-4.6-all.zip
--------------------------------------------------------------------------------
/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 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------