├── .gitignore ├── .idea ├── .gitignore ├── .name ├── codeStyles │ └── Project.xml ├── compiler.xml ├── deploymentTargetDropDown.xml ├── encodings.xml ├── gradle.xml ├── jarRepositories.xml ├── jpa-buddy.xml ├── misc.xml └── vcs.xml ├── LICENSE ├── README.md ├── Screenshot.png ├── app ├── .gitignore ├── build.gradle ├── libs │ └── solarpositioning.jar ├── proguard-rules.pro ├── release │ └── app-release.aab └── src │ └── main │ ├── AndroidManifest.xml │ ├── ic_launcher-web.png │ ├── java │ └── com │ │ └── genewarrior │ │ └── daynightmap │ │ ├── MainActivity.java │ │ ├── Status.java │ │ ├── SunNavigationView.java │ │ ├── SunSeekBar.java │ │ ├── Widget │ │ └── DayNightWidget.java │ │ └── ui │ │ ├── daynight │ │ ├── DayNightFragment.java │ │ └── DayNightHelper.java │ │ └── help │ │ └── HelpFragment.java │ ├── res │ ├── drawable-nodpi │ │ ├── daynight_widget_preview.png │ │ └── ic_day_of_year_button.xml │ ├── drawable │ │ ├── astrodawn.xml │ │ ├── civildawn.xml │ │ ├── ic_clock.xml │ │ ├── ic_launcher_foreground.xml │ │ ├── ic_menu_share.xml │ │ ├── ic_play_arrow.xml │ │ ├── ic_reset.xml │ │ ├── ic_stop.xml │ │ ├── ic_time_of_day_button.xml │ │ ├── icon_sun.xml │ │ ├── map_of_the_world.png │ │ ├── nauticaldawn.xml │ │ ├── roundedcorner.xml │ │ ├── roundedcorner2.xml │ │ ├── roundedcorner2_pressed.xml │ │ ├── roundedcorner2_unpressed.xml │ │ ├── roundedcorner_pressed.xml │ │ ├── roundedcorner_unpressed.xml │ │ └── side_nav_bar.xml │ ├── font │ │ ├── lato.xml │ │ ├── lato_black.ttf │ │ ├── lato_blackitalic.ttf │ │ ├── lato_bold.ttf │ │ ├── lato_bolditalic.ttf │ │ ├── lato_hairline.ttf │ │ ├── lato_hairlineitalic.ttf │ │ ├── lato_italic.ttf │ │ ├── lato_light.ttf │ │ ├── lato_lightitalic.ttf │ │ └── lato_regular.ttf │ ├── layout-land │ │ └── fragment_daynight.xml │ ├── layout │ │ ├── activity_main.xml │ │ ├── app_bar_main.xml │ │ ├── content_main.xml │ │ ├── fragment_daynight.xml │ │ ├── fragment_help.xml │ │ ├── nav_header_main.xml │ │ ├── view_sunnavigationview.xml │ │ └── widget_day_night.xml │ ├── menu │ │ └── activity_main_drawer.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 │ ├── navigation │ │ └── mobile_navigation.xml │ ├── values-night │ │ ├── colors.xml │ │ └── styles.xml │ ├── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── ic_launcher_background.xml │ │ ├── ids.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── xml │ │ └── day_night_widget_info.xml │ └── rs │ └── daynightscript.rs ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── screenshot2.png ├── screenshot3.png └── 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 | .cxx 15 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | Day & Night Map -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 20 | 21 | 22 | 23 | 25 | 26 | 27 |
28 | 29 | 30 | 31 | xmlns:android 32 | 33 | ^$ 34 | 35 | 36 | 37 |
38 |
39 | 40 | 41 | 42 | xmlns:.* 43 | 44 | ^$ 45 | 46 | 47 | BY_NAME 48 | 49 |
50 |
51 | 52 | 53 | 54 | .*:id 55 | 56 | http://schemas.android.com/apk/res/android 57 | 58 | 59 | 60 |
61 |
62 | 63 | 64 | 65 | .*:name 66 | 67 | http://schemas.android.com/apk/res/android 68 | 69 | 70 | 71 |
72 |
73 | 74 | 75 | 76 | name 77 | 78 | ^$ 79 | 80 | 81 | 82 |
83 |
84 | 85 | 86 | 87 | style 88 | 89 | ^$ 90 | 91 | 92 | 93 |
94 |
95 | 96 | 97 | 98 | .* 99 | 100 | ^$ 101 | 102 | 103 | BY_NAME 104 | 105 |
106 |
107 | 108 | 109 | 110 | .* 111 | 112 | http://schemas.android.com/apk/res/android 113 | 114 | 115 | ANDROID_ATTRIBUTE_ORDER 116 | 117 |
118 |
119 | 120 | 121 | 122 | .* 123 | 124 | .* 125 | 126 | 127 | BY_NAME 128 | 129 |
130 |
131 |
132 |
133 |
134 |
-------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/deploymentTargetDropDown.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | -------------------------------------------------------------------------------- /.idea/jpa-buddy.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Rainer Follador 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Screenshot](Screenshot.png) 2 | 3 | # Day & Night Map Android App 4 | 5 | The World map shows what areas of the Earth are in daylight, which are at night and in twilight. 6 | Use the slider to interactively set the time and date and visualize the changing of the seasons and the length of the day. 7 | The widget shows live data on your homescreen and is designed to be very battery friendly. 8 | 9 | This is an open source port of [*Day & Night Map and Widget* available on Google Play](https://play.google.com/store/apps/details?id=com.genewarrior.daynightmap). 10 | 11 | *Day & Night Map and Widget* is integrated in [SunLocator](http://sunlocator.com), an app 12 | to predict the sun's position in multiple ways (Augmented Reality, Map, 3D Terrain simulation). 13 | [Available on Google Play](https://play.google.com/store/apps/details?id=com.genewarrior.sunlocator.pro). 14 | 15 | ## Key Elements 16 | 17 | ### Renderscript 18 | 19 | The Renderscript file [daynightscript.rs](app/src/main/rs/daynightscript.rs) 20 | handles the rendering of the map's shadows on a pixel by pixel basis, depicting regions with night, 21 | civil dawn/dusk, nautical dawn/dusk and astronomical dawn/dusk. Mercator projection of the map makes it 22 | straightforward to calculate which pixel is which longitude/latitude. 23 | 24 | Calculation is based on [Grena, 2012 - Five new algorithms for the computation of sun position from 2010 to 2110](https://www.sciencedirect.com/science/article/abs/pii/S0038092X12000400) 25 | algorithms with modifications. 26 | 27 | [Renderscript](https://developer.android.com/guide/topics/renderscript/compute) 28 | allows running computational intensive tasks in parallel using GPUs and multi-core CPUs. 29 | This lets the app calculate the sun elevation at every pixel of the map (800x400 px) very fast while 30 | allowing interactively changing the time/date. 31 | 32 | **Note:** RenderScript is deprecated starting in Android 12. [See here.](https://developer.android.com/guide/topics/renderscript/migrate) 33 | I still have not got around to migrate it to Vulkan, as suggested in the linked article. 34 | 35 | ### Android Layouts 36 | 37 | The app itself is quite straightforward. [PhotoView](https://github.com/Baseflow/PhotoView) is 38 | used to allow a tiny bit of panning/zooming the map. 39 | The widget is implemented in [DayNightWidget.java](app/src/main/java/com/genewarrior/daynightmap/Widget/DayNightWidget.java). 40 | 41 | ![Screenshot 2](screenshot2.png) 42 | ![Screenshot 3](screenshot3.png) 43 | 44 | 45 | -------------------------------------------------------------------------------- /Screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r-follador/DayNightMap/d2e73cc879603837d5fb04fde6a9fd1cda005792/Screenshot.png -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | 5 | signingConfigs { 6 | config { 7 | keyAlias 'android' 8 | } 9 | } 10 | 11 | compileSdkVersion 32 12 | defaultConfig { 13 | applicationId "com.genewarrior.daynightmap" 14 | minSdkVersion 24 15 | targetSdkVersion 32 16 | versionCode 1 17 | versionName "1" 18 | vectorDrawables.useSupportLibrary = true 19 | renderscriptTargetApi 19 20 | renderscriptSupportModeEnabled true 21 | } 22 | buildTypes { 23 | release { 24 | minifyEnabled true 25 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 26 | signingConfig signingConfigs.config 27 | } 28 | } 29 | compileOptions { 30 | sourceCompatibility = 1.8 31 | targetCompatibility = 1.8 32 | } 33 | } 34 | 35 | dependencies { 36 | implementation fileTree(dir: 'libs', include: ['*.jar']) 37 | implementation("com.google.guava:guava:30.1.1-android") 38 | implementation 'androidx.appcompat:appcompat:1.5.1' 39 | implementation 'androidx.legacy:legacy-support-v4:1.0.0' 40 | implementation 'com.google.android.material:material:1.7.0' 41 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4' 42 | implementation 'androidx.navigation:navigation-fragment:2.5.3' 43 | implementation 'androidx.navigation:navigation-ui:2.5.3' 44 | implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' 45 | implementation 'com.github.Baseflow:PhotoView:2.3.0' 46 | implementation files('libs/solarpositioning.jar') 47 | } 48 | -------------------------------------------------------------------------------- /app/libs/solarpositioning.jar: -------------------------------------------------------------------------------- 1 | /home/rainer/Software_Dev/AndroidStudioProjects/SunLocator/solarpositioning/build/libs/solarpositioning.jar -------------------------------------------------------------------------------- /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 | 23 | -keep class * extends java.util.ListResourceBundle { 24 | protected Object[][] getContents(); 25 | } 26 | 27 | -keep public class com.google.android.gms.common.internal.safeparcel.SafeParcelable { 28 | public static final *** NULL; 29 | } 30 | 31 | -keepnames @com.google.android.gms.common.annotation.KeepName class * 32 | -keepclassmembernames class * { 33 | @com.google.android.gms.common.annotation.KeepName *; 34 | } 35 | 36 | -keepnames class * implements android.os.Parcelable { 37 | public static final ** CREATOR; 38 | } 39 | 40 | -keep class com.android.vending.billing.** 41 | 42 | -keep class androidx.renderscript.** { *; } 43 | -------------------------------------------------------------------------------- /app/release/app-release.aab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r-follador/DayNightMap/d2e73cc879603837d5fb04fde6a9fd1cda005792/app/release/app-release.aab -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 16 | 17 | 18 | 19 | 20 | 21 | 24 | 25 | 26 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r-follador/DayNightMap/d2e73cc879603837d5fb04fde6a9fd1cda005792/app/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /app/src/main/java/com/genewarrior/daynightmap/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.genewarrior.daynightmap; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | 6 | import androidx.core.view.GravityCompat; 7 | import androidx.navigation.NavController; 8 | import androidx.navigation.Navigation; 9 | import androidx.navigation.ui.AppBarConfiguration; 10 | import androidx.navigation.ui.NavigationUI; 11 | import com.google.android.material.navigation.NavigationView; 12 | 13 | import androidx.drawerlayout.widget.DrawerLayout; 14 | 15 | import androidx.appcompat.app.AppCompatActivity; 16 | import androidx.appcompat.widget.Toolbar; 17 | 18 | public class MainActivity extends AppCompatActivity { 19 | 20 | private AppBarConfiguration mAppBarConfiguration; 21 | @Override 22 | protected void onCreate(Bundle savedInstanceState) { 23 | super.onCreate(savedInstanceState); 24 | setContentView(R.layout.activity_main); 25 | Toolbar toolbar = findViewById(R.id.toolbar); 26 | setSupportActionBar(toolbar); 27 | DrawerLayout drawer = findViewById(R.id.drawer_layout); 28 | NavigationView navigationView = findViewById(R.id.nav_view); 29 | // Passing each menu ID as a set of Ids because each 30 | // menu should be considered as top level destinations. 31 | mAppBarConfiguration = new AppBarConfiguration.Builder( 32 | R.id.nav_home, R.id.nav_help) 33 | .setDrawerLayout(drawer) 34 | .build(); 35 | NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment); 36 | NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration); 37 | NavigationUI.setupWithNavController(navigationView, navController); 38 | navigationView.setNavigationItemSelectedListener(menuItem -> { 39 | if (menuItem.getItemId()==R.id.nav_share) 40 | shareAppLink(); 41 | NavigationUI.onNavDestinationSelected(menuItem,navController);//This is for maintaining the behavior of the Navigation view 42 | drawer.closeDrawer(GravityCompat.START); 43 | return true; 44 | }); 45 | } 46 | 47 | @Override 48 | public boolean onSupportNavigateUp() { 49 | NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment); 50 | return NavigationUI.navigateUp(navController, mAppBarConfiguration) 51 | || super.onSupportNavigateUp(); 52 | } 53 | 54 | private void shareAppLink() { 55 | try { 56 | Intent shareIntent = new Intent(Intent.ACTION_SEND); 57 | shareIntent.setType("text/plain"); 58 | shareIntent.putExtra(Intent.EXTRA_SUBJECT, "Day and Night Map"); 59 | //TODO: add correct playstore link 60 | String shareMessage= "I want to recommend the Day and Night App:\n" 61 | + "https://play.google.com/store/apps/details?id=com.genewarrior.daynightmap\n"; 62 | shareIntent.putExtra(Intent.EXTRA_TEXT, shareMessage); 63 | startActivity(Intent.createChooser(shareIntent, "Choose")); 64 | } catch(Exception e) { 65 | //e.toString(); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/java/com/genewarrior/daynightmap/Status.java: -------------------------------------------------------------------------------- 1 | package com.genewarrior.daynightmap; 2 | 3 | import java.util.GregorianCalendar; 4 | 5 | public class Status { 6 | 7 | public enum ButtonChoice { 8 | MinuteOfDay, DayOfYear 9 | }; 10 | 11 | public GregorianCalendar getDate() { 12 | return date; 13 | } 14 | 15 | public void setDate(GregorianCalendar date) { 16 | this.date = date; 17 | } 18 | 19 | public Status(double latitude, double longitude, double altitude, GregorianCalendar date, ButtonChoice buttonChoice) { 20 | this.latitude = latitude; 21 | this.longitude = longitude; 22 | this.altitude = altitude; 23 | this.date = date; 24 | this.buttonChoice = buttonChoice; 25 | } 26 | 27 | double latitude = 0; 28 | double longitude = 0; 29 | double altitude = 0; 30 | 31 | public ButtonChoice getButtonChoice() { 32 | return buttonChoice; 33 | } 34 | 35 | public void setButtonChoice(ButtonChoice buttonChoice) { 36 | this.buttonChoice = buttonChoice; 37 | } 38 | 39 | ButtonChoice buttonChoice = null; 40 | GregorianCalendar date = null; 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/java/com/genewarrior/daynightmap/SunNavigationView.java: -------------------------------------------------------------------------------- 1 | package com.genewarrior.daynightmap; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.app.Activity; 5 | import android.app.DatePickerDialog; 6 | import android.app.Dialog; 7 | import android.app.DialogFragment; 8 | import android.app.TimePickerDialog; 9 | import android.content.Context; 10 | import android.os.Bundle; 11 | import android.os.Handler; 12 | import android.text.format.DateFormat; 13 | import android.util.AttributeSet; 14 | import android.view.View; 15 | import android.widget.DatePicker; 16 | import android.widget.ImageButton; 17 | import android.widget.SeekBar; 18 | import android.widget.TimePicker; 19 | 20 | import androidx.constraintlayout.widget.ConstraintLayout; 21 | 22 | import java.text.SimpleDateFormat; 23 | import java.util.Calendar; 24 | import java.util.GregorianCalendar; 25 | 26 | public class SunNavigationView extends ConstraintLayout { 27 | ImageButton buttonChoice; 28 | ImageButton resetTime; 29 | ImageButton buttonPlay; 30 | ImageButton buttonSet; 31 | 32 | SunSeekBar sunSeekBar; 33 | Status status; 34 | boolean limitLiteVersion; 35 | 36 | 37 | public SunNavigationView(Context context) { 38 | super(context); 39 | init(); 40 | } 41 | 42 | public SunNavigationView(Context context, AttributeSet attrs) { 43 | super(context, attrs); 44 | init(); 45 | } 46 | 47 | public SunNavigationView(Context context, AttributeSet attrs, int defStyle) { 48 | super(context, attrs, defStyle); 49 | init(); 50 | } 51 | 52 | private void init() { 53 | inflate(getContext(), R.layout.view_sunnavigationview, this); 54 | buttonChoice = findViewById(R.id.buttonChoice); 55 | sunSeekBar = findViewById(R.id.sunSeekBar); 56 | resetTime = findViewById(R.id.buttonReset); 57 | buttonSet = findViewById(R.id.buttonSet); 58 | buttonPlay = findViewById(R.id.buttonPlay); 59 | 60 | buttonChoice.setImageResource(R.drawable.ic_time_of_day_button); 61 | 62 | buttonChoice.setOnClickListener(new OnClickListener() { 63 | public void onClick(View v) { 64 | if (status.getButtonChoice() == Status.ButtonChoice.DayOfYear) 65 | setSelection(Status.ButtonChoice.MinuteOfDay); 66 | else 67 | setSelection(Status.ButtonChoice.DayOfYear); 68 | } 69 | }); 70 | 71 | 72 | resetTime.setOnClickListener(new OnClickListener() { 73 | public void onClick(View v) { 74 | stopSunUpdater(); 75 | SimpleDateFormat fmt = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss S Z"); 76 | //System.out.println("Set time to " + fmt.format(originalTime.getTime())); 77 | status.getDate().setTime((new GregorianCalendar()).getTime()); 78 | //status.setDate((GregorianCalendar)originalTime.clone()); 79 | sunSeekBar.update(); 80 | callback.onSunNavigationChange(); 81 | //System.out.println("Time is now " + fmt.format(status.getDate().getTime())); 82 | } 83 | }); 84 | 85 | buttonSet.setOnClickListener(view -> { 86 | if (status.getButtonChoice() == Status.ButtonChoice.DayOfYear) { 87 | DialogFragment newFragment = new DatePickerFragment(this); 88 | newFragment.show(((Activity)getContext()).getFragmentManager(), "datePicker"); 89 | } else { //TimeofDay is selected 90 | DialogFragment newFragment = new TimePickerFragment(this); 91 | newFragment.show(((Activity)getContext()).getFragmentManager(), "timePicker"); 92 | } 93 | }); 94 | 95 | 96 | sunSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 97 | 98 | @Override 99 | public void onStopTrackingTouch(SeekBar seekBar) { 100 | // TODO Auto-generated method stub 101 | } 102 | 103 | @Override 104 | public void onStartTrackingTouch(SeekBar seekBar) { 105 | // TODO Auto-generated method stub 106 | } 107 | 108 | @Override 109 | public void onProgressChanged(SeekBar seekBar2, int progress,boolean fromUser) { 110 | if (fromUser) 111 | stopSunUpdater(); 112 | status.setDate(sunSeekBar.getDate()); 113 | 114 | long t_diff = Math.abs(status.getDate().getTimeInMillis() - System.currentTimeMillis()); 115 | if (t_diff < 60000) //less than one minute difference 116 | resetTime.setVisibility(INVISIBLE); 117 | else 118 | resetTime.setVisibility(VISIBLE); 119 | callback.onSunNavigationChange(); 120 | } 121 | }); 122 | 123 | buttonPlay.setOnClickListener(new OnClickListener() { 124 | public void onClick(View v) { 125 | if (sunupdaterIsRunning) { 126 | stopSunUpdater(); 127 | } else { 128 | sunUpdater.run(); 129 | buttonPlay.setImageResource(R.drawable.ic_stop); 130 | } 131 | } 132 | }); 133 | } 134 | 135 | Handler handler = new Handler(); 136 | 137 | boolean sunupdaterIsRunning = false; 138 | 139 | public Runnable sunUpdater = new Runnable() { 140 | @Override 141 | public void run() { 142 | sunupdaterIsRunning = true; 143 | if (status.buttonChoice == Status.ButtonChoice.DayOfYear) { 144 | status.getDate().add(GregorianCalendar.DAY_OF_YEAR, 1); 145 | } 146 | else { 147 | int dayOfYear = status.getDate().get(GregorianCalendar.DAY_OF_YEAR); 148 | status.getDate().add(GregorianCalendar.MINUTE, 2); 149 | } 150 | sunSeekBar.update(); 151 | callback.onSunNavigationChange(); 152 | handler.postDelayed(sunUpdater,40); 153 | } 154 | }; 155 | 156 | private void stopSunUpdater() { 157 | handler.removeCallbacks(sunUpdater); 158 | sunupdaterIsRunning = false; 159 | buttonPlay.setImageResource(R.drawable.ic_play_arrow); 160 | } 161 | 162 | public interface OnSunNavigationChangeListener { 163 | // TODO: Update argument type and name 164 | void onSunNavigationChange(); 165 | } 166 | 167 | OnSunNavigationChangeListener callback; 168 | 169 | public void setOnFragmentInteractionListener(OnSunNavigationChangeListener callback, Status status, boolean limitLiteVersion) { 170 | this.limitLiteVersion = limitLiteVersion; 171 | this.callback = callback; 172 | this.status = status; 173 | sunSeekBar.setStatus(status); 174 | sunSeekBar.update(); 175 | } 176 | 177 | protected void setSelection(Status.ButtonChoice choice) { 178 | if (choice == Status.ButtonChoice.DayOfYear) { 179 | status.setButtonChoice(choice); 180 | sunSeekBar.update(); 181 | buttonChoice.setImageResource(R.drawable.ic_day_of_year_button); 182 | } else if (choice == Status.ButtonChoice.MinuteOfDay) { 183 | status.setButtonChoice(choice); 184 | sunSeekBar.update(); 185 | buttonChoice.setImageResource(R.drawable.ic_time_of_day_button); 186 | } 187 | sunSeekBar.update(); 188 | } 189 | 190 | @Override 191 | protected void onDetachedFromWindow() { 192 | super.onDetachedFromWindow(); 193 | stopSunUpdater(); 194 | } 195 | 196 | @SuppressLint("ValidFragment") //Shouldn't use custom constructor, but the alternative is a pain in the ass 197 | public static class TimePickerFragment extends DialogFragment implements TimePickerDialog.OnTimeSetListener { 198 | SunNavigationView sunNavigationView; 199 | public TimePickerFragment(SunNavigationView sunNavigationView) { 200 | super(); 201 | this.sunNavigationView = sunNavigationView; 202 | } 203 | 204 | @Override 205 | public Dialog onCreateDialog(Bundle savedInstanceState) { 206 | // Use the current time as the default values for the picker 207 | final Calendar c = Calendar.getInstance(); 208 | int hour = c.get(Calendar.HOUR_OF_DAY); 209 | int minute = c.get(Calendar.MINUTE); 210 | 211 | // Create a new instance of TimePickerDialog and return it 212 | return new TimePickerDialog(getActivity(), this, hour, minute, 213 | DateFormat.is24HourFormat(getActivity())); 214 | } 215 | 216 | public void onTimeSet(TimePicker view, int hourOfDay, int minute) { 217 | sunNavigationView.status.getDate().set(Calendar.HOUR_OF_DAY, hourOfDay); 218 | sunNavigationView.status.getDate().set(Calendar.MINUTE, minute); 219 | sunNavigationView.sunSeekBar.update(); 220 | sunNavigationView.callback.onSunNavigationChange(); 221 | } 222 | } 223 | 224 | @SuppressLint("ValidFragment") //Shouldn't use custom constructor, but the alternative is a pain in the ass 225 | public static class DatePickerFragment extends DialogFragment implements DatePickerDialog.OnDateSetListener { 226 | 227 | SunNavigationView sunNavigationView; 228 | public DatePickerFragment(SunNavigationView sunNavigationView) { 229 | super(); 230 | this.sunNavigationView = sunNavigationView; 231 | } 232 | 233 | @Override 234 | public Dialog onCreateDialog(Bundle savedInstanceState) { 235 | // Use the current date as the default date in the picker 236 | final Calendar c = Calendar.getInstance(); 237 | int year = c.get(Calendar.YEAR); 238 | int month = c.get(Calendar.MONTH); 239 | int day = c.get(Calendar.DAY_OF_MONTH); 240 | 241 | // Create a new instance of DatePickerDialog and return it 242 | return new DatePickerDialog(getActivity(), this, year, month, day); 243 | } 244 | 245 | public void onDateSet(DatePicker view, int year, int month, int day) { 246 | sunNavigationView.status.getDate().set(Calendar.YEAR, year); 247 | sunNavigationView.status.getDate().set(Calendar.MONTH, month); 248 | sunNavigationView.status.getDate().set(Calendar.DAY_OF_MONTH, day); 249 | sunNavigationView.sunSeekBar.update(); 250 | sunNavigationView.callback.onSunNavigationChange(); 251 | } 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /app/src/main/java/com/genewarrior/daynightmap/SunSeekBar.java: -------------------------------------------------------------------------------- 1 | package com.genewarrior.daynightmap; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Paint; 6 | import android.graphics.Typeface; 7 | import android.util.AttributeSet; 8 | import android.util.TypedValue; 9 | import android.widget.SeekBar; 10 | 11 | import androidx.core.content.ContextCompat; 12 | import androidx.core.content.res.ResourcesCompat; 13 | 14 | import java.text.SimpleDateFormat; 15 | import java.util.Calendar; 16 | import java.util.GregorianCalendar; 17 | 18 | public class SunSeekBar extends SeekBar { 19 | static final int maximumPos = 10000; 20 | 21 | Status status; 22 | 23 | public SunSeekBar(Context context) { 24 | super(context); 25 | init(); 26 | } 27 | 28 | public SunSeekBar(Context context, AttributeSet attrs) { 29 | super(context, attrs); 30 | init(); 31 | } 32 | 33 | public SunSeekBar(Context context, AttributeSet attrs, int defStyleAttr) { 34 | super(context, attrs, defStyleAttr); 35 | init(); 36 | } 37 | 38 | public void init() { 39 | setMax(maximumPos); 40 | Typeface font = ResourcesCompat.getFont(getContext(), R.font.lato); 41 | blackPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 42 | blackPaint.setTypeface(font); 43 | blackPaint.setColor(ContextCompat.getColor(getContext(), R.color.colorSunseekbarFont)); 44 | blackPaint.setStyle(Paint.Style.FILL); 45 | fontsize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,12, getResources().getDisplayMetrics()); 46 | blackPaint.setTextSize(fontsize); 47 | drawDate = new GregorianCalendar(); 48 | drawDate.set(GregorianCalendar.DAY_OF_YEAR, 1); 49 | drawDate.set(GregorianCalendar.HOUR, 0); 50 | drawDate.set(GregorianCalendar.MINUTE, 0); 51 | } 52 | 53 | Paint blackPaint; 54 | GregorianCalendar drawDate; 55 | int fontsize; 56 | 57 | public void setStatus(Status status) 58 | { 59 | this.status = status; 60 | } 61 | 62 | 63 | public void update() { 64 | this.setProgress(date2position(status.date, status.getButtonChoice())); 65 | } 66 | 67 | public void setDate(GregorianCalendar date) { 68 | 69 | } 70 | 71 | public GregorianCalendar getDate() { 72 | return position2date(this.getProgress(), status.buttonChoice, status.date); 73 | } 74 | 75 | 76 | 77 | private static int date2position(GregorianCalendar date, Status.ButtonChoice buttonChoice) { 78 | if (buttonChoice == Status.ButtonChoice.MinuteOfDay) { 79 | int minuteOfDay = date.get(Calendar.MINUTE) + (date.get(Calendar.HOUR_OF_DAY)*60); 80 | return Math.round((float)maximumPos*((float)minuteOfDay/(float)((23*60)+59))); 81 | } else if (buttonChoice == Status.ButtonChoice.DayOfYear) { 82 | return Math.round((float)maximumPos*((float)(date.get(Calendar.DAY_OF_YEAR)-1)/364.0f)); 83 | } 84 | return -1; //error 85 | } 86 | 87 | private static GregorianCalendar position2date(int position, Status.ButtonChoice buttonChoice, GregorianCalendar date) { 88 | 89 | GregorianCalendar dateOut = (GregorianCalendar)(date.clone()); 90 | 91 | if (buttonChoice == Status.ButtonChoice.MinuteOfDay) { 92 | int minuteOfDay = Math.round((((float)position/(float)maximumPos)*(float)((23*60)+59))); 93 | int hourOfDay = minuteOfDay/60; 94 | int minuteOfHour = minuteOfDay%60; 95 | 96 | 97 | 98 | dateOut.set(Calendar.HOUR_OF_DAY, hourOfDay); 99 | dateOut.set(Calendar.MINUTE, minuteOfHour); 100 | } else if (buttonChoice == Status.ButtonChoice.DayOfYear) { 101 | dateOut.set(Calendar.DAY_OF_YEAR, 1+Math.round(364.0f*(float)position/(float)maximumPos)); 102 | } 103 | return dateOut; 104 | } 105 | 106 | 107 | 108 | //see https://stackoverflow.com/questions/15011144/how-to-make-custom-seek-bar-in-android 109 | @Override 110 | protected synchronized void onDraw(Canvas canvas) { 111 | 112 | super.onDraw(canvas); 113 | 114 | // size of seek bar. 115 | float width = getWidth(); 116 | float height = getHeight(); 117 | float mPaddingLeft = getPaddingLeft(); 118 | float mPaddingRight = getPaddingRight(); 119 | float mPaddingTop = getPaddingTop(); 120 | float mPaddingBottom = getPaddingBottom(); 121 | float yMiddle = mPaddingTop + ((height-mPaddingTop-mPaddingBottom)/2); 122 | 123 | SimpleDateFormat sdf = new SimpleDateFormat("MMM"); 124 | 125 | float barWidth = width - mPaddingLeft-mPaddingRight; 126 | 127 | if (status.getButtonChoice()==Status.ButtonChoice.DayOfYear) { 128 | for (int month = 0; month < 12; month++) { 129 | drawDate.set(GregorianCalendar.MONTH, month); 130 | int pos = date2position(drawDate, Status.ButtonChoice.DayOfYear); 131 | float x = (barWidth * ((float) pos / (float) maximumPos)) + mPaddingRight; 132 | canvas.drawCircle(x, yMiddle, 5, blackPaint); 133 | float textY = (month % 2 == 0 ? yMiddle - 6 : yMiddle + fontsize + 2); 134 | canvas.drawText(sdf.format(drawDate.getTime()), x + 2, textY, blackPaint); 135 | } 136 | } else { 137 | for (int hour = 0; hour < 25; hour++) { 138 | 139 | float x = (barWidth * ((float)hour/24f)) + mPaddingRight; 140 | canvas.drawCircle(x, yMiddle, 5, blackPaint); 141 | float textY = (hour % 2 == 0 ? yMiddle - 12 : yMiddle + fontsize + 6); 142 | if (hour%3!=0) 143 | continue; 144 | canvas.drawCircle(x, yMiddle, 8, blackPaint); 145 | if (hour==24) 146 | continue; 147 | String txt = Integer.toString(hour)+(hour<12?"am":"pm"); 148 | canvas.drawText(txt, x - 15, textY, blackPaint); 149 | } 150 | } 151 | 152 | } 153 | 154 | } 155 | -------------------------------------------------------------------------------- /app/src/main/java/com/genewarrior/daynightmap/Widget/DayNightWidget.java: -------------------------------------------------------------------------------- 1 | package com.genewarrior.daynightmap.Widget; 2 | 3 | import android.app.AlarmManager; 4 | import android.app.PendingIntent; 5 | import android.appwidget.AppWidgetManager; 6 | import android.appwidget.AppWidgetProvider; 7 | import android.content.ComponentName; 8 | import android.content.Context; 9 | import android.content.Intent; 10 | import android.graphics.Bitmap; 11 | import android.os.SystemClock; 12 | import android.widget.RemoteViews; 13 | 14 | 15 | import com.genewarrior.daynightmap.MainActivity; 16 | import com.genewarrior.daynightmap.R; 17 | import com.genewarrior.daynightmap.ui.daynight.DayNightHelper; 18 | 19 | import java.util.GregorianCalendar; 20 | 21 | 22 | /** 23 | * Implementation of App Widget functionality. 24 | */ 25 | public class DayNightWidget extends AppWidgetProvider { 26 | 27 | @Override 28 | public void onReceive(Context context, Intent intent) { 29 | super.onReceive(context, intent); 30 | //(context.getPackageName()+".DAY_NIGHT_WIDGET_UPDATE_INTENT") 31 | if ((context.getPackageName()+".DAY_NIGHT_WIDGET_UPDATE_INTENT").equals(intent.getAction())) { 32 | // Get the widget manager and ids for this widget provider, then call the shared 33 | // clock update method. 34 | ComponentName thisAppWidget = new ComponentName(context.getPackageName(), getClass().getName()); 35 | AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); 36 | int[] ids = appWidgetManager.getAppWidgetIds(thisAppWidget); 37 | 38 | DayNightHelper dayNightHelper = new DayNightHelper(context); 39 | Bitmap outputBmp = dayNightHelper.getDayNightBmp(new GregorianCalendar()); 40 | 41 | 42 | for (int appWidgetID: ids) { 43 | updateAppWidget(context, appWidgetManager, appWidgetID, outputBmp); 44 | } 45 | } 46 | } 47 | 48 | private PendingIntent createDayNightTickIntent(Context context) { 49 | Intent intent = new Intent(context, DayNightWidget.class); 50 | 51 | intent.setAction(context.getPackageName()+".DAY_NIGHT_WIDGET_UPDATE_INTENT"); 52 | PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); 53 | 54 | return pendingIntent; 55 | } 56 | 57 | 58 | static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, 59 | int appWidgetId, Bitmap bitmap) { 60 | // Construct the RemoteViews object 61 | try { 62 | RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_day_night); 63 | views.setImageViewBitmap(R.id.daynightimageWidget, bitmap); 64 | 65 | Intent mainactivityIntent = new Intent(context, MainActivity.class); 66 | PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, mainactivityIntent, PendingIntent.FLAG_IMMUTABLE); 67 | views.setOnClickPendingIntent(R.id.daynightimageWidget, pendingIntent); 68 | 69 | // Instruct the widget manager to update the widget 70 | appWidgetManager.updateAppWidget(appWidgetId, views); 71 | } catch (Exception e) { 72 | e.printStackTrace(); 73 | //Bla 74 | } 75 | } 76 | 77 | @Override 78 | public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { 79 | DayNightHelper dayNightHelper = null; 80 | 81 | try { 82 | dayNightHelper = new DayNightHelper(context); 83 | } catch (Exception e) { 84 | e.printStackTrace(); 85 | } catch (OutOfMemoryError e) { 86 | System.gc(); 87 | try { 88 | dayNightHelper = new DayNightHelper(context); 89 | } catch (OutOfMemoryError e2) { 90 | e2.printStackTrace(); 91 | } 92 | } 93 | 94 | if (dayNightHelper == null) 95 | return; 96 | 97 | Bitmap outputBmp = dayNightHelper.getDayNightBmp(new GregorianCalendar()); 98 | 99 | for (int appWidgetId : appWidgetIds) { 100 | updateAppWidget(context, appWidgetManager, appWidgetId, outputBmp); 101 | } 102 | 103 | dayNightHelper.destroy(); 104 | super.onUpdate(context, appWidgetManager, appWidgetIds); 105 | } 106 | 107 | 108 | 109 | @Override 110 | public void onEnabled(Context context) { 111 | // Enter relevant functionality for when the first widget is created 112 | super.onEnabled(context); 113 | AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); 114 | 115 | alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime()+1000, 5*60*1000, createDayNightTickIntent(context)); 116 | } 117 | 118 | @Override 119 | public void onDisabled(Context context) { 120 | // Enter relevant functionality for when the last widget is disabled 121 | super.onDisabled(context); 122 | AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); 123 | alarmManager.cancel(createDayNightTickIntent(context)); 124 | 125 | } 126 | } 127 | 128 | -------------------------------------------------------------------------------- /app/src/main/java/com/genewarrior/daynightmap/ui/daynight/DayNightFragment.java: -------------------------------------------------------------------------------- 1 | package com.genewarrior.daynightmap.ui.daynight; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.Matrix; 5 | import android.os.AsyncTask; 6 | import android.os.Bundle; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.TextView; 11 | 12 | import androidx.annotation.NonNull; 13 | import androidx.fragment.app.Fragment; 14 | 15 | import com.genewarrior.daynightmap.R; 16 | import com.genewarrior.daynightmap.Status; 17 | import com.genewarrior.daynightmap.SunNavigationView; 18 | import com.github.chrisbanes.photoview.PhotoView; 19 | 20 | import java.text.SimpleDateFormat; 21 | import java.util.GregorianCalendar; 22 | import java.util.TimeZone; 23 | 24 | public class DayNightFragment extends Fragment implements SunNavigationView.OnSunNavigationChangeListener { 25 | 26 | TextView timeView; 27 | boolean isRunning = false; 28 | GregorianCalendar queue = null; 29 | Status status = null; 30 | DayNightHelper dayNightHelper; 31 | PhotoView dayNightImage; 32 | SimpleDateFormat fmt = new SimpleDateFormat("dd-MMM-yyyy HH:mm"); 33 | SunNavigationView sunNavigationView; 34 | DayLightImageTask dayLightImageTask; 35 | 36 | public View onCreateView(@NonNull LayoutInflater inflater, 37 | ViewGroup container, Bundle savedInstanceState) { 38 | View root = inflater.inflate(R.layout.fragment_daynight, container, false); 39 | dayNightHelper = new DayNightHelper(getContext()); 40 | 41 | status = new Status(0, 0, 0, new GregorianCalendar(), Status.ButtonChoice.MinuteOfDay); 42 | fmt.setTimeZone(status.getDate().getTimeZone()); 43 | timeView = (TextView) root.findViewById(R.id.timeView); 44 | dayNightImage = (PhotoView) root.findViewById(R.id.daynightimage); 45 | sunNavigationView = root.findViewById(R.id.sunnavigationview); 46 | sunNavigationView.setOnFragmentInteractionListener(this, status, false); 47 | 48 | drawNewDate(); 49 | 50 | return root; 51 | } 52 | 53 | protected String getStringFromDate() { 54 | String tz = status.getDate().getTimeZone().getDisplayName(status.getDate().getTimeZone().inDaylightTime(status.getDate().getTime()), TimeZone.SHORT); 55 | return fmt.format(status.getDate().getTime()) + "\n" +tz; 56 | } 57 | 58 | protected void setTimeView(String str) { 59 | timeView.setText(str); 60 | } 61 | 62 | protected void drawNewDate() { 63 | setTimeView(getStringFromDate()); 64 | if (isRunning) { 65 | queue = status.getDate(); 66 | return; 67 | } 68 | 69 | dayLightImageTask = new DayLightImageTask(); 70 | dayLightImageTask.execute(status.getDate()); 71 | 72 | } 73 | 74 | 75 | 76 | @Override 77 | public void onSunNavigationChange() { 78 | drawNewDate(); 79 | } 80 | 81 | protected class DayLightImageTask extends AsyncTask { 82 | 83 | Bitmap outputBmp; 84 | @Override 85 | protected Integer doInBackground(GregorianCalendar... dates) { 86 | GregorianCalendar date = dates[0]; 87 | outputBmp = dayNightHelper.getDayNightBmp(date); 88 | return null; 89 | } 90 | 91 | protected void onProgressUpdate(Integer... progress) { 92 | //do something 93 | } 94 | 95 | protected void onPreExecute() { 96 | isRunning = true; 97 | } 98 | 99 | protected void onPostExecute(Integer result) { 100 | Matrix previousImageMatrix = new Matrix(); 101 | dayNightImage.getSuppMatrix(previousImageMatrix); 102 | dayNightImage.setImageBitmap(outputBmp); 103 | dayNightImage.setDisplayMatrix(previousImageMatrix); 104 | isRunning = false; 105 | 106 | if (queue != null) { 107 | new DayLightImageTask().execute((GregorianCalendar)queue.clone()); 108 | queue = null; 109 | } 110 | } 111 | } 112 | 113 | @Override 114 | public void onDestroyView() { 115 | dayLightImageTask.cancel(true); 116 | dayNightHelper.destroy(); 117 | super.onDestroyView(); 118 | } 119 | } -------------------------------------------------------------------------------- /app/src/main/java/com/genewarrior/daynightmap/ui/daynight/DayNightHelper.java: -------------------------------------------------------------------------------- 1 | package com.genewarrior.daynightmap.ui.daynight; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.graphics.BitmapFactory; 6 | import android.graphics.Canvas; 7 | import android.graphics.Matrix; 8 | import android.graphics.drawable.Drawable; 9 | 10 | import androidx.core.content.res.ResourcesCompat; 11 | import androidx.renderscript.Allocation; 12 | import androidx.renderscript.RenderScript; 13 | 14 | import com.genewarrior.daynightmap.R; 15 | import com.genewarrior.sunlocator.app.ScriptC_daynightscript; 16 | import com.sunlocator.solarpositioning.AzimuthZenithAngle; 17 | import com.sunlocator.solarpositioning.DeltaT; 18 | 19 | import java.util.GregorianCalendar; 20 | 21 | import static com.sunlocator.solarpositioning.Grena3.calcT; 22 | import static java.lang.Math.PI; 23 | import static java.lang.Math.asin; 24 | import static java.lang.Math.atan2; 25 | import static java.lang.Math.cos; 26 | import static java.lang.Math.sin; 27 | import static java.lang.Math.sqrt; 28 | import static java.lang.Math.toDegrees; 29 | import static java.lang.Math.toRadians; 30 | 31 | public class DayNightHelper { 32 | 33 | RenderScript rs; 34 | ScriptC_daynightscript daynightscript; 35 | 36 | Allocation mapBmpInput; 37 | 38 | Bitmap background; 39 | Drawable sunIcon; 40 | int sunIconSize; 41 | int width; 42 | int height; 43 | public DayNightHelper(Context context) { 44 | background = BitmapFactory.decodeResource(context.getResources(), R.drawable.map_of_the_world); 45 | sunIcon = ResourcesCompat.getDrawable(context.getResources(), R.drawable.icon_sun, null); 46 | 47 | width = background.getWidth(); 48 | height = background.getHeight(); 49 | sunIconSize = height/5; 50 | rs = RenderScript.create(context, RenderScript.ContextType.NORMAL); 51 | daynightscript = new ScriptC_daynightscript(rs); 52 | 53 | mapBmpInput = Allocation.createFromBitmap(rs, background); 54 | } 55 | 56 | public Bitmap getDayNightBmp(GregorianCalendar date) { 57 | 58 | if (isDestroyed) { 59 | return null; 60 | } 61 | 62 | SolarElevationPreCalc preCalc = calculateSolarElevation_Step1(date); 63 | 64 | Bitmap.Config conf = Bitmap.Config.ARGB_8888; 65 | Bitmap bmpOutput = Bitmap.createBitmap(background.getWidth(), background.getHeight(), conf); 66 | 67 | 68 | Allocation outputBmpAlloc = Allocation.createFromBitmap(rs, bmpOutput); 69 | 70 | daynightscript.set_alpha((float)preCalc.alpha); //right ascension 71 | daynightscript.set_delta((float)preCalc.delta); //declination 72 | daynightscript.set_h_pre((float)preCalc.H_pre); 73 | daynightscript.set_height(height); 74 | daynightscript.set_width(width); 75 | 76 | daynightscript.forEach_root(mapBmpInput, outputBmpAlloc); 77 | rs.finish(); 78 | outputBmpAlloc.copyTo(bmpOutput); 79 | 80 | Bitmap overlay = background.copy(background.getConfig(), true); 81 | Canvas canvas = new Canvas(overlay); 82 | canvas.drawBitmap(bmpOutput, new Matrix(), null); 83 | 84 | 85 | double pseudoH = preCalc.H_pre-preCalc.alpha; //the hour angle for 0 longitude 86 | pseudoH = ((pseudoH + PI) % (2 * PI)) - PI; 87 | if (pseudoH < -PI) { 88 | pseudoH += 2 * PI; 89 | } 90 | 91 | int longitudePixel = (width/2)-(int)((pseudoH/(Math.PI))*(double)width/2); 92 | int latitudePixel = (height/2)-(int)((preCalc.delta/(Math.PI/2d))*(double)height/2); 93 | sunIcon.setBounds(longitudePixel-(sunIconSize/2), latitudePixel-(sunIconSize/2), longitudePixel+(sunIconSize/2), latitudePixel+(sunIconSize/2)); 94 | sunIcon.draw(canvas); 95 | 96 | if (longitudePixel-(sunIconSize/2)<0) { 97 | sunIcon.setBounds(width+longitudePixel-(sunIconSize/2), latitudePixel-(sunIconSize/2), width+longitudePixel+(sunIconSize/2), latitudePixel+(sunIconSize/2)); 98 | sunIcon.draw(canvas); 99 | } else if (longitudePixel+(sunIconSize/2)>width) { 100 | int bla = ((width-longitudePixel)*-1)-(sunIconSize/2); 101 | sunIcon.setBounds(bla, latitudePixel-(sunIconSize/2), bla+(sunIconSize), latitudePixel+(sunIconSize/2)); 102 | 103 | sunIcon.draw(canvas); 104 | } 105 | outputBmpAlloc.destroy(); 106 | 107 | return overlay; 108 | } 109 | 110 | boolean isDestroyed = false; 111 | public void destroy() { 112 | isDestroyed = true; 113 | mapBmpInput.destroy(); 114 | daynightscript.destroy(); 115 | rs.destroy(); 116 | } 117 | 118 | 119 | /** 120 | * Modified from Grena3; no refraction correction 121 | * Step 1: non lat/lon dependent calculations 122 | * 123 | * @param date Observer's local date and time. 124 | * @return Topocentric solar position (azimuth measured eastward from north) 125 | * @see AzimuthZenithAngle 126 | */ 127 | public static SolarElevationPreCalc calculateSolarElevation_Step1(final GregorianCalendar date) { 128 | final double deltaT = DeltaT.estimate(date); 129 | final double t = calcT(date); 130 | final double tE = t + 1.1574e-5 * deltaT; 131 | final double omegaAtE = 0.0172019715 * tE; 132 | 133 | final double lambda = -1.388803 + 1.720279216e-2 * tE + 3.3366e-2 * sin(omegaAtE - 0.06172) 134 | + 3.53e-4 * sin(2.0 * omegaAtE - 0.1163); 135 | 136 | final double epsilon = 4.089567e-1 - 6.19e-9 * tE; 137 | 138 | final double sLambda = sin(lambda); 139 | final double cLambda = cos(lambda); 140 | final double sEpsilon = sin(epsilon); 141 | final double cEpsilon = sqrt(1 - sEpsilon * sEpsilon); 142 | 143 | double alpha = atan2(sLambda * cEpsilon, cLambda); 144 | if (alpha < 0) { 145 | alpha += 2 * PI; 146 | } 147 | 148 | final double delta = asin(sLambda * sEpsilon); 149 | 150 | double H_pre = 1.7528311 + 6.300388099 * t; 151 | 152 | 153 | return new SolarElevationPreCalc(alpha, delta, H_pre); 154 | } 155 | 156 | private static class SolarElevationPreCalc { 157 | double alpha, delta, H_pre; 158 | public SolarElevationPreCalc(double alpha, double delta, double H_pre) { 159 | this.alpha = alpha; 160 | this.delta = delta; 161 | this.H_pre = H_pre; 162 | } 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /app/src/main/java/com/genewarrior/daynightmap/ui/help/HelpFragment.java: -------------------------------------------------------------------------------- 1 | package com.genewarrior.daynightmap.ui.help; 2 | 3 | import android.os.Bundle; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.TextView; 8 | 9 | import androidx.annotation.Nullable; 10 | import androidx.annotation.NonNull; 11 | import androidx.fragment.app.Fragment; 12 | import androidx.lifecycle.Observer; 13 | import androidx.lifecycle.ViewModelProviders; 14 | 15 | import com.genewarrior.daynightmap.R; 16 | 17 | public class HelpFragment extends Fragment { 18 | 19 | 20 | public View onCreateView(@NonNull LayoutInflater inflater, 21 | ViewGroup container, Bundle savedInstanceState) { 22 | View root = inflater.inflate(R.layout.fragment_help, container, false); 23 | 24 | return root; 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/daynight_widget_preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r-follador/DayNightMap/d2e73cc879603837d5fb04fde6a9fd1cda005792/app/src/main/res/drawable-nodpi/daynight_widget_preview.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/ic_day_of_year_button.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/astrodawn.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/civildawn.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_clock.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 6 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | 19 | 20 | 23 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_menu_share.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_play_arrow.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_reset.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_stop.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_time_of_day_button.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_sun.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 10 | 14 | 16 | 22 | 24 | 30 | 32 | 38 | 40 | 46 | 48 | 54 | 56 | 62 | 64 | 70 | 72 | 78 | 80 | 86 | 88 | 94 | 96 | 102 | 104 | 110 | 112 | 118 | 120 | 124 | 127 | 131 | 134 | 140 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/map_of_the_world.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r-follador/DayNightMap/d2e73cc879603837d5fb04fde6a9fd1cda005792/app/src/main/res/drawable/map_of_the_world.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/nauticaldawn.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/roundedcorner.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | 9 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/roundedcorner2.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | 9 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/roundedcorner2_pressed.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/roundedcorner2_unpressed.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/roundedcorner_pressed.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/roundedcorner_unpressed.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/side_nav_bar.xml: -------------------------------------------------------------------------------- 1 | 3 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/font/lato.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 11 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/font/lato_black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r-follador/DayNightMap/d2e73cc879603837d5fb04fde6a9fd1cda005792/app/src/main/res/font/lato_black.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/lato_blackitalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r-follador/DayNightMap/d2e73cc879603837d5fb04fde6a9fd1cda005792/app/src/main/res/font/lato_blackitalic.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/lato_bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r-follador/DayNightMap/d2e73cc879603837d5fb04fde6a9fd1cda005792/app/src/main/res/font/lato_bold.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/lato_bolditalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r-follador/DayNightMap/d2e73cc879603837d5fb04fde6a9fd1cda005792/app/src/main/res/font/lato_bolditalic.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/lato_hairline.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r-follador/DayNightMap/d2e73cc879603837d5fb04fde6a9fd1cda005792/app/src/main/res/font/lato_hairline.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/lato_hairlineitalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r-follador/DayNightMap/d2e73cc879603837d5fb04fde6a9fd1cda005792/app/src/main/res/font/lato_hairlineitalic.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/lato_italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r-follador/DayNightMap/d2e73cc879603837d5fb04fde6a9fd1cda005792/app/src/main/res/font/lato_italic.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/lato_light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r-follador/DayNightMap/d2e73cc879603837d5fb04fde6a9fd1cda005792/app/src/main/res/font/lato_light.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/lato_lightitalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r-follador/DayNightMap/d2e73cc879603837d5fb04fde6a9fd1cda005792/app/src/main/res/font/lato_lightitalic.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/lato_regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r-follador/DayNightMap/d2e73cc879603837d5fb04fde6a9fd1cda005792/app/src/main/res/font/lato_regular.ttf -------------------------------------------------------------------------------- /app/src/main/res/layout-land/fragment_daynight.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 19 | 20 | 32 | 33 | 40 | 41 | 51 | 52 | 59 | 60 | 70 | 71 | 78 | 79 | 89 | 90 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 15 | 16 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/layout/app_bar_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/layout/content_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_daynight.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 19 | 20 | 32 | 33 | 39 | 40 | 48 | 49 | 56 | 57 | 66 | 67 | 74 | 75 | 85 | 86 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_help.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | 12 | 26 | 27 | 28 | 39 | 40 | 49 | 50 | 60 | 61 | 69 | 70 | 79 | 80 | 89 | 90 | 98 | 99 | 107 | 108 | 116 | 117 | 127 | 128 | 138 | 139 | 149 | 150 | 159 | 160 | 161 | 162 | 163 | -------------------------------------------------------------------------------- /app/src/main/res/layout/nav_header_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 22 | 23 | 30 | 31 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/res/layout/view_sunnavigationview.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 20 | 21 | 34 | 35 | 36 | 37 | 51 | 52 | 66 | 67 | 81 | 82 | -------------------------------------------------------------------------------- /app/src/main/res/layout/widget_day_night.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/menu/activity_main_drawer.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 10 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /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/r-follador/DayNightMap/d2e73cc879603837d5fb04fde6a9fd1cda005792/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r-follador/DayNightMap/d2e73cc879603837d5fb04fde6a9fd1cda005792/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r-follador/DayNightMap/d2e73cc879603837d5fb04fde6a9fd1cda005792/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r-follador/DayNightMap/d2e73cc879603837d5fb04fde6a9fd1cda005792/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r-follador/DayNightMap/d2e73cc879603837d5fb04fde6a9fd1cda005792/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r-follador/DayNightMap/d2e73cc879603837d5fb04fde6a9fd1cda005792/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r-follador/DayNightMap/d2e73cc879603837d5fb04fde6a9fd1cda005792/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r-follador/DayNightMap/d2e73cc879603837d5fb04fde6a9fd1cda005792/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r-follador/DayNightMap/d2e73cc879603837d5fb04fde6a9fd1cda005792/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r-follador/DayNightMap/d2e73cc879603837d5fb04fde6a9fd1cda005792/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/navigation/mobile_navigation.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/values-night/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #303F9F 4 | #ffffff 5 | #000000 6 | #000000 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 16 | 19 | 20 | 25 | 26 | 30 | 31 | 36 | 37 | 40 | 41 | 50 | 51 | 62 | 63 | 68 | 69 | 74 | 75 | 79 | 80 | 84 | 85 | 88 | 89 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3f92b5 4 | #303F9F 5 | #ff7840 6 | #000000 7 | #FFFFFF 8 | #FFFFFF 9 | #FFFFFF 10 | @android:color/black 11 | #c7f9f9f9 12 | #616161 13 | #FFFFFF 14 | @android:color/black 15 | @android:color/white 16 | #66000000 17 | #000000 18 | #EEE 19 | #eef2f4 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 8dp 6 | 176dp 7 | 16dp 8 | 0dp 9 | -------------------------------------------------------------------------------- /app/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFFFFF 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Day & Night Map 4 | Open navigation drawer 5 | Close navigation drawer 6 | Day & Night Map 7 | Made by SunLocator.com 8 | Navigation header 9 | Settings 10 | Day & Night Map 11 | Help 12 | Tools 13 | Share App 14 | Send 15 | 16 | Compass Calibration 17 | -Move away from any magnetic interference (be aware of any magnets in phone cases and holsters)\n-Rotate your device several times around each of its three axes\n-Confirm using a real compass 18 | Decimal Lat/Long 19 | Latitude 20 | Longitude 21 | North 22 | South 23 | West 24 | East 25 | Degrees Minutes Seconds 26 | Longitude is out of range. Max +/- 180° 27 | Latitude is out of range. Max +/- 88° 28 | OK 29 | Cancel 30 | Saved Locations 31 | New Position 32 | Save Location? 33 | Location saved 34 | Last used location 35 | No saved list found 36 | Your list is empty\nShowing example locations 37 | Error saving position 38 | Position and Time 39 | Location 40 | Save 41 | List 42 | GPS 43 | Map 44 | Search 45 | Coordinates 46 | Date/Time 47 | Current time 48 | Set time 49 | Set date 50 | Time zone 51 | Select Time Zone 52 | Device Default 53 | Auto Detect 54 | Set the desired date and time to continue 55 | Set the desired location to continue 56 | Set the location and the date/time to continue 57 | Querying server… 58 | Error querying Server :( 59 | No Time zone chosen 60 | Date and time is not set 61 | No Location chosen 62 | Sun 63 | Camera View 64 | Map View 65 | Morning 66 | Sunrise 67 | Astronomical Dawn 68 | Nautical Dawn 69 | Civil Dawn 70 | Noon 71 | Sun transit 72 | Day duration 73 | Evening 74 | Sunset 75 | Civil Dusk 76 | Nautical Dusk 77 | Astronomical Dusk 78 | Photography 79 | Blue Hour 80 | Golden Hour 81 | Equinox 82 | Spring 83 | Autumn 84 | Solstice 85 | Winter 86 | Summer 87 | Sun position at 88 | Azimuth 89 | Elevation 90 | Shadow ratio 91 | Sun Graph 92 | Day 93 | Year 94 | Day of Year 95 | Time of Day 96 | Moon 97 | Moon rise 98 | Moon set 99 | Moon position at 100 | Moon Phase 101 | Waning 102 | Waxing 103 | Day/Night 104 | Blue Ocean 105 | Transparent Ocean 106 | All times in 107 | Current sun position 108 | Current Moon position 109 | Night 110 | Sun Dial Height 111 | Sun Dial Shadow 112 | Set Sun Dial Height 113 | Map Layer 114 | Compass Direction 115 | Lock Position 116 | Metric 117 | Imperial 118 | Metric/Imperial Unit 119 | Meter 120 | Feet 121 | Set 122 | Delete 123 | Yes 124 | No 125 | Calculating… 126 | Sunrise 127 | Sunset 128 | Time 129 | None 130 | Not visible 131 | Can\'t open Maps. 132 | Can\'t get GPS location. Check if GPS is turned on. 133 | Can\'t open Map View. Make sure Google Maps is installed. 134 | Can\'t open Camera View. 135 | -Remember to regularly calibrate your device.\n-Be aware of your surroundings\n-For children: Parental supervision is important 136 | Sun Locator cannot access your compass. Does your device have a compass? Is it enabled? 137 | unknown 138 | not set 139 | Show Help 140 | Map 141 | Problems? 142 | Having Problems? Read the Help file or send an email to contact@sunlocator.com 143 | Sun Locator Facebook Page 144 | Rate App 145 | Help 146 | Upgrade to Pro 147 | Get the Sun and Moon data for any day of the year 148 | -Plan ahead and know where to be for the perfect photo opportunity\n-Will your plants get enough Sun in the garden or balcony?\n-How does the path of the Sun change throughout the year?\n-Will neighbouring buildings block the Sun in Winter or Summer?\n\n 149 | Predict the Shadow length 150 | Customize the height of the sun dial to calculate and visualize shadow length of physical objects. 151 | Plot the Sunset and Sunrise over the course of a year. 152 | Upgrade! 153 | Later 154 | Help improve Day & Night 155 | Do you like Day & Night?\nPlease rate the app on the Play Store, and help making it more popular. 156 | Rate Day & Night 157 | Don\'t like Day & Night?\nHelp improving the app by sending an email to the developer\nAny feedback is welcome! 158 | Write an email 159 | Remind me later 160 | No, thanks 161 | Sun Locator predicts Sun and Moon positions during the course of a day and a year. 162 | Getting started 163 | First the app must know your current position and the date and time for which you want to predict the solar and lunar location. 164 | Set the position: 165 | By clicking on \"GPS\", you can use your device\'s GPS to get your current position. For this you need to activate your GPS. 166 | By clicking on \"Map\", you can select the position in Google Maps. For this you need to have an internet connection to download the map. 167 | Set the time and date: 168 | You can either use the current time by clicking on \"Use current time\" or set the date and time to a different value by clicking on \"Choose time\" and \"Choose date\", This allows you to predict the sun and moon position on a time and date of your choice. 169 | The Main view shows the important data about the current solar and lunar position, such as sun set and sun rise, twilight, moon phase etc. 170 | The Camera View shows the AR (augmented reality) feature of Sun Locator. The Sun and Moon position is overlaid on your device\'s camera and helps you to directly track the sun and moon path and check for any obstructions. 171 | The Map view shows the Sun and Moon path overlaid on Google Maps and the shadow is displayed on a 3D sundial. 172 | Sunrise, Sunset, Moon rise, Moon set: 173 | The time of day when the sun or moon crosses the Earth\'s horizon. 174 | Sun transit: 175 | Sometimes referred to as solar noon, is the time of the day when the sun passes over the observer\'s meridian line, this is roughly when the sun is at its highest. 176 | Twilight times: 177 | Twilight is the remaining illumination when the sun is below the horizon. The sunlight scatters in the atmosphere and illuminates the Earth. There are three definitions of twilight: 178 | Civil twilight: Approximately the limit at which solar illumination is sufficient, under clear weather conditions, for terrestrial objects to be clearly distinguished by eye. There is enough light from the sun during this period that artificial sources of light are not needed to carry on most outdoor activities. 179 | Nautical twilight: The horizon is clearly visible, but artificial lighting is necessary to see terrestrial objects. 180 | Astronomical twilight: Outside of the astronomical twilight, the sky (away from light pollution, moonlight, auroras, and other sources of light in the sky) is dark enough for nearly all astronomical observations. 181 | Shadow ratio: 182 | The length of the shadow cast by an object of the height of 1 foot or meter. 183 | E.g. at a shadow ratio of 1:0.45 an object of 5 meter height will cast a shadow of 5 × 0.45 = 2.25 meter 184 | Photography: Blue and golden hour: 185 | The Blue hour is the period of twilight when the sun is below the horizon and the indirect sunlight takes on a predominantly blue hue and cold color temperature. 186 | The Golden hour is the short period of time just before sunset or after sunrise when the light is redder, having a warm color temperature. Lighting is diffuse and with little contrast, so no strong shadows exist. 187 | Azimuth: 188 | The horizontal angle clockwise from North. I.e. North is zero degrees (0°), East 90°, South 180° and West 270°. The Magnetic declination is automatically taken into account using the specified location. 189 | Elevation: 190 | Sometimes referred to as altitude, is the angle between the devices horizon and the sun. The horizon is at 0° and the zenith at 90° 191 | Moon phase: 192 | The percentage of the lit surface of the moon. 0% corresponds to a new moon, 100% to a full moon. The moon is either waxing (the lit surface is increasing) or waning (decreasing). 193 | Time zone: 194 | The Time zone can be changed in the Main View. 195 | In the AR (augmented reality) feature of Sun Locator, the Sun and Moon position is overlaid on your device\'s camera and helps you to directly track the sun and moon path and check for any obstructions. The slider lets you fast forward in time throughout the day and year. 196 | [A] Date/Time: Use the bottom slider to change the date and time. 197 | [B] Switch Sun/Moon: Switch from displaying the Sun position and path to displaying the Moon. 198 | [C] Slider range: Using the slider will either set the time of day or day in year. 199 | [D] Slider: set the date and time by going back and forth. 200 | [E] Sun/Moon path throughout the day. 201 | [F] Current position of the Sun/Moon 202 | Accuracy 203 | The Camera view relies heavily on your device\'s internal compass. This sensor becomes inaccurate when not calibrated regularly. It\'s best to calibrate your device before each use of the app. You find instruction for doing so on the apps main menu. 204 | When using the Camera View, make sure to move away from any magnetic interference and remove any magnets (e.g. in phone cases). 205 | The solar and lunar path is overlaid on top of Google Maps and the shadow is displayed on a 3D sundial. 206 | [A] Switch Sun/Moon: Switch from displaying the Sun position and path to displaying the Moon. 207 | [B] Data: Chosen date/time, Sun/Moon position, Shadow ratio/length 208 | [C] Show/Hide Advanced options 209 | [D] Map layer: choose between different map types (normal, satellite, terrain) to be displayed 210 | [E] Lock position: keep position locked (no scrolling) 211 | [F] Compass direction: rotate map according to your device\'s compass 212 | [G] Set Sun dial height: customize the height of the sun dial 213 | [H] Slider range: Using the slider will either set the time of day or day in year 214 | [I] Slider: set the date and time by going back and forth. 215 | [J] Shadow cast by the 3D sundial 216 | [K] Sun/moon path throughout the day 217 | [L] Current sun/moon position 218 | Use pinch to zoom in/out, drag to change the position and two finger drag to tilt the view (just as in Google Maps). 219 | Main View 220 | Camera View 221 | Map View 222 | This is a BETA feature, please report any problems to contact@sunlocator.com 223 | Storage is full, can\'t download file 224 | Querying server for map... 225 | Downloading Map... 226 | Querying Server for Topographical Model... 227 | Downloading Topographical Model... 228 | Time out Error: Are you connected to the Internet? Please try again. 229 | Location is not (yet) supported 230 | Unknown Error 231 | No Internet connection 232 | Error: No WebGL detected on your device 233 | 3D Terrain Simulator 234 | -Simulation of lighting conditions in a 3D landscape during the course of a day and year.\n - Shadows of mountains, hills and valleys are calculated 235 | 3D Terrain Simulation 236 | Can\'t open 3D Terrain. 237 | EXAMPLE 238 | Add widget 239 | Failed to contact Google Play, please try again later. 240 | Google Play purchase was not successful 241 | Google Play purchase is still pending. Check back later 242 | You have already disabled ads.\nThank you for your support! 243 | No one likes ads, but it\'s what pays our coffee to keep the app running. Feed our coffein addiction, buy us a coffee and we remove the ads from the app. 244 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 14 | 18 | 19 | 23 | 24 | 29 | 30 | 31 | 32 | 35 | 36 | 45 | 46 | 57 | 58 | 63 | 64 | 69 | 70 | 74 | 75 | 79 | 80 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /app/src/main/res/xml/day_night_widget_info.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/rs/daynightscript.rs: -------------------------------------------------------------------------------- 1 | #pragma version(1) 2 | #pragma rs_fp_relaxed 3 | #pragma rs java_package_name(com.genewarrior.sunlocator.app) 4 | 5 | #include "rs_debug.rsh" 6 | 7 | float width; 8 | float height; 9 | 10 | float alpha; 11 | float delta; 12 | float h_pre; 13 | 14 | float static elev(float latitude, float longitude) { 15 | float H = h_pre + radians(longitude) - alpha; 16 | H = fmod((H + M_PI),(2 * M_PI)) - M_PI; 17 | if (H < 0 - M_PI) { 18 | H += 2 * M_PI; 19 | } 20 | float sPhi = sin(radians(latitude)); 21 | float cPhi = sqrt((1 - sPhi * sPhi)); 22 | float sDelta = sin(delta); 23 | float cDelta = sqrt(1 - sDelta * sDelta); 24 | float cH = cos(H); 25 | 26 | float sEpsilon0 = sPhi * sDelta + cPhi * cDelta * cH; 27 | float eP = asin(sEpsilon0) - 4.26e-5 * sqrt(1.0f - sEpsilon0 * sEpsilon0); 28 | 29 | 30 | float z = M_PI / 2 - eP; 31 | 32 | return 90.0f - degrees(z); 33 | } 34 | 35 | uchar4 __attribute__((kernel)) root(uchar4 in, uint32_t x, uint32_t y) { 36 | uchar4 nightColor = rsPackColorTo8888(0.1f, 0.1f, 0.1f, 0.85f); 37 | uchar4 astroColor = rsPackColorTo8888(0.15f, 0.15f, 0.2f, 0.8f); 38 | uchar4 nautColor = rsPackColorTo8888(0.12f, 0.2f, 0.40f, 0.7f); 39 | uchar4 civilColor = rsPackColorTo8888(0.20f, 0.35f, 0.53f, 0.6f); 40 | uchar4 dayColor = rsPackColorTo8888(1.0f, 1.0f, 1.0f, 0.0f); //full transparency 41 | 42 | 43 | //conversion from pixel position to lat/lon on plate carée protection 44 | float longitude = ((360.0f/width)*(float)x)-180.0f; 45 | float latitude = 90.0f-((180.0f/height)*(float)y); 46 | 47 | float z = elev(latitude, longitude); 48 | 49 | if (z < -18.0f) { 50 | return nightColor; 51 | } 52 | if (z < -12.0f) { //astronomical twilight 53 | return astroColor; 54 | } 55 | if (z < -6.0f) { //nautical twilight 56 | return nautColor; 57 | } 58 | if (z < -0.8333f) { //civil twilight 59 | return civilColor; 60 | } 61 | return dayColor; //anything above -0.833 is day 62 | 63 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | google() 6 | jcenter() 7 | maven { url "https://jitpack.io" } 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:7.3.1' 11 | 12 | // NOTE: Do not place your application dependencies here; they belong 13 | // in the individual module build.gradle files 14 | } 15 | } 16 | 17 | allprojects { 18 | repositories { 19 | google() 20 | jcenter() 21 | maven { url "https://jitpack.io" } 22 | } 23 | } 24 | 25 | task clean(type: Delete) { 26 | delete rootProject.buildDir 27 | } 28 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | 21 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r-follador/DayNightMap/d2e73cc879603837d5fb04fde6a9fd1cda005792/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Sep 09 21:17:41 CEST 2020 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r-follador/DayNightMap/d2e73cc879603837d5fb04fde6a9fd1cda005792/screenshot2.png -------------------------------------------------------------------------------- /screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r-follador/DayNightMap/d2e73cc879603837d5fb04fde6a9fd1cda005792/screenshot3.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | rootProject.name='Day & Night Map' 3 | --------------------------------------------------------------------------------