├── .gitignore ├── .idea ├── codeStyles │ └── Project.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── runConfigurations.xml └── vcs.xml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── wesaphzt │ │ └── privatelocation │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ ├── bootstrap │ │ │ └── bootstrap.min.css │ │ ├── font-awesome │ │ │ └── 4.7.0 │ │ │ │ ├── css │ │ │ │ └── font-awesome.min.css │ │ │ │ └── fonts │ │ │ │ └── fontawesome-webfont.woff2 │ │ ├── fonts │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ ├── glyphicons-halflings-regular.woff │ │ │ └── glyphicons-halflings-regular.woff2 │ │ ├── leaflet-locate │ │ │ ├── L.Control.Locate.min.css │ │ │ ├── L.Control.Locate.min.css.map │ │ │ └── L.Control.Locate.min.js │ │ ├── leaflet.html │ │ └── leaflet │ │ │ ├── images │ │ │ ├── layers-2x.png │ │ │ ├── layers.png │ │ │ ├── marker-icon-2x.png │ │ │ ├── marker-icon.png │ │ │ └── marker-shadow.png │ │ │ ├── leaflet.css │ │ │ └── leaflet.js │ ├── ic_launcher-web.png │ ├── java │ │ └── com │ │ │ └── wesaphzt │ │ │ └── privatelocation │ │ │ ├── IntroActivity.java │ │ │ ├── MainActivity.java │ │ │ ├── db │ │ │ ├── Favorite.java │ │ │ ├── FavoriteAdapter.java │ │ │ ├── FavoriteViewHolder.java │ │ │ └── SQLiteDB.java │ │ │ ├── fragments │ │ │ ├── DialogFragmentAddFavorite.java │ │ │ ├── DialogFragmentFavorite.java │ │ │ ├── DialogFragmentGoTo.java │ │ │ ├── FragmentAbout.java │ │ │ ├── FragmentDonate.java │ │ │ └── FragmentSettings.java │ │ │ ├── interfaces │ │ │ ├── ILatLong.java │ │ │ └── JSInterface.java │ │ │ ├── receivers │ │ │ ├── ActionReceiver.java │ │ │ └── BootReceiver.java │ │ │ ├── service │ │ │ ├── LocationProvider.java │ │ │ └── LocationService.java │ │ │ └── widget │ │ │ └── LocationWidgetProvider.java │ └── res │ │ ├── anim │ │ ├── enter_from_left.xml │ │ ├── enter_from_right.xml │ │ ├── exit_to_left.xml │ │ └── exit_to_right.xml │ │ ├── drawable │ │ ├── border_dark.xml │ │ ├── border_light.xml │ │ ├── ic_about_bug_report_black_24dp.xml │ │ ├── ic_about_code_black_24dp.xml │ │ ├── ic_about_copyright_black_24dp.xml │ │ ├── ic_about_info_outline_black_24dp.xml │ │ ├── ic_about_person_black_24dp.xml │ │ ├── ic_action_favorite_white_24dp.xml │ │ ├── ic_action_pin_drop_white_24dp.xml │ │ ├── ic_action_settings_white_24dp.xml │ │ ├── ic_donate_bitcoin_logo.xml │ │ ├── ic_donate_ethereum_logo.xml │ │ ├── ic_donate_litecoin_logo.xml │ │ ├── ic_donate_monero_logo.xml │ │ ├── ic_donate_note_text_emoji.xml │ │ ├── ic_empty_view.xml │ │ ├── ic_favorites_delete_black_24dp.xml │ │ ├── ic_favorites_edit_black_24dp.xml │ │ ├── ic_intro_cloud.xml │ │ ├── ic_intro_location.xml │ │ ├── ic_intro_settings.xml │ │ ├── ic_launcher_foreground.xml │ │ ├── ic_notification_pin_drop_white_24dp.xml │ │ ├── ic_widget_location_off_white_24dp.xml │ │ └── ic_widget_location_on_white_24dp.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── app_bar_main.xml │ │ ├── app_widget.xml │ │ ├── content_main.xml │ │ ├── dialog_favorites.xml │ │ ├── dialog_favorites_add.xml │ │ ├── dialog_favorites_list_item.xml │ │ ├── dialog_go_to.xml │ │ ├── fragment_about.xml │ │ └── fragment_donate.xml │ │ ├── menu │ │ └── menu_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── values-es │ │ └── strings.xml │ │ ├── values-it │ │ └── strings.xml │ │ ├── values-pt-rBR │ │ └── strings.xml │ │ ├── values-ru │ │ └── strings.xml │ │ ├── values │ │ ├── arrays.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── ic_launcher_background.xml │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ ├── app_widget_info.xml │ │ └── preferences.xml │ └── test │ └── java │ └── com │ └── wesaphzt │ └── privatelocation │ └── ExampleUnitTest.java ├── build.gradle ├── fastlane └── metadata │ └── android │ └── en-US │ ├── changelogs │ ├── 1.txt │ ├── 2.txt │ └── 4.txt │ ├── full_description.txt │ ├── images │ ├── featureGraphic.png │ ├── icon.png │ └── phoneScreenshots │ │ ├── 01-main.png │ │ ├── 02-favorites.png │ │ └── 03-goto.png │ ├── short_description.txt │ └── title.txt ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | xmlns:android 14 | 15 | ^$ 16 | 17 | 18 | 19 |
20 |
21 | 22 | 23 | 24 | xmlns:.* 25 | 26 | ^$ 27 | 28 | 29 | BY_NAME 30 | 31 |
32 |
33 | 34 | 35 | 36 | .*:id 37 | 38 | http://schemas.android.com/apk/res/android 39 | 40 | 41 | 42 |
43 |
44 | 45 | 46 | 47 | .*:name 48 | 49 | http://schemas.android.com/apk/res/android 50 | 51 | 52 | 53 |
54 |
55 | 56 | 57 | 58 | name 59 | 60 | ^$ 61 | 62 | 63 | 64 |
65 |
66 | 67 | 68 | 69 | style 70 | 71 | ^$ 72 | 73 | 74 | 75 |
76 |
77 | 78 | 79 | 80 | .* 81 | 82 | ^$ 83 | 84 | 85 | BY_NAME 86 | 87 |
88 |
89 | 90 | 91 | 92 | .* 93 | 94 | http://schemas.android.com/apk/res/android 95 | 96 | 97 | ANDROID_ATTRIBUTE_ORDER 98 | 99 |
100 |
101 | 102 | 103 | 104 | .* 105 | 106 | .* 107 | 108 | 109 | BY_NAME 110 | 111 |
112 |
113 |
114 |
115 |
116 |
-------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 27 | 42 | 43 | 44 | 45 | 46 | 47 | 49 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========== 3 | 4 | Release 1.2 *(2019-09-21)* 5 | ---------------------------- 6 | * Stop service correctly to fix memory leak & reduce code 7 | * Option to randomize location every x minutes added 8 | * App widget implemented 9 | * RU (@0ndrey), ES (@sguinetti), IT (@Lion-box), pt-rBR (@iramaro) translations added 10 | * pt-Rbr (@Andre-Gloria) typos corrected 11 | * Intro activity added 12 | * OpenStreetMap data attribution added 13 | * Notification priority changed to low for Nougat and lower 14 | * Minor code improvements & clean up 15 | * Logo update to prevent whitespace around edges 16 | * Gradle & library updates 17 | 18 | Release 1.1 *(2019-06-25)* 19 | ---------------------------- 20 | * Minor design updates 21 | * Start service on boot option added 22 | * Fastlane metadata for F-droid added 23 | 24 | Release 1.0 *(2019-06-01)* 25 | ---------------------------- 26 | * Initial release -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Private Location 2 | 3 | [![License](https://img.shields.io/github/license/wesaphzt/privatelocation.svg?color=green)](LICENSE) 4 | 5 | [Get it on F-Droid](https://f-droid.org/packages/com.wesaphzt.privatelocation/) 6 | 7 | A simple app to set your location to anywhere in the world, and improve general phone location privacy. 8 | 9 | This app will fake/spoof both your GPS and network location on your phone. 10 | 11 | Many apps on your phone won't work without location permissions, and can make repeated and unnecessary location requests in the background throughout the day. 12 | Setting your location somewhere else will help to mitigate that and protect your privacy. 13 | 14 | The open-source Leaflet map library along with Wikimedia to provide map tiles (OpenStreetMap) are used in place of Google Maps. 15 | 16 |

17 | 18 | 19 | 20 | 21 | 22 |

23 | 24 | ## Features 25 | - Set phone location 26 | - Favorite locations 27 | - Go to location from coordinates 28 | - Option to change to random location every x minutes 29 | - Notification when location is set 30 | - Find current location 31 | 32 | *Note: this app, or similar apps, are unlikely to fool apps or services that have a legitimate need to determine whether users are actually where they claim to be (gaming, check-in apps etc.), and will likely deploy various anti-spoofing techniques and methods to determine if you are there or not.* 33 | 34 | ## Requirements 35 | The app will guide you through the steps needed. 36 | 37 | 1. Developer settings will need to be enabled. 38 | 39 | 2. App will need to be set as the mock location app (or 'mock locations enabled' in older Android versions) 40 | 41 | For better privacy, or if you're having issues with your real location being exposed, it's best to turn off any options that allow use of WiFi and network to help determine your location. 42 | 43 | ## Issues 44 | To contribute, or to report issues please use the [Issue Tracker](https://github.com/wesaphzt/privatelocation/issues/). 45 | 46 | ## Privacy 47 | Free from ads and tracking. 48 | 49 | Map tiles are provided by Wikimedia [(Maps Terms of Service)](https://foundation.wikimedia.org/wiki/Maps_Terms_of_Use/), which uses [(OpenStreetMap)](https://www.openstreetmap.org/copyright/) data. 50 | 51 | ## License 52 | [GPL v3.0](LICENSE) 53 | 54 | ## Tips 55 | If you find these apps useful, consider supporting me in some way in my mission to create simple, useful, privacy-oriented, open-source apps. 56 | 57 | BTC: 1GCkvAg9oG79niQTbh6EH9rPALQDXKyHKK 58 | 59 | LTC: LV687s3wVdhmLZyJMFxomJHdHFXeFAKT5R 60 | 61 | ETH: 0x785a8804c85b88683a5cce5e53f60878831e5d03 62 | 63 | XMR: 43Vijzdt3y42mmT954rSYPjXYabDsjYEV2KyhxfC46JibR2ny9VmRS1fjdJTHxxPVPFE8ajgArwjWfyaRgjh9vcNAwmkfJj 64 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 29 5 | defaultConfig { 6 | applicationId "com.wesaphzt.privatelocation" 7 | minSdkVersion 17 8 | targetSdkVersion 29 9 | versionCode 4 10 | versionName "1.2" 11 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 12 | dataBinding.enabled = true 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | //custom dependencies 24 | implementation 'androidx.vectordrawable:vectordrawable-animated:1.1.0' 25 | implementation 'androidx.legacy:legacy-support-v4:1.0.0' 26 | implementation 'androidx.preference:preference:1.1.0' 27 | 28 | implementation 'com.github.GrenderG:Toasty:1.4.2' 29 | implementation 'com.github.AppIntro:AppIntro:5.1.0' 30 | 31 | //db 32 | implementation 'com.intuit.sdp:sdp-android:1.0.3' 33 | 34 | implementation fileTree(dir: 'libs', include: ['*.jar']) 35 | implementation 'androidx.appcompat:appcompat:1.1.0' 36 | implementation 'com.google.android.material:material:1.1.0-alpha10' 37 | implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta2' 38 | testImplementation 'junit:junit:4.13-beta-3' 39 | androidTestImplementation 'androidx.test:runner:1.3.0-alpha02' 40 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0-alpha02' 41 | } 42 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/wesaphzt/privatelocation/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.wesaphzt.privatelocation; 2 | 3 | import android.content.Context; 4 | import androidx.test.InstrumentationRegistry; 5 | import androidx.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.wesaphzt.privatelocation", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 26 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 54 | 55 | 56 | 57 | 58 | 59 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /app/src/main/assets/font-awesome/4.7.0/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesaphzt/privatelocation/d37b12f59d3f8de2b8506f44b09d2436b719fe50/app/src/main/assets/font-awesome/4.7.0/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /app/src/main/assets/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesaphzt/privatelocation/d37b12f59d3f8de2b8506f44b09d2436b719fe50/app/src/main/assets/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /app/src/main/assets/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesaphzt/privatelocation/d37b12f59d3f8de2b8506f44b09d2436b719fe50/app/src/main/assets/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /app/src/main/assets/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesaphzt/privatelocation/d37b12f59d3f8de2b8506f44b09d2436b719fe50/app/src/main/assets/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /app/src/main/assets/leaflet-locate/L.Control.Locate.min.css: -------------------------------------------------------------------------------- 1 | .leaflet-control-locate a { 2 | font-size:1.4em; 3 | color:#444; 4 | cursor:pointer 5 | } 6 | 7 | .leaflet-control-locate.active a { 8 | color:#2074b6 9 | } 10 | 11 | .leaflet-control-locate.active.following a { 12 | color:#fc8428 13 | } 14 | 15 | .leaflet-control-locate-location circle { 16 | animation:leaflet-control-locate-throb 4s ease infinite 17 | } 18 | @keyframes leaflet-control-locate-throb { 19 | 0% { 20 | r:9;stroke-width:1 21 | } 22 | 50% { 23 | r:7;stroke-width:3 24 | } 25 | 100% { 26 | r:9;stroke-width:1 27 | } 28 | } 29 | /*# sourceMappingURL=L.Control.Locate.min.css.map*/ -------------------------------------------------------------------------------- /app/src/main/assets/leaflet-locate/L.Control.Locate.min.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sourceRoot":"","sources":["../src/L.Control.Locate.scss"],"names":[],"mappings":"AAAA;AAEE;EACE;EACA;EACA;;AAGA;EACE;;AAEF;EACE;;;AAKN;EACE;;;AAGF;EACI;IAAK;IAAM;;EACZ;IAAM;IAAM;;EACb;IAAO;IAAM","file":"L.Control.Locate.css"} 2 | -------------------------------------------------------------------------------- /app/src/main/assets/leaflet.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Map 5 | 6 | 7 | 8 | 9 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 32 | 33 | 43 | 44 | 102 | 103 | 104 | 105 | 106 |
107 | 108 | 231 | 232 | 233 | 234 | 235 | -------------------------------------------------------------------------------- /app/src/main/assets/leaflet/images/layers-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesaphzt/privatelocation/d37b12f59d3f8de2b8506f44b09d2436b719fe50/app/src/main/assets/leaflet/images/layers-2x.png -------------------------------------------------------------------------------- /app/src/main/assets/leaflet/images/layers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesaphzt/privatelocation/d37b12f59d3f8de2b8506f44b09d2436b719fe50/app/src/main/assets/leaflet/images/layers.png -------------------------------------------------------------------------------- /app/src/main/assets/leaflet/images/marker-icon-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesaphzt/privatelocation/d37b12f59d3f8de2b8506f44b09d2436b719fe50/app/src/main/assets/leaflet/images/marker-icon-2x.png -------------------------------------------------------------------------------- /app/src/main/assets/leaflet/images/marker-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesaphzt/privatelocation/d37b12f59d3f8de2b8506f44b09d2436b719fe50/app/src/main/assets/leaflet/images/marker-icon.png -------------------------------------------------------------------------------- /app/src/main/assets/leaflet/images/marker-shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesaphzt/privatelocation/d37b12f59d3f8de2b8506f44b09d2436b719fe50/app/src/main/assets/leaflet/images/marker-shadow.png -------------------------------------------------------------------------------- /app/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesaphzt/privatelocation/d37b12f59d3f8de2b8506f44b09d2436b719fe50/app/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /app/src/main/java/com/wesaphzt/privatelocation/IntroActivity.java: -------------------------------------------------------------------------------- 1 | package com.wesaphzt.privatelocation; 2 | 3 | import android.os.Bundle; 4 | import android.view.WindowManager; 5 | 6 | import androidx.annotation.Nullable; 7 | import androidx.fragment.app.Fragment; 8 | 9 | import com.github.paolorotolo.appintro.AppIntro; 10 | import com.github.paolorotolo.appintro.AppIntroFragment; 11 | import com.github.paolorotolo.appintro.model.SliderPage; 12 | 13 | public class IntroActivity extends AppIntro { 14 | 15 | @Override 16 | protected void onCreate(@Nullable Bundle savedInstanceState) { 17 | super.onCreate(savedInstanceState); 18 | 19 | //fullscreen 20 | getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, 21 | WindowManager.LayoutParams.FLAG_FULLSCREEN); 22 | 23 | //slider pages 24 | SliderPage sliderPageOne = new SliderPage(); 25 | sliderPageOne.setTitle(getResources().getString(R.string.slider_page_one_title)); 26 | sliderPageOne.setDescription(getString(R.string.slider_page_one_desc)); 27 | sliderPageOne.setImageDrawable(R.drawable.ic_intro_location); 28 | sliderPageOne.setBgColor(getResources().getColor(R.color.colorPrimary)); 29 | addSlide(AppIntroFragment.newInstance(sliderPageOne)); 30 | 31 | SliderPage sliderPageTwo = new SliderPage(); 32 | sliderPageTwo.setTitle(getResources().getString(R.string.slider_page_two_title)); 33 | sliderPageTwo.setDescription(getResources().getString(R.string.slider_page_two_desc)); 34 | sliderPageTwo.setImageDrawable(R.drawable.ic_intro_cloud); 35 | sliderPageTwo.setBgColor(getResources().getColor(R.color.colorIntroGrey)); 36 | addSlide(AppIntroFragment.newInstance(sliderPageTwo)); 37 | 38 | SliderPage sliderPageThree = new SliderPage(); 39 | sliderPageThree.setTitle(getResources().getString(R.string.slider_page_three_title)); 40 | sliderPageThree.setDescription(getResources().getString(R.string.slider_page_three_desc)); 41 | sliderPageThree.setImageDrawable(R.drawable.ic_intro_settings); 42 | sliderPageThree.setBgColor(getResources().getColor(R.color.colorIntroGreen)); 43 | addSlide(AppIntroFragment.newInstance(sliderPageThree)); 44 | 45 | //options 46 | setFadeAnimation(); 47 | showSkipButton(false); 48 | setProgressButtonEnabled(true); 49 | 50 | //setBarColor(getResources().getColor(R.color.colorPrimary)); 51 | //setSeparatorColor(getResources().getColor(R.color.white)); 52 | } 53 | 54 | @Override 55 | public void onSkipPressed(Fragment currentFragment) { 56 | super.onSkipPressed(currentFragment); 57 | } 58 | 59 | @Override 60 | public void onDonePressed(Fragment currentFragment) { 61 | super.onDonePressed(currentFragment); 62 | finish(); 63 | } 64 | 65 | @Override 66 | public void onSlideChanged(@Nullable Fragment oldFragment, @Nullable Fragment newFragment) { 67 | super.onSlideChanged(oldFragment, newFragment); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /app/src/main/java/com/wesaphzt/privatelocation/db/Favorite.java: -------------------------------------------------------------------------------- 1 | package com.wesaphzt.privatelocation.db; 2 | 3 | public class Favorite { 4 | 5 | private int id; 6 | private String name; 7 | private Double lat; 8 | private Double lng; 9 | 10 | public Favorite(String name, Double lat, Double lng) { 11 | this.name = name; 12 | this.lat = lat; 13 | this.lng = lng; 14 | } 15 | 16 | public Favorite(int id, String name, Double lat, Double lng) { 17 | this.id = id; 18 | this.name = name; 19 | this.lat = lat; 20 | this.lng = lng; 21 | } 22 | 23 | public int getId() { 24 | return id; 25 | } 26 | 27 | public void setId(int id) { 28 | this.id = id; 29 | } 30 | 31 | public String getName() { 32 | return name; 33 | } 34 | 35 | public void setName(String name) { 36 | this.name = name; 37 | } 38 | 39 | public Double getLat() { 40 | return lat; 41 | } 42 | 43 | Double getLong() { 44 | return lng; 45 | } 46 | } -------------------------------------------------------------------------------- /app/src/main/java/com/wesaphzt/privatelocation/db/FavoriteAdapter.java: -------------------------------------------------------------------------------- 1 | package com.wesaphzt.privatelocation.db; 2 | 3 | import android.content.Context; 4 | import android.content.DialogInterface; 5 | 6 | import android.text.TextUtils; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.EditText; 11 | import android.widget.TextView; 12 | import android.widget.Toast; 13 | 14 | import androidx.appcompat.app.AlertDialog; 15 | import androidx.recyclerview.widget.RecyclerView; 16 | 17 | import com.wesaphzt.privatelocation.fragments.DialogFragmentFavorite; 18 | import com.wesaphzt.privatelocation.MainActivity; 19 | import com.wesaphzt.privatelocation.R; 20 | 21 | import java.util.List; 22 | 23 | public class FavoriteAdapter extends RecyclerView.Adapter { 24 | 25 | private Context context; 26 | 27 | private List listFavorites; 28 | private SQLiteDB mDatabase; 29 | private int position; 30 | 31 | private View view; 32 | private MainActivity mainActivity; 33 | private DialogFragmentFavorite dfFavorite; 34 | 35 | public FavoriteAdapter(Context context, List listFavorites, MainActivity mActivity, DialogFragmentFavorite dfFavorite) { 36 | this.context = context; 37 | this.listFavorites = listFavorites; 38 | mDatabase = new SQLiteDB(context); 39 | this.mainActivity = mActivity; 40 | this.dfFavorite = dfFavorite; 41 | } 42 | 43 | @Override 44 | public FavoriteViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 45 | view = LayoutInflater.from(parent.getContext()).inflate(R.layout.dialog_favorites_list_item, parent, false); 46 | return new FavoriteViewHolder(view); 47 | } 48 | 49 | @Override 50 | public void onBindViewHolder(final FavoriteViewHolder holder, final int mPosition) { 51 | final Favorite singleFavorite = listFavorites.get(mPosition); 52 | 53 | view.setOnClickListener(new View.OnClickListener() { 54 | @Override 55 | public void onClick(View v) { 56 | if(mainActivity != null){ 57 | //get position again in case delete operations have been done prior 58 | Favorite favorite = listFavorites.get(mPosition); 59 | mainActivity.setLocation(favorite.getLat(), favorite.getLong()); 60 | } 61 | 62 | dfFavorite.dismiss(); 63 | } 64 | }); 65 | 66 | holder.name.setText(singleFavorite.getName()); 67 | 68 | holder.editFavorite.setOnClickListener(new View.OnClickListener() { 69 | @Override 70 | public void onClick(View view) { 71 | position = mPosition; 72 | 73 | editTaskDialog(singleFavorite); 74 | } 75 | }); 76 | 77 | holder.deleteFavorite.setOnClickListener(new View.OnClickListener() { 78 | @Override 79 | public void onClick(View view) { 80 | position = mPosition; 81 | 82 | //delete row from database 83 | mDatabase.deleteProduct(singleFavorite.getId()); 84 | 85 | //remove and notify change 86 | listFavorites.remove(position); 87 | //notify of position changes 88 | notifyDataSetChanged(); 89 | } 90 | }); 91 | } 92 | 93 | @Override 94 | public int getItemCount() { 95 | return listFavorites.size(); 96 | } 97 | 98 | private void editTaskDialog(final Favorite favorite){ 99 | LayoutInflater inflater = LayoutInflater.from(context); 100 | View subView = inflater.inflate(R.layout.dialog_favorites_add, null); 101 | 102 | final EditText nameField = (EditText)subView.findViewById(R.id.enter_name); 103 | final TextView latLngField = (TextView) subView.findViewById(R.id.display_lat_long); 104 | 105 | if(favorite != null){ 106 | nameField.setText(favorite.getName()); 107 | latLngField.setText("Latitude: " + String.valueOf(favorite.getLat() + "\nLongitude: " + String.valueOf(favorite.getLong()))); 108 | } 109 | 110 | AlertDialog.Builder builder = new AlertDialog.Builder(context); 111 | builder.setTitle("Edit Favorite"); 112 | builder.setView(subView); 113 | builder.create(); 114 | 115 | builder.setPositiveButton("EDIT", new DialogInterface.OnClickListener() { 116 | @Override 117 | public void onClick(DialogInterface dialog, int which) { 118 | 119 | final String name = nameField.getText().toString(); 120 | 121 | if(TextUtils.isEmpty(name) /*|| lat_long <= 0*/){ 122 | Toast.makeText(context, "Can't be empty", Toast.LENGTH_LONG).show(); 123 | } else { 124 | mDatabase.updateFavorite(new Favorite(favorite.getId(), name, favorite.getLat(), favorite.getLong())); 125 | 126 | listFavorites.set(position, new Favorite(favorite.getId(), name, favorite.getLat(), favorite.getLong())); 127 | notifyItemChanged(position); 128 | } 129 | } 130 | }); 131 | 132 | builder.setNegativeButton("CANCEL", new DialogInterface.OnClickListener() { 133 | @Override 134 | public void onClick(DialogInterface dialog, int which) { 135 | 136 | } 137 | }); 138 | 139 | builder.show(); 140 | } 141 | 142 | } -------------------------------------------------------------------------------- /app/src/main/java/com/wesaphzt/privatelocation/db/FavoriteViewHolder.java: -------------------------------------------------------------------------------- 1 | package com.wesaphzt.privatelocation.db; 2 | 3 | import android.view.View; 4 | import android.widget.ImageView; 5 | import android.widget.TextView; 6 | 7 | import androidx.recyclerview.widget.RecyclerView; 8 | 9 | import com.wesaphzt.privatelocation.R; 10 | 11 | public class FavoriteViewHolder extends RecyclerView.ViewHolder { 12 | 13 | public TextView name; 14 | ImageView deleteFavorite; 15 | ImageView editFavorite; 16 | 17 | FavoriteViewHolder(View itemView) { 18 | super(itemView); 19 | 20 | name = (TextView)itemView.findViewById(R.id.favorite_name); 21 | deleteFavorite = (ImageView)itemView.findViewById(R.id.delete_favorite); 22 | editFavorite = (ImageView)itemView.findViewById(R.id.edit_favorite); 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/java/com/wesaphzt/privatelocation/db/SQLiteDB.java: -------------------------------------------------------------------------------- 1 | package com.wesaphzt.privatelocation.db; 2 | 3 | import android.content.ContentValues; 4 | import android.content.Context; 5 | import android.database.Cursor; 6 | import android.database.sqlite.SQLiteOpenHelper; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | public class SQLiteDB extends SQLiteOpenHelper { 12 | private static final int DATABASE_VERSION = 5; 13 | private static final String DATABASE_NAME = "favorites"; 14 | private static final String TABLE_FAVORITES = "favorites"; 15 | private static final String COLUMN_ID = "_id"; 16 | private static final String COLUMN_FAVORITENAME = "favoritename"; 17 | private static final String COLUMN_LAT = "lat"; 18 | private static final String COLUMN_LNG = "lng"; 19 | 20 | public SQLiteDB(Context context) { 21 | super(context, DATABASE_NAME, null, DATABASE_VERSION); 22 | } 23 | 24 | @Override 25 | public void onCreate(android.database.sqlite.SQLiteDatabase db) { 26 | String CREATE_FAVORITES_TABLE = "CREATE TABLE " + TABLE_FAVORITES + "(" + COLUMN_ID + " INTEGER PRIMARY KEY," + COLUMN_FAVORITENAME + " TEXT," + COLUMN_LAT + " DOUBLE," + COLUMN_LNG + " DOUBLE" + ")"; 27 | db.execSQL(CREATE_FAVORITES_TABLE); 28 | } 29 | 30 | @Override 31 | public void onUpgrade(android.database.sqlite.SQLiteDatabase db, int oldVersion, int newVersion) { 32 | db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES); 33 | onCreate(db); 34 | } 35 | 36 | public List listFavorites(){ 37 | String sql = "select * from " + TABLE_FAVORITES; 38 | android.database.sqlite.SQLiteDatabase db = this.getReadableDatabase(); 39 | List storeFavorites = new ArrayList<>(); 40 | Cursor cursor = db.rawQuery(sql, null); 41 | if(cursor.moveToFirst()){ 42 | do{ 43 | int id = Integer.parseInt(cursor.getString(0)); 44 | String name = cursor.getString(1); 45 | Double lat = cursor.getDouble(2); 46 | Double lng = cursor.getDouble(3); 47 | storeFavorites.add(new Favorite(id, name, lat, lng)); 48 | } while (cursor.moveToNext()); 49 | } 50 | cursor.close(); 51 | return storeFavorites; 52 | } 53 | 54 | public void addProduct(Favorite favorite){ 55 | ContentValues values = new ContentValues(); 56 | values.put(COLUMN_FAVORITENAME, favorite.getName()); 57 | values.put(COLUMN_LAT, favorite.getLat()); 58 | values.put(COLUMN_LNG, favorite.getLong()); 59 | android.database.sqlite.SQLiteDatabase db = this.getWritableDatabase(); 60 | db.insert(TABLE_FAVORITES, null, values); 61 | } 62 | 63 | void updateFavorite(Favorite favorite){ 64 | ContentValues values = new ContentValues(); 65 | values.put(COLUMN_FAVORITENAME, favorite.getName()); 66 | values.put(COLUMN_LAT, favorite.getLat()); 67 | values.put(COLUMN_LNG, favorite.getLong()); 68 | android.database.sqlite.SQLiteDatabase db = this.getWritableDatabase(); 69 | db.update(TABLE_FAVORITES, values, COLUMN_ID + " = ?", new String[] { String.valueOf(favorite.getId())}); 70 | } 71 | 72 | public Favorite findFavorite(String name){ 73 | String query = "Select * FROM " + TABLE_FAVORITES + " WHERE " + COLUMN_FAVORITENAME + " = " + "name"; 74 | android.database.sqlite.SQLiteDatabase db = this.getWritableDatabase(); 75 | Favorite mFavorite = null; 76 | Cursor cursor = db.rawQuery(query, null); 77 | if (cursor.moveToFirst()){ 78 | int id = Integer.parseInt(cursor.getString(0)); 79 | String favoriteName = cursor.getString(1); 80 | Double favoriteLat = cursor.getDouble(2); 81 | Double favoriteLng = cursor.getDouble(3); 82 | 83 | mFavorite = new Favorite(id, favoriteName, favoriteLat, favoriteLng); 84 | } 85 | cursor.close(); 86 | return mFavorite; 87 | } 88 | 89 | void deleteProduct(int id){ 90 | android.database.sqlite.SQLiteDatabase db = this.getWritableDatabase(); 91 | db.delete(TABLE_FAVORITES, COLUMN_ID + " = ?", new String[] { String.valueOf(id)}); 92 | } 93 | } -------------------------------------------------------------------------------- /app/src/main/java/com/wesaphzt/privatelocation/fragments/DialogFragmentAddFavorite.java: -------------------------------------------------------------------------------- 1 | package com.wesaphzt.privatelocation.fragments; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.app.Dialog; 5 | import android.content.Context; 6 | import android.content.DialogInterface; 7 | import android.os.Bundle; 8 | import android.text.Editable; 9 | import android.text.TextUtils; 10 | import android.view.LayoutInflater; 11 | import android.view.View; 12 | import android.widget.EditText; 13 | import android.widget.TextView; 14 | import android.widget.Toast; 15 | 16 | import androidx.annotation.NonNull; 17 | import androidx.appcompat.app.AlertDialog; 18 | import androidx.fragment.app.DialogFragment; 19 | 20 | import com.wesaphzt.privatelocation.R; 21 | import com.wesaphzt.privatelocation.db.Favorite; 22 | import com.wesaphzt.privatelocation.db.SQLiteDB; 23 | 24 | import es.dmoral.toasty.Toasty; 25 | 26 | public class DialogFragmentAddFavorite extends DialogFragment { 27 | 28 | private Context context; 29 | 30 | private SQLiteDB mDatabase; 31 | 32 | @NonNull 33 | @SuppressLint("InflateParams") 34 | @Override 35 | public Dialog onCreateDialog(Bundle savedInstanceState) { 36 | 37 | //init 38 | context = getContext(); 39 | 40 | mDatabase = new SQLiteDB(context); 41 | 42 | assert getArguments() != null; 43 | final double mLat = getArguments().getDouble("lat"); 44 | final double mLng = getArguments().getDouble("lng"); 45 | 46 | LayoutInflater inflater = LayoutInflater.from(context); 47 | View subView = inflater.inflate(R.layout.dialog_favorites_add, null); 48 | 49 | final EditText etName = (EditText) subView.findViewById(R.id.enter_name); 50 | final TextView tvLatLong = (TextView) subView.findViewById(R.id.display_lat_long); 51 | 52 | tvLatLong.setText(getString(R.string.dialog_favorites_add_lat_long, mLat, mLng)); 53 | 54 | AlertDialog.Builder builder = new AlertDialog.Builder(context); 55 | builder.setTitle("Add Favorite"); 56 | builder.setView(subView); 57 | builder.create(); 58 | 59 | builder.setPositiveButton("ADD", new DialogInterface.OnClickListener() { 60 | @Override 61 | public void onClick(DialogInterface dialog, int which) { 62 | if (isEmpty(etName.getText())) { 63 | //is empty 64 | Toasty.info(context, "Can't be empty", Toast.LENGTH_SHORT, true).show(); 65 | } else { 66 | //isn't empty 67 | Favorite newFavorite = new Favorite(etName.getText().toString(), mLat, mLng); 68 | mDatabase.addProduct(newFavorite); 69 | 70 | Toasty.success(context, "Favorite added", Toast.LENGTH_SHORT, true).show(); 71 | } 72 | } 73 | }); 74 | 75 | builder.setNegativeButton("CANCEL", new DialogInterface.OnClickListener() { 76 | @Override 77 | public void onClick(DialogInterface dialog, int which) { 78 | dismiss(); 79 | } 80 | }); 81 | 82 | return builder.create(); 83 | } 84 | 85 | private boolean isEmpty(Editable value) { 86 | return TextUtils.isEmpty(value); 87 | } 88 | } -------------------------------------------------------------------------------- /app/src/main/java/com/wesaphzt/privatelocation/fragments/DialogFragmentFavorite.java: -------------------------------------------------------------------------------- 1 | package com.wesaphzt.privatelocation.fragments; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.app.Dialog; 5 | import android.content.Context; 6 | import android.os.Bundle; 7 | import androidx.annotation.NonNull; 8 | import androidx.fragment.app.DialogFragment; 9 | import androidx.appcompat.app.AlertDialog; 10 | import androidx.recyclerview.widget.LinearLayoutManager; 11 | import androidx.recyclerview.widget.RecyclerView; 12 | 13 | import android.view.LayoutInflater; 14 | import android.view.View; 15 | import android.widget.TextView; 16 | 17 | import com.wesaphzt.privatelocation.MainActivity; 18 | import com.wesaphzt.privatelocation.R; 19 | import com.wesaphzt.privatelocation.db.Favorite; 20 | import com.wesaphzt.privatelocation.db.FavoriteAdapter; 21 | import com.wesaphzt.privatelocation.db.SQLiteDB; 22 | 23 | import java.util.List; 24 | 25 | public class DialogFragmentFavorite extends DialogFragment { 26 | 27 | private SQLiteDB mDatabase; 28 | 29 | private MainActivity mainActivity; 30 | 31 | public DialogFragmentFavorite(Context context, MainActivity mActivity) { 32 | mainActivity = mActivity; 33 | } 34 | 35 | //default constructor 36 | public DialogFragmentFavorite() { } 37 | 38 | @NonNull 39 | @SuppressLint("InflateParams") 40 | @Override 41 | public Dialog onCreateDialog(Bundle savedInstanceState) { 42 | 43 | Context context = getContext(); 44 | 45 | //setup alert builder 46 | AlertDialog.Builder builder = new AlertDialog.Builder(context); 47 | builder.setTitle(getString(R.string.dialog_favorites_title)); 48 | 49 | //set listview as view 50 | LayoutInflater inflater = getActivity().getLayoutInflater(); 51 | final View view = inflater.inflate(R.layout.dialog_favorites, null); 52 | builder.setView(view); 53 | 54 | RecyclerView favoriteView = (RecyclerView) view.findViewById(R.id.favorite_list); 55 | LinearLayoutManager linearLayoutManager = new LinearLayoutManager(context); 56 | 57 | //empty view 58 | TextView tvEmpty = view.findViewById(R.id.tvEmpty); 59 | 60 | favoriteView.setLayoutManager(linearLayoutManager); 61 | favoriteView.setHasFixedSize(true); 62 | 63 | //db 64 | mDatabase = new SQLiteDB(context); 65 | List allFavorites = mDatabase.listFavorites(); 66 | 67 | //if db empty 68 | if(allFavorites.isEmpty()) { 69 | favoriteView.setVisibility(View.GONE); 70 | tvEmpty.setVisibility(View.VISIBLE); 71 | 72 | } else { 73 | favoriteView.setVisibility(View.VISIBLE); 74 | tvEmpty.setVisibility(View.GONE); 75 | //pass mainActivity so we can callback in FavoriteAdapter 76 | FavoriteAdapter mAdapter = new FavoriteAdapter(context, allFavorites, mainActivity, DialogFragmentFavorite.this); 77 | favoriteView.setAdapter(mAdapter); 78 | } 79 | 80 | return builder.create(); 81 | } 82 | 83 | @Override 84 | public void onDestroy() { 85 | super.onDestroy(); 86 | 87 | if(mDatabase != null){ 88 | mDatabase.close(); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /app/src/main/java/com/wesaphzt/privatelocation/fragments/DialogFragmentGoTo.java: -------------------------------------------------------------------------------- 1 | package com.wesaphzt.privatelocation.fragments; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.app.Dialog; 5 | import android.content.Context; 6 | import android.content.DialogInterface; 7 | import android.os.Bundle; 8 | 9 | import androidx.annotation.NonNull; 10 | import androidx.fragment.app.DialogFragment; 11 | import androidx.appcompat.app.AlertDialog; 12 | import android.text.Editable; 13 | import android.text.TextUtils; 14 | import android.util.Log; 15 | import android.view.LayoutInflater; 16 | import android.view.View; 17 | import android.widget.EditText; 18 | import android.widget.Toast; 19 | 20 | import com.wesaphzt.privatelocation.R; 21 | import com.wesaphzt.privatelocation.interfaces.ILatLong; 22 | 23 | import es.dmoral.toasty.Toasty; 24 | 25 | public class DialogFragmentGoTo extends DialogFragment { 26 | 27 | private Context context; 28 | 29 | private Double dLat; 30 | private final Double latMin = -90.0; 31 | private final Double latMax = 90.0; 32 | 33 | private Double dLng; 34 | private final Double lngMin = -180.0; 35 | private final Double lngMax = 180.0; 36 | 37 | private ILatLong mCallback; 38 | 39 | @NonNull 40 | @SuppressLint("InflateParams") 41 | @Override 42 | public Dialog onCreateDialog(Bundle savedInstanceState) { 43 | 44 | //init 45 | context = getContext(); 46 | 47 | LayoutInflater inflater = LayoutInflater.from(context); 48 | View subView = inflater.inflate(R.layout.dialog_go_to, null); 49 | 50 | final EditText etLatitude = (EditText) subView.findViewById(R.id.enter_latitude); 51 | final EditText etLongitude = (EditText) subView.findViewById(R.id.enter_longitude); 52 | 53 | AlertDialog.Builder builder = new AlertDialog.Builder(context); 54 | builder.setTitle(R.string.dialog_go_to_title); 55 | builder.setView(subView); 56 | builder.create(); 57 | 58 | builder.setPositiveButton("GO", new DialogInterface.OnClickListener() { 59 | @Override 60 | public void onClick(DialogInterface dialog, int which) { 61 | if(!isEmpty(etLatitude.getText()) || !isEmpty(etLongitude.getText())) { 62 | //isn't empty 63 | try { 64 | //is double 65 | dLat = Double.parseDouble(etLatitude.getText().toString()); 66 | dLng = Double.parseDouble(etLongitude.getText().toString()); 67 | 68 | if (!isMinMax(dLat, latMin, latMax) || !isMinMax(dLng, latMin, latMax)) { 69 | //isn't valid lat/long range 70 | Toasty.info(context, getString(R.string.dialog_go_to_invalid_range), Toast.LENGTH_SHORT, true).show(); 71 | } else { 72 | //is valid lat/long range 73 | mCallback.setLocation(dLat, dLng); 74 | } 75 | } catch (Exception e1) { 76 | //isn't double 77 | Toasty.info(context, R.string.dialog_go_to_invalid_number, Toast.LENGTH_SHORT, true).show(); 78 | } 79 | } else { 80 | //is empty 81 | Toasty.info(context, R.string.dialog_go_to_invalid_empty, Toast.LENGTH_SHORT, true).show(); 82 | } 83 | } 84 | }); 85 | 86 | builder.setNegativeButton("CANCEL", new DialogInterface.OnClickListener() { 87 | @Override 88 | public void onClick(DialogInterface dialog, int which) { 89 | dismiss(); 90 | } 91 | }); 92 | 93 | return builder.create(); 94 | } 95 | 96 | private boolean isMinMax(Double num, Double min, Double max) { 97 | return (num > min && num < max); 98 | } 99 | 100 | private boolean isEmpty(Editable value){ 101 | return TextUtils.isEmpty(value); 102 | } 103 | 104 | @Override 105 | public void onAttach(Context context) { 106 | super.onAttach(context); 107 | 108 | try { 109 | mCallback = (ILatLong) context; 110 | } 111 | catch (ClassCastException e) { 112 | Log.d("DialogFragmentGoTo", "ILatLong not implemented"); 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /app/src/main/java/com/wesaphzt/privatelocation/fragments/FragmentAbout.java: -------------------------------------------------------------------------------- 1 | package com.wesaphzt.privatelocation.fragments; 2 | 3 | import android.content.Intent; 4 | import android.content.pm.PackageInfo; 5 | import android.content.pm.PackageManager; 6 | import android.net.Uri; 7 | import android.os.Bundle; 8 | import androidx.annotation.Nullable; 9 | import androidx.fragment.app.Fragment; 10 | 11 | import android.util.Log; 12 | import android.view.LayoutInflater; 13 | import android.view.Menu; 14 | import android.view.View; 15 | import android.view.ViewGroup; 16 | import android.widget.LinearLayout; 17 | import android.widget.TextView; 18 | 19 | import com.wesaphzt.privatelocation.R; 20 | 21 | public class FragmentAbout extends Fragment { 22 | 23 | private static String GITHUB_URI; 24 | private static String LICENSE_URI; 25 | private static String BUG_REPORT_URI; 26 | private static String AUTHOR_GITHUB; 27 | 28 | @Nullable 29 | @Override 30 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 31 | View v = inflater.inflate(R.layout.fragment_about, container, false); 32 | 33 | GITHUB_URI = getString(R.string.app_github); 34 | LICENSE_URI = GITHUB_URI + "/blob/master/LICENSE"; 35 | BUG_REPORT_URI = GITHUB_URI + "/issues"; 36 | AUTHOR_GITHUB = getString(R.string.app_github_dev); 37 | 38 | String versionName = ""; 39 | try { 40 | PackageInfo packageInfo = getActivity().getPackageManager().getPackageInfo(getActivity().getPackageName(), 0); 41 | versionName = packageInfo.versionName; 42 | } catch (PackageManager.NameNotFoundException e) { 43 | e.printStackTrace(); 44 | } 45 | 46 | TextView version = v.findViewById(R.id.about_text_version); 47 | version.setText(versionName); 48 | 49 | LinearLayout license = v.findViewById(R.id.about_layout_license); 50 | LinearLayout source = v.findViewById(R.id.about_layout_source); 51 | 52 | license.setOnClickListener(new View.OnClickListener() { 53 | @Override 54 | public void onClick(View view) { 55 | openURI(LICENSE_URI); 56 | } 57 | }); 58 | source.setOnClickListener(new View.OnClickListener() { 59 | @Override 60 | public void onClick(View view) { 61 | openURI(GITHUB_URI); 62 | } 63 | }); 64 | 65 | LinearLayout authorLayout = v.findViewById(R.id.aboutLayoutAuthor); 66 | authorLayout.setOnClickListener(new View.OnClickListener() { 67 | @Override 68 | public void onClick(View view) { 69 | openURI(AUTHOR_GITHUB); 70 | } 71 | }); 72 | 73 | LinearLayout bugReport = v.findViewById(R.id.about_layout_bugs); 74 | bugReport.setOnClickListener(new View.OnClickListener() { 75 | @Override 76 | public void onClick(View view) { 77 | openURI(BUG_REPORT_URI); 78 | } 79 | }); 80 | 81 | return v; 82 | } 83 | 84 | @Override 85 | public void onCreate(Bundle savedInstanceState) { 86 | setHasOptionsMenu(true); 87 | 88 | super.onCreate(savedInstanceState); 89 | } 90 | 91 | private void openURI(String uri) { 92 | try { 93 | Intent openURI = new Intent(Intent.ACTION_VIEW); 94 | openURI.setData(Uri.parse(uri)); 95 | startActivity(openURI); 96 | } catch (Exception e) { 97 | Log.d("app-error", "error opening uri"); 98 | } 99 | } 100 | 101 | @Override 102 | public void onPrepareOptionsMenu(Menu menu) { 103 | //hide action bar menu 104 | menu.setGroupVisible(R.id.menu_top, false); 105 | menu.setGroupVisible(R.id.menu_bottom, false); 106 | 107 | super.onPrepareOptionsMenu(menu); 108 | } 109 | 110 | @Override 111 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 112 | super.onViewCreated(view, savedInstanceState); 113 | //set title 114 | getActivity().setTitle(getString(R.string.fragment_about_title)); 115 | } 116 | } -------------------------------------------------------------------------------- /app/src/main/java/com/wesaphzt/privatelocation/fragments/FragmentDonate.java: -------------------------------------------------------------------------------- 1 | package com.wesaphzt.privatelocation.fragments; 2 | 3 | import android.content.ClipData; 4 | import android.content.ClipboardManager; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.net.Uri; 8 | import android.os.Bundle; 9 | 10 | import androidx.annotation.Nullable; 11 | import androidx.fragment.app.Fragment; 12 | import android.view.LayoutInflater; 13 | import android.view.Menu; 14 | import android.view.View; 15 | import android.view.ViewGroup; 16 | import android.widget.TextView; 17 | import android.widget.Toast; 18 | 19 | import com.wesaphzt.privatelocation.R; 20 | 21 | import es.dmoral.toasty.Toasty; 22 | 23 | public class FragmentDonate extends Fragment { 24 | 25 | private Context context; 26 | 27 | private static final String BITCOIN_PREFIX = "bitcoin:"; 28 | private static final String LITECOIN_PREFIX = "litecoin:"; 29 | private static final String ETHEREUM_PREFIX = "ethereum:"; 30 | private static final String MONERO_PREFIX = "monero:"; 31 | 32 | private static String BITCOIN_ADDRESS; 33 | private static String BITCOIN_FULL; 34 | 35 | private static String LITECOIN_ADDRESS; 36 | private static String LITECOIN_FULL; 37 | 38 | private static String ETHEREUM_ADDRESS; 39 | private static String ETHEREUM_FULL; 40 | 41 | private static String MONERO_ADDRESS; 42 | private static String MONERO_FULL; 43 | 44 | @Override 45 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 46 | View view = inflater.inflate(R.layout.fragment_donate, container, false); 47 | 48 | setHasOptionsMenu(true); 49 | 50 | context = getContext(); 51 | 52 | final TextView tvAddressBtc = view.findViewById(R.id.donate_bitcoin_address); 53 | final TextView tvAddressLtc = view.findViewById(R.id.donate_litecoin_address); 54 | final TextView tvAddressEth = view.findViewById(R.id.donate_ethereum_address); 55 | final TextView tvAddressXmr = view.findViewById(R.id.donate_monero_address); 56 | 57 | BITCOIN_ADDRESS = getString(R.string.donate_bitcoin_address); 58 | BITCOIN_FULL = BITCOIN_PREFIX + BITCOIN_ADDRESS; 59 | tvAddressBtc.setText(BITCOIN_ADDRESS); 60 | 61 | LITECOIN_ADDRESS = getString(R.string.donate_litecoin_address); 62 | LITECOIN_FULL = LITECOIN_PREFIX + LITECOIN_ADDRESS; 63 | tvAddressLtc.setText(LITECOIN_ADDRESS); 64 | 65 | ETHEREUM_ADDRESS = getString(R.string.donate_ethereum_address); 66 | ETHEREUM_FULL = ETHEREUM_PREFIX + ETHEREUM_ADDRESS; 67 | tvAddressEth.setText(ETHEREUM_ADDRESS); 68 | 69 | MONERO_ADDRESS = getString(R.string.donate_monero_address); 70 | MONERO_FULL = MONERO_PREFIX + MONERO_ADDRESS; 71 | tvAddressXmr.setText(MONERO_ADDRESS); 72 | 73 | tvAddressBtc.setOnClickListener(new View.OnClickListener() { 74 | @Override 75 | public void onClick(View view) { 76 | //attempt to open bitcoin app, else copy to clipboard 77 | try { 78 | openURI(BITCOIN_FULL); 79 | } catch(Exception ignored) { 80 | copyToClipboard(BITCOIN_ADDRESS); 81 | } 82 | } 83 | }); 84 | tvAddressBtc.setOnLongClickListener(new View.OnLongClickListener() { 85 | @Override 86 | public boolean onLongClick(View v) { 87 | copyToClipboard(BITCOIN_ADDRESS); 88 | return true; 89 | } 90 | }); 91 | 92 | //litecoin 93 | tvAddressLtc.setOnClickListener(new View.OnClickListener() { 94 | @Override 95 | public void onClick(View view) { 96 | //attempt to open litecoin app, else copy to clipboard 97 | try { 98 | openURI(LITECOIN_FULL); 99 | } catch(Exception ignored) { 100 | copyToClipboard(LITECOIN_ADDRESS); 101 | } 102 | } 103 | }); 104 | tvAddressLtc.setOnLongClickListener(new View.OnLongClickListener() { 105 | @Override 106 | public boolean onLongClick(View v) { 107 | copyToClipboard(LITECOIN_ADDRESS); 108 | return true; 109 | } 110 | }); 111 | 112 | //ethereum 113 | tvAddressEth.setOnClickListener(new View.OnClickListener() { 114 | @Override 115 | public void onClick(View view) { 116 | //attempt to open ethereum app, else copy to clipboard 117 | try { 118 | openURI(ETHEREUM_FULL); 119 | } catch(Exception ignored) { 120 | copyToClipboard(ETHEREUM_ADDRESS); 121 | } 122 | } 123 | }); 124 | tvAddressEth.setOnLongClickListener(new View.OnLongClickListener() { 125 | @Override 126 | public boolean onLongClick(View v) { 127 | copyToClipboard(ETHEREUM_ADDRESS); 128 | return true; 129 | } 130 | }); 131 | 132 | //monero 133 | tvAddressXmr.setOnClickListener(new View.OnClickListener() { 134 | @Override 135 | public void onClick(View view) { 136 | //attempt to open ethereum app, else copy to clipboard 137 | try { 138 | openURI(MONERO_FULL); 139 | } catch(Exception ignored) { 140 | copyToClipboard(MONERO_ADDRESS); 141 | } 142 | } 143 | }); 144 | tvAddressXmr.setOnLongClickListener(new View.OnLongClickListener() { 145 | @Override 146 | public boolean onLongClick(View v) { 147 | copyToClipboard(MONERO_ADDRESS); 148 | return true; 149 | } 150 | }); 151 | 152 | return view; 153 | } 154 | 155 | private void copyToClipboard(String AUTHOR_EXTRA) { 156 | ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); 157 | ClipData clip = ClipData.newPlainText(getString(R.string.fragment_donate_clipboard_label), AUTHOR_EXTRA); 158 | clipboard.setPrimaryClip(clip); 159 | 160 | Toasty.success(context, R.string.fragment_donate_clipboard_message, Toast.LENGTH_SHORT, true).show(); 161 | } 162 | 163 | private void openURI(String uri) { 164 | Intent openURI = new Intent(Intent.ACTION_VIEW); 165 | openURI.setData(Uri.parse(uri)); 166 | startActivity(openURI); 167 | } 168 | 169 | @Override 170 | public void onPrepareOptionsMenu(Menu menu) { 171 | //hide action bar menu 172 | menu.setGroupVisible(R.id.menu_top, false); 173 | menu.setGroupVisible(R.id.menu_bottom, false); 174 | 175 | super.onPrepareOptionsMenu(menu); 176 | } 177 | 178 | @Override 179 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 180 | super.onViewCreated(view, savedInstanceState); 181 | //set title 182 | getActivity().setTitle(getString(R.string.fragment_donate_title)); 183 | } 184 | } -------------------------------------------------------------------------------- /app/src/main/java/com/wesaphzt/privatelocation/fragments/FragmentSettings.java: -------------------------------------------------------------------------------- 1 | package com.wesaphzt.privatelocation.fragments; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.os.Bundle; 6 | import androidx.annotation.Nullable; 7 | 8 | import android.view.Menu; 9 | import android.view.View; 10 | import android.widget.Toast; 11 | 12 | import androidx.preference.CheckBoxPreference; 13 | import androidx.preference.PreferenceFragmentCompat; 14 | import androidx.preference.PreferenceManager; 15 | 16 | import com.wesaphzt.privatelocation.R; 17 | 18 | public class FragmentSettings extends PreferenceFragmentCompat { 19 | 20 | private Context context; 21 | 22 | private CheckBoxPreference cbRandomize; 23 | 24 | private SharedPreferences sharedPreferences; 25 | private SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener; 26 | 27 | @Override 28 | public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 29 | addPreferencesFromResource(R.xml.preferences); 30 | 31 | setHasOptionsMenu(true); 32 | context = getContext(); 33 | 34 | sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); 35 | 36 | //this static call will reset default values only on the first ever read 37 | PreferenceManager.setDefaultValues(context, R.xml.preferences, false); 38 | 39 | cbRandomize = findPreference("RANDOMIZE_LOCATION"); 40 | } 41 | 42 | @Override 43 | public void onCreate(Bundle savedInstanceState) { 44 | super.onCreate(savedInstanceState); 45 | } 46 | 47 | @Override 48 | public void onResume() { 49 | super.onResume(); 50 | sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener); 51 | } 52 | 53 | @Override 54 | public void onDestroy() { 55 | super.onDestroy(); 56 | sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener); 57 | } 58 | 59 | @Override 60 | public void onPause() { 61 | super.onPause(); 62 | sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener); 63 | } 64 | 65 | @Override 66 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 67 | super.onViewCreated(view, savedInstanceState); 68 | //set title 69 | getActivity().setTitle("Settings"); 70 | 71 | //background color 72 | view.setBackgroundColor(getResources().getColor(R.color.white)); 73 | 74 | sharedPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() { 75 | @Override 76 | public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { 77 | if(key.equals("RANDOMIZE_LOCATION") && cbRandomize.isChecked()) { 78 | Toast.makeText(context, getString(R.string.settings_randomize_toast), Toast.LENGTH_LONG).show(); 79 | } 80 | } 81 | }; 82 | sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener); 83 | } 84 | 85 | @Override 86 | public void onPrepareOptionsMenu(Menu menu) { 87 | //hide action bar menu 88 | menu.setGroupVisible(R.id.menu_top, false); 89 | menu.setGroupVisible(R.id.menu_bottom, false); 90 | 91 | super.onPrepareOptionsMenu(menu); 92 | } 93 | } -------------------------------------------------------------------------------- /app/src/main/java/com/wesaphzt/privatelocation/interfaces/ILatLong.java: -------------------------------------------------------------------------------- 1 | package com.wesaphzt.privatelocation.interfaces; 2 | 3 | public interface ILatLong { 4 | void setLocation(Double lat, Double lng); 5 | } -------------------------------------------------------------------------------- /app/src/main/java/com/wesaphzt/privatelocation/interfaces/JSInterface.java: -------------------------------------------------------------------------------- 1 | package com.wesaphzt.privatelocation.interfaces; 2 | 3 | import android.content.Context; 4 | import android.webkit.JavascriptInterface; 5 | 6 | import com.wesaphzt.privatelocation.MainActivity; 7 | 8 | public class JSInterface { 9 | 10 | private Context context; 11 | 12 | private MainActivity mainActivity; 13 | public JSInterface(Context context, MainActivity mActivity) { 14 | mainActivity = mActivity; 15 | } 16 | 17 | public Context getContext() { 18 | return context; 19 | } 20 | 21 | //store user location in shared prefs on leaflet move end, unreliable elsewhere 22 | @JavascriptInterface 23 | public void setLatLongZoom(final String latlng, int zoom) { 24 | //separate lat & long 25 | String lat = latlng.substring(latlng.indexOf('(') + 1, latlng.indexOf(',')); 26 | String lng = latlng.substring(latlng.indexOf(',') + 2, latlng.indexOf(')')); 27 | 28 | mainActivity.setSharedPreferencesLatLng(lat, lng, zoom); 29 | } 30 | 31 | @JavascriptInterface 32 | public void setMockLocation(final String location) { 33 | //separate lat & long 34 | String lat = location.substring(location.indexOf('(') + 1, location.indexOf(',')); 35 | String lng = location.substring(location.indexOf(',') + 2, location.indexOf(')')); 36 | 37 | //call method to add to shared prefs 38 | mainActivity.setMockLatLong(lat, lng); 39 | } 40 | 41 | @JavascriptInterface 42 | public void addFavoriteLocation(final String favorite) { 43 | //separate lat & long 44 | String lat = favorite.substring(favorite.indexOf('(') + 1, favorite.indexOf(',')); 45 | String lng = favorite.substring(favorite.indexOf(',') + 2, favorite.indexOf(')')); 46 | 47 | mainActivity.addFavoriteLocation(lat, lng); 48 | } 49 | 50 | @JavascriptInterface 51 | public void onLocationError() { 52 | mainActivity.getLocationGps(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/com/wesaphzt/privatelocation/receivers/ActionReceiver.java: -------------------------------------------------------------------------------- 1 | package com.wesaphzt.privatelocation.receivers; 2 | 3 | import android.app.NotificationManager; 4 | import android.content.BroadcastReceiver; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.location.LocationManager; 8 | import android.os.Build; 9 | 10 | import com.wesaphzt.privatelocation.R; 11 | import com.wesaphzt.privatelocation.service.LocationProvider; 12 | import com.wesaphzt.privatelocation.service.LocationService; 13 | import com.wesaphzt.privatelocation.widget.LocationWidgetProvider; 14 | 15 | import static com.wesaphzt.privatelocation.service.LocationService.isRunning; 16 | import static com.wesaphzt.privatelocation.service.LocationService.mCountDown; 17 | 18 | public class ActionReceiver extends BroadcastReceiver { 19 | 20 | private static final int NOTIFICATION = 100; 21 | 22 | @Override 23 | public void onReceive(Context context, Intent intent) { 24 | String action = intent.getStringExtra("location_service"); 25 | 26 | if(action.equals("service_notification")){ 27 | Intent stopIntent = new Intent(context, LocationService.class); 28 | stopIntent.setAction(LocationService.ACTION_STOP_FOREGROUND_SERVICE); 29 | 30 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 31 | context.startForegroundService(stopIntent); 32 | } else { 33 | context.startService(stopIntent); 34 | } 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /app/src/main/java/com/wesaphzt/privatelocation/receivers/BootReceiver.java: -------------------------------------------------------------------------------- 1 | package com.wesaphzt.privatelocation.receivers; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.content.SharedPreferences; 7 | import android.os.Build; 8 | import android.preference.PreferenceManager; 9 | 10 | import com.wesaphzt.privatelocation.service.LocationService; 11 | 12 | import static com.wesaphzt.privatelocation.MainActivity.USER_LAT_NAME; 13 | import static com.wesaphzt.privatelocation.MainActivity.USER_LNG_NAME; 14 | 15 | public class BootReceiver extends BroadcastReceiver { 16 | 17 | @Override 18 | public void onReceive(Context context, Intent intent) { 19 | 20 | if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) { 21 | //shared prefs 22 | SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); 23 | 24 | if(sharedPreferences.getBoolean("START_ON_BOOT", false)) { 25 | Intent startIntent = new Intent(context, LocationService.class); 26 | startIntent.setAction(LocationService.ACTION_START_FOREGROUND_SERVICE); 27 | 28 | //init 29 | double lat = 0; 30 | double lng = 0; 31 | 32 | try { 33 | lat = Double.parseDouble(sharedPreferences.getString(USER_LAT_NAME, "null")); 34 | lng = Double.parseDouble(sharedPreferences.getString(USER_LNG_NAME, "null")); 35 | } catch (Exception e) { 36 | e.printStackTrace(); 37 | } 38 | 39 | //add location data to the intent 40 | startIntent.putExtra("lat", lat); 41 | startIntent.putExtra("lng", lng); 42 | 43 | //check android api 44 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 45 | context.startForegroundService(startIntent); 46 | } else { 47 | context.startService(startIntent); 48 | } 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /app/src/main/java/com/wesaphzt/privatelocation/service/LocationProvider.java: -------------------------------------------------------------------------------- 1 | package com.wesaphzt.privatelocation.service; 2 | 3 | import android.content.Context; 4 | import android.location.Criteria; 5 | import android.location.Location; 6 | import android.location.LocationManager; 7 | import android.os.Build; 8 | import android.os.SystemClock; 9 | 10 | public class LocationProvider { 11 | 12 | private String providerName; 13 | public Context context; 14 | 15 | public LocationProvider(String name, Context context) { 16 | this.providerName = name; 17 | this.context = context; 18 | 19 | LocationManager locationManager = (LocationManager) context.getSystemService( 20 | Context.LOCATION_SERVICE); 21 | try 22 | { 23 | locationManager.addTestProvider(providerName, false, false, false, false, false, 24 | true, true, Criteria.POWER_LOW, Criteria.ACCURACY_FINE); 25 | locationManager.setTestProviderEnabled(providerName, true); 26 | } catch (SecurityException e) { 27 | throw new SecurityException("Error applying mock location"); 28 | } 29 | } 30 | 31 | void pushLocation(double lat, double lon) { 32 | LocationManager locationManager = (LocationManager) context.getSystemService( 33 | Context.LOCATION_SERVICE); 34 | 35 | Location mockLocation = new Location(providerName); 36 | mockLocation.setLatitude(lat); 37 | mockLocation.setLongitude(lon); 38 | mockLocation.setAltitude(3F); 39 | mockLocation.setTime(System.currentTimeMillis()); 40 | mockLocation.setSpeed(0.01F); 41 | mockLocation.setBearing(1F); 42 | mockLocation.setAccuracy(3F); 43 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 44 | mockLocation.setBearingAccuracyDegrees(0.1F); 45 | } 46 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 47 | mockLocation.setVerticalAccuracyMeters(0.1F); 48 | } 49 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 50 | mockLocation.setSpeedAccuracyMetersPerSecond(0.01F); 51 | } 52 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { 53 | mockLocation.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos()); 54 | } 55 | locationManager.setTestProviderLocation(providerName, mockLocation); 56 | } 57 | 58 | public void shutdown() { 59 | LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); 60 | locationManager.removeTestProvider(providerName); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /app/src/main/java/com/wesaphzt/privatelocation/service/LocationService.java: -------------------------------------------------------------------------------- 1 | package com.wesaphzt.privatelocation.service; 2 | 3 | import android.app.Notification; 4 | import android.app.NotificationChannel; 5 | import android.app.NotificationManager; 6 | import android.app.PendingIntent; 7 | import android.app.Service; 8 | import android.content.Context; 9 | import android.content.Intent; 10 | import android.content.SharedPreferences; 11 | import android.location.LocationManager; 12 | import android.os.Build; 13 | import android.os.CountDownTimer; 14 | import android.os.IBinder; 15 | import android.widget.Toast; 16 | 17 | import androidx.preference.PreferenceManager; 18 | import androidx.core.app.NotificationCompat; 19 | 20 | import com.wesaphzt.privatelocation.MainActivity; 21 | import com.wesaphzt.privatelocation.R; 22 | import com.wesaphzt.privatelocation.receivers.ActionReceiver; 23 | import com.wesaphzt.privatelocation.widget.LocationWidgetProvider; 24 | 25 | import java.util.Random; 26 | 27 | import static androidx.core.app.NotificationCompat.PRIORITY_LOW; 28 | import static androidx.core.app.NotificationCompat.PRIORITY_MIN; 29 | 30 | public class LocationService extends Service { 31 | 32 | LocationProvider mockNetwork; 33 | LocationProvider mockGps; 34 | 35 | //notifications 36 | public static PendingIntent pendingIntent; 37 | public static PendingIntent pendingCloseIntent; 38 | 39 | public static final int NOTIFICATION_ID = 100; 40 | 41 | Notification notification; 42 | NotificationManager notificationManager; 43 | 44 | public static final String CHANNEL_ID = "location_notification_channel_id"; 45 | public static final String CHANNEL_NAME = "Location Notification Service"; 46 | 47 | Context context; 48 | SharedPreferences sharedPreferences; 49 | 50 | public static boolean disabled = true; 51 | 52 | //randomize 53 | public static CountDownTimer mCountDown; 54 | public static boolean isRunning = false; 55 | private static int RANDOMIZE_LOCATION_INTERVAL; 56 | 57 | public static final String ACTION_STOP_FOREGROUND_SERVICE = "ACTION_STOP_FOREGROUND_SERVICE"; 58 | public static final String ACTION_START_FOREGROUND_SERVICE = "ACTION_START_FOREGROUND_SERVICE"; 59 | 60 | @Override 61 | public int onStartCommand(Intent intent, int flags, int startId) { 62 | context = getApplicationContext(); 63 | sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); 64 | notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); 65 | 66 | if (intent != null) { 67 | String action = intent.getAction(); 68 | 69 | LocationWidgetProvider locationWidgetProvider = new LocationWidgetProvider(); 70 | 71 | switch (action) { 72 | case ACTION_START_FOREGROUND_SERVICE: 73 | setNotification(); 74 | 75 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 76 | //create foreground service 77 | startForeground(NOTIFICATION_ID, notification); 78 | pushLocation(intent); 79 | disabled = false; 80 | locationWidgetProvider.setWidgetStart(context); 81 | } else { 82 | notificationManager.notify(NOTIFICATION_ID, notification); 83 | pushLocation(intent); 84 | disabled = false; 85 | locationWidgetProvider.setWidgetStart(context); 86 | } 87 | 88 | break; 89 | 90 | case ACTION_STOP_FOREGROUND_SERVICE: 91 | shutdown(); 92 | stopService(intent); 93 | disabled = true; 94 | locationWidgetProvider.setWidgetStop(context); 95 | 96 | break; 97 | } 98 | } else { 99 | return LocationService.START_REDELIVER_INTENT; 100 | } 101 | 102 | return LocationService.START_REDELIVER_INTENT; 103 | } 104 | 105 | @Override 106 | public void onDestroy() { 107 | super.onDestroy(); 108 | } 109 | 110 | @Override 111 | public IBinder onBind(Intent intent) { 112 | return null; 113 | } 114 | 115 | private void setNotification() { 116 | //open main activity when clicked 117 | pendingIntent = PendingIntent.getActivity(context, 0, 118 | new Intent(context, MainActivity.class) 119 | .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP), 120 | 0); 121 | 122 | //action when notification button clicked 123 | Intent intentAction = new Intent(context, ActionReceiver.class); 124 | intentAction.putExtra("location_service","service_notification"); 125 | pendingCloseIntent = PendingIntent.getBroadcast(context,0, intentAction, PendingIntent.FLAG_UPDATE_CURRENT); 126 | 127 | if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) { 128 | NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this); 129 | notification = notificationBuilder 130 | .setSmallIcon(R.drawable.ic_notification_pin_drop_white_24dp) 131 | .setContentTitle(getString(R.string.app_name) + " is running") 132 | .setCategory(NotificationCompat.CATEGORY_SERVICE) 133 | .setContentIntent(pendingIntent) 134 | .setWhen(System.currentTimeMillis()) 135 | .setTicker(getString(R.string.app_name) + " is running") 136 | .addAction(android.R.drawable.ic_menu_close_clear_cancel, "STOP", pendingCloseIntent) 137 | .setOngoing(true) 138 | .setPriority(PRIORITY_LOW) 139 | .build(); 140 | 141 | } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.N | Build.VERSION.SDK_INT == Build.VERSION_CODES.N_MR1) { 142 | NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, CHANNEL_ID); 143 | notification = notificationBuilder 144 | .setSmallIcon(R.drawable.ic_notification_pin_drop_white_24dp) 145 | .setContentTitle(getString(R.string.app_name) + " is running") 146 | .setCategory(NotificationCompat.CATEGORY_SERVICE) 147 | .setColor(getColor(R.color.colorPrimary)) 148 | .setContentIntent(pendingIntent) 149 | .setWhen(System.currentTimeMillis()) 150 | .setTicker(getString(R.string.app_name) + " is running") 151 | .addAction(android.R.drawable.ic_menu_close_clear_cancel, "STOP", pendingCloseIntent) 152 | .setOngoing(true) 153 | .setPriority(PRIORITY_LOW) 154 | .build(); 155 | } 156 | 157 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 158 | NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW); 159 | channel.setImportance(NotificationManager.IMPORTANCE_LOW); 160 | channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET); 161 | notificationManager.createNotificationChannel(channel); 162 | 163 | NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, CHANNEL_ID); 164 | notification = notificationBuilder 165 | .setSmallIcon(R.drawable.ic_notification_pin_drop_white_24dp) 166 | .setContentTitle(getString(R.string.app_name) + " is running") 167 | .setPriority(PRIORITY_MIN) 168 | .setVisibility(NotificationCompat.VISIBILITY_SECRET) 169 | .setCategory(NotificationCompat.CATEGORY_SERVICE) 170 | .setColor(getColor(R.color.colorPrimary)) 171 | .setContentIntent(pendingIntent) 172 | .setWhen(System.currentTimeMillis()) 173 | .setTicker(getString(R.string.app_name) + " is running") 174 | .addAction(android.R.drawable.ic_menu_close_clear_cancel, "STOP", pendingCloseIntent) 175 | .setOngoing(true) 176 | .build(); 177 | } 178 | } 179 | 180 | private void pushLocation(Intent intent) { 181 | try { 182 | if (intent.hasExtra("lat") && intent.hasExtra("lng") ) { 183 | double lat = intent.getDoubleExtra("lat", 45); 184 | double lng = intent.getDoubleExtra("lng", 45); 185 | 186 | mockNetwork = new LocationProvider(LocationManager.NETWORK_PROVIDER, context); 187 | mockGps = new LocationProvider(LocationManager.GPS_PROVIDER, context); 188 | 189 | mockNetwork.pushLocation(lat, lng); 190 | mockGps.pushLocation(lat, lng); 191 | 192 | if(sharedPreferences.getBoolean("RANDOMIZE_LOCATION", false)) { 193 | randomize(); 194 | } 195 | } 196 | } catch (Exception e) { 197 | e.printStackTrace(); 198 | } 199 | } 200 | 201 | public void shutdown() { 202 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 203 | NotificationManager notificationManager = 204 | (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 205 | String id = "location_notification_channel_id"; 206 | notificationManager.deleteNotificationChannel(id); 207 | } else { 208 | NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE); 209 | notificationManager.cancel(NOTIFICATION_ID); 210 | } 211 | 212 | try { 213 | disabled = true; 214 | isRunning = false; 215 | 216 | if (mockNetwork != null) 217 | mockNetwork.shutdown(); 218 | if (mockGps != null) 219 | mockGps.shutdown(); 220 | if (isRunning) 221 | mCountDown.cancel(); 222 | } catch (Exception e) { 223 | e.printStackTrace(); 224 | } 225 | } 226 | 227 | //--------------------------------------------------------------------- 228 | private void randomize() { 229 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); 230 | try { 231 | RANDOMIZE_LOCATION_INTERVAL = Integer.parseInt(prefs.getString("RANDOMIZE_LOCATION_INTERVAL", "60")); 232 | 233 | float randomLat = randomLat(); 234 | float randomLng = randomLng(); 235 | 236 | mockNetwork = new LocationProvider(LocationManager.NETWORK_PROVIDER, context); 237 | mockGps = new LocationProvider(LocationManager.GPS_PROVIDER, context); 238 | 239 | mockNetwork.pushLocation(randomLat, randomLng); 240 | mockGps.pushLocation(randomLat, randomLng); 241 | 242 | randomizeTimer(); 243 | } catch (Exception e) { 244 | e.printStackTrace(); 245 | } 246 | } 247 | 248 | private void randomizeTimer() { 249 | mCountDown = new CountDownTimer(RANDOMIZE_LOCATION_INTERVAL * 1000 * 60, 1000) { 250 | public void onTick(long millisUntilFinished) { 251 | isRunning = true; 252 | } 253 | public void onFinish() { 254 | isRunning = false; 255 | randomize(); 256 | } 257 | }.start(); 258 | } 259 | 260 | private float randomLat() { 261 | Random r = new Random(); 262 | return r.nextFloat() * (180) - 90; 263 | } 264 | 265 | private float randomLng() { 266 | Random r = new Random(); 267 | return r.nextFloat() * (360) - 180; 268 | } 269 | } -------------------------------------------------------------------------------- /app/src/main/java/com/wesaphzt/privatelocation/widget/LocationWidgetProvider.java: -------------------------------------------------------------------------------- 1 | package com.wesaphzt.privatelocation.widget; 2 | 3 | import android.app.PendingIntent; 4 | import android.appwidget.AppWidgetManager; 5 | import android.appwidget.AppWidgetProvider; 6 | import android.content.ComponentName; 7 | import android.content.Context; 8 | import android.content.Intent; 9 | import android.content.SharedPreferences; 10 | import android.os.Build; 11 | import android.preference.PreferenceManager; 12 | import android.widget.RemoteViews; 13 | 14 | import com.wesaphzt.privatelocation.R; 15 | import com.wesaphzt.privatelocation.service.LocationService; 16 | 17 | import static com.wesaphzt.privatelocation.MainActivity.DEFAULT_LAT; 18 | import static com.wesaphzt.privatelocation.MainActivity.DEFAULT_LNG; 19 | import static com.wesaphzt.privatelocation.MainActivity.USER_LAT_NAME; 20 | import static com.wesaphzt.privatelocation.MainActivity.USER_LNG_NAME; 21 | 22 | public class LocationWidgetProvider extends AppWidgetProvider { 23 | 24 | private static final String ACTION_WIDGET_RECEIVER = "ActionReceiverWidget"; 25 | public boolean SERVICE_STATUS; 26 | 27 | public void onUpdate(Context context, AppWidgetManager appWidgetManager, 28 | int[] appWidgetIds) { 29 | 30 | for (int appWidgetId : appWidgetIds) { 31 | RemoteViews remoteViews = new RemoteViews(context.getPackageName(), 32 | R.layout.app_widget); 33 | 34 | //default status 35 | remoteViews.setTextViewText(R.id.tvWidgetToggle, context.getResources().getString(R.string.widget_start_text)); 36 | 37 | Intent intent = new Intent(context, LocationWidgetProvider.class); 38 | intent.setAction(ACTION_WIDGET_RECEIVER); 39 | PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0); 40 | 41 | remoteViews.setOnClickPendingIntent(R.id.llWidget, pendingIntent); 42 | 43 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); 44 | boolean value = prefs.getBoolean(context.getString(R.string.widget_prefs_service_id), false); 45 | 46 | if (value) { 47 | SharedPreferences.Editor editor = prefs.edit(); 48 | editor.putBoolean(context.getString(R.string.widget_prefs_service_id), false); 49 | editor.apply(); 50 | } 51 | 52 | appWidgetManager.updateAppWidget(appWidgetId, remoteViews); 53 | } 54 | } 55 | 56 | @Override 57 | public void onReceive(Context context, Intent intent) { 58 | if (intent.getAction().equals(ACTION_WIDGET_RECEIVER)) { 59 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); 60 | boolean value = prefs.getBoolean(context.getString(R.string.widget_prefs_service_id), false); 61 | SharedPreferences.Editor editor = prefs.edit(); 62 | 63 | SERVICE_STATUS = value; 64 | 65 | //if service is running 66 | if (SERVICE_STATUS) { 67 | editor.putBoolean(context.getString(R.string.widget_prefs_service_id), false); 68 | editor.apply(); 69 | 70 | Intent stopIntent = new Intent(context, LocationService.class); 71 | stopIntent.setAction(LocationService.ACTION_STOP_FOREGROUND_SERVICE); 72 | 73 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 74 | context.startForegroundService(stopIntent); 75 | 76 | } else { 77 | context.startService(stopIntent); 78 | } 79 | 80 | //if service is not running 81 | } else { 82 | editor.putBoolean(context.getString(R.string.widget_prefs_service_id), true); 83 | editor.commit(); 84 | 85 | double mLat; 86 | double mLng; 87 | 88 | //grab last lat/lng or use defaults if app hasn't run yet 89 | try { 90 | mLat = Double.parseDouble(prefs.getString(USER_LAT_NAME, "null")); 91 | mLng = Double.parseDouble(prefs.getString(USER_LNG_NAME, "null")); 92 | } catch (Exception e) { 93 | e.printStackTrace(); 94 | 95 | mLat = DEFAULT_LAT; 96 | mLng = DEFAULT_LNG; 97 | } 98 | 99 | Intent startIntent = new Intent(context, LocationService.class); 100 | startIntent.setAction(LocationService.ACTION_START_FOREGROUND_SERVICE); 101 | startIntent.putExtra("lat", mLat); 102 | startIntent.putExtra("lng", mLng); 103 | 104 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 105 | context.startForegroundService(startIntent); 106 | } else { 107 | context.startService(startIntent); 108 | } 109 | } 110 | } 111 | super.onReceive(context, intent); 112 | } 113 | 114 | @Override 115 | public void onDeleted(Context context, int[] appWidgetIds) { 116 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); 117 | boolean value = prefs.getBoolean(context.getString(R.string.widget_prefs_service_id), false); 118 | 119 | if (value) { 120 | SharedPreferences.Editor editor = prefs.edit(); 121 | editor.putBoolean(context.getString(R.string.widget_prefs_service_id), false); 122 | editor.apply(); 123 | } 124 | 125 | super.onDeleted(context, appWidgetIds); 126 | } 127 | 128 | //update widget methods 129 | public void setWidgetStart(Context context) { 130 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); 131 | prefs.edit().putBoolean(context.getString(R.string.widget_prefs_service_id), true).apply(); 132 | AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); 133 | ComponentName thisWidget = new ComponentName(context, LocationWidgetProvider.class); 134 | RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.app_widget); 135 | 136 | remoteViews.setTextViewText(R.id.tvWidgetToggle, context.getResources().getString(R.string.widget_stop_text)); 137 | remoteViews.setImageViewResource(R.id.ivWidgetLocation, R.drawable.ic_widget_location_on_white_24dp); 138 | remoteViews.setInt(R.id.llWidget, "setBackgroundResource", R.color.colorWidgetStart); 139 | 140 | appWidgetManager.updateAppWidget(thisWidget, remoteViews); 141 | } 142 | 143 | public void setWidgetStop(Context context) { 144 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); 145 | prefs.edit().putBoolean(context.getString(R.string.widget_prefs_service_id), false).apply(); 146 | 147 | AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); 148 | ComponentName thisWidget = new ComponentName(context, LocationWidgetProvider.class); 149 | RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.app_widget); 150 | 151 | remoteViews.setTextViewText(R.id.tvWidgetToggle, context.getResources().getString(R.string.widget_start_text)); 152 | remoteViews.setImageViewResource(R.id.ivWidgetLocation, R.drawable.ic_widget_location_off_white_24dp); 153 | remoteViews.setInt(R.id.llWidget, "setBackgroundResource", R.color.colorWidgetStop); 154 | 155 | appWidgetManager.updateAppWidget(thisWidget, remoteViews); 156 | } 157 | } -------------------------------------------------------------------------------- /app/src/main/res/anim/enter_from_left.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/anim/enter_from_right.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/anim/exit_to_left.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/anim/exit_to_right.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/border_dark.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/border_light.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_about_bug_report_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_about_code_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_about_copyright_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_about_info_outline_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_about_person_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_action_favorite_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_action_pin_drop_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_action_settings_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_donate_bitcoin_logo.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_donate_ethereum_logo.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 11 | 15 | 19 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_donate_litecoin_logo.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_donate_monero_logo.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_donate_note_text_emoji.xml: -------------------------------------------------------------------------------- 1 | 3 | 5 | 7 | 9 | 11 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_empty_view.xml: -------------------------------------------------------------------------------- 1 | 3 | 5 | 7 | 9 | 11 | 13 | 15 | 17 | 19 | 21 | 23 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_favorites_delete_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_favorites_edit_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_intro_cloud.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 8 | 11 | 14 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_intro_location.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 6 | 8 | 11 | 15 | 20 | 23 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_notification_pin_drop_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_widget_location_off_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_widget_location_on_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/layout/app_bar_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/layout/app_widget.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 17 | 18 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/layout/content_main.xml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 16 | 17 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_favorites.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | 17 | 18 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_favorites_add.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 20 | 21 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_favorites_list_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 16 | 17 | 25 | 26 | 34 | 35 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_go_to.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 22 | 23 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_donate.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 17 | 18 | 19 | 24 | 25 | 33 | 34 | 43 | 44 | 45 | 46 | 47 | 48 | 53 | 54 | 59 | 60 | 68 | 69 | 79 | 80 | 85 | 86 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 104 | 105 | 110 | 111 | 119 | 120 | 130 | 131 | 136 | 137 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 155 | 156 | 161 | 162 | 170 | 171 | 181 | 182 | 187 | 188 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 206 | 207 | 212 | 213 | 221 | 222 | 232 | 233 | 238 | 239 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 14 | 19 | 20 | 21 | 23 | 28 | 32 | 36 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesaphzt/privatelocation/d37b12f59d3f8de2b8506f44b09d2436b719fe50/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesaphzt/privatelocation/d37b12f59d3f8de2b8506f44b09d2436b719fe50/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesaphzt/privatelocation/d37b12f59d3f8de2b8506f44b09d2436b719fe50/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesaphzt/privatelocation/d37b12f59d3f8de2b8506f44b09d2436b719fe50/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesaphzt/privatelocation/d37b12f59d3f8de2b8506f44b09d2436b719fe50/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesaphzt/privatelocation/d37b12f59d3f8de2b8506f44b09d2436b719fe50/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesaphzt/privatelocation/d37b12f59d3f8de2b8506f44b09d2436b719fe50/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesaphzt/privatelocation/d37b12f59d3f8de2b8506f44b09d2436b719fe50/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesaphzt/privatelocation/d37b12f59d3f8de2b8506f44b09d2436b719fe50/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wesaphzt/privatelocation/d37b12f59d3f8de2b8506f44b09d2436b719fe50/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values-es/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Private Location 3 | Open-Source Location Privacy 4 | 5 | https://github.com/wesaphzt 6 | https://github.com/wesaphzt/PrivateLocation 7 | 8 | 9 | Favorito 10 | Ir a 11 | Ajustes 12 | Donar 13 | Mostrar introducción 14 | Acerca de 15 | 16 | 17 | Dona 18 | Dirección copiada 19 | Dirección copiada al portapapeles 20 | Acerca de 21 | 22 | 23 | Lugares favoritos 24 | Nada aquí… 25 | Nombre 26 | Latitud: %1$f \nLongitud: %2$f 27 | Nombre 28 | Editar 29 | Borrar 30 | 31 | Coordinadas del lugar 32 | Latitud 33 | Longitud 34 | No es un rango válido 35 | No es un número válido 36 | No puede estar vacía 37 | 38 | 39 | Ajustes de localización 40 | Si experimenta problemas con la exposición de su ubicación real, especialmente con las aplicaciones y servicios basados en Google Maps, 41 | se recomienda deshabilitar cualquier modificación en los ajustes de ubicación que utilizan puntos de Wi-Fi y más para ayudar a determinar su ubicación de forma más rápida y precisa. 42 | 43 | Ubicación simulada deshabilitada 44 | Seleccione esta aplicación en los ajustes de ubicación simulada. 45 | Active las ubicaciones simuladas en los ajustes. 46 | 47 | Ajustes de desarrollador desactivados 48 | Habilite las opciones de desarrollador haciendo clic en el número de compilación de su teléfono 7 (siete) veces. 49 | 50 | Ajustes de desarrollador 51 | Asegúrese de que los ajustes de desarrollador estén activados antes de intentar establecer la ubicación para evitar problemas en Nougat. 52 | \n\n Puede hacerlo haciendo clic en el número de compilación de su teléfono 7 (siete) veces. 53 | 54 | 55 | Sobre el logo general de la aplicación 56 | 57 | Contribuye 58 | Encuentre errores 59 | Encuentre errores o sugiere características 60 | 61 | Imagen del autor 62 | wesaphzt 63 | Desarrollador 64 | Desarrollador 65 | 66 | Versión 67 | Imagen de la versión 68 | 69 | Licencia 70 | Licencia GPL v3.0 71 | Imagen de la licencia 72 | 73 | Código fuente 74 | Imagen del código fuente 75 | 76 | Imagen de reporte de errores 77 | 78 | 79 | Si el servicio se está ejecutando actualmente, reinicie para aplicar la configuración. 80 | \n\nPuede interferir con la temperatura de la pantalla y la localización. 81 | 82 | 83 | Comienzo 84 | Detener 85 | 86 | SERVICE_STATUS 87 | Imagen de ubicación del widget 88 | 89 | 90 | @string/app_name 91 | El compañero perfecto para ayudar a proteger la privacidad y seguridad de la ubicación de su teléfono al suplantar su ubicación a cualquier parte del mundo. 92 | Limite sus datos 93 | Muchas aplicaciones pueden realizar solicitudes de ubicación repetidas e innecesarias en segundo plano, a menudo enviando estos datos a la nube. 94 | \n\nLos datos de ubicación son personales y potencialmente muy sensibles, esta aplicación puede ayudar a proteger su privacidad y datos. 95 | Verifica tu configuración 96 | Para navegar sin problemas, asegúrese de que la configuración de su ubicación no utilice WiFi, red u otros métodos para determinar su ubicación que pueda filtrar su ubicación real, 97 | y que la optimización de la batería está desactivada para esta aplicación. 98 | 99 | 100 | Imagen de Bitcoin 101 | Imagen de Litecoin 102 | Imagen de Ethereum 103 | Imagen de Monero 104 | 105 | 1GCkvAg9oG79niQTbh6EH9rPALQDXKyHKK 106 | LV687s3wVdhmLZyJMFxomJHdHFXeFAKT5R 107 | 0x785a8804c85b88683a5cce5e53f60878831e5d03 108 | 43Vijzdt3y42mmT954rSYPjXYabDsjYEV2KyhxfC46JibR2ny9VmRS1fjdJTHxxPVPFE8ajgArwjWfyaRgjh9vcNAwmkfJj 109 | 110 | Si estas aplicaciones resultan útiles, considera ayudarme de alguna manera en mi misión para crear aplicaciones simples, útiles, orientadas a la privacidad y de código abierto. 111 | Text emoji image 112 | 113 | -------------------------------------------------------------------------------- /app/src/main/res/values-it/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Private Location 3 | Open-Source Location Privacy 4 | 5 | https://github.com/wesaphzt 6 | https://github.com/wesaphzt/PrivateLocation 7 | 8 | 9 | Preferiti 10 | Vai a 11 | Impostazioni 12 | Dona 13 | Mostra Introduzione 14 | Informazioni 15 | 16 | 17 | Dona 18 | Indirizzo copiato 19 | Indirizzo copiato negli appunti 20 | Informazioni 21 | 22 | 23 | Luoghi Preferiti 24 | Nulla… 25 | Nome 26 | Latitudine: %1$f \nLongitudine: %2$f 27 | Nome 28 | Modifica 29 | Elimina 30 | 31 | Coordinate del luogo 32 | Latitudine 33 | Longitudine 34 | Range non valido 35 | Non è un numero valido 36 | Non può essere vuoto 37 | 38 | 39 | Impostazioni di Geolocalizzazione 40 | Se si verificano problemi di esposizione della propria posizione reale, in particolare con le applicazioni e i servizi basati su Google Maps, 41 | si consiglia di disabilitare nelle impostazioni di geolocalizzazione tutte le funzioni che utilizzano bluetooth e reti Wi-Fi per aiutare a determinare la posizione in modo più rapido. 42 | 43 | Posizione Fittizia Disabilitata 44 | Selezionare questa applicazione nell\'impostazione dell\'app di posizione fittizia. 45 | Attivare le posizioni simulate nelle impostazioni. 46 | 47 | Modalità Sviluppatore Disattivata 48 | Abilita le opzioni sviluppatore cliccando 7 (sette) volte sul numero build del tuo telefono. 49 | 50 | Opzioni Sviluppatore 51 | Assicurati che le impostazioni sviluppatore siano abilitate prima di cercare di impostare la posizione per evitare problemi su Nougat. 52 | \n\nLo puoi verificare cliccando 7 (sette) volte sul numero build del tuo telefono. 53 | 54 | 55 | Informazioni sul logo dell\'applicazione 56 | 57 | Contribuisci 58 | Segnala un Bug 59 | Segnala bugs o richiedi nuove funzioni 60 | 61 | Immagine dell\'autore 62 | wesaphzt 63 | Developer 64 | Developer 65 | 66 | Versione 67 | Version image 68 | 69 | Licenza 70 | GPL v3.0 License 71 | License image 72 | 73 | Codice sorgente 74 | Source code image 75 | 76 | Bug report image 77 | 78 | 79 | Se il servizio è in esecuzione, riavviare per applicare l\'impostazione. 80 | \n\nPuò interferire con la temperatura dello schermo e la localizzazione. 81 | 82 | 83 | Avvia 84 | Stop 85 | 86 | SERVICE_STATUS 87 | Widget location image 88 | 89 | 90 | @string/app_name 91 | Il compagno perfetto per proteggere la privacy della posizione del tuo telefono spostando la tua posizione in qualsiasi parte del mondo. 92 | Limita il consumo di dati 93 | Molte applicazioni possono fare richieste di posizione ripetute e inutili in background, spesso inviando questi dati al cloud. 94 | \n\nI dati di localizzazione sono personali e potenzialmente molto sensibili, questa applicazione può aiutare a proteggere la tua privacy e i tuoi dati. 95 | Controlla le tue Impostazioni 96 | Per un funzionamento ottimale, assicurati che le impostazioni di Geolocalizzazione non utilizzino WiFi, la rete cellulare o altri metodi per determinare la tua posizione che possano far trapelare la tua posizione reale, 97 | e che l\'ottimizzazione della batteria sia disattivata per questa applicazione. 98 | 99 | 100 | Bitcoin image 101 | Litecoin image 102 | Ethereum image 103 | Monero image 104 | 105 | 1GCkvAg9oG79niQTbh6EH9rPALQDXKyHKK 106 | LV687s3wVdhmLZyJMFxomJHdHFXeFAKT5R 107 | 0x785a8804c85b88683a5cce5e53f60878831e5d03 108 | 43Vijzdt3y42mmT954rSYPjXYabDsjYEV2KyhxfC46JibR2ny9VmRS1fjdJTHxxPVPFE8ajgArwjWfyaRgjh9vcNAwmkfJj 109 | 110 | Se trovi utili queste applicazioni, considera la possibilità di supportarmi in qualche modo nella mia missione di creare applicazioni semplici, utili, orientate alla privacy e open-source. 111 | Text emoji image 112 | 113 | -------------------------------------------------------------------------------- /app/src/main/res/values-pt-rBR/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Private Location 3 | Open-Source Location Privacy 4 | 5 | https://github.com/wesaphzt 6 | https://github.com/wesaphzt/PrivateLocation 7 | 8 | 9 | Favoritos 10 | Ir para 11 | Configurações 12 | Doar 13 | Mostrar introdução 14 | Sobre 15 | 16 | 17 | Doar 18 | Endereço copiado 19 | Endereço copiado para a área de transferência 20 | Sobre 21 | 22 | 23 | Localizações Favoritas 24 | Nada aqui… 25 | Nome 26 | Latitude: %1$f \nLongitude: %2$f 27 | Nome 28 | Editar 29 | Apagar 30 | 31 | Coordenadas de localização 32 | Latitude 33 | Longitude 34 | Intervalo inválido 35 | Não é um número válido 36 | Não pode ser vazio. 37 | 38 | 39 | Configurações de localização 40 | Se voce tiver problemas com a sua localização real sendo exposta, em particular com os serviços e aplicativos baseados no Google Maps, é recomendável desabilitar qualquer configuração na opção de Localização que utilize pontos de Wi-Fi e outras opções que ajudam a determinar sua localização com mais rapidez e precisa. 41 | 42 | 43 | Localização falsa desabilitada 44 | Selecione esse aplicativo nas configurações de localização falsa. 45 | Habilitar a localização falsa nas configurações. 46 | 47 | Modo desenvolvedor desabilitado 48 | Habilitar opções de desenvolvedor ao clicar na opção de versão do seu aparelho 7 (sete) vezes. 49 | 50 | Configurações de desenvolvedor 51 | Certifique-se de que as configurações do desenvolvedor estejam ativadas antes de tentar definir o local para evitar problemas no Nougat. 52 | \n\nVocê pode fazer isso clicando na opção de versão do seu aparelho 7 (sete) vezes . 53 | 54 | 55 | Sobre o logótipo do aplicativo de visão geral 56 | 57 | Contribuir 58 | Reportar erros 59 | Reportar erros ou requisitar novas funções 60 | 61 | Imagem do autor 62 | wesaphzt 63 | Desenvolvedor 64 | Desenvolvedor 65 | 66 | Versão 67 | Imagem da versão 68 | 69 | Licença 70 | Licença GPL v3.0 71 | Imagem da licença 72 | 73 | Código fornte 74 | Imagem do código fonte 75 | 76 | Imagem do reportar erro 77 | 78 | 79 | Se o serviço estiver sendo executado, reinicie para aplicar a configuração. 80 | \n\nPode interferir na temperatura e localização da tela. 81 | 82 | 83 | Começar 84 | Pare 85 | 86 | SERVICE_STATUS 87 | Imagem de localização do widget 88 | 89 | 90 | @string/app_name 91 | O companheiro perfeito para ajudar a proteger a privacidade e a segurança da localização do telefone, falsificando sua localização para qualquer lugar do mundo. 92 | Limite seus dados 93 | Muitos aplicativos podem fazer solicitações de localização repetidas e desnecessárias em segundo plano, geralmente enviando esses dados de volta para a nuvem. 94 | \n\nOs dados de localização são pessoais e potencialmente muito sensíveis, este aplicativo pode ajudar a proteger sua privacidade e dados. 95 | Verifique suas configurações 96 | Para uma navegação tranquila, verifique se as configurações de localização não estão usando Wi-Fi, rede ou outros métodos para determinar sua localização que pode vazar sua localização real, 97 | e a otimização da bateria está desativada para este aplicativo. 98 | 99 | 100 | Logo Bitcoin 101 | Logo Litecoin 102 | Logo Ethereum 103 | Logo Monero 104 | 105 | 1GCkvAg9oG79niQTbh6EH9rPALQDXKyHKK 106 | LV687s3wVdhmLZyJMFxomJHdHFXeFAKT5R 107 | 0x785a8804c85b88683a5cce5e53f60878831e5d03 108 | 43Vijzdt3y42mmT954rSYPjXYabDsjYEV2KyhxfC46JibR2ny9VmRS1fjdJTHxxPVPFE8ajgArwjWfyaRgjh9vcNAwmkfJj 109 | 110 | Se você acha esses aplicativos úteis, considere apoiar-me de alguma forma em minha missão de criar aplicativos simples, úteis, orientados à privacidade e de código aberto. 111 | Imagem do texto do emoji 112 | 113 | -------------------------------------------------------------------------------- /app/src/main/res/values-ru/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Private Location 3 | Приватность местоположения с открытым исходным кодом 4 | 5 | https://github.com/wesaphzt 6 | https://github.com/wesaphzt/PrivateLocation 7 | 8 | 9 | Избранные 10 | Перейти к 11 | Настройки 12 | Поддержать 13 | Показать введение 14 | О приложении 15 | 16 | 17 | Поддержать 18 | Скопированный адрес 19 | Адрес скопирован в буфер обмена 20 | О приложении 21 | 22 | 23 | Избранные местоположения 24 | Ничего нет… 25 | Название 26 | Широта: %1$f \nLongitude: %2$f 27 | Название 28 | Изменить 29 | Удалить 30 | 31 | Координаты местоположения 32 | Широта 33 | Долгота 34 | Недопустимый диапазон 35 | Недопустимое значение 36 | Не может быть пустым 37 | 38 | 39 | Настройки местоположения 40 | Если у вас возникают проблемы с отображением вашего реального местоположения, особенно в приложениях и сервисах на основе Карт Google, рекомендуется отключить 41 | в настройках местоположения любые параметры, использующие точки Wi-Fi и другие, чтобы быстрее и точнее определить ваше местоположение. 42 | 43 | Фиктивное местоположение отключено 44 | Выберите это приложение в настройках фиктивного местоположения. 45 | Включите фиктивные местоположения в настройках. 46 | 47 | Режим разработчика отключен 48 | Включите параметры разработчика, кликнув на номер сборки своего телефона 7 (семь) раз. 49 | 50 | Настройки разработчика 51 | Пожалуйста, убедитесь, что настройки разработчика включены, прежде чем пытаться установить местоположение, чтобы предотвратить проблемы на Nougat. 52 | \n\nВы можете сделать это, кликнув на номере сборки вашего телефона 7 (семь) раз. 53 | 54 | 55 | Сведения о логотипе приложения 56 | 57 | Внести свой вклад 58 | Сообщить об ошибках 59 | Сообщить об ошибках или запросить новые возможности 60 | 61 | Изображение автора 62 | wesaphzt 63 | Разработчик 64 | Разработчик 65 | 66 | Версия 67 | Изображение версии 68 | 69 | Лицензия 70 | Лицензия GPL v3.0 71 | Изображение лицензии 72 | 73 | Исходный код 74 | Изображение исходного кода 75 | 76 | Изображение отчета об ошибке 77 | 78 | 79 | Если служба в данный момент запущена, перезапустите для применения настроек. 80 | \n\nМожет влиять на температуру экрана и локализацию. 81 | 82 | 83 | Старт 84 | Стоп 85 | 86 | SERVICE_STATUS 87 | Изображение виджета местоположения 88 | 89 | 90 | @string/app_name 91 | Идеальный помощник, помогающий защитить конфиденциальность и безопасность местоположения вашего телефона путем подмены местоположения в любом уголке мира. 92 | Ограничьте доступ к вашим данным 93 | Многие приложения могут посылать многократные и неоправданные запросы местоположения в фоновом режиме, зачастую отправляя эти данные на свой сервер. 94 | \n\nДанные о местоположении являются конфиденциальными и могут быть весьма деликатными. Данное приложение может помочь защитить вашу конфиденциальность и данные. 95 | Проверьте свои настройки 96 | Для бесперебойной работы убедитесь, что в настройках вашего местоположения не используется Wi-Fi, сотовая сеть или другие методы определения местоположения, которые могут привести к утечке информации о вашем реальном местоположении, 97 | а также в том, что для этого приложения отключена оптимизация заряда батареи. 98 | 99 | 100 | Изображение Bitcoin 101 | Изображение Litecoin 102 | Изображение Ethereum 103 | Изображение Monero 104 | 105 | 1GCkvAg9oG79niQTbh6EH9rPALQDXKyHKK 106 | LV687s3wVdhmLZyJMFxomJHdHFXeFAKT5R 107 | 0x785a8804c85b88683a5cce5e53f60878831e5d03 108 | 43Vijzdt3y42mmT954rSYPjXYabDsjYEV2KyhxfC46JibR2ny9VmRS1fjdJTHxxPVPFE8ajgArwjWfyaRgjh9vcNAwmkfJj 109 | 110 | Если вы находите эти приложения полезными, подумайте о том, чтобы поддержать меня в моей миссии по созданию простых, полезных, ориентированных на конфиденциальность приложений с открытым исходным кодом. 111 | Изображение текстового эмодзи 112 | 113 | -------------------------------------------------------------------------------- /app/src/main/res/values/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 5 minutes 6 | 10 minutes 7 | 15 minutes 8 | 30 minutes 9 | 1 hour 10 | 3 hours 11 | 6 hours 12 | 12 hours 13 | 24 hours 14 | 15 | 16 | 17 | 5 18 | 10 19 | 15 20 | 30 21 | 60 22 | 180 23 | 360 24 | 720 25 | 1440 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #0081dc 4 | #0065ac 5 | #D81B60 6 | 7 | #FFFFFF 8 | 9 | 10 | @color/colorPrimary 11 | #222222 12 | #ff9600 13 | 14 | #7e7e7e 15 | #4caf50 16 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 8dp 6 | 176dp 7 | 16dp 8 | 9 | 10 | 4dp 11 | 8dp 12 | 16dp 13 | 24dp 14 | 32dp 15 | 40dp 16 | 17 | 14dp 18 | 46dp 19 | 20 | 64dp 21 | -------------------------------------------------------------------------------- /app/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #F3F3F3 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Private Location 3 | Open-Source Location Privacy 4 | 5 | https://github.com/wesaphzt 6 | https://github.com/wesaphzt/PrivateLocation 7 | 8 | 9 | Favorites 10 | Go To 11 | Settings 12 | Donate 13 | Show Intro 14 | About 15 | 16 | 17 | Donate 18 | Copied address 19 | Address copied to clipboard 20 | About 21 | 22 | 23 | Favorite Locations 24 | Nothing here… 25 | Name 26 | Latitude: %1$f \nLongitude: %2$f 27 | Name 28 | Edit 29 | Delete 30 | 31 | Location Coordinates 32 | Latitude 33 | Longitude 34 | Not a valid range 35 | Not a valid number 36 | Can\'t be empty 37 | 38 | 39 | Location Settings 40 | If you experience issues with your real location being exposed, particularly with Google Maps based apps and services, 41 | it\'s recommended you disable any settings in your Location settings that use Wi-Fi points and more to help determine your location faster and more accurately. 42 | 43 | Mock Location Disabled 44 | Select this app in the mock location app setting. 45 | Enable mock locations in settings. 46 | 47 | Developer Mode Disabled 48 | Enable developer options by clicking on your phone build number 7 (seven) times. 49 | 50 | Developer Settings 51 | Please make sure developer settings are enabled before trying to set location to prevent issues on Nougat. 52 | \n\nYou can do this by clicking on your phone build number 7 (seven) times. 53 | 54 | 55 | About overview app logo 56 | 57 | Contribute 58 | Report Bugs 59 | Report bugs or request new features 60 | 61 | Author image 62 | wesaphzt 63 | Developer 64 | Developer 65 | 66 | Version 67 | Version image 68 | 69 | License 70 | GPL v3.0 License 71 | License image 72 | 73 | Source code 74 | Source code image 75 | 76 | Bug report image 77 | 78 | 79 | If service is currently running, restart to apply setting. 80 | \n\nMay interfere with screen temperature and localization. 81 | 82 | 83 | Start 84 | Stop 85 | 86 | SERVICE_STATUS 87 | Widget location image 88 | 89 | 90 | @string/app_name 91 | The perfect companion to help protect your phone location privacy and security by spoofing your location to anywhere in the world. 92 | Limit Your Data 93 | Many apps can make repeated and unnecessary location requests in the background, often sending this data back to the cloud. 94 | \n\nLocation data is personal and potentially very sensitive, this app can help protect your privacy and data. 95 | Check Your Settings 96 | For smooth sailing, make sure your location settings aren\'t using WiFi, network or other methods to determine your location that can leak your real location, 97 | and that battery optimization is turned off for this app. 98 | 99 | 100 | Bitcoin image 101 | Litecoin image 102 | Ethereum image 103 | Monero image 104 | 105 | 1GCkvAg9oG79niQTbh6EH9rPALQDXKyHKK 106 | LV687s3wVdhmLZyJMFxomJHdHFXeFAKT5R 107 | 0x785a8804c85b88683a5cce5e53f60878831e5d03 108 | 43Vijzdt3y42mmT954rSYPjXYabDsjYEV2KyhxfC46JibR2ny9VmRS1fjdJTHxxPVPFE8ajgArwjWfyaRgjh9vcNAwmkfJj 109 | 110 | If you find these apps useful, consider supporting me in some way in my mission to create simple, useful, privacy-oriented, open-source apps. 111 | Text emoji image 112 | 113 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 15 | 16 |